Skip to content

Commit 1a8a006

Browse files
committed
Use the diagnostic types in the parser translation layer
1 parent 1ca58e0 commit 1a8a006

File tree

6 files changed

+131
-38
lines changed

6 files changed

+131
-38
lines changed

bin/prism

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -256,14 +256,10 @@ module Prism
256256
buffer.source = source
257257

258258
puts "Parser:"
259-
parser_ast, _, parser_tokens = Parser::Ruby33.new.tokenize(buffer)
260-
pp parser_ast
261-
pp parser_tokens
259+
parser_parse(Parser::Ruby33.new, buffer)
262260

263261
puts "Prism:"
264-
prism_ast, _, prism_tokens = Prism::Translation::Parser33.new.tokenize(buffer)
265-
pp prism_ast
266-
pp prism_tokens
262+
parser_parse(Prism::Translation::Parser33.new, buffer)
267263
end
268264

269265
# bin/prism ripper [source]
@@ -302,6 +298,17 @@ module Prism
302298
# Helpers
303299
############################################################################
304300

301+
# Parse a Parser::Source::Buffer with a given parser.
302+
def parser_parse(parser, buffer)
303+
diagnostics = []
304+
parser.diagnostics.consumer = -> (diagnostic) { diagnostics << diagnostic }
305+
306+
parser_ast, _, parser_tokens = parser.tokenize(buffer, true)
307+
pp diagnostics if diagnostics.any?
308+
pp parser_ast
309+
pp parser_tokens
310+
end
311+
305312
# Generate the list of values that will be used in a lookup table for a
306313
# given encoding.
307314
def lookup_table_values(encoding)

lib/prism/parse_result.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,8 @@ def inspect
366366

367367
# This represents an error that was encountered during parsing.
368368
class ParseError
369-
# The type of error.
369+
# The type of error. This is an _internal_ symbol that is used for
370+
# communicating with translation layers. It is not meant to be public API.
370371
attr_reader :type
371372

372373
# The message associated with this error.
@@ -399,7 +400,8 @@ def inspect
399400

400401
# This represents a warning that was encountered during parsing.
401402
class ParseWarning
402-
# The type of warning.
403+
# The type of warning. This is an _internal_ symbol that is used for
404+
# communicating with translation layers. It is not meant to be public API.
403405
attr_reader :type
404406

405407
# The message associated with this warning.

lib/prism/translation/parser.rb

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@ module Translation
99
# the parser gem, and overrides the parse* methods to parse with prism and
1010
# then translate.
1111
class Parser < ::Parser::Base
12+
Diagnostic = ::Parser::Diagnostic # :nodoc:
13+
private_constant :Diagnostic
14+
1215
# The parser gem has a list of diagnostics with a hard-coded set of error
1316
# messages. We create our own diagnostic class in order to set our own
1417
# error messages.
15-
class Diagnostic < ::Parser::Diagnostic
16-
# The message generated by prism.
18+
class PrismDiagnostic < Diagnostic
19+
# This is the cached message coming from prism.
1720
attr_reader :message
1821

1922
# Initialize a new diagnostic with the given message and location.
@@ -112,20 +115,109 @@ def valid_warning?(warning)
112115
true
113116
end
114117

