rubocop 0.77.0 → 0.79.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +39 -26
  4. data/lib/rubocop.rb +11 -2
  5. data/lib/rubocop/ast/builder.rb +43 -42
  6. data/lib/rubocop/ast/node/def_node.rb +11 -0
  7. data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
  8. data/lib/rubocop/ast/traversal.rb +11 -3
  9. data/lib/rubocop/cli/command/auto_genenerate_config.rb +7 -7
  10. data/lib/rubocop/cli/command/show_cops.rb +11 -4
  11. data/lib/rubocop/config.rb +1 -1
  12. data/lib/rubocop/config_loader.rb +19 -19
  13. data/lib/rubocop/config_obsoletion.rb +3 -3
  14. data/lib/rubocop/config_validator.rb +55 -95
  15. data/lib/rubocop/cop/autocorrect_logic.rb +7 -4
  16. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +2 -2
  17. data/lib/rubocop/cop/cop.rb +3 -1
  18. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  19. data/lib/rubocop/cop/generator.rb +3 -4
  20. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  21. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  22. data/lib/rubocop/cop/layout/hash_alignment.rb +8 -4
  23. data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -4
  24. data/lib/rubocop/cop/{metrics → layout}/line_length.rb +5 -78
  25. data/lib/rubocop/cop/layout/multiline_block_layout.rb +14 -5
  26. data/lib/rubocop/cop/layout/space_around_operators.rb +31 -6
  27. data/lib/rubocop/cop/layout/space_before_block_braces.rb +17 -0
  28. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  29. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
  30. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +89 -0
  31. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -3
  32. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +4 -4
  33. data/lib/rubocop/cop/migration/department_name.rb +16 -1
  34. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  35. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -7
  36. data/lib/rubocop/cop/mixin/line_length_help.rb +88 -0
  37. data/lib/rubocop/cop/mixin/rational_literal.rb +18 -0
  38. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  39. data/lib/rubocop/cop/mixin/trailing_comma.rb +6 -3
  40. data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
  41. data/lib/rubocop/cop/offense.rb +11 -0
  42. data/lib/rubocop/cop/registry.rb +7 -2
  43. data/lib/rubocop/cop/style/attr.rb +8 -0
  44. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  45. data/lib/rubocop/cop/style/guard_clause.rb +3 -2
  46. data/lib/rubocop/cop/style/if_unless_modifier.rb +38 -3
  47. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  48. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +4 -207
  49. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +168 -0
  50. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
  51. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  52. data/lib/rubocop/cop/style/multiline_when_then.rb +5 -1
  53. data/lib/rubocop/cop/style/numeric_predicate.rb +4 -3
  54. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
  55. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -1
  56. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  57. data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
  58. data/lib/rubocop/formatter/base_formatter.rb +2 -2
  59. data/lib/rubocop/formatter/json_formatter.rb +6 -5
  60. data/lib/rubocop/node_pattern.rb +1 -1
  61. data/lib/rubocop/options.rb +8 -8
  62. data/lib/rubocop/result_cache.rb +2 -0
  63. data/lib/rubocop/rspec/shared_contexts.rb +5 -0
  64. data/lib/rubocop/runner.rb +5 -1
  65. data/lib/rubocop/target_ruby.rb +151 -0
  66. data/lib/rubocop/version.rb +1 -1
  67. metadata +12 -5
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ class MethodCallWithArgsParentheses
7
+ # Style omit_parentheses
8
+ module OmitParentheses
9
+ TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
10
+
11
+ def on_send(node)
12
+ return unless node.parenthesized?
13
+ return if node.implicit_call?
14
+ return if super_call_without_arguments?(node)
15
+ return if allowed_camel_case_method_call?(node)
16
+ return if legitimate_call_with_parentheses?(node)
17
+
18
+ add_offense(node, location: node.loc.begin.join(node.loc.end))
19
+ end
20
+ alias on_csend on_send
21
+ alias on_super on_send
22
+ alias on_yield on_send
23
+
24
+ def autocorrect(node)
25
+ lambda do |corrector|
26
+ if parentheses_at_the_end_of_multiline_call?(node)
27
+ corrector.replace(args_begin(node), ' \\')
28
+ else
29
+ corrector.replace(args_begin(node), ' ')
30
+ end
31
+ corrector.remove(node.loc.end)
32
+ end
33
+ end
34
+
35
+ def message(_node = nil)
36
+ 'Omit parentheses for method calls with arguments.'
37
+ end
38
+
39
+ private
40
+
41
+ def super_call_without_arguments?(node)
42
+ node.super_type? && node.arguments.none?
43
+ end
44
+
45
+ def allowed_camel_case_method_call?(node)
46
+ node.camel_case_method? &&
47
+ (node.arguments.none? ||
48
+ cop_config['AllowParenthesesInCamelCaseMethod'])
49
+ end
50
+
51
+ def parentheses_at_the_end_of_multiline_call?(node)
52
+ node.multiline? &&
53
+ node.loc.begin.source_line
54
+ .gsub(TRAILING_WHITESPACE_REGEX, '')
55
+ .end_with?('(')
56
+ end
57
+
58
+ def legitimate_call_with_parentheses?(node)
59
+ call_in_literals?(node) ||
60
+ call_with_ambiguous_arguments?(node) ||
61
+ call_in_logical_operators?(node) ||
62
+ call_in_optional_arguments?(node) ||
63
+ allowed_multiline_call_with_parentheses?(node) ||
64
+ allowed_chained_call_with_parentheses?(node)
65
+ end
66
+
67
+ def call_in_literals?(node)
68
+ node.parent &&
69
+ (node.parent.pair_type? ||
70
+ node.parent.array_type? ||
71
+ node.parent.range_type? ||
72
+ splat?(node.parent) ||
73
+ ternary_if?(node.parent))
74
+ end
75
+
76
+ def call_in_logical_operators?(node)
77
+ node.parent &&
78
+ (logical_operator?(node.parent) ||
79
+ node.parent.send_type? &&
80
+ node.parent.arguments.any?(&method(:logical_operator?)))
81
+ end
82
+
83
+ def call_in_optional_arguments?(node)
84
+ node.parent &&
85
+ (node.parent.optarg_type? || node.parent.kwoptarg_type?)
86
+ end
87
+
88
+ def call_with_ambiguous_arguments?(node)
89
+ call_with_braced_block?(node) ||
90
+ call_as_argument_or_chain?(node) ||
91
+ hash_literal_in_arguments?(node) ||
92
+ node.descendants.any? do |n|
93
+ ambigious_literal?(n) || logical_operator?(n) ||
94
+ call_with_braced_block?(n)
95
+ end
96
+ end
97
+
98
+ def call_with_braced_block?(node)
99
+ (node.send_type? || node.super_type?) &&
100
+ node.block_node && node.block_node.braces?
101
+ end
102
+
103
+ def call_as_argument_or_chain?(node)
104
+ node.parent &&
105
+ (node.parent.send_type? && !assigned_before?(node.parent, node) ||
106
+ node.parent.csend_type? || node.parent.super_type?)
107
+ end
108
+
109
+ def hash_literal_in_arguments?(node)
110
+ node.arguments.any? do |n|
111
+ hash_literal?(n) ||
112
+ n.send_type? && node.descendants.any?(&method(:hash_literal?))
113
+ end
114
+ end
115
+
116
+ def allowed_multiline_call_with_parentheses?(node)
117
+ cop_config['AllowParenthesesInMultilineCall'] && node.multiline?
118
+ end
119
+
120
+ def allowed_chained_call_with_parentheses?(node)
121
+ return false unless cop_config['AllowParenthesesInChaining']
122
+
123
+ previous = node.descendants.first
124
+ return false unless previous&.send_type?
125
+
126
+ previous.parenthesized? ||
127
+ allowed_chained_call_with_parentheses?(previous)
128
+ end
129
+
130
+ def ambigious_literal?(node)
131
+ splat?(node) || ternary_if?(node) || regexp_slash_literal?(node) ||
132
+ unary_literal?(node)
133
+ end
134
+
135
+ def splat?(node)
136
+ node.splat_type? || node.kwsplat_type? || node.block_pass_type?
137
+ end
138
+
139
+ def ternary_if?(node)
140
+ node.if_type? && node.ternary?
141
+ end
142
+
143
+ def logical_operator?(node)
144
+ (node.and_type? || node.or_type?) && node.logical_operator?
145
+ end
146
+
147
+ def hash_literal?(node)
148
+ node.hash_type? && node.braces?
149
+ end
150
+
151
+ def regexp_slash_literal?(node)
152
+ node.regexp_type? && node.loc.begin.source == '/'
153
+ end
154
+
155
+ def unary_literal?(node)
156
+ node.numeric_type? && node.sign? ||
157
+ node.parent&.send_type? && node.parent&.unary_operation?
158
+ end
159
+
160
+ def assigned_before?(node, target)
161
+ node.assignment? &&
162
+ node.loc.operator.begin < target.loc.begin
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ class MethodCallWithArgsParentheses
7
+ # Style require_parentheses
8
+ module RequireParentheses
9
+ def on_send(node)
10
+ return if ignored_method?(node.method_name)
11
+ return if matches_ignored_pattern?(node.method_name)
12
+ return if eligible_for_parentheses_omission?(node)
13
+ return unless node.arguments? && !node.parenthesized?
14
+
15
+ add_offense(node)
16
+ end
17
+ alias on_csend on_send
18
+ alias on_super on_send
19
+ alias on_yield on_send
20
+
21
+ def autocorrect(node)
22
+ lambda do |corrector|
23
+ corrector.replace(args_begin(node), '(')
24
+
25
+ unless args_parenthesized?(node)
26
+ corrector.insert_after(args_end(node), ')')
27
+ end
28
+ end
29
+ end
30
+
31
+ def message(_node = nil)
32
+ 'Use parentheses for method calls with arguments.'
33
+ end
34
+
35
+ private
36
+
37
+ def eligible_for_parentheses_omission?(node)
38
+ node.operator_method? || node.setter_method? || ignored_macro?(node)
39
+ end
40
+
41
+ def included_macros_list
42
+ cop_config.fetch('IncludedMacros', []).map(&:to_sym)
43
+ end
44
+
45
+ def ignored_macro?(node)
46
+ cop_config['IgnoreMacros'] &&
47
+ node.macro? &&
48
+ !included_macros_list.include?(node.method_name)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -53,7 +53,7 @@ module RuboCop
53
53
  end
