rubocop 1.4.2 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +52 -9
  4. data/config/obsoletion.yml +196 -0
  5. data/lib/rubocop.rb +14 -0
  6. data/lib/rubocop/cli.rb +5 -1
  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 +65 -247
  11. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  12. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  13. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  14. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  15. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  16. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  17. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  18. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  19. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  20. data/lib/rubocop/config_validator.rb +18 -4
  21. data/lib/rubocop/cop/base.rb +17 -15
  22. data/lib/rubocop/cop/cop.rb +2 -2
  23. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  24. data/lib/rubocop/cop/generator.rb +1 -1
  25. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  26. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  27. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  28. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  29. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
  30. data/lib/rubocop/cop/layout/line_length.rb +6 -16
  31. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
  32. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  33. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +1 -1
  34. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
  35. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  36. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +8 -5
  37. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  38. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  39. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  40. data/lib/rubocop/cop/metrics/parameter_lists.rb +64 -1
  41. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  42. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  43. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  44. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  45. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  46. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  47. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  48. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  49. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  50. data/lib/rubocop/cop/style/and_or.rb +10 -0
  51. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  52. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  53. data/lib/rubocop/cop/style/float_division.rb +44 -1
  54. data/lib/rubocop/cop/style/if_unless_modifier.rb +4 -0
  55. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  56. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  57. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  58. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  59. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  60. data/lib/rubocop/cop/style/redundant_argument.rb +15 -2
  61. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  62. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
  63. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  64. data/lib/rubocop/cop/style/sole_nested_conditional.rb +65 -3
  65. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  66. data/lib/rubocop/cop/style/string_concatenation.rb +26 -1
  67. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  68. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  69. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  70. data/lib/rubocop/core_ext/hash.rb +20 -0
  71. data/lib/rubocop/ext/regexp_node.rb +29 -12
  72. data/lib/rubocop/ext/regexp_parser.rb +20 -9
  73. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  74. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  75. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  76. data/lib/rubocop/lockfile.rb +40 -0
  77. data/lib/rubocop/version.rb +1 -1
  78. metadata +32 -5
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Encapsulation of a ConfigObsoletion rule for changing a parameter
6
+ # @api private
7
+ class ChangedEnforcedStyles < ParameterRule
8
+ BASE_MESSAGE = 'obsolete `%<parameter>s: %<value>s` (for `%<cop>s`) found in %<path>s'
9
+
10
+ def violated?
11
+ super && config[cop][parameter] == value
12
+ end
13
+
14
+ def message
15
+ base = format(BASE_MESSAGE,
16
+ parameter: parameter, value: value, cop: cop, path: smart_loaded_path)
17
+
18
+ if alternative
19
+ "#{base}\n`#{parameter}: #{value}` has been renamed to " \
20
+ "`#{parameter}: #{alternative.chomp}`."
21
+ else
22
+ "#{base}\n#{reason.chomp}"
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def value
29
+ metadata['value']
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Encapsulation of a ConfigObsoletion rule for changing a parameter
6
+ # @api private
7
+ class ChangedParameter < ParameterRule
8
+ BASE_MESSAGE = 'obsolete parameter `%<parameter>s` (for `%<cop>s`) found in %<path>s'
9
+
10
+ def message
11
+ base = format(BASE_MESSAGE, parameter: parameter, cop: cop, path: smart_loaded_path)
12
+
13
+ if alternative
14
+ "#{base}\n`#{parameter}` has been renamed to `#{alternative.chomp}`."
15
+ else
16
+ "#{base}\n#{reason.chomp}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Base class for ConfigObsoletion rules relating to cops
6
+ # @api private
7
+ class CopRule < Rule
8
+ attr_reader :old_name
9
+
10
+ def initialize(config, old_name)
11
+ super(config)
12
+ @old_name = old_name
13
+ end
14
+
15
+ def cop_rule?
16
+ true
17
+ end
18
+
19
+ def message
20
+ rule_message + "\n(obsolete configuration found in " \
21
+ "#{smart_loaded_path}, please update it)"
22
+ end
23
+
24
+ # Cop rules currently can only be failures, not warnings
25
+ def warning?
26
+ false
27
+ end
28
+
29
+ def violated?
30
+ config.key?(old_name) || config.key?(Cop::Badge.parse(old_name).cop_name)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Encapsulation of a ConfigObsoletion rule for splitting a cop's
6
+ # functionality into multiple new cops.
7
+ # @api private
8
+ class ExtractedCop < CopRule
9
+ attr_reader :gem, :department
10
+
11
+ def initialize(config, old_name, gem)
12
+ super(config, old_name)
13
+ @department, * = old_name.rpartition('/')
14
+ @gem = gem
15
+ end
16
+
17
+ def violated?
18
+ return false if feature_loaded?
19
+
20
+ affected_cops.any?
21
+ end
22
+
23
+ def rule_message
24
+ msg = '%<name>s been extracted to the `%<gem>s` gem.'
25
+ format(msg,
26
+ name: affected_cops.size > 1 ? "`#{department}` cops have" : "`#{old_name}` has",
27
+ gem: gem)
28
+ end
29
+
30
+ private
31
+
32
+ def affected_cops
33
+ return old_name unless old_name.end_with?('*')
34
+
35
+ # Handle whole departments (expressed as `Department/*`)
36
+ config.keys.grep(Regexp.new("^#{department}"))
37
+ end
38
+
39
+ def feature_loaded?
40
+ config.loaded_features.include?(gem)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Base class for ConfigObsoletion rules relating to parameters
6
+ # @api private
7
+ class ParameterRule < Rule
8
+ attr_reader :cop, :parameter, :metadata
9
+
10
+ def initialize(config, cop, parameter, metadata)
11
+ super(config)
12
+ @cop = cop
13
+ @parameter = parameter
14
+ @metadata = metadata
15
+ end
16
+
17
+ def parameter_rule?
18
+ true
19
+ end
20
+
21
+ def violated?
22
+ config[cop]&.key?(parameter)
23
+ end
24
+
25
+ def warning?
26
+ severity == 'warning'
27
+ end
28
+
29
+ private
30
+
31
+ def alternative
32
+ metadata['alternative']
33
+ end
34
+
35
+ def reason
36
+ metadata['reason']
37
+ end
38
+
39
+ def severity
40
+ metadata['severity']
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Encapsulation of a ConfigObsoletion rule for removing
6
+ # a previously defined cop.
7
+ # @api private
8
+ class RemovedCop < CopRule
9
+ attr_reader :old_name, :metadata
10
+
11
+ BASE_MESSAGE = 'The `%<old_name>s` cop has been removed'
12
+
13
+ def initialize(config, old_name, metadata)
14
+ super(config, old_name)
15
+ @metadata = metadata.is_a?(Hash) ? metadata : {}
16
+ end
17
+
18
+ def rule_message
19
+ base = format(BASE_MESSAGE, old_name: old_name)
20
+
21
+ if reason
22
+ "#{base} since #{reason.chomp}."
23
+ elsif alternatives
24
+ "#{base}. Please use #{to_sentence(alternatives, connector: 'and/or')} instead."
25
+ else
26
+ "#{base}."
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def reason
33
+ metadata['reason']
34
+ end
35
+
36
+ def alternatives
37
+ Array(metadata['alternatives']).map { |name| "`#{name}`" }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Encapsulation of a ConfigObsoletion rule for renaming
6
+ # a cop or moving it to a new department.
7
+ # @api private
8
+ class RenamedCop < CopRule
9
+ attr_reader :new_name
10
+
11
+ def initialize(config, old_name, new_name)
12
+ super(config, old_name)
13
+ @new_name = new_name
14
+ end
15
+
16
+ def rule_message
17
+ "The `#{old_name}` cop has been #{verb} to `#{new_name}`."
18
+ end
19
+
20
+ private
21
+
22
+ def moved?
23
+ old_badge = Cop::Badge.parse(old_name)
24
+ new_badge = Cop::Badge.parse(new_name)
25
+
26
+ old_badge.department != new_badge.department && old_badge.cop_name == new_badge.cop_name
27
+ end
28
+
29
+ def verb
30
+ moved? ? 'moved' : 'renamed'
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Abstract base class for ConfigObsoletion rules
6
+ # @api private
7
+ class Rule
8
+ def initialize(config)
9
+ @config = config
10
+ end
11
+
12
+ # Does this rule relate to cops?
13
+ def cop_rule?
14
+ false
15
+ end
16
+
17
+ # Does this rule relate to parameters?
18
+ def parameter_rule?
19
+ false
20
+ end
21
+
22
+ def violated?
23
+ raise NotImplementedError
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :config
29
+
30
+ def to_sentence(collection, connector: 'and')
31
+ return collection.first if collection.size == 1
32
+
33
+ [collection[0..-2].join(', '), collection[-1]].join(" #{connector} ")
34
+ end
35
+
36
+ def smart_loaded_path
37
+ PathUtil.smart_path(config.loaded_path)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ class ConfigObsoletion
5
+ # Encapsulation of a ConfigObsoletion rule for splitting a cop's
6
+ # functionality into multiple new cops.
7
+ # @api private
8
+ class SplitCop < CopRule
9
+ attr_reader :metadata
10
+
11
+ def initialize(config, old_name, metadata)
12
+ super(config, old_name)
13
+ @metadata = metadata
14
+ end
15
+
16
+ def rule_message
17
+ "The `#{old_name}` cop has been split into #{to_sentence(alternatives)}."
18
+ end
19
+
20
+ private
21
+
22
+ def alternatives
23
+ Array(metadata['alternatives']).map { |name| "`#{name}`" }
24
+ end
25
+ end
26
+ end
27
+ end
@@ -42,7 +42,7 @@ module RuboCop
42
42
  ConfigLoader.default_configuration.key?(key)
