rubocop 1.3.1 → 1.5.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +67 -11
  4. data/lib/rubocop.rb +5 -0
  5. data/lib/rubocop/cli.rb +5 -1
  6. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
  8. data/lib/rubocop/config_loader.rb +1 -1
  9. data/lib/rubocop/config_loader_resolver.rb +5 -1
  10. data/lib/rubocop/config_obsoletion.rb +21 -3
  11. data/lib/rubocop/config_regeneration.rb +1 -1
  12. data/lib/rubocop/config_validator.rb +8 -1
  13. data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
  14. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  15. data/lib/rubocop/cop/generator.rb +1 -1
  16. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
  18. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  19. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  20. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  21. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  22. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  23. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  24. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  25. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  26. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +1 -1
  27. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  28. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +20 -6
  29. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  30. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  31. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  32. data/lib/rubocop/cop/metrics/parameter_lists.rb +64 -1
  33. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  34. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  35. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  36. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -2
  37. data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
  38. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  39. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  40. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  41. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  42. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  43. data/lib/rubocop/cop/style/and_or.rb +10 -0
  44. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  45. data/lib/rubocop/cop/style/documentation.rb +12 -1
  46. data/lib/rubocop/cop/style/format_string.rb +8 -3
  47. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  48. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  49. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  50. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  51. data/lib/rubocop/cop/style/redundant_argument.rb +75 -0
  52. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  53. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  54. data/lib/rubocop/cop/style/sole_nested_conditional.rb +49 -3
  55. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  56. data/lib/rubocop/cop/util.rb +1 -1
  57. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  58. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  59. data/lib/rubocop/core_ext/hash.rb +20 -0
  60. data/lib/rubocop/ext/regexp_node.rb +5 -10
  61. data/lib/rubocop/ext/regexp_parser.rb +2 -9
  62. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  63. data/lib/rubocop/options.rb +5 -0
  64. data/lib/rubocop/rake_task.rb +2 -2
  65. data/lib/rubocop/runner.rb +1 -1
  66. data/lib/rubocop/version.rb +1 -1
  67. metadata +12 -10
  68. data/bin/console +0 -10
  69. data/bin/rubocop-profile +0 -32
  70. data/bin/setup +0 -7
@@ -6,7 +6,12 @@ module RuboCop
6
6
  module Utils
7
7
  # @api private
8
8
  #
9
- # Helps to calculate code length for the provided node.
9
+ # Identifies repetitions `&.` on the same variable:
10
+ #
11
+ # my_var&.foo
12
+ # my_var&.bar # => repeated
13
+ # my_var = baz # => reset
14
+ # my_var&.qux # => not repeated
10
15
  module RepeatedCsendDiscount
11
16
  def reset_repeated_csend
12
17
  @repeated_csend = {}
@@ -7,10 +7,11 @@ module RuboCop
7
7
  module ConfigurableNumbering
8
8
  include ConfigurableFormatting
9
9
 
10
+ implicit_param = /\A_\d+\z/
10
11
  FORMATS = {
11
12
  snake_case: /(?:\D|_\d+|\A\d+)\z/,
12
- normalcase: /(?:\D|[^_\d]\d+|\A\d+)\z/,
13
- non_integer: /(\D|\A\d+)\z/
13
+ normalcase: /(?:\D|[^_\d]\d+|\A\d+)\z|#{implicit_param}/,
14
+ non_integer: /(\D|\A\d+)\z|#{implicit_param}/
14
15
  }.freeze
15
16
  end
16
17
  end
@@ -2,7 +2,15 @@
2
2
 
3
3
  module RuboCop
4
4
  module Cop
5
- # Common functionality for enforcing a specific superclass
5
+ # Common functionality for enforcing a specific superclass.
6
+ #
7
+ # IMPORTANT: RuboCop core depended on this module when it supported Rails department.
8
+ # Rails department has been extracted to RuboCop Rails gem.
9
+ # This module is deprecated and will be removed by RuboCop 2.0.
10
+ # It will not be updated to `RuboCop::Cop::Base` v1 API to maintain compatibility
11
+ # with existing RuboCop Rails 2.8 or lower.
12
+ #
13
+ # @api private
6
14
  module EnforceSuperclass
