llcppsigfetch:refactor type reference test logic

This commit is contained in:
luoliwoshang
2024-08-23 14:38:51 +08:00
parent e57ea9b501
commit 3ac95a9213
10 changed files with 257 additions and 775 deletions

View File

@@ -1,8 +1,12 @@
package cvttest package cvttest
import ( import (
"fmt"
"unsafe"
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse" "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse"
) )
@@ -30,3 +34,52 @@ func RunTest(testName string, testCases []string) {
converter.Dispose() converter.Dispose()
} }
} }
type GetTypeOptions struct {
TypeCode string // e.g. "char*", "char**"
// ExpectTypeKind specifies the expected type kind (optional)
// Use clang.Type_Invalid to accept any type (default behavior)
// *For complex types (when <complex.h> is included), specifying this is crucial
// to filter out the correct type, as there will be multiple VarDecl fields present
ExpectTypeKind clang.TypeKind
// Args contains additional compilation arguments passed to Clang (optional)
// Default is []string{"-x", "c++", "-std=c++11"}
// *For complex C types, C language args Must be specified, e.g., []string{"-x", "c", "-std=c99"}
Args []string
}
// GetType returns the clang.Type of the given type code
// Need to dispose the index and unit after using
// e.g. index.Dispose(), unit.Dispose()
func GetType(option *GetTypeOptions) (clang.Type, *clang.Index, *clang.TranslationUnit) {
code := fmt.Sprintf("%s placeholder;", option.TypeCode)
index, unit, err := parse.CreateTranslationUnit(&parse.Config{
File: code,
Temp: true,
Args: option.Args,
})
if err != nil {
panic(err)
}
cursor := unit.Cursor()
visitType := &typeVisitData{typ: &clang.Type{}, expectTypeKind: option.ExpectTypeKind}
clang.VisitChildren(cursor, typeVisit, unsafe.Pointer(visitType))
return *visitType.typ, index, unit
}
type typeVisitData struct {
typ *clang.Type
expectTypeKind clang.TypeKind
}
func typeVisit(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult {
visitData := (*typeVisitData)(clientData)
if cursor.Kind == clang.CursorVarDecl && (visitData.expectTypeKind == clang.TypeInvalid || cursor.Type().Kind == visitData.expectTypeKind) {
*visitData.typ = cursor.Type()
return clang.ChildVisit_Break
}
return clang.ChildVisit_Continue
}

View File

@@ -10,17 +10,7 @@ func TestFuncDecl() {
testCases := []string{ testCases := []string{
`void foo();`, `void foo();`,
`void foo(int a);`, `void foo(int a);`,
`float foo(int a,double b);`,
`float* foo(int a,double b);`, `float* foo(int a,double b);`,
`void foo(char* str);`,
`void* foo(char*** str);`,
`void foo(char str[]);`,
`void foo(int arr[3][4]);`,
`void foo(int& a);`,
`void foo(int&& a);`,
} }
test.RunTest("TestFuncDecl", testCases) test.RunTest("TestFuncDecl", testCases)
} }

View File

@@ -72,63 +72,6 @@ TestFuncDecl Case 2:
} }
TestFuncDecl Case 3: TestFuncDecl Case 3:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"Kind": 6,
"Flags": 0
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "a"
}]
}, {
"Type": {
"Kind": 8,
"Flags": 16
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "b"
}]
}]
},
"Ret": {
"Kind": 8,
"Flags": 0
}
}
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 4:
{ {
"temp.h": { "temp.h": {
"decls": [{ "decls": [{
@@ -187,293 +130,6 @@ TestFuncDecl Case 4:
} }
} }
TestFuncDecl Case 5:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"X": {
"Kind": 2,
"Flags": 1
}
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "str"
}]
}]
},
"Ret": {
"Kind": 0,
"Flags": 0
}
}
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 6:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"X": {
"X": {
"X": {
"Kind": 2,
"Flags": 1
}
}
}
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "str"
}]
}]
},
"Ret": {
"X": {
"Kind": 0,
"Flags": 0
}
}
}
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 7:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"Elt": {
"Kind": 2,
"Flags": 1
},
"Len": null
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "str"
}]
}]
},
"Ret": {
"Kind": 0,
"Flags": 0
}
}
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 8:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"Elt": {
"Elt": {
"Kind": 6,
"Flags": 0
},
"Len": {
"Kind": 0,
"Value": "4"
}
},
"Len": {
"Kind": 0,
"Value": "3"
}
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "arr"
}]
}]
},
"Ret": {
"Kind": 0,
"Flags": 0
}
}
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 9:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"X": {
"Kind": 6,
"Flags": 0
}
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "a"
}]
}]
},
"Ret": {
"Kind": 0,
"Flags": 0
}
}
}],
"includes": [],
"macros": []
}
}
TestFuncDecl Case 10:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": [{
"Type": {
"X": {
"Kind": 6,
"Flags": 0
}
},
"Doc": {
"List": []
},
"Comment": {
"List": []
},
"Names": [{
"Name": "a"
}]
}]
},
"Ret": {
"Kind": 0,
"Flags": 0
}
}
}],
"includes": [],
"macros": []
}
}
#stderr #stderr

