rubocop 0.45.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +17 -0
  4. data/config/enabled.yml +29 -2
  5. data/lib/rubocop.rb +6 -1
  6. data/lib/rubocop/config.rb +0 -10
  7. data/lib/rubocop/config_loader.rb +21 -9
  8. data/lib/rubocop/cop/bundler/duplicated_gem.rb +69 -0
  9. data/lib/rubocop/cop/bundler/ordered_gems.rb +54 -0
  10. data/lib/rubocop/cop/cop.rb +1 -0
  11. data/lib/rubocop/cop/lint/debugger.rb +9 -1
  12. data/lib/rubocop/cop/lint/each_with_object_argument.rb +5 -6
  13. data/lib/rubocop/cop/lint/eval.rb +3 -7
  14. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +6 -4
  15. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +13 -4
  16. data/lib/rubocop/cop/lint/useless_comparison.rb +5 -9
  17. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -0
  18. data/lib/rubocop/cop/metrics/line_length.rb +16 -3
  19. data/lib/rubocop/cop/mixin/access_modifier_node.rb +9 -9
  20. data/lib/rubocop/cop/mixin/configurable_numbering.rb +14 -7
  21. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +92 -20
  22. data/lib/rubocop/cop/performance/compare_with_block.rb +61 -0
  23. data/lib/rubocop/cop/performance/count.rb +21 -57
  24. data/lib/rubocop/cop/performance/detect.rb +15 -15
  25. data/lib/rubocop/cop/performance/flat_map.rb +23 -35
  26. data/lib/rubocop/cop/performance/sample.rb +84 -82
  27. data/lib/rubocop/cop/performance/string_replacement.rb +18 -43
  28. data/lib/rubocop/cop/rails/enum_uniqueness.rb +71 -0
  29. data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
  30. data/lib/rubocop/cop/rails/output.rb +8 -12
  31. data/lib/rubocop/cop/rails/read_write_attribute.rb +10 -6
  32. data/lib/rubocop/cop/rails/request_referer.rb +8 -9
  33. data/lib/rubocop/cop/rails/scope_args.rb +5 -11
  34. data/lib/rubocop/cop/style/access_modifier_indentation.rb +1 -1
  35. data/lib/rubocop/cop/style/and_or.rb +1 -1
  36. data/lib/rubocop/cop/style/array_join.rb +4 -8
  37. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  38. data/lib/rubocop/cop/style/case_equality.rb +3 -3
  39. data/lib/rubocop/cop/style/character_literal.rb +2 -4
  40. data/lib/rubocop/cop/style/class_check.rb +6 -6
  41. data/lib/rubocop/cop/style/colon_method_call.rb +6 -6
  42. data/lib/rubocop/cop/style/each_with_object.rb +13 -17
  43. data/lib/rubocop/cop/style/empty_literal.rb +46 -36
  44. data/lib/rubocop/cop/style/empty_method.rb +96 -0
  45. data/lib/rubocop/cop/style/even_odd.rb +19 -50
  46. data/lib/rubocop/cop/style/hash_syntax.rb +4 -1
  47. data/lib/rubocop/cop/style/lambda.rb +8 -18
  48. data/lib/rubocop/cop/style/module_function.rb +14 -11
  49. data/lib/rubocop/cop/style/nil_comparison.rb +4 -7
  50. data/lib/rubocop/cop/style/non_nil_check.rb +18 -36
  51. data/lib/rubocop/cop/style/numeric_predicate.rb +9 -10
  52. data/lib/rubocop/cop/style/op_method.rb +7 -9
  53. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  54. data/lib/rubocop/cop/style/proc.rb +5 -9
  55. data/lib/rubocop/cop/style/redundant_freeze.rb +6 -7
  56. data/lib/rubocop/cop/style/send.rb +6 -3
  57. data/lib/rubocop/cop/style/space_inside_block_braces.rb +1 -1
  58. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  59. data/lib/rubocop/cop/style/symbol_proc.rb +22 -43
  60. data/lib/rubocop/cop/style/ternary_parentheses.rb +67 -18
  61. data/lib/rubocop/cop/util.rb +1 -1
  62. data/lib/rubocop/cop/variable_force/assignment.rb +2 -0
  63. data/lib/rubocop/cop/variable_force/locatable.rb +8 -6
  64. data/lib/rubocop/cop/variable_force/reference.rb +2 -0
  65. data/lib/rubocop/formatter/base_formatter.rb +4 -8
  66. data/lib/rubocop/formatter/fuubar_style_formatter.rb +6 -0
  67. data/lib/rubocop/node_pattern.rb +7 -5
  68. data/lib/rubocop/processed_source.rb +1 -0
  69. data/lib/rubocop/rspec/cop_helper.rb +4 -0
  70. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +1 -1
  71. data/lib/rubocop/version.rb +1 -1
  72. metadata +7 -3
  73. data/lib/rubocop/cop/performance/sort_with_block.rb +0 -53
