rubocop 0.11.1 → 0.12.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -1
  3. data/README.md +28 -3
  4. data/config/default.yml +14 -12
  5. data/config/disabled.yml +1 -1
  6. data/config/enabled.yml +190 -118
  7. data/lib/rubocop.rb +9 -1
  8. data/lib/rubocop/cli.rb +49 -13
  9. data/lib/rubocop/config.rb +5 -5
  10. data/lib/rubocop/cop/cop.rb +34 -1
  11. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +47 -0
  12. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  13. data/lib/rubocop/cop/lint/useless_assignment.rb +39 -11
  14. data/lib/rubocop/cop/lint/useless_comparison.rb +2 -4
  15. data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +20 -0
  16. data/lib/rubocop/cop/rails/read_attribute.rb +28 -0
  17. data/lib/rubocop/cop/style/access_control.rb +12 -1
  18. data/lib/rubocop/cop/style/attr.rb +7 -0
  19. data/lib/rubocop/cop/style/collection_methods.rb +13 -1
  20. data/lib/rubocop/cop/style/constant_name.rb +1 -1
  21. data/lib/rubocop/cop/style/def_parentheses.rb +18 -0
  22. data/lib/rubocop/cop/style/documentation.rb +1 -1
  23. data/lib/rubocop/cop/style/empty_literal.rb +14 -0
  24. data/lib/rubocop/cop/style/even_odd.rb +56 -0
  25. data/lib/rubocop/cop/style/favor_modifier.rb +2 -2
  26. data/lib/rubocop/cop/style/hash_methods.rb +40 -0
  27. data/lib/rubocop/cop/style/indentation_width.rb +148 -0
  28. data/lib/rubocop/cop/style/method_and_variable_snake_case.rb +40 -25
  29. data/lib/rubocop/cop/style/method_call_parentheses.rb +8 -0
  30. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  31. data/lib/rubocop/cop/style/nil_comparison.rb +38 -0
  32. data/lib/rubocop/cop/style/signal_exception.rb +11 -0
  33. data/lib/rubocop/cop/style/space_after_method_name.rb +34 -0
  34. data/lib/rubocop/cop/util.rb +17 -0
  35. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -2
  36. data/lib/rubocop/formatter/file_list_formatter.rb +3 -2
  37. data/lib/rubocop/formatter/formatter_set.rb +3 -11
  38. data/lib/rubocop/formatter/offence_count_formatter.rb +50 -0
  39. data/lib/rubocop/formatter/progress_formatter.rb +0 -2
  40. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -6
  41. data/lib/rubocop/version.rb +1 -1
  42. data/spec/project_spec.rb +7 -0
  43. data/spec/rubocop/cli_spec.rb +119 -57
  44. data/spec/rubocop/config_spec.rb +23 -17
  45. data/spec/rubocop/cop/commissioner_spec.rb +8 -8
  46. data/spec/rubocop/cop/cop_spec.rb +80 -0
  47. data/spec/rubocop/cop/lint/parentheses_as_grouped_expression_spec.rb +63 -0
  48. data/spec/rubocop/cop/lint/useless_assignment_spec.rb +59 -0
  49. data/spec/rubocop/cop/rails/has_and_belongs_to_many_spec.rb +19 -0
  50. data/spec/rubocop/cop/rails/read_attribute_spec.rb +19 -0
  51. data/spec/rubocop/cop/rails/validation_spec.rb +5 -5
  52. data/spec/rubocop/cop/style/access_control_spec.rb +28 -0
  53. data/spec/rubocop/cop/style/attr_spec.rb +6 -1
  54. data/spec/rubocop/cop/style/collection_methods_spec.rb +5 -0
  55. data/spec/rubocop/cop/style/constant_name_spec.rb +9 -0
  56. data/spec/rubocop/cop/style/def_with_parentheses_spec.rb +14 -9
  57. data/spec/rubocop/cop/style/def_without_parentheses_spec.rb +12 -7
  58. data/spec/rubocop/cop/style/empty_literal_spec.rb +42 -27
  59. data/spec/rubocop/cop/style/even_odd_spec.rb +47 -0
  60. data/spec/rubocop/cop/style/favor_modifier_spec.rb +15 -14
  61. data/spec/rubocop/cop/style/hash_methods_spec.rb +51 -0
  62. data/spec/rubocop/cop/style/indentation_width_spec.rb +390 -0
  63. data/spec/rubocop/cop/style/method_and_variable_snake_case_spec.rb +58 -50
  64. data/spec/rubocop/cop/style/method_call_parentheses_spec.rb +6 -1
  65. data/spec/rubocop/cop/style/nil_comparison_spec.rb +31 -0
  66. data/spec/rubocop/cop/style/signal_exception_spec.rb +28 -0
  67. data/spec/rubocop/cop/style/space_after_method_name_spec.rb +61 -0
  68. data/spec/rubocop/formatter/emacs_style_formatter_spec.rb +9 -2
  69. data/spec/rubocop/formatter/file_list_formatter_spec.rb +3 -3
  70. data/spec/rubocop/formatter/offence_count_formatter_spec.rb +52 -0
  71. data/spec/rubocop/formatter/progress_formatter_spec.rb +70 -84
  72. data/spec/rubocop/source_parser_spec.rb +1 -1
  73. metadata +29 -5
  74. data/lib/rubocop/cop/style/line_continuation.rb +0 -27
  75. data/spec/rubocop/cop/style/line_continuation_spec.rb +0 -26
