Class: RuboCop::AST::ProcessedSource

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocop/ast/processed_source.rb

Overview

ProcessedSource contains objects which are generated by Parser and other information such as disabled lines for cops. It also provides a convenient way to access source lines.

Constant Summary collapse

STRING_SOURCE_NAME =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'(string)'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source, ruby_version, path = nil, parser_engine: :default, prism_result: nil) ⇒ ProcessedSource

Returns a new instance of ProcessedSource.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/rubocop/ast/processed_source.rb', line 94

def initialize(
  source, ruby_version, path = nil, parser_engine: :default, prism_result: nil
)
  parser_engine = normalize_parser_engine(parser_engine, ruby_version)

  # Defaults source encoding to UTF-8, regardless of the encoding it has
  # been read with, which could be non-utf8 depending on the default
  # external encoding.
  source = (+source).force_encoding(Encoding::UTF_8) unless source.encoding == Encoding::UTF_8

  @raw_source = source
  @path = path
  @diagnostics = []
  @ruby_version = ruby_version
  @parser_engine = parser_engine
  @parser_error = nil

  parse(source, ruby_version, parser_engine, prism_result)
end

Instance Attribute Details

#astObject (readonly)

Returns the value of attribute ast.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def ast
  @ast
end

#bufferObject (readonly)

Returns the value of attribute buffer.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def buffer
  @buffer
end

#commentsObject (readonly)

Returns the value of attribute comments.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def comments
  @comments
end

#diagnosticsObject (readonly)

Returns the value of attribute diagnostics.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def diagnostics
  @diagnostics
end

#parser_engineObject (readonly)

Returns the value of attribute parser_engine.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def parser_engine
  @parser_engine
end

#parser_errorObject (readonly)

Returns the value of attribute parser_error.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def parser_error
  @parser_error
end

#pathObject (readonly)

Returns the value of attribute path.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def path
  @path
end

#raw_sourceObject (readonly)

Returns the value of attribute raw_source.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def raw_source
  @raw_source
end

#ruby_versionObject (readonly)

Returns the value of attribute ruby_version.



79
80
81
# File 'lib/rubocop/ast/processed_source.rb', line 79

def ruby_version
  @ruby_version
end

Class Method Details

.from_file(path, ruby_version, parser_engine: :default) ⇒ Object



82
83
84
85
# File 'lib/rubocop/ast/processed_source.rb', line 82

def self.from_file(path, ruby_version, parser_engine: :default)
  file = File.read(path, mode: 'rb')
  new(file, ruby_version, path, parser_engine: parser_engine)
end

.lazy_tokens_parser_class(base) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Subclasses of the prism translation parsers with lazily built tokens.



89
90
91
92
# File 'lib/rubocop/ast/processed_source.rb', line 89

def self.lazy_tokens_parser_class(base)
  @lazy_tokens_parser_classes ||= {}
  @lazy_tokens_parser_classes[base] ||= Class.new(base) { include PrismLazyTokens }
end

Instance Method Details

#[](*args) ⇒ Object



135
136
137
# File 'lib/rubocop/ast/processed_source.rb', line 135

def [](*args)
  lines[*args]
end

#ast_with_commentsObject



114
115
116
117
118
# File 'lib/rubocop/ast/processed_source.rb', line 114

def ast_with_comments
  return if !ast || !comments

  @ast_with_comments ||= Parser::Source::Comment.associate_by_identity(ast, comments)
end

#blank?Boolean

Returns:

  • (Boolean)


174
175
176
# File 'lib/rubocop/ast/processed_source.rb', line 174

def blank?
  ast.nil?
end

#checksumObject

Raw source checksum for tracking infinite loops.



146
147
148
# File 'lib/rubocop/ast/processed_source.rb', line 146

def checksum
  @checksum ||= Digest::SHA1.hexdigest(@raw_source)
end

#comment_at_line(line) ⇒ Comment?

Returns the comment at that line, if any.

Returns:

  • (Comment, nil)

    the comment at that line, if any.



179
180
181
# File 'lib/rubocop/ast/processed_source.rb', line 179

def comment_at_line(line)
  comment_index[line]
end

#comments_before_line(line) ⇒ Object

Deprecated.

Use each_comment_in_lines

Should have been called comments_before_or_at_line. Doubtful it has of any valid use.



209
210
211
# File 'lib/rubocop/ast/processed_source.rb', line 209

def comments_before_line(line)
  each_comment_in_lines(0..line).to_a
end

#contains_comment?(source_range) ⇒ Boolean Also known as: commented?

Consider using each_comment_in_lines instead

Returns:

  • (Boolean)

    if any of the lines in the given source_range has a comment.



201
202
203
# File 'lib/rubocop/ast/processed_source.rb', line 201

def contains_comment?(source_range)
  each_comment_in_lines(source_range.line..source_range.last_line).any?
end

#current_line(token) ⇒ Object



225
226
227
# File 'lib/rubocop/ast/processed_source.rb', line 225

def current_line(token)
  lines[token.line - 1]
end

#each_comment(&block) ⇒ Object

Deprecated.

Use comments.each



151
152
153
# File 'lib/rubocop/ast/processed_source.rb', line 151

def each_comment(&block)
  comments.each(&block)
end

