rubocop 1.86.0 → 1.86.2
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/config/default.yml +14 -3
- data/lib/rubocop/cli/command/auto_generate_config.rb +27 -1
- data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
- data/lib/rubocop/cli/command/show_docs_url.rb +3 -7
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +4 -7
- data/lib/rubocop/comment_config.rb +12 -15
- data/lib/rubocop/config.rb +13 -9
- data/lib/rubocop/config_loader_resolver.rb +2 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
- data/lib/rubocop/cop/correctors.rb +28 -0
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/exclude_limit.rb +31 -5
- data/lib/rubocop/cop/gemspec/require_mfa.rb +3 -3
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +4 -2
- data/lib/rubocop/cop/layout/line_length.rb +5 -3
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +26 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +10 -5
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
- data/lib/rubocop/cop/lint/require_relative_self_path.rb +2 -0
- data/lib/rubocop/cop/lint/syntax.rb +25 -1
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +3 -8
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +33 -8
- data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
- data/lib/rubocop/cop/mixin.rb +85 -0
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +8 -0
- data/lib/rubocop/cop/registry.rb +19 -24
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
- data/lib/rubocop/cop/style/copyright.rb +21 -10
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
- data/lib/rubocop/cop/style/guard_clause.rb +9 -6
- data/lib/rubocop/cop/style/hash_lookup_method.rb +12 -7
- data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
- data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
- data/lib/rubocop/cop/style/one_class_per_file.rb +24 -4
- data/lib/rubocop/cop/style/reduce_to_hash.rb +16 -0
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -1
- data/lib/rubocop/cop/style/redundant_self.rb +2 -2
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +10 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +29 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +3 -3
- data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
- data/lib/rubocop/cop/team.rb +86 -35
- data/lib/rubocop/formatter/disabled_config_formatter.rb +4 -1
- data/lib/rubocop/lsp/runtime.rb +1 -2
- data/lib/rubocop/mcp/server.rb +2 -0
- data/lib/rubocop/options.rb +8 -4
- data/lib/rubocop/rspec/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +21 -0
- data/lib/rubocop/runner.rb +77 -55
- data/lib/rubocop/target_finder.rb +13 -6
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +7 -96
- metadata +7 -4
|
@@ -26,7 +26,31 @@ module RuboCop
|
|
|
26
26
|
"#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
|
|
27
27
|
'configure using `TargetRubyVersion` parameter, under `AllCops`)'
|
|
28
28
|
end
|
|
29
|
-
|
|
29
|
+
location = diagnostic_location(diagnostic.location)
|
|
30
|
+
add_offense(location, message: message, severity: diagnostic.level)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Expand zero-length diagnostic ranges so that editors and formatters
|
|
34
|
+
# can display them. This typically occurs when the parser reports
|
|
35
|
+
# `unexpected token $end` at EOF.
|
|
36
|
+
def diagnostic_location(location)
|
|
37
|
+
return location if location.size.positive?
|
|
38
|
+
|
|
39
|
+
source_buffer = location.source_buffer
|
|
40
|
+
if location.end_pos < source_buffer.source.size
|
|
41
|
+
location.resize(1)
|
|
42
|
+
elsif location.begin_pos.positive?
|
|
43
|
+
location.adjust(begin_pos: -1)
|
|
44
|
+
else
|
|
45
|
+
location
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Override to skip multiline_ranges check which requires AST.
|
|
50
|
+
# Syntax errors mean the AST is nil, so we go directly to
|
|
51
|
+
# the EOL comment insertion path.
|
|
52
|
+
def disable_offense(offense_range)
|
|
53
|
+
disable_offense_with_eol_or_surround_comment(offense_range)
|
|
30
54
|
end
|
|
31
55
|
|
|
32
56
|
def add_offense_from_error(error)
|
|
@@ -95,10 +95,20 @@ module RuboCop
|
|
|
95
95
|
return unless variable.method_argument?
|
|
96
96
|
return if variable.keyword_argument? && cop_config['AllowUnusedKeywordArguments']
|
|
97
97
|
return if ignored_method?(variable.scope.node.body)
|
|
98
|
+
return if block_argument_with_yield?(variable)
|
|
98
99
|
|
|
99
100
|
super
|
|
100
101
|
end
|
|
101
102
|
|
|
103
|
+
def block_argument_with_yield?(variable)
|
|
104
|
+
return false unless variable.declaration_node.blockarg_type?
|
|
105
|
+
|
|
106
|
+
method_body = variable.scope.node.body
|
|
107
|
+
return false unless method_body
|
|
108
|
+
|
|
109
|
+
method_body.yield_type? || method_body.each_descendant(:yield).any?
|
|
110
|
+
end
|
|
111
|
+
|
|
102
112
|
def ignored_method?(body)
|
|
103
113
|
(cop_config['IgnoreEmptyMethods'] && body.nil?) ||
|
|
104
114
|
(cop_config['IgnoreNotImplementedMethods'] && not_implemented?(body))
|
|
@@ -40,8 +40,6 @@ module RuboCop
|
|
|
40
40
|
class UselessAssignment < Base
|
|
41
41
|
extend AutoCorrector
|
|
42
42
|
|
|
43
|
-
include RangeHelp
|
|
44
|
-
|
|
45
43
|
MSG = 'Useless assignment to variable - `%<variable>s`.'
|
|
46
44
|
|
|
47
45
|
def self.joining_forces
|
|
@@ -189,12 +187,9 @@ module RuboCop
|
|
|
189
187
|
# rubocop:enable Metrics/AbcSize
|
|
190
188
|
|
|
191
189
|
def remove_exception_assignment_part(corrector, node)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
node.source_range.end_pos
|
|
196
|
-
)
|
|
197
|
-
)
|
|
190
|
+
range = node.parent.children.first&.source_range || node.parent.location.keyword
|
|
191
|
+
|
|
192
|
+
corrector.remove(range.end.join(node.source_range.end))
|
|
198
193
|
end
|
|
199
194
|
|
|
200
195
|
def rename_variable_with_underscore(corrector, node)
|
|
@@ -60,9 +60,7 @@ module RuboCop
|
|
|
60
60
|
return true if _cant_be_nil?(node.expression, receiver)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
# using left_siblings will not work correctly for them.
|
|
65
|
-
if !else_branch?(node) || (node.if_type? && !node.elsif?)
|
|
63
|
+
if sequentially_reached?(node)
|
|
66
64
|
node.left_siblings.reverse_each do |sibling|
|
|
67
65
|
next unless sibling.is_a?(AST::Node)
|
|
68
66
|
|
|
@@ -82,15 +80,16 @@ module RuboCop
|
|
|
82
80
|
!NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
|
|
83
81
|
end
|
|
84
82
|
|
|
85
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
83
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
86
84
|
def sole_condition_of_parent_if?(node)
|
|
85
|
+
child = node
|
|
87
86
|
parent = node.parent
|
|
88
87
|
|
|
89
88
|
while parent
|
|
90
89
|
if parent.if_type?
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return true
|
|
90
|
+
unless parent.unless?
|
|
91
|
+
condition = parent.condition
|
|
92
|
+
return true if !child.equal?(condition) && non_nil_condition?(condition, node)
|
|
94
93
|
end
|
|
95
94
|
|
|
96
95
|
parent = find_top_if(parent) if parent.elsif?
|
|
@@ -99,12 +98,30 @@ module RuboCop
|
|
|
99
98
|
parent = parent.parent
|
|
100
99
|
end
|
|
101
100
|
|
|
101
|
+
child = parent
|
|
102
102
|
parent = parent&.parent
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
false
|
|
106
106
|
end
|
|
107
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
107
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
108
|
+
|
|
109
|
+
def non_nil_condition?(condition, node)
|
|
110
|
+
return true if condition == node
|
|
111
|
+
|
|
112
|
+
condition.csend_type? && csend_root_receiver(condition) == node
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Whether control reaches `node` by falling through its left siblings rather than by
|
|
116
|
+
# a non-sequential entry. A `resbody` is entered via an exception, the `ensure` branch
|
|
117
|
+
# runs even after a partway raise, and the `else` arm of an `if/elsif` chain is reached by
|
|
118
|
+
# branching (its `if/case` siblings are walked via parent recursion instead).
|
|
119
|
+
def sequentially_reached?(node)
|
|
120
|
+
return false if node.resbody_type?
|
|
121
|
+
return false if node.parent&.ensure_type? && node.parent.branch.equal?(node)
|
|
122
|
+
|
|
123
|
+
!else_branch?(node) || (node.if_type? && !node.elsif?)
|
|
124
|
+
end
|
|
108
125
|
|
|
109
126
|
def else_branch?(node)
|
|
110
127
|
node.parent&.if_type? && node.parent.else_branch == node
|
|
@@ -115,6 +132,14 @@ module RuboCop
|
|
|
115
132
|
|
|
116
133
|
node
|
|
117
134
|
end
|
|
135
|
+
|
|
136
|
+
def csend_root_receiver(node)
|
|
137
|
+
return unless (receiver = node.receiver)
|
|
138
|
+
|
|
139
|
+
receiver = receiver.receiver while receiver.call_type? && receiver.receiver
|
|
140
|
+
|
|
141
|
+
receiver
|
|
142
|
+
end
|
|
118
143
|
end
|
|
119
144
|
end
|
|
120
145
|
end
|
|
@@ -13,11 +13,12 @@ module RuboCop
|
|
|
13
13
|
`max=` is deprecated. Use `exclude_limit <ParameterName>` instead.
|
|
14
14
|
WARNING
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
cop_dir = RuboCop::ExcludeLimit.cop_dir_for(self.class.badge.to_s)
|
|
17
|
+
return unless cop_dir
|
|
18
|
+
|
|
19
|
+
cop_dir.mkpath
|
|
20
|
+
filepath = cop_dir.join(max_parameter_name)
|
|
21
|
+
filepath.write("#{value}\n", mode: 'a')
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def max_parameter_name
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop # rubocop:disable Style/Documentation
|
|
5
|
+
# Autoloads mixin modules included by cops. Mixins are autoloaded to reduce the number of
|
|
6
|
+
# requires because they're used only when the relevant cop class is loaded.
|
|
7
|
+
|
|
8
|
+
autoload :ArrayMinSize, 'rubocop/cop/mixin/array_min_size'
|
|
9
|
+
autoload :ArraySyntax, 'rubocop/cop/mixin/array_syntax'
|
|
10
|
+
autoload :Alignment, 'rubocop/cop/mixin/alignment'
|
|
11
|
+
autoload :AllowedIdentifiers, 'rubocop/cop/mixin/allowed_identifiers'
|
|
12
|
+
autoload :AllowedMethods, 'rubocop/cop/mixin/allowed_methods'
|
|
13
|
+
autoload :AllowedPattern, 'rubocop/cop/mixin/allowed_pattern'
|
|
14
|
+
autoload :AllowedReceivers, 'rubocop/cop/mixin/allowed_receivers'
|
|
15
|
+
autoload :ForbiddenIdentifiers, 'rubocop/cop/mixin/forbidden_identifiers'
|
|
16
|
+
autoload :ForbiddenPattern, 'rubocop/cop/mixin/forbidden_pattern'
|
|
17
|
+
autoload :AutoCorrector, 'rubocop/cop/mixin/auto_corrector' # rubocop:todo Naming/InclusiveLanguage
|
|
18
|
+
autoload :CheckAssignment, 'rubocop/cop/mixin/check_assignment'
|
|
19
|
+
autoload :CheckLineBreakable, 'rubocop/cop/mixin/check_line_breakable'
|
|
20
|
+
autoload :CheckSingleLineSuitability, 'rubocop/cop/mixin/check_single_line_suitability'
|
|
21
|
+
autoload :ConfigurableMax, 'rubocop/cop/mixin/configurable_max'
|
|
22
|
+
autoload :CodeLength, 'rubocop/cop/mixin/code_length'
|
|
23
|
+
autoload :ConfigurableEnforcedStyle, 'rubocop/cop/mixin/configurable_enforced_style'
|
|
24
|
+
autoload :ConfigurableFormatting, 'rubocop/cop/mixin/configurable_formatting'
|
|
25
|
+
autoload :ConfigurableNaming, 'rubocop/cop/mixin/configurable_naming'
|
|
26
|
+
autoload :ConfigurableNumbering, 'rubocop/cop/mixin/configurable_numbering'
|
|
27
|
+
autoload :DigHelp, 'rubocop/cop/mixin/dig_help'
|
|
28
|
+
autoload :DocumentationComment, 'rubocop/cop/mixin/documentation_comment'
|
|
29
|
+
autoload :Duplication, 'rubocop/cop/mixin/duplication'
|
|
30
|
+
autoload :RangeHelp, 'rubocop/cop/mixin/range_help'
|
|
31
|
+
autoload :AnnotationComment, 'rubocop/cop/mixin/annotation_comment'
|
|
32
|
+
autoload :EmptyParameter, 'rubocop/cop/mixin/empty_parameter'
|
|
33
|
+
autoload :EndKeywordAlignment, 'rubocop/cop/mixin/end_keyword_alignment'
|
|
34
|
+
autoload :EndlessMethodRewriter, 'rubocop/cop/mixin/endless_method_rewriter'
|
|
35
|
+
autoload :EnforceSuperclass, 'rubocop/cop/mixin/enforce_superclass'
|
|
36
|
+
autoload :FirstElementLineBreak, 'rubocop/cop/mixin/first_element_line_break'
|
|
37
|
+
autoload :FrozenStringLiteral, 'rubocop/cop/mixin/frozen_string_literal'
|
|
38
|
+
autoload :GemDeclaration, 'rubocop/cop/mixin/gem_declaration'
|
|
39
|
+
autoload :GemspecHelp, 'rubocop/cop/mixin/gemspec_help'
|
|
40
|
+
autoload :HashAlignmentStyles, 'rubocop/cop/mixin/hash_alignment_styles'
|
|
41
|
+
autoload :HashSubset, 'rubocop/cop/mixin/hash_subset'
|
|
42
|
+
autoload :HashTransformMethod, 'rubocop/cop/mixin/hash_transform_method'
|
|
43
|
+
autoload :IntegerNode, 'rubocop/cop/mixin/integer_node'
|
|
44
|
+
autoload :Interpolation, 'rubocop/cop/mixin/interpolation'
|
|
45
|
+
autoload :LineLengthHelp, 'rubocop/cop/mixin/line_length_help'
|
|
46
|
+
autoload :MatchRange, 'rubocop/cop/mixin/match_range'
|
|
47
|
+
autoload :HashShorthandSyntax, 'rubocop/cop/mixin/hash_shorthand_syntax'
|
|
48
|
+
autoload :MethodComplexity, 'rubocop/cop/mixin/method_complexity'
|
|
49
|
+
autoload :MethodPreference, 'rubocop/cop/mixin/method_preference'
|
|
50
|
+
autoload :MinBodyLength, 'rubocop/cop/mixin/min_body_length'
|
|
51
|
+
autoload :MinBranchesCount, 'rubocop/cop/mixin/min_branches_count'
|
|
52
|
+
autoload :MultilineElementIndentation, 'rubocop/cop/mixin/multiline_element_indentation'
|
|
53
|
+
autoload :MultilineElementLineBreaks, 'rubocop/cop/mixin/multiline_element_line_breaks'
|
|
54
|
+
autoload :MultilineExpressionIndentation, 'rubocop/cop/mixin/multiline_expression_indentation'
|
|
55
|
+
autoload :MultilineLiteralBraceLayout, 'rubocop/cop/mixin/multiline_literal_brace_layout'
|
|
56
|
+
autoload :NegativeConditional, 'rubocop/cop/mixin/negative_conditional'
|
|
57
|
+
autoload :Heredoc, 'rubocop/cop/mixin/heredoc'
|
|
58
|
+
autoload :NilMethods, 'rubocop/cop/mixin/nil_methods'
|
|
59
|
+
autoload :OnNormalIfUnless, 'rubocop/cop/mixin/on_normal_if_unless'
|
|
60
|
+
autoload :OrderedGemNode, 'rubocop/cop/mixin/ordered_gem_node'
|
|
61
|
+
autoload :Parentheses, 'rubocop/cop/mixin/parentheses'
|
|
62
|
+
autoload :PercentArray, 'rubocop/cop/mixin/percent_array'
|
|
63
|
+
autoload :PercentLiteral, 'rubocop/cop/mixin/percent_literal'
|
|
64
|
+
autoload :PrecedingFollowingAlignment, 'rubocop/cop/mixin/preceding_following_alignment'
|
|
65
|
+
autoload :PreferredDelimiters, 'rubocop/cop/mixin/preferred_delimiters'
|
|
66
|
+
autoload :RationalLiteral, 'rubocop/cop/mixin/rational_literal'
|
|
67
|
+
autoload :RequireLibrary, 'rubocop/cop/mixin/require_library'
|
|
68
|
+
autoload :RescueNode, 'rubocop/cop/mixin/rescue_node'
|
|
69
|
+
autoload :SafeAssignment, 'rubocop/cop/mixin/safe_assignment'
|
|
70
|
+
autoload :SpaceAfterPunctuation, 'rubocop/cop/mixin/space_after_punctuation'
|
|
71
|
+
autoload :SpaceBeforePunctuation, 'rubocop/cop/mixin/space_before_punctuation'
|
|
72
|
+
autoload :SurroundingSpace, 'rubocop/cop/mixin/surrounding_space'
|
|
73
|
+
autoload :StatementModifier, 'rubocop/cop/mixin/statement_modifier'
|
|
74
|
+
autoload :StringHelp, 'rubocop/cop/mixin/string_help'
|
|
75
|
+
autoload :StringLiteralsHelp, 'rubocop/cop/mixin/string_literals_help'
|
|
76
|
+
autoload :SymbolHelp, 'rubocop/cop/mixin/symbol_help'
|
|
77
|
+
autoload :TargetRubyVersion, 'rubocop/cop/mixin/target_ruby_version'
|
|
78
|
+
autoload :TrailingBody, 'rubocop/cop/mixin/trailing_body'
|
|
79
|
+
autoload :TrailingComma, 'rubocop/cop/mixin/trailing_comma'
|
|
80
|
+
autoload :UncommunicativeName, 'rubocop/cop/mixin/uncommunicative_name'
|
|
81
|
+
autoload :VisibilityHelp, 'rubocop/cop/mixin/visibility_help'
|
|
82
|
+
autoload :CommentsHelp, 'rubocop/cop/mixin/comments_help'
|
|
83
|
+
autoload :DefNode, 'rubocop/cop/mixin/def_node'
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -109,7 +109,7 @@ module RuboCop
|
|
|
109
109
|
# @_foo = calculate_expensive_thing
|
|
110
110
|
# end
|
|
111
111
|
#
|
|
112
|
-
# @example EnforcedStyleForLeadingUnderscores
|
|
112
|
+
# @example EnforcedStyleForLeadingUnderscores: optional
|
|
113
113
|
# # bad
|
|
114
114
|
# def foo
|
|
115
115
|
# @something ||= calculate_expensive_thing
|
data/lib/rubocop/cop/offense.rb
CHANGED
|
@@ -98,6 +98,14 @@ module RuboCop
|
|
|
98
98
|
freeze
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
def marshal_dump
|
|
102
|
+
[@severity, @location, @message, @cop_name, @status]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def marshal_load(array)
|
|
106
|
+
@severity, @location, @message, @cop_name, @status = array
|
|
107
|
+
end
|
|
108
|
+
|
|
101
109
|
# @api public
|
|
102
110
|
#
|
|
103
111
|
# @!attribute [r] correctable?
|
data/lib/rubocop/cop/registry.rb
CHANGED
|
@@ -49,9 +49,8 @@ module RuboCop
|
|
|
49
49
|
attr_reader :options, :warnings
|
|
50
50
|
|
|
51
51
|
def initialize(cops = [], options = {})
|
|
52
|
-
@
|
|
53
|
-
@
|
|
54
|
-
@cops_by_cop_name = Hash.new { |hash, key| hash[key] = [] }
|
|
52
|
+
@departments = Set.new
|
|
53
|
+
@cops_by_badge = {}
|
|
55
54
|
|
|
56
55
|
@enrollment_queue = cops
|
|
57
56
|
@options = options
|
|
@@ -72,22 +71,17 @@ module RuboCop
|
|
|
72
71
|
# @return [Array<Symbol>] list of departments for current cops.
|
|
73
72
|
def departments
|
|
74
73
|
clear_enrollment_queue
|
|
75
|
-
@departments.
|
|
74
|
+
@departments.to_a
|
|
76
75
|
end
|
|
77
76
|
|
|
78
77
|
# @return [Registry] Cops for that specific department.
|
|
79
78
|
def with_department(department)
|
|
80
|
-
|
|
81
|
-
with(@departments.fetch(department, []))
|
|
79
|
+
with(cops.select { |cop| cop.department == department })
|
|
82
80
|
end
|
|
83
81
|
|
|
84
82
|
# @return [Registry] Cops not for a specific department.
|
|
85
83
|
def without_department(department)
|
|
86
|
-
|
|
87
|
-
without_department = @departments.dup
|
|
88
|
-
without_department.delete(department)
|
|
89
|
-
|
|
90
|
-
with(without_department.values.flatten)
|
|
84
|
+
with(cops.reject { |cop| cop.department == department })
|
|
91
85
|
end
|
|
92
86
|
|
|
93
87
|
# @return [Boolean] Checks if given name is department
|
|
@@ -160,31 +154,31 @@ module RuboCop
|
|
|
160
154
|
def unqualified_cop_names
|
|
161
155
|
clear_enrollment_queue
|
|
162
156
|
@unqualified_cop_names ||=
|
|
163
|
-
Set.new(@
|
|
157
|
+
Set.new(@cops_by_badge.keys.map { |badge| File.basename(badge.to_s) }) <<
|
|
164
158
|
'RedundantCopDisableDirective'
|
|
165
159
|
end
|
|
166
160
|
|
|
167
161
|
def qualify_badge(badge)
|
|
168
162
|
clear_enrollment_queue
|
|
169
163
|
@departments
|
|
170
|
-
.map { |department
|
|
164
|
+
.map { |department| badge.with_department(department) }
|
|
171
165
|
.select { |potential_badge| registered?(potential_badge) }
|
|
172
166
|
end
|
|
173
167
|
|
|
174
168
|
# @return [Hash{String => Array<Class>}]
|
|
175
169
|
def to_h
|
|
176
170
|
clear_enrollment_queue
|
|
177
|
-
@
|
|
171
|
+
@cops_by_badge.to_h { |_badge, cop| [cop.cop_name, [cop]] }
|
|
178
172
|
end
|
|
179
173
|
|
|
180
174
|
def cops
|
|
181
175
|
clear_enrollment_queue
|
|
182
|
-
@
|
|
176
|
+
@cops_by_badge.values
|
|
183
177
|
end
|
|
184
178
|
|
|
185
179
|
def length
|
|
186
180
|
clear_enrollment_queue
|
|
187
|
-
@
|
|
181
|
+
@cops_by_badge.size
|
|
188
182
|
end
|
|
189
183
|
|
|
190
184
|
def enabled(config)
|
|
@@ -219,7 +213,8 @@ module RuboCop
|
|
|
219
213
|
end
|
|
220
214
|
|
|
221
215
|
def names
|
|
222
|
-
|
|
216
|
+
clear_enrollment_queue
|
|
217
|
+
@cops_by_badge.keys.map(&:to_s)
|
|
223
218
|
end
|
|
224
219
|
|
|
225
220
|
def cops_for_department(department)
|
|
@@ -236,7 +231,7 @@ module RuboCop
|
|
|
236
231
|
|
|
237
232
|
def sort!
|
|
238
233
|
clear_enrollment_queue
|
|
239
|
-
@
|
|
234
|
+
@cops_by_badge = @cops_by_badge.sort_by { |badge, _cop| badge.cop_name }.to_h
|
|
240
235
|
|
|
241
236
|
self
|
|
242
237
|
end
|
|
@@ -252,7 +247,9 @@ module RuboCop
|
|
|
252
247
|
# @param [String] cop_name
|
|
253
248
|
# @return [Class, nil]
|
|
254
249
|
def find_by_cop_name(cop_name)
|
|
255
|
-
|
|
250
|
+
clear_enrollment_queue
|
|
251
|
+
badge = Badge.parse(cop_name)
|
|
252
|
+
@cops_by_badge[badge]
|
|
256
253
|
end
|
|
257
254
|
|
|
258
255
|
# When a cop name is given returns a single-element array with the cop class.
|
|
@@ -289,10 +286,8 @@ module RuboCop
|
|
|
289
286
|
return if @enrollment_queue.empty?
|
|
290
287
|
|
|
291
288
|
@enrollment_queue.each do |cop|
|
|
292
|
-
@
|
|
293
|
-
@departments
|
|
294
|
-
@departments[cop.department] << cop
|
|
295
|
-
@cops_by_cop_name[cop.cop_name] << cop
|
|
289
|
+
@cops_by_badge[cop.badge] = cop
|
|
290
|
+
@departments << cop.department
|
|
296
291
|
end
|
|
297
292
|
@enrollment_queue = []
|
|
298
293
|
end
|
|
@@ -318,7 +313,7 @@ module RuboCop
|
|
|
318
313
|
|
|
319
314
|
def registered?(badge)
|
|
320
315
|
clear_enrollment_queue
|
|
321
|
-
@
|
|
316
|
+
@cops_by_badge.key?(badge)
|
|
322
317
|
end
|
|
323
318
|
end
|
|
324
319
|
end
|
|
@@ -348,8 +348,20 @@ module RuboCop
|
|
|
348
348
|
|
|
349
349
|
def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
|
|
350
350
|
def_node = begin_node.children[begin_node.children.index(modifier_node) + 1]
|
|
351
|
-
range
|
|
352
|
-
|
|
351
|
+
# Stop the removal range at the first comment that precedes the def, if
|
|
352
|
+
# any exist. Without this, comments between the modifier and the def are
|
|
353
|
+
# dropped because they fall inside the removed range.
|
|
354
|
+
end_pos = first_comment_or_node_start(def_node)
|
|
355
|
+
corrector.remove(modifier_node.source_range.begin.join(end_pos))
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def first_comment_or_node_start(node)
|
|
359
|
+
preceding = processed_source.ast_with_comments[node].select do |comment|
|
|
360
|
+
comment.loc.line < node.loc.line
|
|
361
|
+
end
|
|
362
|
+
return node.source_range.begin if preceding.empty?
|
|
363
|
+
|
|
364
|
+
preceding.first.source_range.begin
|
|
353
365
|
end
|
|
354
366
|
|
|
355
367
|
def def_source(node, def_nodes)
|
|
@@ -46,15 +46,16 @@ module RuboCop
|
|
|
46
46
|
token = insert_notice_before(processed_source)
|
|
47
47
|
range = token.nil? ? range_between(0, 0) : token.pos
|
|
48
48
|
|
|
49
|
-
corrector.insert_before(range, "#{
|
|
49
|
+
corrector.insert_before(range, "#{normalized_autocorrect_notice}\n")
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
def normalized_autocorrect_notice
|
|
53
|
+
autocorrect_notice.lines.map do |line|
|
|
54
|
+
next line if line.start_with?('#')
|
|
55
|
+
next "#\n" if line.chomp.empty?
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
"# #{line}"
|
|
58
|
+
end.join
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def verify_autocorrect_notice!
|
|
@@ -62,8 +63,7 @@ module RuboCop
|
|
|
62
63
|
raise Warning, "#{cop_name}: #{AUTOCORRECT_EMPTY_WARNING}"
|
|
63
64
|
end
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
return if autocorrect_notice.gsub(/^# */, '').match?(regex)
|
|
66
|
+
return if normalized_autocorrect_notice.gsub(/^# */, '').match?(notice_regexp)
|
|
67
67
|
|
|
68
68
|
message = "AutocorrectNotice '#{autocorrect_notice}' must match Notice /#{notice}/"
|
|
69
69
|
raise Warning, "#{cop_name}: #{message}"
|
|
@@ -91,18 +91,29 @@ module RuboCop
|
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
def notice_found?(processed_source)
|
|
94
|
-
notice_regexp = Regexp.new(notice.lines.map(&:strip).join)
|
|
95
94
|
multiline_notice = +''
|
|
96
95
|
processed_source.tokens.each do |token|
|
|
97
96
|
break unless token.comment?
|
|
98
97
|
|
|
99
|
-
multiline_notice << token.text.sub(/\A# */, '')
|
|
98
|
+
multiline_notice << token.text.sub(/\A# */, '') << "\n"
|
|
100
99
|
|
|
101
100
|
break if notice_regexp.match?(token.text)
|
|
102
101
|
end
|
|
103
102
|
|
|
104
103
|
multiline_notice.match?(notice_regexp)
|
|
105
104
|
end
|
|
105
|
+
|
|
106
|
+
def notice_regexp
|
|
107
|
+
@notice_regexp ||= Regexp.new(notice.sub(/\A(?:\\A|\^)?#(?:\\s[*+?]?|\s)*/, ''))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def notice
|
|
111
|
+
cop_config['Notice']
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def autocorrect_notice
|
|
115
|
+
cop_config['AutocorrectNotice']
|
|
116
|
+
end
|
|
106
117
|
end
|
|
107
118
|
end
|
|
108
119
|
end
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for consistent usage of the `
|
|
7
|
-
# `
|
|
6
|
+
# Checks for consistent usage of the `Time` class over the
|
|
7
|
+
# `DateTime` class. This cop is disabled by default since these classes,
|
|
8
8
|
# although highly overlapping, have particularities that make them not
|
|
9
9
|
# replaceable in certain situations when dealing with multiple timezones
|
|
10
10
|
# and/or DST.
|
|
@@ -161,7 +161,12 @@ module RuboCop
|
|
|
161
161
|
source = source.gsub(COMMENT_REGEXP, '')
|
|
162
162
|
return if source.blank?
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
# Treat `\#` (an escaped interpolation marker in the heredoc) as matching
|
|
165
|
+
# either `\#` or `#` in the comment, since the comment may show either
|
|
166
|
+
# the literal source form or the runtime appearance.
|
|
167
|
+
segments = source.strip.split('\\#', -1).map { |segment| Regexp.escape(segment) }
|
|
168
|
+
|
|
169
|
+
/\s*#{segments.join('\\\\?#')}/
|
|
165
170
|
end
|
|
166
171
|
end
|
|
167
172
|
end
|
|
@@ -224,15 +224,18 @@ module RuboCop
|
|
|
224
224
|
end
|
|
225
225
|
|
|
226
226
|
def find_heredoc_argument(node)
|
|
227
|
-
return unless node
|
|
227
|
+
return unless node
|
|
228
228
|
|
|
229
|
-
|
|
229
|
+
node = node.children.first while node.begin_type?
|
|
230
|
+
return node if heredoc?(node)
|
|
231
|
+
return unless node.call_type?
|
|
230
232
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
find_heredoc_argument(last_arg)
|
|
233
|
+
node.arguments.reverse_each do |argument|
|
|
234
|
+
heredoc_argument = find_heredoc_argument(argument)
|
|
235
|
+
return heredoc_argument if heredoc_argument
|
|
235
236
|
end
|
|
237
|
+
|
|
238
|
+
find_heredoc_argument(node.receiver)
|
|
236
239
|
end
|
|
237
240
|
|
|
238
241
|
def autocorrect_heredoc_argument(corrector, node, heredoc_node, leave_branch, guard)
|
|
@@ -82,18 +82,23 @@ module RuboCop
|
|
|
82
82
|
end
|
|
83
83
|
|
|
84
84
|
def correct_fetch_to_brackets(corrector, node)
|
|
85
|
-
receiver = node.receiver.source
|
|
86
85
|
key = node.first_argument.source
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
|
|
87
|
+
if node.csend_type?
|
|
88
|
+
corrector.replace(node, "(#{node.receiver.source}[#{key}])")
|
|
89
|
+
else
|
|
90
|
+
corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
|
|
91
|
+
end
|
|
90
92
|
end
|
|
91
93
|
|
|
92
94
|
def correct_brackets_to_fetch(corrector, node)
|
|
93
|
-
receiver = node.receiver.source
|
|
94
95
|
key = node.first_argument.source
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
|
|
97
|
+
if node.csend_type?
|
|
98
|
+
corrector.replace(node.loc.dot.join(node.source_range.end), "&.fetch(#{key})")
|
|
99
|
+
else
|
|
100
|
+
corrector.replace(node.loc.selector.join(node.source_range.end), ".fetch(#{key})")
|
|
101
|
+
end
|
|
97
102
|
end
|
|
98
103
|
end
|
|
99
104
|
end
|
|
@@ -64,7 +64,7 @@ module RuboCop
|
|
|
64
64
|
|
|
65
65
|
MSG = 'Convert `if` nested inside `else` to `elsif`.'
|
|
66
66
|
|
|
67
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
67
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
68
68
|
def on_if(node)
|
|
69
69
|
return if node.ternary? || node.unless?
|
|
70
70
|
|
|
@@ -72,6 +72,7 @@ module RuboCop
|
|
|
72
72
|
|
|
73
73
|
return unless else_branch&.if_type? && else_branch.if?
|
|
74
74
|
return if allow_if_modifier_in_else_branch?(else_branch)
|
|
75
|
+
return if comments_between_else_and_if?(node, else_branch)
|
|
75
76
|
|
|
76
77
|
add_offense(else_branch.loc.keyword) do |corrector|
|
|
77
78
|
next if part_of_ignored_node?(node)
|
|
@@ -80,12 +81,12 @@ module RuboCop
|
|
|
80
81
|
ignore_node(node)
|
|
81
82
|
end
|
|
82
83
|
end
|
|
83
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
84
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
84
85
|
|
|
85
86
|
private
|
|
86
87
|
|
|
87
88
|
def autocorrect(corrector, node)
|
|
88
|
-
if then?
|
|
89
|
+
if node.then?
|
|
89
90
|
# If the nested `if` is a then node, correct it first,
|
|
90
91
|
# then the next pass will use `correct_to_elsif_from_if_inside_else_form`
|
|
91
92
|
IfThenCorrector.new(node, indentation: 0).call(corrector)
|
|
@@ -124,10 +125,6 @@ module RuboCop
|
|
|
124
125
|
corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
|
|
125
126
|
end
|
|
126
127
|
|
|
127
|
-
def then?(node)
|
|
128
|
-
node.loc.begin&.source == 'then'
|
|
129
|
-
end
|
|
130
|
-
|
|
131
128
|
def find_end_range(node)
|
|
132
129
|
end_range = node.loc.end
|
|
133
130
|
return end_range if end_range
|
|
@@ -139,6 +136,18 @@ module RuboCop
|
|
|
139
136
|
range_between(node.loc.keyword.begin_pos, condition.source_range.end_pos)
|
|
140
137
|
end
|
|
141
138
|
|
|
139
|
+
def comments_between_else_and_if?(node, else_branch)
|
|
140
|
+
return false if else_branch.modifier_form?
|
|
141
|
+
|
|
142
|
+
else_end = node.loc.else.end_pos
|
|
143
|
+
if_begin = else_branch.loc.keyword.begin_pos
|
|
144
|
+
|
|
145
|
+
processed_source.comments.any? do |comment|
|
|
146
|
+
comment_pos = comment.source_range.begin_pos
|
|
147
|
+
comment_pos > else_end && comment_pos < if_begin
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
142
151
|
def allow_if_modifier_in_else_branch?(else_branch)
|
|
143
152
|
allow_if_modifier? && else_branch&.modifier_form?
|
|
144
153
|
end
|