rubocop 0.38.0 → 0.39.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/config/default.yml +13 -0
  4. data/config/enabled.yml +15 -3
  5. data/lib/rubocop.rb +2 -0
  6. data/lib/rubocop/ast_node/builder.rb +6 -0
  7. data/lib/rubocop/cli.rb +2 -2
  8. data/lib/rubocop/config.rb +13 -13
  9. data/lib/rubocop/config_loader.rb +5 -28
  10. data/lib/rubocop/config_loader_resolver.rb +42 -0
  11. data/lib/rubocop/cop/cop.rb +4 -5
  12. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -1
  13. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -2
  14. data/lib/rubocop/cop/mixin/statement_modifier.rb +5 -4
  15. data/lib/rubocop/cop/performance/case_when_splat.rb +1 -1
  16. data/lib/rubocop/cop/performance/count.rb +20 -0
  17. data/lib/rubocop/cop/performance/detect.rb +12 -0
  18. data/lib/rubocop/cop/performance/redundant_merge.rb +10 -1
  19. data/lib/rubocop/cop/performance/times_map.rb +14 -0
  20. data/lib/rubocop/cop/style/case_indentation.rb +14 -7
  21. data/lib/rubocop/cop/style/conditional_assignment.rb +221 -20
  22. data/lib/rubocop/cop/style/file_name.rb +31 -17
  23. data/lib/rubocop/cop/style/if_unless_modifier.rb +8 -0
  24. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +45 -0
  25. data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -2
  26. data/lib/rubocop/cop/style/next.rb +6 -1
  27. data/lib/rubocop/cop/style/one_line_conditional.rb +37 -8
  28. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -0
  29. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  30. data/lib/rubocop/cop/style/space_around_keyword.rb +10 -1
  31. data/lib/rubocop/cop/style/special_global_vars.rb +23 -14
  32. data/lib/rubocop/cop/style/zero_length_predicate.rb +25 -0
  33. data/lib/rubocop/formatter/html_formatter.rb +3 -3
  34. data/lib/rubocop/result_cache.rb +2 -2
  35. data/lib/rubocop/runner.rb +3 -3
  36. data/lib/rubocop/target_finder.rb +2 -6
  37. data/lib/rubocop/version.rb +1 -1
  38. metadata +6 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c755e82e5751b549039b4eb7556f74a512045e00
4
- data.tar.gz: 0c943dff109818624263c8d44e8d533208f066e0
3
+ metadata.gz: dfcbbab3c5c192b9ec4f2af0d65ef6af2218196a
4
+ data.tar.gz: 13951f737deacbbca91730cafca92ed861e5b4b9
5
5
  SHA512:
6
- metadata.gz: 64399fb16df2d1275a11f442b7904d6c036918509eccaffc2be52feea9ed1509d7f5d83c4f9b0eba7542c6d41c60d55c0a5d6ccf7a4c035569b510fcfc014345
7
- data.tar.gz: d87cb665637072f82c9b95800d784e0457c817d7da5a4c95ac55675b1ac71447bd54074fc4018488b949d1e10394379961485583c0314d5385d6d57f6b40ae86
6
+ metadata.gz: 938228431a9934dc8916618e35256c75bf5a95ad3387933019599ecc242a3281ce2a234f3a174793634b44bcf5043b924ee18f1949f3abfd97e67c8db1aea088
7
+ data.tar.gz: 20459e73a5a9fbe29eeac14413a6f2e54253692d474e01cde4ec1155dfff8010a1202297b1d35a4d422cddc27d7d15a2f633725d1ebe6e63ffff41bf672f8114
data/README.md CHANGED
@@ -115,7 +115,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
115
115
  might want to use a conservative version locking in your `Gemfile`:
116
116
 
117
117
  ```rb
118
- gem 'rubocop', '~> 0.38.0', require: false
118
+ gem 'rubocop', '~> 0.39.0', require: false
119
119
  ```
120
120
 