@@ -6,7 +6,8 @@ module RuboCop
6
6
  # This cop checks for non-local exit from iterator, without return value.
7
7
  # It warns only when satisfies all of these: `return` doesn't have return
8
8
  # value, the block is preceded by a method chain, the block has arguments,
9
- # and the method which receives the block is not `define_method`.
9
+ # and the method which receives the block is not `define_method`
10
+ # or `define_singleton_method`.
10
11
  #
11
12
  # @example
12
13
  #
@@ -44,8 +45,9 @@ module RuboCop
44
45
 
45
46
  # `return` does not exit to outside of lambda block, this is safe.
46
47
  break if block_node.lambda?
47
- # if a proc is passed to `Module#define_method`, `return` will not
48
- # cause a non-local exit error
48
+ # if a proc is passed to `Module#define_method` or
49
+ # `Object#define_singleton_method`, `return` will not cause a
50
+ # non-local exit error
49
51
  break if define_method?(send_node)
50
52
 
51
53
  next if args_node.children.empty?
@@ -67,7 +69,7 @@ module RuboCop
67
69
 
68
70
  def define_method?(send_node)
69
71
  _receiver, selector = *send_node
70
- selector == :define_method
72
+ %i(define_method define_singleton_method).include? selector
71
73
  end
72
74
  end
73
75
  end
@@ -64,7 +64,8 @@ module RuboCop
64
64
  return unless ASSIGNMENT_TYPES.include?(node.parent.parent.type)
65
65
  end
66
66
 
67
- if array_splat?(node) && method_argument?(node)
67
+ if array_splat?(node) &&
68
+ (method_argument?(node) || part_of_an_array?(node))
68
69
  add_offense(node, :expression, ARRAY_PARAM_MSG)
69
70
  else
70
71
  add_offense(node, :expression)
@@ -76,13 +77,12 @@ module RuboCop
76
77
 
77
78
  def autocorrect(node)
78
79
  variable, = *node
79
- parent = node.parent
80
80
  loc = node.loc
81
81
 
82
82
  lambda do |corrector|
83
83
  if !variable.array_type?
84
84
  corrector.replace(loc.expression, "[#{variable.source}]")
85
- elsif unneeded_brackets?(parent)
85
+ elsif unneeded_brackets?(node)
86
86
  corrector.replace(loc.expression, remove_brackets(variable))
87
87
  else
88
88
  corrector.remove(loc.operator)
@@ -98,10 +98,19 @@ module RuboCop
98
98
  node.parent.send_type?
99
99
  end
100
100
 
101
+ def part_of_an_array?(node)
102
+ # The parent of a splat expansion is an array that does not have
103
+ # `begin` or `end`
104
+ parent = node.parent
105
+ parent.array_type? && parent.loc.begin && parent.loc.end
106
+ end
107
+
101
108
  def unneeded_brackets?(node)
102
109
  parent = node.parent
110
+ grandparent = node.parent.parent
103
111
 
104
- node.when_type? || node.send_type? || (parent && parent.resbody_type?)
112
+ parent.when_type? || parent.send_type? || part_of_an_array?(node) ||
113
+ (grandparent && grandparent.resbody_type?)
105
114
  end
106
115
 
107
116
  def remove_brackets(array)
@@ -10,18 +10,14 @@ module RuboCop
10
10
  # x.top >= x.top
11
11
  class UselessComparison < Cop
12
12
  MSG = 'Comparison of something with itself detected.'.freeze