54
54
 
55
55
  def max_line_length
56
- config.for_cop('Metrics/LineLength')['Max'] || 80
56
+ config.for_cop('Layout/LineLength')['Max'] || 80
57
57
  end
58
58
  end
59
59
  end
@@ -35,7 +35,7 @@ module RuboCop
35
35
  return if !node.children.last.nil? && !node.multiline? && node.then?
36
36
 
37
37
  # With more than one statements after then, there's not offense
38
- return if node.children.last&.begin_type?
38
+ return if accept_node_type?(node.body)
39
39
 
40
40
  add_offense(node, location: :begin)
41
41
  end
@@ -49,6 +49,10 @@ module RuboCop
49
49
  )
50
50
  end
51
51
  end
52
+
53
+ def accept_node_type?(node)
54
+ node&.begin_type? || node&.array_type? || node&.hash_type?
55
+ end
52
56
  end
53
57
  end
54
58
  end
@@ -54,9 +54,10 @@ module RuboCop
54
54
  }.freeze
55
55
 
56
56
  def on_send(node)
57
- return if node.each_ancestor(:send, :block).any? do |ancestor|
58
- ignored_method?(ancestor.method_name)
59
- end
57
+ return if ignored_method?(node.method_name) ||
58
+ node.each_ancestor(:send, :block).any? do |ancestor|
59
+ ignored_method?(ancestor.method_name)
60
+ end
60
61
 
