rubocop 1.22.1 → 1.24.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 +1 -1
- data/config/default.yml +88 -8
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
- data/lib/rubocop/cli/command/show_docs_url.rb +48 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/if_then_corrector.rb +55 -0
- data/lib/rubocop/cop/documentation.rb +19 -2
- data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
- data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +30 -23
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +46 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +3 -1
- data/lib/rubocop/cop/internal_affairs.rb +2 -0
- data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/comment_indentation.rb +31 -2
- data/lib/rubocop/cop/layout/dot_position.rb +13 -7
- data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_colon.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +4 -0
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
- data/lib/rubocop/cop/layout/space_inside_parens.rb +0 -4
- data/lib/rubocop/cop/lint/ambiguous_range.rb +3 -3
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +1 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +16 -4
- data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +6 -0
- data/lib/rubocop/cop/lint/else_layout.rb +1 -1
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +4 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +5 -2
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -9
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- 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/end_keyword_alignment.rb +1 -2
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +5 -0
- data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +4 -3
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +56 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
- data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -5
- data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +102 -0
- data/lib/rubocop/cop/naming/file_name.rb +37 -4
- data/lib/rubocop/cop/security/json_load.rb +1 -1
- data/lib/rubocop/cop/security/open.rb +11 -1
- data/lib/rubocop/cop/style/character_literal.rb +8 -1
- data/lib/rubocop/cop/style/collection_compact.rb +31 -13
- data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
- data/lib/rubocop/cop/style/commented_keyword.rb +5 -3
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +10 -0
- data/lib/rubocop/cop/style/empty_method.rb +1 -1
- data/lib/rubocop/cop/style/file_read.rb +112 -0
- data/lib/rubocop/cop/style/file_write.rb +98 -0
- data/lib/rubocop/cop/style/format_string_token.rb +2 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +2 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +22 -0
- data/lib/rubocop/cop/style/if_inside_else.rb +15 -0
- data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +68 -0
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
- data/lib/rubocop/cop/style/numeric_literals.rb +10 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +18 -39
- data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
- data/lib/rubocop/cop/style/quoted_symbols.rb +11 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +17 -3
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +5 -1
- data/lib/rubocop/cop/style/redundant_self.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -5
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -3
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +11 -1
- data/lib/rubocop/formatter/html_formatter.rb +5 -2
- data/lib/rubocop/formatter/json_formatter.rb +4 -1
- data/lib/rubocop/options.rb +6 -1
- data/lib/rubocop/remote_config.rb +2 -4
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- data/lib/rubocop.rb +11 -0
- metadata +24 -9
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Naming
|
6
|
+
# In Ruby 3.1, anonymous block forwarding has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `do_something(&block)` can be replaced
|
9
|
+
# by `do_something(&)`.
|
10
|
+
#
|
11
|
+
# It also supports the opposite style by alternative `explicit` option.
|
12
|
+
#
|
13
|
+
# @example EnforcedStyle: anonymous (default)
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# def foo(&block)
|
17
|
+
# bar(&block)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# def foo(&)
|
22
|
+
# bar(&)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @example EnforcedStyle: explicit
|
26
|
+
#
|
27
|
+
# # bad
|
28
|
+
# def foo(&)
|
29
|
+
# bar(&)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# def foo(&block)
|
34
|
+
# bar(&block)
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
class BlockForwarding < Base
|
38
|
+
include ConfigurableEnforcedStyle
|
39
|
+
extend AutoCorrector
|
40
|
+
extend TargetRubyVersion
|
41
|
+
|
42
|
+
minimum_target_ruby_version 3.1
|
43
|
+
|
44
|
+
MSG = 'Use %<style>s block forwarding.'
|
45
|
+
|
46
|
+
def on_def(node)
|
47
|
+
return if node.arguments.empty?
|
48
|
+
|
49
|
+
last_argument = node.arguments.last
|
50
|
+
return if expected_block_forwarding_style?(node, last_argument)
|
51
|
+
|
52
|
+
register_offense(last_argument)
|
53
|
+
|
54
|
+
node.each_descendant(:block_pass) do |block_pass_node|
|
55
|
+
next if block_pass_node.children.first&.sym_type?
|
56
|
+
|
57
|
+
register_offense(block_pass_node)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
alias on_defs on_def
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def expected_block_forwarding_style?(node, last_argument)
|
65
|
+
if style == :anonymous
|
66
|
+
!explicit_block_argument?(last_argument) ||
|
67
|
+
use_kwarg_in_method_definition?(node) ||
|
68
|
+
use_block_argument_as_local_variable?(node, last_argument)
|
69
|
+
else
|
70
|
+
!anonymous_block_argument?(last_argument)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def use_kwarg_in_method_definition?(node)
|
75
|
+
node.arguments.each_descendant(:kwarg, :kwoptarg).any?
|
76
|
+
end
|
77
|
+
|
78
|
+
def anonymous_block_argument?(node)
|
79
|
+
node.blockarg_type? && node.name.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
def explicit_block_argument?(node)
|
83
|
+
node.blockarg_type? && !node.name.nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
def use_block_argument_as_local_variable?(node, last_argument)
|
87
|
+
return if node.body.nil?
|
88
|
+
|
89
|
+
node.body.each_descendant(:lvar).any? do |lvar|
|
90
|
+
!lvar.parent.block_pass_type? && lvar.source == last_argument.source[1..-1]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def register_offense(block_argument)
|
95
|
+
add_offense(block_argument, message: format(MSG, style: style)) do |corrector|
|
96
|
+
corrector.replace(block_argument, '&')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -14,6 +14,18 @@ module RuboCop
|
|
14
14
|
# (i.e. `bundler-console` becomes `Bundler::Console`). As such, the
|
15
15
|
# gemspec is supposed to be named `bundler-console.gemspec`.
|
16
16
|
#
|
17
|
+
# When `ExpectMatchingDefinition` (default: `false`) is `true`, the cop requires
|
18
|
+
# each file to have a class, module or `Struct` defined in it that matches
|
19
|
+
# the filename. This can be further configured using
|
20
|
+
# `CheckDefinitionPathHierarchy` (default: `true`) to determine whether the
|
21
|
+
# path should match the namespace of the above definition.
|
22
|
+
#
|
23
|
+
# When `IgnoreExecutableScripts` (default: `true`) is `true`, files that start
|
24
|
+
# with a shebang line are not considered by the cop.
|
25
|
+
#
|
26
|
+
# When `Regex` is set, the cop will flag any filename that does not match
|
27
|
+
# the regular expression.
|
28
|
+
#
|
17
29
|
# @example
|
18
30
|
# # bad
|
19
31
|
# lib/layoutManager.rb
|
@@ -28,11 +40,19 @@ module RuboCop
|
|
28
40
|
include RangeHelp
|
29
41
|
|
30
42
|
MSG_SNAKE_CASE = 'The name of this source file (`%<basename>s`) should use snake_case.'
|
31
|
-
MSG_NO_DEFINITION = '
|
43
|
+
MSG_NO_DEFINITION = '`%<basename>s` should define a class or module called `%<namespace>s`.'
|
32
44
|
MSG_REGEX = '`%<basename>s` should match `%<regex>s`.'
|
33
45
|
|
34
46
|
SNAKE_CASE = /^[\d[[:lower:]]_.?!]+$/.freeze
|
35
47
|
|
48
|
+
# @!method struct_definition(node)
|
49
|
+
def_node_matcher :struct_definition, <<~PATTERN
|
50
|
+
{
|
51
|
+
(casgn $_ $_ (send (const {nil? cbase} :Struct) :new ...))
|
52
|
+
(casgn $_ $_ (block (send (const {nil? cbase} :Struct) :new ...) ...))
|
53
|
+
}
|
54
|
+
PATTERN
|
55
|
+
|
36
56
|
def on_new_investigation
|
37
57
|
file_path = processed_source.file_path
|
38
58
|
return if config.file_to_exclude?(file_path) || config.allowed_camel_case_file?(file_path)
|
@@ -103,6 +123,10 @@ module RuboCop
|
|
103
123
|
cop_config['CheckDefinitionPathHierarchy']
|
104
124
|
end
|
105
125
|
|
126
|
+
def definition_path_hierarchy_roots
|
127
|
+
cop_config['CheckDefinitionPathHierarchyRoots'] || []
|
128
|
+
end
|
129
|
+
|
106
130
|
def regex
|
107
131
|
cop_config['Regex']
|
108
132
|
end
|
@@ -126,7 +150,7 @@ module RuboCop
|
|
126
150
|
name = namespace.pop
|
127
151
|
|
128
152
|
on_node(%i[class module casgn], node) do |child|
|
129
|
-
next unless (const = child
|
153
|
+
next unless (const = find_definition(child))
|
130
154
|
|
131
155
|
const_namespace, const_name = *const
|
132
156
|
next if name != const_name && !match_acronym?(name, const_name)
|
@@ -138,6 +162,15 @@ module RuboCop
|
|
138
162
|
nil
|
139
163
|
end
|
140
164
|
|
165
|
+
def find_definition(node)
|
166
|
+
node.defined_module || defined_struct(node)
|
167
|
+
end
|
168
|
+
|
169
|
+
def defined_struct(node)
|
170
|
+
namespace, name = *struct_definition(node)
|
171
|
+
s(:const, namespace, name) if name
|
172
|
+
end
|
173
|
+
|
141
174
|
def match_namespace(node, namespace, expected)
|
142
175
|
match_partial = partial_matcher!(expected)
|
143
176
|
|
@@ -177,13 +210,13 @@ module RuboCop
|
|
177
210
|
allowed_acronyms.any? { |acronym| expected.gsub(acronym.capitalize, acronym) == name }
|
178
211
|
end
|
179
212
|
|
180
|
-
def to_namespace(path)
|
213
|
+
def to_namespace(path) # rubocop:disable Metrics/AbcSize
|
181
214
|
components = Pathname(path).each_filename.to_a
|
182
215
|
# To convert a pathname to a Ruby namespace, we need a starting point
|
183
216
|
# But RC can be run from any working directory, and can check any path
|
184
217
|
# We can't assume that the working directory, or any other, is the
|
185
218
|
# "starting point" to build a namespace.
|
186
|
-
start =
|
219
|
+
start = definition_path_hierarchy_roots
|
187
220
|
start_index = nil
|
188
221
|
|
189
222
|
# To find the closest namespace root take the path components, and
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
# security issues.
|
8
8
|
#
|
9
9
|
# @safety
|
10
|
-
#
|
10
|
+
# This cop's autocorrection is unsafe because it's potentially dangerous.
|
11
11
|
# If using a stream, like `JSON.load(open('file'))`, it will need to call
|
12
12
|
# `#read` manually, like `JSON.parse(open('file').read)`.
|
13
13
|
# If reading single values (rather than proper JSON objects), like
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Security
|
6
|
-
# This cop checks for the use of `Kernel#open` and `URI.open
|
6
|
+
# This cop checks for the use of `Kernel#open` and `URI.open` with dynamic
|
7
|
+
# data.
|
7
8
|
#
|
8
9
|
# `Kernel#open` and `URI.open` enable not only file access but also process
|
9
10
|
# invocation by prefixing a pipe symbol (e.g., `open("| ls")`).
|
@@ -11,6 +12,9 @@ module RuboCop
|
|
11
12
|
# the argument of `Kernel#open` and `URI.open`. It would be better to use
|
12
13
|
# `File.open`, `IO.popen` or `URI.parse#open` explicitly.
|
13
14
|
#
|
15
|
+
# NOTE: `open` and `URI.open` with literal strings are not flagged by this
|
16
|
+
# cop.
|
17
|
+
#
|
14
18
|
# @safety
|
15
19
|
# This cop could register false positives if `open` is redefined
|
16
20
|
# in a class and then used without a receiver in that class.
|
@@ -18,12 +22,18 @@ module RuboCop
|
|
18
22
|
# @example
|
19
23
|
# # bad
|
20
24
|
# open(something)
|
25
|
+
# open("| #{something}")
|
21
26
|
# URI.open(something)
|
22
27
|
#
|
23
28
|
# # good
|
24
29
|
# File.open(something)
|
25
30
|
# IO.popen(something)
|
26
31
|
# URI.parse(something).open
|
32
|
+
#
|
33
|
+
# # good (literal strings)
|
34
|
+
# open("foo.text")
|
35
|
+
# open("| foo")
|
36
|
+
# URI.open("http://example.com")
|
27
37
|
class Open < Base
|
28
38
|
MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
|
29
39
|
RESTRICT_ON_SEND = %i[open].freeze
|
@@ -4,6 +4,12 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# Checks for uses of the character literal ?x.
|
7
|
+
# Starting with Ruby 1.9 character literals are
|
8
|
+
# essentially one-character strings, so this syntax
|
9
|
+
# is mostly redundant at this point.
|
10
|
+
#
|
11
|
+
# ? character literal can be used to express meta and control character.
|
12
|
+
# That's a good use case of ? literal so it doesn't count it as an offense.
|
7
13
|
#
|
8
14
|
# @example
|
9
15
|
# # bad
|
@@ -12,8 +18,9 @@ module RuboCop
|
|
12
18
|
# # good
|
13
19
|
# 'x'
|
14
20
|
#
|
15
|
-
# # good
|
21
|
+
# # good - control & meta escapes
|
16
22
|
# ?\C-\M-d
|
23
|
+
# "\C-\M-d" # same as above
|
17
24
|
class CharacterLiteral < Base
|
18
25
|
include StringHelp
|
19
26
|
extend AutoCorrector
|
@@ -16,6 +16,7 @@ module RuboCop
|
|
16
16
|
#
|
17
17
|
# @example
|
18
18
|
# # bad
|
19
|
+
# array.reject(&:nil?)
|
19
20
|
# array.reject { |e| e.nil? }
|
20
21
|
# array.select { |e| !e.nil? }
|
21
22
|
#
|
@@ -23,6 +24,7 @@ module RuboCop
|
|
23
24
|
# array.compact
|
24
25
|
#
|
25
26
|
# # bad
|
27
|
+
# hash.reject!(&:nil?)
|
26
28
|
# hash.reject! { |k, v| v.nil? }
|
27
29
|
# hash.select! { |k, v| !v.nil? }
|
28
30
|
#
|
@@ -37,11 +39,18 @@ module RuboCop
|
|
37
39
|
|
38
40
|
RESTRICT_ON_SEND = %i[reject reject! select select!].freeze
|
39
41
|
|
42
|
+
# @!method reject_method_with_block_pass?(node)
|
43
|
+
def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
|
44
|
+
(send _ {:reject :reject!}
|
45
|
+
(block_pass
|
46
|
+
(sym :nil?)))
|
47
|
+
PATTERN
|
48
|
+
|
40
49
|
# @!method reject_method?(node)
|
41
50
|
def_node_matcher :reject_method?, <<~PATTERN
|
42
51
|
(block
|
43
52
|
(send
|
44
|
-
_
|
53
|
+
_ {:reject :reject!})
|
45
54
|
$(args ...)
|
46
55
|
(send
|
47
56
|
$(lvar _) :nil?))
|
@@ -51,7 +60,7 @@ module RuboCop
|
|
51
60
|
def_node_matcher :select_method?, <<~PATTERN
|
52
61
|
(block
|
53
62
|
(send
|
54
|
-
_
|
63
|
+
_ {:select :select!})
|
55
64
|
$(args ...)
|
56
65
|
(send
|
57
66
|
(send
|
@@ -59,16 +68,9 @@ module RuboCop
|
|
59
68
|
PATTERN
|
60
69
|
|
61
70
|
def on_send(node)
|
62
|
-
|
63
|
-
return unless block_node&.block_type?
|
64
|
-
|
65
|
-
return unless (method_name, args, receiver =
|
66
|
-
reject_method?(block_node) || select_method?(block_node))
|
71
|
+
return unless (range = offense_range(node))
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
range = offense_range(node, block_node)
|
71
|
-
good = good_method_name(method_name)
|
73
|
+
good = good_method_name(node.method_name)
|
72
74
|
message = format(MSG, good: good, bad: range.source)
|
73
75
|
|
74
76
|
add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
|
@@ -76,6 +78,22 @@ module RuboCop
|
|
76
78
|
|
77
79
|
private
|
78
80
|
|
81
|
+
def offense_range(node)
|
82
|
+
if reject_method_with_block_pass?(node)
|
83
|
+
range(node, node)
|
84
|
+
else
|
85
|
+
block_node = node.parent
|
86
|
+
|
87
|
+
return unless block_node&.block_type?
|
88
|
+
unless (args, receiver = reject_method?(block_node) || select_method?(block_node))
|
89
|
+
return
|
90
|
+
end
|
91
|
+
return unless args.last.source == receiver.source
|
92
|
+
|
93
|
+
range(node, block_node)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
79
97
|
def good_method_name(method_name)
|
80
98
|
if method_name.to_s.end_with?('!')
|
81
99
|
'compact!'
|
@@ -84,8 +102,8 @@ module RuboCop
|
|
84
102
|
end
|
85
103
|
end
|
86
104
|
|
87
|
-
def
|
88
|
-
range_between(
|
105
|
+
def range(begin_pos_node, end_pos_node)
|
106
|
+
range_between(begin_pos_node.loc.selector.begin_pos, end_pos_node.loc.end.end_pos)
|
89
107
|
end
|
90
108
|
end
|
91
109
|
end
|
@@ -77,14 +77,14 @@ module RuboCop
|
|
77
77
|
|
78
78
|
def collection_looping_method?(node)
|
79
79
|
# TODO: Remove `Symbol#to_s` after supporting only Ruby >= 2.7.
|
80
|
-
method_name = node.
|
80
|
+
method_name = node.method_name.to_s
|
81
81
|
method_name.start_with?('each') || method_name.end_with?('_each')
|
82
82
|
end
|
83
83
|
|
84
84
|
def same_collection_looping?(node, sibling)
|
85
85
|
sibling&.block_type? &&
|
86
86
|
sibling.send_node.method?(node.method_name) &&
|
87
|
-
sibling.
|
87
|
+
sibling.receiver == node.receiver &&
|
88
88
|
sibling.send_node.arguments == node.send_node.arguments
|
89
89
|
end
|
90
90
|
end
|
@@ -52,9 +52,11 @@ module RuboCop
|
|
52
52
|
ALLOWED_COMMENTS = %w[:nodoc: :yields: rubocop:disable rubocop:todo].freeze
|
53
53
|
ALLOWED_COMMENT_REGEXES = ALLOWED_COMMENTS.map { |c| /#\s*#{c}/ }.freeze
|
54
54
|
|
55
|
+
REGEXP = /(?<keyword>\S+).*#/.freeze
|
56
|
+
|
55
57
|
def on_new_investigation
|
56
58
|
processed_source.comments.each do |comment|
|
57
|
-
next unless offensive?(comment) && (match =
|
59
|
+
next unless offensive?(comment) && (match = source_line(comment).match(REGEXP))
|
58
60
|
|
59
61
|
register_offense(comment, match[:keyword])
|
60
62
|
end
|
@@ -76,12 +78,12 @@ module RuboCop
|
|
76
78
|
end
|
77
79
|
|
78
80
|
def offensive?(comment)
|
79
|
-
line =
|
81
|
+
line = source_line(comment)
|
80
82
|
KEYWORD_REGEXES.any? { |r| r.match?(line) } &&
|
81
83
|
ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
|
82
84
|
end
|
83
85
|
|
84
|
-
def
|
86
|
+
def source_line(comment)
|
85
87
|
comment.location.expression.source_line
|
86
88
|
end
|
87
89
|
end
|
@@ -79,6 +79,8 @@ module RuboCop
|
|
79
79
|
when_nodes.each do |when_node|
|
80
80
|
conditions = when_node.conditions
|
81
81
|
|
82
|
+
replace_then_with_line_break(corrector, conditions, when_node)
|
83
|
+
|
82
84
|
next unless conditions.size > 1
|
83
85
|
|
84
86
|
range = range_between(conditions.first.source_range.begin_pos,
|
@@ -97,6 +99,14 @@ module RuboCop
|
|
97
99
|
line_beginning = case_range.adjust(begin_pos: -case_range.column)
|
98
100
|
corrector.insert_before(line_beginning, comments)
|
99
101
|
end
|
102
|
+
|
103
|
+
def replace_then_with_line_break(corrector, conditions, when_node)
|
104
|
+
return unless when_node.parent.parent && when_node.then?
|
105
|
+
|
106
|
+
range = range_between(conditions.last.source_range.end_pos, when_node.loc.begin.end_pos)
|
107
|
+
|
108
|
+
corrector.replace(range, "\n")
|
109
|
+
end
|
100
110
|
end
|
101
111
|
end
|
102
112
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Favor `File.(bin)read` convenience methods.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# ## text mode
|
10
|
+
# # bad
|
11
|
+
# File.open(filename).read
|
12
|
+
# File.open(filename, &:read)
|
13
|
+
# File.open(filename) { |f| f.read }
|
14
|
+
# File.open(filename) do |f|
|
15
|
+
# f.read
|
16
|
+
# end
|
17
|
+
# File.open(filename, 'r').read
|
18
|
+
# File.open(filename, 'r', &:read)
|
19
|
+
# File.open(filename, 'r') do |f|
|
20
|
+
# f.read
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# File.read(filename)
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# ## binary mode
|
28
|
+
# # bad
|
29
|
+
# File.open(filename, 'rb').read
|
30
|
+
# File.open(filename, 'rb', &:read)
|
31
|
+
# File.open(filename, 'rb') do |f|
|
32
|
+
# f.read
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# File.binread(filename)
|
37
|
+
#
|
38
|
+
class FileRead < Base
|
39
|
+
extend AutoCorrector
|
40
|
+
include RangeHelp
|
41
|
+
|
42
|
+
MSG = 'Use `File.%<read_method>s`.'
|
43
|
+
|
44
|
+
RESTRICT_ON_SEND = %i[open].freeze
|
45
|
+
|
46
|
+
READ_FILE_START_TO_FINISH_MODES = %w[r rt rb r+ r+t r+b].to_set.freeze
|
47
|
+
|
48
|
+
# @!method file_open?(node)
|
49
|
+
def_node_matcher :file_open?, <<~PATTERN
|
50
|
+
(send
|
51
|
+
(const {nil? cbase} :File)
|
52
|
+
:open
|
53
|
+
$_
|
54
|
+
(str $%READ_FILE_START_TO_FINISH_MODES)?
|
55
|
+
$(block-pass (sym :read))?
|
56
|
+
)
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
# @!method send_read?(node)
|
60
|
+
def_node_matcher :send_read?, <<~PATTERN
|
61
|
+
(send _ :read)
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
# @!method block_read?(node)
|
65
|
+
def_node_matcher :block_read?, <<~PATTERN
|
66
|
+
(block _ (args (arg $_)) (send (lvar $_) :read))
|
67
|
+
PATTERN
|
68
|
+
|
69
|
+
def on_send(node)
|
70
|
+
evidence(node) do |filename, mode, read_node|
|
71
|
+
message = format(MSG, read_method: read_method(mode))
|
72
|
+
|
73
|
+
add_offense(read_node, message: message) do |corrector|
|
74
|
+
range = range_between(node.loc.selector.begin_pos, read_node.loc.expression.end_pos)
|
75
|
+
replacement = "#{read_method(mode)}(#{filename.source})"
|
76
|
+
|
77
|
+
corrector.replace(range, replacement)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def evidence(node)
|
85
|
+
file_open?(node) do |filename, mode_array, block_pass|
|
86
|
+
read_node?(node, block_pass) do |read_node|
|
87
|
+
yield(filename, mode_array.first || 'r', read_node)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def read_node?(node, block_pass)
|
93
|
+
if block_pass.any?
|
94
|
+
yield(node)
|
95
|
+
elsif file_open_read?(node.parent)
|
96
|
+
yield(node.parent)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def file_open_read?(node)
|
101
|
+
return true if send_read?(node)
|
102
|
+
|
103
|
+
block_read?(node) { |block_arg, read_lvar| block_arg == read_lvar }
|
104
|
+
end
|
105
|
+
|
106
|
+
def read_method(mode)
|
107
|
+
mode.end_with?('b') ? :binread : :read
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Favor `File.(bin)write` convenience methods.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# ## text mode
|
10
|
+
# # bad
|
11
|
+
# File.open(filename, 'w').write(content)
|
12
|
+
# File.open(filename, 'w') do |f|
|
13
|
+
# f.write(content)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# File.write(filename, content)
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# ## binary mode
|
21
|
+
# # bad
|
22
|
+
# File.open(filename, 'wb').write(content)
|
23
|
+
# File.open(filename, 'wb') do |f|
|
24
|
+
# f.write(content)
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# File.binwrite(filename, content)
|
29
|
+
#
|
30
|
+
class FileWrite < Base
|
31
|
+
extend AutoCorrector
|
32
|
+
include RangeHelp
|
33
|
+
|
34
|
+
MSG = 'Use `File.%<write_method>s`.'
|
35
|
+
|
36
|
+
RESTRICT_ON_SEND = %i[open].to_set.freeze
|
37
|
+
|
38
|
+
TRUNCATING_WRITE_MODES = %w[w wt wb w+ w+t w+b].to_set.freeze
|
39
|
+
|
40
|
+
# @!method file_open?(node)
|
41
|
+
def_node_matcher :file_open?, <<~PATTERN
|
42
|
+
(send
|
43
|
+
(const {nil? cbase} :File)
|
44
|
+
:open
|
45
|
+
$_
|
46
|
+
(str $%TRUNCATING_WRITE_MODES)
|
47
|
+
(block-pass (sym :write))?
|
48
|
+
)
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
# @!method send_write?(node)
|
52
|
+
def_node_matcher :send_write?, <<~PATTERN
|
53
|
+
(send _ :write $_)
|
54
|
+
PATTERN
|
55
|
+
|
56
|
+
# @!method block_write?(node)
|
57
|
+
def_node_matcher :block_write?, <<~PATTERN
|
58
|
+
(block _ (args (arg $_)) (send (lvar $_) :write $_))
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
def on_send(node)
|
62
|
+
evidence(node) do |filename, mode, content, write_node|
|
63
|
+
message = format(MSG, write_method: write_method(mode))
|
64
|
+
|
65
|
+
add_offense(write_node, message: message) do |corrector|
|
66
|
+
range = range_between(node.loc.selector.begin_pos, write_node.loc.expression.end_pos)
|
67
|
+
replacement = "#{write_method(mode)}(#{filename.source}, #{content.source})"
|
68
|
+
|
69
|
+
corrector.replace(range, replacement)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def evidence(node)
|
75
|
+
file_open?(node) do |filename, mode|
|
76
|
+
file_open_write?(node.parent) do |content|
|
77
|
+
yield(filename, mode, content, node.parent)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def file_open_write?(node)
|
85
|
+
content = send_write?(node) || block_write?(node) do |block_arg, lvar, write_arg|
|
86
|
+
write_arg if block_arg == lvar
|
87
|
+
end
|
88
|
+
|
89
|
+
yield(content) if content
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_method(mode)
|
93
|
+
mode.end_with?('b') ? :binwrite : :write
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -102,7 +102,8 @@ module RuboCop
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def use_ignored_method?(node)
|
105
|
-
|
105
|
+
send_parent = node.each_ancestor(:send).first
|
106
|
+
send_parent && ignored_method?(send_parent.method_name)
|
106
107
|
end
|
107
108
|
|
108
109
|
def unannotated_format?(node, detected_style)
|
@@ -101,7 +101,8 @@ module RuboCop
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def requires_parens?(node)
|
104
|
-
node.call_type? && node.arguments.any? && !node.parenthesized?
|
104
|
+
(node.call_type? && node.arguments.any? && !node.parenthesized?) ||
|
105
|
+
node.or_type? || node.and_type?
|
105
106
|
end
|
106
107
|
|
107
108
|
def multi_argument(node)
|