rubocop 1.6.1 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -3
  4. data/config/default.yml +145 -19
  5. data/lib/rubocop.rb +16 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  7. data/lib/rubocop/comment_config.rb +6 -6
  8. data/lib/rubocop/config.rb +13 -7
  9. data/lib/rubocop/config_loader.rb +11 -14
  10. data/lib/rubocop/config_loader_resolver.rb +21 -4
  11. data/lib/rubocop/config_obsoletion.rb +5 -3
  12. data/lib/rubocop/config_store.rb +12 -1
  13. data/lib/rubocop/cop/base.rb +2 -1
  14. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
  16. data/lib/rubocop/cop/generator.rb +1 -3
  17. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  18. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  21. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  22. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
  23. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  24. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +56 -20
  25. data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
  26. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
  27. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  28. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  29. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
  30. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
  31. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  32. data/lib/rubocop/cop/layout/space_before_brackets.rb +67 -0
  33. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
  34. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  35. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
  36. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
  37. data/lib/rubocop/cop/lint/deprecated_constants.rb +80 -0
  38. data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
  39. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  40. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  41. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  42. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  43. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  44. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  45. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  46. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
  47. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  48. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  49. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  50. data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
  51. data/lib/rubocop/cop/message_annotator.rb +4 -1
  52. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  53. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  54. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  55. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  56. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  57. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  58. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  59. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  60. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  61. data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
  62. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  63. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
  64. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  65. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  66. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  67. data/lib/rubocop/cop/registry.rb +10 -0
  68. data/lib/rubocop/cop/severity.rb +3 -3
  69. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
  70. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  71. data/lib/rubocop/cop/style/collection_methods.rb +14 -1
  72. data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
  73. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  74. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  75. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  76. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  77. data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
  78. data/lib/rubocop/cop/style/float_division.rb +3 -0
  79. data/lib/rubocop/cop/style/for.rb +2 -0
  80. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  81. data/lib/rubocop/cop/style/hash_except.rb +95 -0
  82. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  83. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  84. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
  85. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
  86. data/lib/rubocop/cop/style/lambda_call.rb +2 -1
  87. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
  88. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
  89. data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
  90. data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
  91. data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
  92. data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
  93. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  94. data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
  95. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  96. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  97. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  98. data/lib/rubocop/cop/style/raise_args.rb +5 -2
  99. data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
  100. data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
  101. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  102. data/lib/rubocop/cop/style/single_line_methods.rb +36 -2
  103. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  104. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  105. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  106. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  107. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  108. data/lib/rubocop/cop/util.rb +3 -1
  109. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  110. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  111. data/lib/rubocop/magic_comment.rb +30 -1
  112. data/lib/rubocop/options.rb +10 -10
  113. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  114. data/lib/rubocop/rspec/expect_offense.rb +37 -22
  115. data/lib/rubocop/runner.rb +17 -1
  116. data/lib/rubocop/target_finder.rb +4 -2
  117. data/lib/rubocop/target_ruby.rb +47 -11
  118. data/lib/rubocop/util.rb +16 -0
  119. data/lib/rubocop/version.rb +8 -2
  120. metadata +27 -7
@@ -9,6 +9,7 @@ module RuboCop
9
9
  self.command_name = :auto_gen_config
10
10
 
11
11
  AUTO_GENERATED_FILE = '.rubocop_todo.yml'