61
62
  numeric, replacement = check(node)
62
63
 
@@ -88,27 +88,27 @@ module RuboCop
88
88
  end
89
89
 
90
90
  def contains_preferred_delimiter?(node, type)
91
- preferred_delimiters = preferred_delimiters_for(type)
92
- node
93
- .children.map { |n| string_source(n) }.compact
94
- .any? { |s| preferred_delimiters.any? { |d| s.include?(d) } }
91
+ contains_delimiter?(node, preferred_delimiters_for(type))
95
92
  end
96
93
 
97
94
  def include_same_character_as_used_for_delimiter?(node, type)
98
95
  return false unless %w[%w %i].include?(type)
99
96
 
100
97
  used_delimiters = matchpairs(begin_source(node)[-1])
101
- escaped_delimiters = used_delimiters.map { |d| "\\#{d}" }.join('|')
98
+ contains_delimiter?(node, used_delimiters)
99
+ end
102
100
 
101
+ def contains_delimiter?(node, delimiters)
102
+ delimiters_regexp = Regexp.union(delimiters)
103
103
  node
104
104
  .children.map { |n| string_source(n) }.compact
105
- .any? { |s| Regexp.new(escaped_delimiters) =~ s }
105
+ .any? { |s| delimiters_regexp =~ s }
106
106
  end
