mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Add gitea-vet (#10948)
* Add copyright Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add gitea-vet and fix non-compliance Signed-off-by: jolheiser <john.olheiser@gmail.com> * Combine tools.go into build.go and clean up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove extra GO111MODULE=on Signed-off-by: jolheiser <john.olheiser@gmail.com>
This commit is contained in:
388
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go
generated
vendored
Normal file
388
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go
generated
vendored
Normal file
@@ -0,0 +1,388 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package analysisflags defines helpers for processing flags of
|
||||
// analysis driver tools.
|
||||
package analysisflags
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
// flags common to all {single,multi,unit}checkers.
|
||||
var (
|
||||
JSON = false // -json
|
||||
Context = -1 // -c=N: if N>0, display offending line plus N lines of context
|
||||
)
|
||||
|
||||
// Parse creates a flag for each of the analyzer's flags,
|
||||
// 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)
|
||||
for _, a := range analyzers {
|
||||
var prefix string
|
||||
|
||||
// Add -NAME flag to enable it.
|
||||
if multi {
|
||||
prefix = a.Name + "."
|
||||
|
||||
enable := new(triState)
|
||||
enableUsage := "enable " + a.Name + " analysis"
|
||||
flag.Var(enable, a.Name, enableUsage)
|
||||
enabled[a] = enable
|
||||
}
|
||||
|
||||
a.Flags.VisitAll(func(f *flag.Flag) {
|
||||
if !multi && flag.Lookup(f.Name) != nil {
|
||||
log.Printf("%s flag -%s would conflict with driver; skipping", a.Name, f.Name)
|
||||
return
|
||||
}
|
||||
|
||||
name := prefix + f.Name
|
||||
flag.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
// standard flags: -flags, -V.
|
||||
printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
|
||||
addVersionFlag()
|
||||
|
||||
// flags common to all checkers
|
||||
flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
|
||||
flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)
|
||||
|
||||
// Add shims for legacy vet flags to enable existing
|
||||
// scripts that run vet to continue to work.
|
||||
_ = flag.Bool("source", false, "no effect (deprecated)")
|
||||
_ = flag.Bool("v", false, "no effect (deprecated)")
|
||||
_ = flag.Bool("all", false, "no effect (deprecated)")
|
||||
_ = flag.String("tags", "", "no effect (deprecated)")
|
||||
for old, new := range vetLegacyFlags {
|
||||
newFlag := flag.Lookup(new)
|
||||
if newFlag != nil && flag.Lookup(old) == nil {
|
||||
flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
|
||||
}
|
||||
}
|
||||
|
||||
flag.Parse() // (ExitOnError)
|
||||
|
||||
// -flags: print flags so that go vet knows which ones are legitimate.
|
||||
if *printflags {
|
||||
printFlags()
|
||||
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 {
|
||||
var hasTrue, hasFalse bool
|
||||
for _, ts := range enabled {
|
||||
switch *ts {
|
||||
case setTrue:
|
||||
hasTrue = true
|
||||
case setFalse:
|
||||
hasFalse = true
|
||||
}
|
||||
}
|
||||
|
||||
var keep []*analysis.Analyzer
|
||||
if hasTrue {
|
||||
for _, a := range analyzers {
|
||||
if *enabled[a] == setTrue {
|
||||
keep = append(keep, a)
|
||||
}
|
||||
}
|
||||
analyzers = keep
|
||||
} else if hasFalse {
|
||||
for _, a := range analyzers {
|
||||
if *enabled[a] != setFalse {
|
||||
keep = append(keep, a)
|
||||
}
|
||||
}
|
||||
analyzers = keep
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
Bool bool
|
||||
Usage string
|
||||
}
|
||||
var flags []jsonFlag = nil
|
||||
flag.VisitAll(func(f *flag.Flag) {
|
||||
// Don't report {single,multi}checker debugging
|
||||
// flags or fix as these have no effect on unitchecker
|
||||
// (as invoked by 'go vet').
|
||||
switch f.Name {
|
||||
case "debug", "cpuprofile", "memprofile", "trace", "fix":
|
||||
return
|
||||
}
|
||||
|
||||
b, ok := f.Value.(interface{ IsBoolFlag() bool })
|
||||
isBool := ok && b.IsBoolFlag()
|
||||
flags = append(flags, jsonFlag{f.Name, isBool, f.Usage})
|
||||
})
|
||||
data, err := json.MarshalIndent(flags, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Stdout.Write(data)
|
||||
}
|
||||
|
||||
// addVersionFlag registers a -V flag that, if set,
|
||||
// prints the executable version and exits 0.
|
||||
//
|
||||
// If the -V flag already exists — for example, because it was already
|
||||
// registered by a call to cmd/internal/objabi.AddVersionFlag — then
|
||||
// addVersionFlag does nothing.
|
||||
func addVersionFlag() {
|
||||
if flag.Lookup("V") == nil {
|
||||
flag.Var(versionFlag{}, "V", "print version and exit")
|
||||
}
|
||||
}
|
||||
|
||||
// versionFlag minimally complies with the -V protocol required by "go vet".
|
||||
type versionFlag struct{}
|
||||
|
||||
func (versionFlag) IsBoolFlag() bool { return true }
|
||||
func (versionFlag) Get() interface{} { return nil }
|
||||
func (versionFlag) String() string { return "" }
|
||||
func (versionFlag) Set(s string) error {
|
||||
if s != "full" {
|
||||
log.Fatalf("unsupported flag value: -V=%s", s)
|
||||
}
|
||||
|
||||
// This replicates the minimal subset of
|
||||
// cmd/internal/objabi.AddVersionFlag, which is private to the
|
||||
// go tool yet forms part of our command-line interface.
|
||||
// TODO(adonovan): clarify the contract.
|
||||
|
||||
// Print the tool version so the build system can track changes.
|
||||
// Formats:
|
||||
// $progname version devel ... buildID=...
|
||||
// $progname version go1.9.1
|
||||
progname := os.Args[0]
|
||||
f, err := os.Open(progname)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
fmt.Printf("%s version devel comments-go-here buildID=%02x\n",
|
||||
progname, string(h.Sum(nil)))
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A triState is a boolean that knows whether
|
||||
// it has been set to either true or false.
|
||||
// It is used to identify whether a flag appears;
|
||||
// the standard boolean flag cannot
|
||||
// distinguish missing from unset.
|
||||
// It also satisfies flag.Value.
|
||||
type triState int
|
||||
|
||||
const (
|
||||
unset triState = iota
|
||||
setTrue
|
||||
setFalse
|
||||
)
|
||||
|
||||
func triStateFlag(name string, value triState, usage string) *triState {
|
||||
flag.Var(&value, name, usage)
|
||||
return &value
|
||||
}
|
||||
|
||||
// triState implements flag.Value, flag.Getter, and flag.boolFlag.
|
||||
// They work like boolean flags: we can say vet -printf as well as vet -printf=true
|
||||
func (ts *triState) Get() interface{} {
|
||||
return *ts == setTrue
|
||||
}
|
||||
|
||||
func (ts triState) isTrue() bool {
|
||||
return ts == setTrue
|
||||
}
|
||||
|
||||
func (ts *triState) Set(value string) error {
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
// This error message looks poor but package "flag" adds
|
||||
// "invalid boolean value %q for -NAME: %s"
|
||||
return fmt.Errorf("want true or false")
|
||||
}
|
||||
if b {
|
||||
*ts = setTrue
|
||||
} else {
|
||||
*ts = setFalse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ts *triState) String() string {
|
||||
switch *ts {
|
||||
case unset:
|
||||
return "true"
|
||||
case setTrue:
|
||||
return "true"
|
||||
case setFalse:
|
||||
return "false"
|
||||
}
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
func (ts triState) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Legacy flag support
|
||||
|
||||
// vetLegacyFlags maps flags used by legacy vet to their corresponding
|
||||
// new names. The old names will continue to work.
|
||||
var vetLegacyFlags = map[string]string{
|
||||
// Analyzer name changes
|
||||
"bool": "bools",
|
||||
"buildtags": "buildtag",
|
||||
"methods": "stdmethods",
|
||||
"rangeloops": "loopclosure",
|
||||
|
||||
// Analyzer flags
|
||||
"compositewhitelist": "composites.whitelist",
|
||||
"printfuncs": "printf.funcs",
|
||||
"shadowstrict": "shadow.strict",
|
||||
"unusedfuncs": "unusedresult.funcs",
|
||||
"unusedstringmethods": "unusedresult.stringmethods",
|
||||
}
|
||||
|
||||
// ---- output helpers common to all drivers ----
|
||||
|
||||
// PrintPlain prints a diagnostic in plain text form,
|
||||
// with context specified by the -c flag.
|
||||
func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
|
||||
posn := fset.Position(diag.Pos)
|
||||
fmt.Fprintf(os.Stderr, "%s: %s\n", posn, diag.Message)
|
||||
|
||||
// -c=N: show offending line plus N lines of context.
|
||||
if Context >= 0 {
|
||||
posn := fset.Position(diag.Pos)
|
||||
end := fset.Position(diag.End)
|
||||
if !end.IsValid() {
|
||||
end = posn
|
||||
}
|
||||
data, _ := ioutil.ReadFile(posn.Filename)
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for i := posn.Line - Context; i <= end.Line+Context; i++ {
|
||||
if 1 <= i && i <= len(lines) {
|
||||
fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A JSONTree is a mapping from package ID to analysis name to result.
|
||||
// Each result is either a jsonError or a list of jsonDiagnostic.
|
||||
type JSONTree map[string]map[string]interface{}
|
||||
|
||||
// Add adds the result of analysis 'name' on package 'id'.
|
||||
// The result is either a list of diagnostics or an error.
|
||||
func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
|
||||
var v interface{}
|
||||
if err != nil {
|
||||
type jsonError struct {
|
||||
Err string `json:"error"`
|
||||
}
|
||||
v = jsonError{err.Error()}
|
||||
} else if len(diags) > 0 {
|
||||
type jsonDiagnostic struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
Posn string `json:"posn"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
var diagnostics []jsonDiagnostic
|
||||
// TODO(matloob): Should the JSON diagnostics contain ranges?
|
||||
// If so, how should they be formatted?
|
||||
for _, f := range diags {
|
||||
diagnostics = append(diagnostics, jsonDiagnostic{
|
||||
Category: f.Category,
|
||||
Posn: fset.Position(f.Pos).String(),
|
||||
Message: f.Message,
|
||||
})
|
||||
}
|
||||
v = diagnostics
|
||||
}
|
||||
if v != nil {
|
||||
m, ok := tree[id]
|
||||
if !ok {
|
||||
m = make(map[string]interface{})
|
||||
tree[id] = m
|
||||
}
|
||||
m[name] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (tree JSONTree) Print() {
|
||||
data, err := json.MarshalIndent(tree, "", "\t")
|
||||
if err != nil {
|
||||
log.Panicf("internal error: JSON marshalling failed: %v", err)
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
}
|
92
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/help.go
generated
vendored
Normal file
92
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/help.go
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
package analysisflags
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const help = `PROGNAME is a tool for static analysis of Go programs.
|
||||
|
||||
PROGNAME examines Go source code and reports suspicious constructs,
|
||||
such as Printf calls whose arguments do not align with the format
|
||||
string. It uses heuristics that do not guarantee all reports are
|
||||
genuine problems, but it can find errors not caught by the compilers.
|
||||
`
|
||||
|
||||
// Help implements the help subcommand for a multichecker or unitchecker
|
||||
// style command. The optional args specify the analyzers to describe.
|
||||
// Help calls log.Fatal if no such analyzer exists.
|
||||
func Help(progname string, analyzers []*analysis.Analyzer, args []string) {
|
||||
// No args: show summary of all analyzers.
|
||||
if len(args) == 0 {
|
||||
fmt.Println(strings.Replace(help, "PROGNAME", progname, -1))
|
||||
fmt.Println("Registered analyzers:")
|
||||
fmt.Println()
|
||||
sort.Slice(analyzers, func(i, j int) bool {
|
||||
return analyzers[i].Name < analyzers[j].Name
|
||||
})
|
||||
for _, a := range analyzers {
|
||||
title := strings.Split(a.Doc, "\n\n")[0]
|
||||
fmt.Printf(" %-12s %s\n", a.Name, title)
|
||||
}
|
||||
fmt.Println("\nBy default all analyzers are run.")
|
||||
fmt.Println("To select specific analyzers, use the -NAME flag for each one,")
|
||||
fmt.Println(" or -NAME=false to run all analyzers not explicitly disabled.")
|
||||
|
||||
// Show only the core command-line flags.
|
||||
fmt.Println("\nCore flags:")
|
||||
fmt.Println()
|
||||
fs := flag.NewFlagSet("", flag.ExitOnError)
|
||||
flag.VisitAll(func(f *flag.Flag) {
|
||||
if !strings.Contains(f.Name, ".") {
|
||||
fs.Var(f.Value, f.Name, f.Usage)
|
||||
}
|
||||
})
|
||||
fs.SetOutput(os.Stdout)
|
||||
fs.PrintDefaults()
|
||||
|
||||
fmt.Printf("\nTo see details and flags of a specific analyzer, run '%s help name'.\n", progname)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Show help on specific analyzer(s).
|
||||
outer:
|
||||
for _, arg := range args {
|
||||
for _, a := range analyzers {
|
||||
if a.Name == arg {
|
||||
paras := strings.Split(a.Doc, "\n\n")
|
||||
title := paras[0]
|
||||
fmt.Printf("%s: %s\n", a.Name, title)
|
||||
|
||||
// Show only the flags relating to this analysis,
|
||||
// properly prefixed.
|
||||
first := true
|
||||
fs := flag.NewFlagSet(a.Name, flag.ExitOnError)
|
||||
a.Flags.VisitAll(func(f *flag.Flag) {
|
||||
if first {
|
||||
first = false
|
||||
fmt.Println("\nAnalyzer flags:")
|
||||
fmt.Println()
|
||||
}
|
||||
fs.Var(f.Value, a.Name+"."+f.Name, f.Usage)
|
||||
})
|
||||
fs.SetOutput(os.Stdout)
|
||||
fs.PrintDefaults()
|
||||
|
||||
if len(paras) > 1 {
|
||||
fmt.Printf("\n%s\n", strings.Join(paras[1:], "\n\n"))
|
||||
}
|
||||
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
log.Fatalf("Analyzer %q not registered", arg)
|
||||
}
|
||||
}
|
323
vendor/golang.org/x/tools/go/analysis/internal/facts/facts.go
generated
vendored
Normal file
323
vendor/golang.org/x/tools/go/analysis/internal/facts/facts.go
generated
vendored
Normal file
@@ -0,0 +1,323 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Package facts defines a serializable set of analysis.Fact.
|
||||
//
|
||||
// It provides a partial implementation of the Fact-related parts of the
|
||||
// analysis.Pass interface for use in analysis drivers such as "go vet"
|
||||
// and other build systems.
|
||||
//
|
||||
// The serial format is unspecified and may change, so the same version
|
||||
// of this package must be used for reading and writing serialized facts.
|
||||
//
|
||||
// The handling of facts in the analysis system parallels the handling
|
||||
// of type information in the compiler: during compilation of package P,
|
||||
// the compiler emits an export data file that describes the type of
|
||||
// every object (named thing) defined in package P, plus every object
|
||||
// indirectly reachable from one of those objects. Thus the downstream
|
||||
// compiler of package Q need only load one export data file per direct
|
||||
// import of Q, and it will learn everything about the API of package P
|
||||
// and everything it needs to know about the API of P's dependencies.
|
||||
//
|
||||
// Similarly, analysis of package P emits a fact set containing facts
|
||||
// about all objects exported from P, plus additional facts about only
|
||||
// those objects of P's dependencies that are reachable from the API of
|
||||
// package P; the downstream analysis of Q need only load one fact set
|
||||
// per direct import of Q.
|
||||
//
|
||||
// The notion of "exportedness" that matters here is that of the
|
||||
// compiler. According to the language spec, a method pkg.T.f is
|
||||
// unexported simply because its name starts with lowercase. But the
|
||||
// compiler must nonetheless export f so that downstream compilations can
|
||||
// accurately ascertain whether pkg.T implements an interface pkg.I
|
||||
// defined as interface{f()}. Exported thus means "described in export
|
||||
// data".
|
||||
//
|
||||
package facts
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/types/objectpath"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
// A Set is a set of analysis.Facts.
|
||||
//
|
||||
// Decode creates a Set of facts by reading from the imports of a given
|
||||
// package, and Encode writes out the set. Between these operation,
|
||||
// the Import and Export methods will query and update the set.
|
||||
//
|
||||
// All of Set's methods except String are safe to call concurrently.
|
||||
type Set struct {
|
||||
pkg *types.Package
|
||||
mu sync.Mutex
|
||||
m map[key]analysis.Fact
|
||||
}
|
||||
|
||||
type key struct {
|
||||
pkg *types.Package
|
||||
obj types.Object // (object facts only)
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
// ImportObjectFact implements analysis.Pass.ImportObjectFact.
|
||||
func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool {
|
||||
if obj == nil {
|
||||
panic("nil object")
|
||||
}
|
||||
key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if v, ok := s.m[key]; ok {
|
||||
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ExportObjectFact implements analysis.Pass.ExportObjectFact.
|
||||
func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) {
|
||||
if obj.Pkg() != s.pkg {
|
||||
log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package",
|
||||
s.pkg, obj, fact)
|
||||
}
|
||||
key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)}
|
||||
s.mu.Lock()
|
||||
s.m[key] = fact // clobber any existing entry
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
|
||||
var facts []analysis.ObjectFact
|
||||
s.mu.Lock()
|
||||
for k, v := range s.m {
|
||||
if k.obj != nil && filter[k.t] {
|
||||
facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v})
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return facts
|
||||
}
|
||||
|
||||
// ImportPackageFact implements analysis.Pass.ImportPackageFact.
|
||||
func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
|
||||
if pkg == nil {
|
||||
panic("nil package")
|
||||
}
|
||||
key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if v, ok := s.m[key]; ok {
|
||||
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ExportPackageFact implements analysis.Pass.ExportPackageFact.
|
||||
func (s *Set) ExportPackageFact(fact analysis.Fact) {
|
||||
key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
|
||||
s.mu.Lock()
|
||||
s.m[key] = fact // clobber any existing entry
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
|
||||
var facts []analysis.PackageFact
|
||||
s.mu.Lock()
|
||||
for k, v := range s.m {
|
||||
if k.obj == nil && filter[k.t] {
|
||||
facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v})
|
||||
}
|
||||
}
|
||||
s.mu.Unlock()
|
||||
return facts
|
||||
}
|
||||
|
||||
// gobFact is the Gob declaration of a serialized fact.
|
||||
type gobFact struct {
|
||||
PkgPath string // path of package
|
||||
Object objectpath.Path // optional path of object relative to package itself
|
||||
Fact analysis.Fact // type and value of user-defined Fact
|
||||
}
|
||||
|
||||
// Decode decodes all the facts relevant to the analysis of package pkg.
|
||||
// The read function reads serialized fact data from an external source
|
||||
// for one of of pkg's direct imports. The empty file is a valid
|
||||
// encoding of an empty fact set.
|
||||
//
|
||||
// It is the caller's responsibility to call gob.Register on all
|
||||
// necessary fact types.
|
||||
func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) {
|
||||
// Compute the import map for this package.
|
||||
// See the package doc comment.
|
||||
packages := importMap(pkg.Imports())
|
||||
|
||||
// Read facts from imported packages.
|
||||
// Facts may describe indirectly imported packages, or their objects.
|
||||
m := make(map[key]analysis.Fact) // one big bucket
|
||||
for _, imp := range pkg.Imports() {
|
||||
logf := func(format string, args ...interface{}) {
|
||||
if debug {
|
||||
prefix := fmt.Sprintf("in %s, importing %s: ",
|
||||
pkg.Path(), imp.Path())
|
||||
log.Print(prefix, fmt.Sprintf(format, args...))
|
||||
}
|
||||
}
|
||||
|
||||
// Read the gob-encoded facts.
|
||||
data, err := read(imp.Path())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
|
||||
pkg.Path(), imp.Path(), err)
|
||||
}
|
||||
if len(data) == 0 {
|
||||
continue // no facts
|
||||
}
|
||||
var gobFacts []gobFact
|
||||
if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
|
||||
return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err)
|
||||
}
|
||||
if debug {
|
||||
logf("decoded %d facts: %v", len(gobFacts), gobFacts)
|
||||
}
|
||||
|
||||
// Parse each one into a key and a Fact.
|
||||
for _, f := range gobFacts {
|
||||
factPkg := packages[f.PkgPath]
|
||||
if factPkg == nil {
|
||||
// Fact relates to a dependency that was
|
||||
// unused in this translation unit. Skip.
|
||||
logf("no package %q; discarding %v", f.PkgPath, f.Fact)
|
||||
continue
|
||||
}
|
||||
key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
|
||||
if f.Object != "" {
|
||||
// object fact
|
||||
obj, err := objectpath.Object(factPkg, f.Object)
|
||||
if err != nil {
|
||||
// (most likely due to unexported object)
|
||||
// TODO(adonovan): audit for other possibilities.
|
||||
logf("no object for path: %v; discarding %s", err, f.Fact)
|
||||
continue
|
||||
}
|
||||
key.obj = obj
|
||||
logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
|
||||
} else {
|
||||
// package fact
|
||||
logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
|
||||
}
|
||||
m[key] = f.Fact
|
||||
}
|
||||
}
|
||||
|
||||
return &Set{pkg: pkg, m: m}, nil
|
||||
}
|
||||
|
||||
// Encode encodes a set of facts to a memory buffer.
|
||||
//
|
||||
// It may fail if one of the Facts could not be gob-encoded, but this is
|
||||
// a sign of a bug in an Analyzer.
|
||||
func (s *Set) Encode() []byte {
|
||||
|
||||
// TODO(adonovan): opt: use a more efficient encoding
|
||||
// that avoids repeating PkgPath for each fact.
|
||||
|
||||
// Gather all facts, including those from imported packages.
|
||||
var gobFacts []gobFact
|
||||
|
||||
s.mu.Lock()
|
||||
for k, fact := range s.m {
|
||||
if debug {
|
||||
log.Printf("%v => %s\n", k, fact)
|
||||
}
|
||||
var object objectpath.Path
|
||||
if k.obj != nil {
|
||||
path, err := objectpath.For(k.obj)
|
||||
if err != nil {
|
||||
if debug {
|
||||
log.Printf("discarding fact %s about %s\n", fact, k.obj)
|
||||
}
|
||||
continue // object not accessible from package API; discard fact
|
||||
}
|
||||
object = path
|
||||
}
|
||||
gobFacts = append(gobFacts, gobFact{
|
||||
PkgPath: k.pkg.Path(),
|
||||
Object: object,
|
||||
Fact: fact,
|
||||
})
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
// Sort facts by (package, object, type) for determinism.
|
||||
sort.Slice(gobFacts, func(i, j int) bool {
|
||||
x, y := gobFacts[i], gobFacts[j]
|
||||
if x.PkgPath != y.PkgPath {
|
||||
return x.PkgPath < y.PkgPath
|
||||
}
|
||||
if x.Object != y.Object {
|
||||
return x.Object < y.Object
|
||||
}
|
||||
tx := reflect.TypeOf(x.Fact)
|
||||
ty := reflect.TypeOf(y.Fact)
|
||||
if tx != ty {
|
||||
return tx.String() < ty.String()
|
||||
}
|
||||
return false // equal
|
||||
})
|
||||
|
||||
var buf bytes.Buffer
|
||||
if len(gobFacts) > 0 {
|
||||
if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil {
|
||||
// Fact encoding should never fail. Identify the culprit.
|
||||
for _, gf := range gobFacts {
|
||||
if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil {
|
||||
fact := gf.Fact
|
||||
pkgpath := reflect.TypeOf(fact).Elem().PkgPath()
|
||||
log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q",
|
||||
fact, err, fact, pkgpath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("package %q: encode %d facts, %d bytes\n",
|
||||
s.pkg.Path(), len(gobFacts), buf.Len())
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// String is provided only for debugging, and must not be called
|
||||
// concurrent with any Import/Export method.
|
||||
func (s *Set) String() string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("{")
|
||||
for k, f := range s.m {
|
||||
if buf.Len() > 1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
if k.obj != nil {
|
||||
buf.WriteString(k.obj.String())
|
||||
} else {
|
||||
buf.WriteString(k.pkg.Path())
|
||||
}
|
||||
fmt.Fprintf(&buf, ": %v", f)
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.String()
|
||||
}
|
88
vendor/golang.org/x/tools/go/analysis/internal/facts/imports.go
generated
vendored
Normal file
88
vendor/golang.org/x/tools/go/analysis/internal/facts/imports.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
package facts
|
||||
|
||||
import "go/types"
|
||||
|
||||
// importMap computes the import map for a package by traversing the
|
||||
// entire exported API each of its imports.
|
||||
//
|
||||
// This is a workaround for the fact that we cannot access the map used
|
||||
// internally by the types.Importer returned by go/importer. The entries
|
||||
// in this map are the packages and objects that may be relevant to the
|
||||
// current analysis unit.
|
||||
//
|
||||
// Packages in the map that are only indirectly imported may be
|
||||
// incomplete (!pkg.Complete()).
|
||||
//
|
||||
func importMap(imports []*types.Package) map[string]*types.Package {
|
||||
objects := make(map[types.Object]bool)
|
||||
packages := make(map[string]*types.Package)
|
||||
|
||||
var addObj func(obj types.Object) bool
|
||||
var addType func(T types.Type)
|
||||
|
||||
addObj = func(obj types.Object) bool {
|
||||
if !objects[obj] {
|
||||
objects[obj] = true
|
||||
addType(obj.Type())
|
||||
if pkg := obj.Pkg(); pkg != nil {
|
||||
packages[pkg.Path()] = pkg
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
addType = func(T types.Type) {
|
||||
switch T := T.(type) {
|
||||
case *types.Basic:
|
||||
// nop
|
||||
case *types.Named:
|
||||
if addObj(T.Obj()) {
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
addObj(T.Method(i))
|
||||
}
|
||||
}
|
||||
case *types.Pointer:
|
||||
addType(T.Elem())
|
||||
case *types.Slice:
|
||||
addType(T.Elem())
|
||||
case *types.Array:
|
||||
addType(T.Elem())
|
||||
case *types.Chan:
|
||||
addType(T.Elem())
|
||||
case *types.Map:
|
||||
addType(T.Key())
|
||||
addType(T.Elem())
|
||||
case *types.Signature:
|
||||
addType(T.Params())
|
||||
addType(T.Results())
|
||||
case *types.Struct:
|
||||
for i := 0; i < T.NumFields(); i++ {
|
||||
addObj(T.Field(i))
|
||||
}
|
||||
case *types.Tuple:
|
||||
for i := 0; i < T.Len(); i++ {
|
||||
addObj(T.At(i))
|
||||
}
|
||||
case *types.Interface:
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
addObj(T.Method(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, imp := range imports {
|
||||
packages[imp.Path()] = imp
|
||||
|
||||
scope := imp.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
addObj(scope.Lookup(name))
|
||||
}
|
||||
}
|
||||
|
||||
return packages
|
||||
}
|
Reference in New Issue
Block a user