Improve parsing field docs and comments (fixes #5353)

This commit is contained in:
thetarnav
2026-08-29 19:22:18 +02:00
parent db0cd79633
commit db2f88cff9
2 changed files with 97 additions and 13 deletions

View File

@@ -318,9 +318,6 @@ consume_comment :: proc(p: ^Parser) -> (tok: tokenizer.Token, end_line: int) {
}
_ = next_token0(p)
if p.curr_tok.pos.line > tok.pos.line {
end_line += 1
}
return
}
@@ -348,19 +345,13 @@ consume_comment_groups :: proc(p: ^Parser, prev: tokenizer.Token) {
if p.curr_tok.kind != .Comment {
return
}
comment: ^ast.Comment_Group
end_line := 0
if p.curr_tok.pos.line == prev.pos.line {
comment, end_line = consume_comment_group(p, 0)
if p.curr_tok.pos.line != end_line ||
p.curr_tok.pos.line == prev.pos.line+1 ||
p.curr_tok.kind == .EOF {
p.line_comment = comment
}
p.line_comment, _ = consume_comment_group(p, 0)
}
end_line = -1
comment: ^ast.Comment_Group
end_line: int
for p.curr_tok.kind == .Comment {
comment, end_line = consume_comment_group(p, 1)
}
@@ -2048,13 +2039,14 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
}
}
line_comment := p.line_comment
ok := expect_field_separator(p, type)
field := new_ast_field(names, type, default_value)
field.tag = tag
field.docs = docs
field.flags = flags
field.comment = p.line_comment
field.comment = p.line_comment if p.line_comment != nil else line_comment
append(fields, field)
return ok
@@ -3862,6 +3854,8 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
end := p.prev_tok
end_comment := p.line_comment
if p.expr_level >= 0 {
end: ^ast.Expr
if !is_mutable && len(values) > 0 {
@@ -3883,6 +3877,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(end))
decl.docs = docs
decl.comment = end_comment
decl.names = names
decl.type = type
decl.values = values

View File

@@ -65,6 +65,95 @@ Foo :: bit_field uint {
ok := parser.parse_file(&p, &file)
testing.expect(t, ok, "bad parse")
testing.expect(t, file.syntax_error_count == 0, "should contain zero errors")
}
@test
test_parse_struct_field_comments :: proc(t: ^testing.T) {
context.allocator = context.temp_allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
expect_comments :: proc (t: ^testing.T, docs: ^ast.Comment_Group, expected: []string, loc := #caller_location) {
if expected == nil {
testing.expect_value(t, docs, nil, loc)
} else {
testing.expect(t, docs != nil, "comment should not be nil", loc=loc)
testing.expect_value(t, len(docs.list), len(expected), loc)
for tok, i in docs.list {
testing.expect_value(t, tok.text, expected[i], loc)
}
}
}
file := ast.File{
fullpath = "test.odin",
src = `
package main
// foo doc
Foo :: struct {
// doc1
a: int, // c1
b: int, // c2
// not included
// doc2
// doc3
c, d: int, /* c3
c4 */
e: struct {
} // c5
// not included
}
// not included
Bar :: struct {x, y: int /* c4 */} // after`,
}
p := parser.default_parser()
p.err = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.errorf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
p.warn = proc(pos: tokenizer.Pos, format: string, args: ..any) {
message := fmt.tprintf(format, ..args)
log.warnf("%s(%d:%d): %s", pos.file, pos.line, pos.column, message)
}
ok := parser.parse_file(&p, &file)
testing.expect(t, ok, "bad parse")
testing.expect(t, file.syntax_error_count == 0, "should contain zero errors")
testing.expect_value(t, len(file.decls), 2)
foo_decl := file.decls[0].derived.(^ast.Value_Decl)
expect_comments(t, foo_decl.docs, {"// foo doc"})
expect_comments(t, foo_decl.comment, nil)
foo := foo_decl.values[0].derived.(^ast.Struct_Type)
testing.expect_value(t, len(foo.fields.list), 4)
expect_comments(t, foo.fields.list[0].docs, {"// doc1"})
expect_comments(t, foo.fields.list[0].comment, {"// c1"})
expect_comments(t, foo.fields.list[1].docs, nil)
expect_comments(t, foo.fields.list[1].comment, {"// c2"})
expect_comments(t, foo.fields.list[2].docs, {"// doc2", "// doc3"})
expect_comments(t, foo.fields.list[2].comment, {"/* c3\nc4 */"})
expect_comments(t, foo.fields.list[3].docs, nil)
expect_comments(t, foo.fields.list[3].comment, {"// c5"})
bar_decl := file.decls[1].derived.(^ast.Value_Decl)
expect_comments(t, bar_decl.docs, nil)
expect_comments(t, bar_decl.comment, {"// after"})
bar := bar_decl.values[0].derived.(^ast.Struct_Type)
testing.expect_value(t, len(bar.fields.list), 1)
expect_comments(t, bar.fields.list[0].docs, nil)
expect_comments(t, bar.fields.list[0].comment, {"/* c4 */"})
}
@test