107
107
 
108
108
  def string_source(node)
109
109
  if node.is_a?(String)
110
110
  node
111
- elsif node.respond_to?(:type) && node.str_type?
111
+ elsif node.respond_to?(:type) && (node.str_type? || node.sym_type?)
112
112
  node.source
113
113
  end
114
114
  end
@@ -20,8 +20,14 @@ module RuboCop
20
20
  # a, *b, _ = foo()
21
21
  # # => The correction `a, *b, = foo()` is a syntax error
22
22
  #
23
- # # good if AllowNamedUnderscoreVariables is true
23
+ # @example AllowNamedUnderscoreVariables: true (default)
24
+ # # good
24
25
  # a, b, _something = foo()
26
+ #
27
+ # @example AllowNamedUnderscoreVariables: false
28
+ # # bad
29
+ # a, b, _something = foo()
30
+ #
25
31
  class TrailingUnderscoreVariable < Cop
26
32
  include SurroundingSpace
27
33
  include RangeHelp
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for while and until statements that would fit on one line
7
7
  # if written as a modifier while/until. The maximum line length is
8
- # configured in the `Metrics/LineLength` cop.
8
+ # configured in the `Layout/LineLength` cop.
9
9
  #
10
10
  # @example
11
11
  # # bad
@@ -67,9 +67,16 @@ module RuboCop
67
67
 
68
68
  NONCOMMUTATIVE_OPERATORS = %i[===].freeze
69
69
 
