rubocop 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +35 -1
  4. data/CONTRIBUTING.md +1 -1
  5. data/README.md +6 -6
  6. data/config/default.yml +25 -6
  7. data/config/enabled.yml +20 -0
  8. data/lib/rubocop.rb +10 -13
  9. data/lib/rubocop/cli.rb +23 -20
  10. data/lib/rubocop/cop/lint/def_end_alignment.rb +47 -0
  11. data/lib/rubocop/cop/lint/end_alignment.rb +18 -65
  12. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -1
  13. data/lib/rubocop/cop/lint/syntax.rb +28 -4
  14. data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +1 -1
  15. data/lib/rubocop/cop/lint/unused_block_argument.rb +13 -1
  16. data/lib/rubocop/cop/lint/useless_access_modifier.rb +3 -2
  17. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -9
  18. data/lib/rubocop/cop/lint/useless_setter_call.rb +28 -20
  19. data/lib/rubocop/cop/mixin/access_modifier_node.rb +18 -0
  20. data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +4 -2
  21. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +42 -0
  22. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +32 -0
  23. data/lib/rubocop/cop/mixin/surrounding_space.rb +7 -8
  24. data/lib/rubocop/cop/mixin/unused_argument.rb +1 -1
  25. data/lib/rubocop/cop/offense.rb +27 -14
  26. data/lib/rubocop/cop/style/access_modifier_indentation.rb +2 -9
  27. data/lib/rubocop/cop/style/attr.rb +3 -1
  28. data/lib/rubocop/cop/style/class_check.rb +42 -0
  29. data/lib/rubocop/cop/style/each_with_object.rb +5 -1
  30. data/lib/rubocop/cop/style/empty_lines.rb +1 -4
  31. data/lib/rubocop/cop/style/empty_lines_around_access_modifier.rb +2 -8
  32. data/lib/rubocop/cop/style/empty_lines_around_body.rb +1 -4
  33. data/lib/rubocop/cop/style/encoding.rb +3 -5
  34. data/lib/rubocop/cop/style/end_of_line.rb +2 -5
  35. data/lib/rubocop/cop/style/file_name.rb +2 -4
  36. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
  37. data/lib/rubocop/cop/style/indentation_consistency.rb +2 -1
  38. data/lib/rubocop/cop/style/indentation_width.rb +25 -5
  39. data/lib/rubocop/cop/style/line_length.rb +59 -5
  40. data/lib/rubocop/cop/style/next.rb +18 -18
  41. data/lib/rubocop/cop/style/numeric_literals.rb +22 -9
  42. data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -0
  43. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  44. data/lib/rubocop/cop/style/semicolon.rb +1 -3
  45. data/lib/rubocop/cop/style/space_after_colon.rb +7 -3
  46. data/lib/rubocop/cop/style/space_around_equals_in_parameter_default.rb +3 -1
  47. data/lib/rubocop/cop/style/space_before_comma.rb +16 -0
  48. data/lib/rubocop/cop/style/space_before_semicolon.rb +16 -0
  49. data/lib/rubocop/cop/style/tab.rb +6 -5
  50. data/lib/rubocop/cop/style/trailing_comma.rb +33 -6
  51. data/lib/rubocop/cop/style/trailing_whitespace.rb +4 -3
  52. data/lib/rubocop/cop/style/unneeded_capital_w.rb +6 -0
  53. data/lib/rubocop/cop/style/unneeded_percent_q.rb +45 -0
  54. data/lib/rubocop/cop/style/unneeded_percent_x.rb +2 -3
  55. data/lib/rubocop/cop/style/word_array.rb +1 -1
  56. data/lib/rubocop/cop/team.rb +6 -12
  57. data/lib/rubocop/cop/util.rb +26 -8
  58. data/lib/rubocop/cop/variable_force.rb +3 -6
  59. data/lib/rubocop/cop/variable_force/variable.rb +7 -3
  60. data/lib/rubocop/processed_source.rb +52 -12
  61. data/lib/rubocop/{file_inspector.rb → runner.rb} +50 -59
  62. data/lib/rubocop/version.rb +1 -1
  63. data/relnotes/v0.24.0.md +77 -0
  64. data/rubocop.gemspec +4 -4
  65. data/spec/rubocop/cli_spec.rb +38 -21
  66. data/spec/rubocop/config_loader_spec.rb +7 -6
  67. data/spec/rubocop/config_spec.rb +8 -8
  68. data/spec/rubocop/cop/cop_spec.rb +1 -1
  69. data/spec/rubocop/cop/lint/def_end_alignment_spec.rb +108 -0
  70. data/spec/rubocop/cop/lint/end_alignment_spec.rb +0 -47
  71. data/spec/rubocop/cop/lint/invalid_character_literal_spec.rb +6 -7
  72. data/spec/rubocop/cop/lint/unused_block_argument_spec.rb +19 -0
  73. data/spec/rubocop/cop/lint/useless_assignment_spec.rb +8 -18
  74. data/spec/rubocop/cop/lint/useless_setter_call_spec.rb +99 -51
  75. data/spec/rubocop/cop/offense_spec.rb +3 -3
  76. data/spec/rubocop/cop/rails/delegate_spec.rb +1 -1
  77. data/spec/rubocop/cop/style/access_modifier_indentation_spec.rb +12 -0
  78. data/spec/rubocop/cop/style/align_hash_spec.rb +4 -4
  79. data/spec/rubocop/cop/style/align_parameters_spec.rb +1 -1
  80. data/spec/rubocop/cop/style/attr_spec.rb +12 -2
  81. data/spec/rubocop/cop/style/class_check_spec.rb +41 -0
  82. data/spec/rubocop/cop/style/each_with_object_spec.rb +5 -0
  83. data/spec/rubocop/cop/style/if_with_semicolon_spec.rb +5 -0
  84. data/spec/rubocop/cop/style/indentation_width_spec.rb +95 -0
  85. data/spec/rubocop/cop/style/line_length_spec.rb +75 -0
  86. data/spec/rubocop/cop/style/next_spec.rb +28 -0
  87. data/spec/rubocop/cop/style/numeric_literals_spec.rb +10 -0
  88. data/spec/rubocop/cop/style/parentheses_around_condition_spec.rb +5 -0
  89. data/spec/rubocop/cop/style/space_after_colon_spec.rb +17 -0
  90. data/spec/rubocop/cop/style/space_around_equals_in_parameter_default_spec.rb +11 -0
  91. data/spec/rubocop/cop/style/space_around_operators_spec.rb +1 -0
  92. data/spec/rubocop/cop/style/space_before_comma_spec.rb +42 -0
  93. data/spec/rubocop/cop/style/space_before_semicolon_spec.rb +28 -0
  94. data/spec/rubocop/cop/style/trailing_comma_spec.rb +37 -15
  95. data/spec/rubocop/cop/style/unneeded_capital_w_spec.rb +8 -10
  96. data/spec/rubocop/cop/style/unneeded_percent_q_spec.rb +72 -0
  97. data/spec/rubocop/cop/style/word_array_spec.rb +6 -0
  98. data/spec/rubocop/cop/team_spec.rb +8 -8
  99. data/spec/rubocop/cop/util_spec.rb +10 -0
  100. data/spec/rubocop/cop/variable_force/assignment_spec.rb +1 -1
  101. data/spec/rubocop/cop/variable_force/locatable_spec.rb +1 -1
  102. data/spec/rubocop/cop/variable_force/scope_spec.rb +1 -1
  103. data/spec/rubocop/cop/variable_force/variable_spec.rb +4 -4
  104. data/spec/rubocop/formatter/base_formatter_spec.rb +5 -5
  105. data/spec/rubocop/formatter/colorizable_spec.rb +2 -2
  106. data/spec/rubocop/formatter/json_formatter_spec.rb +1 -1
  107. data/spec/rubocop/path_util_spec.rb +15 -15
  108. data/spec/rubocop/processed_source_spec.rb +104 -50
  109. data/spec/rubocop/runner_spec.rb +64 -0
  110. data/spec/spec_helper.rb +8 -10
  111. data/spec/support/shared_examples.rb +22 -0
  112. metadata +39 -15
  113. data/lib/rubocop/source_parser.rb +0 -47
  114. data/spec/rubocop/file_inspector_spec.rb +0 -84
  115. data/spec/rubocop/source_parser_spec.rb +0 -85
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for cops checking for space before
6
+ # punctuation.
7
+ module SpaceBeforePunctuation
8
+ MSG = 'Space found before %s.'
9
+
10
+ def investigate(processed_source)
11
+ processed_source.tokens.each_cons(2) do |t1, t2|
12
+ next unless kind(t2) && t1.pos.line == t2.pos.line &&
13
+ t2.pos.begin_pos > t1.pos.end_pos
14
+ buffer = processed_source.buffer
15
+ pos_before_punctuation = Parser::Source::Range.new(buffer,
16
+ t1.pos.end_pos,
17
+ t2.pos.begin_pos)
18
+
19
+ add_offense(pos_before_punctuation,
20
+ pos_before_punctuation,
21
+ format(MSG, kind(t2)))
22
+ end
23
+ end
24
+
25
+ def autocorrect(pos_before_punctuation)
26
+ @corrections << lambda do |corrector|
27
+ corrector.remove(pos_before_punctuation)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -5,14 +5,13 @@ module RuboCop
5
5
  # Common functionality for checking surrounding space.
