rubocop 0.84.0 → 0.85.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +33 -15
  4. data/lib/rubocop.rb +6 -0
  5. data/lib/rubocop/cli.rb +2 -2
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
  7. data/lib/rubocop/comment_config.rb +1 -1
  8. data/lib/rubocop/config.rb +3 -1
  9. data/lib/rubocop/config_loader.rb +1 -1
  10. data/lib/rubocop/config_loader_resolver.rb +18 -2
  11. data/lib/rubocop/config_store.rb +12 -2
  12. data/lib/rubocop/cop/bundler/gem_comment.rb +70 -1
  13. data/lib/rubocop/cop/commissioner.rb +0 -21
  14. data/lib/rubocop/cop/cop.rb +14 -6
  15. data/lib/rubocop/cop/corrector.rb +3 -1
  16. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +1 -1
  17. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +3 -1
  18. data/lib/rubocop/cop/layout/case_indentation.rb +3 -3
  19. data/lib/rubocop/cop/layout/class_structure.rb +19 -16
  20. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -2
  21. data/lib/rubocop/cop/layout/end_of_line.rb +2 -2
  22. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  23. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +1 -1
  24. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
  25. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -6
  26. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  27. data/lib/rubocop/cop/layout/heredoc_indentation.rb +19 -102
  28. data/lib/rubocop/cop/layout/line_length.rb +17 -17
  29. data/lib/rubocop/cop/layout/multiline_block_layout.rb +3 -1
  30. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  31. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +3 -1
  32. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +3 -1
  33. data/lib/rubocop/cop/lint/ambiguous_operator.rb +2 -1
  34. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +8 -4
  35. data/lib/rubocop/cop/lint/ensure_return.rb +1 -1
  36. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -1
  37. data/lib/rubocop/cop/lint/loop.rb +1 -1
  38. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +62 -0
  39. data/lib/rubocop/cop/lint/nested_percent_literal.rb +1 -1
  40. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +7 -7
  41. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -1
  42. data/lib/rubocop/cop/lint/redundant_require_statement.rb +3 -3
  43. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  44. data/lib/rubocop/cop/lint/suppressed_exception.rb +4 -2
  45. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -1
  46. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +1 -1
  47. data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -1
  48. data/lib/rubocop/cop/migration/department_name.rb +9 -5
  49. data/lib/rubocop/cop/mixin/array_min_size.rb +3 -1
  50. data/lib/rubocop/cop/mixin/check_line_breakable.rb +3 -1
  51. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -1
  52. data/lib/rubocop/cop/mixin/ignored_pattern.rb +1 -1
  53. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  54. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +3 -1
  55. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +16 -0
  56. data/lib/rubocop/cop/mixin/surrounding_space.rb +3 -1
  57. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +1 -1
  58. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +11 -1
  59. data/lib/rubocop/cop/naming/file_name.rb +26 -11
  60. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  61. data/lib/rubocop/cop/registry.rb +3 -1
  62. data/lib/rubocop/cop/style/array_join.rb +1 -1
  63. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -1
  64. data/lib/rubocop/cop/style/copyright.rb +2 -2
  65. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  66. data/lib/rubocop/cop/style/exponential_notation.rb +3 -3
  67. data/lib/rubocop/cop/style/format_string_token.rb +2 -3
  68. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -2
  69. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -1
  70. data/lib/rubocop/cop/style/hash_syntax.rb +5 -3
  71. data/lib/rubocop/cop/style/inline_comment.rb +1 -1
  72. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  73. data/lib/rubocop/cop/style/negated_if.rb +3 -3
  74. data/lib/rubocop/cop/style/negated_unless.rb +3 -3
  75. data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
  76. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  77. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +89 -0
  78. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +130 -0
  79. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  80. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +3 -3
  81. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +3 -3
  82. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +13 -13
  83. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +3 -3
  84. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  85. data/lib/rubocop/cop/style/unless_else.rb +1 -1
  86. data/lib/rubocop/cop/style/when_then.rb +1 -1
  87. data/lib/rubocop/cop/team.rb +61 -25
  88. data/lib/rubocop/cop/util.rb +1 -1
  89. data/lib/rubocop/cop/variable_force/branch.rb +3 -1
  90. data/lib/rubocop/formatter/junit_formatter.rb +14 -4
  91. data/lib/rubocop/magic_comment.rb +1 -1
  92. data/lib/rubocop/options.rb +17 -3
  93. data/lib/rubocop/result_cache.rb +4 -4
  94. data/lib/rubocop/rspec/cop_helper.rb +2 -23
  95. data/lib/rubocop/rspec/expect_offense.rb +42 -6
  96. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  97. data/lib/rubocop/runner.rb +14 -10
  98. data/lib/rubocop/target_finder.rb +3 -1
  99. data/lib/rubocop/target_ruby.rb +4 -1
  100. data/lib/rubocop/version.rb +1 -1
  101. metadata +21 -3
