rubocop 1.88.2 → 1.89.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/config/default.yml +67 -11
- data/lib/rubocop/comment_config.rb +35 -19
- data/lib/rubocop/config.rb +53 -0
- data/lib/rubocop/config_validator.rb +38 -1
- data/lib/rubocop/cop/badge.rb +8 -0
- data/lib/rubocop/cop/base.rb +6 -4
- data/lib/rubocop/cop/bundler.rb +19 -0
- data/lib/rubocop/cop/corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
- data/lib/rubocop/cop/gemspec.rb +22 -0
- data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
- data/lib/rubocop/cop/generator.rb +4 -0
- data/lib/rubocop/cop/layout/class_structure.rb +8 -2
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
- data/lib/rubocop/cop/layout/line_length.rb +85 -12
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +33 -34
- data/lib/rubocop/cop/layout.rb +114 -0
- data/lib/rubocop/cop/lazy_loader.rb +41 -0
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +1 -10
- data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
- data/lib/rubocop/cop/lint/deprecated_reference.rb +185 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +202 -22
- data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_super.rb +31 -1
- data/lib/rubocop/cop/lint/name_typo.rb +223 -0
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +0 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +37 -1
- data/lib/rubocop/cop/lint/unused_private_method.rb +157 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
- data/lib/rubocop/cop/lint/void.rb +17 -6
- data/lib/rubocop/cop/lint.rb +176 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics.rb +31 -0
- data/lib/rubocop/cop/migration/department_name.rb +0 -4
- data/lib/rubocop/cop/migration.rb +13 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
- data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +42 -6
- data/lib/rubocop/cop/mixin.rb +78 -77
- data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
- data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/naming.rb +31 -0
- data/lib/rubocop/cop/offense.rb +10 -6
- data/lib/rubocop/cop/registry.rb +153 -43
- data/lib/rubocop/cop/security.rb +19 -0
- data/lib/rubocop/cop/style/array_intersect.rb +24 -5
- data/lib/rubocop/cop/style/class_and_module_children.rb +78 -18
- data/lib/rubocop/cop/style/conditional_assignment.rb +22 -0
- data/lib/rubocop/cop/style/copyright.rb +6 -1
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +41 -3
- data/lib/rubocop/cop/style/documentation.rb +47 -0
- data/lib/rubocop/cop/style/endless_method.rb +0 -10
- data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -48
- data/lib/rubocop/cop/style/lambda.rb +4 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -3
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
- data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
- data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
- data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
- data/lib/rubocop/cop/style/negative_array_index.rb +3 -1
- data/lib/rubocop/cop/style/redundant_assignment.rb +11 -0
- data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
- data/lib/rubocop/cop/style/redundant_format.rb +12 -3
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
- data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -63
- data/lib/rubocop/cop/style/safe_navigation.rb +10 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +20 -3
- data/lib/rubocop/cop/style/static_class.rb +21 -0
- data/lib/rubocop/cop/style.rb +310 -0
- data/lib/rubocop/cop/team.rb +48 -3
- data/lib/rubocop/cop/variable_force/variable.rb +18 -6
- data/lib/rubocop/cops_documentation_generator.rb +1 -1
- data/lib/rubocop/directive_comment.rb +2 -0
- data/lib/rubocop/feature_loader.rb +0 -6
- data/lib/rubocop/lsp/routes.rb +41 -3
- data/lib/rubocop/lsp/runtime.rb +4 -0
- data/lib/rubocop/lsp/server.rb +4 -0
- data/lib/rubocop/lsp/stdin_runner.rb +20 -0
- data/lib/rubocop/pending_cops_reporter.rb +1 -6
- data/lib/rubocop/project_index_loader.rb +16 -0
- data/lib/rubocop/result_cache.rb +6 -4
- data/lib/rubocop/rspec/shared_contexts.rb +14 -2
- data/lib/rubocop/runner.rb +45 -20
- data/lib/rubocop/server/cache.rb +10 -5
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +17 -626
- metadata +18 -3
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Verifies that a candidate correction of the inspected source is a
|
|
6
|
+
# syntactic no-op: the corrected source must parse successfully and produce
|
|
7
|
+
# the same AST as the original.
|
|
8
|
+
#
|
|
9
|
+
# This turns "is this piece of syntax redundant?" questions into a
|
|
10
|
+
# parser-backed check instead of a hand-maintained enumeration of grammar
|
|
11
|
+
# rules: instead of modeling which constructs make (for example) a line
|
|
12
|
+
# continuation or a pair of parentheses significant, a cop can apply the
|
|
13
|
+
# correction and let the parser answer.
|
|
14
|
+
#
|
|
15
|
+
# Cops implement `#apply_reparse_correction(corrector, item)` and call
|
|
16
|
+
# `verified_by_reparse(items)`, which returns the items whose corrections
|
|
17
|
+
# are verified. Items sharing a reparse scope (the innermost method
|
|
18
|
+
# definition or class/module body, which parse standalone and cannot
|
|
19
|
+
# capture outer local variables) are verified together with a single
|
|
20
|
+
# reparse, falling back to per-item verification when the batch does not
|
|
21
|
+
# hold. Scope groups are keyed by node identity, since structurally
|
|
22
|
+
# identical definitions in different places must not share a group.
|
|
23
|
+
#
|
|
24
|
+
# Two hooks customize the comparison:
|
|
25
|
+
#
|
|
26
|
+
# * `normalize_reparsed_ast(node)` - loosen strict tree equality where a
|
|
27
|
+
# cop's correction is equivalence-preserving beyond parse identity (such
|
|
28
|
+
# laws must be justified per cop; the default compares trees as-is).
|
|
29
|
+
# * `preprocess_reparsed_source(source)` - rewrite both sides before
|
|
30
|
+
# parsing (e.g. to neutralize `__LINE__` when a correction legitimately
|
|
31
|
+
# shifts line numbers).
|
|
32
|
+
#
|
|
33
|
+
# `__FILE__` is handled by parsing with the original path, and fragments
|
|
34
|
+
# containing `__LINE__` are verified by reparsing both sides so that
|
|
35
|
+
# fragment line offsets cancel out.
|
|
36
|
+
#
|
|
37
|
+
# Note that comments are invisible to the AST, so corrections that may
|
|
38
|
+
# touch comment text must be guarded separately by the caller.
|
|
39
|
+
module ReparsedEquivalence
|
|
40
|
+
# Above this size, verification does not reparse. What happens to such
|
|
41
|
+
# items depends on `oversized`; in practice only machine-generated files
|
|
42
|
+
# come near the limit.
|
|
43
|
+
MAX_VERIFICATION_FRAGMENT_SIZE = 64 * 1024
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Returns the items whose corrections are verified. `oversized` controls
|
|
48
|
+
# items whose reparse scope exceeds `MAX_VERIFICATION_FRAGMENT_SIZE`:
|
|
49
|
+
# `:report` accepts them unverified (for cops whose offense logic stands
|
|
50
|
+
# on its own and uses verification as a safety gate), `:verify` reparses
|
|
51
|
+
# regardless of size (for cops where verification is the offense logic).
|
|
52
|
+
def verified_by_reparse(items, oversized: :report)
|
|
53
|
+
scope_groups(items).flat_map do |scope, group|
|
|
54
|
+
if (oversized == :report && verification_too_large?(scope)) ||
|
|
55
|
+
(group.size > 1 && corrections_verify?(scope, group))
|
|
56
|
+
group
|
|
57
|
+
else
|
|
58
|
+
group.select { |item| corrections_verify?(scope, [item]) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Whether the exact correction for `item` produces source that still
|
|
64
|
+
# parses, without requiring an equivalent AST. For corrections that
|
|
65
|
+
# intentionally change the tree.
|
|
66
|
+
def correction_parses?(item)
|
|
67
|
+
corrector = Corrector.new(processed_source)
|
|
68
|
+
apply_reparse_correction(corrector, item)
|
|
69
|
+
|
|
70
|
+
parse(corrector.process, processed_source.path).valid_syntax?
|
|
71
|
+
rescue ::Parser::ClobberingError
|
|
72
|
+
false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Hook: loosen strict tree equality for corrections that are
|
|
76
|
+
# equivalence-preserving beyond parse identity.
|
|
77
|
+
def normalize_reparsed_ast(node)
|
|
78
|
+
node
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Hook: rewrite both sides before parsing.
|
|
82
|
+
def preprocess_reparsed_source(source)
|
|
83
|
+
source
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def scope_groups(items)
|
|
87
|
+
groups = {}.compare_by_identity
|
|
88
|
+
items.each { |item| (groups[reparse_scope(item_range(item))] ||= []) << item }
|
|
89
|
+
|
|
90
|
+
groups
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def item_range(item)
|
|
94
|
+
item.respond_to?(:source_range) ? item.source_range : item
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The innermost scope that both contains `range` and parses standalone.
|
|
98
|
+
# Method definitions and class/module bodies neither capture outer local
|
|
99
|
+
# variables nor continue an outer expression; blocks and single
|
|
100
|
+
# statements do not qualify, since an outer local would reparse as a
|
|
101
|
+
# method call.
|
|
102
|
+
def reparse_scope(range)
|
|
103
|
+
node = processed_source.ast
|
|
104
|
+
scope = nil
|
|
105
|
+
|
|
106
|
+
while node.is_a?(::Parser::AST::Node)
|
|
107
|
+
scope = node if node.type?(:any_def, :class, :module, :sclass)
|
|
108
|
+
node = node.children.find do |child|
|
|
109
|
+
child.is_a?(::Parser::AST::Node) && child.source_range&.contains?(range)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
scope
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def verification_too_large?(scope)
|
|
117
|
+
(scope ? scope.source_range.size : processed_source.raw_source.size) >
|
|
118
|
+
MAX_VERIFICATION_FRAGMENT_SIZE
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def corrections_verify?(scope, items)
|
|
122
|
+
corrector = Corrector.new(processed_source)
|
|
123
|
+
items.each { |item| apply_reparse_correction(corrector, item) }
|
|
124
|
+
corrected = corrector.process
|
|
125
|
+
|
|
126
|
+
if scope
|
|
127
|
+
fragment = corrected_scope_fragment(scope, corrected)
|
|
128
|
+
parses_equivalently?(scope.source, scope, fragment)
|
|
129
|
+
else
|
|
130
|
+
parses_equivalently?(processed_source.raw_source, processed_source.ast, corrected)
|
|
131
|
+
end
|
|
132
|
+
rescue ::Parser::ClobberingError
|
|
133
|
+
false
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# The corrections' edits are all contained within the scope, so the
|
|
137
|
+
# corrected fragment can be cut out of the corrected source by adjusting
|
|
138
|
+
# for the edits' length delta.
|
|
139
|
+
def corrected_scope_fragment(scope, corrected)
|
|
140
|
+
delta = corrected.length - processed_source.raw_source.length
|
|
141
|
+
scope_range = scope.source_range
|
|
142
|
+
|
|
143
|
+
corrected[scope_range.begin_pos...(scope_range.end_pos + delta)]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Both sides are parsed with the original path so that `__FILE__`
|
|
147
|
+
# resolves identically. The original side is reparsed (instead of using
|
|
148
|
+
# the already parsed node) when preprocessing altered it or it contains
|
|
149
|
+
# `__LINE__`, whose values would otherwise differ from a fragment parsed
|
|
150
|
+
# at a different line offset.
|
|
151
|
+
def parses_equivalently?(original_source, original_ast, corrected_source)
|
|
152
|
+
original_normalized = normalized_original(original_source, original_ast)
|
|
153
|
+
return false unless original_normalized
|
|
154
|
+
|
|
155
|
+
rewritten = parse(preprocess_reparsed_source(corrected_source), processed_source.path)
|
|
156
|
+
rewritten.valid_syntax? && normalize_reparsed_ast(rewritten.ast) == original_normalized
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def normalized_original(original_source, original_ast)
|
|
160
|
+
original = preprocess_reparsed_source(original_source)
|
|
161
|
+
if original != original_source || original_source.include?('__LINE__')
|
|
162
|
+
reparsed = parse(original, processed_source.path).ast
|
|
163
|
+
reparsed && normalize_reparsed_ast(reparsed)
|
|
164
|
+
else
|
|
165
|
+
@normalized_originals ||= {}.compare_by_identity
|
|
166
|
+
@normalized_originals[original_ast] ||= normalize_reparsed_ast(original_ast)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -5,6 +5,7 @@ module RuboCop
|
|
|
5
5
|
# Common functionality for modifier cops.
|
|
6
6
|
module StatementModifier
|
|
7
7
|
include LineLengthHelp
|
|
8
|
+
include AllowedPattern
|
|
8
9
|
|
|
9
10
|
private
|
|
10
11
|
|
|
@@ -35,16 +36,51 @@ module RuboCop
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def modifier_fits_on_single_line?(node)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
length_in_modifier_form(node) <= max_line_length
|
|
39
|
+
acceptable_line_length?(line_in_modifier_form(node), node.first_line)
|
|
41
40
|
end
|
|
42
41
|
|
|
43
|
-
def
|
|
42
|
+
def line_in_modifier_form(node)
|
|
44
43
|
keyword_element = node.loc.keyword
|
|
45
44
|
code_before = keyword_element.source_line[0...keyword_element.column]
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
|
|
46
|
+
"#{code_before}#{to_modifier_form(node)}#{code_after(node)}"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Rather than comparing the rendered modifier form against the bare
|
|
50
|
+
# maximum, ask the question `Layout/LineLength` would: a line the user's
|
|
51
|
+
# configuration exempts (`Layout/LineLength` disabled at that line, an
|
|
52
|
+
# allowed pattern, an allowed cop directive, an allowed URI) fits by
|
|
53
|
+
# definition. This keeps every modifier cop consistent with
|
|
54
|
+
# `Layout/LineLength` instead of each reimplementing part of its policy.
|
|
55
|
+
def acceptable_line_length?(line, line_number)
|
|
56
|
+
return true unless max_line_length
|
|
57
|
+
return true if line_length(line) <= max_line_length
|
|
58
|
+
return true unless line_length_enabled_at_line?(line_number)
|
|
59
|
+
return true if matches_allowed_pattern?(line)
|
|
60
|
+
|
|
61
|
+
if allow_cop_directives? && directive_on_source_line?(line_number - 1)
|
|
62
|
+
return line_length_without_directive(line) <= max_line_length
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
allowed_by_uri?(line)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def allowed_by_uri?(line)
|
|
69
|
+
return false unless allow_uri?
|
|
70
|
+
|
|
71
|
+
uri_range = find_excessive_range(line, :uri)
|
|
72
|
+
!uri_range.nil? && allowed_position?(line, uri_range)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def line_length_enabled_at_line?(line)
|
|
76
|
+
processed_source.comment_config.cop_enabled_at_line?('Layout/LineLength', line)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# `Layout/LineLength`'s allowed patterns, so a modifier line that the
|
|
80
|
+
# user has configured that cop to accept is not flagged for length here.
|
|
81
|
+
def allowed_patterns
|
|
82
|
+
line_length_config = config.for_cop('Layout/LineLength')
|
|
83
|
+
line_length_config['AllowedPatterns'] || line_length_config['IgnoredPatterns'] || []
|
|
48
84
|
end
|
|
49
85
|
|
|
50
86
|
def to_modifier_form(node)
|
data/lib/rubocop/cop/mixin.rb
CHANGED
|
@@ -5,82 +5,83 @@ module RuboCop
|
|
|
5
5
|
# Autoloads mixin modules included by cops. Mixins are autoloaded to reduce the number of
|
|
6
6
|
# requires because they're used only when the relevant cop class is loaded.
|
|
7
7
|
|
|
8
|
-
autoload :ArrayMinSize,
|
|
9
|
-
autoload :ArraySyntax,
|
|
10
|
-
autoload :Alignment,
|
|
11
|
-
autoload :AllowedIdentifiers,
|
|
12
|
-
autoload :AllowedMethods,
|
|
13
|
-
autoload :AllowedPattern,
|
|
14
|
-
autoload :AllowedReceivers,
|
|
15
|
-
autoload :ForbiddenIdentifiers,
|
|
16
|
-
autoload :ForbiddenPattern,
|
|
17
|
-
autoload :AutoCorrector,
|
|
18
|
-
autoload :CheckAssignment,
|
|
19
|
-
autoload :CheckLineBreakable,
|
|
20
|
-
autoload :CheckSingleLineSuitability,
|
|
21
|
-
autoload :ConfigurableMax,
|
|
22
|
-
autoload :CodeLength,
|
|
23
|
-
autoload :ConfigurableEnforcedStyle,
|
|
24
|
-
autoload :ConfigurableFormatting,
|
|
25
|
-
autoload :ConfigurableNaming,
|
|
26
|
-
autoload :ConfigurableNumbering,
|
|
27
|
-
autoload :DigHelp,
|
|
28
|
-
autoload :DocumentationComment,
|
|
29
|
-
autoload :Duplication,
|
|
30
|
-
autoload :RangeHelp,
|
|
31
|
-
autoload :ProjectIndexHelp,
|
|
32
|
-
autoload :AnnotationComment,
|
|
33
|
-
autoload :EmptyParameter,
|
|
34
|
-
autoload :EndKeywordAlignment,
|
|
35
|
-
autoload :EndlessMethodRewriter,
|
|
36
|
-
autoload :EnforceSuperclass,
|
|
37
|
-
autoload :FirstElementLineBreak,
|
|
38
|
-
autoload :FrozenStringLiteral,
|
|
39
|
-
autoload :GemDeclaration,
|
|
40
|
-
autoload :GemspecHelp,
|
|
41
|
-
autoload :HashAlignmentStyles,
|
|
42
|
-
autoload :HashSubset,
|
|
43
|
-
autoload :HashTransformMethod,
|
|
44
|
-
autoload :IntegerNode,
|
|
45
|
-
autoload :Interpolation,
|
|
46
|
-
autoload :LineLengthHelp,
|
|
47
|
-
autoload :MatchRange,
|
|
48
|
-
autoload :HashShorthandSyntax,
|
|
49
|
-
autoload :MethodComplexity,
|
|
50
|
-
autoload :MethodPreference,
|
|
51
|
-
autoload :MinBodyLength,
|
|
52
|
-
autoload :MinBranchesCount,
|
|
53
|
-
autoload :MultilineElementIndentation,
|
|
54
|
-
autoload :MultilineElementLineBreaks,
|
|
55
|
-
autoload :MultilineExpressionIndentation,
|
|
56
|
-
autoload :MultilineLiteralBraceLayout,
|
|
57
|
-
autoload :NegativeConditional,
|
|
58
|
-
autoload :Heredoc,
|
|
59
|
-
autoload :NilMethods,
|
|
60
|
-
autoload :OnNormalIfUnless,
|
|
61
|
-
autoload :OrderedGemNode,
|
|
62
|
-
autoload :Parentheses,
|
|
63
|
-
autoload :PercentArray,
|
|
64
|
-
autoload :PercentLiteral,
|
|
65
|
-
autoload :PrecedingFollowingAlignment,
|
|
66
|
-
autoload :PreferredDelimiters,
|
|
67
|
-
autoload :RationalLiteral,
|
|
68
|
-
autoload :
|
|
69
|
-
autoload :
|
|
70
|
-
autoload :
|
|
71
|
-
autoload :
|
|
72
|
-
autoload :
|
|
73
|
-
autoload :
|
|
74
|
-
autoload :
|
|
75
|
-
autoload :
|
|
76
|
-
autoload :
|
|
77
|
-
autoload :
|
|
78
|
-
autoload :
|
|
79
|
-
autoload :
|
|
80
|
-
autoload :
|
|
81
|
-
autoload :
|
|
82
|
-
autoload :
|
|
83
|
-
autoload :
|
|
84
|
-
autoload :
|
|
8
|
+
autoload :ArrayMinSize, "#{__dir__}/mixin/array_min_size"
|
|
9
|
+
autoload :ArraySyntax, "#{__dir__}/mixin/array_syntax"
|
|
10
|
+
autoload :Alignment, "#{__dir__}/mixin/alignment"
|
|
11
|
+
autoload :AllowedIdentifiers, "#{__dir__}/mixin/allowed_identifiers"
|
|
12
|
+
autoload :AllowedMethods, "#{__dir__}/mixin/allowed_methods"
|
|
13
|
+
autoload :AllowedPattern, "#{__dir__}/mixin/allowed_pattern"
|
|
14
|
+
autoload :AllowedReceivers, "#{__dir__}/mixin/allowed_receivers"
|
|
15
|
+
autoload :ForbiddenIdentifiers, "#{__dir__}/mixin/forbidden_identifiers"
|
|
16
|
+
autoload :ForbiddenPattern, "#{__dir__}/mixin/forbidden_pattern"
|
|
17
|
+
autoload :AutoCorrector, "#{__dir__}/mixin/auto_corrector" # rubocop:todo Naming/InclusiveLanguage
|
|
18
|
+
autoload :CheckAssignment, "#{__dir__}/mixin/check_assignment"
|
|
19
|
+
autoload :CheckLineBreakable, "#{__dir__}/mixin/check_line_breakable"
|
|
20
|
+
autoload :CheckSingleLineSuitability, "#{__dir__}/mixin/check_single_line_suitability"
|
|
21
|
+
autoload :ConfigurableMax, "#{__dir__}/mixin/configurable_max"
|
|
22
|
+
autoload :CodeLength, "#{__dir__}/mixin/code_length"
|
|
23
|
+
autoload :ConfigurableEnforcedStyle, "#{__dir__}/mixin/configurable_enforced_style"
|
|
24
|
+
autoload :ConfigurableFormatting, "#{__dir__}/mixin/configurable_formatting"
|
|
25
|
+
autoload :ConfigurableNaming, "#{__dir__}/mixin/configurable_naming"
|
|
26
|
+
autoload :ConfigurableNumbering, "#{__dir__}/mixin/configurable_numbering"
|
|
27
|
+
autoload :DigHelp, "#{__dir__}/mixin/dig_help"
|
|
28
|
+
autoload :DocumentationComment, "#{__dir__}/mixin/documentation_comment"
|
|
29
|
+
autoload :Duplication, "#{__dir__}/mixin/duplication"
|
|
30
|
+
autoload :RangeHelp, "#{__dir__}/mixin/range_help"
|
|
31
|
+
autoload :ProjectIndexHelp, "#{__dir__}/mixin/project_index_help"
|
|
32
|
+
autoload :AnnotationComment, "#{__dir__}/mixin/annotation_comment"
|
|
33
|
+
autoload :EmptyParameter, "#{__dir__}/mixin/empty_parameter"
|
|
34
|
+
autoload :EndKeywordAlignment, "#{__dir__}/mixin/end_keyword_alignment"
|
|
35
|
+
autoload :EndlessMethodRewriter, "#{__dir__}/mixin/endless_method_rewriter"
|
|
36
|
+
autoload :EnforceSuperclass, "#{__dir__}/mixin/enforce_superclass"
|
|
37
|
+
autoload :FirstElementLineBreak, "#{__dir__}/mixin/first_element_line_break"
|
|
38
|
+
autoload :FrozenStringLiteral, "#{__dir__}/mixin/frozen_string_literal"
|
|
39
|
+
autoload :GemDeclaration, "#{__dir__}/mixin/gem_declaration"
|
|
40
|
+
autoload :GemspecHelp, "#{__dir__}/mixin/gemspec_help"
|
|
41
|
+
autoload :HashAlignmentStyles, "#{__dir__}/mixin/hash_alignment_styles"
|
|
42
|
+
autoload :HashSubset, "#{__dir__}/mixin/hash_subset"
|
|
43
|
+
autoload :HashTransformMethod, "#{__dir__}/mixin/hash_transform_method"
|
|
44
|
+
autoload :IntegerNode, "#{__dir__}/mixin/integer_node"
|
|
45
|
+
autoload :Interpolation, "#{__dir__}/mixin/interpolation"
|
|
46
|
+
autoload :LineLengthHelp, "#{__dir__}/mixin/line_length_help"
|
|
47
|
+
autoload :MatchRange, "#{__dir__}/mixin/match_range"
|
|
48
|
+
autoload :HashShorthandSyntax, "#{__dir__}/mixin/hash_shorthand_syntax"
|
|
49
|
+
autoload :MethodComplexity, "#{__dir__}/mixin/method_complexity"
|
|
50
|
+
autoload :MethodPreference, "#{__dir__}/mixin/method_preference"
|
|
51
|
+
autoload :MinBodyLength, "#{__dir__}/mixin/min_body_length"
|
|
52
|
+
autoload :MinBranchesCount, "#{__dir__}/mixin/min_branches_count"
|
|
53
|
+
autoload :MultilineElementIndentation, "#{__dir__}/mixin/multiline_element_indentation"
|
|
54
|
+
autoload :MultilineElementLineBreaks, "#{__dir__}/mixin/multiline_element_line_breaks"
|
|
55
|
+
autoload :MultilineExpressionIndentation, "#{__dir__}/mixin/multiline_expression_indentation"
|
|
56
|
+
autoload :MultilineLiteralBraceLayout, "#{__dir__}/mixin/multiline_literal_brace_layout"
|
|
57
|
+
autoload :NegativeConditional, "#{__dir__}/mixin/negative_conditional"
|
|
58
|
+
autoload :Heredoc, "#{__dir__}/mixin/heredoc"
|
|
59
|
+
autoload :NilMethods, "#{__dir__}/mixin/nil_methods"
|
|
60
|
+
autoload :OnNormalIfUnless, "#{__dir__}/mixin/on_normal_if_unless"
|
|
61
|
+
autoload :OrderedGemNode, "#{__dir__}/mixin/ordered_gem_node"
|
|
62
|
+
autoload :Parentheses, "#{__dir__}/mixin/parentheses"
|
|
63
|
+
autoload :PercentArray, "#{__dir__}/mixin/percent_array"
|
|
64
|
+
autoload :PercentLiteral, "#{__dir__}/mixin/percent_literal"
|
|
65
|
+
autoload :PrecedingFollowingAlignment, "#{__dir__}/mixin/preceding_following_alignment"
|
|
66
|
+
autoload :PreferredDelimiters, "#{__dir__}/mixin/preferred_delimiters"
|
|
67
|
+
autoload :RationalLiteral, "#{__dir__}/mixin/rational_literal"
|
|
68
|
+
autoload :ReparsedEquivalence, "#{__dir__}/mixin/reparsed_equivalence"
|
|
69
|
+
autoload :RequireLibrary, "#{__dir__}/mixin/require_library"
|
|
70
|
+
autoload :RescueNode, "#{__dir__}/mixin/rescue_node"
|
|
71
|
+
autoload :SafeAssignment, "#{__dir__}/mixin/safe_assignment"
|
|
72
|
+
autoload :SpaceAfterPunctuation, "#{__dir__}/mixin/space_after_punctuation"
|
|
73
|
+
autoload :SpaceBeforePunctuation, "#{__dir__}/mixin/space_before_punctuation"
|
|
74
|
+
autoload :SurroundingSpace, "#{__dir__}/mixin/surrounding_space"
|
|
75
|
+
autoload :StatementModifier, "#{__dir__}/mixin/statement_modifier"
|
|
76
|
+
autoload :StringHelp, "#{__dir__}/mixin/string_help"
|
|
77
|
+
autoload :StringLiteralsHelp, "#{__dir__}/mixin/string_literals_help"
|
|
78
|
+
autoload :SymbolHelp, "#{__dir__}/mixin/symbol_help"
|
|
79
|
+
autoload :TargetRubyVersion, "#{__dir__}/mixin/target_ruby_version"
|
|
80
|
+
autoload :TrailingBody, "#{__dir__}/mixin/trailing_body"
|
|
81
|
+
autoload :TrailingComma, "#{__dir__}/mixin/trailing_comma"
|
|
82
|
+
autoload :UncommunicativeName, "#{__dir__}/mixin/uncommunicative_name"
|
|
83
|
+
autoload :VisibilityHelp, "#{__dir__}/mixin/visibility_help"
|
|
84
|
+
autoload :CommentsHelp, "#{__dir__}/mixin/comments_help"
|
|
85
|
+
autoload :DefNode, "#{__dir__}/mixin/def_node"
|
|
85
86
|
end
|
|
86
87
|
end
|
|
@@ -11,6 +11,11 @@ module RuboCop
|
|
|
11
11
|
# Getters (`get_attribute`) must have no arguments to be registered,
|
|
12
12
|
# and setters (`set_attribute(value)`) must have exactly one.
|
|
13
13
|
#
|
|
14
|
+
# When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is
|
|
15
|
+
# installed, methods that override a method defined by an ancestor
|
|
16
|
+
# elsewhere in the project are not reported, since renaming an override
|
|
17
|
+
# breaks the inherited contract.
|
|
18
|
+
#
|
|
14
19
|
# @example
|
|
15
20
|
# # bad
|
|
16
21
|
# def set_attribute(value)
|
|
@@ -36,12 +41,15 @@ module RuboCop
|
|
|
36
41
|
# def set_value
|
|
37
42
|
# end
|
|
38
43
|
class AccessorMethodName < Base
|
|
44
|
+
include ProjectIndexHelp
|
|
45
|
+
|
|
39
46
|
MSG_READER = 'Do not prefix reader method names with `get_`.'
|
|
40
47
|
MSG_WRITER = 'Do not prefix writer method names with `set_`.'
|
|
41
48
|
|
|
42
49
|
def on_def(node)
|
|
43
50
|
return unless proper_attribute_name?(node)
|
|
44
51
|
return unless bad_reader_name?(node) || bad_writer_name?(node)
|
|
52
|
+
return if overrides_inherited_method?(node)
|
|
45
53
|
|
|
46
54
|
message = message(node)
|
|
47
55
|
|
|
@@ -72,6 +80,22 @@ module RuboCop
|
|
|
72
80
|
node.arguments.one? &&
|
|
73
81
|
node.first_argument.arg_type?
|
|
74
82
|
end
|
|
83
|
+
|
|
84
|
+
# When `AllCops/UseProjectIndex` is enabled, methods that override a
|
|
85
|
+
# method defined by an ancestor elsewhere in the project are not
|
|
86
|
+
# reported: renaming an override breaks the inherited contract.
|
|
87
|
+
def overrides_inherited_method?(node)
|
|
88
|
+
return false unless project_index
|
|
89
|
+
return false unless (namespace_node = node.each_ancestor(:class, :module).first)
|
|
90
|
+
|
|
91
|
+
declaration = resolve_constant_in_index(namespace_node.identifier)
|
|
92
|
+
return false unless declaration.is_a?(Rubydex::Namespace)
|
|
93
|
+
|
|
94
|
+
scope = node.defs_type? ? indexed_singleton_of(declaration) : declaration
|
|
95
|
+
!scope.nil? && inherited_index_member?(scope, "#{node.method_name}()")
|
|
96
|
+
rescue StandardError
|
|
97
|
+
false
|
|
98
|
+
end
|
|
75
99
|
end
|
|
76
100
|
end
|
|
77
101
|
end
|
|
@@ -21,6 +21,11 @@ module RuboCop
|
|
|
21
21
|
# offenses if the method has a Sorbet `sig` with a return type of
|
|
22
22
|
# `T::Boolean`. Dynamic methods are not supported with this configuration.
|
|
23
23
|
#
|
|
24
|
+
# When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is
|
|
25
|
+
# installed, methods that override a method defined by an ancestor
|
|
26
|
+
# elsewhere in the project are not reported, since renaming an override
|
|
27
|
+
# breaks the inherited contract.
|
|
28
|
+
#
|
|
24
29
|
# @example NamePrefix: ['is_', 'has_', 'have_'] (default)
|
|
25
30
|
# # bad
|
|
26
31
|
# def is_even(value)
|
|
@@ -101,6 +106,7 @@ module RuboCop
|
|
|
101
106
|
# def_node_matcher(:even?) { |value| }
|
|
102
107
|
#
|
|
103
108
|
class PredicatePrefix < Base
|
|
109
|
+
include ProjectIndexHelp
|
|
104
110
|
include AllowedMethods
|
|
105
111
|
|
|
106
112
|
# @!method dynamic_method_define(node)
|
|
@@ -124,18 +130,24 @@ module RuboCop
|
|
|
124
130
|
end
|
|
125
131
|
|
|
126
132
|
def on_def(node)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
next if allowed_method_name?(method_name, prefix)
|
|
131
|
-
next if use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean')
|
|
133
|
+
method_name = node.method_name.to_s
|
|
134
|
+
offending_prefixes = offending_prefixes_for(node, method_name)
|
|
135
|
+
return if offending_prefixes.empty? || overrides_inherited_method?(node)
|
|
132
136
|
|
|
137
|
+
offending_prefixes.each do |prefix|
|
|
133
138
|
add_offense(
|
|
134
139
|
node.loc.name,
|
|
135
140
|
message: message(method_name, expected_name(method_name, prefix))
|
|
136
141
|
)
|
|
137
142
|
end
|
|
138
143
|
end
|
|
144
|
+
|
|
145
|
+
def offending_prefixes_for(node, method_name)
|
|
146
|
+
predicate_prefixes.reject do |prefix|
|
|
147
|
+
allowed_method_name?(method_name, prefix) ||
|
|
148
|
+
(use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean'))
|
|
149
|
+
end
|
|
150
|
+
end
|
|
139
151
|
alias on_defs on_def
|
|
140
152
|
|
|
141
153
|
def validate_config
|
|
@@ -198,6 +210,22 @@ module RuboCop
|
|
|
198
210
|
def method_definition_macro?(macro_name)
|
|
199
211
|
cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
|
|
200
212
|
end
|
|
213
|
+
|
|
214
|
+
# When `AllCops/UseProjectIndex` is enabled, methods that override a
|
|
215
|
+
# method defined by an ancestor elsewhere in the project are not
|
|
216
|
+
# reported: renaming an override breaks the inherited contract.
|
|
217
|
+
def overrides_inherited_method?(node)
|
|
218
|
+
return false unless project_index
|
|
219
|
+
return false unless (namespace_node = node.each_ancestor(:class, :module).first)
|
|
220
|
+
|
|
221
|
+
declaration = resolve_constant_in_index(namespace_node.identifier)
|
|
222
|
+
return false unless declaration.is_a?(Rubydex::Namespace)
|
|
223
|
+
|
|
224
|
+
scope = node.defs_type? ? indexed_singleton_of(declaration) : declaration
|
|
225
|
+
!scope.nil? && inherited_index_member?(scope, "#{node.method_name}()")
|
|
226
|
+
rescue StandardError
|
|
227
|
+
false
|
|
228
|
+
end
|
|
201
229
|
end
|
|
202
230
|
end
|
|
203
231
|
end
|
|
@@ -132,7 +132,9 @@ module RuboCop
|
|
|
132
132
|
|
|
133
133
|
def on_sym(node)
|
|
134
134
|
@node = node
|
|
135
|
-
|
|
135
|
+
|
|
136
|
+
# Prism parses a quoted empty hash key (`{ "": value }`) as an empty symbol node.
|
|
137
|
+
return if node.value.empty? || allowed_identifier?(node.value)
|
|
136
138
|
|
|
137
139
|
check_name(node, node.value, node) if cop_config['CheckSymbols']
|
|
138
140
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `Naming` department. The department's cops are registered for lazy loading
|
|
6
|
+
# and their files are loaded on demand.
|
|
7
|
+
module Naming
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :AccessorMethodName, "#{__dir__}/naming/accessor_method_name"
|
|
11
|
+
register_cop :AsciiIdentifiers, "#{__dir__}/naming/ascii_identifiers"
|
|
12
|
+
register_cop :BlockForwarding, "#{__dir__}/naming/block_forwarding"
|
|
13
|
+
register_cop :BlockParameterName, "#{__dir__}/naming/block_parameter_name"
|
|
14
|
+
register_cop :ClassAndModuleCamelCase, "#{__dir__}/naming/class_and_module_camel_case"
|
|
15
|
+
register_cop :ConstantName, "#{__dir__}/naming/constant_name"
|
|
16
|
+
register_cop :FileName, "#{__dir__}/naming/file_name"
|
|
17
|
+
register_cop :HeredocDelimiterCase, "#{__dir__}/naming/heredoc_delimiter_case"
|
|
18
|
+
register_cop :HeredocDelimiterNaming, "#{__dir__}/naming/heredoc_delimiter_naming"
|
|
19
|
+
register_cop :InclusiveLanguage, "#{__dir__}/naming/inclusive_language"
|
|
20
|
+
register_cop :MemoizedInstanceVariableName, "#{__dir__}/naming/memoized_instance_variable_name"
|
|
21
|
+
register_cop :MethodName, "#{__dir__}/naming/method_name"
|
|
22
|
+
register_cop :MethodParameterName, "#{__dir__}/naming/method_parameter_name"
|
|
23
|
+
register_cop :BinaryOperatorParameterName, "#{__dir__}/naming/binary_operator_parameter_name"
|
|
24
|
+
register_cop :PredicateMethod, "#{__dir__}/naming/predicate_method"
|
|
25
|
+
register_cop :PredicatePrefix, "#{__dir__}/naming/predicate_prefix"
|
|
26
|
+
register_cop :RescuedExceptionsVariableName, "#{__dir__}/naming/rescued_exceptions_variable_name"
|
|
27
|
+
register_cop :VariableName, "#{__dir__}/naming/variable_name"
|
|
28
|
+
register_cop :VariableNumber, "#{__dir__}/naming/variable_number"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/rubocop/cop/offense.rb
CHANGED
|
@@ -91,6 +91,12 @@ module RuboCop
|
|
|
91
91
|
status = :uncorrected, corrector = nil)
|
|
92
92
|
@severity = RuboCop::Cop::Severity.new(severity)
|
|
93
93
|
@location = location
|
|
94
|
+
|
|
95
|
+
# Pre-compute the position eagerly because the offense is frozen and
|
|
96
|
+
# `location.line` / `location.column` are expensive to compute; sorting
|
|
97
|
+
# many offenses calls them repeatedly through `#<=>`.
|
|
98
|
+
@line = location.line
|
|
99
|
+
@column = location.column
|
|
94
100
|
@message = message.freeze
|
|
95
101
|
@cop_name = cop_name.freeze
|
|
96
102
|
@status = status
|
|
@@ -104,6 +110,8 @@ module RuboCop
|
|
|
104
110
|
|
|
105
111
|
def marshal_load(array)
|
|
106
112
|
@severity, @location, @message, @cop_name, @status = array
|
|
113
|
+
@line = @location.line
|
|
114
|
+
@column = @location.column
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
# @api public
|
|
@@ -167,14 +175,10 @@ module RuboCop
|
|
|
167
175
|
end
|
|
168
176
|
|
|
169
177
|
# @api private
|
|
170
|
-
|
|
171
|
-
location.line
|
|
172
|
-
end
|
|
178
|
+
attr_reader :line
|
|
173
179
|
|
|
174
180
|
# @api private
|
|
175
|
-
|
|
176
|
-
location.column
|
|
177
|
-
end
|
|
181
|
+
attr_reader :column
|
|
178
182
|
|
|
179
183
|
# @api private
|
|
180
184
|
def source_line
|