6
6
  module SurroundingSpace
7
7
  def space_between?(t1, t2)
8
- char_preceding_2nd_token =
9
- @processed_source[t2.pos.line - 1][t2.pos.column - 1]
10
- if char_preceding_2nd_token == '+' && t1.type != :tPLUS
11
- # Special case. A unary plus is not present in the tokens.
12
- char_preceding_2nd_token =
13
- @processed_source[t2.pos.line - 1][t2.pos.column - 2]
14
- end
15
- t2.pos.line > t1.pos.line || char_preceding_2nd_token =~ /[ \t]/
8
+ between = Parser::Source::Range.new(t1.pos.source_buffer,
9
+ t1.pos.end_pos,
10
+ t2.pos.begin_pos).source
11
+ # Check if the range between the tokens starts with a space. It can
12
+ # contain other characters, e.g. a unary plus, but it must start with
13
+ # space.
14
+ between =~ /^\s/
16
15
  end
17
16
 
18
17
  def index_of_first_token(node)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  end
24
24
 
25
25
  def check_argument(variable)
26
- return if variable.name.to_s.start_with?('_')
26
+ return if variable.should_be_unused?
27
27
  return if variable.referenced?
28
28
 
29
29
  message = message(variable)
@@ -6,6 +6,9 @@ module RuboCop
6
6
  class Offense
