rubocop 0.48.1 → 0.49.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.
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