rubocop 0.54.0 → 0.55.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -1
  3. data/config/default.yml +17 -2
  4. data/config/enabled.yml +13 -0
  5. data/lib/rubocop.rb +4 -0
  6. data/lib/rubocop/ast/node/mixin/binary_operator_node.rb +20 -0
  7. data/lib/rubocop/cli.rb +6 -2
  8. data/lib/rubocop/cop/commissioner.rb +21 -25
  9. data/lib/rubocop/cop/layout/end_of_line.rb +33 -0
  10. data/lib/rubocop/cop/layout/space_inside_parens.rb +64 -5
  11. data/lib/rubocop/cop/layout/trailing_whitespace.rb +20 -0
  12. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +80 -0
  13. data/lib/rubocop/cop/lint/shadowed_argument.rb +3 -0
  14. data/lib/rubocop/cop/lint/void.rb +20 -9
  15. data/lib/rubocop/cop/metrics/block_length.rb +17 -1
  16. data/lib/rubocop/cop/metrics/line_length.rb +2 -3
  17. data/lib/rubocop/cop/mixin/percent_literal.rb +9 -8
  18. data/lib/rubocop/cop/performance/end_with.rb +2 -1
  19. data/lib/rubocop/cop/performance/regexp_match.rb +43 -7
  20. data/lib/rubocop/cop/performance/start_with.rb +2 -1
  21. data/lib/rubocop/cop/performance/unneeded_sort.rb +130 -0
  22. data/lib/rubocop/cop/rails/http_status.rb +19 -16
  23. data/lib/rubocop/cop/rails/inverse_of.rb +29 -22
  24. data/lib/rubocop/cop/rails/read_write_attribute.rb +9 -2
  25. data/lib/rubocop/cop/style/array_join.rb +1 -1
  26. data/lib/rubocop/cop/style/class_vars.rb +5 -4
  27. data/lib/rubocop/cop/style/commented_keyword.rb +2 -3
  28. data/lib/rubocop/cop/style/empty_line_after_guard_clause.rb +39 -8
  29. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +22 -11
  30. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +5 -0
  31. data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
  32. data/lib/rubocop/cop/style/negated_while.rb +18 -0
  33. data/lib/rubocop/cop/style/nested_ternary_operator.rb +11 -0
  34. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  35. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -0
  36. data/lib/rubocop/cop/style/option_hash.rb +6 -0
  37. data/lib/rubocop/cop/style/single_line_block_params.rb +20 -0
  38. data/lib/rubocop/cop/style/special_global_vars.rb +52 -0
  39. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  40. data/lib/rubocop/cop/style/unpack_first.rb +0 -2
  41. data/lib/rubocop/formatter/auto_gen_config_formatter.rb +16 -0
  42. data/lib/rubocop/formatter/formatter_set.rb +14 -13
  43. data/lib/rubocop/node_pattern.rb +2 -2
  44. data/lib/rubocop/options.rb +1 -0
  45. data/lib/rubocop/version.rb +1 -1
  46. metadata +13 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b90da169ae8cd99e01c43d41be0a4d43911dcf77
4
- data.tar.gz: 5c1db29014d29765ee9400b93d695a0beee04e4a
3
+ metadata.gz: b6193bafb35931ae47f1a4d40c8b9919f4f8df07
4
+ data.tar.gz: 8b4c066f46a71fe4d35d84d0b1063f22130e2b4d
5
5
  SHA512:
6
- metadata.gz: 750f559ae4fe5bdc11721d45907baf604f37b182602045ef38c62ca277302e3834a99bb7dab80ddd5eb70be9d358b309aeb37129a85aefeb0234ffdb54848f04
7
- data.tar.gz: f3f987115edf268066df468d4077c1f1e160dce133b277b6ac6a261cf26a7e26e41f6f54fe24eecf626858931016a7562e9386b7f2a3180f1c31f0500dfe2f67
6
+ metadata.gz: a619c39a295a5cfdc1a2d82a51391f8d995ea04e20a865eba6951056f57766be7df10718194782f715750f0d9007c6ee730688289acd277c371fc408e137c065
7
+ data.tar.gz: 3ad640cddf0bedeaa238e9e13beef2da09f156c1bc957784c2779cc65076a7f142e0b85c113a6221e7792e491db4ebabeafea4c7424a92750523ddd55ecb4128
data/README.md CHANGED
@@ -52,7 +52,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
52
52
  might want to use a conservative version locking in your `Gemfile`:
53
53
 
54
54
  ```rb
55
- gem 'rubocop', '~> 0.54.0', require: false
55
+ gem 'rubocop', '~> 0.55.0', require: false
56
56
  ```
57
57
 
58
58
  ## Quickstart
@@ -75,6 +75,10 @@ RuboCop supports the following Ruby implementations:
75
75
  * MRI 2.1+
76
76
  * JRuby 9.0+
77
77
 
78
+ The Rails cops support the following versions:
79
+
80
+ * Rails 4.0+
81
+
78
82
  ## Team
79
83
 
80
84
  Here's a list of RuboCop's core developers:
@@ -604,6 +604,12 @@ Layout/SpaceInsideHashLiteralBraces:
604
604
  - space
605
605
  - no_space
606
606
 
607
+ Layout/SpaceInsideParens:
608
+ EnforcedStyle: no_space
609
+ SupportedStyles:
610
+ - space
611
+ - no_space
612
+
607
613
  Layout/SpaceInsideReferenceBrackets:
608
614
  EnforcedStyle: no_space
609
615
  SupportedStyles:
@@ -648,6 +654,9 @@ Layout/TrailingBlankLines:
648
654
  - final_newline
649
655
  - final_blank_line
650
656
 
657
+ Layout/TrailingWhitespace:
658
+ AllowInHeredoc: false
659
+
651
660
  #################### Naming ##########################
652
661
 
653
662
  Naming/FileName:
@@ -712,8 +721,7 @@ Naming/FileName:
712
721
 
713
722
  Naming/HeredocDelimiterNaming:
714
723
  Blacklist:
715
- - END
716
- - !ruby/regexp '/EO[A-Z]{1}/'
724
+ - !ruby/regexp '/(^|\s)(EO[A-Z]{1}|END)(\s|$)/'
717
725
 
718
726
  Naming/HeredocDelimiterCase:
719
727
  EnforcedStyle: uppercase
@@ -769,6 +777,10 @@ Naming/UncommunicativeMethodParamName:
769
777
  - io
770
778
  - id
771
779
  - to
780
+ - by
781
+ - 'on'
782
+ - in
783
+ - at
772
784
  # Blacklisted names that will register an offense
773
785
  ForbiddenNames: []
774
786
 
@@ -1121,6 +1133,9 @@ Style/MethodCallWithArgsParentheses:
1121
1133
  IgnoreMacros: true
1122
1134
  IgnoredMethods: []
1123
1135
 
1136
+ Style/MethodCallWithoutArgsParentheses:
1137
+ IgnoredMethods: []
1138
+
1124
1139
  Style/MethodDefParentheses:
1125
1140
  EnforcedStyle: require_parentheses
1126
1141
  SupportedStyles:
@@ -654,6 +654,13 @@ Lint/SafeNavigationChain:
654
654
  Description: 'Do not chain ordinary method call after safe navigation operator.'
655
655
  Enabled: true
656
656
 
657
+ Lint/SafeNavigationConsistency:
658
+ Description: >-
659
+ Check to make sure that if safe navigation is used for a method
660
+ call in an `&&` or `||` condition that safe navigation is used
661
+ for all method calls on that same object.
662
+ Enabled: true
663
+
657
664
  Lint/ScriptPermission:
658
665
  Description: 'Grant script file execute permission.'
659
666
  Enabled: true