7
7
  include Comparable
8
8
 
9
+ # @api private
10
+ COMPARISON_ATTRIBUTES = [:line, :column, :cop_name, :message, :severity]
11
+
9
12
  # @api public
10
13
  #
11
14
  # @!attribute [r] severity
@@ -56,21 +59,13 @@ module RuboCop
56
59
  attr_reader :corrected
57
60
  alias_method :corrected?, :corrected
58
61
 
59
- # @api private
60
- attr_reader :line
61
-
62
- # @api private
63
- attr_reader :column
64
-
65
62
  # @api private
66
63
  def initialize(severity, location, message, cop_name, corrected = false)
67
64
  @severity = RuboCop::Cop::Severity.new(severity)
68
- @location = location.freeze
69
- @line = location.line.freeze
70
- @column = location.column.freeze
65
+ @location = location
71
66
  @message = message.freeze
72
67
  @cop_name = cop_name.freeze
73
- @corrected = corrected.freeze
68
+ @corrected = corrected
74
69
  freeze
75
70
  end
76
71
 
@@ -81,6 +76,16 @@ module RuboCop
81
76
  severity.code, line, real_column, message)
82
77
  end
83
78
 
79
+ # @api private
80
+ def line
81
+ location.line
82
+ end
83
+
84
+ # @api private
85
+ def column
86
+ location.column
87
+ end
88
+
84
89
  # @api private
85
90
  #
86
91
  # Internally we use column number that start at 0, but when
@@ -95,9 +100,17 @@ module RuboCop
95
100
  # @return [Boolean]
96
101
  # returns `true` if two offenses contain same attributes
97
102
  def ==(other)
98
- severity == other.severity && line == other.line &&
99
- column == other.column && message == other.message &&
100
- cop_name == other.cop_name
103
+ COMPARISON_ATTRIBUTES.all? do |attribute|
104
+ send(attribute) == other.send(attribute)
105
+ end
106
+ end
107
+
108
+ alias_method :eql?, :==
109
+
110
+ def hash
111
+ COMPARISON_ATTRIBUTES.reduce(0) do |hash, attribute|
112
+ hash ^ send(attribute).hash
113
+ end
101
114
  end
102
115
 
103
116
  # @api public
@@ -108,7 +121,7 @@ module RuboCop
108
121
  # @return [Integer]
109
122
  # comparison result
110
123
  def <=>(other)
111
- [:line, :column, :cop_name, :message].each do |attribute|
124
+ COMPARISON_ATTRIBUTES.each do |attribute|
112
125
  result = send(attribute) <=> other.send(attribute)