View File

@@ -200,96 +200,6 @@ TestScope Case 5:
} }
} }
TestScope Case 6:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": {
"X": {
"Name": "b"
},
"Parent": {
"Name": "a"
}
},
"Name": {
"Name": "c"
},
"Type": {
"Tag": 3,
"Fields": {
"List": []
},
"Methods": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": {
"X": {
"Name": "c"
},
"Parent": {
"X": {
"Name": "b"
},
"Parent": {
"Name": "a"
}
}
},
"Name": {
"Name": "foo"
},
"Type": {
"Params": {
"List": []
},
"Ret": {
"Kind": 0,
"Flags": 0
}
}
}]
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "C"
},
"Type": {
"X": {
"Name": "c"
},
"Parent": {
"X": {
"Name": "b"
},
"Parent": {
"Name": "a"
}
}
}
}],
"includes": [],
"macros": []
}
}
#stderr #stderr

View File

@@ -25,15 +25,6 @@ func TestScope() {
void foo(); void foo();
}; };
}`, }`,
`namespace a {
namespace b {
class c {
void foo();
};
}
}
typedef a::b::c C;`,
} }
test.RunTest("TestScope", testCases) test.RunTest("TestScope", testCases)
} }

View File

@@ -21,7 +21,7 @@ func TestStructDecl() {
`struct A { `struct A {
int a; int a;
int b; int b;
float foo(int a,double b);; float foo(int a,double b);
};`, };`,
} }
test.RunTest("TestStructDecl", testCases) test.RunTest("TestStructDecl", testCases)

View File

@@ -61,280 +61,6 @@ TestTypeDefDecl Case 2:
} }
} }
TestTypeDefDecl Case 3:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "StructFoo"
},
"Type": {
"Tag": 0,
"Fields": {
"List": []
},
"Methods": []
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "UnionFoo"
},
"Type": {
"Tag": 1,
"Fields": {
"List": []
},
"Methods": []
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "ClassFoo"
},
"Type": {
"Tag": 3,
"Fields": {
"List": []
},
"Methods": []
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "EnumFoo"
},
"Items": []
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "STRUCT_FOO"
},
"Type": {
"Name": "StructFoo"
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "UNION_FOO"
},
"Type": {
"Name": "UnionFoo"
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "CLASS_FOO"
},
"Type": {
"Name": "ClassFoo"
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "ENUM_FOO"
},
"Type": {
"Name": "EnumFoo"
}
}],
"includes": [],
"macros": []
}
}
TestTypeDefDecl Case 4:
{
"temp.h": {
"decls": [{
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "StructFoo"
},
"Type": {
"Tag": 0,
"Fields": {
"List": []
},
"Methods": []
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "UnionFoo"
},
"Type": {
"Tag": 1,
"Fields": {
"List": []
},
"Methods": []
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "ClassFoo"
},
"Type": {
"Tag": 3,
"Fields": {
"List": []
},
"Methods": []
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "EnumFoo"
},
"Items": []
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "STRUCT_FOO"
},
"Type": {
"Name": {
"Name": "StructFoo"
},
"Tag": 0
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "UNION_FOO"
},
"Type": {
"Name": {
"Name": "UnionFoo"
},
"Tag": 1
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "CLASS_FOO"
},
"Type": {
"Name": {
"Name": "ClassFoo"
},
"Tag": 3
}
}, {
"Loc": {
"File": "temp.h"
},
"Doc": {
"List": []
},
"Parent": null,
"Name": {
"Name": "ENUM_FOO"
},
"Type": {
"Name": {
"Name": "EnumFoo"
},
"Tag": 2
}
}],
"includes": [],
"macros": []
}
}
#stderr #stderr

