rubocop 1.7.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -3
  4. data/config/default.yml +137 -31
  5. data/config/obsoletion.yml +4 -0
  6. data/lib/rubocop.rb +14 -1
  7. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  8. data/lib/rubocop/comment_config.rb +6 -6
  9. data/lib/rubocop/config.rb +5 -2
  10. data/lib/rubocop/config_loader.rb +7 -14
  11. data/lib/rubocop/config_store.rb +12 -1
  12. data/lib/rubocop/cop/base.rb +2 -1
  13. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  14. data/lib/rubocop/cop/gemspec/date_assignment.rb +56 -0
  15. data/lib/rubocop/cop/generator.rb +1 -3
  16. data/lib/rubocop/cop/internal_affairs.rb +5 -1
  17. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  18. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  19. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  21. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  22. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +38 -18
  23. data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
  24. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  25. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +26 -0
  26. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  27. data/lib/rubocop/cop/layout/space_before_brackets.rb +19 -16
  28. data/lib/rubocop/cop/lint/debugger.rb +58 -14
  29. data/lib/rubocop/cop/lint/deprecated_constants.rb +80 -0
  30. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +13 -4
  31. data/lib/rubocop/cop/lint/duplicate_require.rb +2 -2
  32. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  33. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  34. data/lib/rubocop/cop/lint/multiple_comparison.rb +4 -4
  35. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  36. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  37. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  38. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  39. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  40. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  41. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +5 -3
  42. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  43. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  44. data/lib/rubocop/cop/message_annotator.rb +4 -1
  45. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  46. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  47. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  49. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  50. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  51. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  52. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  53. data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
  54. data/lib/rubocop/cop/mixin/preferred_delimiters.rb +2 -2
  55. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  56. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  57. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  58. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  59. data/lib/rubocop/cop/registry.rb +1 -1
  60. data/lib/rubocop/cop/severity.rb +3 -3
  61. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  62. data/lib/rubocop/cop/style/constant_visibility.rb +27 -0
  63. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  64. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  65. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  66. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  67. data/lib/rubocop/cop/style/eval_with_location.rb +138 -49
  68. data/lib/rubocop/cop/style/explicit_block_argument.rb +11 -1
  69. data/lib/rubocop/cop/style/exponential_notation.rb +6 -7
  70. data/lib/rubocop/cop/style/float_division.rb +3 -0
  71. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  72. data/lib/rubocop/cop/style/hash_conversion.rb +81 -0
  73. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  74. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  75. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +4 -0
  77. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  78. data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
  79. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  80. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  81. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  82. data/lib/rubocop/cop/style/raise_args.rb +3 -2
  83. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  84. data/lib/rubocop/cop/style/single_line_methods.rb +32 -2
  85. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  86. data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
  87. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  88. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  89. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  90. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  91. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  92. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  93. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  94. data/lib/rubocop/magic_comment.rb +30 -1
  95. data/lib/rubocop/options.rb +1 -1
  96. data/lib/rubocop/rspec/expect_offense.rb +5 -2
  97. data/lib/rubocop/runner.rb +1 -0
  98. data/lib/rubocop/target_ruby.rb +47 -11
  99. data/lib/rubocop/version.rb +2 -2
  100. metadata +24 -7
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # This cop enforces the use of explicit block argument to avoid writing
7
7
  # block literal that just passes its arguments to another block.
8
8
  #
9
+ # NOTE: This cop only registers an offense if the block args match the
10
+ # yield args exactly.
11
+ #
9
12
  # @example
10
13
  # # bad
11
14
  # def with_tmp_dir
@@ -75,7 +78,14 @@ module RuboCop
75
78
  private
76
79
 
77
80
  def yielding_arguments?(block_args, yield_args)
81
+ yield_args = yield_args.dup.fill(
82
+ nil,
83
+ yield_args.length, block_args.length - yield_args.length
84
+ )
85
+
78
86
  yield_args.zip(block_args).all? do |yield_arg, block_arg|
87
+ next false unless yield_arg && block_arg
88
+
79
89
  block_arg && yield_arg.children.first == block_arg.children.first
80
90
  end
81
91
  end
@@ -87,7 +97,7 @@ module RuboCop
87
97
  replacement = ' &block'
88
98
  replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