113
126
  return result unless result == 0
114
127
  end
@@ -8,13 +8,10 @@ module RuboCop
8
8
  class AccessModifierIndentation < Cop
9
9
  include AutocorrectAlignment
10
10
  include ConfigurableEnforcedStyle
11
+ include AccessModifierNode
11
12
 
12
13
  MSG = '%s access modifiers like `%s`.'
13
14
 
14
- PRIVATE_NODE = s(:send, nil, :private)
15
- PROTECTED_NODE = s(:send, nil, :protected)
16
- PUBLIC_NODE = s(:send, nil, :public)
17
-
18
15
  def on_class(node)
19
16
  _name, _base_class, body = *node
20
17
  check_body(body, node)
@@ -35,16 +32,12 @@ module RuboCop
35
32
  check_body(body, node) if class_constructor?(node)
36
33
  end
37
34
 
38
- def self.modifier_node?(node)
39
- [PRIVATE_NODE, PROTECTED_NODE, PUBLIC_NODE].include?(node)
40
- end
41
-
42
35
  private
43
36
 
44
37
  def check_body(body, node)
45
38
  return if body.nil? # Empty class etc.
46
39
 
47
- modifiers = body.children.select { |c| self.class.modifier_node?(c) }
40
+ modifiers = body.children.select { |c| modifier_node?(c) }
48
41
  class_column = node.loc.expression.column
49
42
 
50
43
  modifiers.each { |modifier| check_modifier(modifier, class_column) }
@@ -8,7 +8,9 @@ module RuboCop
8
8
  MSG = 'Do not use `attr`. Use `attr_reader` instead.'
9
9
 
10
10
  def on_send(node)
11
- add_offense(node, :selector) if command?(:attr, node)
11
+ return unless command?(:attr, node)
12
+ _receiver, _method_name, *args = *node
13
+ add_offense(node, :selector) if args.any?
12
14
  end
13
15
 
14
16
  def autocorrect(node)
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop enforces consistent use of `Object#is_a?` or `Object#kind_of?`.
7
+ class ClassCheck < Cop
8
+ include ConfigurableEnforcedStyle
9
+
10
+ MSG = 'Prefer `Object#%s` over `Object#%s`.'
11
+
12
+ def on_send(node)
13
+ _receiver, method_name, *_args = *node
14
+ return unless [:is_a?,
15
+ :kind_of?].include?(method_name)
16
+
17
+ return if style == method_name
18
+ add_offense(node, :selector)
19
+ end
20
+
21
+ def message(node)
22
+ _receiver, method_name, *_args = *node
23
+
24
+ if method_name == :is_a?
25
+ format(MSG, 'kind_of?', 'is_a?')
26
+ else
27
+ format(MSG, 'is_a?', 'kind_of?')
28
+ end
29
+ end
30
+
31
+ def autocorrect(node)
32
+ _receiver, method_name, *_args = *node
33
+
34
+ @corrections << lambda do |corrector|
35
+ corrector.replace(node.loc.selector,
36
+ method_name == :is_a? ? 'kind_of?' : 'is_a?')
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -19,10 +19,14 @@ module RuboCop
19
19
 
20
20
  def on_block(node)
21
21
  method, args, body = *node
22
+
23
+ # filter out super and zsuper nodes
24
+ return unless method.type == :send
25
+
22
26
  _, method_name, method_args = *method
23
27
 
24
28
  return unless METHODS.include? method_name
25
- return if method_args.type == :sym
29
+ return if method_args && method_args.type == :sym
26
30
 
27
31
  return_value = return_value(body)
28
32
  return unless return_value
@@ -26,10 +26,7 @@ module RuboCop
26
26
  next unless processed_source[line - 2].empty? &&
27
27
  processed_source[line - 1].empty?
28
28
 
29
- range = source_range(processed_source.buffer,
30
- processed_source[0...(line - 1)],
31
- 0,
32
- 1)
29
+ range = source_range(processed_source.buffer, line, 0)
33
30
  add_offense(range, range)
34
31
  end
35
32
  end
@@ -5,11 +5,9 @@ module RuboCop
5
5
  module Style
6
6
  # Access modifiers should be surrounded by blank lines.
7
7
  class EmptyLinesAroundAccessModifier < Cop
8
- MSG = 'Keep a blank line before and after `%s`.'
8
+ include AccessModifierNode
9
9
 
