rubocop 0.91.0 → 1.0.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +100 -54
  4. data/lib/rubocop.rb +10 -6
  5. data/lib/rubocop/cached_data.rb +2 -1
  6. data/lib/rubocop/cli/command/version.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +9 -5
  8. data/lib/rubocop/config.rb +4 -0
  9. data/lib/rubocop/config_loader.rb +19 -2
  10. data/lib/rubocop/config_loader_resolver.rb +7 -5
  11. data/lib/rubocop/config_regeneration.rb +33 -0
  12. data/lib/rubocop/config_validator.rb +7 -6
  13. data/lib/rubocop/cop/badge.rb +9 -24
  14. data/lib/rubocop/cop/base.rb +16 -1
  15. data/lib/rubocop/cop/commissioner.rb +34 -20
  16. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  17. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
  19. data/lib/rubocop/cop/layout/array_alignment.rb +1 -0
  20. data/lib/rubocop/cop/layout/case_indentation.rb +4 -7
  21. data/lib/rubocop/cop/layout/class_structure.rb +8 -1
  22. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  23. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +2 -2
  24. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +4 -13
  25. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +13 -8
  26. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -2
  27. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -2
  28. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +10 -1
  29. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +4 -13
  30. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  31. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -7
  32. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  33. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -18
  34. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -2
  35. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +2 -2
  36. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  37. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  38. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  39. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  40. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +23 -3
  41. data/lib/rubocop/cop/lint/duplicate_rescue_exception.rb +2 -4
  42. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  43. data/lib/rubocop/cop/lint/identity_comparison.rb +5 -3
  44. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +2 -5
  45. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +22 -12
  48. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +14 -4
  49. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  50. data/lib/rubocop/cop/lint/rescue_type.rb +0 -1
  51. data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -6
  52. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  53. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -5
  54. data/lib/rubocop/cop/lint/useless_access_modifier.rb +3 -9
  55. data/lib/rubocop/cop/lint/useless_times.rb +11 -2
  56. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  57. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  58. data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
  59. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +25 -16
  60. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
  61. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  62. data/lib/rubocop/cop/mixin/rescue_node.rb +1 -0
  63. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -3
  64. data/lib/rubocop/cop/mixin/visibility_help.rb +4 -16
  65. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  66. data/lib/rubocop/cop/offense.rb +15 -2
  67. data/lib/rubocop/cop/security/open.rb +12 -10
  68. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  69. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  70. data/lib/rubocop/cop/style/array_coercion.rb +4 -0
  71. data/lib/rubocop/cop/style/case_like_if.rb +20 -4
  72. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  73. data/lib/rubocop/cop/style/combinable_loops.rb +13 -11
  74. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  75. data/lib/rubocop/cop/style/commented_keyword.rb +7 -8
  76. data/lib/rubocop/cop/style/date_time.rb +12 -1
  77. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  78. data/lib/rubocop/cop/style/for.rb +0 -4
  79. data/lib/rubocop/cop/style/format_string_token.rb +48 -3
  80. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +15 -6
  81. data/lib/rubocop/cop/style/if_unless_modifier.rb +0 -4
  82. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -6
  83. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  84. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
  85. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  86. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  87. data/lib/rubocop/cop/style/mixin_usage.rb +7 -27
  88. data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -2
  89. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -0
  90. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  91. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -1
  92. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +12 -1
  93. data/lib/rubocop/cop/style/raise_args.rb +0 -3
  94. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  95. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  97. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  98. data/lib/rubocop/cop/style/redundant_conditional.rb +4 -5
  99. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  100. data/lib/rubocop/cop/style/redundant_parentheses.rb +6 -3
  101. data/lib/rubocop/cop/style/redundant_percent_q.rb +9 -11
  102. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +39 -24
  103. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  104. data/lib/rubocop/cop/style/redundant_return.rb +17 -17
  105. data/lib/rubocop/cop/style/redundant_self.rb +10 -9
  106. data/lib/rubocop/cop/style/redundant_sort.rb +13 -24
  107. data/lib/rubocop/cop/style/redundant_sort_by.rb +5 -9
  108. data/lib/rubocop/cop/style/rescue_standard_error.rb +20 -16
  109. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  110. data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
  111. data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -3
  112. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +4 -3
  113. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  114. data/lib/rubocop/cop/util.rb +0 -1
  115. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  116. data/lib/rubocop/directive_comment.rb +32 -0
  117. data/lib/rubocop/ext/regexp_node.rb +20 -4
  118. data/lib/rubocop/formatter/disabled_config_formatter.rb +12 -5
  119. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  120. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  121. data/lib/rubocop/options.rb +22 -17
  122. data/lib/rubocop/result_cache.rb +8 -2
  123. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  124. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  125. data/lib/rubocop/runner.rb +9 -5
  126. data/lib/rubocop/target_finder.rb +27 -26
  127. data/lib/rubocop/target_ruby.rb +1 -1
  128. data/lib/rubocop/version.rb +61 -6
  129. metadata +14 -17
  130. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
  131. data/lib/rubocop/cop/tokens_util.rb +0 -84