89
99
  corrector.insert_after(arg_range, replacement) unless last_arg.blockarg_type?
90
- elsif node.call_type?
100
+ elsif node.call_type? || node.zsuper_type?
91
101
  corrector.insert_after(node, '(&block)')
92
102
  else
93
103
  corrector.insert_after(node.loc.name, '(&block)')
@@ -5,13 +5,12 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces consistency when using exponential notation
7
7
  # for numbers in the code (eg 1.2e4). Different styles are supported:
8
- # * `scientific` which enforces a mantissa between 1 (inclusive)
9
- # and 10 (exclusive).
10
- # * `engineering` which enforces the exponent to be a multiple of 3
11
- # and the mantissa to be between 0.1 (inclusive)
12
- # and 10 (exclusive).
13
- # * `integral` which enforces the mantissa to always be a whole number
14
- # without trailing zeroes.
8
+ #
9
+ # * `scientific` which enforces a mantissa between 1 (inclusive) and 10 (exclusive).
10
+ # * `engineering` which enforces the exponent to be a multiple of 3 and the mantissa
11
+ # to be between 0.1 (inclusive) and 10 (exclusive).
12
+ # * `integral` which enforces the mantissa to always be a whole number without
13
+ # trailing zeroes.
15
14
  #
16
15
  # @example EnforcedStyle: scientific (default)
17
16
  # # Enforces a mantissa between 1 (inclusive) and 10 (exclusive).
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # It is recommended to either always use `fdiv` or coerce one side only.
8
8
  # This cop also provides other options for code consistency.
9
9
  #
10
+ # This cop is marked as unsafe, because if operand variable is a string object
11
+ # then `.to_f` will be removed and an error will occur.
12
+ #
10
13
  # @example EnforcedStyle: single_coerce (default)
11
14
  # # bad
12
15
  # a.to_f / b.to_f
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # The reason is that _unannotated_ format is very similar
12
12
  # to encoded URLs or Date/Time formatting strings.
13
13
  #
14
+ # This cop can be customized ignored methods with `IgnoredMethods`.
15
+ #
14
16
  # @example EnforcedStyle: annotated (default)
15
17
  #
16
18
  # # bad
@@ -58,12 +60,18 @@ module RuboCop
58
60
  #
59
61
  # # good
60
62
  # format('%06d', 10)
63
+ #
64
+ # @example IgnoredMethods: [redirect]
65
+ #
66
+ # # good
67
+ # redirect('foo/%{bar_id}')
68
+ #
61
69
  class FormatStringToken < Base
62
70
  include ConfigurableEnforcedStyle
71
+ include IgnoredMethods
63
72
 
64
73
  def on_str(node)
65
- return unless node.value.include?('%')
66
- return if node.each_ancestor(:xstr, :regexp).any?
74
+ return if format_string_token?(node) || use_ignored_method?(node)
67
75
 
68
76
  detections = collect_detections(node)
69
77
  return if detections.empty?
@@ -88,6 +96,14 @@ module RuboCop
88
96
  }
89
97
  PATTERN
90
98
 
99
+ def format_string_token?(node)
100
+ !node.value.include?('%') || node.each_ancestor(:xstr, :regexp).any?
101
+ end
102
+
103
+ def use_ignored_method?(node)
104
+ (parent = node.parent) && parent.send_type? && ignored_method?(parent.method_name)
105
+ end
106
+
91
107
  def unannotated_format?(node, detected_style)
92
108
  detected_style == :unannotated && !format_string_in_typical_context?(node)