View File

@@ -12,24 +12,6 @@ func TestTypeDefDecl() {
`typedef int INT; `typedef int INT;
typedef INT STANDARD_INT;`, typedef INT STANDARD_INT;`,
`struct StructFoo {};
union UnionFoo {};
class ClassFoo {};
enum EnumFoo {};
typedef StructFoo STRUCT_FOO;
typedef UnionFoo UNION_FOO;
typedef ClassFoo CLASS_FOO;
typedef EnumFoo ENUM_FOO;`,
`struct StructFoo {};
union UnionFoo {};
class ClassFoo {};
enum EnumFoo {};
typedef struct StructFoo STRUCT_FOO;
typedef union UnionFoo UNION_FOO;
typedef class ClassFoo CLASS_FOO;
typedef enum EnumFoo ENUM_FOO;`,
} }
test.RunTest("TestTypeDefDecl", testCases) test.RunTest("TestTypeDefDecl", testCases)
} }

View File

@@ -26,6 +26,132 @@ Complex:flags:0 kind:11
Complex:flags:16 kind:11 Complex:flags:16 kind:11
Complex:flags:20 kind:11 Complex:flags:20 kind:11
Unknown:flags:0 kind:0 Unknown:flags:0 kind:0
Type: char *:
{
"X": {
"Kind": 2,
"Flags": 1
}
}
Type: char ***:
{
"X": {
"X": {
"X": {
"Kind": 2,
"Flags": 1
}
}
}
}
Type: char[]:
{
"Elt": {
"Kind": 2,
"Flags": 1
},
"Len": null
}
Type: char[10]:
{
"Elt": {
"Kind": 2,
"Flags": 1
},
"Len": {
"Kind": 0,
"Value": "10"
}
}
Type: char[3][4]:
{
"Elt": {
"Elt": {
"Kind": 2,
"Flags": 1
},
"Len": {
"Kind": 0,
"Value": "4"
}
},
"Len": {
"Kind": 0,
"Value": "3"
}
}
Type: int &:
{
"X": {
"Kind": 6,
"Flags": 0
}
}
Type: int &&:
{
"X": {
"Kind": 6,
"Flags": 0
}
}
Type: Foo:
{
"Name": "Foo"
}
Type: struct Foo:
{
"Name": {
"Name": "Foo"
},
"Tag": 0
}
Type: Foo:
{
"Name": "Foo"
}
Type: union Foo:
{
"Name": {
"Name": "Foo"
},
"Tag": 1
}
Type: Foo:
{
"Name": "Foo"
}
Type: enum Foo:
{
"Name": {
"Name": "Foo"
},
"Tag": 2
}
Type: Foo:
{
"Name": "Foo"
}
Type: class Foo:
{
"Name": {
"Name": "Foo"
},
"Tag": 3
}
Type: a::b::c:
{
"X": {
"Name": "c"
},
"Parent": {
"X": {
"Name": "b"
},
"Parent": {
"Name": "a"
}
}
}
#stderr #stderr
todo: unknown builtin type: Ibm128 todo: unknown builtin type: Ibm128

View File

