tools/passes/clientgo/clientgo.go (174 lines of code) (raw):

package clientgo import ( "fmt" "go/ast" "go/token" "path/filepath" "reflect" "golang.org/x/tools/go/analysis" "gitlab.com/gitlab-org/terraform-provider-gitlab/tools/passes" ) const clientGoPackagePath = "gitlab.com/gitlab-org/api/client-go" var Analyzer = &analysis.Analyzer{ Doc: "Intermediate analyzer for extracting summary data from the client-go package", Name: "clientgo", ResultType: reflect.TypeOf((*Result)(nil)), // Using Facts causes the analyzer to visit dependencies; // otherwise it would not analyze the client-go package. FactTypes: []analysis.Fact{ (*typeFact)(nil), (*funcFact)(nil), (*methodFact)(nil), (*fieldFact)(nil), }, Run: run, } type Result struct { TypeToFilenames MultiMap FuncToFilenames MultiMap MethodToFilenames MultiMap FieldToFilenames MultiMap } type MultiMap map[string][]string func run(pass *analysis.Pass) (interface{}, error) { if pass.Pkg.Path() == clientGoPackagePath && !passes.IsTestPackage(pass) { exportTypeFacts(pass) exportFuncFacts(pass) exportMethodFacts(pass) exportFieldFacts(pass) } return makeResult(pass), nil } func exportTypeFacts(pass *analysis.Pass) { export := factExporter(pass, func(nameInFile nameInFile) analysis.Fact { fact := typeFact(nameInFile) return &fact }) for _, file := range pass.Files { for _, decl := range file.Decls { if gen, ok := decl.(*ast.GenDecl); ok { switch gen.Tok { case token.CONST: for _, s := range gen.Specs { for _, n := range s.(*ast.ValueSpec).Names { export(file, n) } } case token.TYPE: for _, s := range gen.Specs { export(file, s.(*ast.TypeSpec).Name) } } } } } } func exportFuncFacts(pass *analysis.Pass) { export := factExporter(pass, func(nameInFile nameInFile) analysis.Fact { fact := funcFact(nameInFile) return &fact }) for _, file := range pass.Files { for _, decl := range file.Decls { if funcDecl, ok := decl.(*ast.FuncDecl); ok { if funcDecl.Recv == nil { export(file, funcDecl.Name) } } } } } func exportMethodFacts(pass *analysis.Pass) { export := factExporter(pass, func(nameInFile nameInFile) analysis.Fact { fact := methodFact(nameInFile) return &fact }) for _, file := range pass.Files { for _, decl := range file.Decls { if funcDecl, ok := decl.(*ast.FuncDecl); ok && funcDecl.Recv != nil { export(file, funcDecl.Name) } } } } func exportFieldFacts(pass *analysis.Pass) { export := factExporter(pass, func(nameInFile nameInFile) analysis.Fact { fact := fieldFact(nameInFile) return &fact }) for _, file := range pass.Files { for _, decl := range file.Decls { if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.TYPE { for _, spec := range gen.Specs { ast.Inspect(spec, func(n ast.Node) bool { switch x := n.(type) { case *ast.TypeSpec: if !x.Name.IsExported() { return false } case *ast.Field: for _, name := range x.Names { export(file, name) } } return true }) } } } } } func makeResult(pass *analysis.Pass) *Result { result := &Result{ TypeToFilenames: make(MultiMap), FuncToFilenames: make(MultiMap), MethodToFilenames: make(MultiMap), FieldToFilenames: make(MultiMap), } addName := func(name, filename string, m MultiMap) { for _, seenFilename := range m[name] { if filename == seenFilename { return } } m[name] = append(m[name], filename) } for _, f := range pass.AllObjectFacts() { switch x := f.Fact.(type) { case *typeFact: addName(x.name, x.filename, result.TypeToFilenames) case *funcFact: addName(x.name, x.filename, result.FuncToFilenames) case *methodFact: addName(x.name, x.filename, result.MethodToFilenames) case *fieldFact: addName(x.name, x.filename, result.FieldToFilenames) default: panic(fmt.Sprintf("unhandled fact %#v", f.Fact)) } } return result } func factExporter(pass *analysis.Pass, newFact func(nameInFile) analysis.Fact) func(*ast.File, *ast.Ident) { return func(file *ast.File, ident *ast.Ident) { if ident.IsExported() { pass.ExportObjectFact( pass.TypesInfo.Defs[ident], newFact(nameInFile{ name: ident.Name, filename: filepath.Base(pass.Fset.File(file.Pos()).Name()), }), ) } } } type nameInFile struct { name string filename string } type typeFact nameInFile func (f *typeFact) AFact() {} type funcFact nameInFile func (f *funcFact) AFact() {} type methodFact nameInFile func (f *methodFact) AFact() {} type fieldFact nameInFile func (f *fieldFact) AFact() {}