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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +32 -19
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
- data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/naming/method_name.rb +15 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +4 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/array_intersect.rb +51 -23
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +2 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -0
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +16 -7
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +2 -0
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5919779b3b5bcc30656adbfd28f00fc821327d6ecf4abff825eea32150319aef
|
4
|
+
data.tar.gz: d94231324f779e1f99469dc032b762825a761bc0a3ed85e44f34ae3fdc4ea78a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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:
|
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:
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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: '
|
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:
|
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: '
|
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
|
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: '
|
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: '
|
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: '
|
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
|
-
|
70
|
-
|
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)
|
@@ -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
|
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
|
@@ -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
|
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.
|
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
|
|