rubocop 0.56.0 → 0.57.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -4
  3. data/assets/output.html.erb +1 -1
  4. data/bin/console +9 -0
  5. data/config/default.yml +23 -3
  6. data/config/disabled.yml +2 -2
  7. data/config/enabled.yml +29 -13
  8. data/{bin → exe}/rubocop +0 -0
  9. data/lib/rubocop.rb +6 -2
  10. data/lib/rubocop/ast/node.rb +3 -1
  11. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +26 -5
  12. data/lib/rubocop/config_loader.rb +0 -1
  13. data/lib/rubocop/config_loader_resolver.rb +4 -2
  14. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  15. data/lib/rubocop/cop/generator.rb +1 -1
  16. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  17. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +130 -0
  18. data/lib/rubocop/cop/layout/dot_position.rb +2 -6
  19. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  20. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +0 -1
  21. data/lib/rubocop/cop/layout/extra_spacing.rb +2 -2
  22. data/lib/rubocop/cop/layout/indent_heredoc.rb +29 -5
  23. data/lib/rubocop/cop/layout/indentation_consistency.rb +1 -1
  24. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  25. data/lib/rubocop/cop/layout/leading_blank_lines.rb +53 -0
  26. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +11 -2
  27. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -1
  28. data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +4 -3
  29. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  30. data/lib/rubocop/cop/mixin/array_syntax.rb +1 -1
  31. data/lib/rubocop/cop/mixin/range_help.rb +3 -7
  32. data/lib/rubocop/cop/rails/assert_not.rb +1 -1
  33. data/lib/rubocop/cop/rails/bulk_change_table.rb +272 -0
  34. data/lib/rubocop/cop/rails/dynamic_find_by.rb +1 -1
  35. data/lib/rubocop/cop/rails/file_path.rb +40 -10
  36. data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
  37. data/lib/rubocop/cop/rails/time_zone.rb +3 -3
  38. data/lib/rubocop/cop/style/access_modifier_declarations.rb +111 -0
  39. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -1
  40. data/lib/rubocop/cop/style/command_literal.rb +1 -5
  41. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +30 -7
  42. data/lib/rubocop/cop/style/mixin_grouping.rb +8 -3
  43. data/lib/rubocop/cop/style/next.rb +1 -1
  44. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +26 -3
  45. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  46. data/lib/rubocop/cop/style/unneeded_condition.rb +73 -0
  47. data/lib/rubocop/cop/style/unneeded_percent_q.rb +13 -0
  48. data/lib/rubocop/cop/variable_force.rb +16 -17
  49. data/lib/rubocop/options.rb +15 -5
  50. data/lib/rubocop/result_cache.rb +3 -3
  51. data/lib/rubocop/string_util.rb +2 -147
  52. data/lib/rubocop/token.rb +2 -1
  53. data/lib/rubocop/version.rb +1 -1
  54. metadata +28 -9
  55. data/lib/rubocop/cop/lint/splat_keyword_arguments.rb +0 -36
@@ -47,7 +47,7 @@ module RuboCop
47
47
  private
48
48
 
49
49
  def check(node)
50
- return if node.loc.respond_to?(:heredoc_body)
50
+ return if node.heredoc?
51
51
  return unless node.loc.respond_to?(:begin)
52
52
  return unless node.loc.begin
53
53
 
@@ -82,7 +82,7 @@ module RuboCop
82
82
  MSG_USE_PERCENT_X = 'Use `%x` around command string.'.freeze
83
83
 
84
84
  def on_xstr(node)
85
- return if heredoc_literal?(node)
85
+ return if node.heredoc?
86
86
 
87
87
  if backtick_literal?(node)
88
88
  check_backtick_literal(node)
@@ -161,10 +161,6 @@ module RuboCop
161
161
  loc.expression.source[loc.begin.length...-loc.end.length]
162
162
  end
163
163
 
164
- def heredoc_literal?(node)
165
- node.loc.respond_to?(:heredoc_body)
166
- end
167
-
168
164
  def backtick_literal?(node)
169
165
  node.loc.begin.source == '`'
170
166
  end
@@ -139,14 +139,37 @@ module RuboCop
139
139
  def insert_comment(corrector)
140
140
  last_special_comment = last_special_comment(processed_source)
141
141
  if last_special_comment.nil?
142
- corrector.insert_before(processed_source.tokens[0].pos,
143
- "#{FROZEN_STRING_LITERAL_ENABLED}\n\n")
144
- elsif processed_source.following_line(last_special_comment).empty?
145
- corrector.insert_after(last_special_comment.pos,
146
- "\n#{FROZEN_STRING_LITERAL_ENABLED}")
142
+ corrector.insert_before(correction_range, preceding_comment)
147
143
  else
148
- corrector.insert_after(last_special_comment.pos,
149
- "\n#{FROZEN_STRING_LITERAL_ENABLED}\n")
144
+ corrector.insert_after(correction_range, proceeding_comment)
145
+ end
146
+ end
147
+
148
+ def preceding_comment
149
+ if processed_source.tokens[0].space_before?
150
+ "#{FROZEN_STRING_LITERAL_ENABLED}\n"
151
+ else
152
+ "#{FROZEN_STRING_LITERAL_ENABLED}\n\n"
153
+ end
154
+ end
155
+
156
+ def proceeding_comment
157
+ last_special_comment = last_special_comment(processed_source)
158
+ if processed_source.following_line(last_special_comment).empty?
159
+ "\n#{FROZEN_STRING_LITERAL_ENABLED}"
160
+ else
161
+ "\n#{FROZEN_STRING_LITERAL_ENABLED}\n"
162
+ end
163
+ end
164
+
165
+ def correction_range
166
+ last_special_comment = last_special_comment(processed_source)
167
+
168
+ if last_special_comment.nil?
169
+ range_with_surrounding_space(range: processed_source.tokens[0],
170
+ side: :left)
171
+ else
172
+ last_special_comment.pos
150
173
  end
151
174
  end
152
175
  end
@@ -36,12 +36,17 @@ module RuboCop
36
36
  MIXIN_METHODS = %i[extend include prepend].freeze
37
37
  MSG = 'Put `%<mixin>s` mixins in %<suffix>s.'.freeze
38
38
 
39
- def on_send(node)
40
- return unless node.macro? && MIXIN_METHODS.include?(node.method_name)
39
+ def on_class(node)
40
+ begin_node = node.child_nodes.find(&:begin_type?) || node
41
+ begin_node.each_child_node(:send).select(&:macro?).each do |macro|
42
+ next unless MIXIN_METHODS.include?(macro.method_name)
41
43
 
42
- check(node)
44
+ check(macro)
45
+ end
43
46
  end
44
47
 
48
+ alias on_module on_class
49
+
45
50
  def autocorrect(node)
46
51
  range = node.loc.expression
47
52
  if separated_style?
@@ -222,7 +222,7 @@ module RuboCop
222
222
 
223
223
  def heredoc_lines(node)
224
224
  node.each_node(:dstr)
225
- .select { |n| n.loc.respond_to?(:heredoc_body) }
225
+ .select(&:heredoc?)
226
226
  .map { |n| n.loc.heredoc_body }
227
227
  .flat_map { |b| (b.line...b.last_line).to_a }
228
228
  end
@@ -6,10 +6,33 @@ module RuboCop
6
6
  # This cop checks for octal, hex, binary and decimal literals using
7
7
  # uppercase prefixes and corrects them to lowercase prefix
8
8
  # or no prefix (in case of decimals).
9
- # eg. for octal use `0o` instead of `0` or `0O`.
10
9
  #
11
- # Can be configured to use `0` only for octal literals using
12
- # `EnforcedOctalStyle` => `zero_only`
10
+ # @example EnforcedOctalStyle: zero_with_o (default)
11
+ # # bad - missing octal prefix
12
+ # num = 01234
13
+ #
14
+ # # bad - uppercase prefix
15
+ # num = 0O1234
16
+ # num = 0X12AB
17
+ # num = 0B10101
18
+ #
19
+ # # bad - redundant decimal prefix
20
+ # num = 0D1234
21
+ # num = 0d1234
22
+ #
23
+ # # good
24
+ # num = 0o1234
25
+ # num = 0x12AB
26
+ # num = 0b10101
27
+ # num = 1234
28
+ #
29
+ # @example EnforcedOctalStyle: zero_only
30
+ # # bad
31
+ # num = 0o1234
32
+ # num = 0O1234
33
+ #
34
+ # # good
35
+ # num = 01234
13
36
  class NumericLiteralPrefix < Cop
14
37
  include IntegerNode
15
38
 
@@ -35,7 +35,7 @@ module RuboCop
35
35
  block_method_name = resolve_block_method_name(send_or_super)
36
36
 
37
37
  # TODO: Rails-specific handling that we should probably make
38
- # configurable - https://github.com/bbatsov/rubocop/issues/1485
38
+ # configurable - https://github.com/rubocop-hq/rubocop/issues/1485
39
39
  # we should ignore lambdas & procs
40
40
  return if proc_node?(send_or_super)
41
41
  return if %i[lambda proc].include?(block_method_name)
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for unnecessary conditional expressions.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # a = b ? b : c
11
+ #
12
+ # # good
13
+ # a = b || c
14
+ #
15
+ # @example
16
+ # # bad
17
+ # if b
18
+ # b
19
+ # else
20
+ # c
21
+ # end
22
+ #
23
+ # # good
24
+ # b || c
25
+ class UnneededCondition < Cop
26
+ include RangeHelp
27
+
28
+ MSG = 'Use double pipes `||` instead.'.freeze
29
+
30
+ def on_if(node)
31
+ return unless offense?(node)
32
+ add_offense(node, location: range_of_offense(node))
33
+ end
34
+
35
+ def autocorrect(node)
36
+ lambda do |corrector|
37
+ if node.ternary?
38
+ corrector.replace(range_of_offense(node), '||')
39
+ else
40
+ corrected = [node.if_branch.source,
41
+ else_source(node.else_branch)].join(' || ')
42
+
43
+ corrector.replace(node.source_range, corrected)
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def range_of_offense(node)
51
+ return :expression unless node.ternary?
52
+ range_between(node.loc.question.begin_pos, node.loc.colon.end_pos)
53
+ end
54
+
55
+ def offense?(node)
56
+ condition, if_branch, else_branch = *node
57
+
58
+ condition == if_branch && !node.elsif? && (
59
+ node.ternary? ||
60
+ !else_branch.instance_of?(AST::Node) ||
61
+ else_branch.single_line?
62
+ )
63
+ end
64
+
65
+ def else_source(else_branch)
66
+ wrap_else = MODIFIER_NODES.include?(else_branch.type) &&
67
+ else_branch.modifier_form?
68
+ wrap_else ? "(#{else_branch.source})" : else_branch.source
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -4,6 +4,19 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop checks for usage of the %q/%Q syntax when '' or "" would do.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # name = %q(Bruce Wayne)
12
+ # time = %q(8 o'clock)
13
+ # question = %q("What did you say?")
14
+ #
15
+ # # good
16
+ # name = 'Bruce Wayne'
17
+ # time = "8 o'clock"
18
+ # question = '"What did you say?"'
19
+ #
7
20
  class UnneededPercentQ < Cop
8
21
  MSG = 'Use `%<q_type>s` only for strings that contain both ' \
9
22
  'single quotes and double quotes%<extra>s.'.freeze
@@ -82,10 +82,9 @@ module RuboCop
82
82
  end
83
83
 
84
84
  def process_node(node)
85
- catch(:skip_children) do
86
- dispatch_node(node)
87
- process_children(node)
88
- end
85
+ method_name = node_handler_method_name(node)
86
+ retval = send(method_name, node) if method_name
87
+ process_children(node) unless retval == :skip_children
89
88
  end
90
89
 
91
90
  private
@@ -105,34 +104,34 @@ module RuboCop
105
104
  end
106
105
 
107
106
  def skip_children!
108
- throw :skip_children
107
+ :skip_children
109
108
  end
110
109
 
111
110
  # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
112
- def dispatch_node(node)
111
+ def node_handler_method_name(node)
113
112
  case node.type
114
113
  when VARIABLE_ASSIGNMENT_TYPE
115
- process_variable_assignment(node)
114
+ :process_variable_assignment
116
115
  when REGEXP_NAMED_CAPTURE_TYPE
117
- process_regexp_named_captures(node)
116
+ :process_regexp_named_captures
118
117
  when MULTIPLE_ASSIGNMENT_TYPE
119
- process_variable_multiple_assignment(node)
118
+ :process_variable_multiple_assignment
120
119
  when VARIABLE_REFERENCE_TYPE
121
- process_variable_referencing(node)
120
+ :process_variable_referencing
122
121
  when RESCUE_TYPE
123
- process_rescue(node)
122
+ :process_rescue
124
123
  when ZERO_ARITY_SUPER_TYPE
125
- process_zero_arity_super(node)
124
+ :process_zero_arity_super
126
125
  when SEND_TYPE
127
- process_send(node)
126
+ :process_send
128
127
  when *ARGUMENT_DECLARATION_TYPES
129
- process_variable_declaration(node)
128
+ :process_variable_declaration
130
129
  when *OPERATOR_ASSIGNMENT_TYPES
131
- process_variable_operator_assignment(node)
130
+ :process_variable_operator_assignment
132
131
  when *LOOP_TYPES
133
- process_loop(node)
132
+ :process_loop
134
133
  when *SCOPE_TYPES
135
- process_scope(node)
134
+ :process_scope
136
135
  end
137
136
  end
138
137
  # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
@@ -65,6 +65,7 @@ module RuboCop
65
65
  add_severity_option(opts)
66
66
  add_flags_with_optional_args(opts)
67
67
  add_boolean_flags(opts)
68
+ add_aliases(opts)
68
69
 
69
70
  option(opts, '-s', '--stdin FILE')
70
71
  end
@@ -142,7 +143,7 @@ module RuboCop
142
143
  end
143
144
  end
144
145
 
145
- def add_boolean_flags(opts) # rubocop:disable Metrics/MethodLength
146
+ def add_boolean_flags(opts)
146
147
  option(opts, '-F', '--fail-fast')
147
148
  option(opts, '-C', '--cache FLAG')
148
149
  option(opts, '-d', '--debug')
@@ -150,10 +151,6 @@ module RuboCop
150
151
  option(opts, '-E', '--extra-details')
151
152
  option(opts, '-S', '--display-style-guide')
152
153
  option(opts, '-R', '--rails')
153
- option(opts, '-l', '--lint') do
154
- @options[:only] ||= []
155
- @options[:only] << 'Lint'
156
- end
157
154
  option(opts, '-a', '--auto-correct')
158
155
 
159
156
  option(opts, '--[no-]color')
@@ -163,6 +160,18 @@ module RuboCop
163
160
  option(opts, '-P', '--parallel')
164
161
  end
165
162
 
163
+ def add_aliases(opts)
164
+ option(opts, '-l', '--lint') do
165
+ @options[:only] ||= []
166
+ @options[:only] << 'Lint'
167
+ end
168
+ option(opts, '-x', '--fix-layout') do
169
+ @options[:only] ||= []
170
+ @options[:only] << 'Layout'
171
+ @options[:auto_correct] = true
172
+ end
173
+ end
174
+
166
175
  def add_list_options(opts)
167
176
  option(opts, '-L', '--list-target-files')
168
177
  end
@@ -379,6 +388,7 @@ module RuboCop
379
388
  lint: 'Run only lint cops.',
380
389
  list_target_files: 'List all files RuboCop will inspect.',
381
390
  auto_correct: 'Auto-correct offenses.',
391
+ fix_layout: 'Run only layout cops, with auto-correct on.',
382
392
  color: 'Force color output on or off.',
383
393
  version: 'Display version.',
384
394
  verbose_version: 'Display verbose version.',
@@ -155,12 +155,12 @@ module RuboCop
155
155
  ResultCache.source_checksum ||=
156
156
  begin
157
157
  lib_root = File.join(File.dirname(__FILE__), '..')
158
- bin_root = File.join(lib_root, '..', 'bin')
158
+ exe_root = File.join(lib_root, '..', 'exe')
159
159
 
160
160
  # These are all the files we have `require`d plus everything in the
161
- # bin directory. A change to any of them could affect the cop output
161
+ # exe directory. A change to any of them could affect the cop output
162
162
  # so we include them in the cache hash.
163
- source_files = $LOADED_FEATURES + Find.find(bin_root).to_a
163
+ source_files = $LOADED_FEATURES + Find.find(exe_root).to_a
164
164
  sources = source_files
165
165
  .select { |path| File.file?(path) }
166
166
  .sort
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'jaro_winkler'
4
+
3
5
  module RuboCop
4
6
  # This module provides approximate string matching methods.
5
7
  module StringUtil
@@ -8,152 +10,5 @@ module RuboCop
8
10
  def similarity(string_a, string_b)
9
11
  JaroWinkler.distance(string_a.to_s, string_b.to_s)
10
12
  end
11
-
12
- # This class computes Jaro distance, which is a measure of similarity
13
- # between two strings.
14
- class Jaro
15
- attr_reader :shorter, :longer
16
-
17
- def self.distance(*args)
18
- new(*args).distance
19
- end
20
-
21
- def initialize(string_a, string_b)
22
- if string_a.size < string_b.size
23
- @shorter = string_a
24
- @longer = string_b
25
- else
26
- @shorter = string_b
27
- @longer = string_a
28
- end
29
- end
30
-
31
- def distance
32
- @distance ||= compute_distance
33
- end
34
-
35
- private
36
-
37
- def compute_distance
38
- common_chars_a, common_chars_b = find_common_characters
39
- matched_count = common_chars_a.size
40
-
41
- return 0.0 if matched_count.zero?
42
-
43
- transposition_count =
44
- count_transpositions(common_chars_a, common_chars_b)
45
-
46
- compute_non_zero_distance(matched_count.to_f, transposition_count)
47
- end
48
-
49
- # rubocop:disable Metrics/AbcSize
50
- def find_common_characters
51
- common_chars_of_shorter = Array.new(shorter.size)
52
- common_chars_of_longer = Array.new(longer.size)
53
-
54
- shorter.each_char.with_index do |shorter_char, shorter_index|
55
- matching_index_range(shorter_index).each do |longer_index|
56
- longer_char = longer.chars[longer_index]
57
-
58
- next unless shorter_char == longer_char
59
-
60
- common_chars_of_shorter[shorter_index] = shorter_char
61
- common_chars_of_longer[longer_index] = longer_char
62
-
63
- # Mark the matching character as already used
64
- longer.chars[longer_index] = nil
65
-
66
- break
67
- end
68
- end
69
-
70
- [common_chars_of_shorter, common_chars_of_longer].map(&:compact)
71
- end
72
- # rubocop:enable Metrics/AbcSize
73
-
74
- def count_transpositions(common_chars_a, common_chars_b)
75
- common_chars_a.size.times.count do |index|
76
- common_chars_a[index] != common_chars_b[index]
77
- end
78
- end
79
-
80
- def compute_non_zero_distance(matched_count, transposition_count)
81
- sum = (matched_count / shorter.size.to_f) +
82
- (matched_count / longer.size.to_f) +
83
- ((matched_count - transposition_count / 2) / matched_count)
84
-
85
- sum / 3.0
86
- end
87
-
88
- def matching_index_range(origin)
89
- min = origin - matching_window
90
- min = 0 if min < 0
91
-
92
- max = origin + matching_window
93
-
94
- min..max
95
- end
96
-
97
- def matching_window
98
- @matching_window ||= (longer.size / 2) - 1
99
- end
100
- end
101
-
102
- # This class computes Jaro-Winkler distance, which adds prefix-matching
103
- # bonus to Jaro distance.
104
- class JaroWinkler < Jaro
105
- # Add the prefix bonus only when the Jaro distance is above this value.
106
- # In other words, if the Jaro distance is less than this value,
107
- # JaroWinkler.distance returns the raw Jaro distance.
108
- DEFAULT_BOOST_THRESHOLD = 0.7
109
-
110
- # How much the prefix bonus is weighted.
111
- # This should not exceed 0.25.
112
- DEFAULT_SCALING_FACTOR = 0.1
113
-
114
- # Cutoff the common prefix length to this value if it's longer than this.
115
- MAX_COMMON_PREFIX_LENGTH = 4
116
-
117
- attr_reader :boost_threshold, :scaling_factor
118
-
119
- def initialize(string_a, string_b,
120
- boost_threshold = nil, scaling_factor = nil)
121
- super(string_a, string_b)
122
- @boost_threshold = boost_threshold || DEFAULT_BOOST_THRESHOLD
123
- @scaling_factor = scaling_factor || DEFAULT_SCALING_FACTOR
124
- end
125
-
126
- private
127
-
128
- def compute_distance
129
- jaro_distance = super
130
-
131
- if jaro_distance >= boost_threshold
132
- bonus = limited_common_prefix_length.to_f * scaling_factor.to_f *
133
- (1.0 - jaro_distance)
134
- jaro_distance + bonus
135
- else
136
- jaro_distance
137
- end
138
- end
139
-
140
- def limited_common_prefix_length
141
- length = common_prefix_length
142
-
143
- if length > MAX_COMMON_PREFIX_LENGTH
144
- MAX_COMMON_PREFIX_LENGTH
145
- else
146
- length
147
- end
148
- end
149
-
150
- def common_prefix_length
151
- shorter.size.times do |index|
152
- return index unless shorter[index] == longer[index]
153
- end
154
-
155
- shorter.size
156
- end
157
- end
158
13
  end
159
14
  end