0

I have a project that looks like this:

mymodule
- mypackage1
-- generate.go  # Generates documentation from models.go
- mypackage2
-- models.go  # Defines structs
- go.mod  # First line "module github.com/mymodule"
- go.sum

In mypackage1/generate.go, I want to import and document the structs defined in mypackage2/models.go. To do that, I tried executing this code in mypackage1/generate.go, modified from here and here:

_ = mypackage2.Mytype{}
pkg, err := importer.Default().Import("example.com/mymodule/mypackage2")
if err != nil {
    fmt.Println("error:", err)
}
for _, declName := range pkg.Scope().Names() {
    fmt.Println(declName)
}

mypackage2 is successfully imported and the first line of this block compiles without problem. But the call to importer fails and prints this error:

error: can't find import: "example.com/mymodule/mypackage2"

The import does work for fmt, but not for github.com/gorilla/mux. Does anyone have an idea what might be going wrong?

This question (How to use go importer ) is the most relevant, but I get the same error following these instructions for github.com/gorilla/mux (a dependency of my package).

This question explains to use the module name defined in go.mod, but I already do that. Neither that nor the mypackage2 standalone name works.

This question is related but uses a deprecated API and has no answer.

FvD
  • 1,286
  • 13
  • 25
  • 1
    [This](https://stackoverflow.com/questions/68794847/list-all-imported-types-given-an-import-path/68796988#68796988) should work for you. Use the [`LoadMode`](https://pkg.go.dev/golang.org/x/tools/go/packages#LoadMode) to request mode info from the loader. – mkopriva Dec 08 '21 at 05:32
  • Thanks for the pointer. That seems to load the module, but the Syntax array is empty. If I change the LoadMode to `NeedTypes`, I get `panic: runtime error: invalid memory address or nil pointer dereference ` from `packages.Load`. – FvD Dec 08 '21 at 07:08
  • 1
    You need to add `NeedSyntax` and, probably, also `NeedTypesInfo`. – mkopriva Dec 08 '21 at 07:10
  • With `loadConfig.Mode = packages.NeedSyntax + packages.NeedTypesInfo` the module loads, but both `Syntax` and `TypesInfo` are empty. This is true for `mypackage` as well as the `encoding/json` example used in the link. – FvD Dec 08 '21 at 07:12
  • 1
    Those enums need to be added with a pipe, not a plus. e.g. `packages.NeedSyntax | packages.NeedTypesInfo` – mkopriva Dec 08 '21 at 07:13
  • Thanks! But the problem persists :) – FvD Dec 08 '21 at 07:16
  • https://go.dev/play/p/uzGzoWSVrd0 -- when I run it I get the following output: https://imgur.com/3907aVO – mkopriva Dec 08 '21 at 07:28
  • If you care only about the AST, then [this](https://go.dev/play/p/KygQkBvoSTl) works as well: https://imgur.com/wh5mOzz – mkopriva Dec 08 '21 at 07:38
  • Thank you for the extensive examples, but I get the same panic at `packages.Load()`. If I remove `packages.loadTypes` it does not panic, but the result has no Syntax or TypesInfo. Am I missing something basic or is something wrong with my installation? – FvD Dec 08 '21 at 08:18
  • You will have to update the question with the exact code that panics and also show the panic's message with, ideally, the full stack trace. – mkopriva Dec 08 '21 at 08:20
  • 1
    I finished setting up the toy problem as described in the question, and the panic does not occur there. The problem might be that I am running the original code inside a test. I will investigate and update the question tomorrow. Thanks for the help once again. Your comments would make helpful full answers. – FvD Dec 08 '21 at 08:35
  • isn't it normally the other way around? One module has many packages. But not one package has many modules. – The Fool Dec 13 '21 at 08:43
  • Yes, I also noticed that I mixed it up. I'll rewrite it to avoid confusion. – FvD Dec 13 '21 at 08:46
  • Regarding the error when using `packages.needTypes`: one of our vendored packages included an outdated version of `/x/tools/`. The error disappeared after upgrading. – FvD Dec 22 '21 at 05:46

1 Answers1

0

The importer package is apparently superceded by packages. I suspect that the problem is related to the GOPATH setting, which has been abandoned in favor of using modules. It appears that packages (written in 2018) knows how to parse the modules, while importer (written in 2012) does not.

The code below suggested by @mkopriva (in the comments and here) using packages instead of importer does what I was attempting to do, and parses the package contents.

To print each type/struct from the loaded package:

package main

import (
    "fmt"
    "go/ast"
    "go/token"
    "go/types"

    "golang.org/x/tools/go/packages"
)

const loadMode = packages.NeedName |
    packages.NeedFiles |
    packages.NeedCompiledGoFiles |
    packages.NeedImports |
    packages.NeedDeps |
    packages.NeedTypes |
    packages.NeedSyntax |
    packages.NeedTypesInfo

func main() {
    loadConfig := new(packages.Config)
    loadConfig.Mode = loadMode
    loadConfig.Fset = token.NewFileSet()
    pkgs, err := packages.Load(loadConfig, "github.com/me/mymodule/mypackage2")
    if err != nil {
        panic(err)
    }

    for _, pkg := range pkgs {
        for _, syn := range pkg.Syntax {
            for _, dec := range syn.Decls {
                if gen, ok := dec.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
                    // print doc comment of the type
                    fmt.Println(gen.Doc.List[0])
                    for _, spec := range gen.Specs {
                        if ts, ok := spec.(*ast.TypeSpec); ok {
                            obj, ok := pkg.TypesInfo.Defs[ts.Name]
                            if !ok {
                                continue
                            }
                            typeName, ok := obj.(*types.TypeName)
                            if !ok {
                                continue
                            }
                            named, ok := typeName.Type().(*types.Named)
                            if !ok {
                                continue
                            }
                            // print the full name of the type
                            fmt.Println(named)

                            s, ok := named.Underlying().(*types.Struct)
                            if !ok {
                                continue
                            }

                            // print the struct's fields and tags
                            for i := 0; i < s.NumFields(); i++ {
                                idx := fmt.Sprint(i)
                                fmt.Println("s.Field(", idx, ").Name(): ", s.Field(i).Name())
                                fmt.Println("s.Tag(", idx, "): ", s.Tag(i))
                            }
                        }
                    }
                }
            }
        }
    }
}

To print each struct field's comments as well (using the AST):

[...]

                if gen, ok := dec.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
                    fmt.Println("type comment:", gen.Doc.List[0].Text)
                    for _, spec := range gen.Specs {
                        if ts, ok := spec.(*ast.TypeSpec); ok {
                            fmt.Println("type name:", ts.Name)
                            st, ok := ts.Type.(*ast.StructType)
                            if !ok {
                                continue
                            }
                            
                            for i := 0; i < len(st.Fields.List); i++ {
                                f := st.Fields.List[i]
                                if f.Doc != nil {
                                    for j := 0; j < len(f.Doc.List); j++ {
                                        fmt.Println("field comment:", f.Doc.List[j].Text)
                                    } 
                                }
                                fmt.Println("field name:", f.Names[0])
                                fmt.Println("field tag:", f.Tag)
                            }
                        }
                    }
                }
            }
        }
    }
}
FvD
  • 1,286
  • 13
  • 25