#each_comment_in_lines(line_range) ⇒ Object

Enumerates on the comments contained with the given line_range



189
190
191
192
193
194
195
196
197
# File 'lib/rubocop/ast/processed_source.rb', line 189

def each_comment_in_lines(line_range)
  return to_enum(:each_comment_in_lines, line_range) unless block_given?

  line_range.each do |line|
    if (comment = comment_index[line])
      yield comment
    end
  end
end

#each_token(&block) ⇒ Object

Deprecated.

Use tokens.each



161
162
163
# File 'lib/rubocop/ast/processed_source.rb', line 161

def each_token(&block)
  tokens.each(&block)
end

#file_pathObject



170
171
172
# File 'lib/rubocop/ast/processed_source.rb', line 170

def file_path
  buffer.name
end

#find_comment(&block) ⇒ Object

Deprecated.

Use comment_at_line, each_comment_in_lines, or comments.find



156
157
158
# File 'lib/rubocop/ast/processed_source.rb', line 156

def find_comment(&block)
  comments.find(&block)
end

#find_token(&block) ⇒ Object

Deprecated.

Use tokens.find



166
167
168
# File 'lib/rubocop/ast/processed_source.rb', line 166

def find_token(&block)
  tokens.find(&block)
end

#first_token_of(range_or_node) ⇒ Object



259
260
261
# File 'lib/rubocop/ast/processed_source.rb', line 259

def first_token_of(range_or_node)
  sorted_tokens[first_token_index(range_or_node)]
end

#following_line(token) ⇒ Object



229
230
231
# File 'lib/rubocop/ast/processed_source.rb', line 229

def following_line(token)
  lines[token.line]
end

#last_token_of(range_or_node) ⇒ Object



263
264
265
# File 'lib/rubocop/ast/processed_source.rb', line 263

def last_token_of(range_or_node)
  sorted_tokens[last_token_index(range_or_node)]
end

#line_indentation(line_number) ⇒ Object



233
234
235
236
237
238
# File 'lib/rubocop/ast/processed_source.rb', line 233

def line_indentation(line_number)
  lines[line_number - 1]
    .match(/^(\s*)/)[1]
    .to_s
    .length
end

#line_with_comment?(line) ⇒ Boolean

Returns if the given line number has a comment.

Returns:

  • (Boolean)

    if the given line number has a comment.



184
185
186
# File 'lib/rubocop/ast/processed_source.rb', line 184

def line_with_comment?(line)
  comment_index.include?(line)
end

#linesObject

Returns the source lines, line break characters removed, excluding a possible END and everything that comes after.



122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/rubocop/ast/processed_source.rb', line 122

def lines
  @lines ||= begin
    all_lines = @buffer.source_lines
    if all_lines.include?('__END__')
      trim_lines_after_data_marker(all_lines)
    else
      # Don't consult the tokens (which may have to be built first) when
      # the source can't contain a data section.
      all_lines.dup
    end
  end
end

#preceding_line(token) ⇒ Object



219
220
221
222
223
# File 'lib/rubocop/ast/processed_source.rb', line 219

def preceding_line(token)
  return nil if token.line < 2

  lines[token.line - 2]
end

#sorted_tokensObject

The tokens list is always sorted by token position, except for cases when heredoc is passed as a method argument. In this case tokens are interleaved by heredoc contents' tokens.



270
271
272
273
274
275
276
277
278
279
280
# File 'lib/rubocop/ast/processed_source.rb', line 270

def sorted_tokens
  # Most sources have their tokens already in order, in which case
  # sorting can be skipped entirely. Callers only ever read from the
  # returned array, so it is safe to reuse `tokens` as is.
  @sorted_tokens ||= if tokens_sorted?
                       tokens
                     else
                       # Use stable sort.
                       tokens.sort_by.with_index { |token, i| [token.begin_pos, i] }
                     end
end

#start_with?(string) ⇒ Boolean

Returns:

  • (Boolean)


213
214
215
216
217
# File 'lib/rubocop/ast/processed_source.rb', line 213

def start_with?(string)
  return false if self[0].nil?

  self[0].start_with?(string)
end

#tokensObject

The tokens of the source. With the prism engine the tokens are built lazily on first access, since their conversion is costly and not every caller needs them.



243
244
245
246
247
248
249
250
251
# File 'lib/rubocop/ast/processed_source.rb', line 243

def tokens
  @tokens ||= begin
    tokens = parser_tokens.map { |t| Token.from_parser_token(t) }
    # The parser tokens are no longer needed, and the deferred conversion
    # holds on to the parser instance, so release both.
    @parser_tokens = nil
    tokens
  end
end

#tokens_within(range_or_node) ⇒ Object



253
254
255
256
257
# File 'lib/rubocop/ast/processed_source.rb', line 253

def tokens_within(range_or_node)
  begin_index = first_token_index(range_or_node)
  end_index = last_token_index(range_or_node)
  sorted_tokens[begin_index..end_index]
end

#valid_syntax?Boolean

Returns:

  • (Boolean)


139
140
141
142
143
# File 'lib/rubocop/ast/processed_source.rb', line 139

def valid_syntax?
  return false if @parser_error

  @diagnostics.none? { |d| INVALID_LEVELS.include?(d.level) }
end