data/lib/rubocop.rb CHANGED
@@ -24,6 +24,7 @@ require 'rubocop/cop/lint/eval'
24
24
  require 'rubocop/cop/lint/handle_exceptions'
25
25
  require 'rubocop/cop/lint/literal_in_condition'
26
26
  require 'rubocop/cop/lint/loop'
27
+ require 'rubocop/cop/lint/parentheses_as_grouped_expression'
27
28
  require 'rubocop/cop/lint/rescue_exception'
28
29
  require 'rubocop/cop/lint/shadowing_outer_local_variable'
29
30
  require 'rubocop/cop/lint/unreachable_code'
@@ -66,23 +67,26 @@ require 'rubocop/cop/style/empty_literal'
66
67
  require 'rubocop/cop/style/encoding'
67
68
  require 'rubocop/cop/style/end_block'
68
69
  require 'rubocop/cop/style/end_of_line'
70
+ require 'rubocop/cop/style/even_odd'
69
71
  require 'rubocop/cop/style/favor_join'
70
72
  require 'rubocop/cop/style/favor_modifier'
71
73
  require 'rubocop/cop/style/favor_sprintf'
72
74
  require 'rubocop/cop/style/favor_unless_over_negated_if'
75
+ require 'rubocop/cop/style/hash_methods'
73
76
  require 'rubocop/cop/style/hash_syntax'
74
77
  require 'rubocop/cop/style/if_then_else'
75
78
  require 'rubocop/cop/style/if_with_semicolon'
79
+ require 'rubocop/cop/style/indentation_width'
76
80
  require 'rubocop/cop/style/multiline_if_then'
77
81
  require 'rubocop/cop/style/one_line_conditional'
78
82
  require 'rubocop/cop/style/lambda'
79
83
  require 'rubocop/cop/style/leading_comment_space'
80
- require 'rubocop/cop/style/line_continuation'
81
84
  require 'rubocop/cop/style/line_length'
82
85
  require 'rubocop/cop/style/method_and_variable_snake_case'
83
86
  require 'rubocop/cop/style/method_call_parentheses'
84
87
  require 'rubocop/cop/style/method_length'
85
88
  require 'rubocop/cop/style/module_function'
89
+ require 'rubocop/cop/style/nil_comparison'
86
90
  require 'rubocop/cop/style/not'
87
91
  require 'rubocop/cop/style/numeric_literals'
88
92
  require 'rubocop/cop/style/op_method'
@@ -100,6 +104,7 @@ require 'rubocop/cop/style/signal_exception'
100
104
  require 'rubocop/cop/style/single_line_methods'
101
105
  require 'rubocop/cop/style/space_after_comma_etc'
102
106
  require 'rubocop/cop/style/space_after_control_keyword'
107
+ require 'rubocop/cop/style/space_after_method_name'
103
108
  require 'rubocop/cop/style/string_literals'
