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>
diff --git a/go/analysis/internal/analysisflags/flags.go b/go/analysis/internal/analysisflags/flags.go
index a03a185..062d062 100644
--- a/go/analysis/internal/analysisflags/flags.go
+++ b/go/analysis/internal/analysisflags/flags.go
@@ -8,6 +8,7 @@
 
 import (
 	"crypto/sha256"
+	"encoding/gob"
 	"encoding/json"
 	"flag"
 	"fmt"
@@ -32,6 +33,14 @@
 // 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 @@
 		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 @@
 		}
 	}
 
+	// 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