Skip to content

Malformed class instance variable member raises RuntimeError instead of RBS::ParsingError #3148

Description

@Halvanhelv

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions