rubocop 0.54.0 → 0.55.0

Sign up to get free protection for your applications and to get access to all the features.
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