llcppsigfetch:determine unexpected named typedecl in typedef anonymous
This commit is contained in:
@@ -19,6 +19,28 @@ type Converter struct {
|
|||||||
curLoc ast.Location
|
curLoc ast.Location
|
||||||
index *clang.Index
|
index *clang.Index
|
||||||
unit *clang.TranslationUnit
|
unit *clang.TranslationUnit
|
||||||
|
|
||||||
|
typeDecls map[string]ast.Decl // cursorUsr -> ast.Decl
|
||||||
|
|
||||||
|
// anonyTypeMap stores mappings for unexpected named declarations in typedefs
|
||||||
|
// that actually represent anonymous types.
|
||||||
|
//
|
||||||
|
// Key: The USR (Unified Symbol Resolution) of the declaration cursor.
|
||||||
|
// Value: The generated name for the anonymous type.
|
||||||
|
//
|
||||||
|
// This map is necessary due to a limitation in libclang where anonymous
|
||||||
|
// structs, unions, or enums within typedefs are incorrectly reported as
|
||||||
|
// named declarations. We use this map to keep track of these cases and
|
||||||
|
// generate appropriate names for them.
|
||||||
|
//
|
||||||
|
// Additionally, for all nodes referencing these anonymous types, their
|
||||||
|
// name references are updated to use the corresponding anonyname from
|
||||||
|
// this map. This ensures consistent naming across the entire AST for
|
||||||
|
// these anonymous types.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// typedef struct { int x; } MyStruct;
|
||||||
|
anonyTypeMap map[string]string // cursorUsr -> anonyname
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -34,9 +56,11 @@ func NewConverter(config *Config) (*Converter, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Converter{
|
return &Converter{
|
||||||
Files: make(map[string]*ast.File),
|
Files: make(map[string]*ast.File),
|
||||||
index: index,
|
index: index,
|
||||||
unit: unit,
|
unit: unit,
|
||||||
|
anonyTypeMap: make(map[string]string),
|
||||||
|
typeDecls: make(map[string]ast.Decl),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +115,27 @@ func (ct *Converter) Dispose() {
|
|||||||
ct.unit.Dispose()
|
ct.unit.Dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ct *Converter) GetTokens(cursor clang.Cursor) []*ast.Token {
|
||||||
|
ran := cursor.Extent()
|
||||||
|
var numTokens c.Uint
|
||||||
|
var tokens *clang.Token
|
||||||
|
ct.unit.Tokenize(ran, &tokens, &numTokens)
|
||||||
|
defer ct.unit.DisposeTokens(tokens, numTokens)
|
||||||
|
|
||||||
|
tokensSlice := unsafe.Slice(tokens, int(numTokens))
|
||||||
|
|
||||||
|
result := make([]*ast.Token, 0, int(numTokens))
|
||||||
|
for _, tok := range tokensSlice {
|
||||||
|
tokStr := ct.unit.Token(tok)
|
||||||
|
result = append(result, &ast.Token{
|
||||||
|
Token: toToken(tok),
|
||||||
|
Lit: c.GoString(tokStr.CStr()),
|
||||||
|
})
|
||||||
|
tokStr.Dispose()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
|
func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
|
||||||
loc := cursor.Location()
|
loc := cursor.Location()
|
||||||
var file clang.File
|
var file clang.File
|
||||||
@@ -106,7 +151,6 @@ func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
|
|||||||
|
|
||||||
filePath := c.GoString(filename.CStr())
|
filePath := c.GoString(filename.CStr())
|
||||||
ct.curLoc = ast.Location{File: filePath}
|
ct.curLoc = ast.Location{File: filePath}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *Converter) GetCurFile() *ast.File {
|
func (ct *Converter) GetCurFile() *ast.File {
|
||||||
@@ -125,6 +169,21 @@ func (ct *Converter) GetCurFile() *ast.File {
|
|||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ct *Converter) AddTypeDecl(cursor clang.Cursor, decl ast.Decl) {
|
||||||
|
usr := cursor.USR()
|
||||||
|
usrStr := c.GoString(usr.CStr())
|
||||||
|
ct.typeDecls[usrStr] = decl
|
||||||
|
usr.Dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ct *Converter) GetTypeDecl(cursor clang.Cursor) (ast.Decl, bool) {
|
||||||
|
usr := cursor.USR()
|
||||||
|
usrStr := c.GoString(usr.CStr())
|
||||||
|
decl, ok := ct.typeDecls[usrStr]
|
||||||
|
usr.Dispose()
|
||||||
|
return decl, ok
|
||||||
|
}
|
||||||
|
|
||||||
func (ct *Converter) CreateDeclBase(cursor clang.Cursor) ast.DeclBase {
|
func (ct *Converter) CreateDeclBase(cursor clang.Cursor) ast.DeclBase {
|
||||||
rawComment := cursor.RawCommentText()
|
rawComment := cursor.RawCommentText()
|
||||||
defer rawComment.Dispose()
|
defer rawComment.Dispose()
|
||||||
@@ -274,11 +333,33 @@ func (ct *Converter) ProcessFunctionType(t clang.Type) *ast.FuncType {
|
|||||||
func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl {
|
func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl {
|
||||||
name := cursor.String()
|
name := cursor.String()
|
||||||
defer name.Dispose()
|
defer name.Dispose()
|
||||||
return &ast.TypedefDecl{
|
|
||||||
|
var typ ast.Expr
|
||||||
|
underlyingTyp := cursor.TypedefDeclUnderlyingType()
|
||||||
|
if underlyingTyp.Kind != clang.TypeElaborated {
|
||||||
|
typ = ct.ProcessType(underlyingTyp)
|
||||||
|
} else {
|
||||||
|
typ = ct.ProcessElaboratedType(underlyingTyp)
|
||||||
|
referTypeCursor := underlyingTyp.TypeDeclaration()
|
||||||
|
if _, ok := typ.(*ast.TagExpr); ok && isCursorChildOf(referTypeCursor, cursor) {
|
||||||
|
// Handle unexpected named structures generated from anonymous RecordTypes in Typedefs
|
||||||
|
// In this case, libclang incorrectly reports an anonymous struct as a named struct
|
||||||
|
// The reference style is TagRefer, for example: struct MyStruct
|
||||||
|
sourceCode := ct.GetTokens(referTypeCursor)
|
||||||
|
if sourceCode[0].Token == token.KEYWORD && (sourceCode[1].Token == token.PUNCT && sourceCode[1].Lit == "{") {
|
||||||
|
println("todo:unexpected named decl in typedef anonymous decl")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decl := &ast.TypedefDecl{
|
||||||
DeclBase: ct.CreateDeclBase(cursor),
|
DeclBase: ct.CreateDeclBase(cursor),
|
||||||
Name: &ast.Ident{Name: c.GoString(name.CStr())},
|
Name: &ast.Ident{Name: c.GoString(name.CStr())},
|
||||||
Type: ct.ProcessType(cursor.TypedefDeclUnderlyingType()),
|
Type: typ,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ct.AddTypeDecl(cursor, decl)
|
||||||
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes.
|
// converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes.
|
||||||
@@ -295,25 +376,27 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
|
|||||||
}
|
}
|
||||||
params := ct.ProcessFieldList(cursor)
|
params := ct.ProcessFieldList(cursor)
|
||||||
funcType.Params = params
|
funcType.Params = params
|
||||||
fn := &ast.FuncDecl{
|
funcDecl := &ast.FuncDecl{
|
||||||
DeclBase: ct.CreateDeclBase(cursor),
|
DeclBase: ct.CreateDeclBase(cursor),
|
||||||
Name: &ast.Ident{Name: c.GoString(name.CStr())},
|
Name: &ast.Ident{Name: c.GoString(name.CStr())},
|
||||||
Type: funcType,
|
Type: funcType,
|
||||||
}
|
}
|
||||||
|
|
||||||
if cursor.IsFunctionInlined() != 0 {
|
if cursor.IsFunctionInlined() != 0 {
|
||||||
fn.IsInline = true
|
funcDecl.IsInline = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if isMethod(cursor) {
|
if isMethod(cursor) {
|
||||||
ct.ProcessMethodAttributes(cursor, fn)
|
ct.ProcessMethodAttributes(cursor, funcDecl)
|
||||||
} else {
|
} else {
|
||||||
if cursor.StorageClass() == clang.SCStatic {
|
if cursor.StorageClass() == clang.SCStatic {
|
||||||
fn.IsStatic = true
|
funcDecl.IsStatic = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn
|
ct.AddTypeDecl(cursor, funcDecl)
|
||||||
|
|
||||||
|
return funcDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
// get Methods Attributes
|
// get Methods Attributes
|
||||||
@@ -392,11 +475,13 @@ func (ct *Converter) ProcessEnumDecl(cursor clang.Cursor) *ast.EnumTypeDecl {
|
|||||||
name := cursor.String()
|
name := cursor.String()
|
||||||
defer name.Dispose()
|
defer name.Dispose()
|
||||||
|
|
||||||
return &ast.EnumTypeDecl{
|
decl := &ast.EnumTypeDecl{
|
||||||
DeclBase: ct.CreateDeclBase(cursor),
|
DeclBase: ct.CreateDeclBase(cursor),
|
||||||
Name: &ast.Ident{Name: c.GoString(name.CStr())},
|
Name: &ast.Ident{Name: c.GoString(name.CStr())},
|
||||||
Type: ct.ProcessEnumType(cursor),
|
Type: ct.ProcessEnumType(cursor),
|
||||||
}
|
}
|
||||||
|
ct.AddTypeDecl(cursor, decl)
|
||||||
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
// current only collect macro which defined in file
|
// current only collect macro which defined in file
|
||||||
@@ -404,26 +489,9 @@ func (ct *Converter) ProcessMacro(cursor clang.Cursor) *ast.Macro {
|
|||||||
name := cursor.String()
|
name := cursor.String()
|
||||||
defer name.Dispose()
|
defer name.Dispose()
|
||||||
|
|
||||||
ran := cursor.Extent()
|
|
||||||
var numTokens c.Uint
|
|
||||||
var tokens *clang.Token
|
|
||||||
ct.unit.Tokenize(ran, &tokens, &numTokens)
|
|
||||||
defer ct.unit.DisposeTokens(tokens, numTokens)
|
|
||||||
|
|
||||||
tokensSlice := unsafe.Slice(tokens, int(numTokens))
|
|
||||||
|
|
||||||
macro := &ast.Macro{
|
macro := &ast.Macro{
|
||||||
Name: c.GoString(name.CStr()),
|
Name: c.GoString(name.CStr()),
|
||||||
Tokens: make([]*ast.Token, 0),
|
Tokens: ct.GetTokens(cursor),
|
||||||
}
|
|
||||||
|
|
||||||
for _, tok := range tokensSlice {
|
|
||||||
tokStr := ct.unit.Token(tok)
|
|
||||||
macro.Tokens = append(macro.Tokens, &ast.Token{
|
|
||||||
Token: toToken(tok),
|
|
||||||
Lit: c.GoString(tokStr.CStr()),
|
|
||||||
})
|
|
||||||
tokStr.Dispose()
|
|
||||||
}
|
}
|
||||||
return macro
|
return macro
|
||||||
}
|
}
|
||||||
@@ -534,7 +602,6 @@ func (ct *Converter) ProcessMethods(cursor clang.Cursor) []*ast.FuncDecl {
|
|||||||
|
|
||||||
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
|
func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
|
||||||
anony := cursor.IsAnonymousRecordDecl()
|
anony := cursor.IsAnonymousRecordDecl()
|
||||||
|
|
||||||
var name *ast.Ident
|
var name *ast.Ident
|
||||||
if anony == 0 {
|
if anony == 0 {
|
||||||
cursorName := cursor.String()
|
cursorName := cursor.String()
|
||||||
@@ -542,11 +609,14 @@ func (ct *Converter) ProcessRecordDecl(cursor clang.Cursor) *ast.TypeDecl {
|
|||||||
name = &ast.Ident{Name: c.GoString(cursorName.CStr())}
|
name = &ast.Ident{Name: c.GoString(cursorName.CStr())}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ast.TypeDecl{
|
decl := &ast.TypeDecl{
|
||||||
DeclBase: ct.CreateDeclBase(cursor),
|
DeclBase: ct.CreateDeclBase(cursor),
|
||||||
Name: name,
|
Name: name,
|
||||||
Type: ct.ProcessRecordType(cursor),
|
Type: ct.ProcessRecordType(cursor),
|
||||||
}
|
}
|
||||||
|
ct.AddTypeDecl(cursor, decl)
|
||||||
|
|
||||||
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl {
|
func (ct *Converter) ProcessStructDecl(cursor clang.Cursor) *ast.TypeDecl {
|
||||||
@@ -560,14 +630,16 @@ func (ct *Converter) ProcessUnionDecl(cursor clang.Cursor) *ast.TypeDecl {
|
|||||||
func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl {
|
func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl {
|
||||||
// Pushing class scope before processing its type and popping after
|
// Pushing class scope before processing its type and popping after
|
||||||
base := ct.CreateDeclBase(cursor)
|
base := ct.CreateDeclBase(cursor)
|
||||||
|
|
||||||
typ := ct.ProcessRecordType(cursor)
|
typ := ct.ProcessRecordType(cursor)
|
||||||
|
|
||||||
return &ast.TypeDecl{
|
decl := &ast.TypeDecl{
|
||||||
DeclBase: base,
|
DeclBase: base,
|
||||||
Name: &ast.Ident{Name: c.GoString(cursor.String().CStr())},
|
Name: &ast.Ident{Name: c.GoString(cursor.String().CStr())},
|
||||||
Type: typ,
|
Type: typ,
|
||||||
}
|
}
|
||||||
|
ct.AddTypeDecl(cursor, decl)
|
||||||
|
|
||||||
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
|
func (ct *Converter) ProcessRecordType(cursor clang.Cursor) *ast.RecordType {
|
||||||
@@ -598,12 +670,14 @@ func (ct *Converter) ProcessElaboratedType(t clang.Type) ast.Expr {
|
|||||||
|
|
||||||
decl := t.TypeDeclaration()
|
decl := t.TypeDeclaration()
|
||||||
if decl.IsAnonymous() != 0 {
|
if decl.IsAnonymous() != 0 {
|
||||||
|
// anonymous type refer (except anonymous RecordType&EnumType in TypedefDecl)
|
||||||
if decl.Kind == clang.CursorEnumDecl {
|
if decl.Kind == clang.CursorEnumDecl {
|
||||||
return ct.ProcessEnumType(decl)
|
return ct.ProcessEnumType(decl)
|
||||||
}
|
}
|
||||||
return ct.ProcessRecordType(decl)
|
return ct.ProcessRecordType(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type name refer
|
||||||
typeName := c.GoString(name.CStr())
|
typeName := c.GoString(name.CStr())
|
||||||
|
|
||||||
tagMap := map[string]ast.Tag{
|
tagMap := map[string]ast.Tag{
|
||||||
@@ -754,3 +828,22 @@ func buildScopingFromParts(parts []string) ast.Expr {
|
|||||||
}
|
}
|
||||||
return expr
|
return expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isCursorChildOf checks if the child cursor is contained within the parent cursor.
|
||||||
|
// This function is necessary because libclang doesn't correctly report the lexical
|
||||||
|
// or semantic parent for anonymous structs inside typedefs. By comparing source ranges,
|
||||||
|
// we can determine if one cursor is nested inside another.
|
||||||
|
func isCursorChildOf(child, parent clang.Cursor) bool {
|
||||||
|
return isRangeChildOf(child.Extent(), parent.Extent())
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRangeChildOf(childRange, parentRange clang.SourceRange) bool {
|
||||||
|
return getOffset(childRange.RangeStart()) >= getOffset(parentRange.RangeStart()) &&
|
||||||
|
getOffset(childRange.RangeEnd()) <= getOffset(parentRange.RangeEnd())
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOffset(location clang.SourceLocation) c.Uint {
|
||||||
|
var offset c.Uint
|
||||||
|
location.SpellingLocation(nil, nil, nil, &offset)
|
||||||
|
return offset
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user