93
109
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks the usage of pre-2.1 `Hash[args]` method of converting enumerables and
7
+ # sequences of values to hashes.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # Hash[ary]
12
+ #
13
+ # # good
14
+ # ary.to_h
15
+ #
16
+ # # bad
17
+ # Hash[key1, value1, key2, value2]
18
+ #
19
+ # # good
20
+ # {key1 => value1, key2 => value2}
21
+ class HashConversion < Base
22
+ extend AutoCorrector
23
+
24
+ MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
25
+ MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to Hash[arg1, arg2, ...].'
26
+ MSG_LITERAL_HASH_ARG = 'Prefer literal hash to Hash[key: value, ...].'
27
+ MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
28
+ RESTRICT_ON_SEND = %i[[]].freeze
29
+
30
+ def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
31
+
32
+ def on_send(node)
33
+ return unless hash_from_array?(node)
34
+
35
+ # There are several cases:
36
+ # If there is one argument:
37
+ # Hash[ary] => ary.to_h
38
+ # Hash[*ary] => don't suggest corrections
39
+ # If there is 0 or 2+ arguments:
40
+ # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
41
+ # ...but don't suggest correction if there is odd number of them (it is a bug)
42
+ node.arguments.count == 1 ? single_argument(node) : multi_argument(node)
43
+ end
44
+
45
+ private
46
+
47
+ def single_argument(node)
48
+ first_argument = node.first_argument
49
+ if first_argument.hash_type?
50
+ add_offense(node, message: MSG_LITERAL_HASH_ARG) do |corrector|
51
+ corrector.replace(node, "{#{first_argument.source}}")
52
+ end
53
+ elsif first_argument.splat_type?
54
+ add_offense(node, message: MSG_SPLAT)
55
+ else
56
+ add_offense(node, message: MSG_TO_H) do |corrector|
57
+ corrector.replace(node, "#{first_argument.source}.to_h")
58
+ end
59
+ end
60
+ end
61
+
62
+ def multi_argument(node)
63
+ if node.arguments.count.odd?
64
+ add_offense(node, message: MSG_LITERAL_MULTI_ARG)
65
+ else
66
+ add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
67
+ corrector.replace(node, args_to_hash(node.arguments))
68
+ end
69
+ end
70
+ end
71
+
72
+ def args_to_hash(args)
73
+ content = args.each_slice(2)
74
+ .map { |arg1, arg2| "#{arg1.source} => #{arg2.source}" }
75
+ .join(', ')
76
+ "{#{content}}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -68,7 +68,8 @@ module RuboCop
68
68
  length = cop_config['MinBranchesCount'] || 3
69
69
  return length if length.is_a?(Integer) && length.positive?
70
70
 
71
- raise 'MinBranchesCount needs to be a positive integer!'
71
+ warn Rainbow('`MinBranchesCount` needs to be a positive integer!').red
72
+ exit!
72
73
  end
73
74
  end
74
75
  end
@@ -82,15 +82,14 @@ module RuboCop
82
82
  def autocorrect(corrector, node)
83
83
  if node.modifier_form?
84
84
  correct_to_elsif_from_modifier_form(corrector, node)
85
- end_range = node.parent.loc.end
86
85
  else
87
86
  correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
88
- end_range = node.loc.end
89
87
  end
90
- corrector.remove(range_by_whole_lines(end_range, include_final_newline: true))
91
- corrector.remove(
92
- range_by_whole_lines(node.if_branch.source_range, include_final_newline: true)
93
- )
88
+ corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
89
+ return unless (if_branch = node.if_branch)
90
+
91
+ range = range_by_whole_lines(if_branch.source_range, include_final_newline: true)
92
+ corrector.remove(range)
94
93
  end
95
94
 
96
95
  def correct_to_elsif_from_modifier_form(corrector, node)
@@ -103,13 +102,26 @@ module RuboCop
103
102
 
104
103
  def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
105
104
  corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
106
- if_condition_range = range_between(
107
- node.loc.keyword.begin_pos, condition.source_range.end_pos
108
- )
109
- corrector.replace(if_condition_range, node.if_branch.source)
105
+ if_condition_range = if_condition_range(node, condition)
106
+ if (if_branch = node.if_branch)
107
+ corrector.replace(if_condition_range, if_branch.source)
108
+ else
109
+ corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
110
+ end
110
111
  corrector.remove(condition)
111
112
  end
112
113
 
114
+ def find_end_range(node)
115
+ end_range = node.loc.end
116
+ return end_range if end_range
117
+
118
+ find_end_range(node.parent)
119
+ end
120
+
121
+ def if_condition_range(node, condition)
122
+ range_between(node.loc.keyword.begin_pos, condition.source_range.end_pos)
123
+ end
124
+
113
125
  def allow_if_modifier_in_else_branch?(else_branch)
114
126
  allow_if_modifier? && else_branch&.modifier_form?
115
127
  end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for redundant `if` with boolean literal branches.