12
+ YAML_OPTIONAL_DOC_START = /\A---(\s+#|\s*\z)/.freeze
12
13
 
13
14
  PHASE_1 = 'Phase 1 of 2: run Layout/LineLength cop'
14
15
  PHASE_2 = 'Phase 2 of 2: run all cops'
@@ -130,10 +131,10 @@ module RuboCop
130
131
  end
131
132
 
132
133
  def write_config_file(file_name, file_string, rubocop_yml_contents)
133
- File.open(file_name, 'w') do |f|
134
- f.write "inherit_from:#{file_string}\n"
135
- f.write "\n#{rubocop_yml_contents}" if /\S/.match?(rubocop_yml_contents)
136
- end
134
+ lines = /\S/.match?(rubocop_yml_contents) ? rubocop_yml_contents.split("\n", -1) : []
135
+ doc_start_index = lines.index { |line| YAML_OPTIONAL_DOC_START.match?(line) } || -1
136
+ lines.insert(doc_start_index + 1, "inherit_from:#{file_string}\n")
137
+ File.open(file_name, 'w') { |f| f.write lines.join("\n") }
137
138
  end
138
139
  end
139
140
  end
@@ -47,6 +47,12 @@ module RuboCop
47
47
  )
48
48
  end
49
49
 
50
+ def comment_only_line?(line_number)
51
+ non_comment_token_line_numbers.none? do |non_comment_line_number|
52
+ non_comment_line_number == line_number
53
+ end
54
+ end
55
+
50
56
  private
51
57
 
52
58
  def extra_enabled_comments_with_names(extras:, names:)
@@ -166,12 +172,6 @@ module RuboCop
166
172
  @all_cop_names ||= Cop::Registry.global.names - [REDUNDANT_DISABLE]
167
173
  end
168
174
 
169
- def comment_only_line?(line_number)
170
- non_comment_token_line_numbers.none? do |non_comment_line_number|
171
- non_comment_line_number == line_number
172
- end
173
- end
174
-
175
175
  def non_comment_token_line_numbers
176
176
  @non_comment_token_line_numbers ||= begin
177
177
  non_comment_tokens = processed_source.tokens.reject(&:comment?)
@@ -33,8 +33,11 @@ module RuboCop
33
33
  @validator = ConfigValidator.new(self)
34
34
  end
35
35
 
36
- def self.create(hash, path)
37
- new(hash, path).check
36
+ def self.create(hash, path, check: true)
37
+ config = new(hash, path)
38
+ config.check if check
39
+
40
+ config
38
41
  end
39
42
 
40
43
  def loaded_features
@@ -50,8 +53,8 @@ module RuboCop
50
53
  self
51
54
  end
52
55
 
53
- def_delegators :@hash, :[], :[]=, :delete, :each, :key?, :keys, :each_key,
54
- :fetch, :map, :merge, :to_h, :to_hash, :transform_values
56
+ def_delegators :@hash, :[], :[]=, :delete, :dig, :each, :key?, :keys, :each_key,
57
+ :fetch, :map, :merge, :replace, :to_h, :to_hash, :transform_values
55
58
  def_delegators :@validator, :validate, :target_ruby_version
56
59
 
57
60
  def to_s
@@ -281,6 +284,9 @@ module RuboCop
281
284
  end
282
285
 
283
286
  def enable_cop?(qualified_cop_name, cop_options)
287
+ # If the cop is explicitly enabled, the other checks can be skipped.
288
+ return true if cop_options['Enabled'] == true
289
+
284
290
  department = department_of(qualified_cop_name)
285
291
  cop_enabled = cop_options.fetch('Enabled') do
286
292
  !for_all_cops['DisabledByDefault']
@@ -292,10 +298,10 @@ module RuboCop
292
298
  end
293
299
 
294
300
  def department_of(qualified_cop_name)
295
- cop_department, cop_name = qualified_cop_name.split('/')
296
- return nil if cop_name.nil?
301
+ *cop_department, _ = qualified_cop_name.split('/')
302
+ return nil if cop_department.empty?
297
303
 
298
- self[cop_department]
304
+ self[cop_department.join('/')]
299
305
  end
300
306
  end
301
307
  end
@@ -25,16 +25,18 @@ module RuboCop
25
25
  attr_accessor :debug, :ignore_parent_exclusion,
26
26
  :disable_pending_cops, :enable_pending_cops
27
27
  attr_writer :default_configuration, :project_root
28
+ attr_reader :loaded_features
28
29
 