7
15
  def self.included(base)
8
16
  base.def_node_matcher :class_definition, <<~PATTERN
@@ -4,15 +4,48 @@ module RuboCop
4
4
  module Cop
5
5
  # This module encapsulates the ability to ignore certain methods when
6
6
  # parsing.
7
+ # Cops that use `IgnoredMethods` can accept either strings or regexes to match
8
+ # against.
7
9
  module IgnoredMethods
8
- private
10
+ # Configuration for IgnoredMethods. It is added to classes that include
11
+ # the module so that configuration can be set using the `ignored_methods`
12
+ # class macro.
13
+ module Config
14
+ attr_accessor :deprecated_key
15
+
16
+ def ignored_methods(**config)
17
+ self.deprecated_key = config[:deprecated_key]
18
+ end
19
+ end
20
+
21
+ def self.included(base)
22
+ base.extend(Config)
23
+ end
9
24
 
10
25
  def ignored_method?(name)
11
- ignored_methods.include?(name.to_s)
26
+ ignored_methods.any? do |value|
27
+ case value
28
+ when Regexp
29
+ value.match? String(name)
30
+ else
31
+ value == String(name)
32
+ end
33
+ end
12
34
  end
13
35
 
14
36
  def ignored_methods
15
- cop_config.fetch('IgnoredMethods', [])
37
+ keys = %w[IgnoredMethods]
38
+ keys << deprecated_key if deprecated_key
39
+
40
+ cop_config.slice(*keys).values.reduce(&:concat)
41
+ end
42
+
43
+ private
44
+
45
+ def deprecated_key
46
+ return unless self.class.respond_to?(:deprecated_key)
47
+
48
+ self.class.deprecated_key&.to_s
16
49
  end
17
50
  end
18
51
  end
@@ -11,6 +11,12 @@ module RuboCop
11
11
  include Metrics::Utils::RepeatedCsendDiscount
12
12
  extend NodePattern::Macros
13
13
 
14
+ # Ensure cops that include `MethodComplexity` have the config
15
+ # `attr_accessor`s that `ignored_method?` needs.
16
+ def self.included(base)
17
+ base.extend(IgnoredMethods::Config)
18
+ end
19
+
14
20
  def on_def(node)
15
21
  return if ignored_method?(node.method_name)
16
22
 
@@ -31,7 +31,7 @@ module RuboCop
31
31
  # b c { block }. <-- b is indented relative to a
32
32
  # d <-- d is indented relative to a
33
33
  def left_hand_side(lhs)
34
- lhs = lhs.parent while lhs.parent&.send_type?
34
+ lhs = lhs.parent while lhs.parent&.send_type? && lhs.parent.loc.dot
35
35
  lhs
36
36
  end
37
37
 
@@ -16,9 +16,7 @@ module RuboCop
16
16
  end
17
17
 
18
18
  def find_visibility_start(node)
19
- node.left_siblings
20
- .reverse
21
- .find(&method(:visibility_block?))
19
+ node.left_siblings.reverse.find { |sibling| visibility_block?(sibling) }
22
20
  end
23
21
 
24
22
  # Navigate to find the last protected method
@@ -104,6 +104,8 @@ module RuboCop
104
104
  def on_arg(node)
105
105
  @node = node
106
106
  name, = *node
107
+ return if allowed_identifier?(name)
108
+
107
109
  check_name(node, name, node.loc.name)
108
110
  end
109
111
  alias on_lvasgn on_arg
@@ -139,7 +141,7 @@ module RuboCop
139
141
  end
140
142
 
141
143
  def allowed_identifier?(name)
142
- allowed_identifiers.include?(name.to_s)
144
+ allowed_identifiers.include?(name.to_s.delete('@'))
143
145
  end
144
146
 
145
147
  def allowed_identifiers
@@ -72,6 +72,8 @@ module RuboCop
72
72
  end
73
73
 
74
74
  corrector.replace(node.loc.operator, node.alternate_operator)
75
+
76
+ keep_operator_precedence(corrector, node)
75
77
  end
