| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build !wasm |
| |
| // The example command demonstrates a simple go/packages-based |
| // analysis driver program. |
| package checker_test |
| |
| import ( |
| "fmt" |
| "log" |
| "maps" |
| "os" |
| |
| "golang.org/x/tools/go/analysis" |
| "golang.org/x/tools/go/analysis/checker" |
| "golang.org/x/tools/go/packages" |
| "golang.org/x/tools/txtar" |
| ) |
| |
| const testdata = ` |
| -- go.mod -- |
| module example.com |
| go 1.21 |
| |
| -- a/a.go -- |
| package a |
| |
| import _ "example.com/b" |
| import _ "example.com/c" |
| |
| func A1() |
| func A2() |
| func A3() |
| |
| -- b/b.go -- |
| package b |
| |
| func B1() |
| func B2() |
| |
| -- c/c.go -- |
| package c |
| |
| import _ "example.com/d" |
| |
| func C1() |
| |
| -- d/d.go -- |
| package d |
| |
| func D1() |
| func D2() |
| func D3() |
| func D4() |
| ` |
| |
| func Example() { |
| // Extract a tree of Go source files. |
| // (Avoid the standard library as it is always evolving.) |
| dir, err := os.MkdirTemp("", "") |
| if err != nil { |
| log.Fatal(err) |
| } |
| fs, err := txtar.FS(txtar.Parse([]byte(testdata))) |
| if err != nil { |
| log.Fatal(err) |
| } |
| if err := os.CopyFS(dir, fs); err != nil { |
| log.Fatal(err) |
| } |
| |
| // Load packages: example.com/a + dependencies |
| // |
| cfg := &packages.Config{Mode: packages.LoadAllSyntax, Dir: dir} |
| initial, err := packages.Load(cfg, "example.com/a") |
| if err != nil { |
| log.Fatal(err) // failure to enumerate packages |
| } |
| |
| // There may be parse or type errors among the |
| // initial packages or their dependencies, |
| // but the analysis driver can handle faulty inputs, |
| // as can some analyzers. |
| packages.PrintErrors(initial) |
| |
| if len(initial) == 0 { |
| log.Fatalf("no initial packages") |
| } |
| |
| // Run analyzers (just one) on example.com packages. |
| analyzers := []*analysis.Analyzer{pkgdecls} |
| graph, err := checker.Analyze(analyzers, initial, nil) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| // Inspect the result of each analysis action, |
| // including those for all dependencies. |
| // |
| // A realistic client would use Result, Err, Diagnostics, |
| // but for test stability, we just print the action string |
| // ("analyzer@package"). |
| for act := range graph.All() { |
| fmt.Println("printing", act) |
| } |
| |
| // Print the package fact for the sole initial package. |
| root := graph.Roots[0] |
| fact := new(pkgdeclsFact) |
| if root.PackageFact(root.Package.Types, fact) { |
| for k, v := range fact.numdecls { |
| fmt.Printf("%s:\t%d decls\n", k, v) |
| } |
| } |
| |
| // Unordered Output: |
| // printing pkgdecls@example.com/a |
| // printing pkgdecls@example.com/b |
| // printing pkgdecls@example.com/c |
| // printing pkgdecls@example.com/d |
| // example.com/a: 3 decls |
| // example.com/b: 2 decls |
| // example.com/c: 1 decls |
| // example.com/d: 4 decls |
| } |
| |
| // pkgdecls is a trivial example analyzer that uses package facts to |
| // compute information from the entire dependency graph. |
| var pkgdecls = &analysis.Analyzer{ |
| Name: "pkgdecls", |
| Doc: "Computes a package fact mapping each package to its number of declarations.", |
| Run: run, |
| FactTypes: []analysis.Fact{(*pkgdeclsFact)(nil)}, |
| } |
| |
| type pkgdeclsFact struct{ numdecls map[string]int } |
| |
| func (*pkgdeclsFact) AFact() {} |
| |
| func run(pass *analysis.Pass) (any, error) { |
| numdecls := map[string]int{ |
| pass.Pkg.Path(): pass.Pkg.Scope().Len(), |
| } |
| |
| // Compute the union across all dependencies. |
| for _, imp := range pass.Pkg.Imports() { |
| if depFact := new(pkgdeclsFact); pass.ImportPackageFact(imp, depFact) { |
| maps.Copy(numdecls, depFact.numdecls) |
| } |
| } |
| |
| pass.ExportPackageFact(&pkgdeclsFact{numdecls}) |
| |
| return nil, nil |
| } |