rubocop 1.7.0 → 1.10.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/LICENSE.txt +1 -1
- data/README.md +4 -3
- data/config/default.yml +137 -31
- data/config/obsoletion.yml +4 -0
- data/lib/rubocop.rb +14 -1
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
- data/lib/rubocop/comment_config.rb +6 -6
- data/lib/rubocop/config.rb +5 -2
- data/lib/rubocop/config_loader.rb +7 -14
- data/lib/rubocop/config_store.rb +12 -1
- data/lib/rubocop/cop/base.rb +2 -1
- data/lib/rubocop/cop/exclude_limit.rb +26 -0
- data/lib/rubocop/cop/gemspec/date_assignment.rb +56 -0
- data/lib/rubocop/cop/generator.rb +1 -3
- data/lib/rubocop/cop/internal_affairs.rb +5 -1
- data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
- data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
- data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
- data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
- data/lib/rubocop/cop/layout/class_structure.rb +7 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +38 -18
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
- data/lib/rubocop/cop/layout/line_length.rb +2 -1
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +26 -0
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/space_before_brackets.rb +19 -16
- data/lib/rubocop/cop/lint/debugger.rb +58 -14
- data/lib/rubocop/cop/lint/deprecated_constants.rb +80 -0
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +13 -4
- data/lib/rubocop/cop/lint/duplicate_require.rb +2 -2
- data/lib/rubocop/cop/lint/else_layout.rb +1 -1
- data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
- data/lib/rubocop/cop/lint/multiple_comparison.rb +4 -4
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
- data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
- data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +5 -3
- data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
- data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
- data/lib/rubocop/cop/message_annotator.rb +4 -1
- data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
- data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
- data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
- data/lib/rubocop/cop/mixin/code_length.rb +3 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
- data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
- data/lib/rubocop/cop/mixin/preferred_delimiters.rb +2 -2
- data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
- data/lib/rubocop/cop/naming/variable_name.rb +2 -0
- data/lib/rubocop/cop/naming/variable_number.rb +2 -9
- data/lib/rubocop/cop/registry.rb +1 -1
- data/lib/rubocop/cop/severity.rb +3 -3
- data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
- data/lib/rubocop/cop/style/constant_visibility.rb +27 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
- data/lib/rubocop/cop/style/double_negation.rb +2 -2
- data/lib/rubocop/cop/style/empty_literal.rb +6 -2
- data/lib/rubocop/cop/style/endless_method.rb +102 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +138 -49
- data/lib/rubocop/cop/style/explicit_block_argument.rb +11 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +6 -7
- data/lib/rubocop/cop/style/float_division.rb +3 -0
- data/lib/rubocop/cop/style/format_string_token.rb +18 -2
- data/lib/rubocop/cop/style/hash_conversion.rb +81 -0
- data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
- data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
- data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
- data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
- data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
- data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
- data/lib/rubocop/cop/style/raise_args.rb +3 -2
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +32 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
- data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/magic_comment.rb +30 -1
- data/lib/rubocop/options.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +5 -2
- data/lib/rubocop/runner.rb +1 -0
- data/lib/rubocop/target_ruby.rb +47 -11
- data/lib/rubocop/version.rb +2 -2
- metadata +24 -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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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?)
|
data/lib/rubocop/config.rb
CHANGED
@@ -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)
|
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
|
@@ -25,17 +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
|
34
|
-
@loaded_features =
|
35
|
+
@loaded_features = Set.new
|
35
36
|
FileFinder.root_level = nil
|
36
37
|
end
|
37
38
|
|
38
|
-
def load_file(file)
|
39
|
+
def load_file(file, check: true)
|
39
40
|
path = file_path(file)
|
40
41
|
|
41
42
|
hash = load_yaml_configuration(path)
|
@@ -52,7 +53,7 @@ module RuboCop
|
|
52
53
|
|
53
54
|
hash.delete('inherit_from')
|
54
55
|
|
55
|
-
Config.create(hash, path)
|
56
|
+
Config.create(hash, path, check: check)
|
56
57
|
end
|
57
58
|
|
58
59
|
def load_yaml_configuration(absolute_path)
|
@@ -99,10 +100,10 @@ module RuboCop
|
|
99
100
|
find_user_xdg_config || DEFAULT_FILE
|
100
101
|
end
|
101
102
|
|
102
|
-
def configuration_from_file(config_file)
|
103
|
+
def configuration_from_file(config_file, check: true)
|
103
104
|
return default_configuration if config_file == DEFAULT_FILE
|
104
105
|
|
105
|
-
config = load_file(config_file)
|
106
|
+
config = load_file(config_file, check: check)
|
106
107
|
if ignore_parent_exclusion?
|
107
108
|
print 'Ignoring AllCops/Exclude from parent folders' if debug?
|
108
109
|
else
|
@@ -174,19 +175,11 @@ module RuboCop
|
|
174
175
|
resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
|
175
176
|
end
|
176
177
|
|
177
|
-
def loaded_features
|
178
|
-
@loaded_features.flatten.compact
|
179
|
-
end
|
180
|
-
|
181
178
|
# @api private
|
182
179
|
# Used to add features that were required inside a config or from
|
183
180
|
# the CLI using `--require`.
|
184
181
|
def add_loaded_features(loaded_features)
|
185
|
-
|
186
|
-
instance_variable_get(:@loaded_features) << loaded_features
|
187
|
-
else
|
188
|
-
instance_variable_set(:@loaded_features, [loaded_features])
|
189
|
-
end
|
182
|
+
@loaded_features.merge(Array(loaded_features))
|
190
183
|
end
|
191
184
|
|
192
185
|
private
|
data/lib/rubocop/config_store.rb
CHANGED
@@ -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
|
data/lib/rubocop/cop/base.rb
CHANGED
@@ -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
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Gemspec
|
6
|
+
# This cop checks that `date =` is not used in gemspec file.
|
7
|
+
# It is set automatically when the gem is packaged.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# Gem::Specification.new do |spec|
|
13
|
+
# s.name = 'your_cool_gem_name'
|
14
|
+
# spec.date = Time.now.strftime('%Y-%m-%d')
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# Gem::Specification.new do |spec|
|
19
|
+
# s.name = 'your_cool_gem_name'
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
class DateAssignment < Base
|
23
|
+
include RangeHelp
|
24
|
+
extend AutoCorrector
|
25
|
+
|
26
|
+
MSG = 'Do not use `date =` in gemspec, it is set automatically when the gem is packaged.'
|
27
|
+
|
28
|
+
def_node_matcher :gem_specification, <<~PATTERN
|
29
|
+
(block
|
30
|
+
(send
|
31
|
+
(const
|
32
|
+
(const {cbase nil?} :Gem) :Specification) :new)
|
33
|
+
...)
|
34
|
+
PATTERN
|
35
|
+
|
36
|
+
def on_block(block_node)
|
37
|
+
return unless gem_specification(block_node)
|
38
|
+
|
39
|
+
block_parameter = block_node.arguments.first.source
|
40
|
+
|
41
|
+
date_assignment = block_node.descendants.detect do |node|
|
42
|
+
node.send_type? && node.receiver&.source == block_parameter && node.method?(:date=)
|
43
|
+
end
|
44
|
+
|
45
|
+
return unless date_assignment
|
46
|
+
|
47
|
+
add_offense(date_assignment) do |corrector|
|
48
|
+
range = range_by_whole_lines(date_assignment.source_range, include_final_newline: true)
|
49
|
+
|
50
|
+
corrector.remove(range)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
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,10 +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/
|
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'
|
9
13
|
require_relative 'internal_affairs/style_detected_api_use'
|
10
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
|