43
43
  end
44
44
 
45
- @config_obsoletion.reject_obsolete_cops_and_parameters
45
+ check_obsoletions
46
46
 
47
47
  alert_about_unrecognized_cops(invalid_cop_names)
48
48
  check_target_ruby
@@ -68,6 +68,13 @@ module RuboCop
68
68
 
69
69
  attr_reader :target_ruby
70
70
 
71
+ def check_obsoletions
72
+ @config_obsoletion.reject_obsolete!
73
+ return unless @config_obsoletion.warnings.any?
74
+
75
+ warn Rainbow("Warning: #{@config_obsoletion.warnings.join("\n")}").yellow
76
+ end
77
+
71
78
  def check_target_ruby
72
79
  return if target_ruby.supported?
73
80
 
@@ -99,10 +106,17 @@ module RuboCop
99
106
  # to do so than to pass the value around to various methods.
100
107
  next if name == 'inherit_mode'
101
108
 
102
- unknown_cops << "unrecognized cop #{name} found in " \
103
- "#{smart_loaded_path}"
109
+ suggestions = NameSimilarity.find_similar_names(name, Cop::Registry.global.map(&:cop_name))
110
+ suggestion = "Did you mean `#{suggestions.join('`, `')}`?" if suggestions.any?
111
+
112
+ message = <<~MESSAGE.rstrip
113
+ unrecognized cop #{name} found in #{smart_loaded_path}
114
+ #{suggestion}
115
+ MESSAGE
116
+
117
+ unknown_cops << message
104
118
  end