121
121
  ## Basic Usage
@@ -289,7 +289,14 @@ Metrics/LineLength:
289
289
  ### Inheritance
290
290
 
291
291
  RuboCop supports inheriting configuration from one or more supplemental
292
- configuration files at runtime.
292
+ configuration files at runtime. Settings in the file that inherits
293
+ override settings in the file that's inherited from. Configuration
294
+ parameter that are hashes, for example `PreferredMethods` in
295
+ `Style/CollectionMethods` are merged with the same parameter in the base
296
+ configuration, while other parameter, such as `AllCops` / `Include`, are
297
+ simply replaced by the local setting. If arrays were merged, there would
298
+ be no way to remove elements through overriding them in local
299
+ configuration.
293
300
 
294
301
  #### Inheriting from another configuration file in the project
295
302
 
@@ -1017,7 +1024,8 @@ require:
1017
1024
 
1018
1025
  Note: The paths are directly passed to `Kernel.require`. If your
1019
1026
  extension file is not in `$LOAD_PATH`, you need to specify the path as
1020
- relative path prefixed with `./` explicitly, or absolute path.
1027
+ relative path prefixed with `./` explicitly, or absolute path. Paths
1028
+ starting with a `.` are resolved relative to `.rubocop.yml`.
1021
1029
 
1022
1030
  ### Custom Cops
1023
1031
 
@@ -344,6 +344,19 @@ Style/CommentAnnotation:
344
344
  - HACK
345
345
  - REVIEW
346
346
 
347
+ Style/ConditionalAssignment:
348
+ EnforcedStyle: assign_to_condition
349
+ SupportedStyles:
350
+ - assign_to_condition
351
+ - assign_inside_condition
352
+ # When configured to `assign_to_condition`, `SingleLineConditionsOnly`
353
+ # will only register an offense when all branches of a condition are
354
+ # a single line.
355
+ # When configured to `assign_inside_condition`, `SingleLineConditionsOnly`
356
+ # will only register an offense for assignment to a condition that has
357
+ # at least one multiline branch.
358
+ SingleLineConditionsOnly: true
359
+
347
360
  # Checks that you have put a copyright in a comment before any code.
348
361
  #
349
362
  # You can override the default Notice in your .rubocop.yml file.
@@ -159,9 +159,6 @@ Style/ConditionalAssignment:
159
159
  assignment to a variable and variable comparison instead
160
160
  of assigning that variable inside of each branch.
161
161
  Enabled: true
162
- SingleLineConditionsOnly: true
163
- # Whether the cop should register offenses for conditionals whose branches
164
- # contain more than one statement a piece
165
162
 
166
163
  Style/ConstantName:
167
164
  Description: 'Constants should use SCREAMING_SNAKE_CASE.'
@@ -324,6 +321,11 @@ Style/IfUnlessModifier:
324
321
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier'
325
322
  Enabled: true
326
323
 
324
+ Style/IfUnlessModifierOfIfUnless:
325
+ Description: >-
326
+ Avoid modifier if/unless usage on conditionals.
327
+ Enabled: true
328
+
327
329
  Style/IfWithSemicolon:
328
330
  Description: 'Do not use if x; .... Use the ternary operator instead.'
329
331
  StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs'
@@ -1173,6 +1175,11 @@ Performance/Count:
1173
1175
  Use `count` instead of `select...size`, `reject...size`,
1174
1176
  `select...count`, `reject...count`, `select...length`,
1175
1177
  and `reject...length`.
1178
+ # This cop has known compatibility issues with `ActiveRecord` and other
1179
+ # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
1180
+ # For more information, see the documentation in the cop itself.
1181
+ # If you understand the known risk, you can disable `SafeMode`.
1182
+ SafeMode: true
1176
1183
  Enabled: true
1177
1184
 
1178
1185
  Performance/Detect:
@@ -1180,6 +1187,11 @@ Performance/Detect:
1180
1187
  Use `detect` instead of `select.first`, `find_all.first`,
1181
1188
  `select.last`, and `find_all.last`.
1182
1189
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
1190
+ # This cop has known compatibility issues with `ActiveRecord` and other
1191
+ # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
1192
+ # has its own meaning. Correcting `ActiveRecord` methods with this cop
1193
+ # should be considered unsafe.
1194
+ SafeMode: true
1183
1195
  Enabled: true
1184
1196
 
1185
1197
  Performance/DoubleStartEndWith:
@@ -238,6 +238,7 @@ require 'rubocop/cop/style/hash_syntax'
238
238
  require 'rubocop/cop/style/identical_conditional_branches'
239
239
  require 'rubocop/cop/style/if_inside_else'
240
240
  require 'rubocop/cop/style/if_unless_modifier'
241
+ require 'rubocop/cop/style/if_unless_modifier_of_if_unless'
241
242
  require 'rubocop/cop/style/if_with_semicolon'
242
243
  require 'rubocop/cop/style/indent_array'
243
244
  require 'rubocop/cop/style/indent_assignment'
@@ -387,6 +388,7 @@ require 'rubocop/formatter/formatter_set'
387
388
 
388
389
  require 'rubocop/cached_data'
389
390
  require 'rubocop/config'
391
+ require 'rubocop/config_loader_resolver'
390
392
  require 'rubocop/config_loader'
391
393
  require 'rubocop/config_store'
392
394
  require 'rubocop/target_finder'
@@ -20,6 +20,12 @@ module RuboCop
20
20
  def n(type, children, source_map)
21
21
  Node.new(type, children, location: source_map)
22
22
  end
23
+
24
+ # TODO: Figure out what to do about literal encoding handling...
25
+ # More details here https://github.com/whitequark/parser/issues/283
26
+ def string_value(token)
27
+ value(token)
28
+ end
23
29
  end
24
30
  end
25
31
  end
@@ -85,8 +85,8 @@ module RuboCop
85
85
  # This must be done after the options have already been processed,
86
86
  # because they can affect how ConfigStore behaves
87
87
  @options[:formatters] ||= begin
88
- cfg = @config_store.for(Dir.pwd)['AllCops']
89
- formatter = (cfg && cfg['DefaultFormatter']) || 'progress'
88
+ cfg = @config_store.for(Dir.pwd).for_all_cops
89
+ formatter = cfg['DefaultFormatter'] || 'progress'
90
90
  [[formatter, @options[:output_path]]]
91
91
  end
92
92
 
@@ -66,12 +66,10 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def add_excludes_from_higher_level(highest_config)
69
- return unless highest_config['AllCops'] &&
70
- highest_config['AllCops']['Exclude']
69
+ return unless highest_config.for_all_cops['Exclude']
71
70
 
72
- self['AllCops'] ||= {}
73
- excludes = self['AllCops']['Exclude'] ||= []
74
- highest_config['AllCops']['Exclude'].each do |path|
71
+ excludes = for_all_cops['Exclude'] ||= []
72
+ highest_config.for_all_cops['Exclude'].each do |path|
75
73
  unless path.is_a?(Regexp) || absolute?(path)
76
74
  path = File.join(File.dirname(highest_config.loaded_path), path)
77
75
  end
@@ -80,14 +78,12 @@ module RuboCop
80
78
  end
81
79
 
82
80
  def deprecation_check
83
- return unless (all_cops = self['AllCops'])
84
-
85
81
  %w(Exclude Include).each do |key|
86
82
  plural = "#{key}s"
87
- next unless all_cops[plural]
83
+ next unless for_all_cops[plural]
88
84
 
89
- all_cops[key] = all_cops[plural] # Stay backwards compatible.
90
- all_cops.delete(plural)
85
+ for_all_cops[key] = for_all_cops[plural] # Stay backwards compatible.
86
+ for_all_cops.delete(plural)
91
87
  yield "AllCops/#{plural} was renamed to AllCops/#{key}"
92
88
  end
93
89
  end
@@ -96,6 +92,10 @@ module RuboCop
96
92
  @for_cop[cop.respond_to?(:cop_name) ? cop.cop_name : cop]
97
93
  end
98
94
 
95
+ def for_all_cops
96
+ @for_all_cops ||= self['AllCops'] || {}
97
+ end
98
+
99
99
  def cop_enabled?(cop)
100
100
  department = cop.cop_type.to_s.capitalize!
101
101
 
@@ -167,11 +167,11 @@ module RuboCop
167
167
  end
168
168
 
169
169
  def patterns_to_include
170
- @patterns_to_include ||= self['AllCops']['Include']
170
+ @patterns_to_include ||= for_all_cops['Include']
171
171
  end
172
172
 
173
173
  def patterns_to_exclude
174
- @patterns_to_exclude ||= self['AllCops']['Exclude']
174
+ @patterns_to_exclude ||= for_all_cops['Exclude']
175
175
  end
176
176
 
177
177
  def path_relative_to_config(path)
@@ -272,7 +272,7 @@ module RuboCop
272
272
  end
273
273
 
274
274
  def check_target_ruby
275
- target = self['AllCops'] && self['AllCops']['TargetRubyVersion']
275
+ target = for_all_cops['TargetRubyVersion']
276
276
  return unless target
277
277
 
278
278
  unless KNOWN_RUBIES.include?(target)
@@ -17,6 +17,8 @@ module RuboCop
17
17
  AUTO_GENERATED_FILE = '.rubocop_todo.yml'.freeze
18
18
 
19
19
  class << self
20
+ include ConfigLoaderResolver
21
+
20
22
  attr_accessor :debug, :auto_gen_config
21
23
  attr_writer :root_level # The upwards search is stopped at this level.
22
24
  attr_writer :default_configuration
@@ -34,11 +36,7 @@ module RuboCop
34
36
 
35
37
  resolve_inheritance_from_gems(hash, hash.delete('inherit_gem'))
36
38
  resolve_inheritance(path, hash)
37
-
38
- config_dir = File.dirname(path)
39
- Array(hash.delete('require')).each do |r|
40
- require(File.join(config_dir, r))
41
- end
39
+ resolve_requires(path, hash)
42
40
 
43
41
  hash.delete('inherit_from')
44
42
  config = Config.new(hash, path)
@@ -119,7 +117,7 @@ module RuboCop
119
117
  # that only cops from user configuration are enabled.
120
118
  def merge_with_default(config, config_file)
121
119
  configs =
122
- if config.key?('AllCops') && config['AllCops']['DisabledByDefault']
120
+ if config.for_all_cops['DisabledByDefault']
123
121
  disabled_default = transform(default_configuration) do |params|
124
122
  params.merge('Enabled' => false) # Overwrite with false.
125
123
  end
@@ -161,7 +159,7 @@ module RuboCop
161
159
 
162
160
  def yaml_safe_load(yaml_code)
163
161
  if YAML.respond_to?(:safe_load) # Ruby 2.1+
164
- if defined?(SafeYAML)
162
+ if defined?(SafeYAML) && SafeYAML.respond_to?(:load)
165
163
  SafeYAML.load(yaml_code, nil, whitelisted_tags: %w(!ruby/regexp))
166
164
  else
167
165
  YAML.safe_load(yaml_code, [Regexp])
@@ -171,27 +169,6 @@ module RuboCop
171
169
  end
172
170
  end
173
171
 