7
+ # It checks only conditions to return boolean value (`true` or `false`) for safe detection.
8
+ # The conditions to be checked are comparison methods, predicate methods, and double negative.
9
+ # However, auto-correction is unsafe because there is no guarantee that all predicate methods
10
+ # will return boolean value. Those methods can be allowed with `AllowedMethods` config.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # if foo == bar
15
+ # true
16
+ # else
17
+ # false
18
+ # end
19
+ #
20
+ # # bad
21
+ # foo == bar ? true : false
22
+ #
23
+ # # good
24
+ # foo == bar
25
+ #
26
+ # @example AllowedMethods: ['nonzero?']
27
+ # # good
28
+ # num.nonzero? ? true : false
29
+ #
30
+ class IfWithBooleanLiteralBranches < Base
31
+ include AllowedMethods
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Remove redundant %<keyword>s with boolean literal branches.'
35
+ MSG_FOR_ELSIF = 'Use `else` instead of redundant `elsif` with boolean literal branches.'
36
+
37
+ def_node_matcher :if_with_boolean_literal_branches?, <<~PATTERN
38
+ (if #return_boolean_value? {(true) (false) | (false) (true)})
39
+ PATTERN
40
+ def_node_matcher :double_negative?, '(send (send _ :!) :!)'
41
+
42
+ def on_if(node)
43
+ return unless if_with_boolean_literal_branches?(node)
44
+
45
+ condition = node.condition
46
+ range, keyword = offense_range_with_keyword(node, condition)
47
+
48
+ add_offense(range, message: message(node, keyword)) do |corrector|
49
+ replacement = replacement_condition(node, condition)
50
+
51
+ if node.elsif?
52
+ corrector.insert_before(node, "else\n")
53
+ corrector.replace(node, "#{indent(node.if_branch)}#{replacement}")
54
+ else
55
+ corrector.replace(node, replacement)
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def offense_range_with_keyword(node, condition)
63
+ if node.ternary?
64
+ range = condition.source_range.end.join(node.source_range.end)
65
+
66
+ [range, 'ternary operator']
67
+ else
68
+ keyword = node.loc.keyword
69
+
70
+ [keyword, "`#{keyword.source}`"]
71
+ end
72
+ end
73
+
74
+ def message(node, keyword)
75
+ message_template = node.elsif? ? MSG_FOR_ELSIF : MSG
76
+
77
+ format(message_template, keyword: keyword)
78
+ end
79
+
80
+ def return_boolean_value?(condition)
81
+ if condition.begin_type?
82
+ return_boolean_value?(condition.children.first)
83
+ elsif condition.or_type?
84
+ return_boolean_value?(condition.lhs) && return_boolean_value?(condition.rhs)
85
+ elsif condition.and_type?
86
+ return_boolean_value?(condition.rhs)
87
+ else
88
+ assume_boolean_value?(condition)
89
+ end
90
+ end
91
+
92
+ def assume_boolean_value?(condition)
93
+ return false unless condition.send_type?
94
+ return false if allowed_method?(condition.method_name)
95
+
96
+ condition.comparison_method? || condition.predicate_method? || double_negative?(condition)
97
+ end
98
+
99
+ def replacement_condition(node, condition)
100
+ bang = '!' if opposite_condition?(node)
101
+
102
+ if bang && require_parentheses?(condition)
103
+ "#{bang}(#{condition.source})"
104
+ else
105
+ "#{bang}#{condition.source}"
106
+ end
107
+ end
108
+
109
+ def opposite_condition?(node)
110
+ !node.unless? && node.if_branch.false_type? || node.unless? && node.if_branch.true_type?
111
+ end
112
+
113
+ def require_parentheses?(condition)
114
+ condition.and_type? || condition.or_type? ||
115
+ condition.send_type? && condition.comparison_method?
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -157,6 +157,10 @@ module RuboCop
157
157
  include OmitParentheses
158
158
  extend AutoCorrector
159
159
 
160
+ def self.autocorrect_incompatible_with
161
+ [Style::NestedParenthesizedCalls]
162
+ end
163
+
160
164
  def on_send(node)
161
165
  send(style, node) # call require_parentheses or omit_parentheses
162
166
  end
@@ -19,6 +19,10 @@ module RuboCop
19
19
 
20
20
  MSG = 'Add parentheses to nested method call `%<source>s`.'
21
21
 
22
+ def self.autocorrect_incompatible_with
23
+ [Style::MethodCallWithArgsParentheses]
24
+ end
25
+
22
26
  def on_send(node)
23
27
  return unless node.parenthesized?
24
28
 
@@ -50,6 +50,9 @@ module RuboCop
50
50
  end
51
51
 
52
52
  corrector.replace(node, new_code)
53
+
54
+ parent = node.parent
55
+ corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
53
56
  end
54
57
  end
55
58
  end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # With `IncludeSemanticChanges` set to `false` by default, this cop
9
9
  # does not report offenses for `!x.nil?` and does no changes that might
10
10
  # change behavior.
11
+ # Also `IncludeSemanticChanges` set to `false` with `EnforcedStyle: comparison` of
12
+ # `Style/NilComparison` cop, this cop does not report offenses for `x != nil` and
13
+ # does no changes to `!x.nil?` style.
11
14
  #
12
15
  # With `IncludeSemanticChanges` set to `true`, this cop reports offenses
13
16
  # for `!x.nil?` and autocorrects that and `x != nil` to solely `x`, which
@@ -41,6 +44,9 @@ module RuboCop
41
44
  class NonNilCheck < Base
42
45
  extend AutoCorrector
43
46
 
47
+ MSG_FOR_REPLACEMENT = 'Prefer `%<prefer>s` over `%<current>s`.'
48
+ MSG_FOR_REDUNDANCY = 'Explicit non-nil checks are usually redundant.'
49
+
44
50
  RESTRICT_ON_SEND = %i[!= nil? !].freeze
45
51
 
46
52
  def_node_matcher :not_equal_to_nil?, '(send _ :!= nil)'
@@ -49,11 +55,12 @@ module RuboCop
49
55
  def_node_matcher :not_and_nil_check?, '(send (send _ :nil?) :!)'
50
56
 
51
57
  def on_send(node)
52
- return if ignored_node?(node)
53
- return unless (offense_node = find_offense_node(node))
58
+ return if ignored_node?(node) ||
59
+ !include_semantic_changes? && nil_comparison_style == 'comparison'
60
+ return unless register_offense?(node)
54
61
 
55
62
  message = message(node)
56
- add_offense(offense_node, message: message) do |corrector|
63
+ add_offense(node, message: message) do |corrector|
57
64
  autocorrect(corrector, node)
58
65
  end
59
66
  end
@@ -73,13 +80,9 @@ module RuboCop
73
80
 
74
81
  private
75
82
 
76
- def find_offense_node(node)
77
- if not_equal_to_nil?(node)
78
- node.loc.selector
79
- elsif include_semantic_changes? &&
80
- (not_and_nil_check?(node) || unless_and_nil_check?(node))
81
- node
82
- end
83
+ def register_offense?(node)
84
+ not_equal_to_nil?(node) ||
85
+ include_semantic_changes? && (not_and_nil_check?(node) || unless_and_nil_check?(node))
83
86
  end
84
87
 
85
88
  def autocorrect(corrector, node)
@@ -101,10 +104,11 @@ module RuboCop
101
104
  end
102
105
 
103
106
  def message(node)
104
- if node.method?(:!=)
105
- 'Prefer `!expression.nil?` over `expression != nil`.'
107
+ if node.method?(:!=) && !include_semantic_changes?
108
+ prefer = "!#{node.receiver.source}.nil?"
109
+ format(MSG_FOR_REPLACEMENT, prefer: prefer, current: node.source)
106
110
  else
107
- 'Explicit non-nil checks are usually redundant.'
111
+ MSG_FOR_REDUNDANCY
108
112
  end
109
113
  end
110
114
 
@@ -138,6 +142,12 @@ module RuboCop
138
142
  corrector.replace(node.parent.loc.keyword, 'if')
139
143
  corrector.replace(node, receiver.source)
140
144
  end
145
+
146
+ def nil_comparison_style
147
+ nil_comparison_conf = config.for_cop('Style/NilComparison')
148
+
149
+ nil_comparison_conf['Enabled'] && nil_comparison_conf['EnforcedStyle']
150
+ end
141
151
  end
142
152
  end
143
153
  end