rubocop 1.88.0 → 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.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +72 -12
  3. data/lib/rubocop/comment_config.rb +35 -19
  4. data/lib/rubocop/config.rb +53 -0
  5. data/lib/rubocop/config_validator.rb +38 -1
  6. data/lib/rubocop/cop/badge.rb +8 -0
  7. data/lib/rubocop/cop/base.rb +19 -12
  8. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -1
  9. data/lib/rubocop/cop/bundler.rb +19 -0
  10. data/lib/rubocop/cop/commissioner.rb +13 -11
  11. data/lib/rubocop/cop/corrector.rb +1 -1
  12. data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
  13. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -1
  15. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
  16. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +12 -2
  18. data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -1
  19. data/lib/rubocop/cop/gemspec.rb +22 -0
  20. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  21. data/lib/rubocop/cop/generator.rb +4 -0
  22. data/lib/rubocop/cop/layout/block_alignment.rb +17 -0
  23. data/lib/rubocop/cop/layout/class_structure.rb +14 -4
  24. data/lib/rubocop/cop/layout/comment_indentation.rb +9 -2
  25. data/lib/rubocop/cop/layout/condition_position.rb +13 -3
  26. data/lib/rubocop/cop/layout/else_alignment.rb +1 -14
  27. data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
  28. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +14 -1
  29. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  30. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +13 -14
  31. data/lib/rubocop/cop/layout/indentation_width.rb +31 -1
  32. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  33. data/lib/rubocop/cop/layout/line_length.rb +95 -17
  34. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  35. data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
  36. data/lib/rubocop/cop/layout/space_around_operators.rb +6 -2
  37. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +33 -34
  38. data/lib/rubocop/cop/layout.rb +114 -0
  39. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  40. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
  41. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
  42. data/lib/rubocop/cop/lint/assignment_in_condition.rb +13 -1
  43. data/lib/rubocop/cop/lint/constant_reassignment.rb +1 -10
  44. data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
  45. data/lib/rubocop/cop/lint/debugger.rb +13 -1
  46. data/lib/rubocop/cop/lint/deprecated_reference.rb +185 -0
  47. data/lib/rubocop/cop/lint/duplicate_methods.rb +202 -22
  48. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  49. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
  50. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  51. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  52. data/lib/rubocop/cop/lint/name_typo.rb +223 -0
  53. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +7 -0
  54. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +0 -4
  55. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
  56. data/lib/rubocop/cop/lint/to_enum_arguments.rb +44 -2
  57. data/lib/rubocop/cop/lint/to_json.rb +8 -1
  58. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +32 -8
  59. data/lib/rubocop/cop/lint/unreachable_code.rb +9 -4
  60. data/lib/rubocop/cop/lint/unused_private_method.rb +157 -0
  61. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
  62. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
  63. data/lib/rubocop/cop/lint/void.rb +19 -8
  64. data/lib/rubocop/cop/lint.rb +176 -0
  65. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  66. data/lib/rubocop/cop/metrics/perceived_complexity.rb +38 -7
  67. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  68. data/lib/rubocop/cop/metrics.rb +31 -0
  69. data/lib/rubocop/cop/migration/department_name.rb +0 -4
  70. data/lib/rubocop/cop/migration.rb +13 -0
  71. data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
  72. data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
  73. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
  74. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  75. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
  76. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
  77. data/lib/rubocop/cop/mixin/hash_subset.rb +8 -0
  78. data/lib/rubocop/cop/mixin/hash_transform_method.rb +4 -0
  79. data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
  80. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
  81. data/lib/rubocop/cop/mixin/statement_modifier.rb +43 -7
  82. data/lib/rubocop/cop/mixin.rb +78 -77
  83. data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
  84. data/lib/rubocop/cop/naming/file_name.rb +4 -3
  85. data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
  86. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +9 -0
  87. data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
  88. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +9 -3
  89. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  90. data/lib/rubocop/cop/naming.rb +31 -0
  91. data/lib/rubocop/cop/offense.rb +10 -6
  92. data/lib/rubocop/cop/registry.rb +153 -43
  93. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  94. data/lib/rubocop/cop/security/marshal_load.rb +1 -1
  95. data/lib/rubocop/cop/security.rb +19 -0
  96. data/lib/rubocop/cop/style/accessor_grouping.rb +11 -1
  97. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  98. data/lib/rubocop/cop/style/array_intersect.rb +24 -5
  99. data/lib/rubocop/cop/style/class_and_module_children.rb +78 -18
  100. data/lib/rubocop/cop/style/collection_compact.rb +1 -1
  101. data/lib/rubocop/cop/style/conditional_assignment.rb +22 -0
  102. data/lib/rubocop/cop/style/copyright.rb +6 -1
  103. data/lib/rubocop/cop/style/data_inheritance.rb +4 -0
  104. data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
  105. data/lib/rubocop/cop/style/dir_empty.rb +4 -0
  106. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +41 -3
  107. data/lib/rubocop/cop/style/documentation.rb +47 -0
  108. data/lib/rubocop/cop/style/documentation_method.rb +5 -1
  109. data/lib/rubocop/cop/style/empty_case_condition.rb +12 -2
  110. data/lib/rubocop/cop/style/empty_class_definition.rb +8 -1
  111. data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
  112. data/lib/rubocop/cop/style/empty_literal.rb +7 -2
  113. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +30 -20
  114. data/lib/rubocop/cop/style/endless_method.rb +0 -10
  115. data/lib/rubocop/cop/style/env_home.rb +4 -0
  116. data/lib/rubocop/cop/style/even_odd.rb +11 -1
  117. data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -1
  118. data/lib/rubocop/cop/style/file_null.rb +4 -2
  119. data/lib/rubocop/cop/style/format_string.rb +13 -1
  120. data/lib/rubocop/cop/style/hash_conversion.rb +14 -6
  121. data/lib/rubocop/cop/style/hash_lookup_method.rb +2 -6
  122. data/lib/rubocop/cop/style/hash_syntax.rb +2 -0
  123. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -4
  124. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -52
  125. data/lib/rubocop/cop/style/if_with_semicolon.rb +9 -1
  126. data/lib/rubocop/cop/style/inline_comment.rb +1 -1
  127. data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
  128. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
  129. data/lib/rubocop/cop/style/keyword_parameters_order.rb +7 -3
  130. data/lib/rubocop/cop/style/lambda.rb +11 -1
  131. data/lib/rubocop/cop/style/lambda_call.rb +11 -0
  132. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
  133. data/lib/rubocop/cop/style/map_into_array.rb +1 -1
  134. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -3
  135. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -5
  136. data/lib/rubocop/cop/style/method_def_parentheses.rb +5 -1
  137. data/lib/rubocop/cop/style/min_max_comparison.rb +3 -0
  138. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +40 -7
  139. data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
  140. data/lib/rubocop/cop/style/module_function.rb +4 -2
  141. data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
  142. data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
  143. data/lib/rubocop/cop/style/multiline_method_signature.rb +11 -4
  144. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  145. data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
  146. data/lib/rubocop/cop/style/negative_array_index.rb +3 -1
  147. data/lib/rubocop/cop/style/nil_lambda.rb +8 -0
  148. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  149. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  150. data/lib/rubocop/cop/style/option_hash.rb +1 -1
  151. data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
  152. data/lib/rubocop/cop/style/parallel_assignment.rb +11 -2
  153. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  154. data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
  155. data/lib/rubocop/cop/style/redundant_assignment.rb +11 -0
  156. data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
  157. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
  158. data/lib/rubocop/cop/style/redundant_exception.rb +6 -0
  159. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  160. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  161. data/lib/rubocop/cop/style/redundant_format.rb +40 -3
  162. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
  163. data/lib/rubocop/cop/style/redundant_line_continuation.rb +80 -129
  164. data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -63
  165. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -4
  166. data/lib/rubocop/cop/style/redundant_self.rb +9 -0
  167. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +23 -4
  168. data/lib/rubocop/cop/style/safe_navigation.rb +10 -0
  169. data/lib/rubocop/cop/style/semicolon.rb +4 -4
  170. data/lib/rubocop/cop/style/single_line_do_end_block.rb +17 -4
  171. data/lib/rubocop/cop/style/sole_nested_conditional.rb +20 -3
  172. data/lib/rubocop/cop/style/static_class.rb +21 -0
  173. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  174. data/lib/rubocop/cop/style/struct_inheritance.rb +3 -1
  175. data/lib/rubocop/cop/style/ternary_parentheses.rb +11 -0
  176. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -8
  177. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  178. data/lib/rubocop/cop/style.rb +310 -0
  179. data/lib/rubocop/cop/team.rb +66 -7
  180. data/lib/rubocop/cop/variable_force/variable.rb +18 -6
  181. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  182. data/lib/rubocop/directive_comment.rb +2 -0
  183. data/lib/rubocop/feature_loader.rb +0 -6
  184. data/lib/rubocop/lsp/routes.rb +41 -3
  185. data/lib/rubocop/lsp/runtime.rb +4 -0
  186. data/lib/rubocop/lsp/server.rb +4 -0
  187. data/lib/rubocop/lsp/stdin_runner.rb +20 -0
  188. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  189. data/lib/rubocop/project_index_loader.rb +16 -0
  190. data/lib/rubocop/result_cache.rb +6 -4
  191. data/lib/rubocop/rspec/shared_contexts.rb +14 -2
  192. data/lib/rubocop/runner.rb +100 -33
  193. data/lib/rubocop/server/cache.rb +10 -5
  194. data/lib/rubocop/target_ruby.rb +1 -1
  195. data/lib/rubocop/version.rb +1 -1
  196. data/lib/rubocop.rb +17 -626
  197. metadata +18 -3