174
- def resolve_inheritance(path, hash)
175
- base_configs(path, hash['inherit_from']).reverse_each do |base_config|
176
- base_config.each do |k, v|
177
- hash[k] = hash.key?(k) ? merge(v, hash[k]) : v if v.is_a?(Hash)
178
- end
179
- end
180
- end
181
-
182
- def resolve_inheritance_from_gems(hash, gems)
183
- (gems || {}).each_pair do |gem_name, config_path|
184
- if gem_name == 'rubocop'
185
- raise ArgumentError,
186
- "can't inherit configuration from the rubocop gem"
187
- end
188
-
189
- hash['inherit_from'] = Array(hash['inherit_from'])
190
- # Put gem configuration first so local configuration overrides it.
191
- hash['inherit_from'].unshift gem_config_path(gem_name, config_path)
192
- end
193
- end
194
-
195
172
  def gem_config_path(gem_name, relative_config_path)
196
173
  spec = Gem::Specification.find_by_name(gem_name)
197
174
  return File.join(spec.gem_dir, relative_config_path)
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'yaml'
5
+ require 'pathname'
6
+
7
+ module RuboCop
8
+ # A mixin to break up ConfigLoader
9
+ module ConfigLoaderResolver
10
+ def resolve_requires(path, hash)
11
+ config_dir = File.dirname(path)
12
+ Array(hash.delete('require')).each do |r|
13
+ if r.start_with?('.')
14
+ require(File.join(config_dir, r))
15
+ else
16
+ require(r)
17
+ end
18
+ end
19
+ end
20
+
21
+ def resolve_inheritance(path, hash)
22
+ base_configs(path, hash['inherit_from']).reverse_each do |base_config|
23
+ base_config.each do |k, v|
24
+ hash[k] = hash.key?(k) ? merge(v, hash[k]) : v if v.is_a?(Hash)
25
+ end
26
+ end
27
+ end
28
+
29
+ def resolve_inheritance_from_gems(hash, gems)
30
+ (gems || {}).each_pair do |gem_name, config_path|
31
+ if gem_name == 'rubocop'
32
+ raise ArgumentError,
33
+ "can't inherit configuration from the rubocop gem"
34
+ end
35
+
36
+ hash['inherit_from'] = Array(hash['inherit_from'])
37
+ # Put gem configuration first so local configuration overrides it.
38
+ hash['inherit_from'].unshift gem_config_path(gem_name, config_path)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -138,18 +138,17 @@ module RuboCop
138
138
 
139
139
  def display_cop_names?
140
140
  debug? || @options[:display_cop_names] ||
141
- config['AllCops'] && config['AllCops']['DisplayCopNames']
141
+ @config.for_all_cops['DisplayCopNames']
142
142
  end
143
143
 
144
144
  def display_style_guide?
145
145
  (style_guide_url || reference_url) &&
146
146
  (@options[:display_style_guide] ||
147
- config['AllCops'] && config['AllCops']['DisplayStyleGuide'])
147
+ config.for_all_cops['DisplayStyleGuide'])
148
148
  end
149
149
 
150
150
  def extra_details?
151
- @options[:extra_details] ||
152
- config['AllCops'] && config['AllCops']['ExtraDetails']
151
+ @options[:extra_details] || config.for_all_cops['ExtraDetails']
153
152
  end
154
153
 
155
154
  def message(_node = nil)
@@ -194,7 +193,7 @@ module RuboCop
194
193
  end
195
194
 
196
195
  def target_ruby_version
197
- @config['AllCops'] && @config['AllCops']['TargetRubyVersion']
196
+ @config.for_all_cops['TargetRubyVersion']
198
197
  end
199
198
 
200
199
  def parse(source, path = nil)
@@ -34,7 +34,7 @@ module RuboCop
34
34
  traverse_node(condition, ASGN_TYPES) do |asgn_node|
35
35
  if asgn_node.type == :send
36
36
  _receiver, method_name, *_args = *asgn_node
37
- next :skip_children if method_name != :[]=
37
+ next :skip_children if method_name !~ /=\z/
38
38
  end
39
39
 
40
40
  # skip safe assignment nodes if safe assignment is allowed
@@ -11,14 +11,14 @@ module RuboCop
11
11
  # "result is #{10}"
12
12
  class LiteralInInterpolation < Cop
13
13
  MSG = 'Literal interpolation detected.'.freeze
