rubocop 1.22.3 → 1.25.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -3
  4. data/config/default.yml +86 -5
  5. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  6. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  7. data/lib/rubocop/cli/command/show_docs_url.rb +48 -0
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  9. data/lib/rubocop/cli.rb +1 -0
  10. data/lib/rubocop/config_loader_resolver.rb +1 -1
  11. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  12. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/if_then_corrector.rb +55 -0
  14. data/lib/rubocop/cop/documentation.rb +19 -2
  15. data/lib/rubocop/cop/gemspec/date_assignment.rb +2 -10
  16. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -10
  17. data/lib/rubocop/cop/gemspec/require_mfa.rb +144 -0
  18. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -3
  19. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -10
  20. data/lib/rubocop/cop/generator.rb +5 -4
  21. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +47 -0
  22. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +3 -1
  23. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  24. data/lib/rubocop/cop/layout/argument_alignment.rb +36 -9
  25. data/lib/rubocop/cop/layout/comment_indentation.rb +31 -2
  26. data/lib/rubocop/cop/layout/dot_position.rb +4 -0
  27. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +22 -1
  28. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  29. data/lib/rubocop/cop/layout/space_after_colon.rb +1 -1
  30. data/lib/rubocop/cop/layout/space_before_first_arg.rb +4 -0
  31. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +11 -5
  32. data/lib/rubocop/cop/lint/ambiguous_range.rb +2 -2
  33. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -1
  34. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +1 -1
  35. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +16 -4
  36. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +6 -0
  37. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
  38. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +10 -5
  39. data/lib/rubocop/cop/lint/number_conversion.rb +5 -2
  40. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +7 -4
  41. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +117 -0
  42. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  43. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -9
  44. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  45. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  46. data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
  47. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  48. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +1 -1
  49. data/lib/rubocop/cop/mixin/enforce_superclass.rb +5 -0
  50. data/lib/rubocop/cop/mixin/gemspec_help.rb +30 -0
  51. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +4 -3
  52. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +68 -0
  53. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -5
  54. data/lib/rubocop/cop/naming/block_forwarding.rb +121 -0
  55. data/lib/rubocop/cop/naming/file_name.rb +37 -4
  56. data/lib/rubocop/cop/security/open.rb +11 -1
  57. data/lib/rubocop/cop/style/character_literal.rb +8 -1
  58. data/lib/rubocop/cop/style/collection_compact.rb +31 -13
  59. data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
  60. data/lib/rubocop/cop/style/documentation.rb +1 -1
  61. data/lib/rubocop/cop/style/empty_case_condition.rb +10 -0
  62. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  63. data/lib/rubocop/cop/style/file_read.rb +112 -0
  64. data/lib/rubocop/cop/style/file_write.rb +124 -0
  65. data/lib/rubocop/cop/style/format_string_token.rb +2 -1
  66. data/lib/rubocop/cop/style/hash_conversion.rb +2 -1
  67. data/lib/rubocop/cop/style/hash_syntax.rb +36 -0
  68. data/lib/rubocop/cop/style/hash_transform_keys.rb +6 -6
  69. data/lib/rubocop/cop/style/hash_transform_values.rb +6 -6
  70. data/lib/rubocop/cop/style/if_inside_else.rb +15 -0
  71. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  72. data/lib/rubocop/cop/style/map_to_hash.rb +68 -0
  73. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -0
  74. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -1
  75. data/lib/rubocop/cop/style/method_def_parentheses.rb +17 -13
  76. data/lib/rubocop/cop/style/numeric_literals.rb +10 -1
  77. data/lib/rubocop/cop/style/one_line_conditional.rb +18 -39
  78. data/lib/rubocop/cop/style/open_struct_use.rb +69 -0
  79. data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -2
  80. data/lib/rubocop/cop/style/quoted_symbols.rb +11 -1
  81. data/lib/rubocop/cop/style/redundant_interpolation.rb +17 -3
  82. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +5 -1
  83. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  84. data/lib/rubocop/cop/style/safe_navigation.rb +1 -5
  85. data/lib/rubocop/cop/style/sample.rb +5 -3
  86. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  87. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  88. data/lib/rubocop/cop/style/ternary_parentheses.rb +16 -2
  89. data/lib/rubocop/cop/team.rb +1 -1
  90. data/lib/rubocop/cop/util.rb +9 -1
  91. data/lib/rubocop/formatter/html_formatter.rb +5 -2
  92. data/lib/rubocop/formatter/json_formatter.rb +4 -1
  93. data/lib/rubocop/options.rb +6 -1
  94. data/lib/rubocop/remote_config.rb +2 -4
  95. data/lib/rubocop/result_cache.rb +1 -1
  96. data/lib/rubocop/version.rb +1 -1
  97. data/lib/rubocop/yaml_duplication_checker.rb +1 -1
  98. data/lib/rubocop.rb +11 -0
  99. metadata +20 -7
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop looks for `ruby2_keywords` calls for methods that do not need it.
7
+ #
8
+ # `ruby2_keywords` should only be called on methods that accept an argument splat
9
+ # (`*args`) but do not explicit keyword arguments (`k:` or `k: true`) or
10
+ # a keyword splat (`**kwargs`).
11
+ #
12
+ # @example
13
+ # # good (splat argument without keyword arguments)
14
+ # ruby2_keywords def foo(*args); end
15
+ #
16
+ # # bad (no arguments)
17
+ # ruby2_keywords def foo; end
18
+ #
19
+ # # good
20
+ # def foo; end
21
+ #
22
+ # # bad (positional argument)
23
+ # ruby2_keywords def foo(arg); end
24
+ #
25
+ # # good
26
+ # def foo(arg); end
27
+ #
28
+ # # bad (double splatted argument)
29
+ # ruby2_keywords def foo(**args); end
30
+ #
31
+ # # good
32
+ # def foo(**args); end
33
+ #
34
+ # # bad (keyword arguments)
35
+ # ruby2_keywords def foo(i:, j:); end
36
+ #
37
+ # # good
38
+ # def foo(i:, j:); end
39
+ #
40
+ # # bad (splat argument with keyword arguments)
41
+ # ruby2_keywords def foo(*args, i:, j:); end
42
+ #
43
+ # # good
44
+ # def foo(*args, i:, j:); end
45
+ #
46
+ # # bad (splat argument with double splat)
47
+ # ruby2_keywords def foo(*args, **kwargs); end
48
+ #
49
+ # # good
50
+ # def foo(*args, **kwargs); end
51
+ #
52
+ # # bad (ruby2_keywords given a symbol)
53
+ # def foo; end
54
+ # ruby2_keywords :foo
55
+ #
56
+ # # good
57
+ # def foo; end
58
+ #
59
+ # # bad (ruby2_keywords with dynamic method)
60
+ # define_method(:foo) { |arg| }
61
+ # ruby2_keywords :foo
62
+ #
63
+ # # good
64
+ # define_method(:foo) { |arg| }
65
+ #
66
+ class UselessRuby2Keywords < Base
67
+ MSG = '`ruby2_keywords` is unnecessary for method `%<method_name>s`.'
68
+ RESTRICT_ON_SEND = %i[ruby2_keywords].freeze
69
+
70
+ # Looks for statically or dynamically defined methods with a given name
71
+ # @!method method_definition(node, method_name)
72
+ def_node_matcher :method_definition, <<~PATTERN
73
+ {
74
+ (def %1 ...)
75
+ ({block numblock} (send _ :define_method (sym %1)) ...)
76
+ }
77
+ PATTERN
78
+
79
+ def on_send(node)
80
+ if node.first_argument.def_type?
81
+ inspect_def(node, node.first_argument)
82
+ elsif node.first_argument.sym_type?
83
+ inspect_sym(node, node.first_argument)
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def inspect_def(node, def_node)
90
+ return if allowed_arguments(def_node.arguments)
91
+
92
+ add_offense(node.loc.selector, message: format(MSG, method_name: def_node.method_name))
93
+ end
94
+
95
+ def inspect_sym(node, sym_node)
96
+ return unless node.parent
97
+
98
+ method_name = sym_node.value
99
+ definition = node.parent.each_child_node.detect { |n| method_definition(n, method_name) }
100
+
101
+ return unless definition
102
+ return if allowed_arguments(definition.arguments)
103
+
104
+ add_offense(node, message: format(MSG, method_name: method_name))
105
+ end
106
+
107
+ # `ruby2_keywords` is only allowed if there's a `restarg` and no keyword arguments
108
+ def allowed_arguments(arguments)
109
+ return false if arguments.empty?
110
+
111
+ arguments.each_child_node(:restarg).any? &&
112
+ arguments.each_child_node(:kwarg, :kwoptarg, :kwrestarg).none?
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -50,6 +50,7 @@ module RuboCop
50
50
 
