rubocop 1.4.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/assets/logo.png +0 -0
- data/assets/output.html.erb +261 -0
- data/config/default.yml +49 -9
- data/lib/rubocop.rb +4 -0
- data/lib/rubocop/cli.rb +5 -1
- data/lib/rubocop/cli/command/suggest_extensions.rb +108 -0
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +5 -1
- data/lib/rubocop/config_obsoletion.rb +21 -3
- data/lib/rubocop/config_validator.rb +8 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +3 -3
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
- data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +1 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +7 -2
- data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
- data/lib/rubocop/cop/metrics/block_length.rb +13 -7
- data/lib/rubocop/cop/metrics/method_length.rb +7 -2
- data/lib/rubocop/cop/metrics/parameter_lists.rb +64 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
- data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
- data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
- data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/style/and_or.rb +10 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
- data/lib/rubocop/cop/style/format_string.rb +8 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
- data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
- data/lib/rubocop/cop/style/redundant_argument.rb +3 -1
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +59 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +7 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
- data/lib/rubocop/core_ext/hash.rb +20 -0
- data/lib/rubocop/ext/regexp_node.rb +29 -12
- data/lib/rubocop/ext/regexp_parser.rb +20 -9
- data/lib/rubocop/version.rb +1 -1
- metadata +23 -5
data/lib/rubocop/cli.rb
CHANGED
@@ -69,10 +69,14 @@ module RuboCop
|
|
69
69
|
if @options[:auto_gen_config]
|
70
70
|
run_command(:auto_gen_config)
|
71
71
|
else
|
72
|
-
run_command(:execute_runner)
|
72
|
+
run_command(:execute_runner).tap { suggest_extensions }
|
73
73
|
end
|
74
74
|
end
|
75
75
|
|
76
|
+
def suggest_extensions
|
77
|
+
run_command(:suggest_extensions)
|
78
|
+
end
|
79
|
+
|
76
80
|
def validate_options_vs_config
|
77
81
|
if @options[:parallel] &&
|
78
82
|
!@config_store.for_pwd.for_all_cops['UseCache']
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
class CLI
|
5
|
+
module Command
|
6
|
+
# Run all the selected cops and report the result.
|
7
|
+
# @api private
|
8
|
+
class SuggestExtensions < Base
|
9
|
+
# Combination of short and long formatter names.
|
10
|
+
INCLUDED_FORMATTERS = %w[p progress fu fuubar pa pacman].freeze
|
11
|
+
|
12
|
+
self.command_name = :suggest_extensions
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Gems that the current bundle depends on
|
16
|
+
# Suggestions are only made for gems that are 1st party dependencies
|
17
|
+
def dependent_gems
|
18
|
+
bundler do |gems|
|
19
|
+
gems.dependencies.map(&:name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# All installed gems in the current bundle
|
24
|
+
# If a suggestion is installed but not depended on, it still should not
|
25
|
+
# be suggested.
|
26
|
+
def installed_gems
|
27
|
+
bundler do
|
28
|
+
# Load specs from the lockfile without trying to resolve them
|
29
|
+
return [] unless Bundler.default_lockfile
|
30
|
+
|
31
|
+
lockfile = Bundler.read_file(Bundler.default_lockfile)
|
32
|
+
Bundler::LockfileParser.new(lockfile).specs.map(&:name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def bundler
|
39
|
+
return [] unless defined?(Bundler)
|
40
|
+
|
41
|
+
yield Bundler.load
|
42
|
+
rescue Bundler::BundlerError
|
43
|
+
[]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
return if skip? || extensions.none?
|
49
|
+
|
50
|
+
puts
|
51
|
+
puts 'Tip: Based on detected gems, the following '\
|
52
|
+
'RuboCop extension libraries might be helpful:'
|
53
|
+
|
54
|
+
extensions.sort.each do |extension|
|
55
|
+
puts " * #{extension} (http://github.com/rubocop-hq/#{extension})"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts
|
59
|
+
puts 'You can opt out of this message by adding the following to your config '\
|
60
|
+
'(see https://docs.rubocop.org/rubocop/extensions.html#extension-suggestions '\
|
61
|
+
'for more options):'
|
62
|
+
puts ' AllCops:'
|
63
|
+
puts ' SuggestExtensions: false'
|
64
|
+
|
65
|
+
puts if @options[:display_time]
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def skip?
|
71
|
+
# Disable outputting the notification:
|
72
|
+
# 1. On CI
|
73
|
+
# 2. When given RuboCop options that it doesn't make sense for
|
74
|
+
# 3. For all formatters except specified in `INCLUDED_FORMATTERS'`
|
75
|
+
ENV['CI'] ||
|
76
|
+
@options[:only] || @options[:debug] || @options[:list_target_files] || @options[:out] ||
|
77
|
+
!INCLUDED_FORMATTERS.include?(current_formatter)
|
78
|
+
end
|
79
|
+
|
80
|
+
def current_formatter
|
81
|
+
@options[:format] || @config_store.for_pwd.for_all_cops['DefaultFormatter'] || 'p'
|
82
|
+
end
|
83
|
+
|
84
|
+
def extensions
|
85
|
+
return [] unless dependent_gems.any?
|
86
|
+
|
87
|
+
@extensions ||= begin
|
88
|
+
extensions = @config_store.for_pwd.for_all_cops['SuggestExtensions'] || {}
|
89
|
+
extensions.select { |_, v| (Array(v) & dependent_gems).any? }.keys - installed_gems
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def puts(*args)
|
94
|
+
output = (@options[:stderr] ? $stderr : $stdout)
|
95
|
+
output.puts(*args)
|
96
|
+
end
|
97
|
+
|
98
|
+
def dependent_gems
|
99
|
+
@dependent_gems ||= self.class.dependent_gems
|
100
|
+
end
|
101
|
+
|
102
|
+
def installed_gems
|
103
|
+
@installed_gems ||= self.class.installed_gems
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -109,7 +109,7 @@ module RuboCop
|
|
109
109
|
end
|
110
110
|
|
111
111
|
merge_with_default(config, config_file).tap do |merged_config|
|
112
|
-
warn_on_pending_cops(merged_config.pending_cops) unless possible_new_cops?(
|
112
|
+
warn_on_pending_cops(merged_config.pending_cops) unless possible_new_cops?(merged_config)
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -92,7 +92,7 @@ module RuboCop
|
|
92
92
|
keys_appearing_in_both.each do |key|
|
93
93
|
if opts[:unset_nil] && derived_hash[key].nil?
|
94
94
|
result.delete(key)
|
95
|
-
elsif base_hash
|
95
|
+
elsif merge_hashes?(base_hash, derived_hash, key)
|
96
96
|
result[key] = merge(base_hash[key], derived_hash[key], **opts)
|
97
97
|
elsif should_union?(base_hash, key, opts[:inherit_mode])
|
98
98
|
result[key] = base_hash[key] | derived_hash[key]
|
@@ -164,6 +164,10 @@ module RuboCop
|
|
164
164
|
inherit_mode['merge'].include?(key)
|
165
165
|
end
|
166
166
|
|
167
|
+
def merge_hashes?(base_hash, derived_hash, key)
|
168
|
+
base_hash[key].is_a?(Hash) && derived_hash[key].is_a?(Hash)
|
169
|
+
end
|
170
|
+
|
167
171
|
def base_configs(path, inherit_from, file)
|
168
172
|
configs = Array(inherit_from).compact.map do |f|
|
169
173
|
ConfigLoader.load_file(inherited_file(path, f, file))
|
@@ -104,6 +104,7 @@ module RuboCop
|
|
104
104
|
OBSOLETE_COPS = Hash[*(RENAMED_COPS + MOVED_COPS + REMOVED_COPS +
|
105
105
|
REMOVED_COPS_WITH_REASON + SPLIT_COPS).flatten]
|
106
106
|
|
107
|
+
# Parameters can be deprecated but not disabled by setting `severity: :warning`
|
107
108
|
OBSOLETE_PARAMETERS = [
|
108
109
|
{
|
109
110
|
cops: %w[Layout/SpaceAroundOperators Style/SpaceAroundOperators],
|
@@ -201,6 +202,12 @@ module RuboCop
|
|
201
202
|
parameters: 'NameWhitelist',
|
202
203
|
alternative: '`NameWhitelist` has been renamed to ' \
|
203
204
|
'`AllowedMethods`.'
|
205
|
+
},
|
206
|
+
{
|
207
|
+
cops: %w[Metrics/BlockLength Metrics/MethodLength],
|
208
|
+
parameters: 'ExcludedMethods',
|
209
|
+
alternative: '`ExcludedMethods` has been renamed to `IgnoredMethods`.',
|
210
|
+
severity: :warning
|
204
211
|
}
|
205
212
|
].freeze
|
206
213
|
|
@@ -214,8 +221,11 @@ module RuboCop
|
|
214
221
|
}
|
215
222
|
].freeze
|
216
223
|
|
224
|
+
attr_reader :warnings
|
225
|
+
|
217
226
|
def initialize(config)
|
218
227
|
@config = config
|
228
|
+
@warnings = []
|
219
229
|
end
|
220
230
|
|
221
231
|
def reject_obsolete_cops_and_parameters
|
@@ -256,9 +266,17 @@ module RuboCop
|
|
256
266
|
end
|
257
267
|
|
258
268
|
def obsolete_parameters
|
259
|
-
OBSOLETE_PARAMETERS.
|
260
|
-
obsolete_parameter_message(params[:cops], params[:parameters],
|
261
|
-
|
269
|
+
OBSOLETE_PARAMETERS.collect do |params|
|
270
|
+
messages = obsolete_parameter_message(params[:cops], params[:parameters],
|
271
|
+
params[:alternative])
|
272
|
+
|
273
|
+
# Warnings are collected separately and not added to the error message
|
274
|
+
if messages && params.fetch(:severity, :error) == :warning
|
275
|
+
@warnings.concat(messages)
|
276
|
+
next
|
277
|
+
end
|
278
|
+
|
279
|
+
messages
|
262
280
|
end
|
263
281
|
end
|
264
282
|
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
42
42
|
ConfigLoader.default_configuration.key?(key)
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
check_obsoletions
|
46
46
|
|
47
47
|
alert_about_unrecognized_cops(invalid_cop_names)
|
48
48
|
check_target_ruby
|
@@ -68,6 +68,13 @@ module RuboCop
|
|
68
68
|
|
69
69
|
attr_reader :target_ruby
|
70
70
|
|
71
|
+
def check_obsoletions
|
72
|
+
@config_obsoletion.reject_obsolete_cops_and_parameters
|
73
|
+
return unless @config_obsoletion.warnings.any?
|
74
|
+
|
75
|
+
warn Rainbow("Warning: #{@config_obsoletion.warnings.join("\n")}").yellow
|
76
|
+
end
|
77
|
+
|
71
78
|
def check_target_ruby
|
72
79
|
return if target_ruby.supported?
|
73
80
|
|
@@ -39,16 +39,31 @@ module RuboCop
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def disable_offense(range)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
disable_offense_at_end_of_line(range_of_first_line(range),
|
46
|
-
eol_comment)
|
42
|
+
heredoc_range = surrounding_heredoc(range)
|
43
|
+
if heredoc_range
|
44
|
+
disable_offense_before_and_after(range_by_lines(heredoc_range))
|
47
45
|
else
|
48
|
-
|
46
|
+
eol_comment = " # rubocop:todo #{cop_name}"
|
47
|
+
needed_line_length = (range.source_line + eol_comment).length
|
48
|
+
if needed_line_length <= max_line_length
|
49
|
+
disable_offense_at_end_of_line(range_of_first_line(range), eol_comment)
|
50
|
+
else
|
51
|
+
disable_offense_before_and_after(range_by_lines(range))
|
52
|
+
end
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
56
|
+
def surrounding_heredoc(offense_range)
|
57
|
+
# The empty offense range is an edge case that can be reached from the Lint/Syntax cop.
|
58
|
+
return nil if offense_range.empty?
|
59
|
+
|
60
|
+
heredoc_nodes = processed_source.ast.each_descendant.select do |node|
|
61
|
+
node.respond_to?(:heredoc?) && node.heredoc?
|
62
|
+
end
|
63
|
+
heredoc_nodes.map { |node| node.loc.expression.join(node.loc.heredoc_end) }
|
64
|
+
.find { |range| range.contains?(offense_range) }
|
65
|
+
end
|
66
|
+
|
52
67
|
def range_of_first_line(range)
|
53
68
|
begin_of_first_line = range.begin_pos - range.column
|
54
69
|
end_of_first_line = begin_of_first_line + range.source_line.length
|
@@ -139,7 +139,7 @@ module RuboCop
|
|
139
139
|
badge: badge,
|
140
140
|
version_added: version_added)
|
141
141
|
|
142
|
-
injector.inject do
|
142
|
+
injector.inject do # rubocop:disable Lint/UnexpectedBlockArity
|
143
143
|
output.puts(format(CONFIGURATION_ADDED_MESSAGE,
|
144
144
|
configuration_file_path: config_file_path))
|
145
145
|
end
|
@@ -3,15 +3,15 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Layout
|
6
|
-
# This cop checks whether method definitions are
|
7
|
-
# separated by one empty
|
6
|
+
# This cop checks whether class/module/method definitions are
|
7
|
+
# separated by one or more empty lines.
|
8
8
|
#
|
9
9
|
# `NumberOfEmptyLines` can be an integer (default is 1) or
|
10
10
|
# an array (e.g. [1, 2]) to specify a minimum and maximum
|
11
11
|
# number of empty lines permitted.
|
12
12
|
#
|
13
13
|
# `AllowAdjacentOneLineDefs` configures whether adjacent
|
14
|
-
# one-line
|
14
|
+
# one-line definitions are considered an offense.
|
15
15
|
#
|
16
16
|
# @example EmptyLineBetweenMethodDefs: true (default)
|
17
17
|
# # checks for empty lines between method definitions.
|
@@ -45,7 +45,8 @@ module RuboCop
|
|
45
45
|
MSG = 'Empty line detected around arguments.'
|
46
46
|
|
47
47
|
def on_send(node)
|
48
|
-
return if node.single_line? || node.arguments.empty?
|
48
|
+
return if node.single_line? || node.arguments.empty? ||
|
49
|
+
receiver_and_method_call_on_different_lines?(node)
|
49
50
|
|
50
51
|
extra_lines(node) do |range|
|
51
52
|
add_offense(range) do |corrector|
|
@@ -57,6 +58,10 @@ module RuboCop
|
|
57
58
|
|
58
59
|
private
|
59
60
|
|
61
|
+
def receiver_and_method_call_on_different_lines?(node)
|
62
|
+
node.receiver && node.receiver.loc.last_line != node.loc.selector&.line
|
63
|
+
end
|
64
|
+
|
60
65
|
def empty_lines(node)
|
61
66
|
lines = processed_lines(node)
|
62
67
|
lines.select! { |code, _| code.empty? }
|
@@ -37,14 +37,14 @@ module RuboCop
|
|
37
37
|
# # good
|
38
38
|
# puts 'Hello' # Return character is CR+LF on all platfoms.
|
39
39
|
#
|
40
|
-
class EndOfLine <
|
40
|
+
class EndOfLine < Base
|
41
41
|
include ConfigurableEnforcedStyle
|
42
42
|
include RangeHelp
|
43
43
|
|
44
44
|
MSG_DETECTED = 'Carriage return character detected.'
|
45
45
|
MSG_MISSING = 'Carriage return character missing.'
|
46
46
|
|
47
|
-
def
|
47
|
+
def on_new_investigation
|
48
48
|
last_line = last_line(processed_source)
|
49
49
|
|
50
50
|
processed_source.raw_source.each_line.with_index do |line, index|
|
@@ -54,9 +54,9 @@ module RuboCop
|
|
54
54
|
next unless msg
|
55
55
|
next if unimportant_missing_cr?(index, last_line, line)
|
56
56
|
|
57
|
-
range =
|
58
|
-
|
59
|
-
add_offense(
|
57
|
+
range = source_range(processed_source.buffer, index + 1, 0, line.length)
|
58
|
+
|
59
|
+
add_offense(range, message: msg)
|
60
60
|
# Usually there will be carriage return characters on all or none
|
61
61
|
# of the lines in a file, so we report only one offense.
|
62
62
|
break
|
@@ -207,8 +207,13 @@ module RuboCop
|
|
207
207
|
PATTERN
|
208
208
|
|
209
209
|
def base_range(send_node, arg_node)
|
210
|
-
|
211
|
-
|
210
|
+
parent = send_node.parent
|
211
|
+
start_node = if parent && (parent.splat_type? || parent.kwsplat_type?)
|
212
|
+
send_node.parent
|
213
|
+
else
|
214
|
+
send_node
|
215
|
+
end
|
216
|
+
range_between(start_node.source_range.begin_pos, arg_node.source_range.begin_pos)
|
212
217
|
end
|
213
218
|
|
214
219
|
# Returns the column of the given range. For single line ranges, this
|
@@ -69,6 +69,7 @@ module RuboCop
|
|
69
69
|
return unless outermost_send
|
70
70
|
return unless outermost_send.loc.end
|
71
71
|
return unless heredoc_arg.first_line != outermost_send.loc.end.line
|
72
|
+
return if subsequent_closing_parentheses_in_same_line?(outermost_send)
|
72
73
|
|
73
74
|
add_offense(outermost_send.loc.end) do |corrector|
|
74
75
|
autocorrect(corrector, outermost_send)
|
@@ -160,6 +161,17 @@ module RuboCop
|
|
160
161
|
|
161
162
|
# Closing parenthesis helpers.
|
162
163
|
|
164
|
+
def subsequent_closing_parentheses_in_same_line?(outermost_send)
|
165
|
+
last_arg_of_outer_send = outermost_send.last_argument
|
166
|
+
return false unless last_arg_of_outer_send&.loc.respond_to?(:end) &&
|
167
|
+
(end_of_last_arg_of_outer_send = last_arg_of_outer_send.loc.end)
|
168
|
+
|
169
|
+
end_of_outer_send = outermost_send.loc.end
|
170
|
+
|
171
|
+
end_of_outer_send.line == end_of_last_arg_of_outer_send.line &&
|
172
|
+
end_of_outer_send.column == end_of_last_arg_of_outer_send.column + 1
|
173
|
+
end
|
174
|
+
|
163
175
|
def fix_closing_parenthesis(node, corrector)
|
164
176
|
remove_incorrect_closing_paren(node, corrector)
|
165
177
|
add_correct_closing_paren(node, corrector)
|
@@ -77,7 +77,7 @@ module RuboCop
|
|
77
77
|
|
78
78
|
@base = alignment_base(node, rhs, given_style)
|
79
79
|
correct_column = if @base
|
80
|
-
@base.column + extra_indentation(given_style)
|
80
|
+
@base.column + extra_indentation(given_style, node.parent)
|
81
81
|
else
|
82
82
|
indentation(lhs) + correct_indentation(node)
|
83
83
|
end
|
@@ -85,9 +85,13 @@ module RuboCop
|
|
85
85
|
rhs if @column_delta.nonzero?
|
86
86
|
end
|
87
87
|
|
88
|
-
def extra_indentation(given_style)
|
88
|
+
def extra_indentation(given_style, parent)
|
89
89
|
if given_style == :indented_relative_to_receiver
|
90
|
-
|
90
|
+
if parent && (parent.splat_type? || parent.kwsplat_type?)
|
91
|
+
configured_indentation_width - parent.loc.operator.length
|
92
|
+
else
|
93
|
+
configured_indentation_width
|
94
|
+
end
|
91
95
|
else
|
92
96
|
0
|
93
97
|
end
|
@@ -23,10 +23,11 @@ module RuboCop
|
|
23
23
|
'Use double quoted strings if you need interpolation.'
|
24
24
|
|
25
25
|
def on_str(node)
|
26
|
-
|
27
|
-
return if
|
26
|
+
return unless node
|
27
|
+
return if string_or_regex?(node.parent)
|
28
28
|
return unless /(?<!\\)#\{.*\}/.match?(node.source)
|
29
29
|
return if heredoc?(node)
|
30
|
+
return unless node.loc.begin && node.loc.end
|
30
31
|
|
31
32
|
add_offense(node) do |corrector|
|
32
33
|
autocorrect(corrector, node)
|
@@ -35,6 +36,10 @@ module RuboCop
|
|
35
36
|
|
36
37
|
private
|
37
38
|
|
39
|
+
def string_or_regex?(node)
|
40
|
+
node&.dstr_type? || node&.regexp_type?
|
41
|
+
end
|
42
|
+
|
38
43
|
def autocorrect(corrector, node)
|
39
44
|
starting_token, ending_token = if node.source.include?('"')
|
40
45
|
['%{', '}']
|