@@ -49,25 +49,36 @@ module RuboCop
49
49
 
50
50
  def for_bad_filename(file_path)
51
51
  basename = File.basename(file_path)
52
- msg = if filename_good?(basename)
53
- return if matching_definition?(file_path)
54
52
 
55
- no_definition_message(basename, file_path)
56
- else
57
- return if bad_filename_allowed?
53
+ if filename_good?(basename)
54
+ msg = perform_class_and_module_naming_checks(file_path, basename)
55
+ else
56
+ msg = other_message(basename) unless bad_filename_allowed?
57
+ end
58
58
 
59
- other_message(basename)
60
- end
59
+ yield source_range(processed_source.buffer, 1, 0), msg if msg
60
+ end
61
61
 
62
- yield source_range(processed_source.buffer, 1, 0), msg
62
+ def perform_class_and_module_naming_checks(file_path, basename)
63
+ return unless expect_matching_definition?
64
+
65
+ if check_definition_path_hierarchy? &&
66
+ !matching_definition?(file_path)
67
+ msg = no_definition_message(basename, file_path)
68
+ elsif !matching_class?(basename)
69
+ msg = no_definition_message(basename, basename)
70
+ end
71
+ msg
63
72
  end
64
73
 
65
74
  def matching_definition?(file_path)
66
- return true unless expect_matching_definition?
67
-
68
75
  find_class_or_module(processed_source.ast, to_namespace(file_path))
69
76
  end
70
77
 
78
+ def matching_class?(file_name)
79
+ find_class_or_module(processed_source.ast, to_namespace(file_name))
80
+ end
81
+
71
82
  def bad_filename_allowed?
72
83
  ignore_executable_scripts? && processed_source.start_with?('#!')
73
84
  end
@@ -94,6 +105,10 @@ module RuboCop
94
105
  cop_config['ExpectMatchingDefinition']
95
106
  end
96
107
 
108
+ def check_definition_path_hierarchy?
109
+ cop_config['CheckDefinitionPathHierarchy']
110
+ end
111
+
97
112
  def regex
98
113
  cop_config['Regex']
99
114
  end
@@ -104,7 +119,7 @@ module RuboCop
104
119
 
105
120
  def filename_good?(basename)
106
121
  basename = basename.sub(/^\./, '')
107
- basename = basename.sub(/\.[^\.]+$/, '')
122
+ basename = basename.sub(/\.[^.]+$/, '')
108
123
  # special handling for Action Pack Variants file names like
109
124
  # some_file.xlsx+mobile.axlsx
110
125
  basename = basename.sub('+', '_')
@@ -67,7 +67,7 @@ module RuboCop
67
67
  private
68
68
 
69
69
  def allowed_method_name?(method_name, prefix)