29
30
  alias debug? debug
30
31
  alias ignore_parent_exclusion? ignore_parent_exclusion
31
32
 
32
33
  def clear_options
33
34
  @debug = nil
35
+ @loaded_features = Set.new
34
36
  FileFinder.root_level = nil
35
37
  end
36
38
 
37
- def load_file(file)
39
+ def load_file(file, check: true)
38
40
  path = file_path(file)
39
41
 
40
42
  hash = load_yaml_configuration(path)
@@ -51,7 +53,7 @@ module RuboCop
51
53
 
52
54
  hash.delete('inherit_from')
53
55
 
54
- Config.create(hash, path)
56
+ Config.create(hash, path, check: check)
55
57
  end
56
58
 
57
59
  def load_yaml_configuration(absolute_path)
@@ -98,10 +100,10 @@ module RuboCop
98
100
  find_user_xdg_config || DEFAULT_FILE
99
101
  end
100
102
 
101
- def configuration_from_file(config_file)
103
+ def configuration_from_file(config_file, check: true)
102
104
  return default_configuration if config_file == DEFAULT_FILE
103
105
 
104
- config = load_file(config_file)
106
+ config = load_file(config_file, check: check)
105
107
  if ignore_parent_exclusion?
106
108
  print 'Ignoring AllCops/Exclude from parent folders' if debug?
107
109
  else
@@ -173,8 +175,11 @@ module RuboCop
173
175
  resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
174
176
  end
175
177
 
176
- def loaded_features
177
- @loaded_features.flatten.compact
178
+ # @api private
179
+ # Used to add features that were required inside a config or from
180
+ # the CLI using `--require`.
181
+ def add_loaded_features(loaded_features)
182
+ @loaded_features.merge(Array(loaded_features))
178
183
  end
179
184
 
180
185
  private
@@ -183,14 +188,6 @@ module RuboCop
183
188
  File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
184
189
  end
185
190
 
186
- def add_loaded_features(loaded_features)
187
- if instance_variable_defined?(:@loaded_features)
188
- instance_variable_get(:@loaded_features) << loaded_features
189
- else
190
- instance_variable_set(:@loaded_features, [loaded_features])
191
- end
192
- end
193
-
194
191
  def find_project_dotfile(target_dir)
195
192
  find_file_upwards(DOTFILE, target_dir, project_root)
196
193
  end
@@ -20,12 +20,13 @@ module RuboCop
20
20
  end
21
21
  end
22
22
 
23
- # rubocop:disable Metrics/MethodLength
24
- def resolve_inheritance(path, hash, file, debug)
23
+ def resolve_inheritance(path, hash, file, debug) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
25
24
  inherited_files = Array(hash['inherit_from'])
26
25
  base_configs(path, inherited_files, file)
27
26
  .reverse.each_with_index do |base_config, index|
28
27
  override_department_setting_for_cops(base_config, hash)
28
+ override_enabled_for_disabled_departments(base_config, hash)
29
+
29
30
  base_config.each do |k, v|
30
31
  next unless v.is_a?(Hash)
31
32
 
@@ -39,7 +40,6 @@ module RuboCop
39
40
  end
40
41
  end
41
42
  end
42
- # rubocop:enable Metrics/MethodLength
43
43
 
44
44
  def resolve_inheritance_from_gems(hash)
45
45
  gems = hash.delete('inherit_gem')
@@ -75,6 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  config = handle_disabled_by_default(config, default_configuration) if disabled_by_default
78
+ override_enabled_for_disabled_departments(default_configuration, config)
78
79
 
79
80
  opts = { inherit_mode: config['inherit_mode'] || {},
80
81
  unset_nil: unset_nil }
@@ -122,10 +123,26 @@ module RuboCop
122
123
  end
123
124
  end
124
125
 