104
109
  require 'rubocop/cop/style/surrounding_space'
105
110
  require 'rubocop/cop/style/symbol_array'
@@ -114,6 +119,8 @@ require 'rubocop/cop/style/when_then'
114
119
  require 'rubocop/cop/style/while_until_do'
115
120
  require 'rubocop/cop/style/word_array'
116
121
 
122
+ require 'rubocop/cop/rails/has_and_belongs_to_many'
123
+ require 'rubocop/cop/rails/read_attribute'
117
124
  require 'rubocop/cop/rails/validation'
118
125
 
119
126
  require 'rubocop/formatter/base_formatter'
@@ -124,6 +131,7 @@ require 'rubocop/formatter/clang_style_formatter'
124
131
  require 'rubocop/formatter/progress_formatter'
125
132
  require 'rubocop/formatter/json_formatter'
126
133
  require 'rubocop/formatter/file_list_formatter'
134
+ require 'rubocop/formatter/offence_count_formatter'
127
135
  require 'rubocop/formatter/formatter_set'
128
136
 
129
137
  require 'rubocop/config'
data/lib/rubocop/cli.rb CHANGED
@@ -62,7 +62,7 @@ module Rubocop
62
62
  formatter_set.finished(inspected_files.freeze)
63
63
  formatter_set.close_output_files
64
64
 
65
- display_error_summary(@errors) unless @options[:silent]
65
+ display_error_summary(@errors)
66
66
 
67
67
  !any_failed && !wants_to_quit ? 0 : 1
68
68
  rescue => e
@@ -83,6 +83,27 @@ module Rubocop
83
83
  end
84
84
  end
85
85
 
86
+ def print_available_cops
87
+ puts "Available cops (#{@cops.length}) + config for #{Dir.pwd.to_s}: "
88
+ dirconf = @config_store.for(Dir.pwd.to_s)
89
+ @cops.types.sort!.each do |type|
90
+ coptypes = @cops.with_type(type).sort_by!(&:cop_name)
91
+ puts "Type '#{type.to_s.capitalize}' (#{coptypes.size}):"
92
+ coptypes.each do |cop|
93
+ name = cop.cop_name
94
+ puts " - #{name}"
95
+ cnf = dirconf.for_cop(name).dup
96
+ print_conf_option('Description',
97
+ cnf.delete('Description') { 'None' })
98
+ cnf.each { |k, v| print_conf_option(k, v) }
99
+ end
100
+ end
101
+ end
102
+
103
+ def print_conf_option(option, value)
104
+ puts " - #{option}: #{value}"
105
+ end
106
+
86
107
  def inspect_file(file)
87
108
  begin
88
109
  processed_source = SourceParser.parse_file(file)
@@ -160,7 +181,8 @@ module Rubocop
160
181
 
161
182
  # rubocop:disable MethodLength
162
183
  def parse_options(args)
163
- convert_deprecated_options!(args)
184
+ ignore_dropped_options(args)
185
+ convert_deprecated_options(args)
164
186
 
165
187
  OptionParser.new do |opts|
166
188
  opts.banner = 'Usage: rubocop [options] [file1, file2, ...]'
@@ -186,6 +208,12 @@ module Rubocop
186
208
  ]
187
209
  validate_auto_gen_config_option(args)
188
210
  end
211
+ opts.on('--show-cops',
212
+ 'Shows cops and their config for the',
213
+ 'current directory.') do
214
+ print_available_cops
215
+ exit(0)
216
+ end
189
217
  opts.on('-f', '--format FORMATTER',
190
218
  'Choose an output formatter. This option',
191
219
  'can be specified multiple times to enable',
@@ -196,6 +224,7 @@ module Rubocop
196
224
  ' [e]macs',
197
225
  ' [j]son',
198
226
  ' [f]iles',
227
+ ' [o]ffences',
199
228
  ' custom formatter class name') do |key|
200
229
  @options[:formatters] ||= []
201
230
  @options[:formatters] << [key]
@@ -220,9 +249,6 @@ module Rubocop
220
249
  opts.on('-a', '--auto-correct', 'Auto-correct offences.') do |a|