13
-
14
13
  OPS = %w(== === != < > <= >= <=>).freeze
15
14
 
16
- def on_send(node)
17
- # lambda.() does not have a selector
18
- return unless node.loc.selector
19
-
20
- op = node.loc.selector.source
21
- return unless OPS.include?(op)
15
+ def_node_matcher :comparison?, "(send $_ {:#{OPS.join(' :')}} $_)"
22
16
 
23
- receiver, _method, args = *node
24
- add_offense(node, :selector) if receiver == args
17
+ def on_send(node)
18
+ comparison?(node) do |receiver, args|
19
+ add_offense(node, :selector) if receiver == args
20
+ end
25
21
  end
26
22
  end
27
23
  end
@@ -53,6 +53,7 @@ module RuboCop
53
53
  class MethodVariableTracker
54
54
  def initialize(body_node)
55
55
  @body_node = body_node
56
+ @local = nil
56
57
  end
57
58
 
58
59
  def contain_local_object?(variable_name)
@@ -23,11 +23,11 @@ module RuboCop
23
23
 
24
24
  def check_line(line, index, heredocs)
25
25
  return if line.length <= max
26
+ return if ignored_line?(line, index, heredocs)
27
+
26
28
  if ignore_cop_directives? && directive_on_source_line?(index)
27
29
  return check_directive_line(line, index)
28
30
  end
29
- return if heredocs &&
30
- line_in_whitelisted_heredoc?(heredocs, index.succ)
31
31
  return check_uri_line(line, index) if allow_uri?
32
32
 
33
33
  offense(
@@ -36,6 +36,11 @@ module RuboCop
36
36
  )
37
37
  end
38
38
 
39
+ def ignored_line?(line, index, heredocs)
40
+ matches_ignored_pattern?(line) ||
41
+ heredocs && line_in_whitelisted_heredoc?(heredocs, index.succ)
42
+ end
43
+
39
44
  def offense(loc, line)
40
45
  message = format(MSG, line.length, max)
41
46
  add_offense(nil, loc, message) { self.max = line.length }
@@ -81,6 +86,14 @@ module RuboCop
81
86
  end
82
87
  end
83
88
 
89
+ def matches_ignored_pattern?(line)
90
+ ignored_patterns.any? { |pattern| Regexp.new(pattern).match(line) }
91
+ end
92
+
93
+ def ignored_patterns
94
+ cop_config['IgnoredPatterns'] || []
95
+ end
96
+
84
97
  def allow_uri?
85
98
  cop_config['AllowURI']
86
99
  end
@@ -117,7 +130,7 @@ module RuboCop
117
130
  end
118
131
 
119
132
  def uri_regexp
120
- @regexp ||= URI.regexp(cop_config['URISchemes'])
133
+ @regexp ||= URI::Parser.new.make_regexp(cop_config['URISchemes'])
121
134
  end
122
135
 
123
136
  def check_directive_line(line, index)
@@ -4,12 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for checking modifier nodes.
6
6
  module AccessModifierNode
7
- extend RuboCop::Sexp
7
+ extend NodePattern::Macros
8
8
 
9
- PRIVATE_NODE = s(:send, nil, :private)
10
- PROTECTED_NODE = s(:send, nil, :protected)
11
- PUBLIC_NODE = s(:send, nil, :public)
12
- MODULE_FUNCTION_NODE = s(:send, nil, :module_function)
9
+ def_node_matcher :private_node?, '(send nil :private)'
10
+ def_node_matcher :protected_node?, '(send nil :protected)'
11
+ def_node_matcher :public_node?, '(send nil :public)'
12
+ def_node_matcher :module_function_node?, '(send nil :module_function)'
13
13
 
14
14
  # Returns true when the node is an access modifier.
15
15
  def modifier_node?(node)
@@ -18,10 +18,10 @@ module RuboCop
18
18
 
19
19
  # Returns true when the node looks like an access modifier.
20
20
  def modifier_structure?(node)
21
- [PRIVATE_NODE,
22
- PROTECTED_NODE,
23
- PUBLIC_NODE,
24
- MODULE_FUNCTION_NODE].include?(node)
21
+ private_node?(node) ||
22
+ protected_node?(node) ||
23
+ public_node?(node) ||
24
+ module_function_node?(node)
25
25
  end
