rubocop 1.78.0 → 1.79.0

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +32 -19
  4. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  5. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  6. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
  7. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  8. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  9. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  10. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  11. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  12. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  13. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  14. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  15. data/lib/rubocop/cop/naming/method_name.rb +15 -1
  16. data/lib/rubocop/cop/naming/predicate_method.rb +4 -1
  17. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  18. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  19. data/lib/rubocop/cop/style/array_intersect.rb +51 -23
  20. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  21. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  22. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  23. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  24. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  25. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  26. data/lib/rubocop/cop/style/it_block_parameter.rb +2 -0
  27. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  28. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -0
  29. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  30. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  31. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -0
  32. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  33. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  34. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  35. data/lib/rubocop/cop/variable_force.rb +16 -7
  36. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  37. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  38. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  39. data/lib/rubocop/version.rb +1 -1
  40. data/lib/rubocop.rb +2 -0
  41. metadata +22 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b840814034fbb98e67e3bb60014f0f00f9387148ab85b8b66f3a7ca711efdd2
4
- data.tar.gz: b223e62eeeef5effd425654bf869ea22935beb8955df6e6fb22f0ba2a623d0f3
3
+ metadata.gz: 5919779b3b5bcc30656adbfd28f00fc821327d6ecf4abff825eea32150319aef
4
+ data.tar.gz: d94231324f779e1f99469dc032b762825a761bc0a3ed85e44f34ae3fdc4ea78a
5
5
  SHA512:
6
- metadata.gz: 83f46c7462c956b9a37e61feb329b3dfdb96e631cab64ffa3d1485bdc2d36a3585dacc17fc0b546ca98a0ad1014e1818eabf328a367654ad0151eaea3eea7369
7
- data.tar.gz: d7cd91419c2ff881ef14b5f4406c9b87e555d2948c2c20b98bf02e6007e44b3d20b0aa73f3b91a595c5613164e551ba5f728e0852d084d04fe37312a65fa2b96
6
+ metadata.gz: fff3036cd812063f449fc41c2e1b536c6735fd1e85742692a5f710769a619b2eab1c5800180744b748ab3ba37fe1f4e6c64dd8edf9d4a9abc2f426bf89b47091
7
+ data.tar.gz: d174e5c8d47b012a591bd59292411c2e23927d378c88362bf1f77427b6bf92230f1f95417e7f2ec44454d78bbfe41bf40fd7e7c07add9047beda6655d307e7ed
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.78', require: false
56
+ gem 'rubocop', '~> 1.79', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -378,7 +378,7 @@ Gemspec/RubyVersionGlobalsUsage:
378
378
  #################### Layout ###########################
379
379
 
380
380
  Layout/AccessModifierIndentation:
381
- Description: Check indentation of private/protected visibility modifiers.
381
+ Description: Checks indentation of private/protected visibility modifiers.
382
382
  StyleGuide: '#indent-public-private-protected'
383
383
  Enabled: true
384
384
  VersionAdded: '0.49'
@@ -638,6 +638,12 @@ Layout/EmptyLines:
638
638
  Enabled: true
639
639
  VersionAdded: '0.49'
640
640
 
641
+ Layout/EmptyLinesAfterModuleInclusion:
642
+ Description: 'Keeps track of empty lines after module inclusion methods.'
643
+ StyleGuide: '#empty-lines-after-module-inclusion'
644
+ Enabled: pending
645
+ VersionAdded: '1.79'
646
+
641
647
  Layout/EmptyLinesAroundAccessModifier:
642
648
  Description: "Keep blank lines around access modifiers."
643
649
  StyleGuide: '#empty-lines-around-access-modifier'
@@ -1051,7 +1057,7 @@ Layout/LeadingCommentSpace:
1051
1057
  AllowSteepAnnotation: false
1052
1058
 
1053
1059
  Layout/LeadingEmptyLines:
1054
- Description: Check for unnecessary blank lines at the beginning of a file.
1060
+ Description: Checks for unnecessary blank lines at the beginning of a file.
1055
1061
  Enabled: true
1056
1062
  VersionAdded: '0.57'
1057
1063
  VersionChanged: '0.77'
@@ -1141,7 +1147,7 @@ Layout/MultilineArrayLineBreaks:
1141
1147
  AllowMultilineFinalElement: false
