Summary
A malformed class instance variable member raises RuntimeError instead of RBS::ParsingError, so it escapes rescue RBS::ParsingError and the CLI prints a raw Ruby backtrace instead of a formatted syntax error.
require "rbs"
RBS::Parser.parse_signature(RBS::Buffer.new(name: "test.rbs", content: "class M self."))
lib/rbs/parser_aux.rb:31:in 'RBS::Parser._parse_signature': Unexpected error (RuntimeError)
Every other syntax error in the same position is reported correctly, which is what makes this stand out:
| input |
raised |
class M <<< |
RBS::ParsingError with location and caret |
class M self. |
RuntimeError: Unexpected error |
module M self. |
RuntimeError: Unexpected error |
class M self.5 |
RuntimeError: Unexpected error |
class M self."x" |
RuntimeError: Unexpected error |
interface _I self. |
RBS::ParsingError |
class M\n self.@x: Integer\nend |
parses fine (valid) |
Note that interface already reports this correctly — only the class/module path is affected.
Impact
RBS::ParsingError is the documented error class for malformed signatures, so a caller that rescues it does not catch this:
begin
RBS::Parser.parse_signature(buffer)
rescue RBS::ParsingError => e
# never reached for "class M self."
end
The CLI is affected the same way. Compare:
$ rbs parse normal.rbs
/tmp/normal.rbs:1:8...1:10: Syntax error: unexpected token for class/module declaration member, token=`<<` (tOPERATOR) (RBS::ParsingError)
class M <<<
^^
$ rbs parse bug.rbs
lib/rbs/parser_aux.rb:31:in 'RBS::Parser._parse_signature': Unexpected error (RuntimeError)
from lib/rbs/cli.rb:943:in 'block in RBS::CLI#run_parse'
...
Cause
src/parser.c:2353, in the kSELF branch of parse_variable_member:
rbs_parser_set_error(parser, parser->current_token, false, "Unexpected error");
The third argument is bool syntax_error (include/rbs/parser.h:161). It is passed false, so ext/rbs_extension/main.c discards the location, token and message:
if (!error->syntax_error) {
rb_raise(rb_eRuntimeError, "Unexpected error");
}
For comparison, 49 other call sites in src/parser.c pass true and produce proper RBS::ParsingErrors.
I checked whether the other 12 false sites have the same problem, by tagging each one and fuzzing: they are defensive default: arms over already-exhausted enums and are not reachable from user input, so RuntimeError is the right semantic there. This is the only reachable one.
Environment
master cc0b38dfebd0de1f5941c1257a72e0ffc7e1c7bc. Also reproduces on released gems 4.2.0, 4.1.3, 4.0.3 and 3.10.0.
I have a patch and will open a PR referencing this issue.
Summary
A malformed class instance variable member raises
RuntimeErrorinstead ofRBS::ParsingError, so it escapesrescue RBS::ParsingErrorand the CLI prints a raw Ruby backtrace instead of a formatted syntax error.Every other syntax error in the same position is reported correctly, which is what makes this stand out:
class M <<<RBS::ParsingErrorwith location and caretclass M self.RuntimeError: Unexpected errormodule M self.RuntimeError: Unexpected errorclass M self.5RuntimeError: Unexpected errorclass M self."x"RuntimeError: Unexpected errorinterface _I self.RBS::ParsingErrorclass M\n self.@x: Integer\nendNote that
interfacealready reports this correctly — only the class/module path is affected.Impact
RBS::ParsingErroris the documented error class for malformed signatures, so a caller that rescues it does not catch this:The CLI is affected the same way. Compare:
Cause
src/parser.c:2353, in thekSELFbranch ofparse_variable_member:The third argument is
bool syntax_error(include/rbs/parser.h:161). It is passedfalse, soext/rbs_extension/main.cdiscards the location, token and message:For comparison, 49 other call sites in
src/parser.cpasstrueand produce properRBS::ParsingErrors.I checked whether the other 12
falsesites have the same problem, by tagging each one and fuzzing: they are defensivedefault:arms over already-exhausted enums and are not reachable from user input, soRuntimeErroris the right semantic there. This is the only reachable one.Environment
master
cc0b38dfebd0de1f5941c1257a72e0ffc7e1c7bc. Also reproduces on released gems 4.2.0, 4.1.3, 4.0.3 and 3.10.0.I have a patch and will open a PR referencing this issue.