26
26
 
27
27
  # Returns true when the parent of what looks like an access modifier
@@ -7,23 +7,30 @@ module RuboCop
7
7
  module ConfigurableNumbering
8
8
  include ConfigurableEnforcedStyle
9
9
 
10
- SNAKE_CASE = /(^_|[a-z]|_\d+)$/
11
- NORMAL_CASE = /(^_|[A-Za-z]\d*)$/
12
- NON_INTEGER = /(^_|[A-Za-z])$/
10
+ SNAKE_CASE = /(?:[a-z_]|_\d+)$/
11
+ NORMAL_CASE = /(?:_\D*|[A-Za-z]\d*)$/
12
+ NON_INTEGER = /[A-Za-z_]$/
13
13
 
14
14
  def check_name(node, name, name_range)
15
15
  return if operator?(name)
16
16
 
17
- if valid_name?(node, name)
17
+ if valid_name?(node, name, style)
18
18
  correct_style_detected
19
19
  else
20
- add_offense(node, name_range, message(style))
20
+ add_offense(node, name_range, message(style)) do
21
+ (supported_styles - [style]).each do |other_style|
22
+ if valid_name?(node, name, other_style)
23
+ unexpected_style_detected(other_style)
24
+ break
25
+ end
26
+ end
27
+ end
21
28
  end
22
29
  end
23
30
 
24
- def valid_name?(node, name)
31
+ def valid_name?(node, name, given_style)
25
32
  pattern =
26
- case style
33
+ case given_style
27
34
  when :snake_case
28
35
  SNAKE_CASE
29
36
  when :normalcase
@@ -11,13 +11,15 @@ module RuboCop
11
11
 
12
12
  MSG_EXTRA = 'Extra empty line detected at %s body %s.'.freeze
13
13
  MSG_MISSING = 'Empty line missing at %s body %s.'.freeze
14
+ MSG_DEFERRED = 'Empty line missing before first %s definition'.freeze
14
15
 
15
16
  def_node_matcher :constant_definition?, '{class module}'
17
+ def_node_matcher :empty_line_required?, '{def defs class module}'
16
18
 
17
19
  def autocorrect(args)
18
- offence_style, range = args
20
+ offense_style, range = args
19
21
  lambda do |corrector|
20
- case offence_style
22
+ case offense_style
21
23
  when :no_empty_lines then
22
24
  corrector.remove(range)
23
25
  when :empty_lines then
@@ -40,33 +42,61 @@ module RuboCop
40
42
 
41
43
  case style
42
44
  when :empty_lines_except_namespace
43
- if namespace?(body, with_one_child: true)
44
- check_source(:no_empty_lines, first_line, last_line)
45
+ check_empty_lines_except_namespace(body, first_line, last_line)
46
+ when :empty_lines_special
47
+ check_empty_lines_special(body, first_line, last_line)
48
+ else
49
+ check_both(style, first_line, last_line)
50
+ end
51
+ end
52
+
53
+ def check_empty_lines_except_namespace(body, first_line, last_line)
54
+ if namespace?(body, with_one_child: true)
55
+ check_both(:no_empty_lines, first_line, last_line)
56
+ else
57
+ check_both(:empty_lines, first_line, last_line)
58
+ end
59
+ end
60
+
61
+ def check_empty_lines_special(body, first_line, last_line)
62
+ return unless body
63
+ if namespace?(body, with_one_child: true)
64
+ check_both(:no_empty_lines, first_line, last_line)
65
+ else
66
+ if first_child_requires_empty_line?(body)
67
+ check_beginning(:empty_lines, first_line)
45
68
  else
46
- check_source(:empty_lines, first_line, last_line)
69
+ check_beginning(:no_empty_lines, first_line)
70
+ check_deferred_empty_line(body)
47
71
  end
48
- else
49
- check_source(style, first_line, last_line)
72
+ check_ending(:empty_lines, last_line)
50
73
  end
51
74
  end
52
75
 