221
250
  @options[:autocorrect] = a
222
251
  end
223
- opts.on('-s', '--silent', 'Silence summary.') do |s|
224
- @options[:silent] = s
225
- end
226
252
  opts.on('-n', '--no-color', 'Disable color output.') do |s|
227
253
  Sickill::Rainbow.enabled = false
228
254
  end
@@ -238,7 +264,17 @@ module Rubocop
238
264
  end
239
265
  # rubocop:enable MethodLength
240
266
 
241
- def convert_deprecated_options!(args)
267
+ def ignore_dropped_options(args)
268
+ # Currently we don't make -s/--silent option raise error
269
+ # since those are mostly used by external tools.
270
+ rejected = args.reject! { |a| %w(-s --silent).include?(a) }
271
+ if rejected
272
+ warn '-s/--silent options is dropped. ' +
273
+ '`emacs` and `files` formatters no longer display summary.'
274
+ end
275
+ end
276
+
277
+ def convert_deprecated_options(args)
242
278
  args.map! do |arg|
243
279
  case arg
244
280
  when '-e', '--emacs'
@@ -262,12 +298,12 @@ module Rubocop
262
298
  def display_error_summary(errors)
263
299
  return if errors.empty?
264
300
  plural = errors.count > 1 ? 's' : ''
265
- puts "\n#{errors.count} error#{plural} occurred:".color(:red)
266
- errors.each { |error| puts error }
267
- puts 'Errors are usually caused by RuboCop bugs.'
268
- puts 'Please, report your problems to RuboCop\'s issue tracker.'
269
- puts 'Mention the following information in the issue report:'
270
- puts Rubocop::Version.version(true)
301
+ warn "\n#{errors.count} error#{plural} occurred:".color(:red)
302
+ errors.each { |error| warn error }
303
+ warn 'Errors are usually caused by RuboCop bugs.'
304
+ warn 'Please, report your problems to RuboCop\'s issue tracker.'
305
+ warn 'Mention the following information in the issue report:'
306
+ warn Rubocop::Version.version(true)
271
307
  end
272
308
 
273
309
  def autocorrect(buffer, cops)
@@ -295,7 +331,7 @@ module Rubocop
295
331
 
296
332
  def formatter_set
297
333
  @formatter_set ||= begin
298
- set = Formatter::FormatterSet.new(!@options[:silent])
334
+ set = Formatter::FormatterSet.new
299
335
  pairs = @options[:formatters] || [[DEFAULT_FORMATTER]]
300
336
  pairs.each do |formatter_key, output_path|
301
337
  set.add_formatter(formatter_key, output_path)
@@ -37,7 +37,7 @@ module Rubocop
37
37
  end
38
38
  base_config.each do |key, value|
39
39
  if value.is_a?(Hash)
40
- hash[key] = hash.has_key?(key) ? merge(value, hash[key]) : value
40
+ hash[key] = hash.key?(key) ? merge(value, hash[key]) : value
41
41
  end
42
42
  end
43
43
  if base_config.loaded_path.include?(AUTO_GENERATED_FILE)
@@ -75,7 +75,7 @@ module Rubocop
75
75
  def merge(base_hash, derived_hash)
76
76
  result = {}
77
77
  base_hash.each do |key, value|
78
- result[key] = if derived_hash.has_key?(key)
78
+ result[key] = if derived_hash.key?(key)
79
79
  if value.is_a?(Hash)
80
80
  value.merge(derived_hash[key])
81
81
  else
@@ -86,7 +86,7 @@ module Rubocop
86
86
  end
87
87
  end
88
88
  derived_hash.each do |key, value|
89
- result[key] = value unless base_hash.has_key?(key)
89
+ result[key] = value unless base_hash.key?(key)
90
90
  end
91
91
  result
92
92
  end
@@ -200,7 +200,7 @@ module Rubocop
200
200
  default_config = self.class.default_configuration
201
201
 
202
202
  valid_cop_names, invalid_cop_names = @hash.keys.partition do |key|
203
- default_config.has_key?(key)
203
+ default_config.key?(key)
204
204
  end
205
205
 
206
206
  invalid_cop_names.each do |name|