14
+ COMPOSITE = [:array, :hash, :pair, :irange, :erange].freeze
14
15
 
15
16
  def on_dstr(node)
16
17
  node.children.select { |n| n.type == :begin }.each do |begin_node|
17
18
  final_node = begin_node.children.last
18
19
  next unless final_node
19
20
  next if special_keyword?(final_node)
20
- next if final_node.xstr_type?
21
- next unless final_node.literal?
21
+ next unless prints_as_self?(final_node)
22
22
 
23
23
  add_offense(final_node, :expression)
24
24
  end
@@ -58,6 +58,13 @@ module RuboCop
58
58
  node.loc.begin.end_pos,
59
59
  end_pos).source
60
60
  end
61
+
62
+ # Does node print its own source when converted to a string?
63
+ def prints_as_self?(node)
64
+ node.basic_literal? ||
65
+ (COMPOSITE.include?(node.type) &&
66
+ node.children.all? { |child| prints_as_self?(child) })
67
+ end
61
68
  end
62
69
  end
63
70
  end
@@ -20,14 +20,15 @@ module RuboCop
20
20
  return false if body_has_comment?(body)
21
21
  return false if end_keyword_has_comment?(node)
22
22
 
23
+ length_in_modifier_form(node, cond, body_length) <= max_line_length
24
+ end
25
+
26
+ def length_in_modifier_form(node, cond, body_length)
23
27
  indentation = node.loc.keyword.column
24
28
  kw_length = node.loc.keyword.size
25
29
  cond_length = cond.source_range.size
26
30
  space = 1
27
- total = indentation + body_length + space + kw_length + space +
28
- cond_length
29
-
30
- total <= max_line_length
31
+ indentation + body_length + space + kw_length + space + cond_length
31
32
  end
32
33
 
33
34
  def max_line_length
@@ -101,7 +101,7 @@ module RuboCop
101
101
  def splat_offenses(when_conditions)
102
102
  found_non_splat = false
103
103
  when_conditions.reverse.each_with_object([]) do |condition, result|
104
- found_non_splat ||= true if error_condition?(condition)
104
+ found_non_splat ||= error_condition?(condition)
105
105
 
106
106
  next unless condition.splat_type?
107
107
  result << condition if found_non_splat
@@ -25,6 +25,19 @@ module RuboCop
25
25
  # [1, 2, 3].count { |e| e < 2 && e.even? }
26
26
  # Model.select('field AS field_one').count
27
27
  # Model.select(:value).count
28
+ #
29
+ # `ActiveRecord` compatibility:
30
+ # `ActiveRecord` will ignore the block that is passed to `count`.
31
+ # Other methods, such as `select`, will convert the association to an
32
+ # array and then run the block on the array. A simple work around to
33
+ # make `count` work with a block is to call `to_a.count {...}`.
34
+ #
35
+ # Example:
36
+ # Model.where(id: [1, 2, 3].select { |m| m.method == true }.size
37
+ #
38
+ # becomes:
39
+ #
40
+ # Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }
28
41
  class Count < Cop
29
42
  MSG = 'Use `count` instead of `%s...%s`.'.freeze
30
43
 
@@ -32,6 +45,7 @@ module RuboCop
32
45
  COUNTERS = [:count, :length, :size].freeze
33
46
 
34
47
  def on_send(node)
48
+ return unless should_run?
35
49
  selector, selector_loc, params, counter = parse(node)
36
50
  return unless COUNTERS.include?(counter)
37
51
  return unless SELECTORS.include?(selector)
@@ -62,6 +76,12 @@ module RuboCop
62
76
 
63
77
  private
64
78
 
79
+ def should_run?
80
+ !(cop_config['SafeMode'.freeze] ||
81
+ config['Rails'.freeze] &&
82
+ config['Rails'.freeze]['Enabled'.freeze])
83
+ end
84
+
65
85
  def parse(node)
66
86
  left, counter = *node
67
87
  expression, selector, params = *left