53
- def check_source(style, start_line, end_line)
76
+ def check_both(style, first_line, last_line)
77
+ check_beginning(style, first_line)
78
+ check_ending(style, last_line)
79
+ end
80
+
81
+ def check_beginning(style, first_line)
82
+ check_source(style, first_line, 'beginning')
83
+ end
84
+
85
+ def check_ending(style, last_line)
86
+ check_source(style, last_line - 2, 'end')
87
+ end
88
+
89
+ def check_source(style, line_no, desc)
54
90
  case style
55
91
  when :no_empty_lines
56
- check_both(style, start_line, end_line, MSG_EXTRA, &:empty?)
92
+ check_line(style, line_no, message(MSG_EXTRA, desc), &:empty?)
57
93
  when :empty_lines
58
- check_both(style, start_line, end_line, MSG_MISSING) do |line|
94
+ check_line(style, line_no, message(MSG_MISSING, desc)) do |line|
59
95
  !line.empty?
60
96
  end
61
97
  end
62
98
  end
63
99
 
64
- def check_both(style, start_line, end_line, msg, &block)
65
- kind = self.class::KIND
66
- check_line(style, start_line, format(msg, kind, 'beginning'), &block)
67
- check_line(style, end_line - 2, format(msg, kind, 'end'), &block)
68
- end
69
-
70
100
  def check_line(style, line, msg)
71
101
  return unless yield(processed_source.lines[line])
72
102
 
@@ -75,13 +105,55 @@ module RuboCop
75
105
  add_offense([style, range], range, msg)
76
106
  end
77
107
 
78
- def namespace?(node, with_one_child: true)
79
- if node.begin_type?
108
+ def check_deferred_empty_line(body)
109
+ node = first_empty_line_required_child(body)
110
+ return unless node
111
+
112
+ line = previous_line_ignoring_comments(node.loc.first_line)
113
+ return if processed_source[line].empty?
114
+
115
+ range = source_range(processed_source.buffer, line + 2, 0)
116
+ add_offense([:empty_lines, range], range, deferred_message(node))
117
+ end
118
+
119
+ def namespace?(body, with_one_child: false)
120
+ if body.begin_type?
80
121
  return false if with_one_child
81
- node.children.all? { |child| constant_definition?(child) }
122
+ body.children.all? { |child| constant_definition?(child) }
82
123
  else
83
- constant_definition?(node)
124
+ constant_definition?(body)
125
+ end
126
+ end
127
+
128
+ def first_child_requires_empty_line?(body)
129
+ if body.begin_type?
130
+ empty_line_required?(body.children.first)
131
+ else
132
+ empty_line_required?(body)
133
+ end
134
+ end
135
+
136
+ def first_empty_line_required_child(body)
137
+ if body.begin_type?
138
+ body.children.find { |child| empty_line_required?(child) }
139
+ elsif empty_line_required?(body)
140
+ body
141
+ end
142
+ end
143
+
144
+ def previous_line_ignoring_comments(send_line)
145
+ (send_line - 2).downto(0) do |line|
146
+ return line unless comment_line?(processed_source[line])
84
147
  end
148
+ 0
149
+ end
150
+
151
+ def message(type, desc)
152
+ format(type, self.class::KIND, desc)
153
+ end
154
+
155
+ def deferred_message(node)
156
+ format(MSG_DEFERRED, node.type)
85
157
  end
86
158
  end
87
159
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where `sort { |a, b| a.foo <=> b.foo }`
7
+ # can be replaced by `sort_by(&:foo)`.
8
+ # This cop also checks `max` and `min` methods.
9
+ #
10
+ # @example
11
+ # @bad
12
+ # array.sort { |a, b| a.foo <=> b.foo }
13
+ # array.max { |a, b| a.foo <=> b.foo }
14
+ # array.min { |a, b| a.foo <=> b.foo }
15
+ #
16
+ # @good
17
+ # array.sort_by(&:foo)
18
+ # array.sort_by { |v| v.foo }
19
+ # array.sort_by do |var|
20
+ # var.foo
21
+ # end
22
+ # array.max_by(&:foo)
23
+ # array.min_by(&:foo)
24
+ class CompareWithBlock < Cop
25
+ MSG = 'Use `%s_by(&:%s)` instead of ' \
26
+ '`%s { |%s, %s| %s.%s <=> %s.%s }`.'.freeze
27
+
28
+ def_node_matcher :compare?, <<-END
29
+ (block $(send _ {:sort :min :max}) (args (arg $_a) (arg $_b)) (send (send (lvar _a) $_m) :<=> (send (lvar _b) $_m)))
30
+ END
31
+
32
+ def on_block(node)
33
+ compare?(node) do |send, var_a, var_b, method|
34
+ range = compare_range(send, node)
35
+ compare_method = send.method_name
36
+ add_offense(node, range,
37
+ format(MSG, compare_method, method,
38
+ compare_method, var_a, var_b,
39
+ var_a, method, var_b, method))
40
+ end
41
+ end
42
+
43
+ def autocorrect(node)
44
+ send, = *node
45
+
46
+ lambda do |corrector|
47
+ method = node.children.last.children.last.children.last
48
+ corrector.replace(compare_range(send, node),
49
+ "#{send.method_name}_by(&:#{method})")
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def compare_range(send, node)
56
+ range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -42,32 +42,32 @@ module RuboCop
42
42
 
