rubocop 1.19.0 → 1.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (236) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +129 -21
  4. data/lib/rubocop/config.rb +5 -0
  5. data/lib/rubocop/config_loader.rb +5 -3
  6. data/lib/rubocop/config_validator.rb +9 -1
  7. data/lib/rubocop/cop/base.rb +3 -3
  8. data/lib/rubocop/cop/bundler/gem_comment.rb +3 -3
  9. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  10. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +45 -21
  11. data/lib/rubocop/cop/bundler/ordered_gems.rb +3 -12
  12. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  13. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +11 -10
  15. data/lib/rubocop/cop/documentation.rb +1 -1
  16. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  17. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  18. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +3 -12
  19. data/lib/rubocop/cop/gemspec/require_mfa.rb +146 -0
  20. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +31 -24
  21. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  22. data/lib/rubocop/cop/generator.rb +14 -8
  23. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +60 -0
  24. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  26. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/assignment_indentation.rb +1 -1
  28. data/lib/rubocop/cop/layout/block_alignment.rb +3 -3
  29. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  30. data/lib/rubocop/cop/layout/dot_position.rb +34 -5
  31. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  32. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  33. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +7 -4
  34. data/lib/rubocop/cop/layout/end_alignment.rb +2 -3
  35. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -1
  37. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  39. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  40. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  41. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  42. data/lib/rubocop/cop/layout/line_length.rb +9 -7
  43. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  44. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -3
  45. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  46. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  47. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -0
  48. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +6 -5
  49. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  50. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  51. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  52. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  53. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  54. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  55. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +74 -28
  57. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  58. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +111 -0
  59. data/lib/rubocop/cop/lint/ambiguous_range.rb +11 -11
  60. data/lib/rubocop/cop/lint/assignment_in_condition.rb +7 -5
  61. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +18 -5
  62. data/lib/rubocop/cop/lint/boolean_symbol.rb +5 -0
  63. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  64. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  65. data/lib/rubocop/cop/lint/deprecated_constants.rb +3 -2
  66. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +24 -1
  67. data/lib/rubocop/cop/lint/else_layout.rb +10 -6
  68. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  69. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  70. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  71. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +12 -3
  72. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  73. data/lib/rubocop/cop/lint/interpolation_check.rb +5 -0
  74. data/lib/rubocop/cop/lint/loop.rb +4 -3
  75. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +5 -1
  76. data/lib/rubocop/cop/lint/number_conversion.rb +16 -2
  77. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  78. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +4 -2
  79. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +17 -0
  80. data/lib/rubocop/cop/lint/percent_string_array.rb +10 -0
  81. data/lib/rubocop/cop/lint/raise_exception.rb +4 -0
  82. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +5 -4
  83. data/lib/rubocop/cop/lint/require_relative_self_path.rb +50 -0
  84. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  85. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  86. data/lib/rubocop/cop/lint/triple_quotes.rb +1 -1
  87. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +8 -3
  88. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  89. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -2
  90. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  91. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  92. data/lib/rubocop/cop/lint/useless_times.rb +4 -3
  93. data/lib/rubocop/cop/metrics/abc_size.rb +6 -0
  94. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  95. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  96. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  97. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  98. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  99. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  100. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  101. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -2
  102. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +23 -1
  103. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  104. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  105. data/lib/rubocop/cop/mixin/heredoc.rb +1 -3
  106. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  107. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  108. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  109. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +9 -1
  110. data/lib/rubocop/cop/mixin/percent_array.rb +11 -3
  111. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  112. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  113. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  114. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  115. data/lib/rubocop/cop/mixin/trailing_body.rb +1 -1
  116. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  117. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  118. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  119. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  120. data/lib/rubocop/cop/naming/inclusive_language.rb +9 -9
  121. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +5 -4
  122. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +7 -0
  123. data/lib/rubocop/cop/security/io_methods.rb +49 -0
  124. data/lib/rubocop/cop/security/json_load.rb +8 -7
  125. data/lib/rubocop/cop/security/open.rb +4 -0
  126. data/lib/rubocop/cop/security/yaml_load.rb +4 -0
  127. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  128. data/lib/rubocop/cop/style/and_or.rb +5 -0
  129. data/lib/rubocop/cop/style/arguments_forwarding.rb +13 -2
  130. data/lib/rubocop/cop/style/array_coercion.rb +21 -3
  131. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  132. data/lib/rubocop/cop/style/block_delimiters.rb +23 -6
  133. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  134. data/lib/rubocop/cop/style/case_like_if.rb +5 -0
  135. data/lib/rubocop/cop/style/class_and_module_children.rb +9 -0
  136. data/lib/rubocop/cop/style/collection_compact.rb +7 -5
  137. data/lib/rubocop/cop/style/collection_methods.rb +8 -6
  138. data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
  139. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  140. data/lib/rubocop/cop/style/commented_keyword.rb +9 -4
  141. data/lib/rubocop/cop/style/date_time.rb +5 -0
  142. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  143. data/lib/rubocop/cop/style/documentation.rb +23 -8
  144. data/lib/rubocop/cop/style/double_negation.rb +27 -6
  145. data/lib/rubocop/cop/style/empty_method.rb +2 -2
  146. data/lib/rubocop/cop/style/encoding.rb +26 -15
  147. data/lib/rubocop/cop/style/explicit_block_argument.rb +21 -11
  148. data/lib/rubocop/cop/style/float_division.rb +10 -2
  149. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  150. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +7 -2
  151. data/lib/rubocop/cop/style/global_std_stream.rb +4 -0
  152. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  153. data/lib/rubocop/cop/style/hash_each_methods.rb +5 -0
  154. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  155. data/lib/rubocop/cop/style/hash_transform_keys.rb +4 -6
  156. data/lib/rubocop/cop/style/hash_transform_values.rb +4 -6
  157. data/lib/rubocop/cop/style/identical_conditional_branches.rb +18 -16
  158. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +18 -4
  159. data/lib/rubocop/cop/style/infinite_loop.rb +4 -3
  160. data/lib/rubocop/cop/style/inverse_methods.rb +9 -2
  161. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  162. data/lib/rubocop/cop/style/line_end_concatenation.rb +14 -1
  163. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  164. data/lib/rubocop/cop/style/module_function.rb +8 -9
  165. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  166. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -1
  167. data/lib/rubocop/cop/style/mutable_constant.rb +73 -6
  168. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  169. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  170. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  171. data/lib/rubocop/cop/style/not.rb +2 -2
  172. data/lib/rubocop/cop/style/numbered_parameters.rb +46 -0
  173. data/lib/rubocop/cop/style/numbered_parameters_limit.rb +50 -0
  174. data/lib/rubocop/cop/style/numeric_literals.rb +7 -8
  175. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -0
  176. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  177. data/lib/rubocop/cop/style/optional_arguments.rb +4 -0
  178. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +14 -4
  179. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  180. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  181. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  182. data/lib/rubocop/cop/style/preferred_hash_methods.rb +9 -4
  183. data/lib/rubocop/cop/style/quoted_symbols.rb +21 -7
  184. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  185. data/lib/rubocop/cop/style/redundant_argument.rb +19 -9
  186. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  187. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  188. data/lib/rubocop/cop/style/redundant_fetch_block.rb +4 -0
  189. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +12 -3
  190. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -4
  191. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  192. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  193. data/lib/rubocop/cop/style/redundant_self.rb +10 -0
  194. data/lib/rubocop/cop/style/redundant_self_assignment.rb +4 -3
  195. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +23 -28
  196. data/lib/rubocop/cop/style/redundant_sort.rb +51 -18
  197. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  198. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  199. data/lib/rubocop/cop/style/safe_navigation.rb +13 -2
  200. data/lib/rubocop/cop/style/select_by_regexp.rb +139 -0
  201. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -0
  202. data/lib/rubocop/cop/style/slicing_with_range.rb +13 -0
  203. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  204. data/lib/rubocop/cop/style/special_global_vars.rb +4 -0
  205. data/lib/rubocop/cop/style/static_class.rb +5 -5
  206. data/lib/rubocop/cop/style/string_chars.rb +4 -2
  207. data/lib/rubocop/cop/style/string_concatenation.rb +5 -1
  208. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -0
  209. data/lib/rubocop/cop/style/struct_inheritance.rb +4 -0
  210. data/lib/rubocop/cop/style/swap_values.rb +4 -2
  211. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  212. data/lib/rubocop/cop/style/symbol_proc.rb +26 -0
  213. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +19 -0
  214. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  215. data/lib/rubocop/cop/style/word_array.rb +3 -3
  216. data/lib/rubocop/cop/style/yoda_condition.rb +24 -7
  217. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -0
  218. data/lib/rubocop/cop/util.rb +15 -4
  219. data/lib/rubocop/cops_documentation_generator.rb +17 -5
  220. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  221. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  222. data/lib/rubocop/magic_comment.rb +44 -15
  223. data/lib/rubocop/options.rb +126 -112
  224. data/lib/rubocop/rake_task.rb +1 -1
  225. data/lib/rubocop/remote_config.rb +1 -1
  226. data/lib/rubocop/result_cache.rb +3 -3
  227. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  228. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  229. data/lib/rubocop/rspec/parallel_formatter.rb +90 -0
  230. data/lib/rubocop/rspec/support.rb +1 -0
  231. data/lib/rubocop/runner.rb +2 -3
  232. data/lib/rubocop/target_finder.rb +1 -1
  233. data/lib/rubocop/version.rb +1 -1
  234. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  235. data/lib/rubocop.rb +14 -2
  236. metadata +20 -5
