go/types with typedef-of-typedef?

219 views
Skip to first unread message

Tim Hockin

unread,
Jul 26, 2024, 12:30:35 AM7/26/24
to golang-nuts
Hi all,  I have been banging my head on this all day.  I am doinking around in some code-generation and find myself deep into the go/types package.

Given code like:

```
type E1 string
type E2 E1
```

I loaded the package with x/tools/go/packages.  I got the root scope via `pkg.Types.Scope()`.  I did a `Lookup(typename)` and got a `TypeName` back.  `TypeName.Type()` gets me a `Named`.

Here's where I am stuck.

In the debugger I can see that E1 and E2 are different.  E1's `fromRHS` field tells me it is a `go/types.Basic` with `.name` =  "string".  E2's `fromRHS` tells me it is `go/types.Named` which is an E1.

Awesome - that is what I want.  But I can't figure out how to learn that through the public API.  AFAICT, `fromRHS` is only used internally and calling `Underlying()` on E1 and E2 yields a string in both cases.

Is there any way to learn what I am trying to learn?  The information is THERE but I can't seem to get at it.

Tim

Jason E. Aten

unread,
Jul 26, 2024, 6:39:33 PM7/26/24
to golang-nuts
I don't know if this is the only way, or the best way, but you can get all distinct types with the "golang.org/x/tools/go/loader" package. Here is an example, in the context of a code generator (greenpack) that produces serialization code:

https://github.com/glycerine/greenpack/blob/master/parse/getast.go#L95

which then calls getTypeSpecs()

which is implemented here

Jason E. Aten

unread,
Jul 26, 2024, 7:23:49 PM7/26/24
to golang-nuts
To be more specific, 

ast.TypeSpec.Name.Name is, I think, what you are after.


And the TypeSpec are found in the ast.File.Decls, after type switching in the manner of getast.go#360


Tim Hockin

unread,
Jul 27, 2024, 10:13:37 PM7/27/24
to Jason E. Aten, golang-nuts
How do you make the jump from a go/type.TypeName to ast Decl?  Is there some easy crosslink?

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/eQxFFSRKL24/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/01c4a98c-30f4-4cd6-bc7f-a7edf781641an%40googlegroups.com.

Jason E. Aten

unread,
Jul 29, 2024, 4:39:30 PM7/29/24
to golang-nuts
I don't know. 

Here is what ChatGPT 4o mini suggested, accessed at https://chatgpt.com/; with some minor corrections to make it build. 

It seems to suggest there is no shortcut crosslink. 

The code below does demonstrate how to use the more up-to-date "golang.org/x/tools/go/packages" which handles modern modules (versus the older "golang.org/x/tools/go/loader", pointed to/demonstrated above, which does not).

Q: In golang, How do you make the jump from a go/type.TypeName to ast.Decl ?

Answer:

In Go, if you want to convert a type or a type name (go/types.TypeName) to an ast.Decl, you're essentially looking to map a type information structure from the go/types package to its corresponding declaration in the go/token package.

The go/types.TypeName represents a type name in the Go type system, while ast.Decl is part of the abstract syntax tree (AST) and represents a declaration in Go source code.

Here's a general approach you can take to achieve this:

  1. Locate the Source Position: Use the go/types package to get the position of the type declaration. The go/types.TypeName object should have information about its position in the source code, but this might be a bit indirect since go/types does not provide direct mapping to ast.Decl.

  2. Read the Source File: Once you have the position, you can use the go/parser package to read and parse the source file to find the specific declaration.

  3. Map the Position to the AST Declaration: Using the source file content and the position, you can find the corresponding declaration in the AST.

Here’s a step-by-step outline of how you might implement this:

Example Code

package main

import (
"fmt"
//"go/parser"
"go/token"
//"go/types"
"log"
"golang.org/x/tools/go/packages"
)

func findDecl(pkgPath string, typeName string) (*token.Position, string) {
// Load the package
cfg := &packages.Config{Mode: packages.NeedTypes | packages.NeedFiles | packages.NeedSyntax}
pkgs, err := packages.Load(cfg, pkgPath)
if err != nil {
log.Fatalf("failed to load package: %v", err)
}

// Find the type name
var typePos token.Position
for _, pkg := range pkgs {
for _, file := range pkg.Syntax {
for _, decl := range file.Decls {
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
for _, spec := range genDecl.Specs {
if typeSpec, ok := spec.(*ast.TypeSpec); ok && typeSpec.Name.Name == typeName {
// Found the type declaration
typePos = pkg.Fset.Position(typeSpec.Pos())
return &typePos, pkg.Fset.File(typeSpec.Pos()).Name()
}
}
}
}
}
}
return nil, ""
}

func main() {
// Example usage
pos, file := findDecl("your_package_path", "YourTypeName")
if pos != nil {
fmt.Printf("Type declaration found in file %s at position %v\n", file, *pos)
} else {
fmt.Println("Type declaration not found")
}
}

Explanation
  1. Load Package: Use golang.org/x/tools/go/packages to load the Go package you’re interested in. This will give you access to the syntax trees and type information.

  2. Search for Type Declaration: Iterate through the syntax trees to find the type declaration that matches your typeName. This is done by checking each declaration in the file to see if it matches the type you're looking for.

  3. Get Position: Once you find the type declaration, get its position using pkg.Fset.Position(). This gives you the line and column information of the declaration.

  4. Return Results: Return the position and filename where the type declaration was found.

Notes
  • This approach assumes that you have access to the source code files and that you can load them using the packages package.
  • The go/types package itself does not directly provide a way to get an ast.Decl, so this indirect approach through positions and source file parsing is necessary.
  • Make sure to adjust pkgPath and YourTypeName according to your specific use case.

This method is useful for tooling and analysis where you need to connect type information with its corresponding code in the source file.

end Answer.

Reply all
Reply to author
Forward
0 new messages