76
78
  end
77
79
 
@@ -123,6 +125,14 @@ module RuboCop
123
125
  corrector.wrap(node, '(', ')')
124
126
  end
125
127
 
128
+ def keep_operator_precedence(corrector, node)
129
+ if node.or_type? && node.parent&.and_type?
130
+ corrector.wrap(node, '(', ')')
131
+ elsif node.and_type? && node.rhs.or_type?
132
+ corrector.wrap(node.rhs, '(', ')')
133
+ end
134
+ end
135
+
126
136
  def correctable_send?(node)
127
137
  !node.parenthesized? && node.arguments? && !node.method?(:[])
128
138
  end
@@ -55,13 +55,18 @@ module RuboCop
55
55
  padding = ((' ' * indent_width) + leading_spaces(node)).to_s
56
56
  padding_for_trailing_end = padding.sub(' ' * node.loc.end.column, '')
57
57
 
58
- replace_keyword_with_module(corrector, node)
58
+ replace_namespace_keyword(corrector, node)
59
59
  split_on_double_colon(corrector, node, padding)
60
60
  add_trailing_end(corrector, node, padding_for_trailing_end)
61
61
  end
62
62
 
63
- def replace_keyword_with_module(corrector, node)
64
- corrector.replace(node.loc.keyword, 'module')
63
+ def replace_namespace_keyword(corrector, node)
64
+ class_definition = node.left_sibling&.each_node(:class)&.find do |class_node|
65
+ class_node.identifier == node.identifier.namespace
66
+ end
67
+ namespace_keyword = class_definition ? 'class' : 'module'
68
+
69
+ corrector.replace(node.loc.keyword, namespace_keyword)
65
70
  end
66
71
 
67
72
  def split_on_double_colon(corrector, node, padding)
@@ -55,6 +55,11 @@ module RuboCop
55
55
  # Public = Class.new
56
56
  # end
57
57
  #
58
+ # # Macro calls
59
+ # module Namespace
60
+ # extend Foo
61
+ # end
62
+ #
58
63
  class Documentation < Base
59
64
  include DocumentationComment
60
65
 
@@ -83,15 +88,21 @@ module RuboCop
83
88
  return if documentation_comment?(node) || nodoc_comment?(node)
84
89
  return if compact_namespace?(node) &&
85
90
  nodoc_comment?(outer_module(node).first)
91
+ return if macro_only?(body)
86
92
 
87
93
  add_offense(node.loc.keyword, message: format(MSG, type: type))
88
94
  end
89
95
 
96
+ def macro_only?(body)
97
+ body.respond_to?(:macro?) && body.macro? ||
98
+ body.respond_to?(:children) && body.children&.all? { |child| macro_only?(child) }
99
+ end
100
+
90
101
  def namespace?(node)
91
102
  return false unless node
92
103
 
93
104
  if node.begin_type?
94
- node.children.all?(&method(:constant_declaration?))
105
+ node.children.all? { |child| constant_declaration?(child) }
95
106
  else
96
107
  constant_definition?(node)
97
108
  end
@@ -111,15 +111,20 @@ module RuboCop
111
111
  format = format_arg.source
112
112
 
113
113
  args = if param_args.one?
114
- arg = param_args.last
115
-
116
- arg.hash_type? ? "{ #{arg.source} }" : arg.source
114
+ format_single_parameter(param_args.last)
117
115
  else
118
116
  "[#{param_args.map(&:source).join(', ')}]"
119
117
  end
120
118
 
121
119
  corrector.replace(node, "#{format} % #{args}")
122
120
  end
121
+
122
+ def format_single_parameter(arg)
123
+ source = arg.source
124
+ return "{ #{source} }" if arg.hash_type?
125
+
126
+ arg.send_type? && arg.operator_method? && !arg.parenthesized? ? "(#{source})" : source
127
+ end
123
128
  end
124
129
  end
125
130
  end
@@ -17,26 +17,61 @@ module RuboCop
17
17
  include OnNormalIfUnless
18
18
  extend AutoCorrector
19
19
 
