rubocop 1.16.0 → 1.18.2
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/README.md +1 -1
- data/config/default.yml +70 -28
- data/lib/rubocop.rb +2 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/config_validator.rb +5 -5
- data/lib/rubocop/cop/base.rb +2 -2
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/bundler/gem_version.rb +38 -4
- data/lib/rubocop/cop/corrector.rb +4 -4
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/array_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +7 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +13 -15
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +12 -9
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +8 -0
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +127 -0
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +2 -2
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +6 -6
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +3 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/redundant_line_break.rb +11 -9
- data/lib/rubocop/cop/layout/space_around_keyword.rb +12 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +7 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +13 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +32 -17
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +1 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +93 -65
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +5 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/unused_block_argument.rb +1 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/migration/department_name.rb +3 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -1
- data/lib/rubocop/cop/naming/inclusive_language.rb +249 -0
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +14 -0
- data/lib/rubocop/cop/style/comment_annotation.rb +50 -6
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +29 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -1
- data/lib/rubocop/cop/style/multiline_when_then.rb +2 -11
- data/lib/rubocop/cop/style/multiple_comparison.rb +1 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/quoted_symbols.rb +8 -3
- data/lib/rubocop/cop/style/raise_args.rb +2 -0
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self.rb +24 -2
- data/lib/rubocop/cop/style/regexp_literal.rb +10 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +32 -5
- data/lib/rubocop/cop/style/string_literals.rb +2 -2
- data/lib/rubocop/cop/style/swap_values.rb +1 -1
- data/lib/rubocop/cop/style/unpack_first.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
- data/lib/rubocop/directive_comment.rb +53 -5
- data/lib/rubocop/options.rb +4 -4
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +10 -2
- data/lib/rubocop/rspec/cop_helper.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +9 -7
@@ -54,6 +54,7 @@ module RuboCop
|
|
54
54
|
directive = DirectiveComment.new(comment)
|
55
55
|
|
56
56
|
cop_names.each do |name|
|
57
|
+
name = name.split('/').first if department?(directive, name)
|
57
58
|
add_offense(
|
58
59
|
range_of_offense(comment, name),
|
59
60
|
message: format(MSG, cop: all_or_name(name))
|
@@ -119,6 +120,10 @@ module RuboCop
|
|
119
120
|
def all_or_name(name)
|
120
121
|
name == 'all' ? 'all cops' : name
|
121
122
|
end
|
123
|
+
|
124
|
+
def department?(directive, name)
|
125
|
+
directive.in_directive_department?(name) && !directive.overridden_by_department?(name)
|
126
|
+
end
|
122
127
|
end
|
123
128
|
end
|
124
129
|
end
|
@@ -70,7 +70,7 @@ module RuboCop
|
|
70
70
|
|
71
71
|
MSG = 'Unnecessary symbol conversion; use `%<correction>s` instead.'
|
72
72
|
MSG_CONSISTENCY = 'Symbol hash key should be quoted for consistency; ' \
|
73
|
-
|
73
|
+
'use `%<correction>s` instead.'
|
74
74
|
RESTRICT_ON_SEND = %i[to_sym intern].freeze
|
75
75
|
|
76
76
|
def on_send(node)
|
@@ -143,7 +143,7 @@ module RuboCop
|
|
143
143
|
|
144
144
|
def message_for_underscore_prefix(variable)
|
145
145
|
"If it's necessary, use `_` or `_#{variable.name}` " \
|
146
|
-
|
146
|
+
"as an argument name to indicate that it won't be used."
|
147
147
|
end
|
148
148
|
|
149
149
|
def define_method_call?(variable)
|
@@ -85,7 +85,7 @@ module RuboCop
|
|
85
85
|
return unless assignment.meta_assignment_node.equal?(return_value_node)
|
86
86
|
|
87
87
|
" Use `#{assignment.operator.sub(/=$/, '')}` " \
|
88
|
-
|
88
|
+
"instead of `#{assignment.operator}`."
|
89
89
|
end
|
90
90
|
|
91
91
|
def similar_name_message(variable)
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
50
50
|
->(node) { heredoc_node?(node) }
|
51
51
|
else
|
52
52
|
raise ArgumentError, "Unknown foldable type: #{type.inspect}. "\
|
53
|
-
|
53
|
+
"Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -61,7 +61,9 @@ module RuboCop
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def valid_content_token?(content_token)
|
64
|
-
/\W+/.match?(content_token) ||
|
64
|
+
/\W+/.match?(content_token) ||
|
65
|
+
DISABLING_COPS_CONTENT_TOKEN.match?(content_token) ||
|
66
|
+
Registry.global.department?(content_token)
|
65
67
|
end
|
66
68
|
|
67
69
|
def contain_unexpected_character_for_department_name?(name)
|
@@ -72,7 +72,9 @@ module RuboCop
|
|
72
72
|
|
73
73
|
# If a `send` node is not parenthesized, don't move the first element, because it
|
74
74
|
# can result in changed behavior or a syntax error.
|
75
|
-
|
75
|
+
if node.send_type? && !node.parenthesized? && !first_argument_is_heredoc?(node)
|
76
|
+
elements = elements.drop(1)
|
77
|
+
end
|
76
78
|
|
77
79
|
i = 0
|
78
80
|
i += 1 while within_column_limit?(elements[i], max, line)
|
@@ -84,6 +86,13 @@ module RuboCop
|
|
84
86
|
elements[i - 1]
|
85
87
|
end
|
86
88
|
|
89
|
+
# @api private
|
90
|
+
def first_argument_is_heredoc?(node)
|
91
|
+
first_argument = node.first_argument
|
92
|
+
|
93
|
+
first_argument.respond_to?(:heredoc?) && first_argument.heredoc?
|
94
|
+
end
|
95
|
+
|
87
96
|
# @api private
|
88
97
|
# If a send node contains a heredoc argument, splitting cannot happen
|
89
98
|
# after the heredoc or else it will cause a syntax error.
|
@@ -0,0 +1,249 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Naming
|
6
|
+
# This cops recommends the use of inclusive language instead of problematic terms.
|
7
|
+
# The cop can check the following locations for offenses:
|
8
|
+
# - identifiers
|
9
|
+
# - constants
|
10
|
+
# - variables
|
11
|
+
# - strings
|
12
|
+
# - symbols
|
13
|
+
# - comments
|
14
|
+
# - file paths
|
15
|
+
# Each of these locations can be individually enabled/disabled via configuration,
|
16
|
+
# for example CheckIdentifiers = true/false.
|
17
|
+
#
|
18
|
+
# Flagged terms are configurable for the cop. For each flagged term an optional
|
19
|
+
# Regex can be specified to identify offenses. Suggestions for replacing a flagged term can
|
20
|
+
# be configured and will be displayed as part of the offense message.
|
21
|
+
# An AllowedRegex can be specified for a flagged term to exempt allowed uses of the term.
|
22
|
+
#
|
23
|
+
# @example FlaggedTerms: { whitelist: { Suggestions: ['allowlist'] } }
|
24
|
+
# # Suggest replacing identifier whitelist with allowlist
|
25
|
+
#
|
26
|
+
# # bad
|
27
|
+
# whitelist_users = %w(user1 user1)
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# allowlist_users = %w(user1 user2)
|
31
|
+
#
|
32
|
+
# @example FlaggedTerms: { master: { Suggestions: ['main', 'primary', 'leader'] } }
|
33
|
+
# # Suggest replacing master in an instance variable name with main, primary, or leader
|
34
|
+
#
|
35
|
+
# # bad
|
36
|
+
# @master_node = 'node1.example.com'
|
37
|
+
#
|
38
|
+
# # good
|
39
|
+
# @primary_node = 'node1.example.com'
|
40
|
+
#
|
41
|
+
# @example FlaggedTerms: { whitelist: { Regex: !ruby/regexp '/white[-_\s]?list' } }
|
42
|
+
# # Identify problematic terms using a Regexp
|
43
|
+
#
|
44
|
+
# # bad
|
45
|
+
# white_list = %w(user1 user2)
|
46
|
+
#
|
47
|
+
# # good
|
48
|
+
# allow_list = %w(user1 user2)
|
49
|
+
#
|
50
|
+
# @example FlaggedTerms: { master: { AllowedRegex: 'master\'?s degree' } }
|
51
|
+
# # Specify allowed uses of the flagged term as a string or regexp.
|
52
|
+
#
|
53
|
+
# # bad
|
54
|
+
# # They had a masters
|
55
|
+
#
|
56
|
+
# # good
|
57
|
+
# # They had a master's degree
|
58
|
+
#
|
59
|
+
class InclusiveLanguage < Base
|
60
|
+
include RangeHelp
|
61
|
+
|
62
|
+
EMPTY_ARRAY = [].freeze
|
63
|
+
|
64
|
+
WordLocation = Struct.new(:word, :position)
|
65
|
+
|
66
|
+
def initialize(config = nil, options = nil)
|
67
|
+
super
|
68
|
+
@flagged_term_hash = {}
|
69
|
+
@flagged_terms_regex = nil
|
70
|
+
@allowed_regex = nil
|
71
|
+
@check_token = preprocess_check_config
|
72
|
+
preprocess_flagged_terms
|
73
|
+
end
|
74
|
+
|
75
|
+
def on_new_investigation
|
76
|
+
investigate_filepath if cop_config['CheckFilepaths']
|
77
|
+
investigate_tokens
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def investigate_tokens
|
83
|
+
processed_source.each_token do |token|
|
84
|
+
next unless check_token?(token.type)
|
85
|
+
|
86
|
+
word_locations = scan_for_words(token.text)
|
87
|
+
next if word_locations.empty?
|
88
|
+
|
89
|
+
add_offenses_for_token(token, word_locations)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_offenses_for_token(token, word_locations)
|
94
|
+
word_locations.each do |word_location|
|
95
|
+
start_position = token.pos.begin_pos + token.pos.source.index(word_location.word)
|
96
|
+
range = range_between(start_position, start_position + word_location.word.length)
|
97
|
+
add_offense(range, message: create_message(word_location.word))
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_token?(type)
|
102
|
+
!!@check_token[type]
|
103
|
+
end
|
104
|
+
|
105
|
+
def preprocess_check_config # rubocop:disable Metrics/AbcSize
|
106
|
+
{
|
107
|
+
tIDENTIFIER: cop_config['CheckIdentifiers'],
|
108
|
+
tCONSTANT: cop_config['CheckConstants'],
|
109
|
+
tIVAR: cop_config['CheckVariables'],
|
110
|
+
tCVAR: cop_config['CheckVariables'],
|
111
|
+
tGVAR: cop_config['CheckVariables'],
|
112
|
+
tSYMBOL: cop_config['CheckSymbols'],
|
113
|
+
tSTRING: cop_config['CheckStrings'],
|
114
|
+
tSTRING_CONTENT: cop_config['CheckStrings'],
|
115
|
+
tCOMMENT: cop_config['CheckComments']
|
116
|
+
}.freeze
|
117
|
+
end
|
118
|
+
|
119
|
+
def preprocess_flagged_terms
|
120
|
+
allowed_strings = []
|
121
|
+
flagged_term_strings = []
|
122
|
+
cop_config['FlaggedTerms'].each do |term, term_definition|
|
123
|
+
next if term_definition.nil?
|
124
|
+
|
125
|
+
allowed_strings.concat(process_allowed_regex(term_definition['AllowedRegex']))
|
126
|
+
regex_string = ensure_regex_string(term_definition['Regex'] || term)
|
127
|
+
flagged_term_strings << regex_string
|
128
|
+
|
129
|
+
add_to_flagged_term_hash(regex_string, term, term_definition)
|
130
|
+
end
|
131
|
+
|
132
|
+
set_regexes(flagged_term_strings, allowed_strings)
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_to_flagged_term_hash(regex_string, term, term_definition)
|
136
|
+
@flagged_term_hash[Regexp.new(regex_string, Regexp::IGNORECASE)] =
|
137
|
+
term_definition.merge('Term' => term,
|
138
|
+
'SuggestionString' =>
|
139
|
+
preprocess_suggestions(term_definition['Suggestions']))
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_regexes(flagged_term_strings, allowed_strings)
|
143
|
+
@flagged_terms_regex = array_to_ignorecase_regex(flagged_term_strings)
|
144
|
+
@allowed_regex = array_to_ignorecase_regex(allowed_strings) unless allowed_strings.empty?
|
145
|
+
end
|
146
|
+
|
147
|
+
def process_allowed_regex(allowed)
|
148
|
+
return EMPTY_ARRAY if allowed.nil?
|
149
|
+
|
150
|
+
Array(allowed).map do |allowed_term|
|
151
|
+
next if allowed_term.is_a?(String) && allowed_term.strip.empty?
|
152
|
+
|
153
|
+
ensure_regex_string(allowed_term)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def ensure_regex_string(regex)
|
158
|
+
regex.is_a?(Regexp) ? regex.source : regex
|
159
|
+
end
|
160
|
+
|
161
|
+
def array_to_ignorecase_regex(strings)
|
162
|
+
Regexp.new(strings.join('|'), Regexp::IGNORECASE)
|
163
|
+
end
|
164
|
+
|
165
|
+
def investigate_filepath
|
166
|
+
word_locations = scan_for_words(processed_source.file_path)
|
167
|
+
|
168
|
+
case word_locations.length
|
169
|
+
when 0
|
170
|
+
return
|
171
|
+
when 1
|
172
|
+
message = create_single_word_message_for_file(word_locations.first.word)
|
173
|
+
else
|
174
|
+
words = word_locations.map(&:word)
|
175
|
+
message = create_multiple_word_message_for_file(words)
|
176
|
+
end
|
177
|
+
|
178
|
+
range = source_range(processed_source.buffer, 1, 0)
|
179
|
+
add_offense(range, message: message)
|
180
|
+
end
|
181
|
+
|
182
|
+
def create_single_word_message_for_file(word)
|
183
|
+
create_message(word).sub(/\.$/, ' in file path.')
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_multiple_word_message_for_file(words)
|
187
|
+
quoted_words = words.map { |word| "'#{word}'" }
|
188
|
+
"Consider replacing problematic terms #{quoted_words.join(', ')} in file path."
|
189
|
+
end
|
190
|
+
|
191
|
+
def scan_for_words(input)
|
192
|
+
mask_input(input).enum_for(:scan, @flagged_terms_regex).map do
|
193
|
+
match = Regexp.last_match
|
194
|
+
WordLocation.new(match.to_s, match.offset(0).first)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def mask_input(str)
|
199
|
+
return str if @allowed_regex.nil?
|
200
|
+
|
201
|
+
safe_str = if str.valid_encoding?
|
202
|
+
str
|
203
|
+
else
|
204
|
+
str.encode('UTF-8', invalid: :replace, undef: :replace)
|
205
|
+
end
|
206
|
+
safe_str.gsub(@allowed_regex) { |match| '*' * match.size }
|
207
|
+
end
|
208
|
+
|
209
|
+
def create_message(word)
|
210
|
+
flagged_term = find_flagged_term(word)
|
211
|
+
"Consider replacing problematic term '#{word}'#{flagged_term['SuggestionString']}."
|
212
|
+
end
|
213
|
+
|
214
|
+
def find_flagged_term(word)
|
215
|
+
_regexp, flagged_term = @flagged_term_hash.find do |key, _term|
|
216
|
+
key.match?(word)
|
217
|
+
end
|
218
|
+
flagged_term
|
219
|
+
end
|
220
|
+
|
221
|
+
def create_message_for_file(word)
|
222
|
+
create_message(word).sub(/\.$/, ' in file path.')
|
223
|
+
end
|
224
|
+
|
225
|
+
def preprocess_suggestions(suggestions)
|
226
|
+
return '' if suggestions.nil? ||
|
227
|
+
(suggestions.is_a?(String) && suggestions.strip.empty?) || suggestions.empty?
|
228
|
+
|
229
|
+
format_suggestions(suggestions)
|
230
|
+
end
|
231
|
+
|
232
|
+
def format_suggestions(suggestions)
|
233
|
+
quoted_suggestions = Array(suggestions).map { |word| "'#{word}'" }
|
234
|
+
suggestion_str = case quoted_suggestions.size
|
235
|
+
when 1
|
236
|
+
quoted_suggestions.first
|
237
|
+
when 2
|
238
|
+
quoted_suggestions.join(' or ')
|
239
|
+
else
|
240
|
+
last_quoted = quoted_suggestions.pop
|
241
|
+
quoted_suggestions << "or #{last_quoted}"
|
242
|
+
quoted_suggestions.join(', ')
|
243
|
+
end
|
244
|
+
" with #{suggestion_str}"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -147,9 +147,9 @@ module RuboCop
|
|
147
147
|
include ConfigurableEnforcedStyle
|
148
148
|
|
149
149
|
MSG = 'Memoized variable `%<var>s` does not match ' \
|
150
|
-
|
150
|
+
'method name `%<method>s`. Use `@%<suggested_var>s` instead.'
|
151
151
|
UNDERSCORE_REQUIRED = 'Memoized variable `%<var>s` does not start ' \
|
152
|
-
|
152
|
+
'with `_`. Use `@%<suggested_var>s` instead.'
|
153
153
|
DYNAMIC_DEFINE_METHODS = %i[define_method define_singleton_method].to_set.freeze
|
154
154
|
|
155
155
|
# @!method method_definition?(node)
|
@@ -84,6 +84,7 @@ module RuboCop
|
|
84
84
|
def compact_definition(corrector, node)
|
85
85
|
compact_node(corrector, node)
|
86
86
|
remove_end(corrector, node.body)
|
87
|
+
unindent(corrector, node)
|
87
88
|
end
|
88
89
|
|
89
90
|
def compact_node(corrector, node)
|
@@ -114,6 +115,19 @@ module RuboCop
|
|
114
115
|
corrector.remove(range)
|
115
116
|
end
|
116
117
|
|
118
|
+
def configured_indentation_width
|
119
|
+
config.for_badge(Layout::IndentationWidth.badge).fetch('Width', 2)
|
120
|
+
end
|
121
|
+
|
122
|
+
def unindent(corrector, node)
|
123
|
+
return if node.body.children.last.nil?
|
124
|
+
|
125
|
+
column_delta = configured_indentation_width - leading_spaces(node.body.children.last).size
|
126
|
+
return if column_delta.zero?
|
127
|
+
|
128
|
+
AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
|
129
|
+
end
|
130
|
+
|
117
131
|
def leading_spaces(node)
|
118
132
|
node.source_range.source_line[/\A\s*/]
|
119
133
|
end
|
@@ -12,7 +12,7 @@ module RuboCop
|
|
12
12
|
# incorrect registering of keywords (eg. `review`) inside a paragraph as an
|
13
13
|
# annotation.
|
14
14
|
#
|
15
|
-
# @example
|
15
|
+
# @example RequireColon: true (default)
|
16
16
|
# # bad
|
17
17
|
# # TODO make better
|
18
18
|
#
|
@@ -36,14 +36,36 @@ module RuboCop
|
|
36
36
|
#
|
37
37
|
# # good
|
38
38
|
# # OPTIMIZE: does not work
|
39
|
+
#
|
40
|
+
# @example RequireColon: false
|
41
|
+
# # bad
|
42
|
+
# # TODO: make better
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# # TODO make better
|
46
|
+
#
|
47
|
+
# # bad
|
48
|
+
# # fixme does not work
|
49
|
+
#
|
50
|
+
# # good
|
51
|
+
# # FIXME does not work
|
52
|
+
#
|
53
|
+
# # bad
|
54
|
+
# # Optimize does not work
|
55
|
+
#
|
56
|
+
# # good
|
57
|
+
# # OPTIMIZE does not work
|
39
58
|
class CommentAnnotation < Base
|
40
59
|
include AnnotationComment
|
41
60
|
include RangeHelp
|
42
61
|
extend AutoCorrector
|
43
62
|
|
44
|
-
|
45
|
-
|
46
|
-
|
63
|
+
MSG_COLON_STYLE = 'Annotation keywords like `%<keyword>s` should be all ' \
|
64
|
+
'upper case, followed by a colon, and a space, ' \
|
65
|
+
'then a note describing the problem.'
|
66
|
+
MSG_SPACE_STYLE = 'Annotation keywords like `%<keyword>s` should be all ' \
|
67
|
+
'upper case, followed by a space, ' \
|
68
|
+
'then a note describing the problem.'
|
47
69
|
MISSING_NOTE = 'Annotation comment, with keyword `%<keyword>s`, is missing a note.'
|
48
70
|
|
49
71
|
def on_new_investigation
|
@@ -63,13 +85,15 @@ module RuboCop
|
|
63
85
|
private
|
64
86
|
|
65
87
|
def register_offense(range, note, first_word)
|
88
|
+
message = requires_colon? ? MSG_COLON_STYLE : MSG_SPACE_STYLE
|
89
|
+
|
66
90
|
add_offense(
|
67
91
|
range,
|
68
|
-
message: format(note ?
|
92
|
+
message: format(note ? message : MISSING_NOTE, keyword: first_word)
|
69
93
|
) do |corrector|
|
70
94
|
next if note.nil?
|
71
95
|
|
72
|
-
corrector
|
96
|
+
correct_offense(corrector, range, first_word)
|
73
97
|
end
|
74
98
|
end
|
75
99
|
|
@@ -92,8 +116,28 @@ module RuboCop
|
|
92
116
|
end
|
93
117
|
|
94
118
|
def correct_annotation?(first_word, colon, space, note)
|
119
|
+
return correct_colon_annotation?(first_word, colon, space, note) if requires_colon?
|
120
|
+
|
121
|
+
correct_space_annotation?(first_word, colon, space, note)
|
122
|
+
end
|
123
|
+
|
124
|
+
def correct_colon_annotation?(first_word, colon, space, note)
|
95
125
|
keyword?(first_word) && (colon && space && note || !colon && !note)
|
96
126
|
end
|
127
|
+
|
128
|
+
def correct_space_annotation?(first_word, colon, space, note)
|
129
|
+
keyword?(first_word) && (!colon && space && note || !colon && !note)
|
130
|
+
end
|
131
|
+
|
132
|
+
def correct_offense(corrector, range, first_word)
|
133
|
+
return corrector.replace(range, "#{first_word.upcase}: ") if requires_colon?
|
134
|
+
|
135
|
+
corrector.replace(range, "#{first_word.upcase} ")
|
136
|
+
end
|
137
|
+
|
138
|
+
def requires_colon?
|
139
|
+
cop_config['RequireColon']
|
140
|
+
end
|
97
141
|
end
|
98
142
|
end
|
99
143
|
end
|