@@ -210,7 +210,7 @@ module Rubocop
210
210
 
211
211
  valid_cop_names.each do |name|
212
212
  @hash[name].each_key do |param|
213
- unless default_config[name].has_key?(param)
213
+ unless default_config[name].key?(param)
214
214
  fail ValidationError,
215
215
  "unrecognized parameter #{name}:#{param} found " +
216
216
  "in #{loaded_path || self}"
@@ -2,6 +2,21 @@
2
2
 
3
3
  module Rubocop
4
4
  module Cop
5
+ # Store for all cops with helper functions
6
+ class CopStore < ::Array
7
+
8
+ # @return [Array<String>] list of types for current cops.
9
+ def types
10
+ @types = map(&:cop_type).uniq! unless defined? @types
11
+ @types
12
+ end
13
+
14
+ # @return [Array<Cop>] Cops for that specific type.
15
+ def with_type(type)
16
+ select { |c| c.cop_type == type }
17
+ end
18
+ end
19
+
5
20
  # A scaffold for concrete cops.
6
21
  #
7
22
  # The Cop class is meant to be extended.
@@ -24,13 +39,20 @@ module Rubocop
24
39
  class Cop
25
40
  extend AST::Sexp
26
41
 
42
+ # http://phrogz.net/programmingruby/language.html#table_18.4
43
+ # Backtick is added last just to help editors parse this code.
44
+ OPERATOR_METHODS = %w(
45
+ | ^ & <=> == === =~ > >= < <= << >>
46
+ + - * / % ** ~ +@ -@ [] []= ! != !~
47
+ ).map(&:to_sym) + [:'`']
48
+
27
49
  attr_accessor :offences
28
50
  attr_accessor :debug
29
51
  attr_accessor :autocorrect
30
52
  attr_writer :disabled_lines
31
53
  attr_reader :corrections
32
54
 
33
- @all = []
55
+ @all = CopStore.new
34
56
  @config = {}
35
57
 
36
58
  class << self
@@ -61,6 +83,17 @@ module Rubocop
61
83
  cop_type == :lint
62
84
  end
63
85
 
86
+ # Extracts the first line out of the description
87
+ def self.short_description
88
+ desc = full_description
89
+ desc ? desc.lines.first.strip : ''
90
+ end
91
+
92
+ # Gets the full description of the cop or nil if no description is set.
93
+ def self.full_description
94
+ config['Description']
95
+ end
96
+
64
97
  def self.rails?
65
98
  cop_type == :rails
66
99
  end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Lint