51
51
  check_code_length(node)
52
52
  end
53
+ alias on_numblock on_block
53
54
 
54
55
  private
55
56
 
@@ -46,15 +46,6 @@ module RuboCop
46
46
  1
47
47
  end
48
48
 
49
- def block_method(node)
50
- case node.type
51
- when :block
52
- node.method_name
53
- when :block_pass
54
- node.parent.method_name
55
- end
56
- end
57
-
58
49
  def count_block?(block)
59
50
  KNOWN_ITERATING_METHODS.include? block.method_name
60
51
  end
@@ -52,6 +52,7 @@ module RuboCop
52
52
 
53
53
  check_code_length(node)
54
54
  end
55
+ alias on_numblock on_block
55
56
 
56
57
  private
57
58
 
@@ -44,7 +44,7 @@ module RuboCop
44
44
 
45
45
  # @!method module_definition?(node)
46
46
  def_node_matcher :module_definition?, <<~PATTERN
47
- (casgn nil? _ (block (send (const {nil? cbase} :Module) :new) ...))
47
+ (casgn nil? _ ({block numblock} (send (const {nil? cbase} :Module) :new) ...))
48
48
  PATTERN
49
49
 
50
50
  def message(length, max_length)
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # as they add less complexity than positional or optional parameters.
11
11
  #