@@ -35,7 +35,6 @@ module RuboCop
35
35
  # baz
36
36
  # end
37
37
  class RescueType < Base
38
- include RescueNode
39
38
  extend AutoCorrector
40
39
 
41
40
  MSG = 'Rescuing from `%<invalid_exceptions>s` will raise a ' \
@@ -75,8 +75,7 @@ module RuboCop
75
75
 
76
76
  def rescued_groups_for(rescues)
77
77
  rescues.map do |group|
78
- rescue_group, = *group
79
- evaluate_exceptions(rescue_group)
78
+ evaluate_exceptions(group)
80
79
  end
81
80
  end
82
81
 
@@ -117,14 +116,15 @@ module RuboCop
117
116
  $VERBOSE = old_verbose
118
117
  end
119
118
 
120
- def evaluate_exceptions(rescue_group)
121
- if rescue_group
122
- rescued_exceptions = rescued_exceptions(rescue_group)
119
+ def evaluate_exceptions(group)
120
+ rescued_exceptions = group.exceptions
121
+
122
+ if rescued_exceptions.any?
123
123
  rescued_exceptions.each_with_object([]) do |exception, converted|
124
124
  begin
125
125
  silence_warnings do
126
126
  # Avoid printing deprecation warnings about constants
127
- converted << Kernel.const_get(exception)
127
+ converted << Kernel.const_get(exception.source)
128
128
  end
129
129
  rescue NameError
130
130
  converted << nil
@@ -31,7 +31,7 @@ module RuboCop
31
31
  class ToJSON < Base
32
32
  extend AutoCorrector
33
33
 
34
- MSG = ' `#to_json` requires an optional argument to be parsable ' \
34
+ MSG = '`#to_json` requires an optional argument to be parsable ' \
35
35
  'via JSON.generate(obj).'
36
36
 
37
37
  def on_def(node)
@@ -159,16 +159,12 @@ module RuboCop
159
159
  end
160
160
 
161
161
  def preceded_by_continue_statement?(break_statement)
162
- left_siblings_of(break_statement).any? do |sibling|
162
+ break_statement.left_siblings.any? do |sibling|
163
163
  next if sibling.loop_keyword? || loop_method?(sibling)
164
164
 
165
165
  sibling.each_descendant(:next, :redo).any?
166
166
  end
167
167
  end
168
-
169
- def left_siblings_of(node)
170
- node.parent.children[0, node.sibling_index]
171
- end
172
168
  end
173
169
  end
174
170
  end
@@ -131,12 +131,10 @@ module RuboCop
131
131
  MSG = 'Useless `%<current>s` access modifier.'
132
132
 
133
133
  def on_class(node)
134
- check_node(node.children[2]) # class body
135
- end
136
-
137
- def on_module(node)
138
- check_node(node.children[1]) # module body
134
+ check_node(node.body)
139
135
  end
136
+ alias on_module on_class
137
+ alias on_sclass on_class
140
138
 
141
139
  def on_block(node)
142
140
  return unless eval_call?(node)
@@ -144,10 +142,6 @@ module RuboCop
144
142
  check_node(node.body)
145
143
  end
146
144
 
147
- def on_sclass(node)
148
- check_node(node.children[1]) # singleton class body
149
- end
150
-
151
145
  private
152
146
 
153
147
  def autocorrect(corrector, node)
@@ -49,7 +49,7 @@ module RuboCop
49
49
  add_offense(node, message: format(MSG, count: count)) do |corrector|
50
50
  next unless own_line?(node)
51
51
 
52
- if count < 1
52
+ if never_process?(count, node)
53
53
  remove_node(corrector, node)
54
54
  elsif !proc_name.empty?
55
55
  autocorrect_block_pass(corrector, node, proc_name)
@@ -61,6 +61,10 @@ module RuboCop
61
61
 