6
+ # Checks for space between a the name of a called method and a left
7
+ # parenthesis.
8
+ #
9
+ # @example
10
+ #
11
+ # puts (x + y)
12
+ class ParenthesesAsGroupedExpression < Cop
13
+ MSG = '(...) interpreted as grouped expression.'
14
+
15
+ def on_send(node)
16
+ receiver, method_name, args = *node
17
+ if OPERATOR_METHODS.include?(method_name) ||
18
+ method_name.to_s.end_with?('=')
19
+ return
20
+ end
21
+ if args && args.loc.expression.source.start_with?('(')
22
+ receiver_length = if receiver
23
+ receiver.loc.expression.source.length
24
+ else
25
+ 0
26
+ end
27
+ without_receiver = node.loc.expression.source[receiver_length..-1]
28
+
29
+ # Escape question mark if any.
30
+ method_regexp = Regexp.escape(method_name)
31
+
32
+ if (match =
33
+ without_receiver.match(/^\s*\.?\s*#{method_regexp}(\s+)\(/))
34
+ expr = args.loc.expression
35
+ space_length = match.captures[0].length
36
+ space_range =
37
+ Parser::Source::Range.new(expr.source_buffer,
38
+ expr.begin_pos - space_length,
39
+ expr.begin_pos)
40
+ add_offence(:warning, space_range, MSG)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -16,10 +16,7 @@ module Rubocop
16
16
  end
17
17
 
18
18
  def targets_exception?(rescue_arg_node)
19
- return false unless rescue_arg_node.type == :const
20
- namespace, klass_name = *rescue_arg_node
21
- return false unless namespace.nil? || namespace.type == :cbase
22
- klass_name == :Exception
19
+ Util.const_name(rescue_arg_node) == 'Exception'
23
20
  end
24
21
  end
25
22
  end
@@ -20,20 +20,20 @@ module Rubocop
20
20
  MSG = 'Useless assignment to local variable %s.'
21
21
 
22
22
  def on_def(node)
23
- _name, _args, body = *node
23
+ _name, args, body = *node
24
24
 
25
- check_for_useless_assignment(body)
25
+ check_for_useless_assignment(body, args)
26
26
  end
27
27
 
28
28
  def on_defs(node)
29
- _target, _name, _args, body = *node
29
+ _target, _name, args, body = *node
30
30
 
31
- check_for_useless_assignment(body)
31
+ check_for_useless_assignment(body, args)
32
32
  end
33
33
 
34
34
  private
35
35
 
36
- def check_for_useless_assignment(body)
36
+ def check_for_useless_assignment(body, args)
37
37
  return unless body
38
38
 
39
39
  if body.type == :begin
@@ -43,19 +43,47 @@ module Rubocop
43
43
  end
44
44
 
45
45
  last_expr = expression.is_a?(Array) ? expression.last : expression
46
+ return unless last_expr
46
47
 
47
- if last_expr && last_expr.type == :lvasgn
48
+ case last_expr.type
49
+ when :lvasgn
48
50
  var_name, = *last_expr
49
51
  add_offence(:warning, last_expr.loc.name, MSG.format(var_name))
50
- elsif last_expr && last_expr.type == :send
52
+ when :send
51
53
  receiver, method, _args = *last_expr
54
+ return unless receiver
55
+ return unless receiver.type == :lvar
56
+ return unless method =~ /\w=$/
52
57
 
53
- if receiver && receiver.type == :lvar && method =~ /\w=$/
54
- add_offence(:warning,
55
- receiver.loc.name,
56
- MSG.format(receiver.loc.name.source))
58
+ var_name, = *receiver
59
+ return if contains_object_passed_as_argument?(var_name, body, args)
60
+
61
+ add_offence(:warning,
62
+ receiver.loc.name,
63
+ MSG.format(receiver.loc.name.source))
64
+ end
65
+ end
66
+
67
+ def contains_object_passed_as_argument?(lvar_name, body, args)
68
+ variable_table = {}
69
+
70
+ args.children.each do |arg_node|
71
+ arg_name, = *arg_node
72
+ variable_table[arg_name] = true
73
+ end
74
+
75
+ on_node([:lvasgn, :ivasgn, :cvasgn, :gvasgn], body) do |asgn_node|
76
+ lhs_var_name, rhs_node = *asgn_node
77
+
78
+ if [:lvar, :ivar, :cvar, :gvar].include?(rhs_node.type)
79
+ rhs_var_name, = *rhs_node
80
+ variable_table[lhs_var_name] = variable_table[rhs_var_name]
81
+ else
82
+ variable_table[lhs_var_name] = false
57
83
  end
58
84
  end
85
+
86
+ variable_table[lvar_name]
59
87
  end
60
88
  end
61
89
  end
@@ -17,11 +17,9 @@ module Rubocop
17
17
  op = node.loc.selector.source
18
18
 
19
19
  if OPS.include?(op)
20
- receiver, _method, selector = *node
20
+ receiver, _method, args = *node
21
21
 
22
- if receiver == selector
23
- add_offence(:warning, node.loc.selector, MSG)
24
- end
22
+ add_offence(:warning, node.loc.selector, MSG) if receiver == args
25
23
  end
26
24
  end
27
25
  end
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+
3
+ module Rubocop
4
+ module Cop
5
+ module Rails
6
+ # This cop checks for the use of the has_and_belongs_to_many macro.
7
+ class HasAndBelongsToMany < Cop
8
+ MSG = 'Prefer has_many :through to has_and_belongs_to_many.'
9
+
10
+ def on_send(node)
11
+ receiver, method_name, *_args = *node
12
+
13
+ if receiver.nil? && method_name == :has_and_belongs_to_many
14
+ add_offence(:convention, node.loc.selector, MSG)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end