118+
# Build a diagnostic from the given prism parse error.
119+
def error_diagnostic(error, offset_cache)
120+
location = error.location
121+
diagnostic_location = build_range(location, offset_cache)
122+
123+
case error.type
124+
when :argument_block_multi
125+
Diagnostic.new(:error, :block_and_blockarg, {}, diagnostic_location, [])
126+
when :argument_formal_constant
127+
Diagnostic.new(:error, :formal_argument, {}, diagnostic_location, [])
128+
when :argument_formal_class
129+
Diagnostic.new(:error, :argument_cvar, {}, diagnostic_location, [])
130+
when :argument_formal_global
131+
Diagnostic.new(:error, :argument_gvar, {}, diagnostic_location, [])
132+
when :argument_formal_ivar
133+
Diagnostic.new(:error, :argument_ivar, {}, diagnostic_location, [])
134+
when :argument_no_forwarding_amp
135+
Diagnostic.new(:error, :no_anonymous_blockarg, {}, diagnostic_location, [])
136+
when :argument_no_forwarding_star
137+
Diagnostic.new(:error, :no_anonymous_restarg, {}, diagnostic_location, [])
138+
when :begin_lonely_else
139+
location = location.copy(length: 4)
140+
diagnostic_location = build_range(location, offset_cache)
141+
Diagnostic.new(:error, :useless_else, {}, diagnostic_location, [])
142+
when :class_name, :module_name
143+
Diagnostic.new(:error, :module_name_const, {}, diagnostic_location, [])
144+
when :class_in_method
145+
Diagnostic.new(:error, :class_in_def, {}, diagnostic_location, [])
146+
when :def_endless_setter
147+
Diagnostic.new(:error, :endless_setter, {}, diagnostic_location, [])
148+
when :embdoc_term
149+
Diagnostic.new(:error, :embedded_document, {}, diagnostic_location, [])
150+
when :incomplete_variable_class, :incomplete_variable_class_3_3_0
151+
location = location.copy(length: location.length + 1)
152+
diagnostic_location = build_range(location, offset_cache)
153+
154+
Diagnostic.new(:error, :cvar_name, { name: location.slice }, diagnostic_location, [])
155+
when :incomplete_variable_instance, :incomplete_variable_instance_3_3_0
156+
location = location.copy(length: location.length + 1)
157+
diagnostic_location = build_range(location, offset_cache)
158+
159+
Diagnostic.new(:error, :ivar_name, { name: location.slice }, diagnostic_location, [])
160+
when :invalid_variable_global, :invalid_variable_global_3_3_0
161+
Diagnostic.new(:error, :gvar_name, { name: location.slice }, diagnostic_location, [])
162+
when :module_in_method
163+
Diagnostic.new(:error, :module_in_def, {}, diagnostic_location, [])
164+
when :numbered_parameter_ordinary
165+
Diagnostic.new(:error, :ordinary_param_defined, {}, diagnostic_location, [])
166+
when :numbered_parameter_outer_scope
167+
Diagnostic.new(:error, :numparam_used_in_outer_scope, {}, diagnostic_location, [])
168+
when :parameter_circular
169+
Diagnostic.new(:error, :circular_argument_reference, { var_name: location.slice }, diagnostic_location, [])
170+
when :parameter_name_repeat
171+
Diagnostic.new(:error, :duplicate_argument, {}, diagnostic_location, [])
172+
when :parameter_numbered_reserved
173+
Diagnostic.new(:error, :reserved_for_numparam, { name: location.slice }, diagnostic_location, [])
174+
when :singleton_for_literals
175+
Diagnostic.new(:error, :singleton_literal, {}, diagnostic_location, [])
176+
when :string_literal_eof
177+
Diagnostic.new(:error, :string_eof, {}, diagnostic_location, [])
178+
when :unexpected_token_ignore
179+
Diagnostic.new(:error, :unexpected_token, { token: location.slice }, diagnostic_location, [])
180+
when :write_target_in_method
181+
Diagnostic.new(:error, :dynamic_const, {}, diagnostic_location, [])
182+
else
183+
PrismDiagnostic.new(error.message, :error, error.type, diagnostic_location)
184+
end
185+
end
186+
187+
# Build a diagnostic from the given prism parse warning.
188+
def warning_diagnostic(warning, offset_cache)
189+
diagnostic_location = build_range(warning.location, offset_cache)
190+
191+
case warning.type
192+
when :ambiguous_first_argument_plus
193+
Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "+" }, diagnostic_location, [])
194+
when :ambiguous_first_argument_minus
195+
Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "-" }, diagnostic_location, [])
196+
when :ambiguous_prefix_star
197+
Diagnostic.new(:warning, :ambiguous_prefix, { prefix: "*" }, diagnostic_location, [])
198+
when :ambiguous_slash
199+
Diagnostic.new(:warning, :ambiguous_regexp, {}, diagnostic_location, [])
200+
when :dot_dot_dot_eol
201+
Diagnostic.new(:warning, :triple_dot_at_eol, {}, diagnostic_location, [])
202+
when :duplicated_hash_key
203+
# skip, parser does this on its own
204+
else
205+
PrismDiagnostic.new(warning.message, :warning, warning.type, diagnostic_location)
206+
end
207+
end
208+
115209
# If there was a error generated during the parse, then raise an
116210
# appropriate syntax error. Otherwise return the result.
117211
def unwrap(result, offset_cache)
118212
result.errors.each do |error|
119213
next unless valid_error?(error)
120-
121-
location = build_range(error.location, offset_cache)
122-
diagnostics.process(Diagnostic.new(error.message, :error, :prism_error, location))
214+
diagnostics.process(error_diagnostic(error, offset_cache))
123215
end
216+
124217
result.warnings.each do |warning|
125218
next unless valid_warning?(warning)
126-
127-
location = build_range(warning.location, offset_cache)
128-
diagnostics.process(Diagnostic.new(warning.message, :warning, :prism_warning, location))
219+
diagnostic = warning_diagnostic(warning, offset_cache)
220+
diagnostics.process(diagnostic) if diagnostic
129221
end
130222