70
+ PROGRAM_NAMES = %i[$0 $PROGRAM_NAME].freeze
71
+
72
+ def_node_matcher :file_constant_equal_program_name?, <<~PATTERN
73
+ (send #source_file_path_constant? {:== :!=} (gvar #program_name?))
74
+ PATTERN
75
+
70
76
  def on_send(node)
71
77
  return unless yoda_compatible_condition?(node)
72
- return if equality_only? && non_equality_operator?(node)
78
+ return if equality_only? && non_equality_operator?(node) ||
79
+ file_constant_equal_program_name?(node)
73
80
 
74
81
  valid_yoda?(node) || add_offense(node)
75
82
  end
@@ -135,6 +142,14 @@ module RuboCop
135
142
  def noncommutative_operator?(node)
136
143
  NONCOMMUTATIVE_OPERATORS.include?(node.method_name)
137
144
  end
145
+
146
+ def source_file_path_constant?(node)
147
+ node.source == '__FILE__'
148
+ end
149
+
150
+ def program_name?(name)
151
+ PROGRAM_NAMES.include?(name)
152
+ end
138
153
  end
139
154
  end
140
155
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Metrics/LineLength
3
+ # rubocop:disable Layout/LineLength
4
4
 
5
5
  module RuboCop
6
6
  module Formatter
@@ -41,7 +41,7 @@ module RuboCop
41
41
  # * `#finished`
42
42
  #
43
43
  class BaseFormatter
44
- # rubocop:enable Metrics/LineLength
44
+ # rubocop:enable Layout/LineLength
45
45
 
46
46
  # @api public
47
47
  #
@@ -53,11 +53,12 @@ module RuboCop
53
53
 
54
54
  def hash_for_offense(offense)
55
55
  {
56
- severity: offense.severity.name,
57
- message: offense.message,
58
- cop_name: offense.cop_name,
59
- corrected: offense.corrected?,
60
- location: hash_for_location(offense)
56
+ severity: offense.severity.name,
57
+ message: offense.message,
58
+ cop_name: offense.cop_name,
59
+ corrected: offense.corrected?,
60
+ correctable: offense.correctable?,
61
+ location: hash_for_location(offense)
61
62
  }
62
63
  end
63
64
 
@@ -380,7 +380,7 @@ module RuboCop
380
380
  def compile_seq_head
381
381
  return unless seq_head?
382
382
 
383
- fail_due_to 'sequences can not start with <' \
383
+ fail_due_to 'sequences cannot start with <' \
384
384
  if @terms[0].respond_to? :call
385
385
 
386
386
  with_seq_head_context(@terms[0])
@@ -264,18 +264,18 @@ module RuboCop
264
264
  # rubocop:disable Metrics/AbcSize
265
265
  def validate_compatibility # rubocop:disable Metrics/MethodLength
266
266
  if only_includes_redundant_disable?
267
- raise OptionArgumentError, 'Lint/RedundantCopDisableDirective can ' \
268
- 'not be used with --only.'
267
+ raise OptionArgumentError, 'Lint/RedundantCopDisableDirective cannot ' \
268
+ 'be used with --only.'
269
269
  end
270
270
  if except_syntax?
271
- raise OptionArgumentError, 'Syntax checking can not be turned off.'
271
+ raise OptionArgumentError, 'Syntax checking cannot be turned off.'
272
272
  end
273
273
  unless boolean_or_empty_cache?
274
274
  raise OptionArgumentError, '-C/--cache argument must be true or false'
275
275
  end
276
276
 
277
277
  if display_only_fail_level_offenses_with_autocorrect?
278
- raise OptionArgumentError, '--autocorrect can not be used with ' \
278
+ raise OptionArgumentError, '--autocorrect cannot be used with ' \
279
279
  '--display-only-fail-level-offenses'
280
280
  end
281
281
  validate_auto_gen_config
@@ -329,8 +329,8 @@ module RuboCop
329
329
  auto_gen_config: '-P/--parallel uses caching to speed up execution, ' \
330
330
  'while --auto-gen-config needs a non-cached run, ' \
331
331
  'so they cannot be combined.',
332
- fail_fast: '-P/--parallel can not be combined with -F/--fail-fast.',
333
- auto_correct: '-P/--parallel can not be combined with --auto-correct.'
332
+ fail_fast: '-P/--parallel cannot be combined with -F/--fail-fast.',
333
+ auto_correct: '-P/--parallel cannot be combined with --auto-correct.'
334
334
  }
335
335
 
336
336
  combos.each do |key, msg|
@@ -373,7 +373,7 @@ module RuboCop
373
373
  # This module contains help texts for command line options.
374
374
  module OptionsHelp
375
375
  MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
376
- # rubocop:disable Metrics/LineLength
376
+ # rubocop:disable Layout/LineLength
377
377
  FORMATTER_OPTION_LIST = RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS.keys
378
378
 
379
379
  TEXT = {
@@ -455,6 +455,6 @@ module RuboCop
455
455
  'reports. This is useful for editor integration.'],
456
456
  init: 'Generate a .rubocop.yml file in the current directory.'
457
457
  }.freeze
458
- # rubocop:enable Metrics/LineLength
458
+ # rubocop:enable Layout/LineLength
459
459
  end
460
460
  end