10
- PRIVATE_NODE = s(:send, nil, :private)
11
- PROTECTED_NODE = s(:send, nil, :protected)
12
- PUBLIC_NODE = s(:send, nil, :public)
10
+ MSG = 'Keep a blank line before and after `%s`.'
13
11
 
14
12
  def on_send(node)
15
13
  return unless modifier_node?(node)
@@ -38,10 +36,6 @@ module RuboCop
38
36
  def message(node)
39
37
  format(MSG, node.loc.selector.source)
40
38
  end
41
-
42
- def modifier_node?(node)
43
- [PRIVATE_NODE, PROTECTED_NODE, PUBLIC_NODE].include?(node)
44
- end
45
39
  end
46
40
  end
47
41
  end
@@ -62,10 +62,7 @@ module RuboCop
62
62
  def check_line(line, msg)
63
63
  return unless processed_source.lines[line].empty?
64
64
 
65
- range = source_range(processed_source.buffer,
66
- processed_source[0...line],
67
- 0,
68
- 1)
65
+ range = source_range(processed_source.buffer, line + 1, 0)
69
66
  add_offense(range, range, msg)
70
67
  end
71
68
  end
@@ -24,11 +24,9 @@ module RuboCop
24
24
  message = offense(processed_source, line_number)
25
25
 
26
26
  return unless message
27
- add_offense(nil,
28
- source_range(processed_source.buffer,
29
- processed_source[0...line_number],
30
- 0, 1),
31
- message)
27
+
28
+ range = source_range(processed_source.buffer, line_number + 1, 0)
29
+ add_offense(nil, range, message)
32
30
  end
33
31
 
34
32
  private
@@ -15,11 +15,8 @@ module RuboCop
15
15
  original_source.lines.each_with_index do |line, index|
16
16
  next unless line =~ /\r$/
17
17
 
18
- add_offense(nil,
19
- source_range(buffer,
20
- processed_source[0...index],
21
- 0, line.length),
22
- MSG)
18
+ range = source_range(buffer, index + 1, 0, line.length)
19
+ add_offense(nil, range, MSG)
23
20
  # Usually there will be carriage return characters on all or none
24
21
  # of the lines in a file, so we report only one offense.
25
22
  break
@@ -16,10 +16,8 @@ module RuboCop
16
16
  basename = File.basename(file_path).sub(/\.[^\.]+$/, '')
17
17
  return if snake_case?(basename)
18
18
 
19
- add_offense(nil,
20
- source_range(processed_source.buffer,
21
- processed_source[0..0],
22
- 0, 1))
19
+ range = source_range(processed_source.buffer, 1, 0)
20
+ add_offense(nil, range)
23
21
  end
24
22
 
25
23
  private
@@ -8,7 +8,8 @@ module RuboCop
8
8
  include IfThenElse
9
9
 
10
10
  def offending_line(node)
11
- node.loc.begin.line if node.loc.begin && node.loc.begin.is?(';')
11
+ b = node.loc.begin
12
+ b.line if b && b.is?(';')
12
13
  end
13
14
 
14
15
  def error_message(_node)
@@ -15,6 +15,7 @@ module RuboCop
15
15
  # end
16
16
  class IndentationConsistency < Cop
17
17
  include AutocorrectAlignment
18
+ include AccessModifierNode
18
19
 
19
20
  MSG = 'Inconsistent indentation detected.'
20
21
 
@@ -32,7 +33,7 @@ module RuboCop
32
33
  children_to_check = node.children.reject do |child|
33
34
  # Don't check nodes that have special indentation and will be
34
35
  # checked by the AccessModifierIndentation cop.
35
- AccessModifierIndentation.modifier_node?(child)
36
+ modifier_node?(child)
36
37
  end
37
38
 
38
39
  check_alignment(children_to_check)
@@ -12,15 +12,33 @@ module RuboCop
12
12
  # puts 'hello'
13
13
  # end
14
14
  # end
15
- class IndentationWidth < Cop
15
+ class IndentationWidth < Cop # rubocop:disable Style/ClassLength
16
16
  include AutocorrectAlignment
17
17
  include CheckMethods
18
18
  include CheckAssignment
19
19
  include IfNode
20
+ include AccessModifierNode
20
21
 
21
22
  CORRECT_INDENTATION = 2
22
23
 