@@ -17,6 +17,14 @@ module RuboCop
17
17
  # remains after stripping `file://` from a `file:///C:/...` URI.
18
18
  WINDOWS_DRIVE_PREFIX = %r{\A/(?=[A-Za-z]:[/\\])}.freeze
19
19
 
20
+ class << self
21
+ # The signature is a property of the index, not of the cop, and computing
22
+ # it stats every indexed file, so all cops sharing an index share one
23
+ # computation. A single-entry cache (instead of a hash keyed by index)
24
+ # avoids retaining stale graphs in long-lived processes.
25
+ attr_accessor :cached_index_signature
26
+ end
27
+
20
28
  def external_dependency_checksum
21
29
  return nil unless project_index
22
30
 
@@ -27,7 +35,139 @@ module RuboCop
27
35
 
28
36
  private
29
37
 
38
+ # Returns the definitions among `definitions` that live in a file other than the
39
+ # one being inspected, ordered by path and line. Definitions without a `file://`
40
+ # URI (e.g. Rubydex's built-in declarations) are ignored.
41
+ def definitions_in_other_files(definitions)
42
+ current = processed_source.file_path
43
+
44
+ definitions
45
+ .select { |definition| definition.location.uri.start_with?(FILE_URI_PREFIX) }
46
+ .reject { |definition| File.identical?(definition.location.to_file_path, current) }
47
+ .sort_by do |definition|
48
+ [definition.location.to_file_path,
49
+ definition.location.start_line]
50
+ end
51
+ end
52
+
53
+ def prior_definition_in_other_file(definitions)
54
+ definitions_in_other_files(definitions).first
55
+ end
56
+
57
+ # Resolves a constant node the way Ruby does: the first segment through
58
+ # the lexical nesting and every following segment inside the previous
59
+ # one. (Qualified names cannot be passed to `resolve_constant` as a
60
+ # whole, since it only applies the nesting to the full name. Later
61
+ # segments use `find_member` — the namespace and its ancestors — since
62
+ # `resolve_constant` cannot take an already-resolved namespace as the
63
+ # scope, only names as they appear in source.)
64
+ def resolve_constant_in_index(const_node)
65
+ segments = const_node.const_name.split('::')
66
+ nesting = const_node.absolute? ? [] : lexical_nesting_of(const_node)
67
+
68
+ declaration = project_index.resolve_constant(segments.first, nesting)
69
+ segments.drop(1).each do |segment|
70
+ return nil unless declaration.is_a?(Rubydex::Namespace)
71
+
72
+ declaration = declaration.find_member(segment)
73
+ end
74
+
75
+ declaration
76
+ end
77
+
78
+ # The lexical nesting the node's constants resolve through, outermost
79
+ # first. Only scopes whose *body* contains the node count: a class or
80
+ # module's identifier and superclass expression are evaluated before its
81
+ # scope exists, so ancestors reached through them are excluded.
82
+ def lexical_nesting_of(node)
83
+ nesting = []
84
+ child = node
85
+
86
+ node.each_ancestor do |ancestor|
87
+ if ancestor.type?(:class, :module) && child.equal?(ancestor.body)
88
+ nesting << ancestor.identifier.const_name
89
+ end
90
+ child = ancestor
91
+ end
92
+
93
+ nesting.reverse
94
+ end
95
+
96
+ # The declaration of `declaration`'s singleton class, or nil when no
97
+ # singleton method is defined on it anywhere in the project.
98
+ def indexed_singleton_of(declaration)
99
+ project_index["#{declaration.name}::<#{declaration.name.split('::').last}>"]
100
+ end
101
+
102
+ # A namespace without any singleton method has no singleton-class
103
+ # declaration of its own, so the lookup starts from the first ancestor
104
+ # that has one; its `find_member` covers the rest of the chain.
105
+ def indexed_singleton_member(namespace, member_name)
106
+ namespace.ancestors.each do |ancestor|
107
+ singleton = indexed_singleton_of(ancestor)
108
+ return singleton.find_member(member_name) if singleton
109
+ end
110
+
111
+ nil
112
+ end
113
+
114
+ # Whether an ancestor of `scope` other than `scope` itself defines
115
+ # `member_name`.
116
+ def inherited_index_member?(scope, member_name)
117
+ scope.ancestors.any? do |ancestor|
118
+ ancestor.name != scope.name && ancestor.member(member_name)
119
+ end
120
+ end
121
+
122
+ # Whether every link of the declaration's ancestry is resolved in the
123
+ # index: no definition of any ancestor has an unresolved superclass or
124
+ # mixin reference. Unresolved links silently vanish from `ancestors`,
125
+ # so this is the completeness check that must pass before reasoning
126
+ # about what an ancestry does or does not define. `extend` mixins only
127
+ # affect the singleton class; pass `ignore_extend: true` when reasoning
128
+ # about instance-side ancestry only.
129
+ def fully_resolved_index_ancestry?(declaration, ignore_extend: false)
130
+ declaration.ancestors.all? do |ancestor|
131
+ ancestor.definitions.all? do |definition|
132
+ resolved_superclass_reference?(definition) &&
133
+ resolved_mixin_references?(definition, ignore_extend: ignore_extend)
134
+ end
135
+ end
136
+ end
137
+
138
+ def resolved_superclass_reference?(definition)
139
+ return true unless definition.is_a?(Rubydex::ClassDefinition)
140
+
141
+ !definition.superclass.is_a?(Rubydex::UnresolvedConstantReference)
142
+ end
143
+
144
+ def resolved_mixin_references?(definition, ignore_extend: false)
145
+ return true unless definition.respond_to?(:mixins)
146
+
147
+ definition.mixins.none? do |mixin|
148
+ next false if ignore_extend && mixin.is_a?(Rubydex::Extend)
149
+
150
+ mixin.constant_reference.is_a?(Rubydex::UnresolvedConstantReference)
151
+ end
152
+ end
153
+
154
+ def same_file?(path, other)
155
+ return true if File.identical?(path, other)
156
+
157
+ normalized = [path, other].map { |p| File.expand_path(p).tr('\\', '/') }
158
+ normalized.uniq.one? || (Platform.windows? && normalized[0].casecmp?(normalized[1]))
159
+ end
160
+
30
161
  def project_index_signature