62
62
  private
63
63
 
64
+ def never_process?(count, node)
65
+ count < 1 || node.block_type? && node.body.nil?
66
+ end
67
+
64
68
  def remove_node(corrector, node)
65
69
  corrector.remove(range_by_whole_lines(node.loc.expression, include_final_newline: true))
66
70
  end
@@ -82,7 +86,12 @@ module RuboCop
82
86
  def fix_indentation(source, range)
83
87
  # Cleanup indentation in a multiline block
84
88
  source_lines = source.split("\n")
85
- source_lines[1..-1].each { |line| line[range] = '' }
89
+
90
+ source_lines[1..-1].each do |line|
91
+ next if line.empty?
92
+
93
+ line[range] = ''
94
+ end
86
95
  source_lines.join("\n")
87
96
  end
88
97
 
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # content.
30
30
  # HEREDOC
31
31
  # end # 5 points
32
+ #
33
+ # NOTE: This cop does not apply for `Struct` definitions.
32
34
  class BlockLength < Base
33
35
  include CodeLength
34
36
 
@@ -36,7 +38,7 @@ module RuboCop
36
38
 
37
39
  def on_block(node)
38
40
  return if excluded_method?(node)
39
- return if node.class_constructor?
41
+ return if node.class_constructor? || node.struct_constructor?
40
42
 
41
43
  check_code_length(node)
42
44
  end
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # HEREDOC
30
30
  # end # 5 points
31
31
  #
32
+ #
33
+ # NOTE: This cop also applies for `Struct` definitions.
32
34
  class ClassLength < Base
33
35
  include CodeLength
34
36
 
@@ -37,17 +39,23 @@ module RuboCop
37
39
  end
38
40
 
39
41
  def on_casgn(node)
40
- class_definition?(node) do
41
- check_code_length(node)
42
+ parent = node.parent
43
+
44
+ if parent&.assignment?
45
+ block_node = parent.children[1]
46
+ elsif parent&.parent&.masgn_type?
47
+ block_node = parent.parent.children[1]
48
+ else
49
+ _scope, _name, block_node = *node
42
50
  end
51
+
52
+ return unless block_node.respond_to?(:class_definition?) && block_node.class_definition?
53
+
54
+ check_code_length(block_node)
43
55
  end
44
56
 
45
57
  private
46
58
 
47
- def_node_matcher :class_definition?, <<~PATTERN
48
- (casgn nil? _ (block (send (const {nil? cbase} :Class) :new) ...))
49
- PATTERN
50
-
51
59
  def message(length, max_length)