20
- MSG = 'Do not use if x; Use the ternary operator instead.'
20
+ MSG_IF_ELSE = 'Do not use `if %<expr>s;` - use `if/else` instead.'
21
+ MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
21
22
 
22
23
  def on_normal_if_unless(node)
23
24
  return unless node.else_branch
25
+ return if node.parent&.if_type?
24
26
 
25
27
  beginning = node.loc.begin
26
28
  return unless beginning&.is?(';')
27
29
 
28
- add_offense(node) do |corrector|
29
- corrector.replace(node, correct_to_ternary(node))
30
+ message = node.else_branch.if_type? ? MSG_IF_ELSE : MSG_TERNARY
31
+
32
+ add_offense(node, message: format(message, expr: node.condition.source)) do |corrector|
33
+ corrector.replace(node, autocorrect(node))
30
34
  end
31
35
  end
32
36
 
33
37
  private
34
38
 
35
- def correct_to_ternary(node)
39
+ def autocorrect(node)
40
+ return correct_elsif(node) if node.else_branch.if_type?
41
+
36
42
  else_code = node.else_branch ? node.else_branch.source : 'nil'
37
43
 
38
44
  "#{node.condition.source} ? #{node.if_branch.source} : #{else_code}"
39
45
  end
46
+
47
+ def correct_elsif(node)
48
+ <<~RUBY.chop
49
+ if #{node.condition.source}
50
+ #{node.if_branch.source}
51
+ #{build_else_branch(node.else_branch).chop}
52
+ end
53
+ RUBY
54
+ end
55
+
56
+ def build_else_branch(second_condition)
57
+ result = <<~RUBY
58
+ elsif #{second_condition.condition.source}
59
+ #{second_condition.if_branch.source}
60
+ RUBY
61
+
62
+ if second_condition.else_branch
63
+ result += if second_condition.else_branch.if_type?
64
+ build_else_branch(second_condition.else_branch)
65
+ else
66
+ <<~RUBY
67
+ else
68
+ #{second_condition.else_branch.source}
69
+ RUBY
70
+ end
71
+ end
72
+
73
+ result
74
+ end
40
75
  end
41
76
  end
42
77
  end
@@ -74,7 +74,7 @@ module RuboCop
74
74
  parent &&
75
75
  (logical_operator?(parent) ||
76
76
  parent.send_type? &&
77
- parent.arguments.any?(&method(:logical_operator?)))
77
+ parent.arguments.any? { |argument| logical_operator?(argument) })
78
78
  end
79
79
 
80
80
  def call_in_optional_arguments?(node)
@@ -110,7 +110,7 @@ module RuboCop
110
110
  def hash_literal_in_arguments?(node)
111
111
  node.arguments.any? do |n|
112
112
  hash_literal?(n) ||
113
- n.send_type? && node.descendants.any?(&method(:hash_literal?))
113
+ n.send_type? && node.descendants.any? { |descendant| hash_literal?(descendant) }
114
114
  end
115
115
  end
116
116
 
@@ -21,21 +21,30 @@ module RuboCop
21
21
  def on_send(node)
22
22
  return unless !node.arguments? && node.parenthesized?
23
23
  return if ineligible_node?(node)
24
+ return if default_argument?(node)
24
25
  return if ignored_method?(node.method_name)
25
26
  return if same_name_assignment?(node)
26
27
 
28
+ register_offense(node)
29
+ end
30
+
31
+ private
32
+
33
+ def register_offense(node)
27
34
  add_offense(offense_range(node)) do |corrector|
28
35
  corrector.remove(node.loc.begin)
29
36
  corrector.remove(node.loc.end)
30
37
  end
31
38
  end
32
39
 
33
- private
34
-
35
40
  def ineligible_node?(node)
36
41
  node.camel_case_method? || node.implicit_call? || node.prefix_not?
37
42
  end
38
43
 
44
+ def default_argument?(node)
45
+ node.parent&.optarg_type?
46
+ end
47
+
39
48
  def same_name_assignment?(node)
40
49
  any_assignment?(node) do |asgn_node|
41
50
  next variable_in_mass_assignment?(node.method_name, asgn_node) if asgn_node.masgn_type?