12
12
  # NOTE: Explicit block argument `&block` is not counted to prevent
13
- # erroneous change that is avoided by making block argument implicit.
13
+ # erroneous change that is avoided by making block argument implicit.
14
14
  #
15
15
  # @example Max: 3
16
16
  # # good
@@ -135,7 +135,7 @@ module RuboCop
135
135
 
136
136
  def extract_body(node)
137
137
  case node.type
138
- when :class, :module, :block, :def, :defs
138
+ when :class, :module, :block, :numblock, :def, :defs
139
139
  node.body
140
140
  when :casgn
141
141
  _scope, _name, value = *node
@@ -59,7 +59,7 @@ module RuboCop
59
59
 
60
60
  # @!method attribute_call?(node)
61
61
  def_node_matcher :attribute_call?, <<~PATTERN
62
- ( {csend send} _receiver _method # and no parameters
62
+ (call _receiver _method # and no parameters
63
63
  )
64
64
  PATTERN
65
65
 
@@ -13,6 +13,11 @@ module RuboCop
13
13
  # @api private
14
14
  module EnforceSuperclass
15
15
  def self.included(base)
16
+ warn Rainbow(
17
+ '`RuboCop::Cop::EnforceSuperclass` is deprecated and will be removed in RuboCop 2.0. ' \
18
+ 'Please upgrade to RuboCop Rails 2.9 or newer to continue.'
19
+ ).yellow
20
+
16
21
  # @!method class_definition(node)
17
22
  base.def_node_matcher :class_definition, <<~PATTERN
18
23
  (class (const _ !:#{base::SUPERCLASS}) #{base::BASE_PATTERN} ...)
@@ -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
@@ -43,7 +43,7 @@ module RuboCop
43
43
  end
44
44
 
45
45
  def value_delta(pair)
46
- return 0 if pair.value_on_new_line?
46
+ return 0 if pair.value_on_new_line? || pair.value_omission?
47
47
 
48
48
  correct_value_column = pair.loc.operator.end.column + 1
49
49
  actual_value_column = pair.value.loc.column
@@ -111,7 +111,8 @@ module RuboCop
111
111
  correct_value_column = first_pair.key.loc.column +
112
112
  current_pair.delimiter(true).length +
113
113
  max_key_width
114
- correct_value_column - current_pair.value.loc.column
114
+
115
+ current_pair.value_omission? ? 0 : correct_value_column - current_pair.value.loc.column
115
116
  end
116
117
  end
117
118
 
@@ -134,7 +135,7 @@ module RuboCop
134
135
  end
135
136
 
136
137
  def value_delta(first_pair, current_pair)
137
- first_pair.value_delta(current_pair)
138
+ current_pair.value_omission? ? 0 : first_pair.value_delta(current_pair)
138
139
  end
139
140
  end
140
141
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This module checks for Ruby 3.1's hash value omission syntax.
6
+ module HashShorthandSyntax
7
+ OMIT_HASH_VALUE_MSG = 'Omit the hash value.'
8
+ EXPLICIT_HASH_VALUE_MSG = 'Explicit the hash value.'
9
+
10
+ def on_pair(node)
11
+ return if target_ruby_version <= 3.0 || enforced_shorthand_syntax == 'either'
12
+
13
+ hash_key_source = node.key.source
14
+
15
+ if enforced_shorthand_syntax == 'always'
16
+ return if node.value_omission? || require_hash_value?(hash_key_source, node)
17
+
18
+ message = OMIT_HASH_VALUE_MSG
19
+ replacement = "#{hash_key_source}:"
20
+ else
21
+ return unless node.value_omission?
22
+
23
+ message = EXPLICIT_HASH_VALUE_MSG
24
+ replacement = "#{hash_key_source}: #{hash_key_source}"
25
+ end
26
+
27
+ add_offense(node.value, message: message) do |corrector|
28
+ corrector.replace(node, replacement)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def enforced_shorthand_syntax
35
+ cop_config.fetch('EnforcedShorthandSyntax', 'always')
36
+ end
37
+
38
+ def require_hash_value?(hash_key_source, node)
39
+ return true if without_parentheses_call_expr_follows?(node)
40
+
41
+ hash_value = node.value
42
+ return true unless hash_value.send_type? || hash_value.lvar_type?
43
+
44
+ hash_key_source != hash_value.source || hash_key_source.end_with?('!', '?')
45
+ end
46
+
47
+ def without_parentheses_call_expr_follows?(node)
48
+ return false unless (ancestor = node.parent.parent)
49
+
50
+ right_sibling = ancestor.right_sibling
51
+
52
+ return true if right_sibling.nil? && without_parentheses?(ancestor)
53
+ return false unless right_sibling
54
+ return true if node_with_block_and_arguments?(right_sibling)
55
+
56
+ without_parentheses?(ancestor) && without_parentheses?(right_sibling)
57
+ end
58
+
59
+ def without_parentheses?(node)
60
+ node.respond_to?(:parenthesized?) && !node.parenthesized?
61
+ end
62
+
63
+ def node_with_block_and_arguments?(node)
64
+ node.respond_to?(:block_type?) && node.block_type? && node.children&.first&.arguments?
65
+ end
66
+ end
67
+ end
68
+ end
@@ -13,11 +13,7 @@ module RuboCop
13
13
  if style == :single_quotes
14
14
  !double_quotes_required?(src)
15
15
  else
16
- # The string needs single quotes if:
17
- # 1. It contains a double quote
18
- # 2. It contains text that would become an escape sequence with double quotes
19
- # 3. It contains text that would become an interpolation with double quotes
20
- !/" | (?<!\\)\\[aAbcdefkMnprsStuUxzZ0-7] | \#[@{$]/x.match?(src)
16
+ !/" | \\[^'\\] | \#[@{$]/x.match?(src)
21
17
  end
22
18
  end
23
19
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Naming
6
+ # In Ruby 3.1, anonymous block forwarding has been added.
7
+ #
8
+ # This cop identifies places where `do_something(&block)` can be replaced
9
+ # by `do_something(&)`.
10
+ #
11
+ # It also supports the opposite style by alternative `explicit` option.
12
+ # You can specify the block variable name for auto-correction with `BlockForwardingName`.
13
+ # The default variable name is `block`. If the name is already in use, it will not be
14
+ # auto-corrected.
15
+ #
16
+ # @example EnforcedStyle: anonymous (default)
17
+ #
18
+ # # bad
19
+ # def foo(&block)
20
+ # bar(&block)
21
+ # end
22
+ #
23
+ # # good
24
+ # def foo(&)
25
+ # bar(&)
26
+ # end
27
+ #
28
+ # @example EnforcedStyle: explicit
29
+ #
30
+ # # bad
31
+ # def foo(&)
32
+ # bar(&)
33
+ # end
34
+ #
35
+ # # good
36
+ # def foo(&block)
37
+ # bar(&block)
38
+ # end
39
+ #
40
+ class BlockForwarding < Base
41
+ include ConfigurableEnforcedStyle
42
+ include RangeHelp
43
+ extend AutoCorrector
44
+ extend TargetRubyVersion
45
+
46
+ minimum_target_ruby_version 3.1
47
+
48
+ MSG = 'Use %<style>s block forwarding.'
49
+
50
+ def on_def(node)
51
+ return if node.arguments.empty?
52
+
53
+ last_argument = node.arguments.last
54
+ return if expected_block_forwarding_style?(node, last_argument)
55
+
56
+ register_offense(last_argument, node)
57
+
58
+ node.each_descendant(:block_pass) do |block_pass_node|
59
+ next if block_pass_node.children.first&.sym_type? ||
60
+ last_argument.source != block_pass_node.source
61
+
62
+ register_offense(block_pass_node, node)
63
+ end
64
+ end
65
+ alias on_defs on_def
66
+
67
+ private
68
+
69
+ def expected_block_forwarding_style?(node, last_argument)
70
+ if style == :anonymous
71
+ !explicit_block_argument?(last_argument) ||
72
+ use_kwarg_in_method_definition?(node) ||
73
+ use_block_argument_as_local_variable?(node, last_argument.source[1..-1])
74
+ else
75
+ !anonymous_block_argument?(last_argument)
76
+ end
77
+ end
78
+
79
+ def use_kwarg_in_method_definition?(node)
80
+ node.arguments.each_descendant(:kwarg, :kwoptarg).any?
81
+ end
82
+
83
+ def anonymous_block_argument?(node)
84
+ node.blockarg_type? && node.name.nil?
85
+ end
86
+
87
+ def explicit_block_argument?(node)
88
+ node.blockarg_type? && !node.name.nil?
89
+ end
90
+
91
+ def register_offense(block_argument, node)
92
+ add_offense(block_argument, message: format(MSG, style: style)) do |corrector|
93
+ if style == :anonymous
94
+ corrector.replace(block_argument, '&')
95
+
96
+ arguments = block_argument.parent
97
+
98
+ add_parentheses(arguments, corrector) unless arguments.parenthesized_call?
99
+ else
100
+ unless use_block_argument_as_local_variable?(node, block_forwarding_name)
101
+ corrector.replace(block_argument, "&#{block_forwarding_name}")
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def use_block_argument_as_local_variable?(node, last_argument)
108
+ return if node.body.nil?
109
+
110
+ node.body.each_descendant(:lvar).any? do |lvar|
111
+ !lvar.parent.block_pass_type? && lvar.source == last_argument
112
+ end
113
+ end
114
+
115
+ def block_forwarding_name
116
+ cop_config.fetch('BlockForwardingName', 'block')
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -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
@@ -3,7 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Security
6
- # This cop checks for the use of `Kernel#open` and `URI.open`.
6
+ # This cop checks for the use of `Kernel#open` and `URI.open` with dynamic
7
+ # data.
7
8
  #
8
9
  # `Kernel#open` and `URI.open` enable not only file access but also process
9
10
  # invocation by prefixing a pipe symbol (e.g., `open("| ls")`).
@@ -11,6 +12,9 @@ module RuboCop
11
12
  # the argument of `Kernel#open` and `URI.open`. It would be better to use
12
13
  # `File.open`, `IO.popen` or `URI.parse#open` explicitly.
13
14
  #
15
+ # NOTE: `open` and `URI.open` with literal strings are not flagged by this
16
+ # cop.
17
+ #
14
18
  # @safety
15
19
  # This cop could register false positives if `open` is redefined
16
20
  # in a class and then used without a receiver in that class.
@@ -18,12 +22,18 @@ module RuboCop
18
22
  # @example
19
23
  # # bad
20
24
  # open(something)
25
+ # open("| #{something}")
21
26
  # URI.open(something)
22
27
  #
23
28
  # # good
24
29
  # File.open(something)
25
30
  # IO.popen(something)
26
31
  # URI.parse(something).open
32
+ #
33
+ # # good (literal strings)
34
+ # open("foo.text")
35
+ # open("| foo")
36
+ # URI.open("http://example.com")
27
37
  class Open < Base
28
38
  MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
29
39
  RESTRICT_ON_SEND = %i[open].freeze
@@ -4,6 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for uses of the character literal ?x.
7
+ # Starting with Ruby 1.9 character literals are
8
+ # essentially one-character strings, so this syntax
9
+ # is mostly redundant at this point.
10
+ #
11
+ # ? character literal can be used to express meta and control character.
12
+ # That's a good use case of ? literal so it doesn't count it as an offense.
7
13
  #
8
14
  # @example
9
15
  # # bad
@@ -12,8 +18,9 @@ module RuboCop
12
18
  # # good
13
19
  # 'x'
14
20
  #
15
- # # good
21
+ # # good - control & meta escapes
16
22
  # ?\C-\M-d
23
+ # "\C-\M-d" # same as above
17
24
  class CharacterLiteral < Base
18
25
  include StringHelp
19
26
  extend AutoCorrector