@@ -2,15 +2,18 @@ package main
import ( import (
"fmt" "fmt"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/c/clang" "github.com/goplus/llgo/c/clang"
"github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse" "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse"
test "github.com/goplus/llgo/chore/_xtool/llcppsigfetch/parse/cvt_test"
"github.com/goplus/llgo/chore/llcppg/ast" "github.com/goplus/llgo/chore/llcppg/ast"
) )
func main() { func main() {
TestBuiltinType() TestBuiltinType()
TestNonBuiltinTypes()
} }
func TestBuiltinType() { func TestBuiltinType() {
tests := []struct { tests := []struct {
@@ -41,9 +44,9 @@ func TestBuiltinType() {
{"Double", btType(clang.TypeDouble), ast.BuiltinType{Kind: ast.Float, Flags: ast.Double}}, {"Double", btType(clang.TypeDouble), ast.BuiltinType{Kind: ast.Float, Flags: ast.Double}},
{"LongDouble", btType(clang.TypeLongDouble), ast.BuiltinType{Kind: ast.Float, Flags: ast.Long | ast.Double}}, {"LongDouble", btType(clang.TypeLongDouble), ast.BuiltinType{Kind: ast.Float, Flags: ast.Long | ast.Double}},
{"Float128", btType(clang.TypeFloat128), ast.BuiltinType{Kind: ast.Float128}}, {"Float128", btType(clang.TypeFloat128), ast.BuiltinType{Kind: ast.Float128}},
{"Complex", mockComplexType(0), ast.BuiltinType{Kind: ast.Complex}}, {"Complex", getComplexType(0), ast.BuiltinType{Kind: ast.Complex}},
{"Complex", mockComplexType(ast.Double), ast.BuiltinType{Flags: ast.Double, Kind: ast.Complex}}, {"Complex", getComplexType(ast.Double), ast.BuiltinType{Flags: ast.Double, Kind: ast.Complex}},
{"Complex", mockComplexType(ast.Long | ast.Double), ast.BuiltinType{Flags: ast.Long | ast.Double, Kind: ast.Complex}}, {"Complex", getComplexType(ast.Long | ast.Double), ast.BuiltinType{Flags: ast.Long | ast.Double, Kind: ast.Complex}},
{"Unknown", btType(clang.TypeIbm128), ast.BuiltinType{Kind: ast.Void}}, {"Unknown", btType(clang.TypeIbm128), ast.BuiltinType{Kind: ast.Void}},
} }
@@ -61,20 +64,72 @@ func TestBuiltinType() {
} }
} }
func TestNonBuiltinTypes() {
tests := []string{
"char*",
"char***",
"char[]",
"char[10]",
"char[3][4]",
"int&",
"int&&",
`struct Foo {};
Foo`,
`struct Foo {};
struct Foo`,
`union Foo {};
Foo`,
`union Foo {};
union Foo`,
`enum Foo {};
Foo`,
`enum Foo {};
enum Foo`,
`class Foo {};
Foo`,
`class Foo {};
class Foo`,
`namespace a {
namespace b {
class c {
};
}
}
a::b::c`,
}
for _, t := range tests {
typ, index, unit := test.GetType(&test.GetTypeOptions{
TypeCode: t,
})
converter := &parse.Converter{}
expr := converter.ProcessType(typ)
json := parse.MarshalASTExpr(expr)
str := json.Print()
c.Printf(c.Str("Type: %s:\n"), typ.String())
c.Printf(c.Str("%s\n"), str)
cjson.FreeCStr(str)
json.Delete()
index.Dispose()
unit.Dispose()
}
}
func btType(kind clang.TypeKind) clang.Type { func btType(kind clang.TypeKind) clang.Type {
return clang.Type{Kind: kind} return clang.Type{Kind: kind}
} }
func visit(cursor, parent clang.Cursor, clientData unsafe.Pointer) clang.ChildVisitResult { // get complex type from source code parsed
typ := (*clang.Type)(clientData) func getComplexType(flag ast.TypeFlag) clang.Type {
if cursor.Kind == clang.CursorVarDecl && cursor.Type().Kind == clang.TypeComplex {
*typ = cursor.Type()
}
return clang.ChildVisit_Continue
}
// mock complex type, this type cannot be directly created in Go
func mockComplexType(flag ast.TypeFlag) clang.Type {
var typeStr string var typeStr string
if flag&(ast.Long|ast.Double) == (ast.Long | ast.Double) { if flag&(ast.Long|ast.Double) == (ast.Long | ast.Double) {
typeStr = "long double" typeStr = "long double"
@@ -84,21 +139,14 @@ func mockComplexType(flag ast.TypeFlag) clang.Type {
typeStr = "float" typeStr = "float"
} }
code := fmt.Sprintf("#include <complex.h>\n%s complex z;", typeStr) code := fmt.Sprintf("#include <complex.h>\n%s complex", typeStr)
index, unit, err := parse.CreateTranslationUnit(&parse.Config{
File: code, // todo(zzy):free index and unit after test
Temp: true, typ, _, _ := test.GetType(&test.GetTypeOptions{
Args: []string{"-x", "c", "-std=c99"}, TypeCode: code,
ExpectTypeKind: clang.TypeComplex,
Args: []string{"-x", "c", "-std=c99"},
}) })
if err != nil {
panic(err)
}
defer index.Dispose() return typ
cursor := unit.Cursor()
complex := &clang.Type{}
clang.VisitChildren(cursor, visit, unsafe.Pointer(complex))
return *complex
} }