@@ -1056,6 +1063,12 @@ Performance/UnfreezeString:
1056
1063
  Description: 'Use unary plus to get an unfrozen string literal.'
1057
1064
  Enabled: true
1058
1065
 
1066
+ Performance/UnneededSort:
1067
+ Description: >-
1068
+ Use `min` instead of `sort.first`,
1069
+ `max_by` instead of `sort_by...last`, etc.
1070
+ Enabled: true
1071
+
1059
1072
  Performance/UriDefaultParser:
1060
1073
  Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.'
1061
1074
  Enabled: true
@@ -293,6 +293,7 @@ require_relative 'rubocop/cop/lint/require_parentheses'
293
293
  require_relative 'rubocop/cop/lint/rescue_exception'
294
294
  require_relative 'rubocop/cop/lint/rescue_type'
295
295
  require_relative 'rubocop/cop/lint/return_in_void_context'
296
+ require_relative 'rubocop/cop/lint/safe_navigation_consistency'
296
297
  require_relative 'rubocop/cop/lint/safe_navigation_chain'
297
298
  require_relative 'rubocop/cop/lint/script_permission'
298
299
  require_relative 'rubocop/cop/lint/shadowed_argument'
@@ -370,6 +371,7 @@ require_relative 'rubocop/cop/performance/start_with'
370
371
  require_relative 'rubocop/cop/performance/string_replacement'
371
372
  require_relative 'rubocop/cop/performance/times_map'
372
373
  require_relative 'rubocop/cop/performance/unfreeze_string'
374
+ require_relative 'rubocop/cop/performance/unneeded_sort'
373
375
  require_relative 'rubocop/cop/performance/uri_default_parser'
374
376
 
375
377
  require_relative 'rubocop/cop/style/alias'
@@ -598,6 +600,8 @@ require_relative 'rubocop/formatter/progress_formatter'
598
600
  require_relative 'rubocop/formatter/quiet_formatter'
599
601
  require_relative 'rubocop/formatter/tap_formatter'
600
602
  require_relative 'rubocop/formatter/worst_offenders_formatter'
603
+ # relies on progress formatter
604
+ require_relative 'rubocop/formatter/auto_gen_config_formatter'
601
605
 
602
606
  require_relative 'rubocop/formatter/formatter_set'
603
607
 
@@ -18,6 +18,26 @@ module RuboCop
18
18
  def rhs
19
19
  node_parts[1]
20
20
  end
21
+
22
+ # Returns all of the conditions, including nested conditions,
23
+ # of the binary operation.
24
+ #
25
+ # @return [Array<Node>] the left and right hand side of the binary
26
+ # operation and the let and right hand side of any nested binary
27
+ # operators
28
+ def conditions
29
+ lhs, rhs = *self
30
+ lhs = lhs.children.first if lhs.begin_type?
31
+ rhs = rhs.children.first if rhs.begin_type?
32
+
33
+ [lhs, rhs].each_with_object([]) do |side, collection|
34
+ if AST::Node::OPERATOR_KEYWORDS.include?(side.type)
35
+ collection.concat(side.conditions)
36
+ else
37
+ collection << side
38
+ end
39
+ end
40
+ end
21
41
  end
22
42
  end
23
43
  end
@@ -178,8 +178,12 @@ module RuboCop
178
178
  # This must be done after the options have already been processed,
179
179
  # because they can affect how ConfigStore behaves
180
180
  @options[:formatters] ||= begin
181
- cfg = @config_store.for(Dir.pwd).for_all_cops
182
- formatter = cfg['DefaultFormatter'] || 'progress'
181
+ if @options[:auto_gen_config]
182
+ formatter = 'autogenconf'
183
+ else
184
+ cfg = @config_store.for(Dir.pwd).for_all_cops
185
+ formatter = cfg['DefaultFormatter'] || 'progress'
186
+ end
183
187
  [[formatter, @options[:output_path]]]