24
+ def on_rescue(node)
25
+ _begin_node, *rescue_nodes, else_node = *node
26
+ rescue_nodes.each do |rescue_node|
27
+ _, _, body = *rescue_node
28
+ check_indentation(rescue_node.loc.keyword, body)
29
+ end
30
+ check_indentation(node.loc.else, else_node)
31
+ end
32
+
33
+ def on_ensure(node)
34
+ _body, ensure_body = *node
35
+ check_indentation(node.loc.keyword, ensure_body)
36
+ end
37
+
23
38
  def on_kwbegin(node)
39
+ # Check indentation against end keyword but only if it's first on its
40
+ # line.
41
+ return unless begins_its_line?(node.loc.end)
24
42
  check_indentation(node.loc.end, node.children.first)
25
43
  end
26
44
 
@@ -78,7 +96,7 @@ module RuboCop
78
96
  latest_when = nil
79
97
  branches.compact.each do |b|
80
98
  if b.type == :when
81
- _condition, body = *b
99
+ *_conditions, body = *b
82
100
  # Check "when" body against "when" keyword indentation.
83
101
  check_indentation(b.loc.keyword, body)
84
102
  latest_when = b
@@ -164,7 +182,10 @@ module RuboCop
164
182
 
165
183
  # This cop only auto-corrects the first statement in a def body, for
166
184
  # example.
167
- body_node = body_node.children.first if body_node.type == :begin
185
+ if body_node.type == :begin && !(body_node.loc.begin &&
186
+ body_node.loc.begin.is?('('))
187
+ body_node = body_node.children.first
188
+ end
168
189
 
169
190
  expr = body_node.loc.expression
170
191
  begin_pos, ind = expr.begin_pos, expr.begin_pos - indentation
@@ -177,8 +198,7 @@ module RuboCop
177
198
  end
178
199
 
179
200
  def starts_with_access_modifier?(body_node)
180
- body_node.type == :begin &&
181
- AccessModifierIndentation.modifier_node?(body_node.children.first)
201
+ body_node.type == :begin && modifier_node?(body_node.children.first)
182
202
  end
183
203
  end
184
204
  end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'uri'
4
+
3
5
  module RuboCop
4
6
  module Cop
5
7
  module Style
@@ -14,12 +16,24 @@ module RuboCop
14
16
  processed_source.lines.each_with_index do |line, index|
15
17
  next unless line.length > max
16
18
 
19
+ if allow_uri?
20
+ uri_range = find_excessive_uri_range(line)
21
+ next if uri_range && allowed_uri_position?(line, uri_range)
22
+ end
23
+
17
24
  message = format(MSG, line.length, max)
18
- add_offense(nil,
19
- source_range(processed_source.buffer,
20
- processed_source[0...index], max,
21
- line.length - max),
22
- message) do
25
+
26
+ excessive_position = if uri_range && uri_range.begin < max
27
+ uri_range.end
28
+ else
29
+ max
30
+ end
31
+
32
+ range = source_range(processed_source.buffer,
33
+ index + 1,
34
+ excessive_position...(line.length))
35
+
36
+ add_offense(nil, range, message) do
23
37
  self.max = line.length
24
38
  end
25
39
  end
@@ -28,6 +42,46 @@ module RuboCop
28
42
  def max
29
43
  cop_config['Max']
30
44
  end
45
+
46
+ def allow_uri?
47
+ cop_config['AllowURI']
48
+ end
49
+
50
+ def allowed_uri_position?(line, uri_range)
51
+ uri_range.begin < max && uri_range.end == line.length
52
+ end
53
+
54
+ def find_excessive_uri_range(line)
55
+ last_uri_match = match_uris(line).last
56
+ return nil unless last_uri_match
57
+ begin_position, end_position = last_uri_match.offset(0)
58
+ return nil if begin_position < max && end_position < max
59
+ begin_position...end_position
60
+ end
61
+
62
+ def match_uris(string)
63
+ matches = []
64
+ unscanned_position = 0
65
+
66
+ loop do
67
+ match_data = string.match(URI.regexp, unscanned_position)
68
+ break unless match_data
69
+
70
+ uri_ish_string = match_data[0]
71
+ matches << match_data if valid_uri?(uri_ish_string)
72
+
73
+ _, unscanned_position = match_data.offset(0)
74
+ end
75
+
76
+ matches
77
+ end
78
+
79
+ def valid_uri?(uri_ish_string)
80
+ URI.parse(uri_ish_string)
81
+ true
82
+ rescue URI::InvalidURIError
83
+ false
84
+ end
31
85
  end
32
86
  end
33
87
  end