126
+ # If a cop was previously explicitly enabled, but then superseded by the
127
+ # department being disabled, disable it.
128
+ def override_enabled_for_disabled_departments(base_hash, derived_hash)
129
+ cops_to_disable = derived_hash.each_key.with_object([]) do |key, cops|
130
+ next unless disabled?(derived_hash, key)
131
+
132
+ cops.concat(base_hash.keys.grep(Regexp.new("^#{key}/")))
133
+ end
134
+
135
+ cops_to_disable.each do |cop_name|
136
+ next unless base_hash.dig(cop_name, 'Enabled') == true
137
+
138
+ derived_hash.replace(merge({ cop_name => { 'Enabled' => false } }, derived_hash))
139
+ end
140
+ end
141
+
125
142
  private
126
143
 
127
144
  def disabled?(hash, department)
128
- hash[department] && hash[department]['Enabled'] == false
145
+ hash[department].is_a?(Hash) && hash[department]['Enabled'] == false
129
146
  end
130
147
 
131
148
  def duplicate_setting?(base_hash, derived_hash, key, inherited_file)
@@ -63,9 +63,11 @@ module RuboCop
63
63
  # Cop rules are keyed by the name of the original cop
64
64
  def load_cop_rules(rules)
65
65
  rules.flat_map do |rule_type, data|
66
- data.map do |configuration|
67
- COP_RULE_CLASSES[rule_type].new(@config, *configuration)
68
- end
66
+ data.map do |cop_name, configuration|
67
+ next unless configuration # allow configurations to be disabled with `CopName: ~`
68
+
69
+ COP_RULE_CLASSES[rule_type].new(@config, cop_name, configuration)
70
+ end.compact
69
71
  end
70
72
  end
71
73
 
@@ -4,6 +4,9 @@ module RuboCop
4
4
  # Handles caching of configurations and association of inspected
5
5
  # ruby files to configurations.
6
6
  class ConfigStore
7
+ attr_reader :validated
8
+ alias validated? validated
9
+
7
10
  def initialize
8
11
  # @options_config stores a config that is specified in the command line.
9
12
  # This takes precedence over configs located in any directories
@@ -17,6 +20,9 @@ module RuboCop
17
20
  # @object_cache maps configuration file paths to
18
21
  # configuration objects so we only need to load them once.
19
22
  @object_cache = {}
23
+
24
+ # By default the config is validated before it can be used.
25
+ @validated = true
20
26
  end
21
27
 
22
28
  def options_config=(options_config)
@@ -29,6 +35,11 @@ module RuboCop
29
35
  @options_config = ConfigLoader.default_configuration
30
36
  end
31
37
 
38
+ def unvalidated
39
+ @validated = false
40
+ self
41
+ end
42
+
32
43
  def for_file(file)
33
44
  for_dir(File.dirname(file))
34
45
  end
@@ -55,7 +66,7 @@ module RuboCop
55
66
  path = @path_cache[dir]
56
67
  @object_cache[path] ||= begin
57
68
  print "For #{dir}: " if ConfigLoader.debug?
58
- ConfigLoader.configuration_from_file(path)
69
+ ConfigLoader.configuration_from_file(path, check: validated?)
59
70
  end
60
71
  end
61
72
  end
@@ -34,6 +34,7 @@ module RuboCop
34
34
  class Base # rubocop:disable Metrics/ClassLength
35
35
  extend RuboCop::AST::Sexp
36
36
  extend NodePattern::Macros
37
+ extend ExcludeLimit
37
38
  include RuboCop::AST::Sexp
38
39
  include Util
39
40
  include IgnoredNode
@@ -351,7 +352,7 @@ module RuboCop
351
352
  def use_corrector(range, corrector)
352
353
  if autocorrect?
353
354
  attempt_correction(range, corrector)
354
- elsif corrector
355
+ elsif corrector && cop_config.fetch('AutoCorrect', true)
355
356
  :uncorrected
356
357
  else
