diff options
author | Ayke van Laethem <[email protected]> | 2024-11-06 13:46:49 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2024-11-18 18:35:20 +0100 |
commit | e12da15f7d230874ca95a257c50bca0fc39f262c (patch) | |
tree | e6c6650baddd1d521053ebf49585ae08e1240090 /cgo/libclang.go | |
parent | c4867c8743e82433526b07eff3c599d05e0b7e6e (diff) | |
download | tinygo-e12da15f7d230874ca95a257c50bca0fc39f262c.tar.gz tinygo-e12da15f7d230874ca95a257c50bca0fc39f262c.zip |
cgo: support function-like macros
This is needed for code like this:
#define __WASI_ERRNO_INVAL (UINT16_C(28))
#define EINVAL __WASI_ERRNO_INVAL
Diffstat (limited to 'cgo/libclang.go')
-rw-r--r-- | cgo/libclang.go | 98 |
1 files changed, 59 insertions, 39 deletions
diff --git a/cgo/libclang.go b/cgo/libclang.go index c66112d53..794d4e81f 100644 --- a/cgo/libclang.go +++ b/cgo/libclang.go @@ -63,6 +63,7 @@ long long tinygo_clang_getEnumConstantDeclValue(GoCXCursor c); CXType tinygo_clang_getEnumDeclIntegerType(GoCXCursor c); unsigned tinygo_clang_Cursor_isAnonymous(GoCXCursor c); unsigned tinygo_clang_Cursor_isBitField(GoCXCursor c); +unsigned tinygo_clang_Cursor_isMacroFunctionLike(GoCXCursor c); int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data); @@ -370,45 +371,8 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) { gen.Specs = append(gen.Specs, valueSpec) return gen, nil case C.CXCursor_MacroDefinition: - // Extract tokens from the Clang tokenizer. - // See: https://stackoverflow.com/a/19074846/559350 - sourceRange := C.tinygo_clang_getCursorExtent(c) - tu := C.tinygo_clang_Cursor_getTranslationUnit(c) - var rawTokens *C.CXToken - var numTokens C.unsigned - C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens) - tokens := unsafe.Slice(rawTokens, numTokens) - // Convert this range of tokens back to source text. - // Ugly, but it works well enough. - sourceBuf := &bytes.Buffer{} - var startOffset int - for i, token := range tokens { - spelling := getString(C.clang_getTokenSpelling(tu, token)) - location := C.clang_getTokenLocation(tu, token) - var tokenOffset C.unsigned - C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset) - if i == 0 { - // The first token is the macro name itself. - // Skip it (after using its location). - startOffset = int(tokenOffset) + len(name) - } else { - // Later tokens are the macro contents. - for int(tokenOffset) > (startOffset + sourceBuf.Len()) { - // Pad the source text with whitespace (that must have been - // present in the original source as well). - sourceBuf.WriteByte(' ') - } - sourceBuf.WriteString(spelling) - } - } - C.clang_disposeTokens(tu, rawTokens, numTokens) - value := sourceBuf.String() - // Try to convert this #define into a Go constant expression. - tokenPos := token.NoPos - if pos != token.NoPos { - tokenPos = pos + token.Pos(len(name)) - } - expr, scannerError := parseConst(tokenPos, f.fset, value, f) + tokenPos, value := f.getMacro(c) + expr, scannerError := parseConst(tokenPos, f.fset, value, nil, token.NoPos, f) if scannerError != nil { f.errors = append(f.errors, *scannerError) return nil, nil @@ -488,6 +452,62 @@ func (f *cgoFile) createASTNode(name string, c clangCursor) (ast.Node, any) { } } +// Return whether this is a macro that's also function-like, like this: +// +// #define add(a, b) (a+b) +func (f *cgoFile) isFunctionLikeMacro(c clangCursor) bool { + if C.tinygo_clang_getCursorKind(c) != C.CXCursor_MacroDefinition { + return false + } + return C.tinygo_clang_Cursor_isMacroFunctionLike(c) != 0 +} + +// Get the macro value: the position in the source file and the string value of +// the macro. +func (f *cgoFile) getMacro(c clangCursor) (pos token.Pos, value string) { + // Extract tokens from the Clang tokenizer. + // See: https://stackoverflow.com/a/19074846/559350 + sourceRange := C.tinygo_clang_getCursorExtent(c) + tu := C.tinygo_clang_Cursor_getTranslationUnit(c) + var rawTokens *C.CXToken + var numTokens C.unsigned + C.clang_tokenize(tu, sourceRange, &rawTokens, &numTokens) + tokens := unsafe.Slice(rawTokens, numTokens) + defer C.clang_disposeTokens(tu, rawTokens, numTokens) + + // Convert this range of tokens back to source text. + // Ugly, but it works well enough. + sourceBuf := &bytes.Buffer{} + var startOffset int + for i, token := range tokens { + spelling := getString(C.clang_getTokenSpelling(tu, token)) + location := C.clang_getTokenLocation(tu, token) + var tokenOffset C.unsigned + C.clang_getExpansionLocation(location, nil, nil, nil, &tokenOffset) + if i == 0 { + // The first token is the macro name itself. + // Skip it (after using its location). + startOffset = int(tokenOffset) + } else { + // Later tokens are the macro contents. + for int(tokenOffset) > (startOffset + sourceBuf.Len()) { + // Pad the source text with whitespace (that must have been + // present in the original source as well). + sourceBuf.WriteByte(' ') + } + sourceBuf.WriteString(spelling) + } + } + value = sourceBuf.String() + + // Obtain the position of this token. This is the position of the first + // character in the 'value' string and is used to report errors at the + // correct location in the source file. + pos = f.getCursorPosition(c) + + return +} + func getString(clangString C.CXString) (s string) { rawString := C.clang_getCString(clangString) s = C.GoString(rawString) |