70
- !method_name.match(/^#{prefix}[^0-9]/) ||
70
+ !method_name.match?(/^#{prefix}[^0-9]/) ||
71
71
  method_name == expected_name(method_name, prefix) ||
72
72
  method_name.end_with?('=') ||
73
73
  allowed_methods.include?(method_name)
@@ -112,7 +112,9 @@ module RuboCop
112
112
 
113
113
  def print_warning(name, path)
114
114
  message = "#{path}: Warning: no department given for #{name}."
115
- message += ' Run `rubocop -a --only Migration/DepartmentName` to fix.' if path.end_with?('.rb')
115
+ if path.end_with?('.rb')
116
+ message += ' Run `rubocop -a --only Migration/DepartmentName` to fix.'
117
+ end
116
118
  warn message
117
119
  end
118
120
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # This cop checks for uses of "\*" as a substitute for *join*.
6
+ # This cop checks for uses of "*" as a substitute for _join_.
7
7
  #
8
8
  # Not all cases can reliably checked, due to Ruby's dynamic
9
9
  # types, so we consider only cases when the first argument is an
@@ -64,7 +64,7 @@ module RuboCop
64
64
  end
65
65
 
66
66
  def requires_bare_percent?(source)
67
- style == :bare_percent && source =~ /^%Q/
67
+ style == :bare_percent && source.start_with?('%Q')
68
68
  end
69
69
 
70
70
  def add_offense_for_wrong_style(node, good, bad)
@@ -8,8 +8,8 @@ module RuboCop
8
8
  # The default regexp for an acceptable copyright notice can be found in
9
9
  # config/default.yml. The default can be changed as follows:
10
10
  #
11
- # Style/Copyright:
12
- # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
11
+ # Style/Copyright:
12
+ # Notice: '^Copyright (\(c\) )?2\d{3} Acme Inc'
13
13
  #
14
14
  # This regex string is treated as an unanchored regex. For each file
15
15
  # that RuboCop scans, a comment that matches this regex must be found or
@@ -8,7 +8,7 @@ module RuboCop
8
8
  # line (compact style), but it can be configured to enforce the `end`
9
9
  # to go on its own line (expanded style).
10
10
  #
11
- # Note: A method definition is not considered empty if it contains
11
+ # NOTE: A method definition is not considered empty if it contains
12
12
  # comments.
13
13
  #
14
14
  # @example EnforcedStyle: compact (default)
@@ -5,12 +5,12 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces consistency when using exponential notation
7
7
  # for numbers in the code (eg 1.2e4). Different styles are supported:
8
- # - `scientific` which enforces a mantissa between 1 (inclusive)
8
+ # * `scientific` which enforces a mantissa between 1 (inclusive)
9
9
  # and 10 (exclusive).
10
- # - `engineering` which enforces the exponent to be a multiple of 3
10
+ # * `engineering` which enforces the exponent to be a multiple of 3
11
11
  # and the mantissa to be between 0.1 (inclusive)
12
12
  # and 10 (exclusive).
13
- # - `integral` which enforces the mantissa to always be a whole number
13
+ # * `integral` which enforces the mantissa to always be a whole number
14
14
  # without trailing zeroes.
15
15
  #
16
16
  # @example EnforcedStyle: scientific (default)
@@ -5,11 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # Use a consistent style for named format string tokens.
7
7
  #
8
- # **Note:**
9
- # `unannotated` style cop only works for strings
8
+ # NOTE: `unannotated` style cop only works for strings
10
9
  # which are passed as arguments to those methods:
11
10
  # `printf`, `sprintf`, `format`, `%`.
12
- # The reason is that *unannotated* format is very similar
11
+ # The reason is that _unannotated_ format is very similar
13
12
  # to encoded URLs or Date/Time formatting strings.
14
13
  #
15
14
  # @example EnforcedStyle: annotated (default)
@@ -8,8 +8,7 @@ module RuboCop
8
8
  # It will add the comment `# frozen_string_literal: true` to the top of
9
9
  # files to enable frozen string literals. Frozen string literals may be
10
10
  # default in future Ruby. The comment will be added below a shebang and
11
- # encoding comment. The frozen string literal comment is only valid in
12
- # Ruby 2.3+.
11
+ # encoding comment.
13
12
  #
14
13
  # Note that the cop will ignore files where the comment exists but is set
15
14
  # to `false` instead of `true`.
@@ -5,7 +5,7 @@ module RuboCop
5
5
  module Style
6
6
  # This cop checks for uses of `each_key` and `each_value` Hash methods.
7
7
  #
8
- # Note: If you have an array of two-element arrays, you can put
8
+ # NOTE: If you have an array of two-element arrays, you can put
9
9
  # parentheses around the block arguments to indicate that you're not
10
10
  # working with a hash, and suppress RuboCop offenses.
11
11
  #
@@ -13,11 +13,11 @@ module RuboCop
13
13
  # The supported styles are:
14
14
  #
15
15
  # * ruby19 - forces use of the 1.9 syntax (e.g. `{a: 1}`) when hashes have
16
- # all symbols for keys
16
+ # all symbols for keys
17
17
  # * hash_rockets - forces use of hash rockets for all hashes
18
18
  # * no_mixed_keys - simply checks for hashes with mixed syntaxes
19
19
  # * ruby19_no_mixed_keys - forces use of ruby 1.9 syntax and forbids mixed
20
- # syntax hashes
20
+ # syntax hashes
21
21
  #
22
22
  # @example EnforcedStyle: ruby19 (default)
23
23
  # # bad
@@ -178,7 +178,9 @@ module RuboCop
178
178
  )
179
179
 
180
180
  hash_node = pair_node.parent
181
- corrector.wrap(hash_node, '{', '}') if hash_node.parent&.return_type? && !hash_node.braces?
181
+ return unless hash_node.parent&.return_type? && !hash_node.braces?
182
+
183
+ corrector.wrap(hash_node, '{', '}')
182
184
  end
183
185
 
184
186
  def range_for_autocorrect_ruby19(pair_node)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  def investigate(processed_source)
24
24
  processed_source.each_comment do |comment|
25
25
  next if comment_line?(processed_source[comment.loc.line - 1]) ||
26
- comment.text.match(/\A# rubocop:(enable|disable)/)
26
+ comment.text.match?(/\A# rubocop:(enable|disable)/)
27
27
 
28
28
  add_offense(comment)
29
29
  end
@@ -82,7 +82,7 @@ module RuboCop
82
82
  end
83
83
 
84
84
  def keyword_end_str(node, node_buf)
85
- if /[^\s\)]/.match?(node_buf.source_line(node.loc.end.line))
85
+ if /[^\s)]/.match?(node_buf.source_line(node.loc.end.line))
86
86
  "\n" + (' ' * node.loc.column) + 'end'
87
87
  else
88
88
  'end'
@@ -6,9 +6,9 @@ module RuboCop
6
6
  # Checks for uses of if with a negated condition. Only ifs
7
7
  # without else are considered. There are three different styles:
8
8
  #
9
- # - both
10
- # - prefix
11
- # - postfix
9
+ # * both
10
+ # * prefix
11
+ # * postfix
12
12
  #
13
13
  # @example EnforcedStyle: both (default)
14
14
  # # enforces `unless` for `prefix` and `postfix` conditionals
@@ -6,9 +6,9 @@ module RuboCop
6
6
  # Checks for uses of unless with a negated condition. Only unless
7
7
  # without else are considered. There are three different styles:
8
8
  #
9
- # - both
10
- # - prefix
11
- # - postfix
9
+ # * both
10
+ # * prefix
11
+ # * postfix
12
12
  #
13
13
  # @example EnforcedStyle: both (default)
14
14
  # # enforces `if` for `prefix` and `postfix` conditionals
@@ -11,7 +11,7 @@ module RuboCop
11
11
  #
12
12
  # With `IncludeSemanticChanges` set to `true`, this cop reports offenses
13
13
  # for `!x.nil?` and autocorrects that and `x != nil` to solely `x`, which
14
- # is **usually** OK, but might change behavior.
14
+ # is *usually* OK, but might change behavior.
15
15
  #
16
16
  # @example
17
17
  # # bad
@@ -27,7 +27,7 @@ module RuboCop
27
27
  EMPTY = ''
28
28
  PERCENT_Q = '%q'
29
29
  PERCENT_CAPITAL_Q = '%Q'
30
- STRING_INTERPOLATION_REGEXP = /#\{.+}/.freeze
30
+ STRING_INTERPOLATION_REGEXP = /#\{.+\}/.freeze
31
31
  ESCAPED_NON_BACKSLASH = /\\[^\\]/.freeze
32
32
 
33
33
  def on_dstr(node)
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for unnecessary single-element Regexp character classes.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # r = /[x]/
12
+ #
13
+ # # good
14
+ # r = /x/
15
+ #
16
+ # # bad
17
+ # r = /[\s]/
18
+ #
19
+ # # good
20
+ # r = /\s/
21
+ #
22
+ # # good
23
+ # r = /[ab]/
24
+ class RedundantRegexpCharacterClass < Cop
25
+ include MatchRange
26
+ include RegexpLiteralHelp
27
+
28
+ MSG_REDUNDANT_CHARACTER_CLASS = 'Redundant single-element character class, ' \
29
+ '`%<char_class>s` can be replaced with `%<element>s`.'
30
+
31
+ PATTERN = /
32
+ (
33
+ (?<!\\) # No \-prefix (i.e. not escaped)
34
+ \[ # Literal [
35
+ (?!\#\{) # Not (the start of) an interpolation
36
+ (?: # Either...
37
+ \\. | # Any escaped character
38
+ [^.*+?{}()|$] | # or one that doesn't require escaping outside the character class
39
+ \\[upP]\{[^}]+\} # or a unicode code-point or property
40
+ )
41
+ \] # Literal ]
42
+ )
43
+ /x.freeze
44
+
45
+ def on_regexp(node)
46
+ each_redundant_character_class(node) do |loc|
47
+ next if whitespace_in_free_space_mode?(node, loc)
48
+
49
+ add_offense(
50
+ node,
51
+ location: loc,
52
+ message: format(
53
+ MSG_REDUNDANT_CHARACTER_CLASS,
54
+ char_class: loc.source,
55
+ element: without_character_class(loc)
56
+ )
57
+ )
58
+ end
59
+ end
60
+
61
+ def autocorrect(node)
62
+ lambda do |corrector|
63
+ each_redundant_character_class(node) do |loc|
64
+ corrector.replace(loc, without_character_class(loc))
65
+ end
66
+ end
67
+ end
68
+
69
+ def each_redundant_character_class(node)
70
+ each_match_range(node.loc.expression, PATTERN) do |loc|
71
+ yield loc
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def without_character_class(loc)
78
+ loc.source[1..-2]
79
+ end
80
+
81
+ def whitespace_in_free_space_mode?(node, loc)
82
+ return false unless freespace_mode_regexp?(node)
83
+
84
+ /\[\s\]/.match?(loc.source)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for redundant escapes inside Regexp literals.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # %r{foo\/bar}
11
+ #
12
+ # # good
13
+ # %r{foo/bar}
14
+ #
15
+ # # good
16
+ # /foo\/bar/
17
+ #
18
+ # # good
19
+ # %r/foo\/bar/
20
+ #
21
+ # # bad
22
+ # /a\-b/
23
+ #
24
+ # # good
25
+ # /a-b/
26
+ #
27
+ # # bad
28
+ # /[\+\-]\d/
29
+ #
30
+ # # good
31
+ # /[+\-]\d/
32
+ class RedundantRegexpEscape < Cop
33
+ include RangeHelp
34
+ include RegexpLiteralHelp
35
+
36
+ MSG_REDUNDANT_ESCAPE = 'Redundant escape inside regexp literal'
37
+
38
+ ALLOWED_ALWAYS_ESCAPES = ' []^\\#'.chars.freeze
39
+ ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES = '-'.chars.freeze
40
+ ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES = '.*+?{}()|$'.chars.freeze
41
+
42
+ def on_regexp(node)
43
+ each_escape(node) do |char, index, within_character_class|
44
+ next if allowed_escape?(node, char, within_character_class)
45
+
46
+ add_offense(
47
+ node,
48
+ location: escape_range_at_index(node, index),
49
+ message: MSG_REDUNDANT_ESCAPE
50
+ )
51
+ end
52
+ end
53
+
54
+ def autocorrect(node)
55
+ lambda do |corrector|
56
+ each_escape(node) do |char, index, within_character_class|
57
+ next if allowed_escape?(node, char, within_character_class)
58
+
59
+ corrector.remove_leading(escape_range_at_index(node, index), 1)
60
+ end
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def slash_literal?(node)
67
+ ['/', '%r/'].include?(node.loc.begin.source)
68
+ end
69
+
70
+ def allowed_escape?(node, char, within_character_class)
71
+ # Strictly speaking a few single-letter metachars are currently
72
+ # unnecessary to "escape", e.g. g, i, E, F, but enumerating them is
73
+ # rather difficult, and their behaviour could change over time with
74
+ # different versions of Ruby so that e.g. /\g/ != /g/
75
+ return true if /[[:alnum:]]/.match?(char)
76
+ return true if ALLOWED_ALWAYS_ESCAPES.include?(char)
77
+
78
+ if char == '/'
79
+ slash_literal?(node)
80
+ elsif within_character_class
81
+ ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
82
+ else
83
+ ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
84
+ end
85
+ end
86
+
87
+ def each_escape(node)
88
+ pattern_source(node).each_char.with_index.reduce(
89
+ [nil, false]
90
+ ) do |(previous, within_character_class), (current, index)|
91
+ if previous == '\\'
92
+ yield [current, index - 1, within_character_class]
93
+
94
+ [nil, within_character_class]
95
+ elsif previous == '[' && current != ':'
96
+ [current, true]
97
+ elsif previous != ':' && current == ']'
98
+ [current, false]
99
+ else
100
+ [current, within_character_class]
101
+ end
102
+ end
103
+ end
104
+
105
+ def escape_range_at_index(node, index)
106
+ regexp_begin = node.loc.begin.end_pos
107
+
108
+ start = regexp_begin + index
109
+
110
+ range_between(start, start + 2)
111
+ end
112
+
113
+ def pattern_source(node)
114
+ freespace_mode = freespace_mode_regexp?(node)
115
+
116
+ node.children.reject(&:regopt_type?).map do |child|
117
+ source = child.source
118
+
119
+ if freespace_mode
120
+ # Remove comments to avoid misleading results
121
+ source.sub(/(?<!\\)#.*/, '')
122
+ else
123
+ source
124
+ end
125
+ end.join
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end