diff --git a/go/analysis/internal/analysisflags/flags.go b/go/analysis/internal/analysisflags/flags.go index a03a185fc0..062d062487 100644 --- a/go/analysis/internal/analysisflags/flags.go +++ b/go/analysis/internal/analysisflags/flags.go @@ -8,6 +8,7 @@ package analysisflags import ( "crypto/sha256" + "encoding/gob" "encoding/json" "flag" "fmt" @@ -32,6 +33,14 @@ var ( // including (in multi mode) a flag named after the analyzer, // parses the flags, then filters and returns the list of // analyzers enabled by flags. +// +// The result is intended to be passed to unitchecker.Run or checker.Run. +// Use in unitchecker.Run will gob.Register all fact types for the returned +// graph of analyzers but of course not the ones only reachable from +// dropped analyzers. To avoid inconsistency about which gob types are +// registered from run to run, Parse itself gob.Registers all the facts +// only reachable from dropped analyzers. +// This is not a particularly elegant API, but this is an internal package. func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { // Connect each analysis flag to the command line as -analysis.flag. enabled := make(map[*analysis.Analyzer]*triState) @@ -88,6 +97,8 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { os.Exit(0) } + everything := expand(analyzers) + // If any -NAME flag is true, run only those analyzers. Otherwise, // if any -NAME flag is false, run all but those analyzers. if multi { @@ -119,9 +130,35 @@ func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer { } } + // Register fact types of skipped analyzers + // in case we encounter them in imported files. + kept := expand(analyzers) + for a := range everything { + if !kept[a] { + for _, f := range a.FactTypes { + gob.Register(f) + } + } + } + return analyzers } +func expand(analyzers []*analysis.Analyzer) map[*analysis.Analyzer]bool { + seen := make(map[*analysis.Analyzer]bool) + var visitAll func([]*analysis.Analyzer) + visitAll = func(analyzers []*analysis.Analyzer) { + for _, a := range analyzers { + if !seen[a] { + seen[a] = true + visitAll(a.Requires) + } + } + } + visitAll(analyzers) + return seen +} + func printFlags() { type jsonFlag struct { Name string