184
188
  end
185
189
 
@@ -11,10 +11,6 @@ module RuboCop
11
11
 
12
12
  attr_reader :errors
13
13
 
14
- def self.callback_methods
15
- Parser::Meta::NODE_TYPES.map { |type| :"on_#{type}" }
16
- end
17
-
18
14
  def initialize(cops, forces = [], options = {})
19
15
  @cops = cops
20
16
  @forces = forces
@@ -24,30 +20,19 @@ module RuboCop
24
20
  reset_errors
25
21
  end
26
22
 
27
- # In the dynamically generated methods below, a call to `super` is used
23
+ # Create methods like :on_send, :on_super, etc. They will be called
24
+ # during AST traversal and try to call corresponding methods on cops.
25
+ # A call to `super` is used
28
26
  # to continue iterating over the children of a node.
29
27
  # However, if we know that a certain node type (like `int`) never has
30
28
  # child nodes, there is no reason to pay the cost of calling `super`.
31
- no_child_callbacks = NO_CHILD_NODES.map do |type|
32
- :"on_#{type}"
33
- end
34
-
35
- callback_methods.each do |callback|
36
- next unless method_defined?(callback)
37
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
38
- def #{callback}(node)
39
- @callbacks[:"#{callback}"] ||= @cops.select do |cop|
40
- cop.respond_to?(:"#{callback}")
41
- end
42
- @callbacks[:#{callback}].each do |cop|
43
- with_cop_error_handling(cop, node) do
44
- cop.send(:#{callback}, node)
45
- end
46
- end
47
-
48
- #{!no_child_callbacks.include?(callback) && 'super'}
49
- end
50
- RUBY
29
+ Parser::Meta::NODE_TYPES.each do |node_type|
30
+ method_name = :"on_#{node_type}"
31
+ next unless method_defined?(method_name)
32
+ define_method(method_name) do |node|
33
+ trigger_responding_cops(method_name, node)
34
+ super(node) unless NO_CHILD_NODES.include?(node_type)
35
+ end
51
36
  end
52
37
 
53
38
  def investigate(processed_source)
@@ -63,6 +48,17 @@ module RuboCop
63
48
 
64
49
  private
65
50
 
51
+ def trigger_responding_cops(callback, node)
52
+ @callbacks[callback] ||= @cops.select do |cop|
53
+ cop.respond_to?(callback)
54
+ end
55
+ @callbacks[callback].each do |cop|
56
+ with_cop_error_handling(cop, node) do
57
+ cop.send(callback, node)
58
+ end
59
+ end
60
+ end
61
+
66
62
  def reset_errors
67
63
  @errors = Hash.new { |hash, k| hash[k] = [] }
68
64
  end
@@ -4,6 +4,39 @@ module RuboCop
4
4
  module Cop
5
5
  module Layout
6
6
  # This cop checks for Windows-style line endings in the source code.
7
+ #
8
+ # @example EnforcedStyle: native (default)
9
+ # # The `native` style means that CR+LF (Carriage Return + Line Feed) is
10
+ # # enforced on Windows, and LF is enforced on other platforms.
11
+ #
12
+ # # bad
13
+ # puts 'Hello' # Return character is LF on Windows.
14
+ # puts 'Hello' # Return character is CR+LF on other than Windows.
15
+ #
16
+ # # good
17
+ # puts 'Hello' # Return character is CR+LF on Windows.
18
+ # puts 'Hello' # Return character is LF on other than Windows.
19
+ #
20
+ # @example EnforcedStyle: lf
21
+ # # The `lf` style means that LF (Line Feed) is enforced on
22
+ # # all platforms.
23
+ #
24
+ # # bad
25
+ # puts 'Hello' # Return character is CR+LF on all platfoms.
26
+ #
27
+ # # good
28
+ # puts 'Hello' # Return character is LF on all platfoms.
29
+ #
30
+ # @example EnforcedStyle: crlf
31
+ # # The `crlf` style means that CR+LF (Carriage Return + Line Feed) is
32
+ # # enforced on all platforms.
33
+ #
34
+ # # bad
35
+ # puts 'Hello' # Return character is LF on all platfoms.
36
+ #
37
+ # # good
38
+ # puts 'Hello' # Return character is CR+LF on all platfoms.
39
+ #
7
40
  class EndOfLine < Cop
8
41
  include ConfigurableEnforcedStyle
9
42
  include RangeHelp
@@ -5,7 +5,9 @@ module RuboCop
5
5
  module Layout
6
6
  # Checks for spaces inside ordinary round parentheses.
7
7
  #
8
- # @example
8
+ # @example EnforcedStyle: no_space (default)
9
+ # # The `no_space` style enforces that parentheses do not have spaces.
10
+ #
9
11
  # # bad
10
12
  # f( 3)
11
13
  # g = (a + 3 )
@@ -13,21 +15,52 @@ module RuboCop
13
15
  # # good
14
16
  # f(3)
15
17
  # g = (a + 3)
18
+ #
19
+ # @example EnforcedStyle: space
20
+ # # The `space` style enforces that parentheses have a space at the
21
+ # # beginning and end.
22
+ # # Note: Empty parentheses should not have spaces.
23
+ #
24
+ # # bad
25
+ # f(3)
26
+ # g = (a + 3)
27
+ # y( )
28
+ #
29
+ # # good
30
+ # f( 3 )
31
+ # g = ( a + 3 )
32
+ # y()
33
+ #
16
34
  class SpaceInsideParens < Cop
17
35
  include SurroundingSpace
18
36
  include RangeHelp
37
+ include ConfigurableEnforcedStyle
19
38
 
20
- MSG = 'Space inside parentheses detected.'.freeze
39
+ MSG = 'Space inside parentheses detected.'.freeze
40
+ MSG_SPACE = 'No space inside parentheses detected.'.freeze
21
41
 
22
42
  def investigate(processed_source)
23
43
  @processed_source = processed_source
24
- each_extraneous_space(processed_source.tokens) do |range|
25
- add_offense(range, location: range)
44
+
45
+ if style == :space
46
+ each_missing_space(processed_source.tokens) do |range|
47
+ add_offense(range, location: range, message: MSG_SPACE)
48
+ end
49
+ else
50
+ each_extraneous_space(processed_source.tokens) do |range|
51
+ add_offense(range, location: range)
52
+ end
26
53
  end
27
54
  end
28
55
 
29
56
  def autocorrect(range)
30
- ->(corrector) { corrector.remove(range) }
57
+ lambda do |corrector|
58
+ if style == :space
59
+ corrector.insert_before(range, ' ')
60
+ else
61
+ corrector.remove(range)
62
+ end
63
+ end
31
64
  end
32
65
 
33
66
  private
@@ -45,9 +78,35 @@ module RuboCop
45
78
  end
46
79
  end
47
80
 
81
+ def each_missing_space(tokens)
82
+ tokens.each_cons(2) do |token1, token2|
83
+ next if can_be_ignored?(token1, token2)
84
+
85
+ next unless token2.line == token1.line && !token1.space_after?
86
+
87
+ if token1.left_parens?
88
+ yield range_between(token2.begin_pos, token2.begin_pos + 1)
89
+
90
+ elsif token2.right_parens?
91
+ yield range_between(token2.begin_pos, token2.end_pos)
92
+ end
93
+ end
94
+ end
95
+
48
96
  def parens?(token1, token2)
49
97
  token1.left_parens? || token2.right_parens?
50
98
  end
99
+
100
+ def can_be_ignored?(token1, token2)
101
+ return true unless parens?(token1, token2)
102
+
103
+ # If the second token is a comment, that means that a line break
104
+ # follows, and that the rules for space inside don't apply.
105
+ return true if token2.comment?
106
+
107
+ # Ignore empty parens. # TODO: Could be configurable.
108
+ return true if token1.left_parens? && token2.right_parens?
109
+ end
51
110
  end
52
111
  end
53
112
  end
@@ -20,8 +20,10 @@ module RuboCop
20
20
  MSG = 'Trailing whitespace detected.'.freeze
21
21
 
22
22
  def investigate(processed_source)
23
+ heredoc_ranges = extract_heredoc_ranges(processed_source.ast)
23
24
  processed_source.lines.each_with_index do |line, index|
24
25
  next unless line.end_with?(' ', "\t")
26
+ next if skip_heredoc? && inside_heredoc?(heredoc_ranges, index + 1)
25
27
 
26
28
  range = source_range(processed_source.buffer,
27
29
  index + 1,
@@ -34,6 +36,24 @@ module RuboCop
34
36
  def autocorrect(range)
35
37
  ->(corrector) { corrector.remove(range) }
36
38
  end
39
+
40
+ private
41
+
42
+ def skip_heredoc?
43
+ cop_config.fetch('AllowInHeredoc', false)
44
+ end
45
+
46
+ def inside_heredoc?(heredoc_ranges, line_number)
47
+ heredoc_ranges.any? { |r| r.include?(line_number) }
48
+ end
49
+
50
+ def extract_heredoc_ranges(ast)
51
+ return [] unless ast
52
+ ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node|
53
+ body = node.location.heredoc_body
54
+ (body.first_line...body.last_line)
55
+ end
56
+ end
37
57
  end
38
58
  end
39
59
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # This cop check to make sure that if safe navigation is used for a method
7
+ # call in an `&&` or `||` condition that safe navigation is used for all
8
+ # method calls on that same object.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # foo&.bar && foo.baz
13
+ #
14
+ # # bad
15
+ # foo.bar || foo&.baz
16
+ #
17
+ # # bad
18
+ # foo&.bar && (foobar.baz || foo.baz)
19
+ #
20
+ # # good
21
+ # foo.bar && foo.baz
22
+ #
23
+ # # good
24
+ # foo&.bar || foo&.baz
25
+ #
26
+ # # good
27
+ # foo&.bar && (foobar.baz || foo&.baz)
28
+ #
29
+ class SafeNavigationConsistency < Cop
30
+ MSG = 'Ensure that safe navigation is used consistently ' \
31
+ 'inside of `&&` and `||`.'.freeze
32
+
33
+ def on_csend(node)
34
+ return unless node.parent &&
35
+ AST::Node::OPERATOR_KEYWORDS.include?(node.parent.type)
36
+ check(node)
37
+ end
38
+
39
+ def check(node)
40
+ ancestor = top_conditional_ancestor(node)
41
+ conditions = ancestor.conditions
42
+ safe_nav_receiver = node.receiver
43
+
44
+ method_calls = conditions.select(&:send_type?)
45
+ unsafe_method_calls = method_calls.select do |method_call|
46
+ safe_nav_receiver == method_call.receiver
47
+ end
48
+
49
+ unsafe_method_calls.each do |unsafe_method_call|
50
+ location =
51
+ node.loc.expression.join(unsafe_method_call.loc.expression)
52
+ add_offense(unsafe_method_call,
53
+ location: location)
54
+ end
55
+ end
56
+
57
+ def autocorrect(node)
58
+ return unless node.dot?
59
+
60
+ lambda do |corrector|
61
+ corrector.insert_before(node.loc.dot, '&')
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def top_conditional_ancestor(node)
68
+ parent = node.parent
69
+ unless parent &&
70
+ (AST::Node::OPERATOR_KEYWORDS.include?(parent.type) ||
71
+ (parent.begin_type? &&
72
+ AST::Node::OPERATOR_KEYWORDS.include?(parent.parent.type)))
73
+ return node
74
+ end
75
+ top_conditional_ancestor(parent)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end