357
358
  :unsupported
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # Allows specified configuration options to have an exclude limit
5
+ # ie. a maximum value tracked that it can be used by `--auto-gen-config`.
6
+ module ExcludeLimit
7
+ # Sets up a configuration option to have an exclude limit tracked.
8
+ # The parameter name given is transformed into a method name (eg. `Max`
9
+ # becomes `self.max=` and `MinDigits` becomes `self.min_digits=`).
10
+ def exclude_limit(parameter_name, method_name: transform(parameter_name))
11
+ define_method("#{method_name}=") do |value|
12
+ cfg = config_to_allow_offenses
13
+ cfg[:exclude_limit] ||= {}
14
+ current_max = cfg[:exclude_limit][parameter_name]
15
+ value = [current_max, value].max if current_max
16
+ cfg[:exclude_limit][parameter_name] = value
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def transform(parameter_name)
23
+ parameter_name.gsub(/(?<!\A)(?=[A-Z])/, '_').downcase
24
+ end
25
+ end
26
+ end
@@ -36,12 +36,13 @@ module RuboCop
36
36
  # spec.required_ruby_version = '>= 2.5'
37
37
  # end
38
38
  #
39
- # # good
39
+ # # accepted but not recommended
40
40
  # Gem::Specification.new do |spec|
41
41
  # spec.required_ruby_version = ['>= 2.5.0', '< 2.7.0']
42
42
  # end
43
43
  #
44
- # # good
44
+ # # accepted but not recommended, since
45
+ # # Ruby does not really follow semantic versionning
45
46
  # Gem::Specification.new do |spec|
46
47
  # spec.required_ruby_version = '~> 2.5'
47
48
  # end
@@ -81,9 +81,7 @@ module RuboCop
81
81
  SPEC_TEMPLATE = <<~SPEC
82
82
  # frozen_string_literal: true
83
83
 
84
- RSpec.describe RuboCop::Cop::%<department>s::%<cop_name>s do
85
- subject(:cop) { described_class.new(config) }
86
-
84
+ RSpec.describe RuboCop::Cop::%<department>s::%<cop_name>s, :config do
87
85
  let(:config) { RuboCop::Config.new }
88
86
 
89
87
  # TODO: Write test code
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'internal_affairs/empty_line_between_expect_offense_and_correction'
4
+ require_relative 'internal_affairs/example_description'
3
5
  require_relative 'internal_affairs/method_name_equal'
4
6
  require_relative 'internal_affairs/node_destructuring'
5
7
  require_relative 'internal_affairs/node_type_predicate'
6
8
  require_relative 'internal_affairs/offense_location_keyword'
7
- require_relative 'internal_affairs/redundant_message_argument'
9
+ require_relative 'internal_affairs/redundant_described_class_as_subject'
10
+ require_relative 'internal_affairs/redundant_let_rubocop_config_new'
8
11
  require_relative 'internal_affairs/redundant_location_argument'
