Module: RuboCop::Cop::Util

Overview

This module contains a collection of useful utility methods. rubocop:disable Metrics/ModuleLength

Constant Summary collapse

LITERAL_REGEX =

Match literal regex characters, not including anchors, character classes, alternatives, groups, repetitions, references, etc

%r{[\w\s\-,"'!#%&<>=;:`~/]|\\[^AbBdDgGhHkpPRwWXsSzZ0-9]}.freeze

Constants included from PathUtil

PathUtil::HIDDEN_FILE_PATTERN

Class Method Summary collapse

Methods included from PathUtil

absolute?, glob?, hidden_dir?, hidden_file?, hidden_file_in_not_hidden_dir?, match_path?, maybe_hidden_file?, relative_path, remote_file?, smart_path

Class Method Details

.add_parentheses(node, corrector) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/rubocop/cop/util.rb', line 39

def add_parentheses(node, corrector)
  if node.args_type?
    arguments_range = node.source_range
    args_with_space = range_with_surrounding_space(arguments_range, side: :left)
    leading_space = range_between(args_with_space.begin_pos, arguments_range.begin_pos)
    corrector.replace(leading_space, '(')
    corrector.insert_after(arguments_range, ')')
  elsif !node.respond_to?(:arguments)
    corrector.wrap(node, '(', ')')
  elsif node.arguments.empty?
    corrector.insert_after(node, '()')
  else
    args_begin = args_begin(node)

    corrector.remove(args_begin)
    corrector.insert_before(args_begin, '(')
    corrector.insert_after(args_end(node), ')')
  end
end

.any_descendant?(node, *types) ⇒ Boolean

rubocop:enable Metrics/AbcSize, Metrics/MethodLength

Returns:

  • (Boolean)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rubocop/cop/util.rb', line 60

def any_descendant?(node, *types)
  if block_given?
    node.each_descendant(*types) do |descendant|
      return true if yield(descendant)
    end
  else
    # Use a block version to avoid allocating enumerators.
    node.each_descendant do # rubocop:disable Lint/UnreachableLoop
      return true
    end
  end

  false
end

.args_begin(node) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/rubocop/cop/util.rb', line 75

def args_begin(node)
  loc = node.loc
  selector = if node.type?(:super, :yield)
               loc.keyword
             elsif node.any_def_type?
               loc.name
             else
               loc.selector
             end
  selector.end.resize(1)
end

.args_end(node) ⇒ Object



87
88
89
# File 'lib/rubocop/cop/util.rb', line 87

def args_end(node)
  node.source_range.end
end

.begins_its_line?(range) ⇒ Boolean

Returns:

  • (Boolean)


108
109
110
111
112
113
114
# File 'lib/rubocop/cop/util.rb', line 108

def begins_its_line?(range)
  if (regex = LINE_BEGINS_REGEX_CACHE[range.column])
    range.source_line.match?(regex)
  else
    range.source_line.index(/\S/) == range.column
  end
end

.comment_line?(line_source) ⇒ Boolean

This is a bad API

Returns:

  • (Boolean)


17
18
19
# File 'lib/rubocop/cop/util.rb', line 17

def comment_line?(line_source)
  /^\s*#/.match?(line_source)
end

.comment_lines?(node) ⇒ Boolean

Deprecated.

Use ProcessedSource#line_with_comment?, contains_comment? or similar

Returns:

  • (Boolean)


22
23
24
25
26
27
28
# File 'lib/rubocop/cop/util.rb', line 22

def comment_lines?(node)
  warn Rainbow("    `comment_lines?` is deprecated. Use `ProcessedSource#line_with_comment?`, `contains_comment?` or similar instead.\n  WARNING\n\n  processed_source[line_range(node)].any? { |line| comment_line?(line) }\nend\n").yellow, uplevel: 1

.double_quotes_required?(string) ⇒ Boolean

If converting a string to Ruby string literal source code, must double quotes be used?

Returns:

  • (Boolean)


134
135
136
137
138
139
140
141
142
# File 'lib/rubocop/cop/util.rb', line 134

def double_quotes_required?(string)
  # Double quotes are required for strings which either:
  # - Contain single quotes
  # - Contain non-printable characters, which must use an escape

  # Regex matches IF there is a ' or there is a \\ in the string that is
  # not preceded/followed by another \\ (e.g. "\\x34") but not "\\\\".
  /'|(?<! \\) \\{2}* \\ (?![\\"])/x.match?(string)
end

.escape_string(string) ⇒ Object



148
149
150
# File 'lib/rubocop/cop/util.rb', line 148

def escape_string(string)
  string.inspect[1..-2].tap { |s| s.gsub!('\\"', '"') }
end

.first_part_of_call_chain(node) ⇒ Object

Returns, for example, a bare if node if the given node is an if with calls chained to the end of it.



118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/rubocop/cop/util.rb', line 118

def first_part_of_call_chain(node)
  while node
    case node.type
    when :send
      node = node.receiver
    when :block
      node = node.send_node
    else
      break
    end
  end
  node
end

.indent(node, offset: 0) ⇒ Object



185
186
187
# File 'lib/rubocop/cop/util.rb', line 185

def indent(node, offset: 0)
  ' ' * (node.loc.column + offset)
end

.interpret_string_escapes(string) ⇒ Object



165
166
167
# File 'lib/rubocop/cop/util.rb', line 165

def interpret_string_escapes(string)
  StringInterpreter.interpret(string)
end

.line(node_or_range) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/rubocop/cop/util.rb', line 169

def line(node_or_range)
  if node_or_range.respond_to?(:line)
    node_or_range.line
  elsif node_or_range.respond_to?(:loc)
    node_or_range.loc.line
  end
end

.line_range(node) ⇒ Object



30
31
32
# File 'lib/rubocop/cop/util.rb', line 30

def line_range(node)
  node.first_line..node.last_line
end

.needs_escaping?(string) ⇒ Boolean

Returns:

  • (Boolean)


144
145
146
# File 'lib/rubocop/cop/util.rb', line 144

def needs_escaping?(string)
  double_quotes_required?(escape_string(string))
end

.on_node(syms, sexp, excludes = []) {|sexp| ... } ⇒ Object

Yields:

  • (sexp)


91
92
93
94
95
96
97
98
# File 'lib/rubocop/cop/util.rb', line 91

def on_node(syms, sexp, excludes = [], &block)
  return to_enum(:on_node, syms, sexp, excludes) unless block

  yield sexp if include_or_equal?(syms, sexp.type)
  return if include_or_equal?(excludes, sexp.type)

  sexp.each_child_node { |elem| on_node(syms, elem, excludes, &block) }
end

.parentheses?(node) ⇒ Boolean

Returns:

  • (Boolean)


34
35
36
# File 'lib/rubocop/cop/util.rb', line 34

def parentheses?(node)
  node.loc_is?(:end, ')')
end

.parse_regexp(text) ⇒ Object



196
197
198
199
200
201
202
# File 'lib/rubocop/cop/util.rb', line 196

def parse_regexp(text)
  Regexp::Parser.parse(text)
rescue Regexp::Parser::Error
  # Upon encountering an invalid regular expression,
  # we aim to proceed and identify any remaining potential offenses.
  nil
end

.same_line?(node1, node2) ⇒ Boolean

Returns:

  • (Boolean)


177
178
179
180
181
182
183
# File 'lib/rubocop/cop/util.rb', line 177

def same_line?(node1, node2)
  line1 = line(node1)
  line2 = line(node2)
  return false unless line1 && line2

  line1 == line2
end

.to_string_literal(string) ⇒ Object



152
153
154
155
156
157
158
159
# File 'lib/rubocop/cop/util.rb', line 152

def to_string_literal(string)
  if needs_escaping?(string) && compatible_external_encoding_for?(string)
    string.inspect
  else
    # In a single-quoted strings, double quotes don't need to be escaped
    "'#{string.gsub('\\') { '\\\\' }.gsub('\"', '"')}'"
  end
end

.to_supported_styles(enforced_style) ⇒ Object



191
192
193
194
# File 'lib/rubocop/cop/util.rb', line 191

def to_supported_styles(enforced_style)
  @to_supported_styles_cache[enforced_style] ||=
    enforced_style.sub(/^Enforced/, 'Supported').sub('Style', 'Styles')
end

.trim_string_interpolation_escape_character(str) ⇒ Object



161
162
163
# File 'lib/rubocop/cop/util.rb', line 161

def trim_string_interpolation_escape_character(str)
  str.gsub(/\\\#\{(.*?)\}/) { "\#{#{Regexp.last_match(1)}}" }
end