@@ -27,12 +27,13 @@ module RuboCop
27
27
  # # bad
28
28
  # 10_000_00 # typical representation of $10,000 in cents
29
29
  #
30
- class NumericLiterals < Cop
30
+ class NumericLiterals < Base
31
31
  # The parameter is called MinDigits (meaning the minimum number of
32
32
  # digits for which an offense can be registered), but essentially it's
33
33
  # a Max parameter (the maximum number of something that's allowed).
34
34
  include ConfigurableMax
35
35
  include IntegerNode
36
+ extend AutoCorrector
36
37
 
37
38
  MSG = 'Use underscores(_) as thousands separator and ' \
38
39
  'separate every 3 digits with them.'
@@ -46,12 +47,6 @@ module RuboCop
46
47
  check(node)
47
48
  end
48
49
 
49
- def autocorrect(node)
50
- lambda do |corrector|
51
- corrector.replace(node, format_number(node))
52
- end
53
- end
54
-
55
50
  private
56
51
 
57
52
  def max_parameter_name
@@ -67,11 +62,19 @@ module RuboCop
67
62
 
68
63
  case int
69
64
  when /^\d+$/
70
- add_offense(node) { self.max = int.size + 1 }
65
+ return unless (self.max = int.size + 1)
66
+
67
+ register_offense(node)
71
68
  when /\d{4}/, short_group_regex
72
- add_offense(node) do
73
- self.config_to_allow_offenses = { 'Enabled' => false }
74
- end
69
+ return unless (self.config_to_allow_offenses = { 'Enabled' => false })
70
+
71
+ register_offense(node)
72
+ end
73
+ end
74
+
75
+ def register_offense(node)
76
+ add_offense(node) do |corrector|
77
+ corrector.replace(node, format_number(node))
75
78
  end
76
79
  end
77
80
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser/current'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Style
8
+ # This cop checks for a redundant argument passed to certain methods.
9
+ #
10
+ # Limitations:
11
+ #
12
+ # 1. This cop matches for method names only and hence cannot tell apart
13
+ # methods with same name in different classes.
14
+ # 2. This cop is limited to methods with single parameter.
15
+ # 3. This cop is unsafe if certain special global variables (e.g. `$;`) are set.
16
+ # That depends on the nature of the target methods, of course.
17
+ #
18
+ # Method names and their redundant arguments can be configured like this:
19
+ #
20
+ # Methods:
21
+ # join: ''
22
+ # split: ' '
23
+ # foo: 2
24
+ #
25
+ # @example
26
+ # # bad
27
+ # array.join('')
28
+ # [1, 2, 3].join("")
29
+ # string.split(" ")
30
+ # "first\nsecond".split(" ")
31
+ # A.foo(2)
32
+ #
33
+ # # good
34
+ # array.join
35
+ # [1, 2, 3].join
36
+ # string.split
37
+ # "first second".split
38
+ # A.foo
39
+ class RedundantArgument < Base
40
+ MSG = 'Argument %<arg>s is redundant because it is implied by default.'
41
+
42
+ def on_send(node)
43
+ return if node.receiver.nil?
44
+ return if node.arguments.count != 1
45
+ return unless redundant_argument?(node)
46
+
47
+ add_offense(node, message: format(MSG, arg: node.arguments.first.source))
48
+ end
49
+
50
+ private
51
+
52
+ def redundant_argument?(node)
53
+ redundant_argument = redundant_arg_for_method(node.method_name.to_s)
54
+ return false if redundant_argument.nil?
55
+
56
+ node.arguments.first == redundant_argument
57
+ end
58
+
59
+ def redundant_arg_for_method(method_name)
60
+ return nil unless cop_config['Methods'].key?(method_name)
61
+
62
+ @mem ||= {}
63
+ @mem[method_name] ||=
64
+ begin
65
+ arg = cop_config['Methods'].fetch(method_name)
66
+ buffer = Parser::Source::Buffer.new('(string)', 1)
67
+ buffer.source = arg.inspect
68
+ builder = RuboCop::AST::Builder.new
69
+ Parser::CurrentRuby.new(builder).parse(buffer)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end