105
- raise ValidationError, unknown_cops.join(', ') if unknown_cops.any?
119
+ raise ValidationError, unknown_cops.join("\n") if unknown_cops.any?
106
120
  end
107
121
 
108
122
  def validate_syntax_cop
@@ -288,13 +288,6 @@ module RuboCop
288
288
  @current_corrector&.merge!(corrector) if corrector
289
289
  end
290
290
 
291
- def correction_strategy
292
- return :unsupported unless correctable?
293
- return :uncorrected unless autocorrect?
294
-
295
- :attempt_correction
296
- end
297
-
298
291
  ### Reserved for Commissioner:
299
292
 
300
293
  def current_offense_locations
@@ -341,33 +334,42 @@ module RuboCop
341
334
 
342
335
  # @return [Symbol, Corrector] offense status
343
336
  def correct(range)
344
- status = correction_strategy
345
-
346
337
  if block_given?
347
338
  corrector = Corrector.new(self)
348
339
  yield corrector
349
- if !corrector.empty? && !self.class.support_autocorrect?
340
+ if corrector.empty?
341
+ corrector = nil
342
+ elsif !self.class.support_autocorrect?
350
343
  raise "The Cop #{name} must `extend AutoCorrector` to be able to autocorrect"
351
344
  end
352
345
  end
353
346
 
354
- status = attempt_correction(range, corrector) if status == :attempt_correction
347
+ [use_corrector(range, corrector), corrector]
348
+ end
355
349
 
356
- [status, corrector]
350
+ # @return [Symbol] offense status
351
+ def use_corrector(range, corrector)
352
+ if autocorrect?
353
+ attempt_correction(range, corrector)
354
+ elsif corrector
355
+ :uncorrected
356
+ else
357
+ :unsupported
358
+ end
357
359
  end
358
360
 
359
361
  # @return [Symbol] offense status
360
362
  def attempt_correction(range, corrector)
361
- if corrector && !corrector.empty?
363
+ if corrector
362
364
  status = :corrected
363
365
  elsif disable_uncorrectable?
364
366
  corrector = disable_uncorrectable(range)
365
367
  status = :corrected_with_todo
366
368
  else
367
- return :uncorrected
369
+ return :unsupported
368
370
  end
369
371
 
370
- apply_correction(corrector) if corrector
372
+ apply_correction(corrector)
371
373
  status
372
374
  end
373
375
 
@@ -27,7 +27,7 @@ module RuboCop
27
27
  def add_offense(node_or_range, location: :expression, message: nil, severity: nil, &block)
28
28
  @v0_argument = node_or_range
29
29
  range = find_location(node_or_range, location)
30
- if block.nil? && !autocorrect?
30
+ if block.nil? && !support_autocorrect?
31
31
  super(range, message: message, severity: severity)
32
32
  else
33
33
  super(range, message: message, severity: severity) do |corrector|
@@ -136,7 +136,7 @@ module RuboCop
136
136
  end
137
137
 
138
138
  def correction_lambda
139
- return unless correction_strategy == :attempt_correction && support_autocorrect?
139
+ return unless support_autocorrect?
140
140
 
141
141
  dedup_on_node(@v0_argument) do
142
142
  autocorrect(@v0_argument)