From c4867c8743e82433526b07eff3c599d05e0b7e6e Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 5 Nov 2024 08:58:40 +0100 Subject: [PATCH] cgo: define idents referenced only from macros --- cgo/cgo.go | 4 ++-- cgo/const.go | 22 +++++++++++++++++++--- cgo/const_test.go | 2 +- cgo/libclang.go | 2 +- cgo/testdata/const.go | 3 +++ cgo/testdata/const.out.go | 2 ++ 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/cgo/cgo.go b/cgo/cgo.go index 4a5d7efed..a90b30753 100644 --- a/cgo/cgo.go +++ b/cgo/cgo.go @@ -1148,7 +1148,7 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st if alias := cgoAliases["C."+name]; alias != "" { return alias } - node := f.getASTDeclNode(name, found, iscall) + node := f.getASTDeclNode(name, found) if node, ok := node.(*ast.FuncDecl); ok { if !iscall { return node.Name.Name + "$funcaddr" @@ -1160,7 +1160,7 @@ func (f *cgoFile) getASTDeclName(name string, found clangCursor, iscall bool) st // getASTDeclNode will declare the given C AST node (if not already defined) and // returns it. -func (f *cgoFile) getASTDeclNode(name string, found clangCursor, iscall bool) ast.Node { +func (f *cgoFile) getASTDeclNode(name string, found clangCursor) ast.Node { if node, ok := f.defined[name]; ok { // Declaration was found in the current file, so return it immediately. return node diff --git a/cgo/const.go b/cgo/const.go index f4707c80a..ab088b3c8 100644 --- a/cgo/const.go +++ b/cgo/const.go @@ -54,8 +54,8 @@ func init() { } // parseConst parses the given string as a C constant. -func parseConst(pos token.Pos, fset *token.FileSet, value string) (ast.Expr, *scanner.Error) { - t := newTokenizer(pos, fset, value) +func parseConst(pos token.Pos, fset *token.FileSet, value string, f *cgoFile) (ast.Expr, *scanner.Error) { + t := newTokenizer(pos, fset, value, f) expr, err := parseConstExpr(t, precedenceLowest) t.Next() if t.curToken != token.EOF { @@ -96,6 +96,20 @@ func parseConstExpr(t *tokenizer, precedence int) (ast.Expr, *scanner.Error) { } func parseIdent(t *tokenizer) (ast.Expr, *scanner.Error) { + // Normally the name is something defined in the file (like another macro) + // which we get the declaration from using getASTDeclName. + // This ensures that names that are only referenced inside a macro are still + // getting defined. + if t.f != nil { + if cursor, ok := t.f.names[t.curValue]; ok { + return &ast.Ident{ + NamePos: t.curPos, + Name: t.f.getASTDeclName(t.curValue, cursor, false), + }, nil + } + } + + // t.f is nil during testing. This is a fallback. return &ast.Ident{ NamePos: t.curPos, Name: "C." + t.curValue, @@ -164,6 +178,7 @@ func unexpectedToken(t *tokenizer, expected token.Token) *scanner.Error { // tokenizer reads C source code and converts it to Go tokens. type tokenizer struct { + f *cgoFile curPos, peekPos token.Pos fset *token.FileSet curToken, peekToken token.Token @@ -173,8 +188,9 @@ type tokenizer struct { // newTokenizer initializes a new tokenizer, positioned at the first token in // the string. -func newTokenizer(start token.Pos, fset *token.FileSet, buf string) *tokenizer { +func newTokenizer(start token.Pos, fset *token.FileSet, buf string, f *cgoFile) *tokenizer { t := &tokenizer{ + f: f, peekPos: start, fset: fset, buf: buf, diff --git a/cgo/const_test.go b/cgo/const_test.go index d150e751f..c2f52c53c 100644 --- a/cgo/const_test.go +++ b/cgo/const_test.go @@ -59,7 +59,7 @@ func TestParseConst(t *testing.T) { } { fset := token.NewFileSet() startPos := fset.AddFile("", -1, 1000).Pos(0) - expr, err := parseConst(startPos, fset, tc.C) + expr, err := parseConst(startPos, fset, tc.C, nil) s := "" if err != nil { if !strings.HasPrefix(tc.Go, "error: ") { diff --git a/cgo/libclang.go b/cgo/libclang.go index 59236bad4..c66112d53 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -408,7 +408,7 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) { if pos != token.NoPos { tokenPos = pos + token.Pos(len(name)) } - expr, scannerError := parseConst(tokenPos, f.fset, value) + expr, scannerError := parseConst(tokenPos, f.fset, value, f) if scannerError != nil { f.errors = append(f.errors, *scannerError) return nil, nil diff --git a/cgo/testdata/const.go b/cgo/testdata/const.go index 43645d3ac..258942235 100644 --- a/cgo/testdata/const.go +++ b/cgo/testdata/const.go @@ -3,10 +3,13 @@ package main /* #define foo 3 #define bar foo +#define unreferenced 4 +#define referenced unreferenced */ import "C" const ( Foo = C.foo Bar = C.bar + Baz = C.referenced ) diff --git a/cgo/testdata/const.out.go b/cgo/testdata/const.out.go index 2b48163b4..fb0bbeeba 100644 --- a/cgo/testdata/const.out.go +++ b/cgo/testdata/const.out.go @@ -47,3 +47,5 @@ type ( const C.foo = 3 const C.bar = C.foo +const C.unreferenced = 4 +const C.referenced = C.unreferenced