1142
1148
 
1143
1149
  Layout/MultilineAssignmentLayout:
1144
- Description: 'Check for a newline after the assignment operator in multi-line assignments.'
1150
+ Description: 'Checks for a newline after the assignment operator in multi-line assignments.'
1145
1151
  StyleGuide: '#indent-conditional-assignment'
1146
1152
  Enabled: false
1147
1153
  VersionAdded: '0.49'
@@ -1655,7 +1661,7 @@ Lint/BinaryOperatorWithIdenticalOperands:
1655
1661
  VersionChanged: '1.69'
1656
1662
 
1657
1663
  Lint/BooleanSymbol:
1658
- Description: 'Check for `:true` and `:false` symbols.'
1664
+ Description: 'Checks for `:true` and `:false` symbols.'
1659
1665
  Enabled: true
1660
1666
  SafeAutoCorrect: false
1661
1667
  VersionAdded: '0.50'
@@ -1688,7 +1694,7 @@ Lint/ConstantReassignment:
1688
1694
  VersionAdded: '1.70'
1689
1695
 
1690
1696
  Lint/ConstantResolution:
1691
- Description: 'Check that constants are fully qualified with `::`.'
1697
+ Description: 'Checks that constants are fully qualified with `::`.'
1692
1698
  Enabled: false
1693
1699
  VersionAdded: '0.86'
1694
1700
  # Restrict this cop to only looking at certain names
@@ -1702,7 +1708,7 @@ Lint/CopDirectiveSyntax:
1702
1708
  VersionAdded: '1.72'
1703
1709
 
1704
1710
  Lint/Debugger:
1705
- Description: 'Check for debugger calls.'
1711
+ Description: 'Checks for debugger calls.'
1706
1712
  Enabled: true
1707
1713
  VersionAdded: '0.14'
1708
1714
  VersionChanged: '1.63'
@@ -1753,7 +1759,7 @@ Lint/Debugger:
1753
1759
  - debug/start
1754
1760
 
1755
1761
  Lint/DeprecatedClassMethods:
1756
- Description: 'Check for deprecated class method calls.'
1762
+ Description: 'Checks for deprecated class method calls.'
1757
1763
  Enabled: true
1758
1764
  VersionAdded: '0.19'
1759
1765
 
@@ -1827,13 +1833,13 @@ Lint/DuplicateElsifCondition:
1827
1833
  VersionAdded: '0.88'
1828
1834
 
1829
1835
  Lint/DuplicateHashKey:
1830
- Description: 'Check for duplicate keys in hash literals.'
1836
+ Description: 'Checks for duplicate keys in hash literals.'
1831
1837
  Enabled: true
1832
1838
  VersionAdded: '0.34'
1833
1839
  VersionChanged: '0.77'
1834
1840
 
1835
1841
  Lint/DuplicateMagicComment:
1836
- Description: 'Check for duplicated magic comments.'
1842
+ Description: 'Checks for duplicated magic comments.'
1837
1843
  Enabled: pending
1838
1844
  VersionAdded: '1.37'
1839
1845
 
@@ -1843,7 +1849,7 @@ Lint/DuplicateMatchPattern:
1843
1849
  VersionAdded: '1.50'
1844
1850
 
1845
1851
  Lint/DuplicateMethods:
1846
- Description: 'Check for duplicate method definitions.'
1852
+ Description: 'Checks for duplicate method definitions.'
1847
1853
  Enabled: true
1848
1854
  VersionAdded: '0.29'
1849
1855
 
@@ -1853,7 +1859,7 @@ Lint/DuplicateRegexpCharacterClassElement:
1853
1859
  VersionAdded: '1.1'
1854
1860
 
1855
1861
  Lint/DuplicateRequire:
1856
- Description: 'Check for duplicate `require`s and `require_relative`s.'
1862
+ Description: 'Checks for duplicate `require`s and `require_relative`s.'
1857
1863
  Enabled: true
1858
1864
  SafeAutoCorrect: false
1859
1865
  VersionAdded: '0.90'
@@ -1870,12 +1876,12 @@ Lint/DuplicateSetElement:
1870
1876
  VersionAdded: '1.67'
