rubocop 0.48.1 → 0.49.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -3
- data/config/default.yml +397 -357
- data/config/disabled.yml +29 -29
- data/config/enabled.yml +366 -326
- data/lib/rubocop.rb +85 -70
- data/lib/rubocop/ast/builder.rb +4 -1
- data/lib/rubocop/ast/node.rb +2 -2
- data/lib/rubocop/ast/node/and_node.rb +1 -1
- data/lib/rubocop/ast/node/args_node.rb +24 -0
- data/lib/rubocop/ast/node/block_node.rb +107 -0
- data/lib/rubocop/ast/node/case_node.rb +1 -1
- data/lib/rubocop/ast/node/ensure_node.rb +1 -1
- data/lib/rubocop/ast/node/for_node.rb +1 -1
- data/lib/rubocop/ast/node/if_node.rb +1 -1
- data/lib/rubocop/ast/node/mixin/parameterized_node.rb +74 -0
- data/lib/rubocop/ast/node/or_node.rb +1 -1
- data/lib/rubocop/ast/node/pair_node.rb +1 -1
- data/lib/rubocop/ast/node/resbody_node.rb +1 -1
- data/lib/rubocop/ast/node/send_node.rb +36 -57
- data/lib/rubocop/ast/node/super_node.rb +42 -0
- data/lib/rubocop/ast/node/until_node.rb +1 -1
- data/lib/rubocop/ast/node/when_node.rb +1 -1
- data/lib/rubocop/ast/node/while_node.rb +1 -1
- data/lib/rubocop/cli.rb +10 -0
- data/lib/rubocop/config.rb +23 -7
- data/lib/rubocop/config_loader.rb +19 -3
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
- data/lib/rubocop/cop/commissioner.rb +1 -1
- data/lib/rubocop/cop/cop.rb +10 -0
- data/lib/rubocop/cop/{style → layout}/access_modifier_indentation.rb +33 -3
- data/lib/rubocop/cop/{style → layout}/align_array.rb +16 -1
- data/lib/rubocop/cop/{style → layout}/align_hash.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/align_parameters.rb +29 -1
- data/lib/rubocop/cop/{style → layout}/block_end_newline.rb +10 -5
- data/lib/rubocop/cop/{style → layout}/case_indentation.rb +64 -1
- data/lib/rubocop/cop/{style → layout}/closing_parenthesis_indentation.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/dot_position.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/else_alignment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_line_after_magic_comment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_access_modifier.rb +2 -7
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_begin_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_block_body.rb +2 -4
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_exception_handling_keywords.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_method_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/empty_lines_around_module_body.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/end_of_line.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/extra_spacing.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_array_element_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_hash_element_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_method_argument_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_method_parameter_line_break.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indent_array.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indent_assignment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indent_hash.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/indent_heredoc.rb +3 -3
- data/lib/rubocop/cop/{style → layout}/indentation_consistency.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/indentation_width.rb +10 -12
- data/lib/rubocop/cop/{style → layout}/initial_indentation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/leading_comment_space.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_array_brace_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_assignment_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_block_layout.rb +21 -36
- data/lib/rubocop/cop/{style → layout}/multiline_hash_brace_layout.rb +5 -1
- data/lib/rubocop/cop/{style → layout}/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_method_call_indentation.rb +3 -3
- data/lib/rubocop/cop/{style → layout}/multiline_method_definition_brace_layout.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/multiline_operation_indentation.rb +6 -5
- data/lib/rubocop/cop/{style → layout}/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_after_colon.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/space_after_comma.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/space_after_method_name.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_after_not.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_after_semicolon.rb +2 -2
- data/lib/rubocop/cop/{style → layout}/space_around_block_parameters.rb +7 -5
- data/lib/rubocop/cop/{style → layout}/space_around_equals_in_parameter_default.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_around_keyword.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_around_operators.rb +6 -2
- data/lib/rubocop/cop/{style → layout}/space_before_block_braces.rb +6 -2
- data/lib/rubocop/cop/{style → layout}/space_before_comma.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_before_comment.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_before_first_arg.rb +4 -2
- data/lib/rubocop/cop/{style → layout}/space_before_semicolon.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_in_lambda_literal.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_array_percent_literal.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_block_braces.rb +3 -4
- data/lib/rubocop/cop/{style → layout}/space_inside_brackets.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_hash_literal_braces.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_parens.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_percent_literal_delimiters.rb +8 -7
- data/lib/rubocop/cop/{style → layout}/space_inside_range_literal.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/space_inside_string_interpolation.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/tab.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/trailing_blank_lines.rb +1 -1
- data/lib/rubocop/cop/{style → layout}/trailing_whitespace.rb +2 -2
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -4
- data/lib/rubocop/cop/lint/debugger.rb +0 -15
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +81 -0
- data/lib/rubocop/cop/lint/script_permission.rb +42 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
- data/lib/rubocop/cop/message_annotator.rb +23 -13
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/mixin/array_min_size.rb +59 -0
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +10 -11
- data/lib/rubocop/cop/mixin/def_node.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +36 -0
- data/lib/rubocop/cop/mixin/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +7 -3
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/performance/caller.rb +41 -0
- data/lib/rubocop/cop/performance/compare_with_block.rb +60 -14
- data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_merge.rb +2 -0
- data/lib/rubocop/cop/rails/action_filter.rb +1 -3
- data/lib/rubocop/cop/rails/application_job.rb +32 -0
- data/lib/rubocop/cop/rails/application_record.rb +32 -0
- data/lib/rubocop/cop/rails/blank.rb +9 -3
- data/lib/rubocop/cop/rails/output_safety.rb +59 -15
- data/lib/rubocop/cop/rails/present.rb +9 -3
- data/lib/rubocop/cop/rails/relative_date_constant.rb +35 -4
- data/lib/rubocop/cop/rails/reversible_migration.rb +82 -18
- data/lib/rubocop/cop/rails/save_bang.rb +7 -2
- data/lib/rubocop/cop/rails/skips_model_validations.rb +7 -0
- data/lib/rubocop/cop/registry.rb +4 -3
- data/lib/rubocop/cop/security/eval.rb +9 -3
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +11 -17
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +1 -3
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/copyright.rb +2 -2
- data/lib/rubocop/cop/style/documentation_method.rb +1 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +2 -1
- data/lib/rubocop/cop/style/each_with_object.rb +10 -6
- data/lib/rubocop/cop/style/empty_case_condition.rb +2 -2
- data/lib/rubocop/cop/style/for.rb +4 -5
- data/lib/rubocop/cop/style/format_string.rb +49 -0
- data/lib/rubocop/cop/style/format_string_token.rb +141 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -2
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +10 -1
- data/lib/rubocop/cop/style/lambda.rb +9 -9
- data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -3
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/method_name.rb +8 -2
- data/lib/rubocop/cop/style/mixin_grouping.rb +41 -3
- data/lib/rubocop/cop/style/multiline_block_chain.rb +7 -11
- data/lib/rubocop/cop/style/multiple_comparison.rb +77 -0
- data/lib/rubocop/cop/style/next.rb +11 -22
- data/lib/rubocop/cop/style/parallel_assignment.rb +10 -19
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -2
- data/lib/rubocop/cop/style/self_assignment.rb +4 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +23 -17
- data/lib/rubocop/cop/style/symbol_array.rb +24 -13
- data/lib/rubocop/cop/style/symbol_proc.rb +4 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/unneeded_interpolation.rb +4 -0
- data/lib/rubocop/cop/style/word_array.rb +33 -53
- data/lib/rubocop/cop/style/yoda_condition.rb +78 -0
- data/lib/rubocop/cop/team.rb +1 -14
- data/lib/rubocop/cop/util.rb +16 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -11
- data/lib/rubocop/node_pattern.rb +52 -52
- data/lib/rubocop/options.rb +25 -0
- data/lib/rubocop/path_util.rb +17 -1
- data/lib/rubocop/result_cache.rb +8 -7
- data/lib/rubocop/rspec/expect_offense.rb +167 -0
- data/lib/rubocop/rspec/shared_examples.rb +0 -8
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/runner.rb +12 -2
- data/lib/rubocop/target_finder.rb +5 -0
- data/lib/rubocop/version.rb +1 -1
- metadata +101 -72
@@ -64,17 +64,6 @@ module RuboCop
|
|
64
64
|
@total_correction_count += offenses.count(&:corrected?)
|
65
65
|
end
|
66
66
|
|
67
|
-
def smart_path(path)
|
68
|
-
# Ideally, we calculate this relative to the project root.
|
69
|
-
base_dir = Dir.pwd
|
70
|
-
|
71
|
-
if path.start_with? base_dir
|
72
|
-
relative_path(path, base_dir)
|
73
|
-
else
|
74
|
-
path
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
67
|
def colored_severity_code(offense)
|
79
68
|
color = COLOR_FOR_SEVERITY[offense.severity.name]
|
80
69
|
colorize(offense.severity.code, color)
|
data/lib/rubocop/node_pattern.rb
CHANGED
@@ -23,55 +23,55 @@ module RuboCop
|
|
23
23
|
#
|
24
24
|
# ## Pattern string format examples
|
25
25
|
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
26
|
+
# ':sym' # matches a literal symbol
|
27
|
+
# '1' # matches a literal integer
|
28
|
+
# 'nil' # matches a literal nil
|
29
|
+
# 'send' # matches (send ...)
|
30
|
+
# '(send)' # matches (send)
|
31
|
+
# '(send ...)' # matches (send ...)
|
32
|
+
# '(op-asgn)' # node types with hyphenated names also work
|
33
|
+
# '{send class}' # matches (send ...) or (class ...)
|
34
|
+
# '({send class})' # matches (send) or (class)
|
35
|
+
# '(send const)' # matches (send (const ...))
|
36
|
+
# '(send _ :new)' # matches (send <anything> :new)
|
37
|
+
# '(send $_ :new)' # as above, but whatever matches the $_ is captured
|
38
|
+
# '(send $_ $_)' # you can use as many captures as you want
|
39
|
+
# '(send !const ...)' # ! negates the next part of the pattern
|
40
|
+
# '$(send const ...)' # arbitrary matching can be performed on a capture
|
41
|
+
# '(send _recv _msg)' # wildcards can be named (for readability)
|
42
|
+
# '(send ... :new)' # you can specifically match against the last child
|
43
|
+
# # (this only works for the very last)
|
44
|
+
# '(send $...)' # capture all the children as an array
|
45
|
+
# '(send $... int)' # capture all children but the last as an array
|
46
|
+
# '(send _x :+ _x)' # unification is performed on named wildcards
|
47
|
+
# # (like Prolog variables...)
|
48
|
+
# # (#== is used to see if values unify)
|
49
|
+
# '(int odd?)' # words which end with a ? are predicate methods,
|
50
|
+
# # are are called on the target to see if it matches
|
51
|
+
# # any Ruby method which the matched object supports
|
52
|
+
# # can be used
|
53
|
+
# # if a truthy value is returned, the match succeeds
|
54
|
+
# '(int [!1 !2])' # [] contains multiple patterns, ALL of which must
|
55
|
+
# # match in that position
|
56
|
+
# # in other words, while {} is pattern union (logical
|
57
|
+
# # OR), [] is intersection (logical AND)
|
58
|
+
# '(send %1 _)' # % stands for a parameter which must be supplied to
|
59
|
+
# # #match at matching time
|
60
|
+
# # it will be compared to the corresponding value in
|
61
|
+
# # the AST using #==
|
62
|
+
# # a bare '%' is the same as '%1'
|
63
|
+
# # the number of extra parameters passed to #match
|
64
|
+
# # must equal the highest % value in the pattern
|
65
|
+
# # for consistency, %0 is the 'root node' which is
|
66
|
+
# # passed as the 1st argument to #match, where the
|
67
|
+
# # matching process starts
|
68
|
+
# '^^send' # each ^ ascends one level in the AST
|
69
|
+
# # so this matches against the grandparent node
|
70
|
+
# '#method' # we call this a 'funcall'; it calls a method in the
|
71
|
+
# # context where a pattern-matching method is defined
|
72
|
+
# # if that returns a truthy value, the match succeeds
|
73
|
+
# 'equal?(%1)' # predicates can be given 1 or more extra args
|
74
|
+
# '#method(%0, 1)' # funcalls can also be given 1 or more extra args
|
75
75
|
#
|
76
76
|
# You can nest arbitrarily deep:
|
77
77
|
#
|
@@ -475,8 +475,8 @@ module RuboCop
|
|
475
475
|
"#{compiler.emit_trailing_params});" \
|
476
476
|
"#{compiler.emit_method_code};end"
|
477
477
|
|
478
|
-
|
479
|
-
class_eval(src,
|
478
|
+
location = caller_locations(1, 1).first
|
479
|
+
class_eval(src, location.path, location.lineno)
|
480
480
|
end
|
481
481
|
|
482
482
|
# Define a method which recurses over the descendants of an AST node,
|
@@ -487,7 +487,7 @@ module RuboCop
|
|
487
487
|
# yield all descendants which match.
|
488
488
|
def def_node_search(method_name, pattern_str)
|
489
489
|
compiler = RuboCop::NodePattern::Compiler.new(pattern_str, 'node')
|
490
|
-
called_from = caller.first.split(':')
|
490
|
+
called_from = caller(1..1).first.split(':')
|
491
491
|
|
492
492
|
if method_name.to_s.end_with?('?')
|
493
493
|
node_search_first(method_name, compiler, called_from)
|
data/lib/rubocop/options.rb
CHANGED
@@ -156,6 +156,7 @@ module RuboCop
|
|
156
156
|
|
157
157
|
option(opts, '-v', '--version')
|
158
158
|
option(opts, '-V', '--verbose-version')
|
159
|
+
option(opts, '-P', '--parallel')
|
159
160
|
end
|
160
161
|
|
161
162
|
def add_list_options(opts)
|
@@ -217,11 +218,33 @@ module RuboCop
|
|
217
218
|
raise ArgumentError, '--no-offense-counts can only be used together ' \
|
218
219
|
'with --auto-gen-config.'
|
219
220
|
end
|
221
|
+
validate_parallel
|
222
|
+
|
220
223
|
return if incompatible_options.size <= 1
|
221
224
|
raise ArgumentError, 'Incompatible cli options: ' \
|
222
225
|
"#{incompatible_options.inspect}"
|
223
226
|
end
|
224
227
|
|
228
|
+
def validate_parallel
|
229
|
+
return unless @options.key?(:parallel)
|
230
|
+
|
231
|
+
if @options[:cache] == 'false'
|
232
|
+
raise ArgumentError, '-P/--parallel uses caching to speed up ' \
|
233
|
+
'execution, so combining with --cache false is ' \
|
234
|
+
'not allowed.'
|
235
|
+
end
|
236
|
+
|
237
|
+
combos = {
|
238
|
+
auto_gen_config: '-P/--parallel uses caching to speed up execution, ' \
|
239
|
+
'while --auto-gen-config needs a non-cached run, ' \
|
240
|
+
'so they cannot be combined.',
|
241
|
+
fail_fast: '-P/--parallel can not be combined with -F/--fail-fast.',
|
242
|
+
auto_correct: '-P/--parallel can not be combined with --auto-correct.'
|
243
|
+
}
|
244
|
+
|
245
|
+
combos.each { |key, msg| raise ArgumentError, msg if @options.key?(key) }
|
246
|
+
end
|
247
|
+
|
225
248
|
def only_includes_unneeded_disable?
|
226
249
|
@options.key?(:only) &&
|
227
250
|
(@options[:only] & %w[Lint/UnneededDisable UnneededDisable]).any?
|
@@ -323,6 +346,8 @@ module RuboCop
|
|
323
346
|
no_color: 'Force color output on or off.',
|
324
347
|
version: 'Display version.',
|
325
348
|
verbose_version: 'Display verbose version.',
|
349
|
+
parallel: ['Use available CPUs to execute inspection in',
|
350
|
+
'parallel.'],
|
326
351
|
stdin: ['Pipe source from STDIN, using FILE in offense',
|
327
352
|
'reports. This is useful for editor integration.']
|
328
353
|
}.freeze
|
data/lib/rubocop/path_util.rb
CHANGED
@@ -14,12 +14,28 @@ module RuboCop
|
|
14
14
|
path_name.relative_path_from(Pathname.new(base_dir)).to_s
|
15
15
|
end
|
16
16
|
|
17
|
+
def smart_path(path)
|
18
|
+
# Ideally, we calculate this relative to the project root.
|
19
|
+
base_dir = Dir.pwd
|
20
|
+
|
21
|
+
if path.start_with? base_dir
|
22
|
+
relative_path(path, base_dir)
|
23
|
+
else
|
24
|
+
path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
17
28
|
def match_path?(pattern, path)
|
18
29
|
case pattern
|
19
30
|
when String
|
20
31
|
File.fnmatch?(pattern, path, File::FNM_PATHNAME)
|
21
32
|
when Regexp
|
22
|
-
|
33
|
+
begin
|
34
|
+
path =~ pattern
|
35
|
+
rescue ArgumentError => e
|
36
|
+
return false if e.message.start_with?('invalid byte sequence')
|
37
|
+
raise e
|
38
|
+
end
|
23
39
|
end
|
24
40
|
end
|
25
41
|
|
data/lib/rubocop/result_cache.rb
CHANGED
@@ -2,14 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'digest/md5'
|
4
4
|
require 'find'
|
5
|
-
require 'tmpdir'
|
6
5
|
require 'etc'
|
7
6
|
|
8
7
|
module RuboCop
|
9
8
|
# Provides functionality for caching rubocop runs.
|
10
9
|
class ResultCache
|
11
10
|
NON_CHANGING = %i[color format formatters out debug fail_level
|
12
|
-
cache fail_fast stdin].freeze
|
11
|
+
cache fail_fast stdin parallel].freeze
|
13
12
|
|
14
13
|
# Remove old files so that the cache doesn't grow too big. When the
|
15
14
|
# threshold MaxFilesInCache has been exceeded, the oldest 50% of all the
|
@@ -63,11 +62,13 @@ module RuboCop
|
|
63
62
|
|
64
63
|
def self.cache_root(config_store)
|
65
64
|
root = config_store.for('.').for_all_cops['CacheRootDirectory']
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
65
|
+
root ||= if ENV.key?('XDG_CACHE_HOME')
|
66
|
+
# Include user ID in the path to make sure the user has write
|
67
|
+
# access.
|
68
|
+
File.join(ENV['XDG_CACHE_HOME'], Process.uid.to_s)
|
69
|
+
else
|
70
|
+
File.join(ENV['HOME'], '.cache')
|
71
|
+
end
|
71
72
|
File.join(root, 'rubocop_cache')
|
72
73
|
end
|
73
74
|
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module RSpec
|
5
|
+
# Mixin for `expect_offense` and `expect_no_offenses`
|
6
|
+
#
|
7
|
+
# This mixin makes it easier to specify strict offense expectations
|
8
|
+
# and a declarative and visual fashion. Just type out the code that
|
9
|
+
# should generate a offense, annotate code by writing '^'s
|
10
|
+
# underneath each character that should be highlighted, and follow
|
11
|
+
# the carets with a string (separated by a space) that is the
|
12
|
+
# message of the offense. You can include multiple offenses in
|
13
|
+
# one code snippet.
|
14
|
+
#
|
15
|
+
# @example Usage
|
16
|
+
#
|
17
|
+
# expect_offense(<<-RUBY.strip_indent)
|
18
|
+
# a do
|
19
|
+
# b
|
20
|
+
# end.c
|
21
|
+
# ^^^^^ Avoid chaining a method call on a do...end block.
|
22
|
+
# RUBY
|
23
|
+
#
|
24
|
+
# @example Equivalent assertion without `expect_offense`
|
25
|
+
#
|
26
|
+
# inspect_source(cop, <<-RUBY.strip_indent)
|
27
|
+
# a do
|
28
|
+
# b
|
29
|
+
# end.c
|
30
|
+
# RUBY
|
31
|
+
#
|
32
|
+
# expect(cop.offenses.size).to be(1)
|
33
|
+
#
|
34
|
+
# offense = cop.offenses.first
|
35
|
+
# expect(offense.line).to be(3)
|
36
|
+
# expect(offense.column_range).to be(0...5)
|
37
|
+
# expect(offense.message).to eql(
|
38
|
+
# 'Avoid chaining a method call on a do...end block.'
|
39
|
+
# )
|
40
|
+
#
|
41
|
+
# If you do not want to specify an offense then use the
|
42
|
+
# companion method `expect_no_offenses`. This method is a much
|
43
|
+
# simpler assertion since it just inspects the source and checks
|
44
|
+
# that there were no offenses. The `expect_offenses` method has
|
45
|
+
# to do more work by parsing out lines that contain carets.
|
46
|
+
module ExpectOffense
|
47
|
+
DEFAULT_FILENAME = 'example.rb'.freeze
|
48
|
+
|
49
|
+
def expect_offense(source, filename = DEFAULT_FILENAME)
|
50
|
+
expected_annotations = AnnotatedSource.parse(source)
|
51
|
+
|
52
|
+
if expected_annotations.plain_source == source
|
53
|
+
raise 'Use expect_no_offenses to assert that no offenses are found'
|
54
|
+
end
|
55
|
+
|
56
|
+
inspect_source(cop, expected_annotations.plain_source, filename)
|
57
|
+
actual_annotations =
|
58
|
+
expected_annotations.with_offense_annotations(cop.offenses)
|
59
|
+
expect(expected_annotations.to_s).to eq(actual_annotations.to_s)
|
60
|
+
end
|
61
|
+
|
62
|
+
def expect_no_offenses(source, filename = DEFAULT_FILENAME)
|
63
|
+
inspect_source(cop, source, filename)
|
64
|
+
|
65
|
+
expect(cop.offenses).to be_empty
|
66
|
+
end
|
67
|
+
|
68
|
+
# Parsed representation of code annotated with the `^^^ Message` style
|
69
|
+
class AnnotatedSource
|
70
|
+
ANNOTATION_PATTERN = /\A\s*\^+ /
|
71
|
+
|
72
|
+
# @param annotated_source [String] string passed to the matchers
|
73
|
+
#
|
74
|
+
# Separates annotation lines from source lines. Tracks the real
|
75
|
+
# source line number that each annotation corresponds to.
|
76
|
+
#
|
77
|
+
# @return [AnnotatedSource]
|
78
|
+
def self.parse(annotated_source)
|
79
|
+
source = []
|
80
|
+
annotations = []
|
81
|
+
|
82
|
+
annotated_source.each_line do |source_line|
|
83
|
+
if source_line =~ ANNOTATION_PATTERN
|
84
|
+
annotations << [source.size, source_line]
|
85
|
+
else
|
86
|
+
source << source_line
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
new(source, annotations)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param lines [Array<String>]
|
94
|
+
# @param annotations [Array<(Integer, String)>]
|
95
|
+
# each entry is the annotated line number and the annotation text
|
96
|
+
#
|
97
|
+
# @note annotations are sorted so that reconstructing the annotation
|
98
|
+
# text via {#to_s} is deterministic
|
99
|
+
def initialize(lines, annotations)
|
100
|
+
@lines = lines.freeze
|
101
|
+
@annotations = annotations.sort.freeze
|
102
|
+
end
|
103
|
+
|
104
|
+
# Construct annotated source string (like what we parse)
|
105
|
+
#
|
106
|
+
# Reconstruct a deterministic annotated source string. This is
|
107
|
+
# useful for eliminating semantically irrelevant annotation
|
108
|
+
# ordering differences.
|
109
|
+
#
|
110
|
+
# @example standardization
|
111
|
+
#
|
112
|
+
# source1 = AnnotatedSource.parse(<<-RUBY)
|
113
|
+
# line1
|
114
|
+
# ^ Annotation 1
|
115
|
+
# ^^ Annotation 2
|
116
|
+
# RUBY
|
117
|
+
#
|
118
|
+
# source2 = AnnotatedSource.parse(<<-RUBY)
|
119
|
+
# line1
|
120
|
+
# ^^ Annotation 2
|
121
|
+
# ^ Annotation 1
|
122
|
+
# RUBY
|
123
|
+
#
|
124
|
+
# source1.to_s == source2.to_s # => true
|
125
|
+
#
|
126
|
+
# @return [String]
|
127
|
+
def to_s
|
128
|
+
reconstructed = lines.dup
|
129
|
+
|
130
|
+
annotations.reverse_each do |line_number, annotation|
|
131
|
+
reconstructed.insert(line_number, annotation)
|
132
|
+
end
|
133
|
+
|
134
|
+
reconstructed.join
|
135
|
+
end
|
136
|
+
|
137
|
+
# Return the plain source code without annotations
|
138
|
+
#
|
139
|
+
# @return [String]
|
140
|
+
def plain_source
|
141
|
+
lines.join
|
142
|
+
end
|
143
|
+
|
144
|
+
# Annotate the source code with the RuboCop offenses provided
|
145
|
+
#
|
146
|
+
# @param offenses [Array<RuboCop::Cop::Offense>]
|
147
|
+
#
|
148
|
+
# @return [self]
|
149
|
+
def with_offense_annotations(offenses)
|
150
|
+
offense_annotations =
|
151
|
+
offenses.map do |offense|
|
152
|
+
indent = ' ' * offense.column
|
153
|
+
carets = '^' * offense.column_length
|
154
|
+
|
155
|
+
[offense.line, "#{indent}#{carets} #{offense.message}\n"]
|
156
|
+
end
|
157
|
+
|
158
|
+
self.class.new(lines, offense_annotations)
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
attr_reader :lines, :annotations
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -82,14 +82,6 @@ shared_examples_for 'debugger' do |name, src|
|
|
82
82
|
.to eq(src.map { |s| "Remove debugger entry point `#{s}`." })
|
83
83
|
expect(cop.highlights).to eq(src)
|
84
84
|
end
|
85
|
-
|
86
|
-
it "can autocorrect a #{name} call" do
|
87
|
-
lines = src.is_a?(String) ? src : src.join("\n")
|
88
|
-
new_source = autocorrect_source(cop, ['def a',
|
89
|
-
" #{lines}",
|
90
|
-
'end'].join("\n"))
|
91
|
-
expect(new_source).to eq("def a\nend")
|
92
|
-
end
|
93
85
|
end
|
94
86
|
|
95
87
|
shared_examples_for 'non-debugger' do |name, src|
|
data/lib/rubocop/runner.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'parallel'
|
4
|
+
|
3
5
|
module RuboCop
|
4
6
|
# This class handles the processing of files, which includes dealing with
|
5
7
|
# formatters and letting cops inspect the files.
|
@@ -33,6 +35,7 @@ module RuboCop
|
|
33
35
|
if @options[:list_target_files]
|
34
36
|
list_files(target_files)
|
35
37
|
else
|
38
|
+
warm_cache(target_files) if @options[:parallel]
|
36
39
|
inspect_files(target_files)
|
37
40
|
end
|
38
41
|
end
|
@@ -43,6 +46,13 @@ module RuboCop
|
|
43
46
|
|
44
47
|
private
|
45
48
|
|
49
|
+
# Warms up the RuboCop cache by forking a suitable number of rubocop
|
50
|
+
# instances that each inspects its alotted group of files.
|
51
|
+
def warm_cache(target_files)
|
52
|
+
puts 'Running parallel inspection'
|
53
|
+
Parallel.each(target_files, &method(:file_offenses))
|
54
|
+
end
|
55
|
+
|
46
56
|
def find_target_files(paths)
|
47
57
|
target_finder = TargetFinder.new(@config_store, @options)
|
48
58
|
target_files = target_finder.find(paths)
|
@@ -116,7 +126,7 @@ module RuboCop
|
|
116
126
|
end
|
117
127
|
|
118
128
|
def add_unneeded_disables(file, offenses, source)
|
119
|
-
if
|
129
|
+
if check_for_unneeded_disables?(source)
|
120
130
|
config = @config_store.for(file)
|
121
131
|
if config.for_cop(Cop::Lint::UnneededDisable).fetch('Enabled')
|
122
132
|
cop = Cop::Lint::UnneededDisable.new(config, @options)
|
@@ -132,7 +142,7 @@ module RuboCop
|
|
132
142
|
offenses.sort.reject(&:disabled?).freeze
|
133
143
|
end
|
134
144
|
|
135
|
-
def
|
145
|
+
def check_for_unneeded_disables?(source)
|
136
146
|
!source.disabled_line_ranges.empty? && !filtered_run?
|
137
147
|
end
|
138
148
|
|