131223
result

lib/prism/translation/parser/lexer.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,11 @@ def initialize(source_buffer, lexed, offset_cache)
213213
# Convert the prism tokens into the expected format for the parser gem.
214214
def to_a
215215
tokens = []
216+
216217
index = 0
218+
length = lexed.length
217219

218-
while index < lexed.length
220+
while index < length
219221
token, state = lexed[index]
220222
index += 1
221223
next if %i[IGNORED_NEWLINE __END__ EOF].include?(token.type)
@@ -229,14 +231,18 @@ def to_a
229231
value.delete_prefix!("?")
230232
when :tCOMMENT
231233
if token.type == :EMBDOC_BEGIN
232-
until (next_token = lexed[index][0]) && next_token.type == :EMBDOC_END
234+
start_index = index
235+
236+
while !((next_token = lexed[index][0]) && next_token.type == :EMBDOC_END) && (index < length - 1)
233237
value += next_token.value
234238
index += 1
235239
end
236240

237-
value += next_token.value
238-
location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[lexed[index][0].location.end_offset])
239-
index += 1
241+
if start_index != index
242+
value += next_token.value
243+
location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[lexed[index][0].location.end_offset])
244+
index += 1
245+
end
240246
else
241247
value.chomp!
242248
location = Range.new(source_buffer, offset_cache[token.location.start_offset], offset_cache[token.location.end_offset - 1])

templates/src/diagnostic.c.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ pm_diagnostic_id_human(pm_diagnostic_id_t diag_id) {
331331
case PM_WARN_<%= warning.name %>: return "<%= warning.name.downcase %>";
332332
<%- end -%>
333333
}
334+
335+
assert(false && "unreachable");
336+
return "";
334337
}
335338

336339
static inline const char *

test/prism/parser_test.rb

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,21 +95,6 @@ class ParserTest < TestCase
9595
end
9696
end
9797

98-
def test_warnings
99-
buffer = Parser::Source::Buffer.new("inline ruby with warning", 1)
100-
buffer.source = "do_something *array"
101-
102-
parser = Prism::Translation::Parser33.new
103-
parser.diagnostics.all_errors_are_fatal = false
104-
105-
warning = nil
106-
parser.diagnostics.consumer = ->(received) { warning = received }
107-
parser.parse(buffer)
108-
109-
assert_equal :warning, warning.level
110-
assert_includes warning.message, "has been interpreted as"
111-
end
112-
11398
private
11499

115100
def assert_equal_parses(filepath, compare_tokens: true)
@@ -186,8 +171,6 @@ def assert_equal_tokens(expected_tokens, actual_tokens)
186171
actual_token[0] = expected_token[0] if %i[kDO_BLOCK kDO_LAMBDA].include?(expected_token[0])
187172
when :tLPAREN
188173
actual_token[0] = expected_token[0] if expected_token[0] == :tLPAREN2
189-
when :tLCURLY
190-
actual_token[0] = expected_token[0] if %i[tLBRACE tLBRACE_ARG].include?(expected_token[0])
191174
when :tPOW
192175
actual_token[0] = expected_token[0] if expected_token[0] == :tDSTAR
193176
end

0 commit comments

Comments
 (0)