rubocop 1.6.1 → 1.9.1
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/LICENSE.txt +1 -1
- data/README.md +4 -3
- data/config/default.yml +145 -19
- data/lib/rubocop.rb +16 -1
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
- data/lib/rubocop/comment_config.rb +6 -6
- data/lib/rubocop/config.rb +13 -7
- data/lib/rubocop/config_loader.rb +11 -14
- data/lib/rubocop/config_loader_resolver.rb +21 -4
- data/lib/rubocop/config_obsoletion.rb +5 -3
- data/lib/rubocop/config_store.rb +12 -1
- data/lib/rubocop/cop/base.rb +2 -1
- data/lib/rubocop/cop/exclude_limit.rb +26 -0
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
- data/lib/rubocop/cop/generator.rb +1 -3
- data/lib/rubocop/cop/internal_affairs.rb +6 -1
- data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
- data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
- data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
- data/lib/rubocop/cop/layout/class_structure.rb +7 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +56 -20
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
- data/lib/rubocop/cop/layout/line_length.rb +2 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
- data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +67 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
- data/lib/rubocop/cop/lint/deprecated_constants.rb +80 -0
- data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
- data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
- data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
- data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
- data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
- data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
- data/lib/rubocop/cop/message_annotator.rb +4 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
- data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
- data/lib/rubocop/cop/mixin/code_length.rb +3 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
- data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
- data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
- data/lib/rubocop/cop/naming/variable_name.rb +2 -0
- data/lib/rubocop/cop/naming/variable_number.rb +2 -9
- data/lib/rubocop/cop/registry.rb +10 -0
- data/lib/rubocop/cop/severity.rb +3 -3
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
- data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +14 -1
- data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
- data/lib/rubocop/cop/style/empty_literal.rb +6 -2
- data/lib/rubocop/cop/style/endless_method.rb +102 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
- data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
- data/lib/rubocop/cop/style/float_division.rb +3 -0
- data/lib/rubocop/cop/style/for.rb +2 -0
- data/lib/rubocop/cop/style/format_string_token.rb +18 -2
- data/lib/rubocop/cop/style/hash_except.rb +95 -0
- data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
- data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
- data/lib/rubocop/cop/style/lambda_call.rb +2 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
- data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
- data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
- data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
- data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
- data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
- data/lib/rubocop/cop/style/raise_args.rb +5 -2
- data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +36 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
- data/lib/rubocop/cop/util.rb +3 -1
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
- data/lib/rubocop/magic_comment.rb +30 -1
- data/lib/rubocop/options.rb +10 -10
- data/lib/rubocop/rspec/cop_helper.rb +0 -4
- data/lib/rubocop/rspec/expect_offense.rb +37 -22
- data/lib/rubocop/runner.rb +17 -1
- data/lib/rubocop/target_finder.rb +4 -2
- data/lib/rubocop/target_ruby.rb +47 -11
- data/lib/rubocop/util.rb +16 -0
- data/lib/rubocop/version.rb +8 -2
- metadata +27 -7
@@ -106,23 +106,13 @@ module RuboCop
|
|
106
106
|
error && error.ancestors[1] == SystemCallError
|
107
107
|
end
|
108
108
|
|
109
|
-
def silence_warnings
|
110
|
-
# Replaces Kernel::silence_warnings since it hides any warnings,
|
111
|
-
# including the RuboCop ones
|
112
|
-
old_verbose = $VERBOSE
|
113
|
-
$VERBOSE = nil
|
114
|
-
yield
|
115
|
-
ensure
|
116
|
-
$VERBOSE = old_verbose
|
117
|
-
end
|
118
|
-
|
119
109
|
def evaluate_exceptions(group)
|
120
110
|
rescued_exceptions = group.exceptions
|
121
111
|
|
122
112
|
if rescued_exceptions.any?
|
123
113
|
rescued_exceptions.each_with_object([]) do |exception, converted|
|
124
114
|
begin
|
125
|
-
silence_warnings do
|
115
|
+
RuboCop::Util.silence_warnings do
|
126
116
|
# Avoid printing deprecation warnings about constants
|
127
117
|
converted << Kernel.const_get(exception.source)
|
128
118
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for uses of literal strings converted to
|
7
|
+
# a symbol where a literal symbol could be used instead.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# 'string'.to_sym
|
12
|
+
# :symbol.to_sym
|
13
|
+
# 'underscored_string'.to_sym
|
14
|
+
# :'underscored_symbol'
|
15
|
+
# 'hyphenated-string'.to_sym
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# :string
|
19
|
+
# :symbol
|
20
|
+
# :underscored_string
|
21
|
+
# :underscored_symbol
|
22
|
+
# :'hyphenated-string'
|
23
|
+
#
|
24
|
+
class SymbolConversion < Base
|
25
|
+
extend AutoCorrector
|
26
|
+
|
27
|
+
MSG = 'Unnecessary symbol conversion; use `%<correction>s` instead.'
|
28
|
+
RESTRICT_ON_SEND = %i[to_sym intern].freeze
|
29
|
+
|
30
|
+
def on_send(node)
|
31
|
+
return unless node.receiver
|
32
|
+
return unless node.receiver.str_type? || node.receiver.sym_type?
|
33
|
+
|
34
|
+
register_offense(node, correction: node.receiver.value.to_sym.inspect)
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_sym(node)
|
38
|
+
return if properly_quoted?(node.source, node.value.inspect)
|
39
|
+
|
40
|
+
# `alias` arguments are symbols but since a symbol that requires
|
41
|
+
# being quoted is not a valid method identifier, it can be ignored
|
42
|
+
return if in_alias?(node)
|
43
|
+
|
44
|
+
# The `%I[]` and `%i[]` macros are parsed as normal arrays of symbols
|
45
|
+
# so they need to be ignored.
|
46
|
+
return if in_percent_literal_array?(node)
|
47
|
+
|
48
|
+
# Symbol hash keys have a different format and need to be handled separately
|
49
|
+
return correct_hash_key(node) if hash_key?(node)
|
50
|
+
|
51
|
+
register_offense(node, correction: node.value.inspect)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def register_offense(node, correction:, message: format(MSG, correction: correction))
|
57
|
+
add_offense(node, message: message) do |corrector|
|
58
|
+
corrector.replace(node, correction)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def properly_quoted?(source, value)
|
63
|
+
return true if !source.match?(/['"]/) || value.end_with?('=')
|
64
|
+
|
65
|
+
source == value ||
|
66
|
+
# `Symbol#inspect` uses double quotes, but allow single-quoted
|
67
|
+
# symbols to work as well.
|
68
|
+
source.tr("'", '"') == value
|
69
|
+
end
|
70
|
+
|
71
|
+
def in_alias?(node)
|
72
|
+
node.parent&.alias_type?
|
73
|
+
end
|
74
|
+
|
75
|
+
def in_percent_literal_array?(node)
|
76
|
+
node.parent&.array_type? && node.parent&.percent_literal?
|
77
|
+
end
|
78
|
+
|
79
|
+
def hash_key?(node)
|
80
|
+
node.parent&.pair_type? && node == node.parent.child_nodes.first
|
81
|
+
end
|
82
|
+
|
83
|
+
def correct_hash_key(node)
|
84
|
+
# Although some operators can be converted to symbols normally
|
85
|
+
# (ie. `:==`), these are not accepted as hash keys and will
|
86
|
+
# raise a syntax error (eg. `{ ==: ... }`). Therefore, if the
|
87
|
+
# symbol does not start with an alpha-numeric or underscore, it
|
88
|
+
# will be ignored.
|
89
|
+
return unless node.value.to_s.match?(/\A[a-z0-9_]/i)
|
90
|
+
|
91
|
+
correction = node.value.inspect.gsub(/\A:/, '')
|
92
|
+
return if properly_quoted?(node.source, correction)
|
93
|
+
|
94
|
+
register_offense(
|
95
|
+
node,
|
96
|
+
correction: correction,
|
97
|
+
message: format(MSG, correction: "#{correction}:")
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for "triple quotes" (strings delimted by any odd number
|
7
|
+
# of quotes greater than 1).
|
8
|
+
#
|
9
|
+
# Ruby allows multiple strings to be implicitly concatenated by just
|
10
|
+
# being adjacent in a statement (ie. `"foo""bar" == "foobar"`). This sometimes
|
11
|
+
# gives the impression that there is something special about triple quotes, but
|
12
|
+
# in fact it is just extra unnecessary quotes and produces the same string. Each
|
13
|
+
# pair of quotes produces an additional concatenated empty string, so the result
|
14
|
+
# is still only the "actual" string within the delimiters.
|
15
|
+
#
|
16
|
+
# NOTE: Although this cop is called triple quotes, the same behavior is present
|
17
|
+
# for strings delimited by 5, 7, etc. quotation marks.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# # bad
|
21
|
+
# """
|
22
|
+
# A string
|
23
|
+
# """
|
24
|
+
#
|
25
|
+
# # bad
|
26
|
+
# '''
|
27
|
+
# A string
|
28
|
+
# '''
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# "
|
32
|
+
# A string
|
33
|
+
# "
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# <<STRING
|
37
|
+
# A string
|
38
|
+
# STRING
|
39
|
+
#
|
40
|
+
# # good (but not the same spacing as the bad case)
|
41
|
+
# 'A string'
|
42
|
+
class TripleQuotes < Base
|
43
|
+
extend AutoCorrector
|
44
|
+
|
45
|
+
MSG = 'Delimiting a string with multiple quotes has no effect, use a single quote instead.'
|
46
|
+
|
47
|
+
def on_dstr(node)
|
48
|
+
return if (empty_str_nodes = empty_str_nodes(node)).none?
|
49
|
+
|
50
|
+
opening_quotes = node.source.scan(/(?<=\A)['"]*/)[0]
|
51
|
+
return if opening_quotes.size < 3
|
52
|
+
|
53
|
+
# If the node is composed of only empty `str` nodes, keep one
|
54
|
+
empty_str_nodes.shift if empty_str_nodes.size == node.child_nodes.size
|
55
|
+
|
56
|
+
add_offense(node) do |corrector|
|
57
|
+
empty_str_nodes.each do |str|
|
58
|
+
corrector.remove(str)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def empty_str_nodes(node)
|
66
|
+
node.each_child_node(:str).select { |str| str.value == '' }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -9,6 +9,12 @@ module RuboCop
|
|
9
9
|
# In rare cases where only one iteration (or at most one iteration) is intended behavior,
|
10
10
|
# the code should be refactored to use `if` conditionals.
|
11
11
|
#
|
12
|
+
# NOTE: Block methods that are used with `Enumerable`s are considered to be loops.
|
13
|
+
#
|
14
|
+
# `IgnoredPatterns` can be used to match against the block receiver in order to allow
|
15
|
+
# code that would otherwise be registered as an offense (eg. `times` used not in an
|
16
|
+
# `Enumerable` context).
|
17
|
+
#
|
12
18
|
# @example
|
13
19
|
# # bad
|
14
20
|
# while node
|
@@ -70,7 +76,16 @@ module RuboCop
|
|
70
76
|
# raise NotFoundError
|
71
77
|
# end
|
72
78
|
#
|
79
|
+
# # bad
|
80
|
+
# 2.times { raise ArgumentError }
|
81
|
+
#
|
82
|
+
# @example IgnoredPatterns: [/(exactly|at_least|at_most)\(\d+\)\.times/] (default)
|
83
|
+
#
|
84
|
+
# # good
|
85
|
+
# exactly(2).times { raise StandardError }
|
73
86
|
class UnreachableLoop < Base
|
87
|
+
include IgnoredPattern
|
88
|
+
|
74
89
|
MSG = 'This loop will have at most one iteration.'
|
75
90
|
|
76
91
|
def on_while(node)
|
@@ -91,6 +106,8 @@ module RuboCop
|
|
91
106
|
return false unless node.block_type?
|
92
107
|
|
93
108
|
send_node = node.send_node
|
109
|
+
return false if matches_ignored_pattern?(send_node.source)
|
110
|
+
|
94
111
|
send_node.enumerable_method? || send_node.enumerator_method? || send_node.method?(:loop)
|
95
112
|
end
|
96
113
|
|
@@ -85,8 +85,11 @@ module RuboCop
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
# Returns the base style guide URL from AllCops or the specific department
|
89
|
+
#
|
90
|
+
# @return [String] style guide URL
|
88
91
|
def style_guide_base_url
|
89
|
-
department_name = cop_name.split('/').
|
92
|
+
department_name = cop_name.split('/')[0..-2].join('/')
|
90
93
|
|
91
94
|
config.for_department(department_name)['StyleGuideBaseURL'] ||
|
92
95
|
config.for_all_cops['StyleGuideBaseURL']
|
@@ -12,13 +12,13 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# The maximum level of nesting allowed is configurable.
|
14
14
|
class BlockNesting < Base
|
15
|
-
include ConfigurableMax
|
16
|
-
|
17
15
|
NESTING_BLOCKS = %i[
|
18
16
|
case if while while_post
|
19
17
|
until until_post for resbody
|
20
18
|
].freeze
|
21
19
|
|
20
|
+
exclude_limit 'Max'
|
21
|
+
|
22
22
|
def on_new_investigation
|
23
23
|
return if processed_source.blank?
|
24
24
|
|
@@ -51,7 +51,8 @@ module RuboCop
|
|
51
51
|
# end
|
52
52
|
#
|
53
53
|
class ParameterLists < Base
|
54
|
-
|
54
|
+
exclude_limit 'Max'
|
55
|
+
exclude_limit 'MaxOptionalParameters'
|
55
56
|
|
56
57
|
MSG = 'Avoid parameter lists longer than %<max>d parameters. ' \
|
57
58
|
'[%<count>d/%<max>d]'
|
@@ -70,7 +71,9 @@ module RuboCop
|
|
70
71
|
count: optargs.count
|
71
72
|
)
|
72
73
|
|
73
|
-
add_offense(node, message: message)
|
74
|
+
add_offense(node, message: message) do
|
75
|
+
self.max_optional_parameters = optargs.count
|
76
|
+
end
|
74
77
|
end
|
75
78
|
alias on_defs on_def
|
76
79
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# This module encapsulates the ability to allow certain identifiers in a cop.
|
6
|
+
module AllowedIdentifiers
|
7
|
+
SIGILS = '@$' # if a variable starts with a sigil it will be removed
|
8
|
+
|
9
|
+
def allowed_identifier?(name)
|
10
|
+
allowed_identifiers.include?(name.to_s.delete(SIGILS))
|
11
|
+
end
|
12
|
+
|
13
|
+
def allowed_identifiers
|
14
|
+
cop_config.fetch('AllowedIdentifiers', [])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -69,6 +69,11 @@ module RuboCop
|
|
69
69
|
# @api private
|
70
70
|
def extract_first_element_over_column_limit(node, elements, max)
|
71
71
|
line = node.first_line
|
72
|
+
|
73
|
+
# If the first argument is a hash pair but the method is not parenthesized,
|
74
|
+
# the argument cannot be moved to another line because it cause a syntax error.
|
75
|
+
elements.shift if node.send_type? && !node.parenthesized? && elements.first.pair_type?
|
76
|
+
|
72
77
|
i = 0
|
73
78
|
i += 1 while within_column_limit?(elements[i], max, line)
|
74
79
|
return elements.first if i.zero?
|
@@ -4,10 +4,12 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
# Common functionality for checking length of code segments.
|
6
6
|
module CodeLength
|
7
|
-
|
7
|
+
extend ExcludeLimit
|
8
8
|
|
9
9
|
MSG = '%<label>s has too many lines. [%<length>d/%<max>d]'
|
10
10
|
|
11
|
+
exclude_limit 'Max'
|
12
|
+
|
11
13
|
private
|
12
14
|
|
13
15
|
def message(length, max_length)
|
@@ -9,7 +9,6 @@ module RuboCop
|
|
9
9
|
def source_range_with_comment(node)
|
10
10
|
begin_pos = begin_pos_with_comment(node)
|
11
11
|
end_pos = end_position_for(node)
|
12
|
-
end_pos += 1 if node.def_type?
|
13
12
|
|
14
13
|
Parser::Source::Range.new(buffer, begin_pos, end_pos)
|
15
14
|
end
|
@@ -22,16 +21,7 @@ module RuboCop
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def begin_pos_with_comment(node)
|
25
|
-
|
26
|
-
first_comment = nil
|
27
|
-
|
28
|
-
processed_source.comments_before_line(annotation_line)
|
29
|
-
.reverse_each do |comment|
|
30
|
-
if comment.location.line == annotation_line
|
31
|
-
first_comment = comment
|
32
|
-
annotation_line -= 1
|
33
|
-
end
|
34
|
-
end
|
24
|
+
first_comment = processed_source.ast_with_comments[node].first
|
35
25
|
|
36
26
|
start_line_position(first_comment || node)
|
37
27
|
end
|
@@ -6,10 +6,12 @@ module RuboCop
|
|
6
6
|
#
|
7
7
|
# This module handles measurement and reporting of complexity in methods.
|
8
8
|
module MethodComplexity
|
9
|
-
include ConfigurableMax
|
10
9
|
include IgnoredMethods
|
11
10
|
include Metrics::Utils::RepeatedCsendDiscount
|
12
11
|
extend NodePattern::Macros
|
12
|
+
extend ExcludeLimit
|
13
|
+
|
14
|
+
exclude_limit 'Max'
|
13
15
|
|
14
16
|
# Ensure cops that include `MethodComplexity` have the config
|
15
17
|
# `attr_accessor`s that `ignored_method?` needs.
|
@@ -24,7 +24,11 @@ module RuboCop
|
|
24
24
|
name = full_name.gsub(/\A(_+)/, '')
|
25
25
|
next if allowed_names.include?(name)
|
26
26
|
|
27
|
-
|
27
|
+
length = full_name.size
|
28
|
+
length += 1 if arg.restarg_type?
|
29
|
+
length += 2 if arg.kwrestarg_type?
|
30
|
+
|
31
|
+
range = arg_range(arg, length)
|
28
32
|
issue_offenses(node, range, name)
|
29
33
|
end
|
30
34
|
end
|
@@ -4,7 +4,9 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Naming
|
6
6
|
# This cop checks for memoized methods whose instance variable name
|
7
|
-
# does not match the method name.
|
7
|
+
# does not match the method name. Applies to both regular methods
|
8
|
+
# (defined with `def`) and dynamic methods (defined with
|
9
|
+
# `define_method` or `define_singleton_method`).
|
8
10
|
#
|
9
11
|
# This cop can be configured with the EnforcedStyleForLeadingUnderscores
|
10
12
|
# directive. It can be configured to allow for memoized instance variables
|
@@ -48,6 +50,17 @@ module RuboCop
|
|
48
50
|
# @foo ||= calculate_expensive_thing(helper_variable)
|
49
51
|
# end
|
50
52
|
#
|
53
|
+
# # good
|
54
|
+
# define_method(:foo) do
|
55
|
+
# @foo ||= calculate_expensive_thing
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# define_method(:foo) do
|
60
|
+
# return @foo if defined?(@foo)
|
61
|
+
# @foo = calculate_expensive_thing
|
62
|
+
# end
|
63
|
+
#
|
51
64
|
# @example EnforcedStyleForLeadingUnderscores: required
|
52
65
|
# # bad
|
53
66
|
# def foo
|
@@ -79,6 +92,17 @@ module RuboCop
|
|
79
92
|
# @_foo = calculate_expensive_thing
|
80
93
|
# end
|
81
94
|
#
|
95
|
+
# # good
|
96
|
+
# define_method(:foo) do
|
97
|
+
# @_foo ||= calculate_expensive_thing
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# # good
|
101
|
+
# define_method(:foo) do
|
102
|
+
# return @_foo if defined?(@_foo)
|
103
|
+
# @_foo = calculate_expensive_thing
|
104
|
+
# end
|
105
|
+
#
|
82
106
|
# @example EnforcedStyleForLeadingUnderscores :optional
|
83
107
|
# # bad
|
84
108
|
# def foo
|
@@ -105,6 +129,16 @@ module RuboCop
|
|
105
129
|
# return @_foo if defined?(@_foo)
|
106
130
|
# @_foo = calculate_expensive_thing
|
107
131
|
# end
|
132
|
+
#
|
133
|
+
# # good
|
134
|
+
# define_method(:foo) do
|
135
|
+
# @foo ||= calculate_expensive_thing
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# # good
|
139
|
+
# define_method(:foo) do
|
140
|
+
# @_foo ||= calculate_expensive_thing
|
141
|
+
# end
|
108
142
|
class MemoizedInstanceVariableName < Base
|
109
143
|
include ConfigurableEnforcedStyle
|
110
144
|
|
@@ -112,17 +146,27 @@ module RuboCop
|
|
112
146
|
'method name `%<method>s`. Use `@%<suggested_var>s` instead.'
|
113
147
|
UNDERSCORE_REQUIRED = 'Memoized variable `%<var>s` does not start ' \
|
114
148
|
'with `_`. Use `@%<suggested_var>s` instead.'
|
149
|
+
DYNAMIC_DEFINE_METHODS = %i[define_method define_singleton_method].to_set.freeze
|
150
|
+
|
151
|
+
def_node_matcher :method_definition?, <<~PATTERN
|
152
|
+
${
|
153
|
+
(block (send _ %DYNAMIC_DEFINE_METHODS ({sym str} $_)) ...)
|
154
|
+
(def $_ ...)
|
155
|
+
(defs _ $_ ...)
|
156
|
+
}
|
157
|
+
PATTERN
|
115
158
|
|
116
159
|
# rubocop:disable Metrics/AbcSize
|
117
160
|
def on_or_asgn(node)
|
118
161
|
lhs, _value = *node
|
119
162
|
return unless lhs.ivasgn_type?
|
120
|
-
|
163
|
+
|
164
|
+
method_node, method_name = find_definition(node)
|
165
|
+
return unless method_node
|
121
166
|
|
122
167
|
body = method_node.body
|
123
168
|
return unless body == node || body.children.last == node
|
124
169
|
|
125
|
-
method_name = method_node.method_name
|
126
170
|
return if matches?(method_name, lhs)
|
127
171
|
|
128
172
|
msg = format(
|
@@ -147,11 +191,10 @@ module RuboCop
|
|
147
191
|
arg = node.arguments.first
|
148
192
|
return unless arg.ivar_type?
|
149
193
|
|
150
|
-
method_node = node
|
194
|
+
method_node, method_name = find_definition(node)
|
151
195
|
return unless method_node
|
152
196
|
|
153
197
|
var_name = arg.children.first
|
154
|
-
method_name = method_node.method_name
|
155
198
|
defined_memoized?(method_node.body, var_name) do |defined_ivar, return_ivar, ivar_assign|
|
156
199
|
return if matches?(method_name, ivar_assign)
|
157
200
|
|
@@ -174,6 +217,17 @@ module RuboCop
|
|
174
217
|
'EnforcedStyleForLeadingUnderscores'
|
175
218
|
end
|
176
219
|
|
220
|
+
def find_definition(node)
|
221
|
+
# Methods can be defined in a `def` or `defs`,
|
222
|
+
# or dynamically via a `block` node.
|
223
|
+
node.each_ancestor(:def, :defs, :block).each do |ancestor|
|
224
|
+
method_node, method_name = method_definition?(ancestor)
|
225
|
+
return [method_node, method_name] if method_node
|
226
|
+
end
|
227
|
+
|
228
|
+
nil
|
229
|
+
end
|
230
|
+
|
177
231
|
def matches?(method_name, ivar_assign)
|
178
232
|
return true if ivar_assign.nil? || method_name == :initialize
|
179
233
|
|