go/analysis/internal/analysisflags: call gob.Register on deleted analyzers

Otherwise the specific set of gob registrations varies
according to the command line, which makes it impossible
for a narrow analysis run (for example, just one analyzer)
to read fact files written by less narrow runs (for example, all the analyzers).

This will start mattering in the standard repo vet.

For golang/go#31916.

Change-Id: I6fa90b3dfdf28ede6f995db3904211b6be68bb73
Reviewed-on: https://go-review.googlesource.com/c/tools/+/176357
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Russ Cox 2019-05-09 17:18:50 -04:00
parent 7d589f28aa
commit 4789ca9922

View File

@ -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