1871
1877
 
1872
1878
  Lint/EachWithObjectArgument:
1873
- Description: 'Check for immutable argument given to each_with_object.'
1879
+ Description: 'Checks for immutable argument given to each_with_object.'
1874
1880
  Enabled: true
1875
1881
  VersionAdded: '0.31'
1876
1882
 
1877
1883
  Lint/ElseLayout:
1878
- Description: 'Check for odd code arrangement in an else block.'
1884
+ Description: 'Checks for odd code arrangement in an else block.'
1879
1885
  Enabled: true
1880
1886
  VersionAdded: '0.17'
1881
1887
  VersionChanged: '1.2'
@@ -2278,6 +2284,7 @@ Lint/RedundantSafeNavigation:
2278
2284
  Description: 'Checks for redundant safe navigation calls.'
2279
2285
  Enabled: true
2280
2286
  VersionAdded: '0.93'
2287
+ VersionChanged: '1.79'
2281
2288
  AllowedMethods:
2282
2289
  - instance_of?
2283
2290
  - kind_of?
@@ -2285,6 +2292,12 @@ Lint/RedundantSafeNavigation:
2285
2292
  - eql?
2286
2293
  - respond_to?
2287
2294
  - equal?
2295
+ InferNonNilReceiver: false
2296
+ AdditionalNilMethods:
2297
+ - present?
2298
+ - blank?
2299
+ - try
2300
+ - try!
2288
2301
  Safe: false
2289
2302
 
2290
2303
  Lint/RedundantSplatExpansion:
@@ -2817,7 +2830,7 @@ Migration/DepartmentName:
2817
2830
  #################### Naming ##############################
2818
2831
 
2819
2832
  Naming/AccessorMethodName:
2820
- Description: Check the naming of accessor methods for get_/set_.
2833
+ Description: Checks the naming of accessor methods for get_/set_.
2821
2834
  StyleGuide: '#accessor_mutator_method_names'
2822
2835
  Enabled: true
2823
2836
  VersionAdded: '0.50'
@@ -4224,7 +4237,7 @@ Style/GlobalVars:
4224
4237
  AllowedVariables: []
4225
4238
 
4226
4239
  Style/GuardClause:
4227
- Description: 'Check for conditionals that can be replaced with guard clauses.'
4240
+ Description: 'Checks for conditionals that can be replaced with guard clauses.'
4228
4241
  StyleGuide: '#no-nested-conditionals'
4229
4242
  Enabled: true
4230
4243
  VersionAdded: '0.20'
@@ -4486,7 +4499,7 @@ Style/IpAddresses:
4486
4499
  - '**/*.gemspec'
4487
4500
 
4488
4501
  Style/ItAssignment:
4489
- Description: 'Checks for assignment to `it` inside a block.'
4502
+ Description: 'Checks for local variables and method parameters named `it`.'
4490
4503
  Enabled: pending
4491
4504
  VersionAdded: '1.70'
4492
4505
 
@@ -5187,7 +5200,7 @@ Style/RandomWithOffset:
5187
5200
  VersionAdded: '0.52'
5188
5201
 
5189
5202
  Style/RedundantArgument:
5190
- Description: 'Check for a redundant argument passed to certain methods.'
5203
+ Description: 'Checks for a redundant argument passed to certain methods.'
5191
5204
  Enabled: pending
5192
5205
  Safe: false
5193
5206
  VersionAdded: '1.4'
@@ -5348,7 +5361,7 @@ Style/RedundantInterpolationUnfreeze:
5348
5361
  VersionAdded: '1.66'
5349
5362
 
5350
5363
  Style/RedundantLineContinuation:
5351
- Description: 'Check for redundant line continuation.'
5364
+ Description: 'Checks for redundant line continuation.'
5352
5365
  Enabled: pending
5353
5366
  VersionAdded: '1.49'
5354
5367
 
@@ -5642,7 +5655,7 @@ Style/SpecialGlobalVars:
5642
5655
  - use_builtin_english_names
5643
5656
 
5644
5657
  Style/StabbyLambdaParentheses:
5645
- Description: 'Check for the usage of parentheses around stabby lambda arguments.'
5658
+ Description: 'Checks for the usage of parentheses around stabby lambda arguments.'
5646
5659
  StyleGuide: '#stabby-lambda-with-args'