52
60
  format('Class has too many lines. [%<length>d/%<max>d]',
53
61
  length: length,
@@ -12,6 +12,9 @@ module RuboCop
12
12
  MSG = 'Avoid parameter lists longer than %<max>d parameters. ' \
13
13
  '[%<count>d/%<max>d]'
14
14
 
15
+ NAMED_KEYWORD_TYPES = %i[kwoptarg kwarg].freeze
16
+ private_constant :NAMED_KEYWORD_TYPES
17
+
15
18
  def on_args(node)
16
19
  count = args_count(node)
17
20
  return unless count > max_params
@@ -33,7 +36,7 @@ module RuboCop
33
36
  if count_keyword_args?
34
37
  node.children.size
35
38
  else
36
- node.children.count { |a| !%i[kwoptarg kwarg].include?(a.type) }
39
+ node.children.count { |a| !NAMED_KEYWORD_TYPES.include?(a.type) }
37
40
  end
38
41
  end
39
42
 
@@ -81,40 +81,49 @@ module RuboCop
81
81
  private
82
82
 
83
83
  def assignment?(node)
84
+ return compound_assignment(node) if node.masgn_type? || node.shorthand_asgn?
85
+
84
86
  node.for_type? ||
85
- node.op_asgn_type? ||
86
87
  (node.respond_to?(:setter_method?) && node.setter_method?) ||
87
- (simple_assignment?(node) && capturing_variable?(node.children.first))
88
+ simple_assignment?(node) ||
89
+ argument?(node)
88
90
  end
89
91
 
90
- def simple_assignment?(node)
91
- return false if node.masgn_type?
92
+ def compound_assignment(node)
93
+ # Methods setter can not be detected for multiple assignments
94
+ # and shorthand assigns, so we'll count them here instead
95
+ children = node.masgn_type? ? node.children[0].children : node.children
92
96
 
93
- if node.equals_asgn?
94
- reset_on_lvasgn(node) if node.lvasgn_type?
95
- return true
97
+ will_be_miscounted = children.count do |child|
98
+ child.respond_to?(:setter_method?) &&
99
+ !child.setter_method?
96
100
  end
101
+ @assignment += will_be_miscounted
97
102
 
98
- argument?(node)
103
+ false
99
104
  end
100
105
 
101
- def capturing_variable?(name)
102
- name && !/^_/.match?(name)
106
+ def simple_assignment?(node)
107
+ if !node.equals_asgn?
108
+ false
109
+ elsif node.lvasgn_type?
110
+ reset_on_lvasgn(node)
111
+ capturing_variable?(node.children.first)
112
+ else
113
+ true
114
+ end
103
115
  end
104
116
 
105
- # Returns true for nodes which otherwise would be counted
106
- # as one too many assignment
107
- def assignment_doubled_in_ast?(node)
108
- node.masgn_type? || node.or_asgn_type? || node.and_asgn_type?
117
+ def capturing_variable?(name)
118
+ name && !/^_/.match?(name)
109
119
  end
110
120
 
111
121
  def branch?(node)
112
122
  BRANCH_NODES.include?(node.type)
113
123
  end
114
124
 
115
- # TODO: move to rubocop-ast
116
125
  def argument?(node)
117
- ARGUMENT_TYPES.include?(node.type)
126
+ ARGUMENT_TYPES.include?(node.type) && capturing_variable?(node.children.first)
118
127
  end
119
128
 
120
129
  def condition?(node)
@@ -8,9 +8,9 @@ module RuboCop
8
8
  include ConfigurableFormatting
9
9
 
10
10
  FORMATS = {
11
- snake_case: /(?:[a-z_]|_\d+)$/,
12
- normalcase: /(?:_\D*|[A-Za-z]\d*)$/,
13
- non_integer: /[A-Za-z_]$/
11
+ snake_case: /(?:[[[:lower:]]_]|_\d+)$/,
12
+ normalcase: /(?:_\D*|[[[:upper:]][[:lower:]]]\d*)$/,
13
+ non_integer: /[[[:upper:]][[:lower:]]_]$/
14
14
  }.freeze
15
15
  end
16
16
  end
@@ -137,7 +137,7 @@ module RuboCop
137
137
  end
138
138
 
139
139
  # Internal helper class to hold autocorrect data
140
- Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do # rubocop:disable Metrics/BlockLength
140
+ Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do
141
141
  def self.from_each_with_object(node, match)
142
142
  new(match, node, 0, 0)
143
143
  end
@@ -18,6 +18,7 @@ module RuboCop
18
18
  @modifier_locations.include?(node.loc.keyword)
19
19
  end
20
20
 
21
+ # @deprecated Use ResbodyNode#exceptions instead
21
22
  def rescued_exceptions(resbody)
22
23
  rescue_group, = *resbody
23
24
  if rescue_group
@@ -57,10 +57,11 @@ module RuboCop
57
57
  end
58
58
 
59
59
  def first_line_comment(node)
60
- comment =
61
- processed_source.find_comment { |c| c.loc.line == node.loc.line }
60
+ comment = processed_source.find_comment { |c| c.loc.line == node.loc.line }
61
+ return unless comment
62
62
 
63
- comment ? comment.loc.expression.source : nil
63
+ comment_source = comment.loc.expression.source
64
+ comment_source unless comment_disables_cop?(comment_source)
64
65
  end
65
66
 
66
67
  def parenthesize?(node)
@@ -80,6 +81,11 @@ module RuboCop
80
81
 
81
82
  config.for_cop('Layout/LineLength')['Max']
82
83
  end
84
+
85
+ def comment_disables_cop?(comment)
86
+ regexp_pattern = "# rubocop : (disable|todo) ([^,],)* (all|#{cop_name})"
87
+ Regexp.new(regexp_pattern.gsub(' ', '\s*')).match?(comment)
88
+ end
83
89
  end
84
90
  end
85
91
  end
@@ -16,32 +16,20 @@ module RuboCop
16
16
  end
17
17
 
18
18
  def find_visibility_start(node)
19
- left_siblings_of(node)
20
- .reverse
21
- .find(&method(:visibility_block?))
19
+ node.left_siblings
20
+ .reverse
21
+ .find(&method(:visibility_block?))
22
22
  end
23
23
 
24
24
  # Navigate to find the last protected method
25
25
  def find_visibility_end(node)
26
26
  possible_visibilities = VISIBILITY_SCOPES - [node_visibility(node)]
27
- right = right_siblings_of(node)
27
+ right = node.right_siblings
28
28
  right.find do |child_node|
29
29
  possible_visibilities.include?(node_visibility(child_node))
30
30
  end || right.last
31
31
  end
32
32
 
33
- def left_siblings_of(node)
34
- siblings_of(node)[0, node.sibling_index]
35
- end
36
-
37
- def right_siblings_of(node)
38
- siblings_of(node)[node.sibling_index..-1]
39
- end
40
-
41
- def siblings_of(node)
42
- node.parent.children
43
- end
44
-
45
33
  def_node_matcher :visibility_block?, <<~PATTERN
46
34
  (send nil? { :private :protected :public })
47
35
  PATTERN
@@ -18,7 +18,7 @@ module RuboCop
18
18
  'name its argument `other`.'
19
19
 
20
20
  OP_LIKE_METHODS = %i[eql? equal?].freeze
21
- EXCLUDED = %i[+@ -@ [] []= << === `].freeze
21
+ EXCLUDED = %i[+@ -@ [] []= << === ` =~].freeze
22
22
 
23
23
  def_node_matcher :op_method_candidate?, <<~PATTERN
24
24
  (def [#op_method? $_] (args $(arg [!:other !:_other])) _)
@@ -63,10 +63,23 @@ module RuboCop
63
63
  attr_reader :corrector
64
64
 
65
65
  PseudoSourceRange = Struct.new(:line, :column, :source_line, :begin_pos,
66
- :end_pos)
66
+ :end_pos) do
67
+ alias_method :first_line, :line
68
+ alias_method :last_line, :line
69
+ alias_method :last_column, :column
70
+
71
+ def column_range
72
+ column...last_column
73
+ end
74
+
75
+ def size
76
+ end_pos - begin_pos
77
+ end
78
+ alias_method :length, :size
79
+ end
67
80
  private_constant :PseudoSourceRange
68
81
 
69
- NO_LOCATION = PseudoSourceRange.new(1, 0, '', 0, 1).freeze
82
+ NO_LOCATION = PseudoSourceRange.new(1, 0, '', 0, 0).freeze
70
83
 
71
84
  # @api private
72
85
  def initialize(severity, location, message, cop_name, # rubocop:disable Metrics/ParameterLists
@@ -3,35 +3,37 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Security
6
- # This cop checks for the use of `Kernel#open`.
6
+ # This cop checks for the use of `Kernel#open` and `URI.open`.
7
7
  #
8
- # `Kernel#open` enables not only file access but also process invocation
9
- # by prefixing a pipe symbol (e.g., `open("| ls")`). So, it may lead to
10
- # a serious security risk by using variable input to the argument of
11
- # `Kernel#open`. It would be better to use `File.open`, `IO.popen` or
12
- # `URI#open` explicitly.
8
+ # `Kernel#open` and `URI.open` enable not only file access but also process
9
+ # invocation by prefixing a pipe symbol (e.g., `open("| ls")`).
10
+ # So, it may lead to a serious security risk by using variable input to
11
+ # the argument of `Kernel#open` and `URI.open`. It would be better to use
12
+ # `File.open`, `IO.popen` or `URI.parse#open` explicitly.
13
13
  #
14
14
  # @example
15
15
  # # bad
16
16
  # open(something)
17
+ # URI.open(something)
17
18
  #
18
19
  # # good
19
20
  # File.open(something)
20
21
  # IO.popen(something)
21
22
  # URI.parse(something).open
22
23
  class Open < Base
23
- MSG = 'The use of `Kernel#open` is a serious security risk.'
24
+ MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
24
25
  RESTRICT_ON_SEND = %i[open].freeze
25
26
 
26
27
  def_node_matcher :open?, <<~PATTERN
27
- (send nil? :open $!str ...)
28
+ (send ${nil? (const {nil? cbase} :URI)} :open $!str ...)
28
29
  PATTERN
29
30
 
30
31
  def on_send(node)
31
- open?(node) do |code|
32
+ open?(node) do |receiver, code|
32
33
  return if safe?(code)
33
34
 
34
- add_offense(node.loc.selector)
35
+ message = format(MSG, receiver: receiver ? "#{receiver.source}." : 'Kernel#')
36
+ add_offense(node.loc.selector, message: message)
35
37
  end
36
38
  end
37
39