Как разрешить типы в примитивы в синтаксическом анализаторе AST

# #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 параметров.