@@ -2,40 +2,63 @@
2
2
 
3
3
  module RuboCop
4
4
  module Cop
5
- module Style
6
- # Common functionality related to annotation comments.
7
- module AnnotationComment
8
- private
9
-
10
- # @api public
11
- def annotation?(comment)
12
- _margin, first_word, colon, space, note = split_comment(comment)
13
- keyword_appearance?(first_word, colon, space) &&
14
- !just_first_word_of_sentence?(first_word, colon, space, note)
15
- end
16
-
17
- # @api public
18
- def split_comment(comment)
19
- match = comment.text.match(/^(# ?)([A-Za-z]+)(\s*:)?(\s+)?(\S+)?/)
20
- return false unless match
21
-
22
- match.captures
23
- end
24
-
25
- # @api public
26
- def keyword_appearance?(first_word, colon, space)
27
- first_word && keyword?(first_word.upcase) && (colon || space)
28
- end
29
-
30
- # @api private
31
- def just_first_word_of_sentence?(first_word, colon, space, note)
32
- first_word == first_word.capitalize && !colon && space && note
33
- end
34
-
35
- # @api public
36
- def keyword?(word)
37
- config.for_cop('Style/CommentAnnotation')['Keywords'].include?(word)
38
- end
5
+ # Representation of an annotation comment in source code (eg. `# TODO: blah blah blah`).
6
+ class AnnotationComment
7
+ extend Forwardable
8
+
9
+ attr_reader :comment, :margin, :keyword, :colon, :space, :note
10
+
11
+ # @param [Parser::Source::Comment] comment
12
+ # @param [Array<String>] keywords
13
+ def initialize(comment, keywords)
14
+ @comment = comment
15
+ @keywords = keywords
16
+ @margin, @keyword, @colon, @space, @note = split_comment(comment)
17
+ end
18
+
19
+ def annotation?
20
+ keyword_appearance? && !just_keyword_of_sentence?
21
+ end
22
+
23
+ def correct?(colon:)
24
+ return false unless keyword && space && note
25
+ return false unless keyword == keyword.upcase
26
+
27
+ self.colon.nil? == !colon
28
+ end
29
+
30
+ # Returns the range bounds for just the annotation
31
+ def bounds
32
+ start = comment.loc.expression.begin_pos + margin.length
33
+ length = [keyword, colon, space].reduce(0) { |len, elem| len + elem.to_s.length }
34
+ [start, start + length]
35
+ end
36
+
37
+ private
38
+
39
+ attr_reader :keywords
40
+
41
+ def split_comment(comment)
42
+ # Sort keywords by reverse length so that if a keyword is in a phrase
43
+ # but also on its own, both will match properly.
44
+ keywords_regex = Regexp.new(
45
+ Regexp.union(keywords.sort_by { |w| -w.length }).source,
46
+ Regexp::IGNORECASE
47
+ )
48
+ regex = /^(# ?)(\b#{keywords_regex}\b)(\s*:)?(\s+)?(\S+)?/i
49
+
50
+ match = comment.text.match(regex)
51
+ return false unless match
52
+
53
+ match.captures
54
+ end
55
+
56
+ def keyword_appearance?
57
+ keyword && (colon || space)
58
+ end
59
+
60
+ def just_keyword_of_sentence?
61
+ keyword == keyword.capitalize && !colon && space && note
39
62
  end
40
63
  end
41
64
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
 
44
44
  # Returns true for lines that shall not be included in the count.
45
45
  def irrelevant_line(source_line)
46
- source_line.blank? || !count_comments? && comment_line?(source_line)
46
+ source_line.blank? || (!count_comments? && comment_line?(source_line))
47
47
  end
48
48
 
49
49
  def build_code_length_calculator(node)
@@ -5,7 +5,6 @@ module RuboCop
5
5
  # Common functionality for checking documentation.
6
6
  module DocumentationComment
7
7
  extend NodePattern::Macros
8
- include Style::AnnotationComment
9
8
 
10
9
  private
11
10
 
@@ -15,7 +14,7 @@ module RuboCop
15
14
  return false unless preceding_comment?(node, preceding_lines.last)
16
15
 
17
16
  preceding_lines.any? do |comment|
18
- !annotation?(comment) &&
17
+ !AnnotationComment.new(comment, annotation_keywords).annotation? &&
19
18
  !interpreter_directive_comment?(comment) &&
20
19
  !rubocop_directive_comment?(comment)
21
20
  end
@@ -44,6 +43,10 @@ module RuboCop
44
43
  def rubocop_directive_comment?(comment)
45
44
  !!DirectiveComment.new(comment).match_captures
46
45
  end
46
+
47
+ def annotation_keywords
48
+ config.for_cop('Style/CommentAnnotation')['Keywords']
49
+ end
47
50
  end
48
51
  end
49
52
  end
@@ -34,8 +34,7 @@ module RuboCop
34
34
 
35
35
  def matching_ranges(end_loc, align_ranges)
36
36
  align_ranges.select do |_, range|
37
- range.line == end_loc.line ||
38
- column_offset_between(range, end_loc).zero?
37
+ same_line?(range, end_loc) || column_offset_between(range, end_loc).zero?
39
38
  end
40
39
  end
41
40
 
@@ -8,7 +8,9 @@ module RuboCop
8
8
 
9
9
  FROZEN_STRING_LITERAL = '# frozen_string_literal:'
10
10
  FROZEN_STRING_LITERAL_ENABLED = '# frozen_string_literal: true'
11
- FROZEN_STRING_LITERAL_TYPES = %i[str dstr].freeze
11
+ FROZEN_STRING_LITERAL_TYPES_RUBY27 = %i[str dstr].freeze
12
+
13
+ private_constant :FROZEN_STRING_LITERAL_TYPES_RUBY27
12
14
 
13
15
  def frozen_string_literal_comment_exists?
14
16
  leading_comment_lines.any? { |line| MagicComment.parse(line).valid_literal_value? }
@@ -16,6 +18,26 @@ module RuboCop
16
18
 
17
19
  private
18
20
 
21
+ def frozen_string_literal?(node)
22
+ frozen_string = if target_ruby_version >= 3.0
23
+ uninterpolated_string?(node) || frozen_heredoc?(node)
24
+ else
25
+ FROZEN_STRING_LITERAL_TYPES_RUBY27.include?(node.type)
26
+ end
27
+
28
+ frozen_string && frozen_string_literals_enabled?
29
+ end
30
+
31
+ def uninterpolated_string?(node)
32
+ node.str_type? || (node.dstr_type? && node.each_descendant(:begin).none?)
33
+ end
34
+
35
+ def frozen_heredoc?(node)
36
+ return false unless node.dstr_type? && node.heredoc?
37
+
38
+ node.children.all?(&:str_type?)
39
+ end
40
+
19
41
  def frozen_string_literals_enabled?
20
42
  ruby_version = processed_source.ruby_version
21
43
  return false unless ruby_version
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for checking gem declarations.
6
+ module GemspecHelp
7
+ extend NodePattern::Macros
8
+
9
+ # @!method gem_specification?(node)
10
+ def_node_matcher :gem_specification?, <<~PATTERN
11
+ (block
12
+ (send
13
+ (const
14
+ (const {cbase nil?} :Gem) :Specification) :new)
15
+ (args
16
+ (arg $_)) ...)
17
+ PATTERN
18
+
19
+ # @!method gem_specification(node)
20
+ def_node_search :gem_specification, <<~PATTERN
21
+ (block
22
+ (send
23
+ (const
24
+ (const {cbase nil?} :Gem) :Specification) :new)
25
+ (args
26
+ (arg $_)) ...)
27
+ PATTERN
28
+ end
29
+ end
30
+ end
@@ -139,9 +139,9 @@ module RuboCop
139
139
  end
140
140
 
141
141
  def self.from_map_to_h(node, match)
142
- strip_trailing_chars = 0
143
-
144
- unless node.parent&.block_type?
142
+ if node.parent&.block_type? && node.parent.send_node == node
143
+ strip_trailing_chars = 0
144
+ else
145
145
  map_range = node.children.first.source_range
146
146
  node_range = node.source_range
147
147
  strip_trailing_chars = node_range.end_pos - map_range.end_pos
@@ -21,9 +21,7 @@ module RuboCop
21
21
  private
22
22
 
23
23
  def indent_level(str)
24
- indentations = str.lines
25
- .map { |line| line[/^\s*/] }
26
- .reject { |line| line.end_with?("\n") }
24
+ indentations = str.lines.map { |line| line[/^\s*/] }.reject { |line| line.end_with?("\n") }
27
25
  indentations.empty? ? 0 : indentations.min_by(&:size).size
28
26
  end
29
27
 
@@ -15,7 +15,7 @@ module RuboCop
15
15
  node.arguments.each do |arg|
16
16
  on_node(type, arg, :send) do |type_node|
17
17
  left_brace = type_node.loc.begin
18
- if left_brace && left_brace.line == left_parenthesis.line
18
+ if left_brace && same_line?(left_brace, left_parenthesis)
19
19
  yield type_node, left_parenthesis
20
20
  ignore_node(type_node)
21
21
  end
@@ -134,7 +134,7 @@ module RuboCop
134
134
 
135
135
  next if a.setter_method?
136
136
  next unless kind == :with_or_without_parentheses ||
137
- kind == :with_parentheses && parentheses?(a)
137
+ (kind == :with_parentheses && parentheses?(a))
138
138
 
139
139
  a.arguments.any? { |arg| within_node?(node, arg) }
140
140
  end
@@ -156,7 +156,7 @@ module RuboCop
156
156
 
157
157
  def disqualified_rhs?(candidate, ancestor)
158
158
  UNALIGNED_RHS_TYPES.include?(ancestor.type) ||
159
- ancestor.block_type? && part_of_block_body?(candidate, ancestor)
159
+ (ancestor.block_type? && part_of_block_body?(candidate, ancestor))
160
160
  end
161
161
 
162
162
  def valid_rhs?(candidate, ancestor)
@@ -90,7 +90,7 @@ module RuboCop
90
90
  # This method depends on the fact that we have guarded
91
91
  # against implicit and empty literals.
92
92
  def opening_brace_on_same_line?(node)
93
- node.loc.begin.line == children(node).first.first_line
93
+ same_line?(node.loc.begin, children(node).first)
94
94
  end
95
95
 
96
96
  # This method depends on the fact that we have guarded
@@ -35,7 +35,15 @@ module RuboCop
35
35
  previous: gem_name(current),
36
36
  current: gem_name(previous)
37
37
  )
38
- add_offense(current, message: message)
38
+
39
+ add_offense(current, message: message) do |corrector|
40
+ OrderedGemCorrector.correct(
41
+ processed_source,
42
+ current,
43
+ previous_declaration(current),
44
+ treat_comments_as_separators
45
+ ).call(corrector)
46
+ end
39
47
  end
40
48
 
41
49
  def gem_name(declaration_node)
@@ -36,10 +36,18 @@ module RuboCop
36
36
  def check_percent_array(node)
37
37
  array_style_detected(:percent, node.values.size)
38
38
 
39
- return unless style == :brackets || invalid_percent_array_contents?(node)
39
+ brackets_required = invalid_percent_array_contents?(node)
40
+ return unless style == :brackets || brackets_required
40
41
 
41
- add_offense(node, message: self.class::ARRAY_MSG) do |corrector|
42
- correct_bracketed(corrector, node)
42
+ # If in percent style but brackets are required due to
43
+ # string content, the file should be excluded in auto-gen-config
44
+ no_acceptable_style! if brackets_required
45
+
46
+ bracketed_array = build_bracketed_array(node)
47
+ message = format(self.class::ARRAY_MSG, prefer: bracketed_array)
48
+
49
+ add_offense(node, message: message) do |corrector|
50
+ corrector.replace(node, bracketed_array)
43
51
  end
44
52
  end
45
53
 
@@ -93,7 +93,15 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def aligned_assignment?(range, line)
96
- range.source[-1] == '=' && line[range.last_column - 1] == '='
96
+ (range.source[-1] == '=' && line[range.last_column - 1] == '=') ||
97
+ aligned_with_append_operator?(range, line)
98
+ end
99
+
100
+ def aligned_with_append_operator?(range, line)
101
+ last_column = range.last_column
102
+
103
+ (range.source == '<<' && line[last_column - 1] == '=') ||
104
+ (range.source[-1] == '=' && line[(last_column - 2)..(last_column - 1)] == '<<')
97
105
  end
98
106
 
99
107
  def aligned_identical?(range, line)
@@ -28,7 +28,7 @@ module RuboCop
28
28
  end
29
29
 
30
30
  def space_missing?(token1, token2)
31
- token1.line == token2.line && token2.column == token1.column + offset
31
+ same_line?(token1, token2) && token2.column == token1.column + offset
32
32
  end
33
33
 
34
34
  def space_required_before?(token)
@@ -32,7 +32,7 @@ module RuboCop
32
32
  end
33
33
 
34
34
  def space_missing?(token1, token2)
35
- token1.line == token2.line && token2.begin_pos > token1.end_pos
35
+ same_line?(token1, token2) && token2.begin_pos > token1.end_pos
36
36
  end
37
37
 
38
38
  def space_required_after?(token)
@@ -54,7 +54,7 @@ module RuboCop
54
54
  end
55
55
 
56
56
  def first_line_comment(node)
57
- comment = processed_source.find_comment { |c| c.loc.line == node.loc.line }
57
+ comment = processed_source.find_comment { |c| same_line?(c, node) }
58
58
  return unless comment
59
59
 
60
60
  comment_source = comment.loc.expression.source
@@ -10,7 +10,7 @@ module RuboCop
10
10
  end
11
11
 
12
12
  def body_on_first_line?(node, body)
13
- node.source_range.first_line == body.source_range.first_line
13
+ same_line?(node, body)
14
14
  end
15
15
 
16
16
  def first_part_of(body)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable Style/AsciiComments
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Naming
@@ -90,4 +88,3 @@ module RuboCop
90
88
  end
91
89
  end
92
90
  end
93
- # rubocop:enable Style/AsciiComments
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # # With `AllowNamesEndingInNumbers` set to false
25
25
  # foo { |num1, num2| num1 * num2 }
26
26
  #
27
- # # With `MinParamNameLength` set to number greater than 1
27
+ # # With `MinNameLength` set to number greater than 1
28
28
  # baz { |a, b, c| do_stuff(a, b, c) }
29
29
  #
30
30
  # # good
@@ -54,7 +54,7 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def allowed_assignment?(value)
57
- value && %i[block const casgn].include?(value.type) ||
57
+ (value && %i[block const casgn].include?(value.type)) ||
58
58
  allowed_method_call_on_rhs?(value) ||
59
59
  class_or_struct_return_method?(value) ||
60
60
  allowed_conditional_expression_on_rhs?(value)
@@ -14,6 +14,18 @@ module RuboCop
14
14
  # (i.e. `bundler-console` becomes `Bundler::Console`). As such, the
15
15
  # gemspec is supposed to be named `bundler-console.gemspec`.
16
16
  #
17
+ # When `ExpectMatchingDefinition` (default: `false`) is `true`, the cop requires
18
+ # each file to have a class, module or `Struct` defined in it that matches
19
+ # the filename. This can be further configured using
20
+ # `CheckDefinitionPathHierarchy` (default: `true`) to determine whether the
21
+ # path should match the namespace of the above definition.
22
+ #
23
+ # When `IgnoreExecutableScripts` (default: `true`) is `true`, files that start
24
+ # with a shebang line are not considered by the cop.
25
+ #
26
+ # When `Regex` is set, the cop will flag any filename that does not match
27
+ # the regular expression.
28
+ #
17
29
  # @example
18
30
  # # bad
19
31
  # lib/layoutManager.rb
@@ -28,11 +40,19 @@ module RuboCop
28
40
  include RangeHelp
29
41
 
30
42
  MSG_SNAKE_CASE = 'The name of this source file (`%<basename>s`) should use snake_case.'
31
- MSG_NO_DEFINITION = '%<basename>s should define a class or module called `%<namespace>s`.'
43
+ MSG_NO_DEFINITION = '`%<basename>s` should define a class or module called `%<namespace>s`.'
32
44
  MSG_REGEX = '`%<basename>s` should match `%<regex>s`.'
33
45
 
34
46
  SNAKE_CASE = /^[\d[[:lower:]]_.?!]+$/.freeze
35
47
 
48
+ # @!method struct_definition(node)
49
+ def_node_matcher :struct_definition, <<~PATTERN
50
+ {
51
+ (casgn $_ $_ (send (const {nil? cbase} :Struct) :new ...))
52
+ (casgn $_ $_ (block (send (const {nil? cbase} :Struct) :new ...) ...))
53
+ }
54
+ PATTERN
55
+
36
56
  def on_new_investigation
37
57
  file_path = processed_source.file_path
38
58
  return if config.file_to_exclude?(file_path) || config.allowed_camel_case_file?(file_path)
@@ -103,6 +123,10 @@ module RuboCop
103
123
  cop_config['CheckDefinitionPathHierarchy']
104
124
  end
105
125
 
126
+ def definition_path_hierarchy_roots
127
+ cop_config['CheckDefinitionPathHierarchyRoots'] || []
128
+ end
129
+
106
130
  def regex
107
131
  cop_config['Regex']
108
132
  end
@@ -126,7 +150,7 @@ module RuboCop
126
150
  name = namespace.pop
127
151
 
128
152
  on_node(%i[class module casgn], node) do |child|
129
- next unless (const = child.defined_module)
153
+ next unless (const = find_definition(child))
130
154
 
131
155
  const_namespace, const_name = *const
132
156
  next if name != const_name && !match_acronym?(name, const_name)
@@ -138,6 +162,15 @@ module RuboCop
138
162
  nil
139
163
  end
140
164
 
165
+ def find_definition(node)
166
+ node.defined_module || defined_struct(node)
167
+ end
168
+
169
+ def defined_struct(node)
170
+ namespace, name = *struct_definition(node)
171
+ s(:const, namespace, name) if name
172
+ end
173
+
141
174
  def match_namespace(node, namespace, expected)
142
175
  match_partial = partial_matcher!(expected)
143
176
 
@@ -177,13 +210,13 @@ module RuboCop
177
210
  allowed_acronyms.any? { |acronym| expected.gsub(acronym.capitalize, acronym) == name }
178
211
  end
179
212
 
180
- def to_namespace(path)
213
+ def to_namespace(path) # rubocop:disable Metrics/AbcSize
181
214
  components = Pathname(path).each_filename.to_a
182
215
  # To convert a pathname to a Ruby namespace, we need a starting point
183
216
  # But RC can be run from any working directory, and can check any path
184
217
  # We can't assume that the working directory, or any other, is the
185
218
  # "starting point" to build a namespace.
186
- start = %w[lib spec test src]
219
+ start = definition_path_hierarchy_roots
187
220
  start_index = nil
188
221
 
189
222
  # To find the closest namespace root take the path components, and
@@ -70,6 +70,8 @@ module RuboCop
70
70
  include RangeHelp
71
71
 
72
72
  EMPTY_ARRAY = [].freeze
73
+ MSG = "Consider replacing '%<term>s'%<suffix>s."
74
+ MSG_FOR_FILE_PATH = "Consider replacing '%<term>s' in file path%<suffix>s."
73
75
 
74
76
  WordLocation = Struct.new(:word, :position)
75
77
 
@@ -197,12 +199,11 @@ module RuboCop
197
199
  end
198
200
 
199
201
  def create_single_word_message_for_file(word)
200
- create_message(word).sub(/\.$/, ' in file path.')
202
+ create_message(word, MSG_FOR_FILE_PATH)
201
203
  end
202
204
 
203
205
  def create_multiple_word_message_for_file(words)
204
- quoted_words = words.map { |word| "'#{word}'" }
205
- "Consider replacing problematic terms #{quoted_words.join(', ')} in file path."
206
+ format(MSG_FOR_FILE_PATH, term: words.join("', '"), suffix: ' with other terms')
206
207
  end
207
208
 
208
209
  def scan_for_words(input)
@@ -223,9 +224,12 @@ module RuboCop
223
224
  safe_str.gsub(@allowed_regex) { |match| '*' * match.size }
224
225
  end
225
226
 
226
- def create_message(word)
227
+ def create_message(word, message = MSG)
227
228
  flagged_term = find_flagged_term(word)
228
- "Consider replacing problematic term '#{word}'#{flagged_term['SuggestionString']}."
229
+ suggestions = flagged_term['SuggestionString']
230
+ suggestions = ' with another term' if suggestions.blank?
231
+
232
+ format(message, term: word, suffix: suggestions)
229
233
  end
230
234
 
231
235
  def find_flagged_term(word)
@@ -235,10 +239,6 @@ module RuboCop
235
239
  flagged_term
236
240
  end
237
241
 
238
- def create_message_for_file(word)
239
- create_message(word).sub(/\.$/, ' in file path.')
240
- end
241
-
242
242
  def preprocess_suggestions(suggestions)
243
243
  return '' if suggestions.nil? ||
244
244
  (suggestions.is_a?(String) && suggestions.strip.empty?) || suggestions.empty?
@@ -14,6 +14,11 @@ module RuboCop
14
14
  # convention that is used to implicitly indicate that an ivar should not
15
15
  # be set or referenced outside of the memoization method.
16
16
  #
17
+ # @safety
18
+ # This cop relies on the pattern `@instance_var ||= ...`,
19
+ # but this is sometimes used for other purposes than memoization
20
+ # so this cop is considered unsafe.
21
+ #
17
22
  # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
18
23
  # # bad
19
24
  # # Method foo is memoized using an instance variable that is
@@ -139,10 +144,6 @@ module RuboCop
139
144
  # define_method(:foo) do
140
145
  # @_foo ||= calculate_expensive_thing
141
146
  # end
142
- #
143
- # This cop relies on the pattern `@instance_var ||= ...`,
144
- # but this is sometimes used for other purposes than memoization
145
- # so this cop is considered unsafe.
146
147
  class MemoizedInstanceVariableName < Base
147
148
  include ConfigurableEnforcedStyle
148
149
 
@@ -75,6 +75,9 @@ module RuboCop
75
75
  preferred_name = preferred_name(offending_name)
76
76
  return if preferred_name.to_sym == offending_name
77
77
 
78
+ # check variable shadowing for exception variable
79
+ return if shadowed_variable_name?(node)
80
+
78
81
  range = offense_range(node)
79
82
  message = message(node)
80
83
 
@@ -150,6 +153,10 @@ module RuboCop
150
153
  preferred_name = preferred_name(offending_name)
151
154
  format(MSG, preferred: preferred_name, bad: offending_name)
152
155
  end
156
+
157
+ def shadowed_variable_name?(node)
158
+ node.each_descendant(:lvar).any? { |n| n.children.first.to_s == preferred_name(n) }
159
+ end
153
160
  end
154
161
  end
155
162
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Security
6
+ # Checks for the first argument to `IO.read`, `IO.binread`, `IO.write`, `IO.binwrite`,
7
+ # `IO.foreach`, and `IO.readlines`.
8
+ #
9
+ # If argument starts with a pipe character (`'|'`) and the receiver is the `IO` class,
10
+ # a subprocess is created in the same way as `Kernel#open`, and its output is returned.
11
+ # `Kernel#open` may allow unintentional command injection, which is the reason these
12
+ # `IO` methods are a security risk.
13
+ # Consider to use `File.read` to disable the behavior of subprocess invocation.
14
+ #
15
+ # @safety
16
+ # This cop is unsafe because false positive will occur if the variable passed as
17
+ # the first argument is a command that is not a file path.
18
+ #
19
+ # @example
20
+ #
21
+ # # bad
22
+ # IO.read(path)
23
+ # IO.read('path')
24
+ #
25
+ # # good
26
+ # File.read(path)
27
+ # File.read('path')
28
+ # IO.read('| command') # Allow intentional command invocation.
29
+ #
30
+ class IoMethods < Base
31
+ extend AutoCorrector
32
+
33
+ MSG = '`File.%<method_name>s` is safer than `IO.%<method_name>s`.'
34
+ RESTRICT_ON_SEND = %i[read binread write binwrite foreach readlines].freeze
35
+
36
+ def on_send(node)
37
+ return unless (receiver = node.receiver) && receiver.source == 'IO'
38
+
39
+ argument = node.first_argument
40
+ return if argument.respond_to?(:value) && argument.value.strip.start_with?('|')
41
+
42
+ add_offense(node, message: format(MSG, method_name: node.method_name)) do |corrector|
43
+ corrector.replace(receiver, 'File')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end