162
+ index, signature = ProjectIndexHelp.cached_index_signature
163
+ return signature if index.equal?(project_index)
164
+
165
+ compute_project_index_signature.tap do |computed|
166
+ ProjectIndexHelp.cached_index_signature = [project_index, computed]
167
+ end
168
+ end
169
+
170
+ def compute_project_index_signature
31
171
  project_index.documents.filter_map do |doc|
32
172
  uri = doc.uri
33
173
  next if uri == BUILTIN_DOCUMENT_URI
@@ -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
- return true unless max_line_length
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 length_in_modifier_form(node)
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
- expression = to_modifier_form(node)
47
- line_length("#{code_before}#{expression}#{code_after(node)}")
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)
@@ -75,7 +111,7 @@ module RuboCop
75
111
  end
76
112
 
77
113
  def first_line_comment(node)
78
- comment = processed_source.comments.find { |c| same_line?(c, node) }
114
+ comment = processed_source.comment_at_line(node.first_line)
79
115
  return unless comment
80
116
 
81
117
  comment_source = comment.source
@@ -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, '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 :ProjectIndexHelp, 'rubocop/cop/mixin/project_index_help'
32
- autoload :AnnotationComment, 'rubocop/cop/mixin/annotation_comment'
33
- autoload :EmptyParameter, 'rubocop/cop/mixin/empty_parameter'
34
- autoload :EndKeywordAlignment, 'rubocop/cop/mixin/end_keyword_alignment'
35
- autoload :EndlessMethodRewriter, 'rubocop/cop/mixin/endless_method_rewriter'
36
- autoload :EnforceSuperclass, 'rubocop/cop/mixin/enforce_superclass'
37
- autoload :FirstElementLineBreak, 'rubocop/cop/mixin/first_element_line_break'
38
- autoload :FrozenStringLiteral, 'rubocop/cop/mixin/frozen_string_literal'
39
- autoload :GemDeclaration, 'rubocop/cop/mixin/gem_declaration'
40
- autoload :GemspecHelp, 'rubocop/cop/mixin/gemspec_help'
41
- autoload :HashAlignmentStyles, 'rubocop/cop/mixin/hash_alignment_styles'
42
- autoload :HashSubset, 'rubocop/cop/mixin/hash_subset'
43
- autoload :HashTransformMethod, 'rubocop/cop/mixin/hash_transform_method'
44
- autoload :IntegerNode, 'rubocop/cop/mixin/integer_node'
45
- autoload :Interpolation, 'rubocop/cop/mixin/interpolation'
46
- autoload :LineLengthHelp, 'rubocop/cop/mixin/line_length_help'
47
- autoload :MatchRange, 'rubocop/cop/mixin/match_range'
48
- autoload :HashShorthandSyntax, 'rubocop/cop/mixin/hash_shorthand_syntax'
49
- autoload :MethodComplexity, 'rubocop/cop/mixin/method_complexity'
50
- autoload :MethodPreference, 'rubocop/cop/mixin/method_preference'
51
- autoload :MinBodyLength, 'rubocop/cop/mixin/min_body_length'
52
- autoload :MinBranchesCount, 'rubocop/cop/mixin/min_branches_count'
53
- autoload :MultilineElementIndentation, 'rubocop/cop/mixin/multiline_element_indentation'
54
- autoload :MultilineElementLineBreaks, 'rubocop/cop/mixin/multiline_element_line_breaks'
55
- autoload :MultilineExpressionIndentation, 'rubocop/cop/mixin/multiline_expression_indentation'
56
- autoload :MultilineLiteralBraceLayout, 'rubocop/cop/mixin/multiline_literal_brace_layout'
57
- autoload :NegativeConditional, 'rubocop/cop/mixin/negative_conditional'
58
- autoload :Heredoc, 'rubocop/cop/mixin/heredoc'
59
- autoload :NilMethods, 'rubocop/cop/mixin/nil_methods'
60
- autoload :OnNormalIfUnless, 'rubocop/cop/mixin/on_normal_if_unless'
61
- autoload :OrderedGemNode, 'rubocop/cop/mixin/ordered_gem_node'
62
- autoload :Parentheses, 'rubocop/cop/mixin/parentheses'
63
- autoload :PercentArray, 'rubocop/cop/mixin/percent_array'
64
- autoload :PercentLiteral, 'rubocop/cop/mixin/percent_literal'
65
- autoload :PrecedingFollowingAlignment, 'rubocop/cop/mixin/preceding_following_alignment'
66
- autoload :PreferredDelimiters, 'rubocop/cop/mixin/preferred_delimiters'
67
- autoload :RationalLiteral, 'rubocop/cop/mixin/rational_literal'
68
- autoload :RequireLibrary, 'rubocop/cop/mixin/require_library'
69
- autoload :RescueNode, 'rubocop/cop/mixin/rescue_node'
70
- autoload :SafeAssignment, 'rubocop/cop/mixin/safe_assignment'
71
- autoload :SpaceAfterPunctuation, 'rubocop/cop/mixin/space_after_punctuation'
72
- autoload :SpaceBeforePunctuation, 'rubocop/cop/mixin/space_before_punctuation'
73
- autoload :SurroundingSpace, 'rubocop/cop/mixin/surrounding_space'
74
- autoload :StatementModifier, 'rubocop/cop/mixin/statement_modifier'
75
- autoload :StringHelp, 'rubocop/cop/mixin/string_help'
76
- autoload :StringLiteralsHelp, 'rubocop/cop/mixin/string_literals_help'
77
- autoload :SymbolHelp, 'rubocop/cop/mixin/symbol_help'
78
- autoload :TargetRubyVersion, 'rubocop/cop/mixin/target_ruby_version'
79
- autoload :TrailingBody, 'rubocop/cop/mixin/trailing_body'
80
- autoload :TrailingComma, 'rubocop/cop/mixin/trailing_comma'
81
- autoload :UncommunicativeName, 'rubocop/cop/mixin/uncommunicative_name'
82
- autoload :VisibilityHelp, 'rubocop/cop/mixin/visibility_help'
83
- autoload :CommentsHelp, 'rubocop/cop/mixin/comments_help'
84
- autoload :DefNode, 'rubocop/cop/mixin/def_node'
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
@@ -203,9 +203,10 @@ module RuboCop
203
203
 
204
204
  def match_acronym?(expected, name)
205
205
  expected = expected.to_s
206
- name = name.to_s
207
-
208
- allowed_acronyms.any? { |acronym| expected.gsub(acronym.capitalize, acronym) == name }
206
+ name = allowed_acronyms.reduce(name.to_s) do |result, acronym|
207
+ result.gsub(acronym, acronym.capitalize)
208
+ end
209
+ expected == name
209
210
  end
210
211
 
211
212
  def to_namespace(path) # rubocop:disable Metrics/AbcSize