rubocop 0.48.1 → 0.49.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (184) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -3
  3. data/config/default.yml +397 -357
  4. data/config/disabled.yml +29 -29
  5. data/config/enabled.yml +366 -326
  6. data/lib/rubocop.rb +85 -70
  7. data/lib/rubocop/ast/builder.rb +4 -1
  8. data/lib/rubocop/ast/node.rb +2 -2
  9. data/lib/rubocop/ast/node/and_node.rb +1 -1
  10. data/lib/rubocop/ast/node/args_node.rb +24 -0
  11. data/lib/rubocop/ast/node/block_node.rb +107 -0
  12. data/lib/rubocop/ast/node/case_node.rb +1 -1
  13. data/lib/rubocop/ast/node/ensure_node.rb +1 -1
  14. data/lib/rubocop/ast/node/for_node.rb +1 -1
  15. data/lib/rubocop/ast/node/if_node.rb +1 -1
  16. data/lib/rubocop/ast/node/mixin/parameterized_node.rb +74 -0
  17. data/lib/rubocop/ast/node/or_node.rb +1 -1
  18. data/lib/rubocop/ast/node/pair_node.rb +1 -1
  19. data/lib/rubocop/ast/node/resbody_node.rb +1 -1
  20. data/lib/rubocop/ast/node/send_node.rb +36 -57
  21. data/lib/rubocop/ast/node/super_node.rb +42 -0
  22. data/lib/rubocop/ast/node/until_node.rb +1 -1
  23. data/lib/rubocop/ast/node/when_node.rb +1 -1
  24. data/lib/rubocop/ast/node/while_node.rb +1 -1
  25. data/lib/rubocop/cli.rb +10 -0
  26. data/lib/rubocop/config.rb +23 -7
  27. data/lib/rubocop/config_loader.rb +19 -3
  28. data/lib/rubocop/cop/badge.rb +1 -1
  29. data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
  30. data/lib/rubocop/cop/commissioner.rb +1 -1
  31. data/lib/rubocop/cop/cop.rb +10 -0
  32. data/lib/rubocop/cop/{style → layout}/access_modifier_indentation.rb +33 -3
  33. data/lib/rubocop/cop/{style → layout}/align_array.rb +16 -1
  34. data/lib/rubocop/cop/{style → layout}/align_hash.rb +1 -1
  35. data/lib/rubocop/cop/{style → layout}/align_parameters.rb +29 -1
  36. data/lib/rubocop/cop/{style → layout}/block_end_newline.rb +10 -5
  37. data/lib/rubocop/cop/{style → layout}/case_indentation.rb +64 -1
  38. data/lib/rubocop/cop/{style → layout}/closing_parenthesis_indentation.rb +2 -2
  39. data/lib/rubocop/cop/{style → layout}/comment_indentation.rb +1 -1
  40. data/lib/rubocop/cop/{style → layout}/dot_position.rb +1 -1
  41. data/lib/rubocop/cop/{style → layout}/else_alignment.rb +1 -1
  42. data/lib/rubocop/cop/{style → layout}/empty_line_after_magic_comment.rb +1 -1
  43. data/lib/rubocop/cop/{style → layout}/empty_line_between_defs.rb +1 -1
  44. data/lib/rubocop/cop/{style → layout}/empty_lines.rb +1 -1
  45. data/lib/rubocop/cop/{style → layout}/empty_lines_around_access_modifier.rb +2 -7
  46. data/lib/rubocop/cop/{style → layout}/empty_lines_around_begin_body.rb +1 -1
  47. data/lib/rubocop/cop/{style → layout}/empty_lines_around_block_body.rb +2 -4
  48. data/lib/rubocop/cop/{style → layout}/empty_lines_around_class_body.rb +1 -1
  49. data/lib/rubocop/cop/{style → layout}/empty_lines_around_exception_handling_keywords.rb +1 -1
  50. data/lib/rubocop/cop/{style → layout}/empty_lines_around_method_body.rb +1 -1
  51. data/lib/rubocop/cop/{style → layout}/empty_lines_around_module_body.rb +1 -1
  52. data/lib/rubocop/cop/{style → layout}/end_of_line.rb +1 -1
  53. data/lib/rubocop/cop/{style → layout}/extra_spacing.rb +1 -1
  54. data/lib/rubocop/cop/{style → layout}/first_array_element_line_break.rb +1 -1
  55. data/lib/rubocop/cop/{style → layout}/first_hash_element_line_break.rb +1 -1
  56. data/lib/rubocop/cop/{style → layout}/first_method_argument_line_break.rb +1 -1
  57. data/lib/rubocop/cop/{style → layout}/first_method_parameter_line_break.rb +1 -1
  58. data/lib/rubocop/cop/{style → layout}/first_parameter_indentation.rb +1 -1
  59. data/lib/rubocop/cop/{style → layout}/indent_array.rb +1 -1
  60. data/lib/rubocop/cop/{style → layout}/indent_assignment.rb +1 -1
  61. data/lib/rubocop/cop/{style → layout}/indent_hash.rb +2 -2
  62. data/lib/rubocop/cop/{style → layout}/indent_heredoc.rb +3 -3
  63. data/lib/rubocop/cop/{style → layout}/indentation_consistency.rb +1 -1
  64. data/lib/rubocop/cop/{style → layout}/indentation_width.rb +10 -12
  65. data/lib/rubocop/cop/{style → layout}/initial_indentation.rb +1 -1
  66. data/lib/rubocop/cop/{style → layout}/leading_comment_space.rb +1 -1
  67. data/lib/rubocop/cop/{style → layout}/multiline_array_brace_layout.rb +1 -1
  68. data/lib/rubocop/cop/{style → layout}/multiline_assignment_layout.rb +1 -1
  69. data/lib/rubocop/cop/{style → layout}/multiline_block_layout.rb +21 -36
  70. data/lib/rubocop/cop/{style → layout}/multiline_hash_brace_layout.rb +5 -1
  71. data/lib/rubocop/cop/{style → layout}/multiline_method_call_brace_layout.rb +1 -1
  72. data/lib/rubocop/cop/{style → layout}/multiline_method_call_indentation.rb +3 -3
  73. data/lib/rubocop/cop/{style → layout}/multiline_method_definition_brace_layout.rb +1 -1
  74. data/lib/rubocop/cop/{style → layout}/multiline_operation_indentation.rb +6 -5
  75. data/lib/rubocop/cop/{style → layout}/rescue_ensure_alignment.rb +1 -1
  76. data/lib/rubocop/cop/{style → layout}/space_after_colon.rb +2 -2
  77. data/lib/rubocop/cop/{style → layout}/space_after_comma.rb +2 -2
  78. data/lib/rubocop/cop/{style → layout}/space_after_method_name.rb +1 -1
  79. data/lib/rubocop/cop/{style → layout}/space_after_not.rb +1 -1
  80. data/lib/rubocop/cop/{style → layout}/space_after_semicolon.rb +2 -2
  81. data/lib/rubocop/cop/{style → layout}/space_around_block_parameters.rb +7 -5
  82. data/lib/rubocop/cop/{style → layout}/space_around_equals_in_parameter_default.rb +1 -1
  83. data/lib/rubocop/cop/{style → layout}/space_around_keyword.rb +1 -1
  84. data/lib/rubocop/cop/{style → layout}/space_around_operators.rb +6 -2
  85. data/lib/rubocop/cop/{style → layout}/space_before_block_braces.rb +6 -2
  86. data/lib/rubocop/cop/{style → layout}/space_before_comma.rb +1 -1
  87. data/lib/rubocop/cop/{style → layout}/space_before_comment.rb +1 -1
  88. data/lib/rubocop/cop/{style → layout}/space_before_first_arg.rb +4 -2
  89. data/lib/rubocop/cop/{style → layout}/space_before_semicolon.rb +1 -1
  90. data/lib/rubocop/cop/{style → layout}/space_in_lambda_literal.rb +1 -1
  91. data/lib/rubocop/cop/{style → layout}/space_inside_array_percent_literal.rb +1 -1
  92. data/lib/rubocop/cop/{style → layout}/space_inside_block_braces.rb +3 -4
  93. data/lib/rubocop/cop/{style → layout}/space_inside_brackets.rb +1 -1
  94. data/lib/rubocop/cop/{style → layout}/space_inside_hash_literal_braces.rb +1 -1
  95. data/lib/rubocop/cop/{style → layout}/space_inside_parens.rb +1 -1
  96. data/lib/rubocop/cop/{style → layout}/space_inside_percent_literal_delimiters.rb +8 -7
  97. data/lib/rubocop/cop/{style → layout}/space_inside_range_literal.rb +1 -1
  98. data/lib/rubocop/cop/{style → layout}/space_inside_string_interpolation.rb +1 -1
  99. data/lib/rubocop/cop/{style → layout}/tab.rb +1 -1
  100. data/lib/rubocop/cop/{style → layout}/trailing_blank_lines.rb +1 -1
  101. data/lib/rubocop/cop/{style → layout}/trailing_whitespace.rb +2 -2
  102. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  103. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -4
  104. data/lib/rubocop/cop/lint/debugger.rb +0 -15
  105. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -1
  106. data/lib/rubocop/cop/lint/rescue_type.rb +81 -0
  107. data/lib/rubocop/cop/lint/script_permission.rb +42 -0
  108. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  109. data/lib/rubocop/cop/message_annotator.rb +23 -13
  110. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  111. data/lib/rubocop/cop/mixin/array_min_size.rb +59 -0
  112. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +10 -11
  113. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  114. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  115. data/lib/rubocop/cop/mixin/enforce_superclass.rb +36 -0
  116. data/lib/rubocop/cop/mixin/hash_alignment.rb +1 -1
  117. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +7 -3
  118. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  119. data/lib/rubocop/cop/performance/caller.rb +41 -0
  120. data/lib/rubocop/cop/performance/compare_with_block.rb +60 -14
  121. data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
  122. data/lib/rubocop/cop/performance/redundant_merge.rb +2 -0
  123. data/lib/rubocop/cop/rails/action_filter.rb +1 -3
  124. data/lib/rubocop/cop/rails/application_job.rb +32 -0
  125. data/lib/rubocop/cop/rails/application_record.rb +32 -0
  126. data/lib/rubocop/cop/rails/blank.rb +9 -3
  127. data/lib/rubocop/cop/rails/output_safety.rb +59 -15
  128. data/lib/rubocop/cop/rails/present.rb +9 -3
  129. data/lib/rubocop/cop/rails/relative_date_constant.rb +35 -4
  130. data/lib/rubocop/cop/rails/reversible_migration.rb +82 -18
  131. data/lib/rubocop/cop/rails/save_bang.rb +7 -2
  132. data/lib/rubocop/cop/rails/skips_model_validations.rb +7 -0
  133. data/lib/rubocop/cop/registry.rb +4 -3
  134. data/lib/rubocop/cop/security/eval.rb +9 -3
  135. data/lib/rubocop/cop/style/and_or.rb +1 -1
  136. data/lib/rubocop/cop/style/block_delimiters.rb +11 -17
  137. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  138. data/lib/rubocop/cop/style/collection_methods.rb +1 -3
  139. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  140. data/lib/rubocop/cop/style/copyright.rb +2 -2
  141. data/lib/rubocop/cop/style/documentation_method.rb +1 -1
  142. data/lib/rubocop/cop/style/each_for_simple_loop.rb +2 -1
  143. data/lib/rubocop/cop/style/each_with_object.rb +10 -6
  144. data/lib/rubocop/cop/style/empty_case_condition.rb +2 -2
  145. data/lib/rubocop/cop/style/for.rb +4 -5
  146. data/lib/rubocop/cop/style/format_string.rb +49 -0
  147. data/lib/rubocop/cop/style/format_string_token.rb +141 -0
  148. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  149. data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -2
  150. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -1
  151. data/lib/rubocop/cop/style/inverse_methods.rb +10 -1
  152. data/lib/rubocop/cop/style/lambda.rb +9 -9
  153. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -0
  154. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -3
  155. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -2
  156. data/lib/rubocop/cop/style/method_name.rb +8 -2
  157. data/lib/rubocop/cop/style/mixin_grouping.rb +41 -3
  158. data/lib/rubocop/cop/style/multiline_block_chain.rb +7 -11
  159. data/lib/rubocop/cop/style/multiple_comparison.rb +77 -0
  160. data/lib/rubocop/cop/style/next.rb +11 -22
  161. data/lib/rubocop/cop/style/parallel_assignment.rb +10 -19
  162. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -2
  163. data/lib/rubocop/cop/style/self_assignment.rb +4 -0
  164. data/lib/rubocop/cop/style/single_line_block_params.rb +23 -17
  165. data/lib/rubocop/cop/style/symbol_array.rb +24 -13
  166. data/lib/rubocop/cop/style/symbol_proc.rb +4 -0
  167. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  168. data/lib/rubocop/cop/style/unneeded_interpolation.rb +4 -0
  169. data/lib/rubocop/cop/style/word_array.rb +33 -53
  170. data/lib/rubocop/cop/style/yoda_condition.rb +78 -0
  171. data/lib/rubocop/cop/team.rb +1 -14
  172. data/lib/rubocop/cop/util.rb +16 -0
  173. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -11
  174. data/lib/rubocop/node_pattern.rb +52 -52
  175. data/lib/rubocop/options.rb +25 -0
  176. data/lib/rubocop/path_util.rb +17 -1
  177. data/lib/rubocop/result_cache.rb +8 -7
  178. data/lib/rubocop/rspec/expect_offense.rb +167 -0
  179. data/lib/rubocop/rspec/shared_examples.rb +0 -8
  180. data/lib/rubocop/rspec/support.rb +1 -0
  181. data/lib/rubocop/runner.rb +12 -2
  182. data/lib/rubocop/target_finder.rb +5 -0
  183. data/lib/rubocop/version.rb +1 -1
  184. 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)
@@ -23,55 +23,55 @@ module RuboCop
23
23
  #
24
24
  # ## Pattern string format examples
25
25
  #
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
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
- file, lineno = *caller.first.split(':')
479
- class_eval(src, file, lineno.to_i)
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)
@@ -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
@@ -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
- path =~ pattern
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
 
@@ -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
- if root == '/tmp'
67
- tmpdir = File.realpath(Dir.tmpdir)
68
- # Include user ID in the path to make sure the user has write access.
69
- root = File.join(tmpdir, Process.uid.to_s)
70
- end
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|
@@ -6,3 +6,4 @@ require 'rubocop/rspec/cop_helper'
6
6
  require 'rubocop/rspec/host_environment_simulation_helper'
7
7
  require 'rubocop/rspec/shared_contexts'
8
8
  require 'rubocop/rspec/shared_examples'
9
+ require 'rubocop/rspec/expect_offense'
@@ -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 check_for_unneded_disables?(source)
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 check_for_unneded_disables?(source)
145
+ def check_for_unneeded_disables?(source)
136
146
  !source.disabled_line_ranges.empty? && !filtered_run?
137
147
  end
138
148