5647
5660
  Enabled: true
5648
5661
  VersionAdded: '0.35'
@@ -66,8 +66,9 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def autocorrect_to_explicit_predicate(corrector, node, group_name)
69
- corrector.replace(node.selector, "#{group_name}_type?")
70
- corrector.remove(arguments_range(node))
69
+ range = node.loc.selector.begin.join(node.source_range.end)
70
+
71
+ corrector.replace(range, "#{group_name}_type?")
71
72
  end
72
73
 
73
74
  def autocorrect_keep_method(corrector, symbol_args, group_name, group_types)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module InternalAffairs
6
- # Check for useless `RESTRICT_ON_SEND`.
6
+ # Checks for useless `RESTRICT_ON_SEND`.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Layout
6
+ # Checks for an empty line after a module inclusion method (`extend`,
7
+ # `include` and `prepend`), or a group of them.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # class Foo
12
+ # include Bar
13
+ # attr_reader :baz
14
+ # end
15
+ #
16
+ # # good
17
+ # class Foo
18
+ # include Bar
19
+ #
20
+ # attr_reader :baz
21
+ # end
22
+ #
23
+ # # also good - multiple module inclusions grouped together
24
+ # class Foo
25
+ # extend Bar
26
+ # include Baz
27
+ # prepend Qux
28
+ # end
29
+ #
30
+ class EmptyLinesAfterModuleInclusion < Base
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Add an empty line after module inclusion.'
35
+
36
+ MODULE_INCLUSION_METHODS = %i[include extend prepend].freeze
37
+
38
+ RESTRICT_ON_SEND = MODULE_INCLUSION_METHODS
39
+
40
+ def on_send(node)
41
+ return if node.receiver
42
+ return if node.parent&.send_type?
43
+
44
+ return if next_line_empty_or_enable_directive_comment?(node.last_line)
45
+
46
+ next_line_node = next_line_node(node)
47
+ return unless require_empty_line?(next_line_node)
48
+
49
+ add_offense(node) { |corrector| autocorrect(corrector, node) }
50
+ end
51
+
52
+ private
53
+
54
+ def autocorrect(corrector, node)
55
+ node_range = range_by_whole_lines(node.source_range)
56
+
57
+ next_line = node_range.last_line + 1
58
+ if enable_directive_comment?(next_line)
59
+ node_range = processed_source.comment_at_line(next_line)
60
+ end
61
+
62
+ corrector.insert_after(node_range, "\n")
63
+ end
64
+
65
+ def next_line_empty_or_enable_directive_comment?(line)
66
+ line_empty?(line) || (enable_directive_comment?(line + 1) && line_empty?(line + 1))
67
+ end
68
+
69
+ def enable_directive_comment?(line)
70
+ return false unless (comment = processed_source.comment_at_line(line))
71
+
72
+ DirectiveComment.new(comment).enabled?
73
+ end
74
+
75
+ def line_empty?(line)
76
+ processed_source[line].nil? || processed_source[line].blank?
77
+ end
78
+
79
+ def require_empty_line?(node)
80
+ return false unless node
81
+
82
+ !allowed_method?(node)
83
+ end
84
+
85
+ def allowed_method?(node)
86
+ return false unless node.send_type?
87
+
88
+ MODULE_INCLUSION_METHODS.include?(node.method_name)
89
+ end
90
+
91
+ def next_line_node(node)
92
+ return if node.parent.if_type?
93
+
94
+ node.right_sibling
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -16,6 +16,8 @@ module RuboCop
16
16
  #
17
17
  # something = 123if test
18
18
  #
19
+ # return(foo + bar)
20
+ #
19
21
  # # good
20
22
  # something 'test' do |x|
21
23
  # end
@@ -24,6 +26,9 @@ module RuboCop
24
26
  # end
25
27
  #
26
28
  # something = 123 if test
29
+ #
30
+ # return (foo + bar)
31
+ #
27
32
  class SpaceAroundKeyword < Base
28
33
  extend AutoCorrector
29
34
 
@@ -33,7 +38,7 @@ module RuboCop
33
38
  DO = 'do'
34
39
  SAFE_NAVIGATION = '&.'