12
+ require_relative 'internal_affairs/redundant_message_argument'
13
+ require_relative 'internal_affairs/style_detected_api_use'
9
14
  require_relative 'internal_affairs/useless_message_assertion'
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks whether `expect_offense` and correction expectation methods
7
+ # (i.e. `expect_correction` and `expect_no_corrections`) are separated by empty line.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # it 'registers and corrects an offense' do
12
+ # expect_offense(<<~RUBY)
13
+ # bad_method
14
+ # ^^^^^^^^^^ Use `good_method`.
15
+ # RUBY
16
+ # expect_correction(<<~RUBY)
17
+ # good_method
18
+ # RUBY
19
+ # end
20
+ #
21
+ # # good
22
+ # it 'registers and corrects an offense' do
23
+ # expect_offense(<<~RUBY)
24
+ # bad_method
25
+ # ^^^^^^^^^^ Use `good_method`.
26
+ # RUBY
27
+ #
28
+ # expect_correction(<<~RUBY)
29
+ # good_method
30
+ # RUBY
31
+ # end
32
+ #
33
+ class EmptyLineBetweenExpectOffenseAndCorrection < Base
34
+ extend AutoCorrector
35
+
36
+ MSG = 'Add empty line between `expect_offense` and `%<expect_correction>s`.'
37
+ RESTRICT_ON_SEND = %i[expect_offense].freeze
38
+ CORRECTION_EXPECTATION_METHODS = %i[expect_correction expect_no_corrections].freeze
39
+
40
+ def on_send(node)
41
+ return unless (next_sibling = node.right_sibling) && next_sibling.send_type?
42
+
43
+ method_name = next_sibling.method_name
44
+ return unless CORRECTION_EXPECTATION_METHODS.include?(method_name)
45
+
46
+ range = offense_range(node)
47
+ return unless range.last_line + 1 == next_sibling.loc.line
48
+
49
+ add_offense(range, message: format(MSG, expect_correction: method_name)) do |corrector|
50
+ corrector.insert_after(range, "\n")
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def offense_range(node)
57
+ first_argument = node.first_argument
58
+
59
+ if first_argument.respond_to?(:heredoc?) && first_argument.heredoc?
60
+ first_argument.loc.heredoc_end
61
+ else
62
+ node
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks that RSpec examples that use `expects_offense`
7
+ # or `expects_no_offenses` do not have conflicting
8
+ # descriptions.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # it 'does not register an offense' do
13
+ # expect_offense('...')
14
+ # end
15
+ #
16
+ # it 'registers an offense' do
17
+ # expect_no_offenses('...')
18
+ # end
19
+ #
20
+ # # good
21
+ # it 'registers an offense' do
22
+ # expect_offense('...')
23
+ # end
24
+ #
25
+ # it 'does not register an offense' do
26
+ # expect_no_offenses('...')
27
+ # end
28
+ class ExampleDescription < Base
29
+ class << self
30
+ attr_accessor :descriptions
31
+ end
32
+
33
+ MSG = 'Description does not match use of `%<method_name>s`.'
34
+
35
+ RESTRICT_ON_SEND = %i[
36
+ expect_offense
37
+ expect_no_offenses
38
+ expect_correction
39
+ expect_no_corrections
40
+ ].to_set.freeze
41
+
42
+ EXPECT_NO_OFFENSES_INCORRECT_DESCRIPTIONS = [
43
+ /^(adds|registers|reports|finds) (an? )?offense/,
44
+ /^flags\b/
45
+ ].freeze
46
+
47
+ EXPECT_OFFENSE_INCORRECT_DESCRIPTIONS = [
48
+ /^(does not|doesn't) (register|find|flag|report)/,
49
+ /^(does not|doesn't) add (a|an|any )?offense/
50
+ ].freeze
51
+
52
+ EXPECT_NO_CORRECTIONS_INCORRECT_DESCRIPTIONS = [
53
+ /^(auto[- ]?)?correct/
54
+ ].freeze
55
+
56
+ EXPECT_CORRECTION_INCORRECT_DESCRIPTIONS = [
57
+ /\b(does not|doesn't) (auto[- ]?)?correct/
58
+ ].freeze
59
+
60
+ def_node_matcher :offense_example?, <<~PATTERN
61
+ (block
62
+ (send _ {:it :specify} $_description)
63
+ _args
64
+ `(send nil? %RESTRICT_ON_SEND ...)
65
+ )
66
+ PATTERN
67
+
68
+ def on_send(node)
69
+ parent = node.each_ancestor(:block).first
70
+ return unless parent && (description = offense_example?(parent))
71
+
72
+ method_name = node.method_name
73
+ message = format(MSG, method_name: method_name)
74
+
75
+ regexp_group = self.class.const_get("#{method_name}_incorrect_descriptions".upcase)
76
+ check_description(description, regexp_group, message)
77
+ end
78
+
79
+ private
80
+
81
+ def check_description(description, regexps, message)
82
+ return unless regexps.any? { |regexp| regexp.match?(description.value) }
83
+
84
+ add_offense(description, message: message)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end