rubocop 1.50.2 → 1.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/config/default.yml +76 -6
- data/lib/rubocop/cli/command/lsp.rb +19 -0
- data/lib/rubocop/cli.rb +3 -0
- data/lib/rubocop/config.rb +4 -0
- data/lib/rubocop/config_loader_resolver.rb +4 -3
- data/lib/rubocop/config_obsoletion.rb +2 -2
- data/lib/rubocop/cop/base.rb +5 -1
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_version.rb +2 -2
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
- data/lib/rubocop/cop/gemspec/dependency_version.rb +2 -2
- data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +32 -8
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +5 -5
- data/lib/rubocop/cop/layout/class_structure.rb +7 -0
- data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +2 -0
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
- data/lib/rubocop/cop/layout/space_inside_range_literal.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +13 -1
- data/lib/rubocop/cop/lint/debugger.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_hash_key.rb +2 -1
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +46 -19
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -4
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +1 -1
- data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -3
- data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
- data/lib/rubocop/cop/lint/missing_super.rb +34 -5
- data/lib/rubocop/cop/lint/mixed_case_range.rb +109 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +5 -0
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
- data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +120 -0
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +8 -3
- data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -1
- data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
- data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
- data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
- data/lib/rubocop/cop/lint/useless_assignment.rb +59 -1
- data/lib/rubocop/cop/lint/void.rb +63 -7
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
- data/lib/rubocop/cop/migration/department_name.rb +2 -2
- data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
- data/lib/rubocop/cop/mixin/comments_help.rb +7 -3
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/constant_name.rb +1 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +25 -10
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
- data/lib/rubocop/cop/naming/variable_name.rb +6 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +5 -1
- data/lib/rubocop/cop/style/attr.rb +11 -1
- data/lib/rubocop/cop/style/begin_block.rb +1 -2
- data/lib/rubocop/cop/style/block_comments.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +3 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
- data/lib/rubocop/cop/style/class_equality_comparison.rb +17 -39
- data/lib/rubocop/cop/style/collection_compact.rb +16 -6
- data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
- data/lib/rubocop/cop/style/combinable_loops.rb +26 -6
- data/lib/rubocop/cop/style/conditional_assignment.rb +5 -3
- data/lib/rubocop/cop/style/copyright.rb +5 -2
- data/lib/rubocop/cop/style/dir.rb +1 -1
- data/lib/rubocop/cop/style/dir_empty.rb +8 -14
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +5 -5
- data/lib/rubocop/cop/style/exact_regexp_match.rb +68 -0
- data/lib/rubocop/cop/style/file_read.rb +2 -2
- data/lib/rubocop/cop/style/guard_clause.rb +2 -0
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -22
- data/lib/rubocop/cop/style/hash_except.rb +19 -8
- data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
- data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +6 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -0
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +10 -6
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/preferred_hash_methods.rb +1 -1
- data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +38 -0
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +101 -0
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +7 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +86 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +3 -1
- data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
- data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
- data/lib/rubocop/cop/style/require_order.rb +11 -5
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +81 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +15 -5
- data/lib/rubocop/cop/style/semicolon.rb +12 -1
- data/lib/rubocop/cop/style/signal_exception.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +3 -4
- data/lib/rubocop/cop/style/yaml_file_read.rb +66 -0
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/utils/regexp_ranges.rb +100 -0
- data/lib/rubocop/cop/variable_force/assignment.rb +33 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
- data/lib/rubocop/cop/variable_force.rb +1 -0
- data/lib/rubocop/cops_documentation_generator.rb +1 -1
- data/lib/rubocop/ext/regexp_parser.rb +4 -1
- data/lib/rubocop/lsp/logger.rb +22 -0
- data/lib/rubocop/lsp/routes.rb +223 -0
- data/lib/rubocop/lsp/runtime.rb +79 -0
- data/lib/rubocop/lsp/server.rb +62 -0
- data/lib/rubocop/lsp/severity.rb +27 -0
- data/lib/rubocop/options.rb +11 -1
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +1 -1
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/target_ruby.rb +3 -2
- data/lib/rubocop/version.rb +10 -6
- data/lib/rubocop.rb +13 -0
- metadata +38 -6
@@ -52,6 +52,8 @@ module RuboCop
|
|
52
52
|
# expect { do_something }.to not_change { object.attribute }
|
53
53
|
#
|
54
54
|
class AmbiguousBlockAssociation < Base
|
55
|
+
extend AutoCorrector
|
56
|
+
|
55
57
|
include AllowedMethods
|
56
58
|
include AllowedPattern
|
57
59
|
|
@@ -68,7 +70,9 @@ module RuboCop
|
|
68
70
|
|
69
71
|
message = message(node)
|
70
72
|
|
71
|
-
add_offense(node, message: message)
|
73
|
+
add_offense(node, message: message) do |corrector|
|
74
|
+
wrap_in_parentheses(corrector, node)
|
75
|
+
end
|
72
76
|
end
|
73
77
|
alias on_csend on_send
|
74
78
|
|
@@ -89,6 +93,14 @@ module RuboCop
|
|
89
93
|
|
90
94
|
format(MSG, param: block_param.source, method: block_param.send_node.source)
|
91
95
|
end
|
96
|
+
|
97
|
+
def wrap_in_parentheses(corrector, node)
|
98
|
+
range = node.loc.selector.end.join(node.first_argument.source_range.begin)
|
99
|
+
|
100
|
+
corrector.remove(range)
|
101
|
+
corrector.insert_before(range, '(')
|
102
|
+
corrector.insert_after(node.last_argument, ')')
|
103
|
+
end
|
92
104
|
end
|
93
105
|
end
|
94
106
|
end
|
@@ -90,7 +90,7 @@ module RuboCop
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def debugger_method?(send_node)
|
93
|
-
return if send_node.parent&.send_type? && send_node.parent.receiver == send_node
|
93
|
+
return false if send_node.parent&.send_type? && send_node.parent.receiver == send_node
|
94
94
|
|
95
95
|
debugger_methods.include?(chained_method_name(send_node))
|
96
96
|
end
|
@@ -4,6 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
6
|
# Checks for duplicated keys in hash literals.
|
7
|
+
# This cop considers both primitive types and constants for the hash keys.
|
7
8
|
#
|
8
9
|
# This cop mirrors a warning in Ruby 2.2.
|
9
10
|
#
|
@@ -24,7 +25,7 @@ module RuboCop
|
|
24
25
|
MSG = 'Duplicated key in hash literal.'
|
25
26
|
|
26
27
|
def on_hash(node)
|
27
|
-
keys = node.keys.select
|
28
|
+
keys = node.keys.select { |key| key.recursive_basic_literal? || key.const_type? }
|
28
29
|
|
29
30
|
return unless duplicates?(keys)
|
30
31
|
|
@@ -24,6 +24,8 @@ module RuboCop
|
|
24
24
|
|
25
25
|
MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
|
26
26
|
|
27
|
+
OCTAL_DIGITS_AFTER_ESCAPE = 2
|
28
|
+
|
27
29
|
def on_regexp(node)
|
28
30
|
each_repeated_character_class_element_loc(node) do |loc|
|
29
31
|
add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
|
@@ -32,35 +34,57 @@ module RuboCop
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
36
37
|
def each_repeated_character_class_element_loc(node)
|
37
38
|
node.parsed_tree&.each_expression do |expr|
|
38
39
|
next if skip_expression?(expr)
|
39
40
|
|
40
41
|
seen = Set.new
|
41
|
-
|
42
|
-
|
42
|
+
group_expressions(node, expr.expressions) do |group|
|
43
|
+
group_source = group.map(&:to_s).join
|
43
44
|
|
44
|
-
|
45
|
-
current_child = enum.next
|
46
|
-
next if within_interpolation?(node, current_child)
|
45
|
+
yield source_range(group) if seen.include?(group_source)
|
47
46
|
|
48
|
-
|
49
|
-
|
47
|
+
seen << group_source
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
50
51
|
|
51
|
-
|
52
|
-
next if start_with_escaped_zero_number?(current_child_source, next_child.to_s)
|
52
|
+
private
|
53
53
|
|
54
|
-
|
55
|
-
|
54
|
+
def group_expressions(node, expressions)
|
55
|
+
# Create a mutable list to simplify state tracking while we iterate.
|
56
|
+
expressions = expressions.to_a
|
56
57
|
|
57
|
-
|
58
|
-
|
58
|
+
until expressions.empty?
|
59
|
+
# With we may need to compose a group of multiple expressions.
|
60
|
+
group = [expressions.shift]
|
61
|
+
next if within_interpolation?(node, group.first)
|
62
|
+
|
63
|
+
# With regexp_parser < 2.7 escaped octal sequences may be up to 3
|
64
|
+
# separate expressions ("\\0", "0", "1").
|
65
|
+
pop_octal_digits(group, expressions) if escaped_octal?(group.first.to_s)
|
66
|
+
|
67
|
+
yield(group)
|
59
68
|
end
|
60
69
|
end
|
61
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
62
70
|
|
63
|
-
|
71
|
+
def pop_octal_digits(current_child, expressions)
|
72
|
+
OCTAL_DIGITS_AFTER_ESCAPE.times do
|
73
|
+
next_child = expressions.first
|
74
|
+
break unless octal?(next_child.to_s)
|
75
|
+
|
76
|
+
current_child << expressions.shift
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def source_range(children)
|
81
|
+
return children.first.expression if children.size == 1
|
82
|
+
|
83
|
+
range_between(
|
84
|
+
children.first.expression.begin_pos,
|
85
|
+
children.last.expression.begin_pos + children.last.to_s.length
|
86
|
+
)
|
87
|
+
end
|
64
88
|
|
65
89
|
def skip_expression?(expr)
|
66
90
|
expr.type != :set || expr.token == :intersection
|
@@ -75,9 +99,12 @@ module RuboCop
|
|
75
99
|
interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
|
76
100
|
end
|
77
101
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
102
|
+
def escaped_octal?(string)
|
103
|
+
string.length == 2 && string[0] == '\\' && octal?(string[1])
|
104
|
+
end
|
105
|
+
|
106
|
+
def octal?(char)
|
107
|
+
('0'..'7').cover?(char)
|
81
108
|
end
|
82
109
|
|
83
110
|
def interpolation_locs(node)
|
@@ -3,14 +3,13 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
#
|
7
|
-
# This cop emulates the following Ruby warnings in Ruby 2.6.
|
6
|
+
# Emulates the following Ruby warnings in Ruby 2.6.
|
8
7
|
#
|
9
8
|
# [source,console]
|
10
9
|
# ----
|
11
|
-
#
|
10
|
+
# $ cat example.rb
|
12
11
|
# ERB.new('hi', nil, '-', '@output_buffer')
|
13
|
-
#
|
12
|
+
# $ ruby -rerb example.rb
|
14
13
|
# example.rb:1: warning: Passing safe_level with the 2nd argument of ERB.new is
|
15
14
|
# deprecated. Do not use it, and specify other arguments as keyword arguments.
|
16
15
|
# example.rb:1: warning: Passing trim_mode with the 3rd argument of ERB.new is
|
@@ -3,8 +3,10 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
+
# Checks for `IO.select` that is incompatible with Fiber Scheduler since Ruby 3.0.
|
6
7
|
#
|
7
|
-
#
|
8
|
+
# When an array of IO objects waiting for an exception (the third argument of `IO.select`)
|
9
|
+
# is used as an argument, there is no alternative API, so offenses are not registered.
|
8
10
|
#
|
9
11
|
# NOTE: When the method is successful the return value of `IO.select` is `[[IO]]`,
|
10
12
|
# and the return value of `io.wait_readable` and `io.wait_writable` are `self`.
|
@@ -42,8 +44,8 @@ module RuboCop
|
|
42
44
|
PATTERN
|
43
45
|
|
44
46
|
def on_send(node)
|
45
|
-
read, write,
|
46
|
-
return
|
47
|
+
read, write, excepts, timeout = *io_select(node)
|
48
|
+
return if excepts && !excepts.children.empty?
|
47
49
|
return unless scheduler_compatible?(read, write) || scheduler_compatible?(write, read)
|
48
50
|
|
49
51
|
preferred = preferred_method(read, write, timeout)
|
@@ -58,6 +58,7 @@ module RuboCop
|
|
58
58
|
|
59
59
|
def on_class(node)
|
60
60
|
return unless node.parent_class && exception_class?(node.parent_class)
|
61
|
+
return if inherit_exception_class_with_omitted_namespace?(node)
|
61
62
|
|
62
63
|
message = message(node.parent_class)
|
63
64
|
|
@@ -87,6 +88,14 @@ module RuboCop
|
|
87
88
|
class_node.const_name == 'Exception'
|
88
89
|
end
|
89
90
|
|
91
|
+
def inherit_exception_class_with_omitted_namespace?(class_node)
|
92
|
+
return false if class_node.parent_class.namespace&.cbase_type?
|
93
|
+
|
94
|
+
class_node.left_siblings.any? do |sibling|
|
95
|
+
sibling.respond_to?(:identifier) && exception_class?(sibling.identifier)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
90
99
|
def preferred_base_class
|
91
100
|
PREFERRED_BASE_CLASS[style]
|
92
101
|
end
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# Checks uses of lambda without a literal block.
|
7
7
|
# It emulates the following warning in Ruby 3.0:
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# $ ruby -vwe 'lambda(&proc {})'
|
10
10
|
# ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
|
11
11
|
# -e:1: warning: lambda without a literal block is deprecated; use the proc without
|
12
12
|
# lambda instead
|
@@ -11,6 +11,16 @@ module RuboCop
|
|
11
11
|
# missing method. In other cases, the theoretical ideal handling could be
|
12
12
|
# challenging or verbose for no actual gain.
|
13
13
|
#
|
14
|
+
# Autocorrection is not supported because the position of `super` cannot be
|
15
|
+
# determined automatically.
|
16
|
+
#
|
17
|
+
# `Object` and `BasicObject` are allowed by this cop because of their
|
18
|
+
# stateless nature. However, sometimes you might want to allow other parent
|
19
|
+
# classes from this cop, for example in the case of an abstract class that is
|
20
|
+
# not meant to be called with `super`. In those cases, you can use the
|
21
|
+
# `AllowedParentClasses` option to specify which classes should be allowed
|
22
|
+
# *in addition to* `Object` and `BasicObject`.
|
23
|
+
#
|
14
24
|
# @example
|
15
25
|
# # bad
|
16
26
|
# class Employee < Person
|
@@ -57,6 +67,21 @@ module RuboCop
|
|
57
67
|
# end
|
58
68
|
# end
|
59
69
|
#
|
70
|
+
# # good
|
71
|
+
# class ClassWithNoParent
|
72
|
+
# def initialize
|
73
|
+
# do_something
|
74
|
+
# end
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# @example AllowedParentClasses: [MyAbstractClass]
|
78
|
+
# # good
|
79
|
+
# class MyConcreteClass < MyAbstractClass
|
80
|
+
# def initialize
|
81
|
+
# do_something
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
#
|
60
85
|
class MissingSuper < Base
|
61
86
|
CONSTRUCTOR_MSG = 'Call `super` to initialize state of the parent class.'
|
62
87
|
CALLBACK_MSG = 'Call `super` to invoke callback defined in the parent class.'
|
@@ -100,7 +125,7 @@ module RuboCop
|
|
100
125
|
end
|
101
126
|
|
102
127
|
def callback_method_def?(node)
|
103
|
-
return unless CALLBACKS.include?(node.method_name)
|
128
|
+
return false unless CALLBACKS.include?(node.method_name)
|
104
129
|
|
105
130
|
node.each_ancestor(:class, :sclass, :module).first
|
106
131
|
end
|
@@ -113,16 +138,20 @@ module RuboCop
|
|
113
138
|
if (block_node = node.each_ancestor(:block, :numblock).first)
|
114
139
|
return false unless (super_class = class_new_block(block_node))
|
115
140
|
|
116
|
-
!
|
141
|
+
!allowed_class?(super_class)
|
117
142
|
elsif (class_node = node.each_ancestor(:class).first)
|
118
|
-
class_node.parent_class && !
|
143
|
+
class_node.parent_class && !allowed_class?(class_node.parent_class)
|
119
144
|
else
|
120
145
|
false
|
121
146
|
end
|
122
147
|
end
|
123
148
|
|
124
|
-
def
|
125
|
-
|
149
|
+
def allowed_class?(node)
|
150
|
+
allowed_classes.include?(node.const_name)
|
151
|
+
end
|
152
|
+
|
153
|
+
def allowed_classes
|
154
|
+
@allowed_classes ||= STATELESS_CLASSES + cop_config.fetch('AllowedParentClasses', [])
|
126
155
|
end
|
127
156
|
end
|
128
157
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for mixed-case character ranges since they include likely unintended characters.
|
7
|
+
#
|
8
|
+
# Offenses are registered for regexp character classes like `/[A-z]/`
|
9
|
+
# as well as range objects like `('A'..'z')`.
|
10
|
+
#
|
11
|
+
# NOTE: Range objects cannot be autocorrected.
|
12
|
+
#
|
13
|
+
# @safety
|
14
|
+
# The cop autocorrects regexp character classes
|
15
|
+
# by replacing one character range with two: `A-z` becomes `A-Za-z`.
|
16
|
+
# In most cases this is probably what was originally intended
|
17
|
+
# but it changes the regexp to no longer match symbols it used to include.
|
18
|
+
# For this reason, this cop's autocorrect is unsafe (it will
|
19
|
+
# change the behavior of the code).
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
#
|
23
|
+
# # bad
|
24
|
+
# r = /[A-z]/
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# r = /[A-Za-z]/
|
28
|
+
class MixedCaseRange < Base
|
29
|
+
extend AutoCorrector
|
30
|
+
include RangeHelp
|
31
|
+
|
32
|
+
MSG = 'Ranges from upper to lower case ASCII letters may include unintended ' \
|
33
|
+
'characters. Instead of `A-z` (which also includes several symbols) ' \
|
34
|
+
'specify each range individually: `A-Za-z` and individually specify any symbols.'
|
35
|
+
RANGES = [('a'..'z').freeze, ('A'..'Z').freeze].freeze
|
36
|
+
|
37
|
+
def on_irange(node)
|
38
|
+
return unless node.children.compact.all?(&:str_type?)
|
39
|
+
|
40
|
+
range_start, range_end = node.children
|
41
|
+
|
42
|
+
return if range_start.nil? || range_end.nil?
|
43
|
+
|
44
|
+
add_offense(node) if unsafe_range?(range_start.value, range_end.value)
|
45
|
+
end
|
46
|
+
alias on_erange on_irange
|
47
|
+
|
48
|
+
def on_regexp(node)
|
49
|
+
each_unsafe_regexp_range(node) do |loc|
|
50
|
+
add_offense(loc) do |corrector|
|
51
|
+
corrector.replace(loc, rewrite_regexp_range(loc.source))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def each_unsafe_regexp_range(node)
|
57
|
+
node.parsed_tree&.each_expression do |expr|
|
58
|
+
next if skip_expression?(expr)
|
59
|
+
|
60
|
+
range_pairs(expr).reject do |range_start, range_end|
|
61
|
+
next if skip_range?(range_start, range_end)
|
62
|
+
|
63
|
+
next unless unsafe_range?(range_start.text, range_end.text)
|
64
|
+
|
65
|
+
yield(build_source_range(range_start, range_end))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def build_source_range(range_start, range_end)
|
73
|
+
range_between(range_start.expression.begin_pos, range_end.expression.end_pos)
|
74
|
+
end
|
75
|
+
|
76
|
+
def range_for(char)
|
77
|
+
RANGES.detect do |range|
|
78
|
+
range.include?(char)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def range_pairs(expr)
|
83
|
+
RuboCop::Cop::Utils::RegexpRanges.new(expr).pairs
|
84
|
+
end
|
85
|
+
|
86
|
+
def unsafe_range?(range_start, range_end)
|
87
|
+
range_for(range_start) != range_for(range_end)
|
88
|
+
end
|
89
|
+
|
90
|
+
def skip_expression?(expr)
|
91
|
+
!(expr.type == :set && expr.token == :character)
|
92
|
+
end
|
93
|
+
|
94
|
+
def skip_range?(range_start, range_end)
|
95
|
+
[range_start, range_end].any? do |bound|
|
96
|
+
bound.type == :escape
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def rewrite_regexp_range(source)
|
101
|
+
open, close = source.split('-')
|
102
|
+
first = [open, range_for(open).end]
|
103
|
+
second = [range_for(close).begin, close]
|
104
|
+
"#{first.uniq.join('-')}#{second.uniq.join('-')}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -74,6 +74,7 @@ module RuboCop
|
|
74
74
|
extend AutoCorrector
|
75
75
|
include AllowedMethods
|
76
76
|
include AllowedPattern
|
77
|
+
include IgnoredNode
|
77
78
|
|
78
79
|
CONVERSION_METHOD_CLASS_MAPPING = {
|
79
80
|
to_i: "#{Integer.name}(%<number_object>s, 10)",
|
@@ -116,7 +117,11 @@ module RuboCop
|
|
116
117
|
corrected_method: correct_method(node, receiver)
|
117
118
|
)
|
118
119
|
add_offense(node, message: message) do |corrector|
|
120
|
+
next if part_of_ignored_node?(node)
|
121
|
+
|
119
122
|
corrector.replace(node, correct_method(node, node.receiver))
|
123
|
+
|
124
|
+
ignore_node(node)
|
120
125
|
end
|
121
126
|
end
|
122
127
|
end
|
@@ -6,13 +6,13 @@ module RuboCop
|
|
6
6
|
# Checks for uses of numbered parameter assignment.
|
7
7
|
# It emulates the following warning in Ruby 2.7:
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# $ ruby -ve '_1 = :value'
|
10
10
|
# ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin19]
|
11
11
|
# -e:1: warning: `_1' is reserved for numbered parameter; consider another name
|
12
12
|
#
|
13
13
|
# Assigning to a numbered parameter (from `_1` to `_9`) causes an error in Ruby 3.0.
|
14
14
|
#
|
15
|
-
#
|
15
|
+
# $ ruby -ve '_1 = :value'
|
16
16
|
# ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-darwin19]
|
17
17
|
# -e:1: _1 is reserved for numbered parameter
|
18
18
|
#
|
@@ -65,13 +65,13 @@ module RuboCop
|
|
65
65
|
def on_when(node)
|
66
66
|
regexp_conditions = node.conditions.select(&:regexp_type?)
|
67
67
|
|
68
|
-
@valid_ref = regexp_conditions.
|
68
|
+
@valid_ref = regexp_conditions.filter_map { |condition| check_regexp(condition) }.max
|
69
69
|
end
|
70
70
|
|
71
71
|
def on_in_pattern(node)
|
72
72
|
regexp_patterns = regexp_patterns(node)
|
73
73
|
|
74
|
-
@valid_ref = regexp_patterns.
|
74
|
+
@valid_ref = regexp_patterns.filter_map { |pattern| check_regexp(pattern) }.max
|
75
75
|
end
|
76
76
|
|
77
77
|
def on_nth_ref(node)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for redundant quantifiers inside Regexp literals.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# /(?:x+)+/
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# /(?:x)+/
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# /(?:x+)/
|
17
|
+
#
|
18
|
+
# # bad
|
19
|
+
# /(?:x+)?/
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# /(?:x)*/
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# /(?:x*)/
|
26
|
+
class RedundantRegexpQuantifiers < Base
|
27
|
+
include RangeHelp
|
28
|
+
extend AutoCorrector
|
29
|
+
|
30
|
+
MSG_REDUNDANT_QUANTIFIER = 'Replace redundant quantifiers ' \
|
31
|
+
'`%<inner_quantifier>s` and `%<outer_quantifier>s` ' \
|
32
|
+
'with a single `%<replacement>s`.'
|
33
|
+
|
34
|
+
def on_regexp(node)
|
35
|
+
each_redundantly_quantified_pair(node) do |group, child|
|
36
|
+
replacement = merged_quantifier(group, child)
|
37
|
+
add_offense(
|
38
|
+
quantifier_range(group, child),
|
39
|
+
message: message(group, child, replacement)
|
40
|
+
) do |corrector|
|
41
|
+
# drop outer quantifier
|
42
|
+
corrector.replace(group.loc.quantifier, '')
|
43
|
+
# replace inner quantifier
|
44
|
+
corrector.replace(child.loc.quantifier, replacement)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def each_redundantly_quantified_pair(node)
|
52
|
+
seen = Set.new
|
53
|
+
node.parsed_tree&.each_expression do |(expr)|
|
54
|
+
next if seen.include?(expr) || !redundant_group?(expr) || !mergeable_quantifier(expr)
|
55
|
+
|
56
|
+
expr.each_expression do |(subexp)|
|
57
|
+
seen << subexp
|
58
|
+
break unless redundantly_quantifiable?(subexp)
|
59
|
+
|
60
|
+
yield(expr, subexp) if mergeable_quantifier(subexp)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def redundant_group?(expr)
|
66
|
+
expr.is?(:passive, :group) && expr.count { |child| child.type != :free_space } == 1
|
67
|
+
end
|
68
|
+
|
69
|
+
def redundantly_quantifiable?(node)
|
70
|
+
redundant_group?(node) || character_set?(node) || node.terminal?
|
71
|
+
end
|
72
|
+
|
73
|
+
def character_set?(expr)
|
74
|
+
expr.is?(:character, :set)
|
75
|
+
end
|
76
|
+
|
77
|
+
def mergeable_quantifier(expr)
|
78
|
+
# Merging reluctant or possessive quantifiers would be more complex,
|
79
|
+
# and Ruby does not emit warnings for these cases.
|
80
|
+
return unless expr.quantifier&.greedy?
|
81
|
+
|
82
|
+
# normalize quantifiers, e.g. "{1,}" => "+"
|
83
|
+
case expr.quantity
|
84
|
+
when [0, -1]
|
85
|
+
'*'
|
86
|
+
when [0, 1]
|
87
|
+
'?'
|
88
|
+
when [1, -1]
|
89
|
+
'+'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def merged_quantifier(exp1, exp2)
|
94
|
+
quantifier1 = mergeable_quantifier(exp1)
|
95
|
+
quantifier2 = mergeable_quantifier(exp2)
|
96
|
+
if quantifier1 == quantifier2
|
97
|
+
# (?:a+)+ equals (?:a+) ; (?:a*)* equals (?:a*) ; # (?:a?)? equals (?:a?)
|
98
|
+
quantifier1
|
99
|
+
else
|
100
|
+
# (?:a+)*, (?:a+)?, (?:a*)+, (?:a*)?, (?:a?)+, (?:a?)* - all equal (?:a*)
|
101
|
+
'*'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def quantifier_range(group, child)
|
106
|
+
range_between(child.loc.quantifier.begin_pos, group.loc.quantifier.end_pos)
|
107
|
+
end
|
108
|
+
|
109
|
+
def message(group, child, replacement)
|
110
|
+
format(
|
111
|
+
MSG_REDUNDANT_QUANTIFIER,
|
112
|
+
inner_quantifier: child.quantifier.to_s,
|
113
|
+
outer_quantifier: group.quantifier.to_s,
|
114
|
+
replacement: replacement
|
115
|
+
)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -49,6 +49,11 @@ module RuboCop
|
|
49
49
|
(str #redundant_feature?))
|
50
50
|
PATTERN
|
51
51
|
|
52
|
+
# @!method pp_const?(node)
|
53
|
+
def_node_matcher :pp_const?, <<~PATTERN
|
54
|
+
(const {nil? cbase} :PP)
|
55
|
+
PATTERN
|
56
|
+
|
52
57
|
def on_send(node)
|
53
58
|
return unless redundant_require_statement?(node)
|
54
59
|
|
@@ -72,16 +77,16 @@ module RuboCop
|
|
72
77
|
feature_name == 'enumerator' ||
|
73
78
|
(target_ruby_version >= 2.1 && feature_name == 'thread') ||
|
74
79
|
(target_ruby_version >= 2.2 && RUBY_22_LOADED_FEATURES.include?(feature_name)) ||
|
75
|
-
(target_ruby_version >= 2.5 && feature_name == 'pp' && !
|
80
|
+
(target_ruby_version >= 2.5 && feature_name == 'pp' && !need_to_require_pp?) ||
|
76
81
|
(target_ruby_version >= 2.7 && feature_name == 'ruby2_keywords') ||
|
77
82
|
(target_ruby_version >= 3.1 && feature_name == 'fiber') ||
|
78
83
|
(target_ruby_version >= 3.2 && feature_name == 'set')
|
79
84
|
end
|
80
85
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
81
86
|
|
82
|
-
def
|
87
|
+
def need_to_require_pp?
|
83
88
|
processed_source.ast.each_descendant(:send).any? do |node|
|
84
|
-
PRETTY_PRINT_METHODS.include?(node.method_name)
|
89
|
+
pp_const?(node.receiver) || PRETTY_PRINT_METHODS.include?(node.method_name)
|
85
90
|
end
|
86
91
|
end
|
87
92
|
end
|