diff --git a/src/parser.c b/src/parser.c index 5effb219e..fc6f59c72 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1671,11 +1671,12 @@ bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type global_decl ::= {tGIDENT} `:` */ RBS_NODISCARD -static bool parse_global_decl(rbs_parser_t *parser, rbs_node_list_t *annotations, rbs_ast_declarations_global_t **global) { +static bool parse_global_decl(rbs_parser_t *parser, rbs_position_t comment_pos, rbs_node_list_t *annotations, rbs_ast_declarations_global_t **global) { rbs_range_t decl_range; decl_range.start = parser->current_token.range.start; + comment_pos = rbs_nonnull_pos_or(comment_pos, decl_range.start); - rbs_ast_comment_t *comment = rbs_parser_get_comment(parser, decl_range.start.line); + rbs_ast_comment_t *comment = rbs_parser_get_comment(parser, comment_pos.line); rbs_range_t name_range = parser->current_token.range; @@ -1696,11 +1697,12 @@ static bool parse_global_decl(rbs_parser_t *parser, rbs_node_list_t *annotations const_decl ::= {const_name} `:` */ RBS_NODISCARD -static bool parse_const_decl(rbs_parser_t *parser, rbs_node_list_t *annotations, rbs_ast_declarations_constant_t **constant) { +static bool parse_const_decl(rbs_parser_t *parser, rbs_position_t comment_pos, rbs_node_list_t *annotations, rbs_ast_declarations_constant_t **constant) { rbs_range_t decl_range; decl_range.start = parser->current_token.range.start; - rbs_ast_comment_t *comment = rbs_parser_get_comment(parser, decl_range.start.line); + comment_pos = rbs_nonnull_pos_or(comment_pos, decl_range.start); + rbs_ast_comment_t *comment = rbs_parser_get_comment(parser, comment_pos.line); rbs_range_t name_range; rbs_type_name_t *type_name = NULL; @@ -2999,13 +3001,13 @@ static bool parse_nested_decl(rbs_parser_t *parser, const char *nested_in, rbs_p case tUIDENT: case pCOLON2: { rbs_ast_declarations_constant_t *constant = NULL; - CHECK_PARSE(parse_const_decl(parser, annotations, &constant)); + CHECK_PARSE(parse_const_decl(parser, annot_pos, annotations, &constant)); *decl = (rbs_node_t *) constant; break; } case tGIDENT: { rbs_ast_declarations_global_t *global = NULL; - CHECK_PARSE(parse_global_decl(parser, annotations, &global)); + CHECK_PARSE(parse_global_decl(parser, annot_pos, annotations, &global)); *decl = (rbs_node_t *) global; break; } @@ -3055,13 +3057,13 @@ static bool parse_decl(rbs_parser_t *parser, rbs_node_t **decl) { case tUIDENT: case pCOLON2: { rbs_ast_declarations_constant_t *constant = NULL; - CHECK_PARSE(parse_const_decl(parser, annotations, &constant)); + CHECK_PARSE(parse_const_decl(parser, annot_pos, annotations, &constant)); *decl = (rbs_node_t *) constant; return true; } case tGIDENT: { rbs_ast_declarations_global_t *global = NULL; - CHECK_PARSE(parse_global_decl(parser, annotations, &global)); + CHECK_PARSE(parse_global_decl(parser, annot_pos, annotations, &global)); *decl = (rbs_node_t *) global; return true; } diff --git a/test/rbs/signature_parsing_test.rb b/test/rbs/signature_parsing_test.rb index bcf666f5a..04eec0ab0 100644 --- a/test/rbs/signature_parsing_test.rb +++ b/test/rbs/signature_parsing_test.rb @@ -2340,6 +2340,114 @@ def test_constant__annotation end end + def test_constant__comment_and_annotation + Parser.parse_signature(<<~RBS).tap do |_, _, decls| + # Comment above annotation + %a{deprecated} + Foo::BAR: String + + %a{deprecated} + # Comment below annotation + Foo::BAZ: String + + # Comment without annotation + Foo::QUX: String + RBS + + assert_equal 3, decls.size + + decls[0].tap do |decl| + assert_instance_of RBS::AST::Declarations::Constant, decl + assert_equal "Comment above annotation\n", decl.comment&.string + assert_equal ["deprecated"], decl.annotations.map(&:string) + end + decls[1].tap do |decl| + assert_instance_of RBS::AST::Declarations::Constant, decl + # Same as class, module, and other declarations + assert_nil decl.comment + assert_equal ["deprecated"], decl.annotations.map(&:string) + end + decls[2].tap do |decl| + assert_instance_of RBS::AST::Declarations::Constant, decl + assert_equal "Comment without annotation\n", decl.comment&.string + assert_empty decl.annotations + end + end + end + + def test_nested_constant__comment_and_annotation + Parser.parse_signature(<<~RBS).tap do |_, _, decls| + module M + # Comment above annotation + %a{deprecated} + BAR: String + + %a{deprecated} + # Comment below annotation + BAZ: String + + # Comment without annotation + QUX: String + end + RBS + + members = decls[0].members + assert_equal 3, members.size + + members[0].tap do |decl| + assert_instance_of RBS::AST::Declarations::Constant, decl + assert_equal "Comment above annotation\n", decl.comment&.string + assert_equal ["deprecated"], decl.annotations.map(&:string) + end + members[1].tap do |decl| + assert_instance_of RBS::AST::Declarations::Constant, decl + # Same as class, module, and other declarations + assert_nil decl.comment + assert_equal ["deprecated"], decl.annotations.map(&:string) + end + members[2].tap do |decl| + assert_instance_of RBS::AST::Declarations::Constant, decl + assert_equal "Comment without annotation\n", decl.comment&.string + assert_empty decl.annotations + end + end + end + + def test_global__comment_and_annotation + Parser.parse_signature(<<~RBS).tap do |_, _, decls| + # Comment above annotation + %a{deprecated} + $bar: String + + %a{deprecated} + # Comment below annotation + $baz: String + + # Comment without annotation + $qux: String + RBS + + assert_equal 3, decls.size + + decls[0].tap do |decl| + assert_instance_of RBS::AST::Declarations::Global, decl + assert_equal "Comment above annotation\n", decl.comment&.string + assert_equal ["deprecated"], decl.annotations.map(&:string) + end + decls[1].tap do |decl| + assert_instance_of RBS::AST::Declarations::Global, decl + # Same as class, module, and other declarations + assert_nil decl.comment + assert_equal ["deprecated"], decl.annotations.map(&:string) + end + decls[2].tap do |decl| + assert_instance_of RBS::AST::Declarations::Global, decl + assert_equal "Comment without annotation\n", decl.comment&.string + assert_empty decl.annotations + end + end + end + def test__method_type__untyped_function_and_block assert_raises(RBS::ParsingError) do Parser.parse_signature(<<~RBS).tap do |_, _, decls|