rubocop 0.92.0 → 0.93.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +30 -0
  4. data/lib/rubocop.rb +3 -1
  5. data/lib/rubocop/cached_data.rb +2 -1
  6. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  7. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
  8. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  9. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -7
  10. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  11. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -11
  12. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  13. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  14. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  15. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  16. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  17. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  18. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  19. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  20. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  21. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  22. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  23. data/lib/rubocop/cop/offense.rb +15 -2
  24. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  25. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  26. data/lib/rubocop/cop/style/case_like_if.rb +20 -4
  27. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  28. data/lib/rubocop/cop/style/combinable_loops.rb +8 -1
  29. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  30. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  31. data/lib/rubocop/cop/style/for.rb +0 -4
  32. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  33. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  34. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  35. data/lib/rubocop/cop/style/raise_args.rb +0 -3
  36. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  37. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  38. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  39. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +39 -24
  40. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  41. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  42. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  43. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  44. data/lib/rubocop/ext/regexp_node.rb +20 -4
  45. data/lib/rubocop/result_cache.rb +8 -2
  46. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  47. data/lib/rubocop/runner.rb +4 -4
  48. data/lib/rubocop/target_finder.rb +23 -25
  49. data/lib/rubocop/version.rb +1 -1
  50. metadata +11 -9
  51. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
@@ -51,7 +51,12 @@ module RuboCop
51
51
  end
52
52
 
53
53
  def single_variable_interpolation?(node)
54
- node.children.one? && variable_interpolation?(node.children.first)
54
+ return false unless node.children.one?
55
+
56
+ first_child = node.children.first
57
+
58
+ variable_interpolation?(first_child) ||
59
+ first_child.send_type? && !first_child.operator_method?
55
60
  end
56
61
 
57
62
  def interpolation?(node)
@@ -22,32 +22,14 @@ module RuboCop
22
22
  # # good
23
23
  # r = /[ab]/
24
24
  class RedundantRegexpCharacterClass < Base
25
- include MatchRange
26
- include RegexpLiteralHelp
27
25
  extend AutoCorrector
28
26
 
27
+ REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS = '.*+?{}()|$'.chars.freeze
29
28
  MSG_REDUNDANT_CHARACTER_CLASS = 'Redundant single-element character class, ' \
30
29
  '`%<char_class>s` can be replaced with `%<element>s`.'
31
30
 
32
- PATTERN = /
33
- (
34
- (?<!\\) # No \-prefix (i.e. not escaped)
35
- \[ # Literal [
36
- (?!\#\{) # Not (the start of) an interpolation
37
- (?: # Either...
38
- \\[^b] | # Any escaped character except b (which would change behaviour)
39
- [^.*+?{}()|$] | # or one that doesn't require escaping outside the character class
40
- \\[upP]\{[^}]+\} # or a unicode code-point or property
41
- )
42
- (?<!\\) # No \-prefix (i.e. not escaped)
43
- \] # Literal ]
44
- )
45
- /x.freeze
46
-
47
31
  def on_regexp(node)
48
32
  each_redundant_character_class(node) do |loc|
49
- next if whitespace_in_free_space_mode?(node, loc)
50
-
51
33
  add_offense(
52
34
  loc, message: format(
53
35
  MSG_REDUNDANT_CHARACTER_CLASS,
@@ -63,19 +45,52 @@ module RuboCop
63
45
  private
64
46
 
65
47
  def each_redundant_character_class(node)
66
- pattern_source(node).scan(PATTERN) do
67
- yield match_range(node.loc.begin.end, Regexp.last_match)
48
+ each_single_element_character_class(node) do |char_class|
49
+ next unless redundant_single_element_character_class?(node, char_class)
50
+
51
+ yield node.loc.begin.adjust(begin_pos: 1 + char_class.ts, end_pos: char_class.te)
52
+ end
53
+ end
54
+
55
+ def each_single_element_character_class(node)
56
+ node.parsed_tree&.each_expression do |expr|
57
+ next if expr.type != :set || expr.expressions.size != 1
58
+ next if expr.negative?
59
+ next if %i[set posixclass nonposixclass].include?(expr.expressions.first.type)
60
+
61
+ yield expr
68
62
  end
69
63
  end
70
64
 
65
+ def redundant_single_element_character_class?(node, char_class)
66
+ class_elem = char_class.expressions.first.text
67
+
68
+ non_redundant =
69
+ whitespace_in_free_space_mode?(node, class_elem) ||
70
+ backslash_b?(class_elem) ||
71
+ requires_escape_outside_char_class?(class_elem)
72
+
73
+ !non_redundant
74
+ end
75
+
71
76
  def without_character_class(loc)
72
77
  loc.source[1..-2]
73
78
  end
74
79
 
75
- def whitespace_in_free_space_mode?(node, loc)
76
- return false unless freespace_mode_regexp?(node)
80
+ def whitespace_in_free_space_mode?(node, elem)
81
+ return false unless node.extended?
82
+
83
+ /\s/.match?(elem)
84
+ end
85
+
86
+ def backslash_b?(elem)
87
+ # \b's behaviour is different inside and outside of a character class, matching word
88
+ # boundaries outside but backspace (0x08) when inside.
89
+ elem == '\b'
90
+ end
77
91
 
78
- /\[\s\]/.match?(loc.source)
92
+ def requires_escape_outside_char_class?(elem)
93
+ REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS.include?(elem)
79
94
  end
80
95
  end
81
96
  end
@@ -34,7 +34,6 @@ module RuboCop
34
34
  # /[+\-]\d/
35
35
  class RedundantRegexpEscape < Base
36
36
  include RangeHelp
37
- include RegexpLiteralHelp
38
37
  extend AutoCorrector
39
38
 
40
39
  MSG_REDUNDANT_ESCAPE = 'Redundant escape inside regexp literal'
@@ -59,9 +58,9 @@ module RuboCop
59
58
 
60
59
  def allowed_escape?(node, char, within_character_class)
61
60
  # Strictly speaking a few single-letter metachars are currently
62
- # unnecessary to "escape", e.g. g, i, E, F, but enumerating them is
61
+ # unnecessary to "escape", e.g. i, E, F, but enumerating them is
63
62
  # rather difficult, and their behaviour could change over time with
64
- # different versions of Ruby so that e.g. /\g/ != /g/
63
+ # different versions of Ruby so that e.g. /\i/ != /i/
65
64
  return true if /[[:alnum:]]/.match?(char)
66
65
  return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
67
66
 
@@ -82,19 +81,13 @@ module RuboCop
82
81
  end
83
82
 
84
83
  def each_escape(node)
85
- pattern_source(node).each_char.with_index.reduce(
86
- [nil, 0]
87
- ) do |(previous, char_class_depth), (current, index)|
88
- if previous == '\\'
89
- yield [current, index - 1, !char_class_depth.zero?]
90
-
91
- [nil, char_class_depth]
92
- elsif previous == '['
93
- [current, char_class_depth + 1]
94
- elsif current == ']'
95
- [current, char_class_depth - 1]
84
+ node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
85
+ yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
86
+
87
+ if expr.type == :set
88
+ char_class_depth + (event == :enter ? 1 : -1)
96
89
  else
97
- [current, char_class_depth]
90
+ char_class_depth
98
91
  end
99
92
  end
100
93
  end
@@ -87,7 +87,7 @@ module RuboCop
87
87
  if single_quoted?(part)
88
88
  part.value.gsub('\\') { '\\\\' }
89
89
  else
90
- escape_string(part.value)
90
+ part.value.inspect[1..-2]
91
91
  end
92
92
  else
93
93
  "\#{#{part.source}}"
@@ -192,7 +192,7 @@ module RuboCop
192
192
  end
193
193
 
194
194
  def_node_matcher :method_name, <<~PATTERN
195
- {($:defined? (send nil? _) ...)
195
+ {($:defined? _ ...)
196
196
  (send {_ nil?} $_ _ ...)}
197
197
  PATTERN
198
198
 
@@ -20,8 +20,6 @@ module RuboCop
20
20
  nil
21
21
  end
22
22
 
23
- # rubocop:disable Metrics/BlockLength
24
-
25
23
  # Abstract base class for branch classes.
26
24
  # A branch represents a conditional branch in a scope.
27
25
  #
@@ -42,8 +40,6 @@ module RuboCop
42
40
  # do_something # no branch
43
41
  # end
44
42
  Base = Struct.new(:child_node, :scope) do
45
- # rubocop:enable Metrics/BlockLength
46
-
47
43
  def self.classes
48
44
  @classes ||= []
49
45
  end
@@ -17,11 +17,9 @@ module RuboCop
17
17
 
18
18
  # @return [Regexp::Expression::Root, nil]
19
19
  def parsed_tree
20
- return if interpolation?
21
-
22
- str = content
20
+ str = with_interpolations_blanked
23
21
  Ext::RegexpNode.parsed_cache[str] ||= begin
24
- Regexp::Parser.parse(str)
22
+ Regexp::Parser.parse(str, options: options)
25
23
  rescue StandardError
26
24
  nil
27
25
  end
@@ -40,6 +38,24 @@ module RuboCop
40
38
  self
41
39
  end
42
40
 
41
+ private
42
+
43
+ def with_interpolations_blanked
44
+ # Ignore the trailing regopt node
45
+ children[0...-1].map do |child|
46
+ source = child.source
47
+
48
+ # We don't want to consider the contents of interpolations as part of the pattern source,
49
+ # but need to preserve their width, to allow offsets to correctly line up with the
50
+ # original source: spaces have no effect, and preserve width.
51
+ if child.begin_type?
52
+ ' ' * source.length
53
+ else
54
+ source
55
+ end
56
+ end.join
57
+ end
58
+
43
59
  AST::RegexpNode.include self
44
60
  end
45
61
  end
@@ -95,6 +95,11 @@ module RuboCop
95
95
  context_checksum(team, options),
96
96
  file_checksum(file, config_store))
97
97
  @cached_data = CachedData.new(file)
98
+ @debug = options[:debug]
99
+ end
100
+
101
+ def debug?
102
+ @debug
98
103
  end
99
104
 
100
105
  def valid?
@@ -102,6 +107,7 @@ module RuboCop
102
107
  end
103
108
 
104
109
  def load
110
+ puts "Loading cache from #{@path}" if debug?
105
111
  @cached_data.from_json(IO.read(@path, encoding: Encoding::UTF_8))
106
112
  end
107
113
 
@@ -209,8 +215,8 @@ module RuboCop
209
215
  # The external dependency checksums are cached per RuboCop team so that
210
216
  # the checksums don't need to be recomputed for each file.
211
217
  def team_checksum(team)
212
- @checksum_by_team ||= {}
213
- @checksum_by_team[team.object_id] ||= team.external_dependency_checksum
218
+ @checksum_by_team ||= {}.compare_by_identity
219
+ @checksum_by_team[team] ||= team.external_dependency_checksum
214
220
  end
215
221
 
216
222
  # We combine team and options into a single "context" checksum to avoid
@@ -26,7 +26,7 @@ module CopHelper
26
26
  end
27
27
 
28
28
  def parse_source(source, file = nil)
29
- if file&.respond_to?(:write)
29
+ if file.respond_to?(:write)
30
30
  file.write(source)
31
31
  file.rewind
32
32
  file = file.path
@@ -324,8 +324,8 @@ module RuboCop
324
324
  end
325
325
 
326
326
  def mobilized_cop_classes(config)
327
- @mobilized_cop_classes ||= {}
328
- @mobilized_cop_classes[config.object_id] ||= begin
327
+ @mobilized_cop_classes ||= {}.compare_by_identity
328
+ @mobilized_cop_classes[config] ||= begin
329
329
  cop_classes = Cop::Registry.all
330
330
 
331
331
  OptionsValidator.new(@options).validate_cop_options
@@ -399,8 +399,8 @@ module RuboCop
399
399
  # otherwise dormant team that can be used for config- and option-
400
400
  # level caching in ResultCache.
401
401
  def standby_team(config)
402
- @team_by_config ||= {}
403
- @team_by_config[config.object_id] ||=
402
+ @team_by_config ||= {}.compare_by_identity
403
+ @team_by_config[config] ||=
404
404
  Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
405
405
  end
406
406
  end
@@ -81,34 +81,32 @@ module RuboCop
81
81
  # the top level directories that are excluded in configuration in the
82
82
  # normal way (dir/**/*).
83
83
  def find_files(base_dir, flags)
84
- wanted_toplevel_dirs = toplevel_dirs(base_dir, flags) -
85
- excluded_dirs(base_dir)
86
- wanted_toplevel_dirs.map! { |dir| dir << '/**/*' }
87
-
88
- pattern = if wanted_toplevel_dirs.empty?
89
- # We need this special case to avoid creating the pattern
90
- # /**/* which searches the entire file system.
91
- ["#{base_dir}/**/*"]
92
- else
93
- # Search the non-excluded top directories, but also add files
94
- # on the top level, which would otherwise not be found.
95
- wanted_toplevel_dirs.unshift("#{base_dir}/*")
84
+ # get all wanted directories first to improve speed of finding all files
85
+ exclude_pattern = combined_exclude_glob_patterns(base_dir)
86
+ dir_flags = flags | File::FNM_PATHNAME | File::FNM_EXTGLOB
87
+ patterns = wanted_dir_patterns(base_dir, exclude_pattern, dir_flags)
88
+ patterns.map! { |dir| File.join(dir, '*') }
89
+ # We need this special case to avoid creating the pattern
90
+ # /**/* which searches the entire file system.
91
+ patterns = [File.join(dir, '**/*')] if patterns.empty?
92
+
93
+ Dir.glob(patterns, flags).select { |path| FileTest.file?(path) }
94
+ end
95
+
96
+ def wanted_dir_patterns(base_dir, exclude_pattern, flags)
97
+ dirs = Dir.glob(File.join(base_dir, '*/'), flags)
98
+ .reject do |dir|
99
+ dir.end_with?('/./', '/../') || File.fnmatch?(exclude_pattern, dir, flags)
96
100
  end
97
- Dir.glob(pattern, flags).select { |path| FileTest.file?(path) }
101
+ dirs.flat_map { |dir| wanted_dir_patterns(dir, exclude_pattern, flags) }
102
+ .unshift(base_dir)
98
103
  end
99
104
 
100
- def toplevel_dirs(base_dir, flags)
101
- Dir.glob(File.join(base_dir, '*'), flags).select do |dir|
102
- File.directory?(dir) && !dir.end_with?('/.', '/..')
103
- end
104
- end
105
-
106
- def excluded_dirs(base_dir)
107
- all_cops_config = @config_store.for(base_dir).for_all_cops
108
- dir_tree_excludes = all_cops_config['Exclude'].select do |pattern|
109
- pattern.is_a?(String) && pattern.end_with?('/**/*')
110
- end
111
- dir_tree_excludes.map { |pattern| pattern.sub(%r{/\*\*/\*$}, '') }
105
+ def combined_exclude_glob_patterns(base_dir)
106
+ exclude = @config_store.for(base_dir).for_all_cops['Exclude']
107
+ patterns = exclude.select { |pattern| pattern.is_a?(String) && pattern.end_with?('/**/*') }
108
+ .map { |pattern| pattern.sub("#{base_dir}/", '') }
109
+ "#{base_dir}/{#{patterns.join(',')}}"
112
110
  end
113
111
 
114
112
  def ruby_extension?(file)
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.92.0'
6
+ STRING = '0.93.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.92.0
4
+ version: 0.93.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2020-09-25 00:00:00.000000000 Z
13
+ date: 2020-10-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: parallel
@@ -66,14 +66,14 @@ dependencies:
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: '1.7'
69
+ version: '1.8'
70
70
  type: :runtime
71
71
  prerelease: false
72
72
  version_requirements: !ruby/object:Gem::Requirement
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: '1.7'
76
+ version: '1.8'
77
77
  - !ruby/object:Gem::Dependency
78
78
  name: rexml
79
79
  requirement: !ruby/object:Gem::Requirement
@@ -94,14 +94,14 @@ dependencies:
94
94
  requirements:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
- version: 0.5.0
97
+ version: 0.6.0
98
98
  type: :runtime
99
99
  prerelease: false
100
100
  version_requirements: !ruby/object:Gem::Requirement
101
101
  requirements:
102
102
  - - ">="
103
103
  - !ruby/object:Gem::Version
104
- version: 0.5.0
104
+ version: 0.6.0
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: ruby-progressbar
107
107
  requirement: !ruby/object:Gem::Requirement
@@ -367,6 +367,7 @@ files:
367
367
  - lib/rubocop/cop/lint/float_comparison.rb
368
368
  - lib/rubocop/cop/lint/float_out_of_range.rb
369
369
  - lib/rubocop/cop/lint/format_parameter_mismatch.rb
370
+ - lib/rubocop/cop/lint/hash_compare_by_identity.rb
370
371
  - lib/rubocop/cop/lint/heredoc_method_call_position.rb
371
372
  - lib/rubocop/cop/lint/identity_comparison.rb
372
373
  - lib/rubocop/cop/lint/implicit_string_concatenation.rb
@@ -396,6 +397,7 @@ files:
396
397
  - lib/rubocop/cop/lint/redundant_cop_disable_directive.rb
397
398
  - lib/rubocop/cop/lint/redundant_cop_enable_directive.rb
398
399
  - lib/rubocop/cop/lint/redundant_require_statement.rb
400
+ - lib/rubocop/cop/lint/redundant_safe_navigation.rb
399
401
  - lib/rubocop/cop/lint/redundant_splat_expansion.rb
400
402
  - lib/rubocop/cop/lint/redundant_string_coercion.rb
401
403
  - lib/rubocop/cop/lint/redundant_with_index.rb
@@ -501,7 +503,6 @@ files:
501
503
  - lib/rubocop/cop/mixin/preferred_delimiters.rb
502
504
  - lib/rubocop/cop/mixin/range_help.rb
503
505
  - lib/rubocop/cop/mixin/rational_literal.rb
504
- - lib/rubocop/cop/mixin/regexp_literal_help.rb
505
506
  - lib/rubocop/cop/mixin/rescue_node.rb
506
507
  - lib/rubocop/cop/mixin/safe_assignment.rb
507
508
  - lib/rubocop/cop/mixin/space_after_punctuation.rb
@@ -559,6 +560,7 @@ files:
559
560
  - lib/rubocop/cop/style/character_literal.rb
560
561
  - lib/rubocop/cop/style/class_and_module_children.rb
561
562
  - lib/rubocop/cop/style/class_check.rb
563
+ - lib/rubocop/cop/style/class_equality_comparison.rb
562
564
  - lib/rubocop/cop/style/class_methods.rb
563
565
  - lib/rubocop/cop/style/class_methods_definitions.rb
564
566
  - lib/rubocop/cop/style/class_vars.rb
@@ -803,7 +805,7 @@ metadata:
803
805
  homepage_uri: https://rubocop.org/
804
806
  changelog_uri: https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md
805
807
  source_code_uri: https://github.com/rubocop-hq/rubocop/
806
- documentation_uri: https://docs.rubocop.org/rubocop/0.92/
808
+ documentation_uri: https://docs.rubocop.org/rubocop/0.93/
807
809
  bug_tracker_uri: https://github.com/rubocop-hq/rubocop/issues
808
810
  post_install_message:
809
811
  rdoc_options: []
@@ -820,7 +822,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
820
822
  - !ruby/object:Gem::Version
821
823
  version: '0'
822
824
  requirements: []
823
- rubygems_version: 3.1.2
825
+ rubygems_version: 3.1.4
824
826
  signing_key:
825
827
  specification_version: 4
826
828
  summary: Automatic Ruby code style checking tool.