43
43
  MSG = 'Use `count` instead of `%s...%s`.'.freeze
44
44
 
45
- SELECTORS = [:reject, :select].freeze
46
- COUNTERS = [:count, :length, :size].freeze
45
+ def_node_matcher :count_candidate?, <<-PATTERN
46
+ {
47
+ (send (block $(send _ ${:select :reject}) ...) ${:count :length :size})
48
+ (send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size})
49
+ }
50
+ PATTERN
47
51
 
48
52
  def on_send(node)
49
53
  return if rails_safe_mode?
50
54
 
51
- @selector, @selector_loc, @params, @counter = parse(node)
55
+ count_candidate?(node) do |selector_node, selector, counter|
56
+ return unless eligible_node?(node)
52
57
 
53
- check(node)
58
+ range = source_starting_at(node) do
59
+ selector_node.loc.selector.begin_pos
60
+ end
61
+
62
+ add_offense(node, range, format(MSG, selector, counter))
63
+ end
54
64
  end
55
65
 
56
66
  private
57
67
 
58
- attr_reader :selector, :selector_loc, :params, :counter
59
-
60
- def check(node)
61
- return unless eligible_node?(node) && eligible_params? &&
62
- eligible_method_chain?
63
-
64
- range = source_starting_at(node) { @selector_loc.begin_pos }
65
-
66
- add_offense(node, range, format(MSG, @selector, @counter))
67
- end
68
-
69
68
  def autocorrect(node)
70
- selector, selector_loc = parse(node)
69
+ selector_node, selector, _counter = count_candidate?(node)
70
+ selector_loc = selector_node.loc.selector
71
71
 
72
72
  return if selector == :reject
73
73
 
@@ -83,48 +83,12 @@ module RuboCop
83
83
  !(node.parent && node.parent.block_type?)
84
84
  end
85
85
 
86
- def eligible_params?
87
- !(params && !params.block_pass_type?)
88
- end
89
-
90
- def eligible_method_chain?
91
- COUNTERS.include?(counter) && SELECTORS.include?(selector)
92
- end
93
-
94
- def parse(node)
95
- head, counter = *node
96
- expression, selector, params = *head
97
- if selector.is_a?(Symbol)
98
- selector_loc = selector_location(expression, head.loc)
99
- else
100
- _, selector, params = *expression
101
- if contains_selector?(expression)
102
- selector_loc = expression.loc.selector
103
- end
104
- end
105
-
106
- [selector, selector_loc, params, counter]
107
- end
108
-
109
- def selector_location(expression, head_loc)
110
- if expression && expression.parent.loc.respond_to?(:selector)
111
- expression.parent.loc.selector
112
- elsif head_loc.respond_to?(:selector)
113
- head_loc.selector
114
- end
115
- end
116
-
117
- def contains_selector?(node)
118
- node.respond_to?(:loc) && node.loc.respond_to?(:selector)
119
- end
120
-
121
86
  def source_starting_at(node)
122
- begin_pos =
123
- if block_given?
124
- yield node
125
- else
126
- node.source_range.begin_pos
127
- end
87
+ begin_pos = if block_given?
88
+ yield node
89
+ else
90
+ node.source_range.begin_pos
91
+ end
128
92
 
129
93
  range_between(begin_pos, node.source_range.end_pos)
130
94
  end