35
40
  NAMESPACE_OPERATOR = '::'
36
- ACCEPT_LEFT_PAREN = %w[break defined? next not rescue return super yield].freeze
41
+ ACCEPT_LEFT_PAREN = %w[break defined? next not rescue super yield].freeze
37
42
  ACCEPT_LEFT_SQUARE_BRACKET = %w[super yield].freeze
38
43
  ACCEPT_NAMESPACE_OPERATOR = 'super'
39
44
  RESTRICT_ON_SEND = %i[!].freeze
@@ -151,6 +151,14 @@ module RuboCop
151
151
  check_operator(:match_pattern, node.loc.operator, node)
152
152
  end
153
153
 
154
+ def on_match_alt(node)
155
+ check_operator(:match_alt, node.loc.operator, node)
156
+ end
157
+
158
+ def on_match_as(node)
159
+ check_operator(:match_as, node.loc.operator, node)
160
+ end
161
+
154
162
  alias on_or on_binary
155
163
  alias on_and on_binary
156
164
  alias on_lvasgn on_assignment
@@ -45,6 +45,7 @@ module RuboCop
45
45
  #
46
46
  class NumericOperationWithConstantResult < Base
47
47
  extend AutoCorrector
48
+
48
49
  MSG = 'Numeric operation with a constant result detected.'
49
50
  RESTRICT_ON_SEND = %i[* / **].freeze
50
51
 
@@ -20,6 +20,15 @@ module RuboCop
20
20
  # In the example below, the safe navigation operator (`&.`) is unnecessary
21
21
  # because `NilClass` has methods like `respond_to?` and `is_a?`.
22
22
  #
23
+ # The `InferNonNilReceiver` option specifies whether to look into previous code
24
+ # paths to infer if the receiver can't be nil. This check is unsafe because the receiver
25
+ # can be redefined between the safe navigation call and previous regular method call.
26
+ # It does the inference only in the current scope, e.g. within the same method definition etc.
27
+ #
28
+ # The `AdditionalNilMethods` option specifies additional custom methods which are
29
+ # defined on `NilClass`. When `InferNonNilReceiver` is set, they are used to determine
30
+ # whether the receiver can be nil.
31
+ #
23
32
  # @safety
24
33
  # This cop is unsafe, because autocorrection can change the return type of
25
34
  # the expression. An offending expression that previously could return `nil`
@@ -33,6 +42,20 @@ module RuboCop
33
42
  # CamelCaseConst.do_something
34
43
  #
35
44
  # # bad
45
+ # foo.to_s&.strip
46
+ # foo.to_i&.zero?
47
+ # foo.to_f&.zero?
48
+ # foo.to_a&.size
49
+ # foo.to_h&.size
50
+ #
51
+ # # good
52
+ # foo.to_s.strip
53
+ # foo.to_i.zero?
54
+ # foo.to_f.zero?
55
+ # foo.to_a.size
56
+ # foo.to_h.size
57
+ #
58
+ # # bad
36
59
  # do_something if attrs&.respond_to?(:[])
37
60
  #
38
61
  # # good
@@ -81,17 +104,59 @@ module RuboCop
81
104
  # do_something if attrs.nil_safe_method(:[])
82
105
  # do_something if attrs&.not_nil_safe_method(:[])
83
106
  #
107
+ # @example InferNonNilReceiver: false (default)
108
+ # # good
109
+ # foo.bar
110
+ # foo&.baz
111
+ #
112
+ # @example InferNonNilReceiver: true
113
+ # # bad
114
+ # foo.bar
115
+ # foo&.baz # would raise on previous line if `foo` is nil
116
+ #
117
+ # # good
118
+ # foo.bar
119
+ # foo.baz
120
+ #
121
+ # # bad
122
+ # if foo.condition?
123
+ # foo&.bar
124
+ # end
125
+ #
126
+ # # good
127
+ # if foo.condition?
128
+ # foo.bar
129
+ # end
130
+ #
131
+ # # good (different scopes)
132
+ # def method1
133
+ # foo.bar
134
+ # end
135
+ #
136
+ # def method2
137
+ # foo&.bar
138
+ # end
139
+ #
140
+ # @example AdditionalNilMethods: [present?]
141
+ # # good
142
+ # foo.present?
143
+ # foo&.bar
144
+ #
84
145
  class RedundantSafeNavigation < Base
