# #go #code-generation
Вопрос:
Я хочу извлечь сигнатуры функций, чтобы иметь возможность создавать над ними некоторые методы-оболочки. Для этого я использую golang.org/x/tools/go/packages
то, что дает мне возможность читать AST.
Например, для определения функции func MyFunc(param int)
вы получаете некоторые
ast.FuncDecl{
Type: *FieldList{
List: []*Field{
{
Names: []*Ident{ /*...*/ },
Type: nil, /*...*/
},
},
},
}
Где Тип представляет тип.
Я хочу сгенерировать специальный код для всех int
параметров, но int также может быть скрыт с помощью некоторого объявления типа
type MyType int
Как я могу преобразовать ast
тип в реальный, который есть у компилятора?
Комментарии:
1. Можете ли вы попытаться быть более точным? Что вы хотите превратить во что? Что вы подразумеваете под «преобразованием типа «ast» в реальный, который есть у компилятора» ?
2. @mkopriva: Я получаю часть кода с помощью
packages.Load
функции ( я загружаю какой — то пакет-нахожу там некоторые функции и пишу поверх них обертки ) . Таким образом, на этом шаге я могу получить список сигнатур функций . Для функцииfunc MyFunc(param1 MyType)
я получу как типast.Ident("MyType")
( это то, что я называю типом AST ) . Но у нас где — то есть другая строка:type MyType int
— и это то, что я хочу иметь : param1 имеет тип : «MyType», который может быть разрешен до типа int .3. У каждого загруженного пакета
packages.Load
естьTypesInfo
поле, это поле будет заполнено, если вы использовалиpackages.NeedTypesInfo
его в режиме загрузки. ТипTypesInfo
поля объявлен здесь , и вы можете использовать егоDefs
поле с*ast.Ident("MyType")
помощью, чтобы получить определение типа. Это то, о чем вы спрашиваете?4. Чтобы немного исправить мой предыдущий комментарий… вам также нужен
packages.NeedTypes
режим загрузки, и вам нужно использоватьTypes
поле вместоDefs
поля. Попробуйте запустить это: play.golang.org/p/93-DNIxPoMk (на игровой площадке это не работает из-за «обновления go.mod» или чего-то в этом роде), выполненный на моей машине, вывод выглядит следующим образом: imgur.com/zyZ7pRM .5. пожалуйста, не могли бы вы привести минимальный воспроизводимый пример. Что-то, что загружает немного кода, анализирует его и пытается использовать результат для получения некоторой информации. Я понимаю, что вам нужно иметь дело с пакетами типов больше, чем с ast. pkg.go.dev/go/типы , как предложил mkopriva.
Ответ №1:
Добавьте packages.NeedTypes
и packages.NeedTypesInfo
в режим загрузки. При этом у каждого загруженного пакета будет TypesInfo
инициализировано свое поле, и тип этого поля *types.Info
имеет поле, называемое Types
полем, которое сопоставляет выражения ast типам. Вы можете использовать это следующим образом:
func main() {
loadConfig := new(packages.Config)
loadConfig.Mode = packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo
loadConfig.Fset = token.NewFileSet()
pkgs, err := packages.Load(loadConfig, "syscall")
if err != nil {
panic(err)
}
for _, pkg := range pkgs {
for _, syn := range pkg.Syntax {
for _, dec := range syn.Decls {
if fd, ok := dec.(*ast.FuncDecl); ok amp;amp; fd.Name.Name == "Kill" {
x1 := fd.Type.Params.List[0].Type // int
x2 := fd.Type.Params.List[1].Type // syscall.Signal
tv1 := pkg.TypesInfo.Types[x1]
tv2 := pkg.TypesInfo.Types[x2]
if basic, ok := tv1.Type.(*types.Basic); ok {
fmt.Printf("%#vn", basic) // int
}
if named, ok := tv2.Type.(*types.Named); ok {
fmt.Printf("%vn", named.Obj()) // *types.TypeName (Signal)
fmt.Printf("%#vn", named.Underlying()) // *types.Basic (int)
}
}
}
}
}
}
Комментарии:
1. Я думаю, что ОП должна быть направлена УП.иди.Дев/идти/типы#AssignableTo / УП.иди.Дев/идти/типы#ConvertibleTo / УП.иди.Дев/идти/типы#сопоставимой и ПКГ.иди.Дев/идти/типы#ПКГ-переменные , чтобы помочь разобраться со своим описание проблемы. Но я признаю, что не совсем уверен, какой из них можно использовать для перебора определений и проверки их, например, для извлечения всех
~~int
параметров.