85
146
  include AllowedMethods
86
147
  extend AutoCorrector
87
148
 
88
149
  MSG = 'Redundant safe navigation detected, use `.` instead.'
89
150
  MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
151
+ MSG_NON_NIL = 'Redundant safe navigation on non-nil receiver (detected by analyzing ' \
152
+ 'previous code/method invocations).'
90
153
 
91
154
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
92
155
 
93
156
  SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
94
157
 
158
+ GUARANTEED_INSTANCE_METHODS = %i[to_s to_i to_f to_a to_h].freeze
159
+
95
160
  # @!method respond_to_nil_specific_method?(node)
96
161
  def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
97
162
  (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
@@ -111,15 +176,27 @@ module RuboCop
111
176
 
112
177
  # rubocop:disable Metrics/AbcSize
113
178
  def on_csend(node)
179
+ range = node.loc.dot
180
+
181
+ if infer_non_nil_receiver?
182
+ checker = Lint::Utils::NilReceiverChecker.new(node.receiver, additional_nil_methods)
183
+
184
+ if checker.cant_be_nil?
185
+ add_offense(range, message: MSG_NON_NIL) { |corrector| corrector.replace(range, '.') }
186
+ return
187
+ end
188
+ end
189
+
114
190
  unless assume_receiver_instance_exists?(node.receiver)
115
- return unless check?(node) && allowed_method?(node.method_name)
191
+ return if !guaranteed_instance?(node.receiver) && !check?(node)
116
192
  return if respond_to_nil_specific_method?(node)
117
193
  end
118
194
 
119
- range = node.loc.dot
120
195
  add_offense(range) { |corrector| corrector.replace(range, '.') }
121
196
  end
197
+ # rubocop:enable Metrics/AbcSize
122
198
 
199
+ # rubocop:disable Metrics/AbcSize
123
200
  def on_or(node)
124
201
  conversion_with_default?(node) do |send_node|
125
202
  range = send_node.loc.dot.begin.join(node.source_range.end)
@@ -142,7 +219,20 @@ module RuboCop
142
219
  receiver.self_type? || (receiver.literal? && !receiver.nil_type?)
143
220
  end
144
221
 
222
+ def guaranteed_instance?(node)
223
+ receiver = if node.any_block_type?
224
+ node.send_node
225
+ else
226
+ node
227
+ end
228
+ return false unless receiver.send_type?
229
+
230
+ GUARANTEED_INSTANCE_METHODS.include?(receiver.method_name)
231
+ end
232
+
145
233
  def check?(node)
234
+ return false unless allowed_method?(node.method_name)
235
+
146
236
  parent = node.parent
147
237
  return false unless parent
148
238
 
@@ -154,6 +244,15 @@ module RuboCop
154
244
  def condition?(parent, node)
155
245
  (parent.conditional? || parent.post_condition_loop?) && parent.condition == node
156
246
  end
247
+
248
+ def infer_non_nil_receiver?
249
+ cop_config['InferNonNilReceiver']
250
+ end
251
+
252
+ def additional_nil_methods
253
+ @additional_nil_methods ||=
254
+ Array(cop_config.fetch('AdditionalNilMethods', []).map(&:to_sym))
255
+ end
157
256
  end
158
257
  end
159
258
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  def on_irange(node)
44
44
  return if node.parent&.begin_type?
45
45
  return unless node.begin && node.end
46
- return if same_line?(node.begin, node.end)
46
+ return if same_line?(node.loc.operator, node.end)
47
47
 
48
48
  message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
49
49
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Check for arguments to `rescue` that will result in a `TypeError`
6
+ # Checks for arguments to `rescue` that will result in a `TypeError`
7
7
  # if an exception is raised.
8
8
  #
9
9
  # @example
@@ -31,6 +31,7 @@ module RuboCop
31
31
  #
32
32
  class UselessNumericOperation < Base
33
33
  extend AutoCorrector
34
+
34
35
  MSG = 'Do not apply inconsequential numeric operations to variables.'
35
36
  RESTRICT_ON_SEND = %i[+ - * / **].freeze
36
37