Bumping k8s dependencies to 1.13
This commit is contained in:
192
vendor/golang.org/x/tools/go/analysis/analysis.go
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/analysis.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// An Analyzer describes an analysis function and its options.
|
||||
type Analyzer struct {
|
||||
// The Name of the analyzer must be a valid Go identifier
|
||||
// as it may appear in command-line flags, URLs, and so on.
|
||||
Name string
|
||||
|
||||
// Doc is the documentation for the analyzer.
|
||||
// The part before the first "\n\n" is the title
|
||||
// (no capital or period, max ~60 letters).
|
||||
Doc string
|
||||
|
||||
// Flags defines any flags accepted by the analyzer.
|
||||
// The manner in which these flags are exposed to the user
|
||||
// depends on the driver which runs the analyzer.
|
||||
Flags flag.FlagSet
|
||||
|
||||
// Run applies the analyzer to a package.
|
||||
// It returns an error if the analyzer failed.
|
||||
//
|
||||
// On success, the Run function may return a result
|
||||
// computed by the Analyzer; its type must match ResultType.
|
||||
// The driver makes this result available as an input to
|
||||
// another Analyzer that depends directly on this one (see
|
||||
// Requires) when it analyzes the same package.
|
||||
//
|
||||
// To pass analysis results between packages (and thus
|
||||
// potentially between address spaces), use Facts, which are
|
||||
// serializable.
|
||||
Run func(*Pass) (interface{}, error)
|
||||
|
||||
// RunDespiteErrors allows the driver to invoke
|
||||
// the Run method of this analyzer even on a
|
||||
// package that contains parse or type errors.
|
||||
RunDespiteErrors bool
|
||||
|
||||
// Requires is a set of analyzers that must run successfully
|
||||
// before this one on a given package. This analyzer may inspect
|
||||
// the outputs produced by each analyzer in Requires.
|
||||
// The graph over analyzers implied by Requires edges must be acyclic.
|
||||
//
|
||||
// Requires establishes a "horizontal" dependency between
|
||||
// analysis passes (different analyzers, same package).
|
||||
Requires []*Analyzer
|
||||
|
||||
// ResultType is the type of the optional result of the Run function.
|
||||
ResultType reflect.Type
|
||||
|
||||
// FactTypes indicates that this analyzer imports and exports
|
||||
// Facts of the specified concrete types.
|
||||
// An analyzer that uses facts may assume that its import
|
||||
// dependencies have been similarly analyzed before it runs.
|
||||
// Facts must be pointers.
|
||||
//
|
||||
// FactTypes establishes a "vertical" dependency between
|
||||
// analysis passes (same analyzer, different packages).
|
||||
FactTypes []Fact
|
||||
}
|
||||
|
||||
func (a *Analyzer) String() string { return a.Name }
|
||||
|
||||
// A Pass provides information to the Run function that
|
||||
// applies a specific analyzer to a single Go package.
|
||||
//
|
||||
// It forms the interface between the analysis logic and the driver
|
||||
// program, and has both input and an output components.
|
||||
//
|
||||
// As in a compiler, one pass may depend on the result computed by another.
|
||||
//
|
||||
// The Run function should not call any of the Pass functions concurrently.
|
||||
type Pass struct {
|
||||
Analyzer *Analyzer // the identity of the current analyzer
|
||||
|
||||
// syntax and type information
|
||||
Fset *token.FileSet // file position information
|
||||
Files []*ast.File // the abstract syntax tree of each file
|
||||
OtherFiles []string // names of non-Go files of this package
|
||||
Pkg *types.Package // type information about the package
|
||||
TypesInfo *types.Info // type information about the syntax trees
|
||||
|
||||
// Report reports a Diagnostic, a finding about a specific location
|
||||
// in the analyzed source code such as a potential mistake.
|
||||
// It may be called by the Run function.
|
||||
Report func(Diagnostic)
|
||||
|
||||
// ResultOf provides the inputs to this analysis pass, which are
|
||||
// the corresponding results of its prerequisite analyzers.
|
||||
// The map keys are the elements of Analysis.Required,
|
||||
// and the type of each corresponding value is the required
|
||||
// analysis's ResultType.
|
||||
ResultOf map[*Analyzer]interface{}
|
||||
|
||||
// -- facts --
|
||||
|
||||
// ImportObjectFact retrieves a fact associated with obj.
|
||||
// Given a value ptr of type *T, where *T satisfies Fact,
|
||||
// ImportObjectFact copies the value to *ptr.
|
||||
//
|
||||
// ImportObjectFact panics if called after the pass is complete.
|
||||
// ImportObjectFact is not concurrency-safe.
|
||||
ImportObjectFact func(obj types.Object, fact Fact) bool
|
||||
|
||||
// ImportPackageFact retrieves a fact associated with package pkg,
|
||||
// which must be this package or one of its dependencies.
|
||||
// See comments for ImportObjectFact.
|
||||
ImportPackageFact func(pkg *types.Package, fact Fact) bool
|
||||
|
||||
// ExportObjectFact associates a fact of type *T with the obj,
|
||||
// replacing any previous fact of that type.
|
||||
//
|
||||
// ExportObjectFact panics if it is called after the pass is
|
||||
// complete, or if obj does not belong to the package being analyzed.
|
||||
// ExportObjectFact is not concurrency-safe.
|
||||
ExportObjectFact func(obj types.Object, fact Fact)
|
||||
|
||||
// ExportPackageFact associates a fact with the current package.
|
||||
// See comments for ExportObjectFact.
|
||||
ExportPackageFact func(fact Fact)
|
||||
|
||||
/* Further fields may be added in future. */
|
||||
// For example, suggested or applied refactorings.
|
||||
}
|
||||
|
||||
// Reportf is a helper function that reports a Diagnostic using the
|
||||
// specified position and formatted error message.
|
||||
func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
pass.Report(Diagnostic{Pos: pos, Message: msg})
|
||||
}
|
||||
|
||||
func (pass *Pass) String() string {
|
||||
return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path())
|
||||
}
|
||||
|
||||
// A Fact is an intermediate fact produced during analysis.
|
||||
//
|
||||
// Each fact is associated with a named declaration (a types.Object) or
|
||||
// with a package as a whole. A single object or package may have
|
||||
// multiple associated facts, but only one of any particular fact type.
|
||||
//
|
||||
// A Fact represents a predicate such as "never returns", but does not
|
||||
// represent the subject of the predicate such as "function F" or "package P".
|
||||
//
|
||||
// Facts may be produced in one analysis pass and consumed by another
|
||||
// analysis pass even if these are in different address spaces.
|
||||
// If package P imports Q, all facts about Q produced during
|
||||
// analysis of that package will be available during later analysis of P.
|
||||
// Facts are analogous to type export data in a build system:
|
||||
// just as export data enables separate compilation of several passes,
|
||||
// facts enable "separate analysis".
|
||||
//
|
||||
// Each pass (a, p) starts with the set of facts produced by the
|
||||
// same analyzer a applied to the packages directly imported by p.
|
||||
// The analysis may add facts to the set, and they may be exported in turn.
|
||||
// An analysis's Run function may retrieve facts by calling
|
||||
// Pass.Import{Object,Package}Fact and update them using
|
||||
// Pass.Export{Object,Package}Fact.
|
||||
//
|
||||
// A fact is logically private to its Analysis. To pass values
|
||||
// between different analyzers, use the results mechanism;
|
||||
// see Analyzer.Requires, Analyzer.ResultType, and Pass.ResultOf.
|
||||
//
|
||||
// A Fact type must be a pointer.
|
||||
// Facts are encoded and decoded using encoding/gob.
|
||||
// A Fact may implement the GobEncoder/GobDecoder interfaces
|
||||
// to customize its encoding. Fact encoding should not fail.
|
||||
//
|
||||
// A Fact should not be modified once exported.
|
||||
type Fact interface {
|
||||
AFact() // dummy method to avoid type errors
|
||||
}
|
||||
|
||||
// A Diagnostic is a message associated with a source location.
|
||||
//
|
||||
// An Analyzer may return a variety of diagnostics; the optional Category,
|
||||
// which should be a constant, may be used to classify them.
|
||||
// It is primarily intended to make it easy to look up documentation.
|
||||
type Diagnostic struct {
|
||||
Pos token.Pos
|
||||
Category string // optional
|
||||
Message string
|
||||
}
|
||||
387
vendor/golang.org/x/tools/go/analysis/analysistest/analysistest.go
generated
vendored
Normal file
387
vendor/golang.org/x/tools/go/analysis/analysistest/analysistest.go
generated
vendored
Normal file
@@ -0,0 +1,387 @@
|
||||
// Package analysistest provides utilities for testing analyzers.
|
||||
package analysistest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/internal/checker"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
// WriteFiles is a helper function that creates a temporary directory
|
||||
// and populates it with a GOPATH-style project using filemap (which
|
||||
// maps file names to contents). On success it returns the name of the
|
||||
// directory and a cleanup function to delete it.
|
||||
func WriteFiles(filemap map[string]string) (dir string, cleanup func(), err error) {
|
||||
gopath, err := ioutil.TempDir("", "analysistest")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
cleanup = func() { os.RemoveAll(gopath) }
|
||||
|
||||
for name, content := range filemap {
|
||||
filename := filepath.Join(gopath, "src", name)
|
||||
os.MkdirAll(filepath.Dir(filename), 0777) // ignore error
|
||||
if err := ioutil.WriteFile(filename, []byte(content), 0666); err != nil {
|
||||
cleanup()
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
return gopath, cleanup, nil
|
||||
}
|
||||
|
||||
// TestData returns the effective filename of
|
||||
// the program's "testdata" directory.
|
||||
// This function may be overridden by projects using
|
||||
// an alternative build system (such as Blaze) that
|
||||
// does not run a test in its package directory.
|
||||
var TestData = func() string {
|
||||
testdata, err := filepath.Abs("testdata")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return testdata
|
||||
}
|
||||
|
||||
// Testing is an abstraction of a *testing.T.
|
||||
type Testing interface {
|
||||
Errorf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
// Run applies an analysis to the packages denoted by the "go list" patterns.
|
||||
//
|
||||
// It loads the packages from the specified GOPATH-style project
|
||||
// directory using golang.org/x/tools/go/packages, runs the analysis on
|
||||
// them, and checks that each analysis emits the expected diagnostics
|
||||
// and facts specified by the contents of '// want ...' comments in the
|
||||
// package's source files.
|
||||
//
|
||||
// An expectation of a Diagnostic is specified by a string literal
|
||||
// containing a regular expression that must match the diagnostic
|
||||
// message. For example:
|
||||
//
|
||||
// fmt.Printf("%s", 1) // want `cannot provide int 1 to %s`
|
||||
//
|
||||
// An expectation of a Fact associated with an object is specified by
|
||||
// 'name:"pattern"', where name is the name of the object, which must be
|
||||
// declared on the same line as the comment, and pattern is a regular
|
||||
// expression that must match the string representation of the fact,
|
||||
// fmt.Sprint(fact). For example:
|
||||
//
|
||||
// func panicf(format string, args interface{}) { // want panicf:"printfWrapper"
|
||||
//
|
||||
// Package facts are specified by the name "package" and appear on
|
||||
// line 1 of the first source file of the package.
|
||||
//
|
||||
// A single 'want' comment may contain a mixture of diagnostic and fact
|
||||
// expectations, including multiple facts about the same object:
|
||||
//
|
||||
// // want "diag" "diag2" x:"fact1" x:"fact2" y:"fact3"
|
||||
//
|
||||
// Unexpected diagnostics and facts, and unmatched expectations, are
|
||||
// reported as errors to the Testing.
|
||||
//
|
||||
// Run reports an error to the Testing if loading or analysis failed.
|
||||
// Run also returns a Result for each package for which analysis was
|
||||
// attempted, even if unsuccessful. It is safe for a test to ignore all
|
||||
// the results, but a test may use it to perform additional checks.
|
||||
func Run(t Testing, dir string, a *analysis.Analyzer, patterns ...string) []*Result {
|
||||
pkgs, err := loadPackages(dir, patterns...)
|
||||
if err != nil {
|
||||
t.Errorf("loading %s: %v", patterns, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
results := checker.TestAnalyzer(a, pkgs)
|
||||
for _, result := range results {
|
||||
if result.Err != nil {
|
||||
t.Errorf("error analyzing %s: %v", result.Pass, result.Err)
|
||||
} else {
|
||||
check(t, dir, result.Pass, result.Diagnostics, result.Facts)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// A Result holds the result of applying an analyzer to a package.
|
||||
type Result = checker.TestAnalyzerResult
|
||||
|
||||
// loadPackages uses go/packages to load a specified packages (from source, with
|
||||
// dependencies) from dir, which is the root of a GOPATH-style project
|
||||
// tree. It returns an error if any package had an error, or the pattern
|
||||
// matched no packages.
|
||||
func loadPackages(dir string, patterns ...string) ([]*packages.Package, error) {
|
||||
// packages.Load loads the real standard library, not a minimal
|
||||
// fake version, which would be more efficient, especially if we
|
||||
// have many small tests that import, say, net/http.
|
||||
// However there is no easy way to make go/packages to consume
|
||||
// a list of packages we generate and then do the parsing and
|
||||
// typechecking, though this feature seems to be a recurring need.
|
||||
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadAllSyntax,
|
||||
Dir: dir,
|
||||
Tests: true,
|
||||
Env: append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off"),
|
||||
}
|
||||
pkgs, err := packages.Load(cfg, patterns...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Print errors but do not stop:
|
||||
// some Analyzers may be disposed to RunDespiteErrors.
|
||||
packages.PrintErrors(pkgs)
|
||||
|
||||
if len(pkgs) == 0 {
|
||||
return nil, fmt.Errorf("no packages matched %s", patterns)
|
||||
}
|
||||
return pkgs, nil
|
||||
}
|
||||
|
||||
// check inspects an analysis pass on which the analysis has already
|
||||
// been run, and verifies that all reported diagnostics and facts match
|
||||
// specified by the contents of "// want ..." comments in the package's
|
||||
// source files, which must have been parsed with comments enabled.
|
||||
func check(t Testing, gopath string, pass *analysis.Pass, diagnostics []analysis.Diagnostic, facts map[types.Object][]analysis.Fact) {
|
||||
|
||||
type key struct {
|
||||
file string
|
||||
line int
|
||||
}
|
||||
|
||||
want := make(map[key][]expectation)
|
||||
|
||||
// processComment parses expectations out of comments.
|
||||
processComment := func(filename string, linenum int, text string) {
|
||||
text = strings.TrimSpace(text)
|
||||
|
||||
// Any comment starting with "want" is treated
|
||||
// as an expectation, even without following whitespace.
|
||||
if rest := strings.TrimPrefix(text, "want"); rest != text {
|
||||
expects, err := parseExpectations(rest)
|
||||
if err != nil {
|
||||
t.Errorf("%s:%d: in 'want' comment: %s", filename, linenum, err)
|
||||
return
|
||||
}
|
||||
if expects != nil {
|
||||
want[key{filename, linenum}] = expects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract 'want' comments from Go files.
|
||||
for _, f := range pass.Files {
|
||||
for _, cgroup := range f.Comments {
|
||||
for _, c := range cgroup.List {
|
||||
|
||||
text := strings.TrimPrefix(c.Text, "//")
|
||||
if text == c.Text {
|
||||
continue // not a //-comment
|
||||
}
|
||||
|
||||
// Hack: treat a comment of the form "//...// want..."
|
||||
// as if it starts at 'want'.
|
||||
// This allows us to add comments on comments,
|
||||
// as required when testing the buildtag analyzer.
|
||||
if i := strings.Index(text, "// want"); i >= 0 {
|
||||
text = text[i+len("// "):]
|
||||
}
|
||||
|
||||
// It's tempting to compute the filename
|
||||
// once outside the loop, but it's
|
||||
// incorrect because it can change due
|
||||
// to //line directives.
|
||||
posn := pass.Fset.Position(c.Pos())
|
||||
filename := sanitize(gopath, posn.Filename)
|
||||
processComment(filename, posn.Line, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract 'want' comments from non-Go files.
|
||||
// TODO(adonovan): we may need to handle //line directives.
|
||||
for _, filename := range pass.OtherFiles {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Errorf("can't read '// want' comments from %s: %v", filename, err)
|
||||
continue
|
||||
}
|
||||
filename := sanitize(gopath, filename)
|
||||
linenum := 0
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
linenum++
|
||||
if i := strings.Index(line, "//"); i >= 0 {
|
||||
line = line[i+len("//"):]
|
||||
processComment(filename, linenum, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkMessage := func(posn token.Position, kind, name, message string) {
|
||||
posn.Filename = sanitize(gopath, posn.Filename)
|
||||
k := key{posn.Filename, posn.Line}
|
||||
expects := want[k]
|
||||
var unmatched []string
|
||||
for i, exp := range expects {
|
||||
if exp.kind == kind && exp.name == name {
|
||||
if exp.rx.MatchString(message) {
|
||||
// matched: remove the expectation.
|
||||
expects[i] = expects[len(expects)-1]
|
||||
expects = expects[:len(expects)-1]
|
||||
want[k] = expects
|
||||
return
|
||||
}
|
||||
unmatched = append(unmatched, fmt.Sprintf("%q", exp.rx))
|
||||
}
|
||||
}
|
||||
if unmatched == nil {
|
||||
t.Errorf("%v: unexpected %s: %v", posn, kind, message)
|
||||
} else {
|
||||
t.Errorf("%v: %s %q does not match pattern %s",
|
||||
posn, kind, message, strings.Join(unmatched, " or "))
|
||||
}
|
||||
}
|
||||
|
||||
// Check the diagnostics match expectations.
|
||||
for _, f := range diagnostics {
|
||||
posn := pass.Fset.Position(f.Pos)
|
||||
checkMessage(posn, "diagnostic", "", f.Message)
|
||||
}
|
||||
|
||||
// Check the facts match expectations.
|
||||
// Report errors in lexical order for determinism.
|
||||
// (It's only deterministic within each file, not across files,
|
||||
// because go/packages does not guarantee file.Pos is ascending
|
||||
// across the files of a single compilation unit.)
|
||||
var objects []types.Object
|
||||
for obj := range facts {
|
||||
objects = append(objects, obj)
|
||||
}
|
||||
sort.Slice(objects, func(i, j int) bool {
|
||||
return objects[i].Pos() < objects[j].Pos()
|
||||
})
|
||||
for _, obj := range objects {
|
||||
var posn token.Position
|
||||
var name string
|
||||
if obj != nil {
|
||||
// Object facts are reported on the declaring line.
|
||||
name = obj.Name()
|
||||
posn = pass.Fset.Position(obj.Pos())
|
||||
} else {
|
||||
// Package facts are reported at the start of the file.
|
||||
name = "package"
|
||||
posn = pass.Fset.Position(pass.Files[0].Pos())
|
||||
posn.Line = 1
|
||||
}
|
||||
|
||||
for _, fact := range facts[obj] {
|
||||
checkMessage(posn, "fact", name, fmt.Sprint(fact))
|
||||
}
|
||||
}
|
||||
|
||||
// Reject surplus expectations.
|
||||
//
|
||||
// Sometimes an Analyzer reports two similar diagnostics on a
|
||||
// line with only one expectation. The reader may be confused by
|
||||
// the error message.
|
||||
// TODO(adonovan): print a better error:
|
||||
// "got 2 diagnostics here; each one needs its own expectation".
|
||||
var surplus []string
|
||||
for key, expects := range want {
|
||||
for _, exp := range expects {
|
||||
err := fmt.Sprintf("%s:%d: no %s was reported matching %q", key.file, key.line, exp.kind, exp.rx)
|
||||
surplus = append(surplus, err)
|
||||
}
|
||||
}
|
||||
sort.Strings(surplus)
|
||||
for _, err := range surplus {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
type expectation struct {
|
||||
kind string // either "fact" or "diagnostic"
|
||||
name string // name of object to which fact belongs, or "package" ("fact" only)
|
||||
rx *regexp.Regexp
|
||||
}
|
||||
|
||||
func (ex expectation) String() string {
|
||||
return fmt.Sprintf("%s %s:%q", ex.kind, ex.name, ex.rx) // for debugging
|
||||
}
|
||||
|
||||
// parseExpectations parses the content of a "// want ..." comment
|
||||
// and returns the expections, a mixture of diagnostics ("rx") and
|
||||
// facts (name:"rx").
|
||||
func parseExpectations(text string) ([]expectation, error) {
|
||||
var scanErr string
|
||||
sc := new(scanner.Scanner).Init(strings.NewReader(text))
|
||||
sc.Error = func(s *scanner.Scanner, msg string) {
|
||||
scanErr = msg // e.g. bad string escape
|
||||
}
|
||||
sc.Mode = scanner.ScanIdents | scanner.ScanStrings | scanner.ScanRawStrings
|
||||
|
||||
scanRegexp := func(tok rune) (*regexp.Regexp, error) {
|
||||
if tok != scanner.String && tok != scanner.RawString {
|
||||
return nil, fmt.Errorf("got %s, want regular expression",
|
||||
scanner.TokenString(tok))
|
||||
}
|
||||
pattern, _ := strconv.Unquote(sc.TokenText()) // can't fail
|
||||
return regexp.Compile(pattern)
|
||||
}
|
||||
|
||||
var expects []expectation
|
||||
for {
|
||||
tok := sc.Scan()
|
||||
switch tok {
|
||||
case scanner.String, scanner.RawString:
|
||||
rx, err := scanRegexp(tok)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expects = append(expects, expectation{"diagnostic", "", rx})
|
||||
|
||||
case scanner.Ident:
|
||||
name := sc.TokenText()
|
||||
tok = sc.Scan()
|
||||
if tok != ':' {
|
||||
return nil, fmt.Errorf("got %s after %s, want ':'",
|
||||
scanner.TokenString(tok), name)
|
||||
}
|
||||
tok = sc.Scan()
|
||||
rx, err := scanRegexp(tok)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expects = append(expects, expectation{"fact", name, rx})
|
||||
|
||||
case scanner.EOF:
|
||||
if scanErr != "" {
|
||||
return nil, fmt.Errorf("%s", scanErr)
|
||||
}
|
||||
return expects, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected %s", scanner.TokenString(tok))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize removes the GOPATH portion of the filename,
|
||||
// typically a gnarly /tmp directory, and returns the rest.
|
||||
func sanitize(gopath, filename string) string {
|
||||
prefix := gopath + string(os.PathSeparator) + "src" + string(os.PathSeparator)
|
||||
return filepath.ToSlash(strings.TrimPrefix(filename, prefix))
|
||||
}
|
||||
93
vendor/golang.org/x/tools/go/analysis/analysistest/analysistest_test.go
generated
vendored
Normal file
93
vendor/golang.org/x/tools/go/analysis/analysistest/analysistest_test.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package analysistest_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// This test currently requires GOPATH mode.
|
||||
// Explicitly disabling module mode should suffix, but
|
||||
// we'll also turn off GOPROXY just for good measure.
|
||||
if err := os.Setenv("GO111MODULE", "off"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := os.Setenv("GOPROXY", "off"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestTheTest tests the analysistest testing infrastructure.
|
||||
func TestTheTest(t *testing.T) {
|
||||
// We'll simulate a partly failing test of the findcall analysis,
|
||||
// which (by default) reports calls to functions named 'println'.
|
||||
findcall.Analyzer.Flags.Set("name", "println")
|
||||
|
||||
filemap := map[string]string{"a/b.go": `package main
|
||||
|
||||
func main() {
|
||||
// The expectation is ill-formed:
|
||||
print() // want: "diagnostic"
|
||||
print() // want foo"fact"
|
||||
print() // want foo:
|
||||
print() // want "\xZZ scan error"
|
||||
|
||||
// A dignostic is reported at this line, but the expectation doesn't match:
|
||||
println("hello, world") // want "wrong expectation text"
|
||||
|
||||
// An unexpected diagnostic is reported at this line:
|
||||
println() // trigger an unexpected diagnostic
|
||||
|
||||
// No diagnostic is reported at this line:
|
||||
print() // want "unsatisfied expectation"
|
||||
|
||||
// OK
|
||||
println("hello, world") // want "call of println"
|
||||
|
||||
// OK (multiple expectations on same line)
|
||||
println(); println() // want "call of println(...)" "call of println(...)"
|
||||
}
|
||||
|
||||
// OK (facts and diagnostics on same line)
|
||||
func println(...interface{}) { println() } // want println:"found" "call of println(...)"
|
||||
|
||||
`}
|
||||
dir, cleanup, err := analysistest.WriteFiles(filemap)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
var got []string
|
||||
t2 := errorfunc(func(s string) { got = append(got, s) }) // a fake *testing.T
|
||||
analysistest.Run(t2, dir, findcall.Analyzer, "a")
|
||||
|
||||
want := []string{
|
||||
`a/b.go:5: in 'want' comment: unexpected ":"`,
|
||||
`a/b.go:6: in 'want' comment: got String after foo, want ':'`,
|
||||
`a/b.go:7: in 'want' comment: got EOF, want regular expression`,
|
||||
`a/b.go:8: in 'want' comment: illegal char escape`,
|
||||
`a/b.go:11:9: diagnostic "call of println(...)" does not match pattern "wrong expectation text"`,
|
||||
`a/b.go:14:9: unexpected diagnostic: call of println(...)`,
|
||||
`a/b.go:11: no diagnostic was reported matching "wrong expectation text"`,
|
||||
`a/b.go:17: no diagnostic was reported matching "unsatisfied expectation"`,
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got:\n%s\nwant:\n%s",
|
||||
strings.Join(got, "\n"),
|
||||
strings.Join(want, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
type errorfunc func(string)
|
||||
|
||||
func (f errorfunc) Errorf(format string, args ...interface{}) {
|
||||
f(fmt.Sprintf(format, args...))
|
||||
}
|
||||
74
vendor/golang.org/x/tools/go/analysis/cmd/vet-lite/main.go
generated
vendored
Normal file
74
vendor/golang.org/x/tools/go/analysis/cmd/vet-lite/main.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// The vet-lite command is a driver for static checkers conforming to
|
||||
// the golang.org/x/tools/go/analysis API. It must be run by go vet:
|
||||
//
|
||||
// $ go vet -vettool=$(which vet-lite)
|
||||
//
|
||||
// For a checker also capable of running standalone, use multichecker.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/unitchecker"
|
||||
|
||||
"golang.org/x/tools/go/analysis/passes/asmdecl"
|
||||
"golang.org/x/tools/go/analysis/passes/assign"
|
||||
"golang.org/x/tools/go/analysis/passes/atomic"
|
||||
"golang.org/x/tools/go/analysis/passes/bools"
|
||||
"golang.org/x/tools/go/analysis/passes/buildtag"
|
||||
"golang.org/x/tools/go/analysis/passes/cgocall"
|
||||
"golang.org/x/tools/go/analysis/passes/composite"
|
||||
"golang.org/x/tools/go/analysis/passes/copylock"
|
||||
"golang.org/x/tools/go/analysis/passes/httpresponse"
|
||||
"golang.org/x/tools/go/analysis/passes/loopclosure"
|
||||
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
||||
"golang.org/x/tools/go/analysis/passes/nilfunc"
|
||||
"golang.org/x/tools/go/analysis/passes/printf"
|
||||
"golang.org/x/tools/go/analysis/passes/shift"
|
||||
"golang.org/x/tools/go/analysis/passes/stdmethods"
|
||||
"golang.org/x/tools/go/analysis/passes/structtag"
|
||||
"golang.org/x/tools/go/analysis/passes/tests"
|
||||
"golang.org/x/tools/go/analysis/passes/unmarshal"
|
||||
"golang.org/x/tools/go/analysis/passes/unreachable"
|
||||
"golang.org/x/tools/go/analysis/passes/unsafeptr"
|
||||
"golang.org/x/tools/go/analysis/passes/unusedresult"
|
||||
)
|
||||
|
||||
// Legacy vet had the concept of "experimental" checkers. There
|
||||
// was exactly one, shadow, and it had to be explicitly enabled
|
||||
// by the -shadow flag, which would of course disable all the
|
||||
// other tristate flags, requiring the -all flag to reenable them.
|
||||
// (By itself, -all did not enable all checkers.)
|
||||
// The -all flag is no longer needed, so it is a no-op.
|
||||
//
|
||||
// The shadow analyzer has been removed from the suite,
|
||||
// but can be run using these additional commands:
|
||||
// $ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
|
||||
// $ go vet -vettool=$(which shadow)
|
||||
// Alternatively, one could build a multichecker containing all
|
||||
// the desired checks (vet's suite + shadow) and run it in a
|
||||
// single "go vet" command.
|
||||
|
||||
func main() {
|
||||
unitchecker.Main(
|
||||
asmdecl.Analyzer,
|
||||
assign.Analyzer,
|
||||
atomic.Analyzer,
|
||||
bools.Analyzer,
|
||||
buildtag.Analyzer,
|
||||
cgocall.Analyzer,
|
||||
composite.Analyzer,
|
||||
copylock.Analyzer,
|
||||
httpresponse.Analyzer,
|
||||
loopclosure.Analyzer,
|
||||
lostcancel.Analyzer,
|
||||
nilfunc.Analyzer,
|
||||
printf.Analyzer,
|
||||
shift.Analyzer,
|
||||
stdmethods.Analyzer,
|
||||
structtag.Analyzer,
|
||||
tests.Analyzer,
|
||||
unmarshal.Analyzer,
|
||||
unreachable.Analyzer,
|
||||
unsafeptr.Analyzer,
|
||||
unusedresult.Analyzer,
|
||||
)
|
||||
}
|
||||
33
vendor/golang.org/x/tools/go/analysis/cmd/vet/README
generated
vendored
Normal file
33
vendor/golang.org/x/tools/go/analysis/cmd/vet/README
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
Vet is a tool that checks correctness of Go programs. It runs a suite of tests,
|
||||
each tailored to check for a particular class of errors. Examples include incorrect
|
||||
Printf format verbs and malformed build tags.
|
||||
|
||||
Over time many checks have been added to vet's suite, but many more have been
|
||||
rejected as not appropriate for the tool. The criteria applied when selecting which
|
||||
checks to add are:
|
||||
|
||||
Correctness:
|
||||
|
||||
Vet's checks are about correctness, not style. A vet check must identify real or
|
||||
potential bugs that could cause incorrect compilation or execution. A check that
|
||||
only identifies stylistic points or alternative correct approaches to a situation
|
||||
is not acceptable.
|
||||
|
||||
Frequency:
|
||||
|
||||
Vet is run every day by many programmers, often as part of every compilation or
|
||||
submission. The cost in execution time is considerable, especially in aggregate,
|
||||
so checks must be likely enough to find real problems that they are worth the
|
||||
overhead of the added check. A new check that finds only a handful of problems
|
||||
across all existing programs, even if the problem is significant, is not worth
|
||||
adding to the suite everyone runs daily.
|
||||
|
||||
Precision:
|
||||
|
||||
Most of vet's checks are heuristic and can generate both false positives (flagging
|
||||
correct programs) and false negatives (not flagging incorrect ones). The rate of
|
||||
both these failures must be very small. A check that is too noisy will be ignored
|
||||
by the programmer overwhelmed by the output; a check that misses too many of the
|
||||
cases it's looking for will give a false sense of security. Neither is acceptable.
|
||||
A vet check must be accurate enough that everything it reports is worth examining,
|
||||
and complete enough to encourage real confidence.
|
||||
80
vendor/golang.org/x/tools/go/analysis/cmd/vet/vet.go
generated
vendored
Normal file
80
vendor/golang.org/x/tools/go/analysis/cmd/vet/vet.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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.
|
||||
|
||||
// The vet command is a static checker for Go programs. It has pluggable
|
||||
// analyzers defined using the golang.org/x/tools/go/analysis API, and
|
||||
// using the golang.org/x/tools/go/packages API to load packages in any
|
||||
// build system.
|
||||
//
|
||||
// Each analyzer flag name is preceded by the analyzer name: -NAME.flag.
|
||||
// In addition, the -NAME flag itself controls whether the
|
||||
// diagnostics of that analyzer are displayed. (A disabled analyzer may yet
|
||||
// be run if it is required by some other analyzer that is enabled.)
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/multichecker"
|
||||
|
||||
// analysis plug-ins
|
||||
"golang.org/x/tools/go/analysis/passes/asmdecl"
|
||||
"golang.org/x/tools/go/analysis/passes/assign"
|
||||
"golang.org/x/tools/go/analysis/passes/atomic"
|
||||
"golang.org/x/tools/go/analysis/passes/bools"
|
||||
"golang.org/x/tools/go/analysis/passes/buildtag"
|
||||
"golang.org/x/tools/go/analysis/passes/cgocall"
|
||||
"golang.org/x/tools/go/analysis/passes/composite"
|
||||
"golang.org/x/tools/go/analysis/passes/copylock"
|
||||
"golang.org/x/tools/go/analysis/passes/httpresponse"
|
||||
"golang.org/x/tools/go/analysis/passes/loopclosure"
|
||||
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
||||
"golang.org/x/tools/go/analysis/passes/nilfunc"
|
||||
"golang.org/x/tools/go/analysis/passes/nilness"
|
||||
"golang.org/x/tools/go/analysis/passes/printf"
|
||||
"golang.org/x/tools/go/analysis/passes/shift"
|
||||
"golang.org/x/tools/go/analysis/passes/stdmethods"
|
||||
"golang.org/x/tools/go/analysis/passes/structtag"
|
||||
"golang.org/x/tools/go/analysis/passes/tests"
|
||||
"golang.org/x/tools/go/analysis/passes/unmarshal"
|
||||
"golang.org/x/tools/go/analysis/passes/unreachable"
|
||||
"golang.org/x/tools/go/analysis/passes/unsafeptr"
|
||||
"golang.org/x/tools/go/analysis/passes/unusedresult"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// This suite of analyzers is applied to all code
|
||||
// in GOROOT by GOROOT/src/cmd/vet/all. When adding
|
||||
// a new analyzer, update the whitelist used by vet/all,
|
||||
// or change its vet command to disable the new analyzer.
|
||||
multichecker.Main(
|
||||
// the traditional vet suite:
|
||||
asmdecl.Analyzer,
|
||||
assign.Analyzer,
|
||||
atomic.Analyzer,
|
||||
bools.Analyzer,
|
||||
buildtag.Analyzer,
|
||||
cgocall.Analyzer,
|
||||
composite.Analyzer,
|
||||
copylock.Analyzer,
|
||||
httpresponse.Analyzer,
|
||||
loopclosure.Analyzer,
|
||||
lostcancel.Analyzer,
|
||||
nilfunc.Analyzer,
|
||||
printf.Analyzer,
|
||||
shift.Analyzer,
|
||||
stdmethods.Analyzer,
|
||||
structtag.Analyzer,
|
||||
tests.Analyzer,
|
||||
unmarshal.Analyzer,
|
||||
unreachable.Analyzer,
|
||||
unsafeptr.Analyzer,
|
||||
unusedresult.Analyzer,
|
||||
|
||||
// for debugging:
|
||||
// findcall.Analyzer,
|
||||
// pkgfact.Analyzer,
|
||||
|
||||
// uses SSA:
|
||||
nilness.Analyzer,
|
||||
)
|
||||
}
|
||||
340
vendor/golang.org/x/tools/go/analysis/doc.go
generated
vendored
Normal file
340
vendor/golang.org/x/tools/go/analysis/doc.go
generated
vendored
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
|
||||
The analysis package defines the interface between a modular static
|
||||
analysis and an analysis driver program.
|
||||
|
||||
|
||||
THIS INTERFACE IS EXPERIMENTAL AND SUBJECT TO CHANGE.
|
||||
We aim to finalize it by November 2018.
|
||||
|
||||
Background
|
||||
|
||||
A static analysis is a function that inspects a package of Go code and
|
||||
reports a set of diagnostics (typically mistakes in the code), and
|
||||
perhaps produces other results as well, such as suggested refactorings
|
||||
or other facts. An analysis that reports mistakes is informally called a
|
||||
"checker". For example, the printf checker reports mistakes in
|
||||
fmt.Printf format strings.
|
||||
|
||||
A "modular" analysis is one that inspects one package at a time but can
|
||||
save information from a lower-level package and use it when inspecting a
|
||||
higher-level package, analogous to separate compilation in a toolchain.
|
||||
The printf checker is modular: when it discovers that a function such as
|
||||
log.Fatalf delegates to fmt.Printf, it records this fact, and checks
|
||||
calls to that function too, including calls made from another package.
|
||||
|
||||
By implementing a common interface, checkers from a variety of sources
|
||||
can be easily selected, incorporated, and reused in a wide range of
|
||||
driver programs including command-line tools (such as vet), text editors and
|
||||
IDEs, build and test systems (such as go build, Bazel, or Buck), test
|
||||
frameworks, code review tools, code-base indexers (such as SourceGraph),
|
||||
documentation viewers (such as godoc), batch pipelines for large code
|
||||
bases, and so on.
|
||||
|
||||
|
||||
Analyzer
|
||||
|
||||
The primary type in the API is Analyzer. An Analyzer statically
|
||||
describes an analysis function: its name, documentation, flags,
|
||||
relationship to other analyzers, and of course, its logic.
|
||||
|
||||
To define an analysis, a user declares a (logically constant) variable
|
||||
of type Analyzer. Here is a typical example from one of the analyzers in
|
||||
the go/analysis/passes/ subdirectory:
|
||||
|
||||
package unusedresult
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "unusedresult",
|
||||
Doc: "check for unused results of calls to some functions",
|
||||
Run: run,
|
||||
...
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
An analysis driver is a program such as vet that runs a set of
|
||||
analyses and prints the diagnostics that they report.
|
||||
The driver program must import the list of Analyzers it needs.
|
||||
Typically each Analyzer resides in a separate package.
|
||||
To add a new Analyzer to an existing driver, add another item to the list:
|
||||
|
||||
import ( "unusedresult"; "nilness"; "printf" )
|
||||
|
||||
var analyses = []*analysis.Analyzer{
|
||||
unusedresult.Analyzer,
|
||||
nilness.Analyzer,
|
||||
printf.Analyzer,
|
||||
}
|
||||
|
||||
A driver may use the name, flags, and documentation to provide on-line
|
||||
help that describes the analyses its performs.
|
||||
The doc comment contains a brief one-line summary,
|
||||
optionally followed by paragraphs of explanation.
|
||||
The vet command, shown below, is an example of a driver that runs
|
||||
multiple analyzers. It is based on the multichecker package
|
||||
(see the "Standalone commands" section for details).
|
||||
|
||||
$ go build golang.org/x/tools/go/analysis/cmd/vet
|
||||
$ ./vet help
|
||||
vet is a tool for static analysis of Go programs.
|
||||
|
||||
Usage: vet [-flag] [package]
|
||||
|
||||
Registered analyzers:
|
||||
|
||||
asmdecl report mismatches between assembly files and Go declarations
|
||||
assign check for useless assignments
|
||||
atomic check for common mistakes using the sync/atomic package
|
||||
...
|
||||
unusedresult check for unused results of calls to some functions
|
||||
|
||||
$ ./vet help unusedresult
|
||||
unusedresult: check for unused results of calls to some functions
|
||||
|
||||
Analyzer flags:
|
||||
|
||||
-unusedresult.funcs value
|
||||
comma-separated list of functions whose results must be used (default Error,String)
|
||||
-unusedresult.stringmethods value
|
||||
comma-separated list of names of methods of type func() string whose results must be used
|
||||
|
||||
Some functions like fmt.Errorf return a result and have no side effects,
|
||||
so it is always a mistake to discard the result. This analyzer reports
|
||||
calls to certain functions in which the result of the call is ignored.
|
||||
|
||||
The set of functions may be controlled using flags.
|
||||
|
||||
The Analyzer type has more fields besides those shown above:
|
||||
|
||||
type Analyzer struct {
|
||||
Name string
|
||||
Doc string
|
||||
Flags flag.FlagSet
|
||||
Run func(*Pass) (interface{}, error)
|
||||
RunDespiteErrors bool
|
||||
ResultType reflect.Type
|
||||
Requires []*Analyzer
|
||||
FactTypes []Fact
|
||||
}
|
||||
|
||||
The Flags field declares a set of named (global) flag variables that
|
||||
control analysis behavior. Unlike vet, analysis flags are not declared
|
||||
directly in the command line FlagSet; it is up to the driver to set the
|
||||
flag variables. A driver for a single analysis, a, might expose its flag
|
||||
f directly on the command line as -f, whereas a driver for multiple
|
||||
analyses might prefix the flag name by the analysis name (-a.f) to avoid
|
||||
ambiguity. An IDE might expose the flags through a graphical interface,
|
||||
and a batch pipeline might configure them from a config file.
|
||||
See the "findcall" analyzer for an example of flags in action.
|
||||
|
||||
The RunDespiteErrors flag indicates whether the analysis is equipped to
|
||||
handle ill-typed code. If not, the driver will skip the analysis if
|
||||
there were parse or type errors.
|
||||
The optional ResultType field specifies the type of the result value
|
||||
computed by this analysis and made available to other analyses.
|
||||
The Requires field specifies a list of analyses upon which
|
||||
this one depends and whose results it may access, and it constrains the
|
||||
order in which a driver may run analyses.
|
||||
The FactTypes field is discussed in the section on Modularity.
|
||||
The analysis package provides a Validate function to perform basic
|
||||
sanity checks on an Analyzer, such as that its Requires graph is
|
||||
acyclic, its fact and result types are unique, and so on.
|
||||
|
||||
Finally, the Run field contains a function to be called by the driver to
|
||||
execute the analysis on a single package. The driver passes it an
|
||||
instance of the Pass type.
|
||||
|
||||
|
||||
Pass
|
||||
|
||||
A Pass describes a single unit of work: the application of a particular
|
||||
Analyzer to a particular package of Go code.
|
||||
The Pass provides information to the Analyzer's Run function about the
|
||||
package being analyzed, and provides operations to the Run function for
|
||||
reporting diagnostics and other information back to the driver.
|
||||
|
||||
type Pass struct {
|
||||
Fset *token.FileSet
|
||||
Files []*ast.File
|
||||
OtherFiles []string
|
||||
Pkg *types.Package
|
||||
TypesInfo *types.Info
|
||||
ResultOf map[*Analyzer]interface{}
|
||||
Report func(Diagnostic)
|
||||
...
|
||||
}
|
||||
|
||||
The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees,
|
||||
type information, and source positions for a single package of Go code.
|
||||
|
||||
The OtherFiles field provides the names, but not the contents, of non-Go
|
||||
files such as assembly that are part of this package. See the "asmdecl"
|
||||
or "buildtags" analyzers for examples of loading non-Go files and report
|
||||
diagnostics against them.
|
||||
|
||||
The ResultOf field provides the results computed by the analyzers
|
||||
required by this one, as expressed in its Analyzer.Requires field. The
|
||||
driver runs the required analyzers first and makes their results
|
||||
available in this map. Each Analyzer must return a value of the type
|
||||
described in its Analyzer.ResultType field.
|
||||
For example, the "ctrlflow" analyzer returns a *ctrlflow.CFGs, which
|
||||
provides a control-flow graph for each function in the package (see
|
||||
golang.org/x/tools/go/cfg); the "inspect" analyzer returns a value that
|
||||
enables other Analyzers to traverse the syntax trees of the package more
|
||||
efficiently; and the "buildssa" analyzer constructs an SSA-form
|
||||
intermediate representation.
|
||||
Each of these Analyzers extends the capabilities of later Analyzers
|
||||
without adding a dependency to the core API, so an analysis tool pays
|
||||
only for the extensions it needs.
|
||||
|
||||
The Report function emits a diagnostic, a message associated with a
|
||||
source position. For most analyses, diagnostics are their primary
|
||||
result.
|
||||
For convenience, Pass provides a helper method, Reportf, to report a new
|
||||
diagnostic by formatting a string.
|
||||
Diagnostic is defined as:
|
||||
|
||||
type Diagnostic struct {
|
||||
Pos token.Pos
|
||||
Category string // optional
|
||||
Message string
|
||||
}
|
||||
|
||||
The optional Category field is a short identifier that classifies the
|
||||
kind of message when an analysis produces several kinds of diagnostic.
|
||||
|
||||
Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
|
||||
and buildtag, inspect the raw text of Go source files or even non-Go
|
||||
files such as assembly. To report a diagnostic against a line of a
|
||||
raw text file, use the following sequence:
|
||||
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil { ... }
|
||||
tf := fset.AddFile(filename, -1, len(content))
|
||||
tf.SetLinesForContent(content)
|
||||
...
|
||||
pass.Reportf(tf.LineStart(line), "oops")
|
||||
|
||||
|
||||
Modular analysis with Facts
|
||||
|
||||
To improve efficiency and scalability, large programs are routinely
|
||||
built using separate compilation: units of the program are compiled
|
||||
separately, and recompiled only when one of their dependencies changes;
|
||||
independent modules may be compiled in parallel. The same technique may
|
||||
be applied to static analyses, for the same benefits. Such analyses are
|
||||
described as "modular".
|
||||
|
||||
A compiler’s type checker is an example of a modular static analysis.
|
||||
Many other checkers we would like to apply to Go programs can be
|
||||
understood as alternative or non-standard type systems. For example,
|
||||
vet's printf checker infers whether a function has the "printf wrapper"
|
||||
type, and it applies stricter checks to calls of such functions. In
|
||||
addition, it records which functions are printf wrappers for use by
|
||||
later analysis units to identify other printf wrappers by induction.
|
||||
A result such as “f is a printf wrapper” that is not interesting by
|
||||
itself but serves as a stepping stone to an interesting result (such as
|
||||
a diagnostic) is called a "fact".
|
||||
|
||||
The analysis API allows an analysis to define new types of facts, to
|
||||
associate facts of these types with objects (named entities) declared
|
||||
within the current package, or with the package as a whole, and to query
|
||||
for an existing fact of a given type associated with an object or
|
||||
package.
|
||||
|
||||
An Analyzer that uses facts must declare their types:
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "printf",
|
||||
FactTypes: []reflect.Type{reflect.TypeOf(new(isWrapper))},
|
||||
...
|
||||
}
|
||||
|
||||
type isWrapper struct{} // => *types.Func f “is a printf wrapper”
|
||||
|
||||
A driver program ensures that facts for a pass’s dependencies are
|
||||
generated before analyzing the pass and are responsible for propagating
|
||||
facts between from one pass to another, possibly across address spaces.
|
||||
Consequently, Facts must be serializable. The API requires that drivers
|
||||
use the gob encoding, an efficient, robust, self-describing binary
|
||||
protocol. A fact type may implement the GobEncoder/GobDecoder interfaces
|
||||
if the default encoding is unsuitable. Facts should be stateless.
|
||||
|
||||
The Pass type has functions to import and export facts,
|
||||
associated either with an object or with a package:
|
||||
|
||||
type Pass struct {
|
||||
...
|
||||
ExportObjectFact func(types.Object, Fact)
|
||||
ImportObjectFact func(types.Object, Fact) bool
|
||||
|
||||
ExportPackageFact func(fact Fact)
|
||||
ImportPackageFact func(*types.Package, Fact) bool
|
||||
}
|
||||
|
||||
An Analyzer may only export facts associated with the current package or
|
||||
its objects, though it may import facts from any package or object that
|
||||
is an import dependency of the current package.
|
||||
|
||||
Conceptually, ExportObjectFact(obj, fact) inserts fact into a hidden map keyed by
|
||||
the pair (obj, TypeOf(fact)), and the ImportObjectFact function
|
||||
retrieves the entry from this map and copies its value into the variable
|
||||
pointed to by fact. This scheme assumes that the concrete type of fact
|
||||
is a pointer; this assumption is checked by the Validate function.
|
||||
See the "printf" analyzer for an example of object facts in action.
|
||||
|
||||
Some driver implementations (such as those based on Bazel and Blaze) do
|
||||
not currently apply analyzers to packages of the standard library.
|
||||
Therefore, for best results, analyzer authors should not rely on
|
||||
analysis facts being available for standard packages.
|
||||
For example, although the printf checker is capable of deducing during
|
||||
analysis of the log package that log.Printf is a printf-wrapper,
|
||||
this fact is built in to the analyzer so that it correctly checks
|
||||
calls to log.Printf even when run in a driver that does not apply
|
||||
it to standard packages. We plan to remove this limitation in future.
|
||||
|
||||
|
||||
Testing an Analyzer
|
||||
|
||||
The analysistest subpackage provides utilities for testing an Analyzer.
|
||||
In a few lines of code, it is possible to run an analyzer on a package
|
||||
of testdata files and check that it reported all the expected
|
||||
diagnostics and facts (and no more). Expectations are expressed using
|
||||
"// want ..." comments in the input code.
|
||||
|
||||
|
||||
Standalone commands
|
||||
|
||||
Analyzers are provided in the form of packages that a driver program is
|
||||
expected to import. The vet command imports a set of several analyzers,
|
||||
but users may wish to define their own analysis commands that perform
|
||||
additional checks. To simplify the task of creating an analysis command,
|
||||
either for a single analyzer or for a whole suite, we provide the
|
||||
singlechecker and multichecker subpackages.
|
||||
|
||||
The singlechecker package provides the main function for a command that
|
||||
runs one analyzer. By convention, each analyzer such as
|
||||
go/passes/findcall should be accompanied by a singlechecker-based
|
||||
command such as go/analysis/passes/findcall/cmd/findcall, defined in its
|
||||
entirety as:
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(findcall.Analyzer) }
|
||||
|
||||
A tool that provides multiple analyzers can use multichecker in a
|
||||
similar way, giving it the list of Analyzers.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
package analysis
|
||||
343
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go
generated
vendored
Normal file
343
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go
generated
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
// 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/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.
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
return analyzers
|
||||
}
|
||||
|
||||
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 as these have no effect on unitchecker
|
||||
// (as invoked by 'go vet').
|
||||
switch f.Name {
|
||||
case "debug", "cpuprofile", "memprofile", "trace":
|
||||
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.
|
||||
//
|
||||
// It is a variable not a function to permit easy
|
||||
// overriding in the copy vendored in $GOROOT/src/cmd/vet:
|
||||
//
|
||||
// func init() { addVersionFlag = objabi.AddVersionFlag }
|
||||
var addVersionFlag = func() {
|
||||
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 miminal 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 {
|
||||
data, _ := ioutil.ReadFile(posn.Filename)
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for i := posn.Line - Context; i <= posn.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
|
||||
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)
|
||||
}
|
||||
67
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags_test.go
generated
vendored
Normal file
67
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags_test.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/internal/analysisflags"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(analysisflags.Parse([]*analysis.Analyzer{
|
||||
{Name: "a1", Doc: "a1"},
|
||||
{Name: "a2", Doc: "a2"},
|
||||
{Name: "a3", Doc: "a3"},
|
||||
}, true))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// This test fork/execs the main function above.
|
||||
func TestExec(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skipf("skipping fork/exec test on this platform")
|
||||
}
|
||||
|
||||
progname := os.Args[0]
|
||||
|
||||
if os.Getenv("ANALYSISFLAGS_CHILD") == "1" {
|
||||
// child process
|
||||
os.Args = strings.Fields(progname + " " + os.Getenv("FLAGS"))
|
||||
main()
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
flags string
|
||||
want string
|
||||
}{
|
||||
{"", "[a1 a2 a3]"},
|
||||
{"-a1=0", "[a2 a3]"},
|
||||
{"-a1=1", "[a1]"},
|
||||
{"-a1", "[a1]"},
|
||||
{"-a1=1 -a3=1", "[a1 a3]"},
|
||||
{"-a1=1 -a3=0", "[a1]"},
|
||||
} {
|
||||
cmd := exec.Command(progname, "-test.run=TestExec")
|
||||
cmd.Env = append(os.Environ(), "ANALYSISFLAGS_CHILD=1", "FLAGS="+test.flags)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("exec failed: %v; output=<<%s>>", err, output)
|
||||
}
|
||||
|
||||
got := strings.TrimSpace(string(output))
|
||||
if got != test.want {
|
||||
t.Errorf("got %s, want %s", got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
89
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/help.go
generated
vendored
Normal file
89
vendor/golang.org/x/tools/go/analysis/internal/analysisflags/help.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package analysisflags
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"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 vet-lite
|
||||
// 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.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.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)
|
||||
}
|
||||
}
|
||||
701
vendor/golang.org/x/tools/go/analysis/internal/checker/checker.go
generated
vendored
Normal file
701
vendor/golang.org/x/tools/go/analysis/internal/checker/checker.go
generated
vendored
Normal file
@@ -0,0 +1,701 @@
|
||||
// Package checker defines the implementation of the checker commands.
|
||||
// The same code drives the multi-analysis driver, the single-analysis
|
||||
// driver that is conventionally provided for convenience along with
|
||||
// each analysis package, and the test driver.
|
||||
package checker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/internal/analysisflags"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
var (
|
||||
// Debug is a set of single-letter flags:
|
||||
//
|
||||
// f show [f]acts as they are created
|
||||
// p disable [p]arallel execution of analyzers
|
||||
// s do additional [s]anity checks on fact types and serialization
|
||||
// t show [t]iming info (NB: use 'p' flag to avoid GC/scheduler noise)
|
||||
// v show [v]erbose logging
|
||||
//
|
||||
Debug = ""
|
||||
|
||||
// Log files for optional performance tracing.
|
||||
CPUProfile, MemProfile, Trace string
|
||||
)
|
||||
|
||||
// RegisterFlags registers command-line flags used the analysis driver.
|
||||
func RegisterFlags() {
|
||||
// When adding flags here, remember to update
|
||||
// the list of suppressed flags in analysisflags.
|
||||
|
||||
flag.StringVar(&Debug, "debug", Debug, `debug flags, any subset of "lpsv"`)
|
||||
|
||||
flag.StringVar(&CPUProfile, "cpuprofile", "", "write CPU profile to this file")
|
||||
flag.StringVar(&MemProfile, "memprofile", "", "write memory profile to this file")
|
||||
flag.StringVar(&Trace, "trace", "", "write trace log to this file")
|
||||
}
|
||||
|
||||
// Run loads the packages specified by args using go/packages,
|
||||
// then applies the specified analyzers to them.
|
||||
// Analysis flags must already have been set.
|
||||
// It provides most of the logic for the main functions of both the
|
||||
// singlechecker and the multi-analysis commands.
|
||||
// It returns the appropriate exit code.
|
||||
func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
|
||||
if CPUProfile != "" {
|
||||
f, err := os.Create(CPUProfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// NB: profile won't be written in case of error.
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
if Trace != "" {
|
||||
f, err := os.Create(Trace)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := trace.Start(f); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// NB: trace log won't be written in case of error.
|
||||
defer func() {
|
||||
trace.Stop()
|
||||
log.Printf("To view the trace, run:\n$ go tool trace view %s", Trace)
|
||||
}()
|
||||
}
|
||||
|
||||
if MemProfile != "" {
|
||||
f, err := os.Create(MemProfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// NB: memprofile won't be written in case of error.
|
||||
defer func() {
|
||||
runtime.GC() // get up-to-date statistics
|
||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||
log.Fatalf("Writing memory profile: %v", err)
|
||||
}
|
||||
f.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
// Load the packages.
|
||||
if dbg('v') {
|
||||
log.SetPrefix("")
|
||||
log.SetFlags(log.Lmicroseconds) // display timing
|
||||
log.Printf("load %s", args)
|
||||
}
|
||||
|
||||
// Optimization: if the selected analyzers don't produce/consume
|
||||
// facts, we need source only for the initial packages.
|
||||
allSyntax := needFacts(analyzers)
|
||||
initial, err := load(args, allSyntax)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 1 // load errors
|
||||
}
|
||||
|
||||
// Print the results.
|
||||
roots := analyze(initial, analyzers)
|
||||
|
||||
return printDiagnostics(roots)
|
||||
}
|
||||
|
||||
// load loads the initial packages.
|
||||
func load(patterns []string, allSyntax bool) ([]*packages.Package, error) {
|
||||
mode := packages.LoadSyntax
|
||||
if allSyntax {
|
||||
mode = packages.LoadAllSyntax
|
||||
}
|
||||
conf := packages.Config{
|
||||
Mode: mode,
|
||||
Tests: true,
|
||||
}
|
||||
initial, err := packages.Load(&conf, patterns...)
|
||||
if err == nil {
|
||||
if n := packages.PrintErrors(initial); n > 1 {
|
||||
err = fmt.Errorf("%d errors during loading", n)
|
||||
} else if n == 1 {
|
||||
err = fmt.Errorf("error during loading")
|
||||
}
|
||||
}
|
||||
if len(initial) == 0 {
|
||||
err = fmt.Errorf("%s matched no packages", strings.Join(patterns, " "))
|
||||
}
|
||||
return initial, err
|
||||
}
|
||||
|
||||
// TestAnalyzer applies an analysis to a set of packages (and their
|
||||
// dependencies if necessary) and returns the results.
|
||||
//
|
||||
// Facts about pkg are returned in a map keyed by object; package facts
|
||||
// have a nil key.
|
||||
//
|
||||
// This entry point is used only by analysistest.
|
||||
func TestAnalyzer(a *analysis.Analyzer, pkgs []*packages.Package) []*TestAnalyzerResult {
|
||||
var results []*TestAnalyzerResult
|
||||
for _, act := range analyze(pkgs, []*analysis.Analyzer{a}) {
|
||||
facts := make(map[types.Object][]analysis.Fact)
|
||||
for key, fact := range act.objectFacts {
|
||||
if key.obj.Pkg() == act.pass.Pkg {
|
||||
facts[key.obj] = append(facts[key.obj], fact)
|
||||
}
|
||||
}
|
||||
for key, fact := range act.packageFacts {
|
||||
if key.pkg == act.pass.Pkg {
|
||||
facts[nil] = append(facts[nil], fact)
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, &TestAnalyzerResult{act.pass, act.diagnostics, facts, act.result, act.err})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
type TestAnalyzerResult struct {
|
||||
Pass *analysis.Pass
|
||||
Diagnostics []analysis.Diagnostic
|
||||
Facts map[types.Object][]analysis.Fact
|
||||
Result interface{}
|
||||
Err error
|
||||
}
|
||||
|
||||
func analyze(pkgs []*packages.Package, analyzers []*analysis.Analyzer) []*action {
|
||||
// Construct the action graph.
|
||||
if dbg('v') {
|
||||
log.Printf("building graph of analysis passes")
|
||||
}
|
||||
|
||||
// Each graph node (action) is one unit of analysis.
|
||||
// Edges express package-to-package (vertical) dependencies,
|
||||
// and analysis-to-analysis (horizontal) dependencies.
|
||||
type key struct {
|
||||
*analysis.Analyzer
|
||||
*packages.Package
|
||||
}
|
||||
actions := make(map[key]*action)
|
||||
|
||||
var mkAction func(a *analysis.Analyzer, pkg *packages.Package) *action
|
||||
mkAction = func(a *analysis.Analyzer, pkg *packages.Package) *action {
|
||||
k := key{a, pkg}
|
||||
act, ok := actions[k]
|
||||
if !ok {
|
||||
act = &action{a: a, pkg: pkg}
|
||||
|
||||
// Add a dependency on each required analyzers.
|
||||
for _, req := range a.Requires {
|
||||
act.deps = append(act.deps, mkAction(req, pkg))
|
||||
}
|
||||
|
||||
// An analysis that consumes/produces facts
|
||||
// must run on the package's dependencies too.
|
||||
if len(a.FactTypes) > 0 {
|
||||
paths := make([]string, 0, len(pkg.Imports))
|
||||
for path := range pkg.Imports {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
sort.Strings(paths) // for determinism
|
||||
for _, path := range paths {
|
||||
dep := mkAction(a, pkg.Imports[path])
|
||||
act.deps = append(act.deps, dep)
|
||||
}
|
||||
}
|
||||
|
||||
actions[k] = act
|
||||
}
|
||||
return act
|
||||
}
|
||||
|
||||
// Build nodes for initial packages.
|
||||
var roots []*action
|
||||
for _, a := range analyzers {
|
||||
for _, pkg := range pkgs {
|
||||
root := mkAction(a, pkg)
|
||||
root.isroot = true
|
||||
roots = append(roots, root)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the graph in parallel.
|
||||
execAll(roots)
|
||||
|
||||
return roots
|
||||
}
|
||||
|
||||
// printDiagnostics prints the diagnostics for the root packages in either
|
||||
// plain text or JSON format. JSON format also includes errors for any
|
||||
// dependencies.
|
||||
//
|
||||
// It returns the exitcode: in plain mode, 0 for success, 1 for analysis
|
||||
// errors, and 3 for diagnostics. We avoid 2 since the flag package uses
|
||||
// it. JSON mode always succeeds at printing errors and diagnostics in a
|
||||
// structured form to stdout.
|
||||
func printDiagnostics(roots []*action) (exitcode int) {
|
||||
// Print the output.
|
||||
//
|
||||
// Print diagnostics only for root packages,
|
||||
// but errors for all packages.
|
||||
printed := make(map[*action]bool)
|
||||
var print func(*action)
|
||||
var visitAll func(actions []*action)
|
||||
visitAll = func(actions []*action) {
|
||||
for _, act := range actions {
|
||||
if !printed[act] {
|
||||
printed[act] = true
|
||||
visitAll(act.deps)
|
||||
print(act)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if analysisflags.JSON {
|
||||
// JSON output
|
||||
tree := make(analysisflags.JSONTree)
|
||||
print = func(act *action) {
|
||||
var diags []analysis.Diagnostic
|
||||
if act.isroot {
|
||||
diags = act.diagnostics
|
||||
}
|
||||
tree.Add(act.pkg.Fset, act.pkg.ID, act.a.Name, diags, act.err)
|
||||
}
|
||||
visitAll(roots)
|
||||
tree.Print()
|
||||
} else {
|
||||
// plain text output
|
||||
|
||||
// De-duplicate diagnostics by position (not token.Pos) to
|
||||
// avoid double-reporting in source files that belong to
|
||||
// multiple packages, such as foo and foo.test.
|
||||
type key struct {
|
||||
token.Position
|
||||
*analysis.Analyzer
|
||||
message string
|
||||
}
|
||||
seen := make(map[key]bool)
|
||||
|
||||
print = func(act *action) {
|
||||
if act.err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", act.a.Name, act.err)
|
||||
exitcode = 1 // analysis failed, at least partially
|
||||
return
|
||||
}
|
||||
if act.isroot {
|
||||
for _, diag := range act.diagnostics {
|
||||
// We don't display a.Name/f.Category
|
||||
// as most users don't care.
|
||||
|
||||
posn := act.pkg.Fset.Position(diag.Pos)
|
||||
k := key{posn, act.a, diag.Message}
|
||||
if seen[k] {
|
||||
continue // duplicate
|
||||
}
|
||||
seen[k] = true
|
||||
|
||||
analysisflags.PrintPlain(act.pkg.Fset, diag)
|
||||
}
|
||||
}
|
||||
}
|
||||
visitAll(roots)
|
||||
|
||||
if exitcode == 0 && len(seen) > 0 {
|
||||
exitcode = 3 // successfuly produced diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
// Print timing info.
|
||||
if dbg('t') {
|
||||
if !dbg('p') {
|
||||
log.Println("Warning: times are mostly GC/scheduler noise; use -debug=tp to disable parallelism")
|
||||
}
|
||||
var all []*action
|
||||
var total time.Duration
|
||||
for act := range printed {
|
||||
all = append(all, act)
|
||||
total += act.duration
|
||||
}
|
||||
sort.Slice(all, func(i, j int) bool {
|
||||
return all[i].duration > all[j].duration
|
||||
})
|
||||
|
||||
// Print actions accounting for 90% of the total.
|
||||
var sum time.Duration
|
||||
for _, act := range all {
|
||||
fmt.Fprintf(os.Stderr, "%s\t%s\n", act.duration, act)
|
||||
sum += act.duration
|
||||
if sum >= total*9/10 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exitcode
|
||||
}
|
||||
|
||||
// needFacts reports whether any analysis required by the specified set
|
||||
// needs facts. If so, we must load the entire program from source.
|
||||
func needFacts(analyzers []*analysis.Analyzer) bool {
|
||||
seen := make(map[*analysis.Analyzer]bool)
|
||||
var q []*analysis.Analyzer // for BFS
|
||||
q = append(q, analyzers...)
|
||||
for len(q) > 0 {
|
||||
a := q[0]
|
||||
q = q[1:]
|
||||
if !seen[a] {
|
||||
seen[a] = true
|
||||
if len(a.FactTypes) > 0 {
|
||||
return true
|
||||
}
|
||||
q = append(q, a.Requires...)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// An action represents one unit of analysis work: the application of
|
||||
// one analysis to one package. Actions form a DAG, both within a
|
||||
// package (as different analyzers are applied, either in sequence or
|
||||
// parallel), and across packages (as dependencies are analyzed).
|
||||
type action struct {
|
||||
once sync.Once
|
||||
a *analysis.Analyzer
|
||||
pkg *packages.Package
|
||||
pass *analysis.Pass
|
||||
isroot bool
|
||||
deps []*action
|
||||
objectFacts map[objectFactKey]analysis.Fact
|
||||
packageFacts map[packageFactKey]analysis.Fact
|
||||
inputs map[*analysis.Analyzer]interface{}
|
||||
result interface{}
|
||||
diagnostics []analysis.Diagnostic
|
||||
err error
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
type objectFactKey struct {
|
||||
obj types.Object
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
type packageFactKey struct {
|
||||
pkg *types.Package
|
||||
typ reflect.Type
|
||||
}
|
||||
|
||||
func (act *action) String() string {
|
||||
return fmt.Sprintf("%s@%s", act.a, act.pkg)
|
||||
}
|
||||
|
||||
func execAll(actions []*action) {
|
||||
sequential := dbg('p')
|
||||
var wg sync.WaitGroup
|
||||
for _, act := range actions {
|
||||
wg.Add(1)
|
||||
work := func(act *action) {
|
||||
act.exec()
|
||||
wg.Done()
|
||||
}
|
||||
if sequential {
|
||||
work(act)
|
||||
} else {
|
||||
go work(act)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (act *action) exec() { act.once.Do(act.execOnce) }
|
||||
|
||||
func (act *action) execOnce() {
|
||||
// Analyze dependencies.
|
||||
execAll(act.deps)
|
||||
|
||||
// TODO(adonovan): uncomment this during profiling.
|
||||
// It won't build pre-go1.11 but conditional compilation
|
||||
// using build tags isn't warranted.
|
||||
//
|
||||
// ctx, task := trace.NewTask(context.Background(), "exec")
|
||||
// trace.Log(ctx, "pass", act.String())
|
||||
// defer task.End()
|
||||
|
||||
// Record time spent in this node but not its dependencies.
|
||||
// In parallel mode, due to GC/scheduler contention, the
|
||||
// time is 5x higher than in sequential mode, even with a
|
||||
// semaphore limiting the number of threads here.
|
||||
// So use -debug=tp.
|
||||
if dbg('t') {
|
||||
t0 := time.Now()
|
||||
defer func() { act.duration = time.Since(t0) }()
|
||||
}
|
||||
|
||||
// Report an error if any dependency failed.
|
||||
var failed []string
|
||||
for _, dep := range act.deps {
|
||||
if dep.err != nil {
|
||||
failed = append(failed, dep.String())
|
||||
}
|
||||
}
|
||||
if failed != nil {
|
||||
sort.Strings(failed)
|
||||
act.err = fmt.Errorf("failed prerequisites: %s", strings.Join(failed, ", "))
|
||||
return
|
||||
}
|
||||
|
||||
// Plumb the output values of the dependencies
|
||||
// into the inputs of this action. Also facts.
|
||||
inputs := make(map[*analysis.Analyzer]interface{})
|
||||
act.objectFacts = make(map[objectFactKey]analysis.Fact)
|
||||
act.packageFacts = make(map[packageFactKey]analysis.Fact)
|
||||
for _, dep := range act.deps {
|
||||
if dep.pkg == act.pkg {
|
||||
// Same package, different analysis (horizontal edge):
|
||||
// in-memory outputs of prerequisite analyzers
|
||||
// become inputs to this analysis pass.
|
||||
inputs[dep.a] = dep.result
|
||||
|
||||
} else if dep.a == act.a { // (always true)
|
||||
// Same analysis, different package (vertical edge):
|
||||
// serialized facts produced by prerequisite analysis
|
||||
// become available to this analysis pass.
|
||||
inheritFacts(act, dep)
|
||||
}
|
||||
}
|
||||
|
||||
// Run the analysis.
|
||||
pass := &analysis.Pass{
|
||||
Analyzer: act.a,
|
||||
Fset: act.pkg.Fset,
|
||||
Files: act.pkg.Syntax,
|
||||
OtherFiles: act.pkg.OtherFiles,
|
||||
Pkg: act.pkg.Types,
|
||||
TypesInfo: act.pkg.TypesInfo,
|
||||
ResultOf: inputs,
|
||||
Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
|
||||
ImportObjectFact: act.importObjectFact,
|
||||
ExportObjectFact: act.exportObjectFact,
|
||||
ImportPackageFact: act.importPackageFact,
|
||||
ExportPackageFact: act.exportPackageFact,
|
||||
}
|
||||
act.pass = pass
|
||||
|
||||
var err error
|
||||
if act.pkg.IllTyped && !pass.Analyzer.RunDespiteErrors {
|
||||
err = fmt.Errorf("analysis skipped due to errors in package")
|
||||
} else {
|
||||
act.result, err = pass.Analyzer.Run(pass)
|
||||
if err == nil {
|
||||
if got, want := reflect.TypeOf(act.result), pass.Analyzer.ResultType; got != want {
|
||||
err = fmt.Errorf(
|
||||
"internal error: on package %s, analyzer %s returned a result of type %v, but declared ResultType %v",
|
||||
pass.Pkg.Path(), pass.Analyzer, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
act.err = err
|
||||
|
||||
// disallow calls after Run
|
||||
pass.ExportObjectFact = nil
|
||||
pass.ExportPackageFact = nil
|
||||
}
|
||||
|
||||
// inheritFacts populates act.facts with
|
||||
// those it obtains from its dependency, dep.
|
||||
func inheritFacts(act, dep *action) {
|
||||
serialize := dbg('s')
|
||||
|
||||
for key, fact := range dep.objectFacts {
|
||||
// Filter out facts related to objects
|
||||
// that are irrelevant downstream
|
||||
// (equivalently: not in the compiler export data).
|
||||
if !exportedFrom(key.obj, dep.pkg.Types) {
|
||||
if false {
|
||||
log.Printf("%v: discarding %T fact from %s for %s: %s", act, fact, dep, key.obj, fact)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Optionally serialize/deserialize fact
|
||||
// to verify that it works across address spaces.
|
||||
if serialize {
|
||||
var err error
|
||||
fact, err = codeFact(fact)
|
||||
if err != nil {
|
||||
log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
|
||||
}
|
||||
}
|
||||
|
||||
if false {
|
||||
log.Printf("%v: inherited %T fact for %s: %s", act, fact, key.obj, fact)
|
||||
}
|
||||
act.objectFacts[key] = fact
|
||||
}
|
||||
|
||||
for key, fact := range dep.packageFacts {
|
||||
// TODO: filter out facts that belong to
|
||||
// packages not mentioned in the export data
|
||||
// to prevent side channels.
|
||||
|
||||
// Optionally serialize/deserialize fact
|
||||
// to verify that it works across address spaces
|
||||
// and is deterministic.
|
||||
if serialize {
|
||||
var err error
|
||||
fact, err = codeFact(fact)
|
||||
if err != nil {
|
||||
log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
|
||||
}
|
||||
}
|
||||
|
||||
if false {
|
||||
log.Printf("%v: inherited %T fact for %s: %s", act, fact, key.pkg.Path(), fact)
|
||||
}
|
||||
act.packageFacts[key] = fact
|
||||
}
|
||||
}
|
||||
|
||||
// codeFact encodes then decodes a fact,
|
||||
// just to exercise that logic.
|
||||
func codeFact(fact analysis.Fact) (analysis.Fact, error) {
|
||||
// We encode facts one at a time.
|
||||
// A real modular driver would emit all facts
|
||||
// into one encoder to improve gob efficiency.
|
||||
var buf bytes.Buffer
|
||||
if err := gob.NewEncoder(&buf).Encode(fact); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Encode it twice and assert that we get the same bits.
|
||||
// This helps detect nondeterministic Gob encoding (e.g. of maps).
|
||||
var buf2 bytes.Buffer
|
||||
if err := gob.NewEncoder(&buf2).Encode(fact); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !bytes.Equal(buf.Bytes(), buf2.Bytes()) {
|
||||
return nil, fmt.Errorf("encoding of %T fact is nondeterministic", fact)
|
||||
}
|
||||
|
||||
new := reflect.New(reflect.TypeOf(fact).Elem()).Interface().(analysis.Fact)
|
||||
if err := gob.NewDecoder(&buf).Decode(new); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return new, nil
|
||||
}
|
||||
|
||||
// exportedFrom reports whether obj may be visible to a package that imports pkg.
|
||||
// This includes not just the exported members of pkg, but also unexported
|
||||
// constants, types, fields, and methods, perhaps belonging to oether packages,
|
||||
// that find there way into the API.
|
||||
// This is an overapproximation of the more accurate approach used by
|
||||
// gc export data, which walks the type graph, but it's much simpler.
|
||||
//
|
||||
// TODO(adonovan): do more accurate filtering by walking the type graph.
|
||||
func exportedFrom(obj types.Object, pkg *types.Package) bool {
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
return obj.Exported() && obj.Pkg() == pkg ||
|
||||
obj.Type().(*types.Signature).Recv() != nil
|
||||
case *types.Var:
|
||||
return obj.Exported() && obj.Pkg() == pkg ||
|
||||
obj.IsField()
|
||||
case *types.TypeName, *types.Const:
|
||||
return true
|
||||
}
|
||||
return false // Nil, Builtin, Label, or PkgName
|
||||
}
|
||||
|
||||
// importObjectFact implements Pass.ImportObjectFact.
|
||||
// Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
|
||||
// importObjectFact copies the fact value to *ptr.
|
||||
func (act *action) importObjectFact(obj types.Object, ptr analysis.Fact) bool {
|
||||
if obj == nil {
|
||||
panic("nil object")
|
||||
}
|
||||
key := objectFactKey{obj, factType(ptr)}
|
||||
if v, ok := act.objectFacts[key]; ok {
|
||||
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// exportObjectFact implements Pass.ExportObjectFact.
|
||||
func (act *action) exportObjectFact(obj types.Object, fact analysis.Fact) {
|
||||
if act.pass.ExportObjectFact == nil {
|
||||
log.Panicf("%s: Pass.ExportObjectFact(%s, %T) called after Run", act, obj, fact)
|
||||
}
|
||||
|
||||
if obj.Pkg() != act.pkg.Types {
|
||||
log.Panicf("internal error: in analysis %s of package %s: Fact.Set(%s, %T): can't set facts on objects belonging another package",
|
||||
act.a, act.pkg, obj, fact)
|
||||
}
|
||||
|
||||
key := objectFactKey{obj, factType(fact)}
|
||||
act.objectFacts[key] = fact // clobber any existing entry
|
||||
if dbg('f') {
|
||||
objstr := types.ObjectString(obj, (*types.Package).Name)
|
||||
fmt.Fprintf(os.Stderr, "%s: object %s has fact %s\n",
|
||||
act.pkg.Fset.Position(obj.Pos()), objstr, fact)
|
||||
}
|
||||
}
|
||||
|
||||
// importPackageFact implements Pass.ImportPackageFact.
|
||||
// Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
|
||||
// fact copies the fact value to *ptr.
|
||||
func (act *action) importPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
|
||||
if pkg == nil {
|
||||
panic("nil package")
|
||||
}
|
||||
key := packageFactKey{pkg, factType(ptr)}
|
||||
if v, ok := act.packageFacts[key]; ok {
|
||||
reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// exportPackageFact implements Pass.ExportPackageFact.
|
||||
func (act *action) exportPackageFact(fact analysis.Fact) {
|
||||
if act.pass.ExportPackageFact == nil {
|
||||
log.Panicf("%s: Pass.ExportPackageFact(%T) called after Run", act, fact)
|
||||
}
|
||||
|
||||
key := packageFactKey{act.pass.Pkg, factType(fact)}
|
||||
act.packageFacts[key] = fact // clobber any existing entry
|
||||
if dbg('f') {
|
||||
fmt.Fprintf(os.Stderr, "%s: package %s has fact %s\n",
|
||||
act.pkg.Fset.Position(act.pass.Files[0].Pos()), act.pass.Pkg.Path(), fact)
|
||||
}
|
||||
}
|
||||
|
||||
func factType(fact analysis.Fact) reflect.Type {
|
||||
t := reflect.TypeOf(fact)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
log.Fatalf("invalid Fact type: got %T, want pointer", t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func dbg(b byte) bool { return strings.IndexByte(Debug, b) >= 0 }
|
||||
299
vendor/golang.org/x/tools/go/analysis/internal/facts/facts.go
generated
vendored
Normal file
299
vendor/golang.org/x/tools/go/analysis/internal/facts/facts.go
generated
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
// 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 nonethless 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()
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
174
vendor/golang.org/x/tools/go/analysis/internal/facts/facts_test.go
generated
vendored
Normal file
174
vendor/golang.org/x/tools/go/analysis/internal/facts/facts_test.go
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
// 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_test
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/internal/facts"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
type myFact struct {
|
||||
S string
|
||||
}
|
||||
|
||||
func (f *myFact) String() string { return fmt.Sprintf("myFact(%s)", f.S) }
|
||||
func (f *myFact) AFact() {}
|
||||
|
||||
func TestEncodeDecode(t *testing.T) {
|
||||
gob.Register(new(myFact))
|
||||
|
||||
// c -> b -> a, a2
|
||||
// c does not directly depend on a, but it indirectly uses a.T.
|
||||
//
|
||||
// Package a2 is never loaded directly so it is incomplete.
|
||||
//
|
||||
// We use only types in this example because we rely on
|
||||
// types.Eval to resolve the lookup expressions, and it only
|
||||
// works for types. This is a definite gap in the typechecker API.
|
||||
files := map[string]string{
|
||||
"a/a.go": `package a; type A int; type T int`,
|
||||
"a2/a.go": `package a2; type A2 int; type Unneeded int`,
|
||||
"b/b.go": `package b; import ("a"; "a2"); type B chan a2.A2; type F func() a.T`,
|
||||
"c/c.go": `package c; import "b"; type C []b.B`,
|
||||
}
|
||||
dir, cleanup, err := analysistest.WriteFiles(files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// factmap represents the passing of encoded facts from one
|
||||
// package to another. In practice one would use the file system.
|
||||
factmap := make(map[string][]byte)
|
||||
read := func(path string) ([]byte, error) { return factmap[path], nil }
|
||||
|
||||
// In the following table, we analyze packages (a, b, c) in order,
|
||||
// look up various objects accessible within each package,
|
||||
// and see if they have a fact. The "analysis" exports a fact
|
||||
// for every object at package level.
|
||||
//
|
||||
// Note: Loop iterations are not independent test cases;
|
||||
// order matters, as we populate factmap.
|
||||
type lookups []struct {
|
||||
objexpr string
|
||||
want string
|
||||
}
|
||||
for _, test := range []struct {
|
||||
path string
|
||||
lookups lookups
|
||||
}{
|
||||
{"a", lookups{
|
||||
{"A", "myFact(a.A)"},
|
||||
}},
|
||||
{"b", lookups{
|
||||
{"a.A", "myFact(a.A)"},
|
||||
{"a.T", "myFact(a.T)"},
|
||||
{"B", "myFact(b.B)"},
|
||||
{"F", "myFact(b.F)"},
|
||||
{"F(nil)()", "myFact(a.T)"}, // (result type of b.F)
|
||||
}},
|
||||
{"c", lookups{
|
||||
{"b.B", "myFact(b.B)"},
|
||||
{"b.F", "myFact(b.F)"},
|
||||
//{"b.F(nil)()", "myFact(a.T)"}, // no fact; TODO(adonovan): investigate
|
||||
{"C", "myFact(c.C)"},
|
||||
{"C{}[0]", "myFact(b.B)"},
|
||||
{"<-(C{}[0])", "no fact"}, // object but no fact (we never "analyze" a2)
|
||||
}},
|
||||
} {
|
||||
// load package
|
||||
pkg, err := load(dir, test.path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// decode
|
||||
facts, err := facts.Decode(pkg, read)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode failed: %v", err)
|
||||
}
|
||||
if true {
|
||||
t.Logf("decode %s facts = %v", pkg.Path(), facts) // show all facts
|
||||
}
|
||||
|
||||
// export
|
||||
// (one fact for each package-level object)
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
obj := scope.Lookup(name)
|
||||
fact := &myFact{obj.Pkg().Name() + "." + obj.Name()}
|
||||
facts.ExportObjectFact(obj, fact)
|
||||
}
|
||||
|
||||
// import
|
||||
// (after export, because an analyzer may import its own facts)
|
||||
for _, lookup := range test.lookups {
|
||||
fact := new(myFact)
|
||||
var got string
|
||||
if obj := find(pkg, lookup.objexpr); obj == nil {
|
||||
got = "no object"
|
||||
} else if facts.ImportObjectFact(obj, fact) {
|
||||
got = fact.String()
|
||||
} else {
|
||||
got = "no fact"
|
||||
}
|
||||
if got != lookup.want {
|
||||
t.Errorf("in %s, ImportObjectFact(%s, %T) = %s, want %s",
|
||||
pkg.Path(), lookup.objexpr, fact, got, lookup.want)
|
||||
}
|
||||
}
|
||||
|
||||
// encode
|
||||
factmap[pkg.Path()] = facts.Encode()
|
||||
}
|
||||
}
|
||||
|
||||
func find(p *types.Package, expr string) types.Object {
|
||||
// types.Eval only allows us to compute a TypeName object for an expression.
|
||||
// TODO(adonovan): support other expressions that denote an object:
|
||||
// - an identifier (or qualified ident) for a func, const, or var
|
||||
// - new(T).f for a field or method
|
||||
// I've added CheckExpr in https://go-review.googlesource.com/c/go/+/144677.
|
||||
// If that becomes available, use it.
|
||||
|
||||
// Choose an arbitrary position within the (single-file) package
|
||||
// so that we are within the scope of its import declarations.
|
||||
somepos := p.Scope().Lookup(p.Scope().Names()[0]).Pos()
|
||||
tv, err := types.Eval(token.NewFileSet(), p, somepos, expr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if n, ok := tv.Type.(*types.Named); ok {
|
||||
return n.Obj()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func load(dir string, path string) (*types.Package, error) {
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.LoadSyntax,
|
||||
Dir: dir,
|
||||
Env: append(os.Environ(), "GOPATH="+dir, "GO111MODULE=off", "GOPROXY=off"),
|
||||
}
|
||||
pkgs, err := packages.Load(cfg, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if packages.PrintErrors(pkgs) > 0 {
|
||||
return nil, fmt.Errorf("packages had errors")
|
||||
}
|
||||
if len(pkgs) == 0 {
|
||||
return nil, fmt.Errorf("no package matched %s", path)
|
||||
}
|
||||
return pkgs[0].Types, nil
|
||||
}
|
||||
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
|
||||
}
|
||||
60
vendor/golang.org/x/tools/go/analysis/multichecker/multichecker.go
generated
vendored
Normal file
60
vendor/golang.org/x/tools/go/analysis/multichecker/multichecker.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// 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 multichecker defines the main function for an analysis driver
|
||||
// with several analyzers. This package makes it easy for anyone to build
|
||||
// an analysis tool containing just the analyzers they need.
|
||||
package multichecker
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/internal/analysisflags"
|
||||
"golang.org/x/tools/go/analysis/internal/checker"
|
||||
"golang.org/x/tools/go/analysis/unitchecker"
|
||||
)
|
||||
|
||||
func Main(analyzers ...*analysis.Analyzer) {
|
||||
progname := filepath.Base(os.Args[0])
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix(progname + ": ") // e.g. "vet: "
|
||||
|
||||
if err := analysis.Validate(analyzers); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
checker.RegisterFlags()
|
||||
|
||||
analyzers = analysisflags.Parse(analyzers, true)
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
fmt.Fprintf(os.Stderr, `%[1]s is a tool for static analysis of Go programs.
|
||||
|
||||
Usage: %[1]s [-flag] [package]
|
||||
|
||||
Run '%[1]s help' for more detail,
|
||||
or '%[1]s help name' for details and flags of a specific analyzer.
|
||||
`, progname)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if args[0] == "help" {
|
||||
analysisflags.Help(progname, analyzers, args[1:])
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if len(args) == 1 && strings.HasSuffix(args[0], ".cfg") {
|
||||
unitchecker.Run(args[0], analyzers)
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
os.Exit(checker.Run(args, analyzers))
|
||||
}
|
||||
82
vendor/golang.org/x/tools/go/analysis/multichecker/multichecker_test.go
generated
vendored
Normal file
82
vendor/golang.org/x/tools/go/analysis/multichecker/multichecker_test.go
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// +build go1.12
|
||||
|
||||
package multichecker_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/multichecker"
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fail := &analysis.Analyzer{
|
||||
Name: "fail",
|
||||
Doc: "always fail on a package 'sort'",
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
if pass.Pkg.Path() == "sort" {
|
||||
return nil, fmt.Errorf("failed")
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
multichecker.Main(findcall.Analyzer, fail)
|
||||
}
|
||||
|
||||
// TestExitCode ensures that analysis failures are reported correctly.
|
||||
// This test fork/execs the main function above.
|
||||
func TestExitCode(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skipf("skipping fork/exec test on this platform")
|
||||
}
|
||||
|
||||
if os.Getenv("MULTICHECKER_CHILD") == "1" {
|
||||
// child process
|
||||
|
||||
// replace [progname -test.run=TestExitCode -- ...]
|
||||
// by [progname ...]
|
||||
os.Args = os.Args[2:]
|
||||
os.Args[0] = "vet"
|
||||
main()
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
args []string
|
||||
want int
|
||||
}{
|
||||
{[]string{"nosuchdir/..."}, 1}, // matched no packages
|
||||
{[]string{"nosuchpkg"}, 1}, // matched no packages
|
||||
{[]string{"-unknownflag"}, 2}, // flag error
|
||||
{[]string{"-findcall.name=panic", "io"}, 3}, // finds diagnostics
|
||||
{[]string{"-findcall=0", "io"}, 0}, // no checkers
|
||||
{[]string{"-findcall.name=nosuchfunc", "io"}, 0}, // no diagnostics
|
||||
{[]string{"-findcall.name=panic", "sort", "io"}, 1}, // 'fail' failed on 'sort'
|
||||
|
||||
// -json: exits zero even in face of diagnostics or package errors.
|
||||
{[]string{"-findcall.name=panic", "-json", "io"}, 0},
|
||||
{[]string{"-findcall.name=panic", "-json", "io"}, 0},
|
||||
{[]string{"-findcall.name=panic", "-json", "sort", "io"}, 0},
|
||||
} {
|
||||
args := []string{"-test.run=TestExitCode", "--"}
|
||||
args = append(args, test.args...)
|
||||
cmd := exec.Command(os.Args[0], args...)
|
||||
cmd.Env = append(os.Environ(), "MULTICHECKER_CHILD=1")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s: out=<<%s>>", test.args, out)
|
||||
}
|
||||
var exitcode int
|
||||
if err, ok := err.(*exec.ExitError); ok {
|
||||
exitcode = err.ExitCode() // requires go1.12
|
||||
}
|
||||
if exitcode != test.want {
|
||||
t.Errorf("%s: exited %d, want %d", test.args, exitcode, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
8
vendor/golang.org/x/tools/go/analysis/passes/README
generated
vendored
Normal file
8
vendor/golang.org/x/tools/go/analysis/passes/README
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
This directory does not contain a Go package,
|
||||
but acts as a container for various analyses
|
||||
that implement the golang.org/x/tools/go/analysis
|
||||
API and may be imported into an analysis tool.
|
||||
|
||||
By convention, each package foo provides the analysis,
|
||||
and each command foo/cmd/foo provides a standalone driver.
|
||||
760
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
generated
vendored
Normal file
760
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
generated
vendored
Normal file
@@ -0,0 +1,760 @@
|
||||
// Copyright 2013 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 asmdecl defines an Analyzer that reports mismatches between
|
||||
// assembly files and Go declarations.
|
||||
package asmdecl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "asmdecl",
|
||||
Doc: "report mismatches between assembly files and Go declarations",
|
||||
Run: run,
|
||||
}
|
||||
|
||||
// 'kind' is a kind of assembly variable.
|
||||
// The kinds 1, 2, 4, 8 stand for values of that size.
|
||||
type asmKind int
|
||||
|
||||
// These special kinds are not valid sizes.
|
||||
const (
|
||||
asmString asmKind = 100 + iota
|
||||
asmSlice
|
||||
asmArray
|
||||
asmInterface
|
||||
asmEmptyInterface
|
||||
asmStruct
|
||||
asmComplex
|
||||
)
|
||||
|
||||
// An asmArch describes assembly parameters for an architecture
|
||||
type asmArch struct {
|
||||
name string
|
||||
bigEndian bool
|
||||
stack string
|
||||
lr bool
|
||||
// calculated during initialization
|
||||
sizes types.Sizes
|
||||
intSize int
|
||||
ptrSize int
|
||||
maxAlign int
|
||||
}
|
||||
|
||||
// An asmFunc describes the expected variables for a function on a given architecture.
|
||||
type asmFunc struct {
|
||||
arch *asmArch
|
||||
size int // size of all arguments
|
||||
vars map[string]*asmVar
|
||||
varByOffset map[int]*asmVar
|
||||
}
|
||||
|
||||
// An asmVar describes a single assembly variable.
|
||||
type asmVar struct {
|
||||
name string
|
||||
kind asmKind
|
||||
typ string
|
||||
off int
|
||||
size int
|
||||
inner []*asmVar
|
||||
}
|
||||
|
||||
var (
|
||||
asmArch386 = asmArch{name: "386", bigEndian: false, stack: "SP", lr: false}
|
||||
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
|
||||
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
|
||||
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
|
||||
asmArchAmd64p32 = asmArch{name: "amd64p32", bigEndian: false, stack: "SP", lr: false}
|
||||
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
|
||||
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
|
||||
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
|
||||
asmArchMips64LE = asmArch{name: "mips64le", bigEndian: false, stack: "R29", lr: true}
|
||||
asmArchPpc64 = asmArch{name: "ppc64", bigEndian: true, stack: "R1", lr: true}
|
||||
asmArchPpc64LE = asmArch{name: "ppc64le", bigEndian: false, stack: "R1", lr: true}
|
||||
asmArchS390X = asmArch{name: "s390x", bigEndian: true, stack: "R15", lr: true}
|
||||
asmArchWasm = asmArch{name: "wasm", bigEndian: false, stack: "SP", lr: false}
|
||||
|
||||
arches = []*asmArch{
|
||||
&asmArch386,
|
||||
&asmArchArm,
|
||||
&asmArchArm64,
|
||||
&asmArchAmd64,
|
||||
&asmArchAmd64p32,
|
||||
&asmArchMips,
|
||||
&asmArchMipsLE,
|
||||
&asmArchMips64,
|
||||
&asmArchMips64LE,
|
||||
&asmArchPpc64,
|
||||
&asmArchPpc64LE,
|
||||
&asmArchS390X,
|
||||
&asmArchWasm,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for _, arch := range arches {
|
||||
arch.sizes = types.SizesFor("gc", arch.name)
|
||||
if arch.sizes == nil {
|
||||
// TODO(adonovan): fix: now that asmdecl is not in the standard
|
||||
// library we cannot assume types.SizesFor is consistent with arches.
|
||||
// For now, assume 64-bit norms and print a warning.
|
||||
// But this warning should really be deferred until we attempt to use
|
||||
// arch, which is very unlikely.
|
||||
arch.sizes = types.SizesFor("gc", "amd64")
|
||||
log.Printf("unknown architecture %s", arch.name)
|
||||
}
|
||||
arch.intSize = int(arch.sizes.Sizeof(types.Typ[types.Int]))
|
||||
arch.ptrSize = int(arch.sizes.Sizeof(types.Typ[types.UnsafePointer]))
|
||||
arch.maxAlign = int(arch.sizes.Alignof(types.Typ[types.Int64]))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
re = regexp.MustCompile
|
||||
asmPlusBuild = re(`//\s+\+build\s+([^\n]+)`)
|
||||
asmTEXT = re(`\bTEXT\b(.*)·([^\(]+)\(SB\)(?:\s*,\s*([0-9A-Z|+()]+))?(?:\s*,\s*\$(-?[0-9]+)(?:-([0-9]+))?)?`)
|
||||
asmDATA = re(`\b(DATA|GLOBL)\b`)
|
||||
asmNamedFP = re(`([a-zA-Z0-9_\xFF-\x{10FFFF}]+)(?:\+([0-9]+))\(FP\)`)
|
||||
asmUnnamedFP = re(`[^+\-0-9](([0-9]+)\(FP\))`)
|
||||
asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
|
||||
asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
|
||||
ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
|
||||
)
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// No work if no assembly files.
|
||||
var sfiles []string
|
||||
for _, fname := range pass.OtherFiles {
|
||||
if strings.HasSuffix(fname, ".s") {
|
||||
sfiles = append(sfiles, fname)
|
||||
}
|
||||
}
|
||||
if sfiles == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Gather declarations. knownFunc[name][arch] is func description.
|
||||
knownFunc := make(map[string]map[string]*asmFunc)
|
||||
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body == nil {
|
||||
knownFunc[decl.Name.Name] = asmParseDecl(pass, decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Files:
|
||||
for _, fname := range sfiles {
|
||||
content, tf, err := analysisutil.ReadFile(pass.Fset, fname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Determine architecture from file name if possible.
|
||||
var arch string
|
||||
var archDef *asmArch
|
||||
for _, a := range arches {
|
||||
if strings.HasSuffix(fname, "_"+a.name+".s") {
|
||||
arch = a.name
|
||||
archDef = a
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lines := strings.SplitAfter(string(content), "\n")
|
||||
var (
|
||||
fn *asmFunc
|
||||
fnName string
|
||||
localSize, argSize int
|
||||
wroteSP bool
|
||||
haveRetArg bool
|
||||
retLine []int
|
||||
)
|
||||
|
||||
flushRet := func() {
|
||||
if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
|
||||
v := fn.vars["ret"]
|
||||
for _, line := range retLine {
|
||||
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %d-byte ret+%d(FP)", arch, fnName, v.size, v.off)
|
||||
}
|
||||
}
|
||||
retLine = nil
|
||||
}
|
||||
for lineno, line := range lines {
|
||||
lineno++
|
||||
|
||||
badf := func(format string, args ...interface{}) {
|
||||
pass.Reportf(analysisutil.LineStart(tf, lineno), "[%s] %s: %s", arch, fnName, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
if arch == "" {
|
||||
// Determine architecture from +build line if possible.
|
||||
if m := asmPlusBuild.FindStringSubmatch(line); m != nil {
|
||||
// There can be multiple architectures in a single +build line,
|
||||
// so accumulate them all and then prefer the one that
|
||||
// matches build.Default.GOARCH.
|
||||
var archCandidates []*asmArch
|
||||
for _, fld := range strings.Fields(m[1]) {
|
||||
for _, a := range arches {
|
||||
if a.name == fld {
|
||||
archCandidates = append(archCandidates, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, a := range archCandidates {
|
||||
if a.name == build.Default.GOARCH {
|
||||
archCandidates = []*asmArch{a}
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(archCandidates) > 0 {
|
||||
arch = archCandidates[0].name
|
||||
archDef = archCandidates[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if m := asmTEXT.FindStringSubmatch(line); m != nil {
|
||||
flushRet()
|
||||
if arch == "" {
|
||||
// Arch not specified by filename or build tags.
|
||||
// Fall back to build.Default.GOARCH.
|
||||
for _, a := range arches {
|
||||
if a.name == build.Default.GOARCH {
|
||||
arch = a.name
|
||||
archDef = a
|
||||
break
|
||||
}
|
||||
}
|
||||
if arch == "" {
|
||||
log.Printf("%s: cannot determine architecture for assembly file", fname)
|
||||
continue Files
|
||||
}
|
||||
}
|
||||
fnName = m[2]
|
||||
if pkgPath := strings.TrimSpace(m[1]); pkgPath != "" {
|
||||
// The assembler uses Unicode division slash within
|
||||
// identifiers to represent the directory separator.
|
||||
pkgPath = strings.Replace(pkgPath, "∕", "/", -1)
|
||||
if pkgPath != pass.Pkg.Path() {
|
||||
log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath)
|
||||
fn = nil
|
||||
fnName = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
flag := m[3]
|
||||
fn = knownFunc[fnName][arch]
|
||||
if fn != nil {
|
||||
size, _ := strconv.Atoi(m[5])
|
||||
if size != fn.size && (flag != "7" && !strings.Contains(flag, "NOSPLIT") || size != 0) {
|
||||
badf("wrong argument size %d; expected $...-%d", size, fn.size)
|
||||
}
|
||||
}
|
||||
localSize, _ = strconv.Atoi(m[4])
|
||||
localSize += archDef.intSize
|
||||
if archDef.lr && !strings.Contains(flag, "NOFRAME") {
|
||||
// Account for caller's saved LR
|
||||
localSize += archDef.intSize
|
||||
}
|
||||
argSize, _ = strconv.Atoi(m[5])
|
||||
if fn == nil && !strings.Contains(fnName, "<>") {
|
||||
badf("function %s missing Go declaration", fnName)
|
||||
}
|
||||
wroteSP = false
|
||||
haveRetArg = false
|
||||
continue
|
||||
} else if strings.Contains(line, "TEXT") && strings.Contains(line, "SB") {
|
||||
// function, but not visible from Go (didn't match asmTEXT), so stop checking
|
||||
flushRet()
|
||||
fn = nil
|
||||
fnName = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, "RET") {
|
||||
retLine = append(retLine, lineno)
|
||||
}
|
||||
|
||||
if fnName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if asmDATA.FindStringSubmatch(line) != nil {
|
||||
fn = nil
|
||||
}
|
||||
|
||||
if archDef == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
|
||||
wroteSP = true
|
||||
continue
|
||||
}
|
||||
|
||||
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
|
||||
if m[3] != archDef.stack || wroteSP {
|
||||
continue
|
||||
}
|
||||
off := 0
|
||||
if m[1] != "" {
|
||||
off, _ = strconv.Atoi(m[2])
|
||||
}
|
||||
if off >= localSize {
|
||||
if fn != nil {
|
||||
v := fn.varByOffset[off-localSize]
|
||||
if v != nil {
|
||||
badf("%s should be %s+%d(FP)", m[1], v.name, off-localSize)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if off >= localSize+argSize {
|
||||
badf("use of %s points beyond argument frame", m[1])
|
||||
continue
|
||||
}
|
||||
badf("use of %s to access argument frame", m[1])
|
||||
}
|
||||
}
|
||||
|
||||
if fn == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, m := range asmUnnamedFP.FindAllStringSubmatch(line, -1) {
|
||||
off, _ := strconv.Atoi(m[2])
|
||||
v := fn.varByOffset[off]
|
||||
if v != nil {
|
||||
badf("use of unnamed argument %s; offset %d is %s+%d(FP)", m[1], off, v.name, v.off)
|
||||
} else {
|
||||
badf("use of unnamed argument %s", m[1])
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range asmNamedFP.FindAllStringSubmatch(line, -1) {
|
||||
name := m[1]
|
||||
off := 0
|
||||
if m[2] != "" {
|
||||
off, _ = strconv.Atoi(m[2])
|
||||
}
|
||||
if name == "ret" || strings.HasPrefix(name, "ret_") {
|
||||
haveRetArg = true
|
||||
}
|
||||
v := fn.vars[name]
|
||||
if v == nil {
|
||||
// Allow argframe+0(FP).
|
||||
if name == "argframe" && off == 0 {
|
||||
continue
|
||||
}
|
||||
v = fn.varByOffset[off]
|
||||
if v != nil {
|
||||
badf("unknown variable %s; offset %d is %s+%d(FP)", name, off, v.name, v.off)
|
||||
} else {
|
||||
badf("unknown variable %s", name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
asmCheckVar(badf, fn, line, m[0], off, v)
|
||||
}
|
||||
}
|
||||
flushRet()
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func asmKindForType(t types.Type, size int) asmKind {
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.String:
|
||||
return asmString
|
||||
case types.Complex64, types.Complex128:
|
||||
return asmComplex
|
||||
}
|
||||
return asmKind(size)
|
||||
case *types.Pointer, *types.Chan, *types.Map, *types.Signature:
|
||||
return asmKind(size)
|
||||
case *types.Struct:
|
||||
return asmStruct
|
||||
case *types.Interface:
|
||||
if t.Empty() {
|
||||
return asmEmptyInterface
|
||||
}
|
||||
return asmInterface
|
||||
case *types.Array:
|
||||
return asmArray
|
||||
case *types.Slice:
|
||||
return asmSlice
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// A component is an assembly-addressable component of a composite type,
|
||||
// or a composite type itself.
|
||||
type component struct {
|
||||
size int
|
||||
offset int
|
||||
kind asmKind
|
||||
typ string
|
||||
suffix string // Such as _base for string base, _0_lo for lo half of first element of [1]uint64 on 32 bit machine.
|
||||
outer string // The suffix for immediately containing composite type.
|
||||
}
|
||||
|
||||
func newComponent(suffix string, kind asmKind, typ string, offset, size int, outer string) component {
|
||||
return component{suffix: suffix, kind: kind, typ: typ, offset: offset, size: size, outer: outer}
|
||||
}
|
||||
|
||||
// componentsOfType generates a list of components of type t.
|
||||
// For example, given string, the components are the string itself, the base, and the length.
|
||||
func componentsOfType(arch *asmArch, t types.Type) []component {
|
||||
return appendComponentsRecursive(arch, t, nil, "", 0)
|
||||
}
|
||||
|
||||
// appendComponentsRecursive implements componentsOfType.
|
||||
// Recursion is required to correct handle structs and arrays,
|
||||
// which can contain arbitrary other types.
|
||||
func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suffix string, off int) []component {
|
||||
s := t.String()
|
||||
size := int(arch.sizes.Sizeof(t))
|
||||
kind := asmKindForType(t, size)
|
||||
cc = append(cc, newComponent(suffix, kind, s, off, size, suffix))
|
||||
|
||||
switch kind {
|
||||
case 8:
|
||||
if arch.ptrSize == 4 {
|
||||
w1, w2 := "lo", "hi"
|
||||
if arch.bigEndian {
|
||||
w1, w2 = w2, w1
|
||||
}
|
||||
cc = append(cc, newComponent(suffix+"_"+w1, 4, "half "+s, off, 4, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_"+w2, 4, "half "+s, off+4, 4, suffix))
|
||||
}
|
||||
|
||||
case asmEmptyInterface:
|
||||
cc = append(cc, newComponent(suffix+"_type", asmKind(arch.ptrSize), "interface type", off, arch.ptrSize, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
|
||||
|
||||
case asmInterface:
|
||||
cc = append(cc, newComponent(suffix+"_itable", asmKind(arch.ptrSize), "interface itable", off, arch.ptrSize, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_data", asmKind(arch.ptrSize), "interface data", off+arch.ptrSize, arch.ptrSize, suffix))
|
||||
|
||||
case asmSlice:
|
||||
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "slice base", off, arch.ptrSize, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "slice len", off+arch.ptrSize, arch.intSize, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_cap", asmKind(arch.intSize), "slice cap", off+arch.ptrSize+arch.intSize, arch.intSize, suffix))
|
||||
|
||||
case asmString:
|
||||
cc = append(cc, newComponent(suffix+"_base", asmKind(arch.ptrSize), "string base", off, arch.ptrSize, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_len", asmKind(arch.intSize), "string len", off+arch.ptrSize, arch.intSize, suffix))
|
||||
|
||||
case asmComplex:
|
||||
fsize := size / 2
|
||||
cc = append(cc, newComponent(suffix+"_real", asmKind(fsize), fmt.Sprintf("real(complex%d)", size*8), off, fsize, suffix))
|
||||
cc = append(cc, newComponent(suffix+"_imag", asmKind(fsize), fmt.Sprintf("imag(complex%d)", size*8), off+fsize, fsize, suffix))
|
||||
|
||||
case asmStruct:
|
||||
tu := t.Underlying().(*types.Struct)
|
||||
fields := make([]*types.Var, tu.NumFields())
|
||||
for i := 0; i < tu.NumFields(); i++ {
|
||||
fields[i] = tu.Field(i)
|
||||
}
|
||||
offsets := arch.sizes.Offsetsof(fields)
|
||||
for i, f := range fields {
|
||||
cc = appendComponentsRecursive(arch, f.Type(), cc, suffix+"_"+f.Name(), off+int(offsets[i]))
|
||||
}
|
||||
|
||||
case asmArray:
|
||||
tu := t.Underlying().(*types.Array)
|
||||
elem := tu.Elem()
|
||||
// Calculate offset of each element array.
|
||||
fields := []*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "fake0", elem),
|
||||
types.NewVar(token.NoPos, nil, "fake1", elem),
|
||||
}
|
||||
offsets := arch.sizes.Offsetsof(fields)
|
||||
elemoff := int(offsets[1])
|
||||
for i := 0; i < int(tu.Len()); i++ {
|
||||
cc = appendComponentsRecursive(arch, elem, cc, suffix+"_"+strconv.Itoa(i), i*elemoff)
|
||||
}
|
||||
}
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
// asmParseDecl parses a function decl for expected assembly variables.
|
||||
func asmParseDecl(pass *analysis.Pass, decl *ast.FuncDecl) map[string]*asmFunc {
|
||||
var (
|
||||
arch *asmArch
|
||||
fn *asmFunc
|
||||
offset int
|
||||
)
|
||||
|
||||
// addParams adds asmVars for each of the parameters in list.
|
||||
// isret indicates whether the list are the arguments or the return values.
|
||||
// TODO(adonovan): simplify by passing (*types.Signature).{Params,Results}
|
||||
// instead of list.
|
||||
addParams := func(list []*ast.Field, isret bool) {
|
||||
argnum := 0
|
||||
for _, fld := range list {
|
||||
t := pass.TypesInfo.Types[fld.Type].Type
|
||||
|
||||
// Work around github.com/golang/go/issues/28277.
|
||||
if t == nil {
|
||||
if ell, ok := fld.Type.(*ast.Ellipsis); ok {
|
||||
t = types.NewSlice(pass.TypesInfo.Types[ell.Elt].Type)
|
||||
}
|
||||
}
|
||||
|
||||
align := int(arch.sizes.Alignof(t))
|
||||
size := int(arch.sizes.Sizeof(t))
|
||||
offset += -offset & (align - 1)
|
||||
cc := componentsOfType(arch, t)
|
||||
|
||||
// names is the list of names with this type.
|
||||
names := fld.Names
|
||||
if len(names) == 0 {
|
||||
// Anonymous args will be called arg, arg1, arg2, ...
|
||||
// Similarly so for return values: ret, ret1, ret2, ...
|
||||
name := "arg"
|
||||
if isret {
|
||||
name = "ret"
|
||||
}
|
||||
if argnum > 0 {
|
||||
name += strconv.Itoa(argnum)
|
||||
}
|
||||
names = []*ast.Ident{ast.NewIdent(name)}
|
||||
}
|
||||
argnum += len(names)
|
||||
|
||||
// Create variable for each name.
|
||||
for _, id := range names {
|
||||
name := id.Name
|
||||
for _, c := range cc {
|
||||
outer := name + c.outer
|
||||
v := asmVar{
|
||||
name: name + c.suffix,
|
||||
kind: c.kind,
|
||||
typ: c.typ,
|
||||
off: offset + c.offset,
|
||||
size: c.size,
|
||||
}
|
||||
if vo := fn.vars[outer]; vo != nil {
|
||||
vo.inner = append(vo.inner, &v)
|
||||
}
|
||||
fn.vars[v.name] = &v
|
||||
for i := 0; i < v.size; i++ {
|
||||
fn.varByOffset[v.off+i] = &v
|
||||
}
|
||||
}
|
||||
offset += size
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m := make(map[string]*asmFunc)
|
||||
for _, arch = range arches {
|
||||
fn = &asmFunc{
|
||||
arch: arch,
|
||||
vars: make(map[string]*asmVar),
|
||||
varByOffset: make(map[int]*asmVar),
|
||||
}
|
||||
offset = 0
|
||||
addParams(decl.Type.Params.List, false)
|
||||
if decl.Type.Results != nil && len(decl.Type.Results.List) > 0 {
|
||||
offset += -offset & (arch.maxAlign - 1)
|
||||
addParams(decl.Type.Results.List, true)
|
||||
}
|
||||
fn.size = offset
|
||||
m[arch.name] = fn
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// asmCheckVar checks a single variable reference.
|
||||
func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
|
||||
m := asmOpcode.FindStringSubmatch(line)
|
||||
if m == nil {
|
||||
if !strings.HasPrefix(strings.TrimSpace(line), "//") {
|
||||
badf("cannot find assembly opcode")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Determine operand sizes from instruction.
|
||||
// Typically the suffix suffices, but there are exceptions.
|
||||
var src, dst, kind asmKind
|
||||
op := m[1]
|
||||
switch fn.arch.name + "." + op {
|
||||
case "386.FMOVLP":
|
||||
src, dst = 8, 4
|
||||
case "arm.MOVD":
|
||||
src = 8
|
||||
case "arm.MOVW":
|
||||
src = 4
|
||||
case "arm.MOVH", "arm.MOVHU":
|
||||
src = 2
|
||||
case "arm.MOVB", "arm.MOVBU":
|
||||
src = 1
|
||||
// LEA* opcodes don't really read the second arg.
|
||||
// They just take the address of it.
|
||||
case "386.LEAL":
|
||||
dst = 4
|
||||
case "amd64.LEAQ":
|
||||
dst = 8
|
||||
case "amd64p32.LEAL":
|
||||
dst = 4
|
||||
default:
|
||||
switch fn.arch.name {
|
||||
case "386", "amd64":
|
||||
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "D") || strings.HasSuffix(op, "DP")) {
|
||||
// FMOVDP, FXCHD, etc
|
||||
src = 8
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(op, "P") && strings.HasSuffix(op, "RD") {
|
||||
// PINSRD, PEXTRD, etc
|
||||
src = 4
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(op, "F") && (strings.HasSuffix(op, "F") || strings.HasSuffix(op, "FP")) {
|
||||
// FMOVFP, FXCHF, etc
|
||||
src = 4
|
||||
break
|
||||
}
|
||||
if strings.HasSuffix(op, "SD") {
|
||||
// MOVSD, SQRTSD, etc
|
||||
src = 8
|
||||
break
|
||||
}
|
||||
if strings.HasSuffix(op, "SS") {
|
||||
// MOVSS, SQRTSS, etc
|
||||
src = 4
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(op, "SET") {
|
||||
// SETEQ, etc
|
||||
src = 1
|
||||
break
|
||||
}
|
||||
switch op[len(op)-1] {
|
||||
case 'B':
|
||||
src = 1
|
||||
case 'W':
|
||||
src = 2
|
||||
case 'L':
|
||||
src = 4
|
||||
case 'D', 'Q':
|
||||
src = 8
|
||||
}
|
||||
case "ppc64", "ppc64le":
|
||||
// Strip standard suffixes to reveal size letter.
|
||||
m := ppc64Suff.FindStringSubmatch(op)
|
||||
if m != nil {
|
||||
switch m[1][0] {
|
||||
case 'B':
|
||||
src = 1
|
||||
case 'H':
|
||||
src = 2
|
||||
case 'W':
|
||||
src = 4
|
||||
case 'D':
|
||||
src = 8
|
||||
}
|
||||
}
|
||||
case "mips", "mipsle", "mips64", "mips64le":
|
||||
switch op {
|
||||
case "MOVB", "MOVBU":
|
||||
src = 1
|
||||
case "MOVH", "MOVHU":
|
||||
src = 2
|
||||
case "MOVW", "MOVWU", "MOVF":
|
||||
src = 4
|
||||
case "MOVV", "MOVD":
|
||||
src = 8
|
||||
}
|
||||
case "s390x":
|
||||
switch op {
|
||||
case "MOVB", "MOVBZ":
|
||||
src = 1
|
||||
case "MOVH", "MOVHZ":
|
||||
src = 2
|
||||
case "MOVW", "MOVWZ", "FMOVS":
|
||||
src = 4
|
||||
case "MOVD", "FMOVD":
|
||||
src = 8
|
||||
}
|
||||
}
|
||||
}
|
||||
if dst == 0 {
|
||||
dst = src
|
||||
}
|
||||
|
||||
// Determine whether the match we're holding
|
||||
// is the first or second argument.
|
||||
if strings.Index(line, expr) > strings.Index(line, ",") {
|
||||
kind = dst
|
||||
} else {
|
||||
kind = src
|
||||
}
|
||||
|
||||
vk := v.kind
|
||||
vs := v.size
|
||||
vt := v.typ
|
||||
switch vk {
|
||||
case asmInterface, asmEmptyInterface, asmString, asmSlice:
|
||||
// allow reference to first word (pointer)
|
||||
vk = v.inner[0].kind
|
||||
vs = v.inner[0].size
|
||||
vt = v.inner[0].typ
|
||||
}
|
||||
|
||||
if off != v.off {
|
||||
var inner bytes.Buffer
|
||||
for i, vi := range v.inner {
|
||||
if len(v.inner) > 1 {
|
||||
fmt.Fprintf(&inner, ",")
|
||||
}
|
||||
fmt.Fprintf(&inner, " ")
|
||||
if i == len(v.inner)-1 {
|
||||
fmt.Fprintf(&inner, "or ")
|
||||
}
|
||||
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
|
||||
}
|
||||
badf("invalid offset %s; expected %s+%d(FP)%s", expr, v.name, v.off, inner.String())
|
||||
return
|
||||
}
|
||||
if kind != 0 && kind != vk {
|
||||
var inner bytes.Buffer
|
||||
if len(v.inner) > 0 {
|
||||
fmt.Fprintf(&inner, " containing")
|
||||
for i, vi := range v.inner {
|
||||
if i > 0 && len(v.inner) > 2 {
|
||||
fmt.Fprintf(&inner, ",")
|
||||
}
|
||||
fmt.Fprintf(&inner, " ")
|
||||
if i > 0 && i == len(v.inner)-1 {
|
||||
fmt.Fprintf(&inner, "and ")
|
||||
}
|
||||
fmt.Fprintf(&inner, "%s+%d(FP)", vi.name, vi.off)
|
||||
}
|
||||
}
|
||||
badf("invalid %s of %s; %s is %d-byte value%s", op, expr, vt, vs, inner.String())
|
||||
}
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 asmdecl_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/asmdecl"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, asmdecl.Analyzer, "a")
|
||||
}
|
||||
48
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm.go
generated
vendored
Normal file
48
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// This file contains declarations to test the assembly in test_asm.s.
|
||||
|
||||
package a
|
||||
|
||||
type S struct {
|
||||
i int32
|
||||
b bool
|
||||
s string
|
||||
}
|
||||
|
||||
func arg1(x int8, y uint8)
|
||||
func arg2(x int16, y uint16)
|
||||
func arg4(x int32, y uint32)
|
||||
func arg8(x int64, y uint64)
|
||||
func argint(x int, y uint)
|
||||
func argptr(x *byte, y *byte, c chan int, m map[int]int, f func())
|
||||
func argstring(x, y string)
|
||||
func argslice(x, y []string)
|
||||
func argiface(x interface{}, y interface {
|
||||
m()
|
||||
})
|
||||
func argcomplex(x complex64, y complex128)
|
||||
func argstruct(x S, y struct{})
|
||||
func argarray(x [2]S)
|
||||
func returnint() int
|
||||
func returnbyte(x int) byte
|
||||
func returnnamed(x byte) (r1 int, r2 int16, r3 string, r4 byte)
|
||||
func returnintmissing() int
|
||||
func leaf(x, y int) int
|
||||
|
||||
func noprof(x int)
|
||||
func dupok(x int)
|
||||
func nosplit(x int)
|
||||
func rodata(x int)
|
||||
func noptr(x int)
|
||||
func wrapper(x int)
|
||||
|
||||
func f15271() (x uint32)
|
||||
func f17584(x float32, y complex64)
|
||||
|
||||
func noframe1(x int32)
|
||||
func noframe2(x int32)
|
||||
|
||||
func fvariadic(int, ...int)
|
||||
314
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm1.s
generated
vendored
Normal file
314
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm1.s
generated
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// +build amd64
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), AX
|
||||
// MOVB x+0(FP), AX // commented out instructions used to panic
|
||||
MOVB y+1(FP), BX
|
||||
MOVW x+0(FP), AX // want `\[amd64\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVL y+1(FP), AX // want `invalid MOVL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVQ y+1(FP), AX // want `invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
TESTB x+0(FP), AX
|
||||
TESTB y+1(FP), BX
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTW y+1(FP), AX // want `invalid TESTW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTL y+1(FP), AX // want `invalid TESTL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTQ y+1(FP), AX // want `invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
TESTB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 8(SP), AX // want `8\(SP\) should be x\+0\(FP\)`
|
||||
MOVB 9(SP), AX // want `9\(SP\) should be y\+1\(FP\)`
|
||||
MOVB 10(SP), AX // want `use of 10\(SP\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), AX // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVW y+2(FP), BX
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVL y+2(FP), AX // want `invalid MOVL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVQ y+2(FP), AX // want `invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTB y+2(FP), AX // want `invalid TESTB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTW x+0(FP), AX
|
||||
TESTW y+2(FP), BX
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTL y+2(FP), AX // want `invalid TESTL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTQ y+2(FP), AX // want `invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
TESTW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVL y+4(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTL x+0(FP), AX
|
||||
TESTL y+4(FP), AX
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVQ y+8(FP), AX
|
||||
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTQ x+0(FP), AX
|
||||
TESTQ y+8(FP), AX
|
||||
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int is 8-byte value`
|
||||
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVQ y+8(FP), AX
|
||||
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int is 8-byte value`
|
||||
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); uint is 8-byte value`
|
||||
TESTQ x+0(FP), AX
|
||||
TESTQ y+8(FP), AX
|
||||
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVQ y+8(FP), AX
|
||||
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); \*byte is 8-byte value`
|
||||
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); \*byte is 8-byte value`
|
||||
TESTQ x+0(FP), AX
|
||||
TESTQ y+8(FP), AX
|
||||
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVL c+16(FP), AX // want `invalid MOVL of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVL m+24(FP), AX // want `invalid MOVL of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVL f+32(FP), AX // want `invalid MOVL of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVL x_base+0(FP), AX // want `invalid MOVL of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVQ x_base+0(FP), AX
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+8(FP), AX // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVL x_len+8(FP), AX // want `invalid MOVL of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVQ x_len+8(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVQ y_len+8(FP), AX // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVL x_base+0(FP), AX // want `invalid MOVL of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVQ x_base+0(FP), AX
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+8(FP), AX // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVL x_len+8(FP), AX // want `invalid MOVL of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVQ x_len+8(FP), AX
|
||||
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVL x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+16(FP), AX // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVL x_cap+16(FP), AX // want `invalid MOVL of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVQ x_cap+16(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVQ y_len+8(FP), AX // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVQ y_cap+16(FP), AX // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVQ x+0(FP), AX
|
||||
MOVW x_type+0(FP), AX // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVL x_type+0(FP), AX // want `invalid MOVL of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVQ x_type+0(FP), AX
|
||||
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVL x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+8(FP), AX // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVL x_data+8(FP), AX // want `invalid MOVL of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVQ x_data+8(FP), AX
|
||||
MOVW y+16(FP), AX // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVL y+16(FP), AX // want `invalid MOVL of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVQ y+16(FP), AX
|
||||
MOVW y_itable+16(FP), AX // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVL y_itable+16(FP), AX // want `invalid MOVL of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVQ y_itable+16(FP), AX
|
||||
MOVQ y_type+16(FP), AX // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVW y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVL y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVQ y_data+16(FP), AX // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+24(FP), AX // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVL y_data+24(FP), AX // want `invalid MOVL of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVQ y_data+24(FP), AX
|
||||
RET
|
||||
|
||||
TEXT ·argcomplex(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVSS x+0(FP), X0 // want `invalid MOVSS of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)`
|
||||
MOVSD x+0(FP), X0 // want `invalid MOVSD of x\+0\(FP\); complex64 is 8-byte value containing x_real\+0\(FP\) and x_imag\+4\(FP\)`
|
||||
MOVSS x_real+0(FP), X0
|
||||
MOVSD x_real+0(FP), X0 // want `invalid MOVSD of x_real\+0\(FP\); real\(complex64\) is 4-byte value`
|
||||
MOVSS x_real+4(FP), X0 // want `invalid offset x_real\+4\(FP\); expected x_real\+0\(FP\)`
|
||||
MOVSS x_imag+4(FP), X0
|
||||
MOVSD x_imag+4(FP), X0 // want `invalid MOVSD of x_imag\+4\(FP\); imag\(complex64\) is 4-byte value`
|
||||
MOVSS x_imag+8(FP), X0 // want `invalid offset x_imag\+8\(FP\); expected x_imag\+4\(FP\)`
|
||||
MOVSD y+8(FP), X0 // want `invalid MOVSD of y\+8\(FP\); complex128 is 16-byte value containing y_real\+8\(FP\) and y_imag\+16\(FP\)`
|
||||
MOVSS y_real+8(FP), X0 // want `invalid MOVSS of y_real\+8\(FP\); real\(complex128\) is 8-byte value`
|
||||
MOVSD y_real+8(FP), X0
|
||||
MOVSS y_real+16(FP), X0 // want `invalid offset y_real\+16\(FP\); expected y_real\+8\(FP\)`
|
||||
MOVSS y_imag+16(FP), X0 // want `invalid MOVSS of y_imag\+16\(FP\); imag\(complex128\) is 8-byte value`
|
||||
MOVSD y_imag+16(FP), X0
|
||||
MOVSS y_imag+24(FP), X0 // want `invalid offset y_imag\+24\(FP\); expected y_imag\+16\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argstruct(SB),0,$64 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); a.S is 24-byte value`
|
||||
MOVQ x_i+0(FP), AX // want `invalid MOVQ of x_i\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ x_b+0(FP), AX // want `invalid offset x_b\+0\(FP\); expected x_b\+4\(FP\)`
|
||||
MOVQ x_s+8(FP), AX
|
||||
MOVQ x_s_base+8(FP), AX
|
||||
MOVQ x_s+16(FP), AX // want `invalid offset x_s\+16\(FP\); expected x_s\+8\(FP\), x_s_base\+8\(FP\), or x_s_len\+16\(FP\)`
|
||||
MOVQ x_s_len+16(FP), AX
|
||||
RET
|
||||
|
||||
TEXT ·argarray(SB),0,$64 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); \[2\]a.S is 48-byte value`
|
||||
MOVQ x_0_i+0(FP), AX // want `invalid MOVQ of x_0_i\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ x_0_b+0(FP), AX // want `invalid offset x_0_b\+0\(FP\); expected x_0_b\+4\(FP\)`
|
||||
MOVQ x_0_s+8(FP), AX
|
||||
MOVQ x_0_s_base+8(FP), AX
|
||||
MOVQ x_0_s+16(FP), AX // want `invalid offset x_0_s\+16\(FP\); expected x_0_s\+8\(FP\), x_0_s_base\+8\(FP\), or x_0_s_len\+16\(FP\)`
|
||||
MOVQ x_0_s_len+16(FP), AX
|
||||
MOVB foo+25(FP), AX // want `unknown variable foo; offset 25 is x_1_i\+24\(FP\)`
|
||||
MOVQ x_1_s+32(FP), AX
|
||||
MOVQ x_1_s_base+32(FP), AX
|
||||
MOVQ x_1_s+40(FP), AX // want `invalid offset x_1_s\+40\(FP\); expected x_1_s\+32\(FP\), x_1_s_base\+32\(FP\), or x_1_s_len\+40\(FP\)`
|
||||
MOVQ x_1_s_len+40(FP), AX
|
||||
RET
|
||||
|
||||
TEXT ·returnint(SB),0,$0-8
|
||||
MOVB AX, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW AX, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVL AX, ret+0(FP) // want `invalid MOVL of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVQ AX, ret+0(FP)
|
||||
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVQ AX, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-9
|
||||
MOVQ x+0(FP), AX
|
||||
MOVB AX, ret+8(FP)
|
||||
MOVW AX, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVL AX, ret+8(FP) // want `invalid MOVL of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVQ AX, ret+8(FP) // want `invalid MOVQ of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB AX, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-41
|
||||
MOVB x+0(FP), AX
|
||||
MOVQ AX, r1+8(FP)
|
||||
MOVW AX, r2+16(FP)
|
||||
MOVQ AX, r3+24(FP)
|
||||
MOVQ AX, r3_base+24(FP)
|
||||
MOVQ AX, r3_len+32(FP)
|
||||
MOVB AX, r4+40(FP)
|
||||
MOVL AX, r1+8(FP) // want `invalid MOVL of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
|
||||
|
||||
// issue 15271
|
||||
TEXT ·f15271(SB), NOSPLIT, $0-4
|
||||
// Stick 123 into the low 32 bits of X0.
|
||||
MOVQ $123, AX
|
||||
PINSRD $0, AX, X0
|
||||
|
||||
// Return them.
|
||||
PEXTRD $0, X0, x+0(FP)
|
||||
RET
|
||||
|
||||
// issue 17584
|
||||
TEXT ·f17584(SB), NOSPLIT, $12
|
||||
MOVSS x+0(FP), X0
|
||||
MOVSS y_real+4(FP), X0
|
||||
MOVSS y_imag+8(FP), X0
|
||||
RET
|
||||
256
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm2.s
generated
vendored
Normal file
256
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm2.s
generated
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// +build 386
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), AX
|
||||
MOVB y+1(FP), BX
|
||||
MOVW x+0(FP), AX // want `\[386\] arg1: invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVL y+1(FP), AX // want `invalid MOVL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVQ y+1(FP), AX // want `invalid MOVQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
TESTB x+0(FP), AX
|
||||
TESTB y+1(FP), BX
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTW y+1(FP), AX // want `invalid TESTW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTL y+1(FP), AX // want `invalid TESTL of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int8 is 1-byte value`
|
||||
TESTQ y+1(FP), AX // want `invalid TESTQ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
TESTB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
TESTB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 4(SP), AX // want `4\(SP\) should be x\+0\(FP\)`
|
||||
MOVB 5(SP), AX // want `5\(SP\) should be y\+1\(FP\)`
|
||||
MOVB 6(SP), AX // want `use of 6\(SP\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), AX // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVW y+2(FP), BX
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVL y+2(FP), AX // want `invalid MOVL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVQ y+2(FP), AX // want `invalid MOVQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTB y+2(FP), AX // want `invalid TESTB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTW x+0(FP), AX
|
||||
TESTW y+2(FP), BX
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTL y+2(FP), AX // want `invalid TESTL of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int16 is 2-byte value`
|
||||
TESTQ y+2(FP), AX // want `invalid TESTQ of y\+2\(FP\); uint16 is 2-byte value`
|
||||
TESTW x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
TESTW y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVL y+4(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTL x+0(FP), AX
|
||||
TESTL y+4(FP), AX
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int32 is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint32 is 4-byte value`
|
||||
TESTL x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTL y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVL x+0(FP), AX // want `invalid MOVL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
|
||||
MOVL x_lo+0(FP), AX
|
||||
MOVL x_hi+4(FP), AX
|
||||
MOVL y+8(FP), AX // want `invalid MOVL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
|
||||
MOVL y_lo+8(FP), AX
|
||||
MOVL y_hi+12(FP), AX
|
||||
MOVQ x+0(FP), AX
|
||||
MOVQ y+8(FP), AX
|
||||
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTB y+8(FP), BX // want `invalid TESTB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int64 is 8-byte value`
|
||||
TESTW y+8(FP), AX // want `invalid TESTW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
TESTL x+0(FP), AX // want `invalid TESTL of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
|
||||
TESTL y+8(FP), AX // want `invalid TESTL of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
|
||||
TESTQ x+0(FP), AX
|
||||
TESTQ y+8(FP), AX
|
||||
TESTQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVL y+4(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); int is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); int is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); uint is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); int is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); uint is 4-byte value`
|
||||
TESTL x+0(FP), AX
|
||||
TESTL y+4(FP), AX
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); int is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); uint is 4-byte value`
|
||||
TESTQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVW y+4(FP), AX // want `invalid MOVW of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVL y+4(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVQ y+4(FP), AX // want `invalid MOVQ of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
TESTB x+0(FP), AX // want `invalid TESTB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
TESTB y+4(FP), BX // want `invalid TESTB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
TESTW x+0(FP), AX // want `invalid TESTW of x\+0\(FP\); \*byte is 4-byte value`
|
||||
TESTW y+4(FP), AX // want `invalid TESTW of y\+4\(FP\); \*byte is 4-byte value`
|
||||
TESTL x+0(FP), AX
|
||||
TESTL y+4(FP), AX
|
||||
TESTQ x+0(FP), AX // want `invalid TESTQ of x\+0\(FP\); \*byte is 4-byte value`
|
||||
TESTQ y+4(FP), AX // want `invalid TESTQ of y\+4\(FP\); \*byte is 4-byte value`
|
||||
TESTQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
TESTQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
MOVW c+8(FP), AX // want `invalid MOVW of c\+8\(FP\); chan int is 4-byte value`
|
||||
MOVW m+12(FP), AX // want `invalid MOVW of m\+12\(FP\); map\[int\]int is 4-byte value`
|
||||
MOVW f+16(FP), AX // want `invalid MOVW of f\+16\(FP\); func\(\) is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); string base is 4-byte value`
|
||||
MOVL x_base+0(FP), AX
|
||||
MOVQ x_base+0(FP), AX // want `invalid MOVQ of x_base\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+4(FP), AX // want `invalid MOVW of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVL x_len+4(FP), AX
|
||||
MOVQ x_len+4(FP), AX // want `invalid MOVQ of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x_base+0(FP), AX // want `invalid MOVW of x_base\+0\(FP\); slice base is 4-byte value`
|
||||
MOVL x_base+0(FP), AX
|
||||
MOVQ x_base+0(FP), AX // want `invalid MOVQ of x_base\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVL x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+4(FP), AX // want `invalid MOVW of x_len\+4\(FP\); slice len is 4-byte value`
|
||||
MOVL x_len+4(FP), AX
|
||||
MOVQ x_len+4(FP), AX // want `invalid MOVQ of x_len\+4\(FP\); slice len is 4-byte value`
|
||||
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVL x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVW x_cap+8(FP), AX // want `invalid MOVW of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVL x_cap+8(FP), AX
|
||||
MOVQ x_cap+8(FP), AX // want `invalid MOVQ of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
|
||||
MOVQ y_cap+8(FP), AX // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-16
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVL x+0(FP), AX
|
||||
MOVQ x+0(FP), AX // want `invalid MOVQ of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x_type+0(FP), AX // want `invalid MOVW of x_type\+0\(FP\); interface type is 4-byte value`
|
||||
MOVL x_type+0(FP), AX
|
||||
MOVQ x_type+0(FP), AX // want `invalid MOVQ of x_type\+0\(FP\); interface type is 4-byte value`
|
||||
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVL x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVW x_data+4(FP), AX // want `invalid MOVW of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVL x_data+4(FP), AX
|
||||
MOVQ x_data+4(FP), AX // want `invalid MOVQ of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVL y+8(FP), AX
|
||||
MOVQ y+8(FP), AX // want `invalid MOVQ of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y_itable+8(FP), AX // want `invalid MOVW of y_itable\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVL y_itable+8(FP), AX
|
||||
MOVQ y_itable+8(FP), AX // want `invalid MOVQ of y_itable\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVQ y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
|
||||
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVL y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVQ y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVW y_data+12(FP), AX // want `invalid MOVW of y_data\+12\(FP\); interface data is 4-byte value`
|
||||
MOVL y_data+12(FP), AX
|
||||
MOVQ y_data+12(FP), AX // want `invalid MOVQ of y_data\+12\(FP\); interface data is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnint(SB),0,$0-4
|
||||
MOVB AX, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVW AX, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVL AX, ret+0(FP)
|
||||
MOVQ AX, ret+0(FP) // want `invalid MOVQ of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVQ AX, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-5
|
||||
MOVL x+0(FP), AX
|
||||
MOVB AX, ret+4(FP)
|
||||
MOVW AX, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVL AX, ret+4(FP) // want `invalid MOVL of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVQ AX, ret+4(FP) // want `invalid MOVQ of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB AX, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-21
|
||||
MOVB x+0(FP), AX
|
||||
MOVL AX, r1+4(FP)
|
||||
MOVW AX, r2+8(FP)
|
||||
MOVL AX, r3+12(FP)
|
||||
MOVL AX, r3_base+12(FP)
|
||||
MOVL AX, r3_len+16(FP)
|
||||
MOVB AX, r4+20(FP)
|
||||
MOVQ AX, r1+4(FP) // want `invalid MOVQ of r1\+4\(FP\); int is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-4
|
||||
RET // want `RET without writing to 4-byte ret\+0\(FP\)`
|
||||
191
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm3.s
generated
vendored
Normal file
191
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm3.s
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// +build arm
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), AX
|
||||
MOVB y+1(FP), BX
|
||||
MOVH x+0(FP), AX // want `\[arm\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVH y+1(FP), AX // want `invalid MOVH of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVW y+1(FP), AX // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), AX // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVB y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 8(R13), AX // want `8\(R13\) should be x\+0\(FP\)`
|
||||
MOVB 9(R13), AX // want `9\(R13\) should be y\+1\(FP\)`
|
||||
MOVB 10(R13), AX // want `use of 10\(R13\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVB x+0(FP), AX // want `arg2: invalid MOVB of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), AX // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVH x+0(FP), AX
|
||||
MOVH y+2(FP), BX
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), AX // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVH x+2(FP), AX // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), AX // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVW y+4(FP), AX
|
||||
MOVW x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), BX // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), AX // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), AX // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
|
||||
MOVW x_lo+0(FP), AX
|
||||
MOVW x_hi+4(FP), AX
|
||||
MOVW y+8(FP), AX // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
|
||||
MOVW y_lo+8(FP), AX
|
||||
MOVW y_hi+12(FP), AX
|
||||
MOVQ x+0(FP), AX
|
||||
MOVQ y+8(FP), AX
|
||||
MOVQ x+8(FP), AX // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); int is 4-byte value`
|
||||
MOVH y+4(FP), AX // want `invalid MOVH of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVW y+4(FP), AX
|
||||
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
|
||||
MOVB x+0(FP), AX // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVB y+4(FP), BX // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVH y+4(FP), AX // want `invalid MOVH of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVW y+4(FP), AX
|
||||
MOVQ x+4(FP), AX // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVQ y+2(FP), AX // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
MOVH c+8(FP), AX // want `invalid MOVH of c\+8\(FP\); chan int is 4-byte value`
|
||||
MOVH m+12(FP), AX // want `invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value`
|
||||
MOVH f+16(FP), AX // want `invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVH x_base+0(FP), AX // want `invalid MOVH of x_base\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x_base+0(FP), AX
|
||||
MOVH x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), AX // want `invalid MOVH of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVW x_len+4(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVH x_base+0(FP), AX // want `invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x_base+0(FP), AX
|
||||
MOVH x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVQ x_len+0(FP), AX // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), AX // want `invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value`
|
||||
MOVW x_len+4(FP), AX
|
||||
MOVH x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVW x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVQ x_cap+0(FP), AX // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVH x_cap+8(FP), AX // want `invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVW x_cap+8(FP), AX
|
||||
MOVQ y+0(FP), AX // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
|
||||
MOVQ y_len+4(FP), AX // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
|
||||
MOVQ y_cap+8(FP), AX // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-16
|
||||
MOVH x+0(FP), AX // want `invalid MOVH of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x+0(FP), AX
|
||||
MOVH x_type+0(FP), AX // want `invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x_type+0(FP), AX
|
||||
MOVQ x_itable+0(FP), AX // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), AX // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVW x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVQ x_data+0(FP), AX // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVH x_data+4(FP), AX // want `invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVW x_data+4(FP), AX
|
||||
MOVH y+8(FP), AX // want `invalid MOVH of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y+8(FP), AX
|
||||
MOVH y_itable+8(FP), AX // want `invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y_itable+8(FP), AX
|
||||
MOVQ y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
|
||||
MOVH y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVQ y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVH y_data+12(FP), AX // want `invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value`
|
||||
MOVW y_data+12(FP), AX
|
||||
RET
|
||||
|
||||
TEXT ·returnint(SB),0,$0-4
|
||||
MOVB AX, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVH AX, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 4-byte value`
|
||||
MOVW AX, ret+0(FP)
|
||||
MOVQ AX, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVQ AX, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-5
|
||||
MOVW x+0(FP), AX
|
||||
MOVB AX, ret+4(FP)
|
||||
MOVH AX, ret+4(FP) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVW AX, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB AX, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-21
|
||||
MOVB x+0(FP), AX
|
||||
MOVW AX, r1+4(FP)
|
||||
MOVH AX, r2+8(FP)
|
||||
MOVW AX, r3+12(FP)
|
||||
MOVW AX, r3_base+12(FP)
|
||||
MOVW AX, r3_len+16(FP)
|
||||
MOVB AX, r4+20(FP)
|
||||
MOVB AX, r1+4(FP) // want `invalid MOVB of r1\+4\(FP\); int is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-4
|
||||
RET // want `RET without writing to 4-byte ret\+0\(FP\)`
|
||||
|
||||
TEXT ·leaf(SB),0,$-4-12
|
||||
MOVW x+0(FP), AX
|
||||
MOVW y+4(FP), AX
|
||||
MOVW AX, ret+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·noframe1(SB),0,$0-4
|
||||
MOVW 0(R13), AX // Okay; our saved LR
|
||||
MOVW 4(R13), AX // Okay; caller's saved LR
|
||||
MOVW x+8(R13), AX // Okay; x argument
|
||||
MOVW 12(R13), AX // want `use of 12\(R13\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·noframe2(SB),NOFRAME,$0-4
|
||||
MOVW 0(R13), AX // Okay; caller's saved LR
|
||||
MOVW x+4(R13), AX // Okay; x argument
|
||||
MOVW 8(R13), AX // want `use of 8\(R13\) points beyond argument frame`
|
||||
MOVW 12(R13), AX // want `use of 12\(R13\) points beyond argument frame`
|
||||
RET
|
||||
25
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm4.s
generated
vendored
Normal file
25
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm4.s
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// +build amd64
|
||||
|
||||
// Test cases for symbolic NOSPLIT etc. on TEXT symbols.
|
||||
|
||||
TEXT ·noprof(SB),NOPROF,$0-8
|
||||
RET
|
||||
|
||||
TEXT ·dupok(SB),DUPOK,$0-8
|
||||
RET
|
||||
|
||||
TEXT ·nosplit(SB),NOSPLIT,$0
|
||||
RET
|
||||
|
||||
TEXT ·rodata(SB),RODATA,$0-8
|
||||
RET
|
||||
|
||||
TEXT ·noptr(SB),NOPTR|NOSPLIT,$0
|
||||
RET
|
||||
|
||||
TEXT ·wrapper(SB),WRAPPER,$0-8
|
||||
RET
|
||||
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm5.s
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm5.s
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// +build mips64
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R1
|
||||
MOVBU y+1(FP), R2
|
||||
MOVH x+0(FP), R1 // want `\[mips64\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHU y+1(FP), R1 // want `invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWU y+1(FP), R1 // want `invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVV y+1(FP), R1 // want `invalid MOVV of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBU y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 16(R29), R1 // want `16\(R29\) should be x\+0\(FP\)`
|
||||
MOVB 17(R29), R1 // want `17\(R29\) should be y\+1\(FP\)`
|
||||
MOVB 18(R29), R1 // want `use of 18\(R29\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBU x+0(FP), R1 // want `arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R1 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHU x+0(FP), R1
|
||||
MOVH y+2(FP), R2
|
||||
MOVWU x+0(FP), R1 // want `invalid MOVWU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVV y+2(FP), R1 // want `invalid MOVV of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHU x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVW y+4(FP), R1
|
||||
MOVV x+0(FP), R1 // want `invalid MOVV of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVV y+4(FP), R1 // want `invalid MOVV of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVV y+8(FP), R1
|
||||
MOVV x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVV y+8(FP), R1
|
||||
MOVV x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVV y+8(FP), R1
|
||||
MOVV x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVV y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVW c+16(FP), R1 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVW m+24(FP), R1 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVW f+32(FP), R1 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVV x_base+0(FP), R1
|
||||
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVV x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVV x_len+8(FP), R1
|
||||
MOVV y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVV y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVV x_base+0(FP), R1
|
||||
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVV x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVV x_len+8(FP), R1
|
||||
MOVH x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVV x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVH x_cap+16(FP), R1 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVW x_cap+16(FP), R1 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVV x_cap+16(FP), R1
|
||||
MOVV y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVV y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVV y_cap+16(FP), R1 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVV x+0(FP), R1
|
||||
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x_type+0(FP), R1 // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVV x_type+0(FP), R1
|
||||
MOVV x_itable+0(FP), R1 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVV x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVV x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVH x_data+8(FP), R1 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVW x_data+8(FP), R1 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVV x_data+8(FP), R1
|
||||
MOVH y+16(FP), R1 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y+16(FP), R1 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVV y+16(FP), R1
|
||||
MOVH y_itable+16(FP), R1 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y_itable+16(FP), R1 // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVV y_itable+16(FP), R1
|
||||
MOVV y_type+16(FP), R1 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVH y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVV y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVH y_data+24(FP), R1 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVW y_data+24(FP), R1 // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVV y_data+24(FP), R1
|
||||
RET
|
||||
|
||||
TEXT ·returnint(SB),0,$0-8
|
||||
MOVB R1, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVH R1, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW R1, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVV R1, ret+0(FP)
|
||||
MOVV R1, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVV R1, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-9
|
||||
MOVV x+0(FP), R1
|
||||
MOVB R1, ret+8(FP)
|
||||
MOVH R1, ret+8(FP) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVV R1, ret+8(FP) // want `invalid MOVV of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-41
|
||||
MOVB x+0(FP), R1
|
||||
MOVV R1, r1+8(FP)
|
||||
MOVH R1, r2+16(FP)
|
||||
MOVV R1, r3+24(FP)
|
||||
MOVV R1, r3_base+24(FP)
|
||||
MOVV R1, r3_len+32(FP)
|
||||
MOVB R1, r4+40(FP)
|
||||
MOVW R1, r1+8(FP) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm6.s
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm6.s
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// +build s390x
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R1
|
||||
MOVBZ y+1(FP), R2
|
||||
MOVH x+0(FP), R1 // want `\[s390x\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHZ y+1(FP), R1 // want `invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWZ y+1(FP), R1 // want `invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVD y+1(FP), R1 // want `invalid MOVD of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBZ y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 16(R15), R1 // want `16\(R15\) should be x\+0\(FP\)`
|
||||
MOVB 17(R15), R1 // want `17\(R15\) should be y\+1\(FP\)`
|
||||
MOVB 18(R15), R1 // want `use of 18\(R15\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBZ x+0(FP), R1 // want `arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R1 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHZ x+0(FP), R1
|
||||
MOVH y+2(FP), R2
|
||||
MOVWZ x+0(FP), R1 // want `invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVD y+2(FP), R1 // want `invalid MOVD of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHZ x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVW y+4(FP), R1
|
||||
MOVD x+0(FP), R1 // want `invalid MOVD of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVD y+4(FP), R1 // want `invalid MOVD of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVD y+8(FP), R1
|
||||
MOVD x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVD y+8(FP), R1
|
||||
MOVD x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVD y+8(FP), R1
|
||||
MOVD x+8(FP), R1 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVW c+16(FP), R1 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVW m+24(FP), R1 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVW f+32(FP), R1 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVD x_base+0(FP), R1
|
||||
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVD x_len+8(FP), R1
|
||||
MOVD y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVD y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x_base+0(FP), R1 // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVD x_base+0(FP), R1
|
||||
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R1 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVW x_len+8(FP), R1 // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVD x_len+8(FP), R1
|
||||
MOVH x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVD x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVH x_cap+16(FP), R1 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVW x_cap+16(FP), R1 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVD x_cap+16(FP), R1
|
||||
MOVD y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVD y_len+8(FP), R1 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVD y_cap+16(FP), R1 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVD x+0(FP), R1
|
||||
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x_type+0(FP), R1 // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVD x_type+0(FP), R1
|
||||
MOVD x_itable+0(FP), R1 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVD x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVD x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVH x_data+8(FP), R1 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVW x_data+8(FP), R1 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVD x_data+8(FP), R1
|
||||
MOVH y+16(FP), R1 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y+16(FP), R1 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVD y+16(FP), R1
|
||||
MOVH y_itable+16(FP), R1 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y_itable+16(FP), R1 // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVD y_itable+16(FP), R1
|
||||
MOVD y_type+16(FP), R1 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVH y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVD y_data+16(FP), R1 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVH y_data+24(FP), R1 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVW y_data+24(FP), R1 // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVD y_data+24(FP), R1
|
||||
RET
|
||||
|
||||
TEXT ·returnint(SB),0,$0-8
|
||||
MOVB R1, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVH R1, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW R1, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVD R1, ret+0(FP)
|
||||
MOVD R1, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVD R1, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-9
|
||||
MOVD x+0(FP), R1
|
||||
MOVB R1, ret+8(FP)
|
||||
MOVH R1, ret+8(FP) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVD R1, ret+8(FP) // want `invalid MOVD of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-41
|
||||
MOVB x+0(FP), R1
|
||||
MOVD R1, r1+8(FP)
|
||||
MOVH R1, r2+16(FP)
|
||||
MOVD R1, r3+24(FP)
|
||||
MOVD R1, r3_base+24(FP)
|
||||
MOVD R1, r3_len+32(FP)
|
||||
MOVB R1, r4+40(FP)
|
||||
MOVW R1, r1+8(FP) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm7.s
generated
vendored
Normal file
192
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm7.s
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// +build ppc64 ppc64le
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R3
|
||||
MOVBZ y+1(FP), R4
|
||||
MOVH x+0(FP), R3 // want `\[(ppc64|ppc64le)\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHZ y+1(FP), R3 // want `invalid MOVHZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWZ y+1(FP), R3 // want `invalid MOVWZ of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVD y+1(FP), R3 // want `invalid MOVD of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R3 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBZ y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 16(R1), R3 // want `16\(R1\) should be x\+0\(FP\)`
|
||||
MOVB 17(R1), R3 // want `17\(R1\) should be y\+1\(FP\)`
|
||||
MOVB 18(R1), R3 // want `use of 18\(R1\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBZ x+0(FP), R3 // want `arg2: invalid MOVBZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R3 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHZ x+0(FP), R3
|
||||
MOVH y+2(FP), R4
|
||||
MOVWZ x+0(FP), R3 // want `invalid MOVWZ of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R3 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVD y+2(FP), R3 // want `invalid MOVD of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHZ x+2(FP), R3 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R4 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R3 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), R3
|
||||
MOVW y+4(FP), R3
|
||||
MOVD x+0(FP), R3 // want `invalid MOVD of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVD y+4(FP), R3 // want `invalid MOVD of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+4(FP), R3 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVW y+8(FP), R3 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVD y+8(FP), R3
|
||||
MOVD x+8(FP), R3 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); int is 8-byte value`
|
||||
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); int is 8-byte value`
|
||||
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); int is 8-byte value`
|
||||
MOVW y+8(FP), R3 // want `invalid MOVW of y\+8\(FP\); uint is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVD y+8(FP), R3
|
||||
MOVD x+8(FP), R3 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-40`
|
||||
MOVB x+0(FP), R3 // want `invalid MOVB of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVB y+8(FP), R4 // want `invalid MOVB of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVH y+8(FP), R3 // want `invalid MOVH of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); \*byte is 8-byte value`
|
||||
MOVW y+8(FP), R3 // want `invalid MOVW of y\+8\(FP\); \*byte is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVD y+8(FP), R3
|
||||
MOVD x+8(FP), R3 // want `invalid offset x\+8\(FP\); expected x\+0\(FP\)`
|
||||
MOVD y+2(FP), R3 // want `invalid offset y\+2\(FP\); expected y\+8\(FP\)`
|
||||
MOVW c+16(FP), R3 // want `invalid MOVW of c\+16\(FP\); chan int is 8-byte value`
|
||||
MOVW m+24(FP), R3 // want `invalid MOVW of m\+24\(FP\); map\[int\]int is 8-byte value`
|
||||
MOVW f+32(FP), R3 // want `invalid MOVW of f\+32\(FP\); func\(\) is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$32 // want `wrong argument size 0; expected \$\.\.\.-32`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); string base is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVH x_base+0(FP), R3 // want `invalid MOVH of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVW x_base+0(FP), R3 // want `invalid MOVW of x_base\+0\(FP\); string base is 8-byte value`
|
||||
MOVD x_base+0(FP), R3
|
||||
MOVH x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R3 // want `invalid MOVH of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVW x_len+8(FP), R3 // want `invalid MOVW of x_len\+8\(FP\); string len is 8-byte value`
|
||||
MOVD x_len+8(FP), R3
|
||||
MOVD y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+16\(FP\)`
|
||||
MOVD y_len+8(FP), R3 // want `invalid offset y_len\+8\(FP\); expected y_len\+24\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$48 // want `wrong argument size 0; expected \$\.\.\.-48`
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); slice base is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVH x_base+0(FP), R3 // want `invalid MOVH of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVW x_base+0(FP), R3 // want `invalid MOVW of x_base\+0\(FP\); slice base is 8-byte value`
|
||||
MOVD x_base+0(FP), R3
|
||||
MOVH x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVW x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVD x_len+0(FP), R3 // want `invalid offset x_len\+0\(FP\); expected x_len\+8\(FP\)`
|
||||
MOVH x_len+8(FP), R3 // want `invalid MOVH of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVW x_len+8(FP), R3 // want `invalid MOVW of x_len\+8\(FP\); slice len is 8-byte value`
|
||||
MOVD x_len+8(FP), R3
|
||||
MOVH x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVW x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVD x_cap+0(FP), R3 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+16\(FP\)`
|
||||
MOVH x_cap+16(FP), R3 // want `invalid MOVH of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVW x_cap+16(FP), R3 // want `invalid MOVW of x_cap\+16\(FP\); slice cap is 8-byte value`
|
||||
MOVD x_cap+16(FP), R3
|
||||
MOVD y+0(FP), R3 // want `invalid offset y\+0\(FP\); expected y\+24\(FP\)`
|
||||
MOVD y_len+8(FP), R3 // want `invalid offset y_len\+8\(FP\); expected y_len\+32\(FP\)`
|
||||
MOVD y_cap+16(FP), R3 // want `invalid offset y_cap\+16\(FP\); expected y_cap\+40\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-32
|
||||
MOVH x+0(FP), R3 // want `invalid MOVH of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x+0(FP), R3 // want `invalid MOVW of x\+0\(FP\); interface type is 8-byte value`
|
||||
MOVD x+0(FP), R3
|
||||
MOVH x_type+0(FP), R3 // want `invalid MOVH of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVW x_type+0(FP), R3 // want `invalid MOVW of x_type\+0\(FP\); interface type is 8-byte value`
|
||||
MOVD x_type+0(FP), R3
|
||||
MOVD x_itable+0(FP), R3 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVD x_itable+1(FP), R3 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVW x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVD x_data+0(FP), R3 // want `invalid offset x_data\+0\(FP\); expected x_data\+8\(FP\)`
|
||||
MOVH x_data+8(FP), R3 // want `invalid MOVH of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVW x_data+8(FP), R3 // want `invalid MOVW of x_data\+8\(FP\); interface data is 8-byte value`
|
||||
MOVD x_data+8(FP), R3
|
||||
MOVH y+16(FP), R3 // want `invalid MOVH of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y+16(FP), R3 // want `invalid MOVW of y\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVD y+16(FP), R3
|
||||
MOVH y_itable+16(FP), R3 // want `invalid MOVH of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVW y_itable+16(FP), R3 // want `invalid MOVW of y_itable\+16\(FP\); interface itable is 8-byte value`
|
||||
MOVD y_itable+16(FP), R3
|
||||
MOVD y_type+16(FP), R3 // want `unknown variable y_type; offset 16 is y_itable\+16\(FP\)`
|
||||
MOVH y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVW y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVD y_data+16(FP), R3 // want `invalid offset y_data\+16\(FP\); expected y_data\+24\(FP\)`
|
||||
MOVH y_data+24(FP), R3 // want `invalid MOVH of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVW y_data+24(FP), R3 // want `invalid MOVW of y_data\+24\(FP\); interface data is 8-byte value`
|
||||
MOVD y_data+24(FP), R3
|
||||
RET
|
||||
|
||||
TEXT ·returnint(SB),0,$0-8
|
||||
MOVB R3, ret+0(FP) // want `invalid MOVB of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVH R3, ret+0(FP) // want `invalid MOVH of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVW R3, ret+0(FP) // want `invalid MOVW of ret\+0\(FP\); int is 8-byte value`
|
||||
MOVD R3, ret+0(FP)
|
||||
MOVD R3, ret+1(FP) // want `invalid offset ret\+1\(FP\); expected ret\+0\(FP\)`
|
||||
MOVD R3, r+0(FP) // want `unknown variable r; offset 0 is ret\+0\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-9
|
||||
MOVD x+0(FP), R3
|
||||
MOVB R3, ret+8(FP)
|
||||
MOVH R3, ret+8(FP) // want `invalid MOVH of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVW R3, ret+8(FP) // want `invalid MOVW of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVD R3, ret+8(FP) // want `invalid MOVD of ret\+8\(FP\); byte is 1-byte value`
|
||||
MOVB R3, ret+7(FP) // want `invalid offset ret\+7\(FP\); expected ret\+8\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-41
|
||||
MOVB x+0(FP), R3
|
||||
MOVD R3, r1+8(FP)
|
||||
MOVH R3, r2+16(FP)
|
||||
MOVD R3, r3+24(FP)
|
||||
MOVD R3, r3_base+24(FP)
|
||||
MOVD R3, r3_len+32(FP)
|
||||
MOVB R3, r4+40(FP)
|
||||
MOVW R3, r1+8(FP) // want `invalid MOVW of r1\+8\(FP\); int is 8-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-8
|
||||
RET // want `RET without writing to 8-byte ret\+0\(FP\)`
|
||||
164
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm8.s
generated
vendored
Normal file
164
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/testdata/src/a/asm8.s
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// +build mipsle
|
||||
|
||||
TEXT ·arg1(SB),0,$0-2
|
||||
MOVB x+0(FP), R1
|
||||
MOVBU y+1(FP), R2
|
||||
MOVH x+0(FP), R1 // want `\[mipsle\] arg1: invalid MOVH of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVHU y+1(FP), R1 // want `invalid MOVHU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int8 is 1-byte value`
|
||||
MOVWU y+1(FP), R1 // want `invalid MOVWU of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVW y+1(FP), R1 // want `invalid MOVW of y\+1\(FP\); uint8 is 1-byte value`
|
||||
MOVB x+1(FP), R1 // want `invalid offset x\+1\(FP\); expected x\+0\(FP\)`
|
||||
MOVBU y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+1\(FP\)`
|
||||
MOVB 8(R29), R1 // want `8\(R29\) should be x\+0\(FP\)`
|
||||
MOVB 9(R29), R1 // want `9\(R29\) should be y\+1\(FP\)`
|
||||
MOVB 10(R29), R1 // want `use of 10\(R29\) points beyond argument frame`
|
||||
RET
|
||||
|
||||
TEXT ·arg2(SB),0,$0-4
|
||||
MOVBU x+0(FP), R1 // want `arg2: invalid MOVBU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVB y+2(FP), R1 // want `invalid MOVB of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHU x+0(FP), R1
|
||||
MOVH y+2(FP), R2
|
||||
MOVWU x+0(FP), R1 // want `invalid MOVWU of x\+0\(FP\); int16 is 2-byte value`
|
||||
MOVW y+2(FP), R1 // want `invalid MOVW of y\+2\(FP\); uint16 is 2-byte value`
|
||||
MOVHU x+2(FP), R1 // want `invalid offset x\+2\(FP\); expected x\+0\(FP\)`
|
||||
MOVH y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+2\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg4(SB),0,$0-2 // want `arg4: wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int32 is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint32 is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVW y+4(FP), R1
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·arg8(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-16`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVB y+8(FP), R2 // want `invalid MOVB of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int64 is 8-byte value`
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); uint64 is 8-byte value`
|
||||
MOVW x+0(FP), R1 // want `invalid MOVW of x\+0\(FP\); int64 is 8-byte value containing x_lo\+0\(FP\) and x_hi\+4\(FP\)`
|
||||
MOVW x_lo+0(FP), R1
|
||||
MOVW x_hi+4(FP), R1
|
||||
MOVW y+8(FP), R1 // want `invalid MOVW of y\+8\(FP\); uint64 is 8-byte value containing y_lo\+8\(FP\) and y_hi\+12\(FP\)`
|
||||
MOVW y_lo+8(FP), R1
|
||||
MOVW y_hi+12(FP), R1
|
||||
RET
|
||||
|
||||
TEXT ·argint(SB),0,$0-2 // want `wrong argument size 2; expected \$\.\.\.-8`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); int is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); int is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); uint is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVW y+4(FP), R1
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argptr(SB),7,$0-2 // want `wrong argument size 2; expected \$\.\.\.-20`
|
||||
MOVB x+0(FP), R1 // want `invalid MOVB of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVB y+4(FP), R2 // want `invalid MOVB of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); \*byte is 4-byte value`
|
||||
MOVH y+4(FP), R1 // want `invalid MOVH of y\+4\(FP\); \*byte is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVW y+4(FP), R1
|
||||
MOVW x+4(FP), R1 // want `invalid offset x\+4\(FP\); expected x\+0\(FP\)`
|
||||
MOVW y+2(FP), R1 // want `invalid offset y\+2\(FP\); expected y\+4\(FP\)`
|
||||
MOVH c+8(FP), R1 // want `invalid MOVH of c\+8\(FP\); chan int is 4-byte value`
|
||||
MOVH m+12(FP), R1 // want `invalid MOVH of m\+12\(FP\); map\[int\]int is 4-byte value`
|
||||
MOVH f+16(FP), R1 // want `invalid MOVH of f\+16\(FP\); func\(\) is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·argstring(SB),0,$16 // want `wrong argument size 0; expected \$\.\.\.-16`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); string base is 4-byte value`
|
||||
MOVW x_base+0(FP), R1
|
||||
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), R1 // want `invalid MOVH of x_len\+4\(FP\); string len is 4-byte value`
|
||||
MOVW x_len+4(FP), R1
|
||||
MOVW y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+8\(FP\)`
|
||||
MOVW y_len+4(FP), R1 // want `invalid offset y_len\+4\(FP\); expected y_len\+12\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argslice(SB),0,$24 // want `wrong argument size 0; expected \$\.\.\.-24`
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVH x_base+0(FP), R1 // want `invalid MOVH of x_base\+0\(FP\); slice base is 4-byte value`
|
||||
MOVW x_base+0(FP), R1
|
||||
MOVH x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVW x_len+0(FP), R1 // want `invalid offset x_len\+0\(FP\); expected x_len\+4\(FP\)`
|
||||
MOVH x_len+4(FP), R1 // want `invalid MOVH of x_len\+4\(FP\); slice len is 4-byte value`
|
||||
MOVW x_len+4(FP), R1
|
||||
MOVH x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVW x_cap+0(FP), R1 // want `invalid offset x_cap\+0\(FP\); expected x_cap\+8\(FP\)`
|
||||
MOVH x_cap+8(FP), R1 // want `invalid MOVH of x_cap\+8\(FP\); slice cap is 4-byte value`
|
||||
MOVW x_cap+8(FP), R1
|
||||
MOVW y+0(FP), R1 // want `invalid offset y\+0\(FP\); expected y\+12\(FP\)`
|
||||
MOVW y_len+4(FP), R1 // want `invalid offset y_len\+4\(FP\); expected y_len\+16\(FP\)`
|
||||
MOVW y_cap+8(FP), R1 // want `invalid offset y_cap\+8\(FP\); expected y_cap\+20\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·argiface(SB),0,$0-16
|
||||
MOVH x+0(FP), R1 // want `invalid MOVH of x\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x+0(FP), R1
|
||||
MOVH x_type+0(FP), R1 // want `invalid MOVH of x_type\+0\(FP\); interface type is 4-byte value`
|
||||
MOVW x_type+0(FP), R1
|
||||
MOVQ x_itable+0(FP), R1 // want `unknown variable x_itable; offset 0 is x_type\+0\(FP\)`
|
||||
MOVQ x_itable+1(FP), R1 // want `unknown variable x_itable; offset 1 is x_type\+0\(FP\)`
|
||||
MOVH x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVW x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVQ x_data+0(FP), R1 // want `invalid offset x_data\+0\(FP\); expected x_data\+4\(FP\)`
|
||||
MOVH x_data+4(FP), R1 // want `invalid MOVH of x_data\+4\(FP\); interface data is 4-byte value`
|
||||
MOVW x_data+4(FP), R1
|
||||
MOVH y+8(FP), R1 // want `invalid MOVH of y\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y+8(FP), R1
|
||||
MOVH y_itable+8(FP), R1 // want `invalid MOVH of y_itable\+8\(FP\); interface itable is 4-byte value`
|
||||
MOVW y_itable+8(FP), R1
|
||||
MOVW y_type+8(FP), AX // want `unknown variable y_type; offset 8 is y_itable\+8\(FP\)`
|
||||
MOVH y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVW y_data+8(FP), AX // want `invalid offset y_data\+8\(FP\); expected y_data\+12\(FP\)`
|
||||
MOVH y_data+12(FP), AX // want `invalid MOVH of y_data\+12\(FP\); interface data is 4-byte value`
|
||||
MOVW y_data+12(FP), AX
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-5
|
||||
MOVW x+0(FP), R1
|
||||
MOVB R1, ret+4(FP)
|
||||
MOVH R1, ret+4(FP) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnbyte(SB),0,$0-5
|
||||
MOVW x+0(FP), R1
|
||||
MOVB R1, ret+4(FP)
|
||||
MOVH R1, ret+4(FP) // want `invalid MOVH of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVW R1, ret+4(FP) // want `invalid MOVW of ret\+4\(FP\); byte is 1-byte value`
|
||||
MOVB R1, ret+3(FP) // want `invalid offset ret\+3\(FP\); expected ret\+4\(FP\)`
|
||||
RET
|
||||
|
||||
TEXT ·returnnamed(SB),0,$0-21
|
||||
MOVB x+0(FP), AX
|
||||
MOVW R1, r1+4(FP)
|
||||
MOVH R1, r2+8(FP)
|
||||
MOVW R1, r3+12(FP)
|
||||
MOVW R1, r3_base+12(FP)
|
||||
MOVW R1, r3_len+16(FP)
|
||||
MOVB R1, r4+20(FP)
|
||||
MOVB R1, r1+4(FP) // want `invalid MOVB of r1\+4\(FP\); int is 4-byte value`
|
||||
RET
|
||||
|
||||
TEXT ·returnintmissing(SB),0,$0-4
|
||||
RET // want `RET without writing to 4-byte ret\+0\(FP\)`
|
||||
68
vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go
generated
vendored
Normal file
68
vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2013 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 assign defines an Analyzer that detects useless assignments.
|
||||
package assign
|
||||
|
||||
// TODO(adonovan): check also for assignments to struct fields inside
|
||||
// methods that are on T instead of *T.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for useless assignments
|
||||
|
||||
This checker reports assignments of the form x = x or a[i] = a[i].
|
||||
These are almost always useless, and even when they aren't they are
|
||||
usually a mistake.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "assign",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
stmt := n.(*ast.AssignStmt)
|
||||
if stmt.Tok != token.ASSIGN {
|
||||
return // ignore :=
|
||||
}
|
||||
if len(stmt.Lhs) != len(stmt.Rhs) {
|
||||
// If LHS and RHS have different cardinality, they can't be the same.
|
||||
return
|
||||
}
|
||||
for i, lhs := range stmt.Lhs {
|
||||
rhs := stmt.Rhs[i]
|
||||
if analysisutil.HasSideEffects(pass.TypesInfo, lhs) ||
|
||||
analysisutil.HasSideEffects(pass.TypesInfo, rhs) {
|
||||
continue // expressions may not be equal
|
||||
}
|
||||
if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) {
|
||||
continue // short-circuit the heavy-weight gofmt check
|
||||
}
|
||||
le := analysisutil.Format(pass.Fset, lhs)
|
||||
re := analysisutil.Format(pass.Fset, rhs)
|
||||
if le == re {
|
||||
pass.Reportf(stmt.Pos(), "self-assignment of %s to %s", re, le)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/assign/assign_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/assign/assign_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 assign_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/assign"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, assign.Analyzer, "a")
|
||||
}
|
||||
31
vendor/golang.org/x/tools/go/analysis/passes/assign/testdata/src/a/a.go
generated
vendored
Normal file
31
vendor/golang.org/x/tools/go/analysis/passes/assign/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file contains tests for the useless-assignment checker.
|
||||
|
||||
package testdata
|
||||
|
||||
import "math/rand"
|
||||
|
||||
type ST struct {
|
||||
x int
|
||||
l []int
|
||||
}
|
||||
|
||||
func (s *ST) SetX(x int, ch chan int) {
|
||||
// Accidental self-assignment; it should be "s.x = x"
|
||||
x = x // want "self-assignment of x to x"
|
||||
// Another mistake
|
||||
s.x = s.x // want "self-assignment of s.x to s.x"
|
||||
|
||||
s.l[0] = s.l[0] // want "self-assignment of s.l.0. to s.l.0."
|
||||
|
||||
// Bail on any potential side effects to avoid false positives
|
||||
s.l[num()] = s.l[num()]
|
||||
rng := rand.New(rand.NewSource(0))
|
||||
s.l[rng.Intn(len(s.l))] = s.l[rng.Intn(len(s.l))]
|
||||
s.l[<-ch] = s.l[<-ch]
|
||||
}
|
||||
|
||||
func num() int { return 2 }
|
||||
96
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go
generated
vendored
Normal file
96
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2013 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 atomic defines an Analyzer that checks for common mistakes
|
||||
// using the sync/atomic package.
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for common mistakes using the sync/atomic package
|
||||
|
||||
The atomic checker looks for assignment statements of the form:
|
||||
|
||||
x = atomic.AddUint64(&x, 1)
|
||||
|
||||
which are not atomic.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "atomic",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(node ast.Node) {
|
||||
n := node.(*ast.AssignStmt)
|
||||
if len(n.Lhs) != len(n.Rhs) {
|
||||
return
|
||||
}
|
||||
if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
|
||||
return
|
||||
}
|
||||
|
||||
for i, right := range n.Rhs {
|
||||
call, ok := right.(*ast.CallExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
sel, ok := call.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
pkgIdent, _ := sel.X.(*ast.Ident)
|
||||
pkgName, ok := pass.TypesInfo.Uses[pkgIdent].(*types.PkgName)
|
||||
if !ok || pkgName.Imported().Path() != "sync/atomic" {
|
||||
continue
|
||||
}
|
||||
|
||||
switch sel.Sel.Name {
|
||||
case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
|
||||
checkAtomicAddAssignment(pass, n.Lhs[i], call)
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// checkAtomicAddAssignment walks the atomic.Add* method calls checking
|
||||
// for assigning the return value to the same variable being used in the
|
||||
// operation
|
||||
func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.CallExpr) {
|
||||
if len(call.Args) != 2 {
|
||||
return
|
||||
}
|
||||
arg := call.Args[0]
|
||||
broken := false
|
||||
|
||||
gofmt := func(e ast.Expr) string { return analysisutil.Format(pass.Fset, e) }
|
||||
|
||||
if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
|
||||
broken = gofmt(left) == gofmt(uarg.X)
|
||||
} else if star, ok := left.(*ast.StarExpr); ok {
|
||||
broken = gofmt(star.X) == gofmt(arg)
|
||||
}
|
||||
|
||||
if broken {
|
||||
pass.Reportf(left.Pos(), "direct assignment to atomic value")
|
||||
}
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 atomic_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/atomic"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, atomic.Analyzer, "a")
|
||||
}
|
||||
62
vendor/golang.org/x/tools/go/analysis/passes/atomic/testdata/src/a/a.go
generated
vendored
Normal file
62
vendor/golang.org/x/tools/go/analysis/passes/atomic/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file contains tests for the atomic checker.
|
||||
|
||||
package a
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Counter uint64
|
||||
|
||||
func AtomicTests() {
|
||||
x := uint64(1)
|
||||
x = atomic.AddUint64(&x, 1) // want "direct assignment to atomic value"
|
||||
_, x = 10, atomic.AddUint64(&x, 1) // want "direct assignment to atomic value"
|
||||
x, _ = atomic.AddUint64(&x, 1), 10 // want "direct assignment to atomic value"
|
||||
|
||||
y := &x
|
||||
*y = atomic.AddUint64(y, 1) // want "direct assignment to atomic value"
|
||||
|
||||
var su struct{ Counter uint64 }
|
||||
su.Counter = atomic.AddUint64(&su.Counter, 1) // want "direct assignment to atomic value"
|
||||
z1 := atomic.AddUint64(&su.Counter, 1)
|
||||
_ = z1 // Avoid err "z declared and not used"
|
||||
|
||||
var sp struct{ Counter *uint64 }
|
||||
*sp.Counter = atomic.AddUint64(sp.Counter, 1) // want "direct assignment to atomic value"
|
||||
z2 := atomic.AddUint64(sp.Counter, 1)
|
||||
_ = z2 // Avoid err "z declared and not used"
|
||||
|
||||
au := []uint64{10, 20}
|
||||
au[0] = atomic.AddUint64(&au[0], 1) // want "direct assignment to atomic value"
|
||||
au[1] = atomic.AddUint64(&au[0], 1)
|
||||
|
||||
ap := []*uint64{&au[0], &au[1]}
|
||||
*ap[0] = atomic.AddUint64(ap[0], 1) // want "direct assignment to atomic value"
|
||||
*ap[1] = atomic.AddUint64(ap[0], 1)
|
||||
|
||||
x = atomic.AddUint64() // Used to make vet crash; now silently ignored.
|
||||
|
||||
{
|
||||
// A variable declaration creates a new variable in the current scope.
|
||||
x := atomic.AddUint64(&x, 1)
|
||||
|
||||
// Re-declaration assigns a new value.
|
||||
x, w := atomic.AddUint64(&x, 1), 10 // want "direct assignment to atomic value"
|
||||
_ = w
|
||||
}
|
||||
}
|
||||
|
||||
type T struct{}
|
||||
|
||||
func (T) AddUint64(addr *uint64, delta uint64) uint64 { return 0 }
|
||||
|
||||
func NonAtomic() {
|
||||
x := uint64(1)
|
||||
var atomic T
|
||||
x = atomic.AddUint64(&x, 1) // ok; not the imported pkg
|
||||
}
|
||||
214
vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go
generated
vendored
Normal file
214
vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go
generated
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright 2014 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 bools defines an Analyzer that detects common mistakes
|
||||
// involving boolean operators.
|
||||
package bools
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "bools",
|
||||
Doc: "check for common mistakes involving boolean operators",
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.BinaryExpr)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
e := n.(*ast.BinaryExpr)
|
||||
|
||||
var op boolOp
|
||||
switch e.Op {
|
||||
case token.LOR:
|
||||
op = or
|
||||
case token.LAND:
|
||||
op = and
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(adonovan): this reports n(n-1)/2 errors for an
|
||||
// expression e||...||e of depth n. Fix.
|
||||
// See https://github.com/golang/go/issues/28086.
|
||||
comm := op.commutativeSets(pass.TypesInfo, e)
|
||||
for _, exprs := range comm {
|
||||
op.checkRedundant(pass, exprs)
|
||||
op.checkSuspect(pass, exprs)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type boolOp struct {
|
||||
name string
|
||||
tok token.Token // token corresponding to this operator
|
||||
badEq token.Token // token corresponding to the equality test that should not be used with this operator
|
||||
}
|
||||
|
||||
var (
|
||||
or = boolOp{"or", token.LOR, token.NEQ}
|
||||
and = boolOp{"and", token.LAND, token.EQL}
|
||||
)
|
||||
|
||||
// commutativeSets returns all side effect free sets of
|
||||
// expressions in e that are connected by op.
|
||||
// For example, given 'a || b || f() || c || d' with the or op,
|
||||
// commutativeSets returns {{b, a}, {d, c}}.
|
||||
func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr) [][]ast.Expr {
|
||||
exprs := op.split(e)
|
||||
|
||||
// Partition the slice of expressions into commutative sets.
|
||||
i := 0
|
||||
var sets [][]ast.Expr
|
||||
for j := 0; j <= len(exprs); j++ {
|
||||
if j == len(exprs) || hasSideEffects(info, exprs[j]) {
|
||||
if i < j {
|
||||
sets = append(sets, exprs[i:j])
|
||||
}
|
||||
i = j + 1
|
||||
}
|
||||
}
|
||||
|
||||
return sets
|
||||
}
|
||||
|
||||
// checkRedundant checks for expressions of the form
|
||||
// e && e
|
||||
// e || e
|
||||
// Exprs must contain only side effect free expressions.
|
||||
func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) {
|
||||
seen := make(map[string]bool)
|
||||
for _, e := range exprs {
|
||||
efmt := analysisutil.Format(pass.Fset, e)
|
||||
if seen[efmt] {
|
||||
pass.Reportf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
|
||||
} else {
|
||||
seen[efmt] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkSuspect checks for expressions of the form
|
||||
// x != c1 || x != c2
|
||||
// x == c1 && x == c2
|
||||
// where c1 and c2 are constant expressions.
|
||||
// If c1 and c2 are the same then it's redundant;
|
||||
// if c1 and c2 are different then it's always true or always false.
|
||||
// Exprs must contain only side effect free expressions.
|
||||
func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) {
|
||||
// seen maps from expressions 'x' to equality expressions 'x != c'.
|
||||
seen := make(map[string]string)
|
||||
|
||||
for _, e := range exprs {
|
||||
bin, ok := e.(*ast.BinaryExpr)
|
||||
if !ok || bin.Op != op.badEq {
|
||||
continue
|
||||
}
|
||||
|
||||
// In order to avoid false positives, restrict to cases
|
||||
// in which one of the operands is constant. We're then
|
||||
// interested in the other operand.
|
||||
// In the rare case in which both operands are constant
|
||||
// (e.g. runtime.GOOS and "windows"), we'll only catch
|
||||
// mistakes if the LHS is repeated, which is how most
|
||||
// code is written.
|
||||
var x ast.Expr
|
||||
switch {
|
||||
case pass.TypesInfo.Types[bin.Y].Value != nil:
|
||||
x = bin.X
|
||||
case pass.TypesInfo.Types[bin.X].Value != nil:
|
||||
x = bin.Y
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
// e is of the form 'x != c' or 'x == c'.
|
||||
xfmt := analysisutil.Format(pass.Fset, x)
|
||||
efmt := analysisutil.Format(pass.Fset, e)
|
||||
if prev, found := seen[xfmt]; found {
|
||||
// checkRedundant handles the case in which efmt == prev.
|
||||
if efmt != prev {
|
||||
pass.Reportf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
|
||||
}
|
||||
} else {
|
||||
seen[xfmt] = efmt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hasSideEffects reports whether evaluation of e has side effects.
|
||||
func hasSideEffects(info *types.Info, e ast.Expr) bool {
|
||||
safe := true
|
||||
ast.Inspect(e, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
typVal := info.Types[n.Fun]
|
||||
switch {
|
||||
case typVal.IsType():
|
||||
// Type conversion, which is safe.
|
||||
case typVal.IsBuiltin():
|
||||
// Builtin func, conservatively assumed to not
|
||||
// be safe for now.
|
||||
safe = false
|
||||
return false
|
||||
default:
|
||||
// A non-builtin func or method call.
|
||||
// Conservatively assume that all of them have
|
||||
// side effects for now.
|
||||
safe = false
|
||||
return false
|
||||
}
|
||||
case *ast.UnaryExpr:
|
||||
if n.Op == token.ARROW {
|
||||
safe = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return !safe
|
||||
}
|
||||
|
||||
// split returns a slice of all subexpressions in e that are connected by op.
|
||||
// For example, given 'a || (b || c) || d' with the or op,
|
||||
// split returns []{d, c, b, a}.
|
||||
func (op boolOp) split(e ast.Expr) (exprs []ast.Expr) {
|
||||
for {
|
||||
e = unparen(e)
|
||||
if b, ok := e.(*ast.BinaryExpr); ok && b.Op == op.tok {
|
||||
exprs = append(exprs, op.split(b.Y)...)
|
||||
e = b.X
|
||||
} else {
|
||||
exprs = append(exprs, e)
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// unparen returns e with any enclosing parentheses stripped.
|
||||
func unparen(e ast.Expr) ast.Expr {
|
||||
for {
|
||||
p, ok := e.(*ast.ParenExpr)
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
e = p.X
|
||||
}
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/bools/bools_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/bools/bools_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 bools_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/bools"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, bools.Analyzer, "a")
|
||||
}
|
||||
137
vendor/golang.org/x/tools/go/analysis/passes/bools/testdata/src/a/a.go
generated
vendored
Normal file
137
vendor/golang.org/x/tools/go/analysis/passes/bools/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This file contains tests for the bool checker.
|
||||
|
||||
package a
|
||||
|
||||
import "io"
|
||||
|
||||
type T int
|
||||
|
||||
func (t T) Foo() int { return int(t) }
|
||||
|
||||
type FT func() int
|
||||
|
||||
var S []int
|
||||
|
||||
func RatherStupidConditions() {
|
||||
var f, g func() int
|
||||
if f() == 0 || f() == 0 { // OK f might have side effects
|
||||
}
|
||||
var t T
|
||||
_ = t.Foo() == 2 || t.Foo() == 2 // OK Foo might have side effects
|
||||
if v, w := f(), g(); v == w || v == w { // want `redundant or: v == w \|\| v == w`
|
||||
}
|
||||
_ = f == nil || f == nil // want `redundant or: f == nil \|\| f == nil`
|
||||
|
||||
var B byte
|
||||
_ = B == byte(1) || B == byte(1) // want `redundant or: B == byte\(1\) \|\| B == byte\(1\)`
|
||||
_ = t == T(2) || t == T(2) // want `redundant or: t == T\(2\) \|\| t == T\(2\)`
|
||||
_ = FT(f) == nil || FT(f) == nil // want `redundant or: FT\(f\) == nil \|\| FT\(f\) == nil`
|
||||
|
||||
_ = (func() int)(f) == nil || (func() int)(f) == nil // want `redundant or: \(func\(\) int\)\(f\) == nil \|\| \(func\(\) int\)\(f\) == nil`
|
||||
_ = append(S, 3) == nil || append(S, 3) == nil // OK append has side effects
|
||||
|
||||
var namedFuncVar FT
|
||||
_ = namedFuncVar() == namedFuncVar() // OK still func calls
|
||||
|
||||
var c chan int
|
||||
_ = 0 == <-c || 0 == <-c // OK subsequent receives may yield different values
|
||||
for i, j := <-c, <-c; i == j || i == j; i, j = <-c, <-c { // want `redundant or: i == j \|\| i == j`
|
||||
}
|
||||
|
||||
var i, j, k int
|
||||
_ = i+1 == 1 || i+1 == 1 // want `redundant or: i\+1 == 1 \|\| i\+1 == 1`
|
||||
_ = i == 1 || j+1 == i || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
// The various r.* patterns are intended to match duplicate
|
||||
// diagnostics reported for the same underlying problem.
|
||||
// See github.com/golang/go/issues/28086.
|
||||
// TODO(adonovan): fix the checker.
|
||||
|
||||
_ = i == 1 || i == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
|
||||
_ = i == 1 || f() == 1 || i == 1 // OK f may alter i as a side effect
|
||||
_ = f() == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
// Test partition edge cases
|
||||
_ = f() == 1 || i == 1 || i == 1 || j == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
|
||||
_ = f() == 1 || j == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || f() == 1 || i == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || i == 1 || f() == 1 || i == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
|
||||
_ = i == 1 || i == 1 || j == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
|
||||
_ = j == 1 || i == 1 || i == 1 || f() == 1 // want `redundant or: i == 1 \|\| i == 1` `r.*`
|
||||
_ = i == 1 || f() == 1 || f() == 1 || i == 1
|
||||
|
||||
_ = i == 1 || (i == 1 || i == 2) // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || (f() == 1 || i == 1) // OK f may alter i as a side effect
|
||||
_ = i == 1 || (i == 1 || f() == 1) // want `redundant or: i == 1 \|\| i == 1`
|
||||
_ = i == 1 || (i == 2 || (i == 1 || i == 3)) // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
var a, b bool
|
||||
_ = i == 1 || (a || (i == 1 || b)) // want `redundant or: i == 1 \|\| i == 1`
|
||||
|
||||
// Check that all redundant ors are flagged
|
||||
_ = j == 0 ||
|
||||
i == 1 ||
|
||||
f() == 1 ||
|
||||
j == 0 || // want `redundant or: j == 0 \|\| j == 0` `r.*`
|
||||
i == 1 || // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*` `r.*`
|
||||
i == 1 || // want `redundant or: i == 1 \|\| i == 1` `r.*` `r.*`
|
||||
i == 1 ||
|
||||
j == 0 ||
|
||||
k == 0
|
||||
|
||||
_ = i == 1*2*3 || i == 1*2*3 // want `redundant or: i == 1\*2\*3 \|\| i == 1\*2\*3`
|
||||
|
||||
// These test that redundant, suspect expressions do not trigger multiple errors.
|
||||
_ = i != 0 || i != 0 // want `redundant or: i != 0 \|\| i != 0`
|
||||
_ = i == 0 && i == 0 // want `redundant and: i == 0 && i == 0`
|
||||
|
||||
// and is dual to or; check the basics and
|
||||
// let the or tests pull the rest of the weight.
|
||||
_ = 0 != <-c && 0 != <-c // OK subsequent receives may yield different values
|
||||
_ = f() != 0 && f() != 0 // OK f might have side effects
|
||||
_ = f != nil && f != nil // want `redundant and: f != nil && f != nil`
|
||||
_ = i != 1 && i != 1 && f() != 1 // want `redundant and: i != 1 && i != 1` `r.*`
|
||||
_ = i != 1 && f() != 1 && i != 1 // OK f may alter i as a side effect
|
||||
_ = f() != 1 && i != 1 && i != 1 // want `redundant and: i != 1 && i != 1`
|
||||
}
|
||||
|
||||
func RoyallySuspectConditions() {
|
||||
var i, j int
|
||||
|
||||
_ = i == 0 || i == 1 // OK
|
||||
_ = i != 0 || i != 1 // want `suspect or: i != 0 \|\| i != 1`
|
||||
_ = i != 0 || 1 != i // want `suspect or: i != 0 \|\| 1 != i`
|
||||
_ = 0 != i || 1 != i // want `suspect or: 0 != i \|\| 1 != i`
|
||||
_ = 0 != i || i != 1 // want `suspect or: 0 != i \|\| i != 1`
|
||||
|
||||
_ = (0 != i) || i != 1 // want `suspect or: 0 != i \|\| i != 1`
|
||||
|
||||
_ = i+3 != 7 || j+5 == 0 || i+3 != 9 // want `suspect or: i\+3 != 7 \|\| i\+3 != 9`
|
||||
|
||||
_ = i != 0 || j == 0 || i != 1 // want `suspect or: i != 0 \|\| i != 1`
|
||||
|
||||
_ = i != 0 || i != 1<<4 // want `suspect or: i != 0 \|\| i != 1<<4`
|
||||
|
||||
_ = i != 0 || j != 0
|
||||
_ = 0 != i || 0 != j
|
||||
|
||||
var s string
|
||||
_ = s != "one" || s != "the other" // want `suspect or: s != .one. \|\| s != .the other.`
|
||||
|
||||
_ = "et" != "alii" || "et" != "cetera" // want `suspect or: .et. != .alii. \|\| .et. != .cetera.`
|
||||
_ = "me gustas" != "tu" || "le gustas" != "tu" // OK we could catch this case, but it's not worth the code
|
||||
|
||||
var err error
|
||||
_ = err != nil || err != io.EOF // TODO catch this case?
|
||||
|
||||
// Sanity check and.
|
||||
_ = i != 0 && i != 1 // OK
|
||||
_ = i == 0 && i == 1 // want `suspect and: i == 0 && i == 1`
|
||||
_ = i == 0 && 1 == i // want `suspect and: i == 0 && 1 == i`
|
||||
_ = 0 == i && 1 == i // want `suspect and: 0 == i && 1 == i`
|
||||
_ = 0 == i && i == 1 // want `suspect and: 0 == i && i == 1`
|
||||
}
|
||||
117
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa.go
generated
vendored
Normal file
117
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// 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 buildssa defines an Analyzer that constructs the SSA
|
||||
// representation of an error-free package and returns the set of all
|
||||
// functions within it. It does not report any diagnostics itself but
|
||||
// may be used as an input to other analyzers.
|
||||
//
|
||||
// THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.
|
||||
package buildssa
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildssa",
|
||||
Doc: "build SSA-form IR for later passes",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(SSA)),
|
||||
}
|
||||
|
||||
// SSA provides SSA-form intermediate representation for all the
|
||||
// non-blank source functions in the current package.
|
||||
type SSA struct {
|
||||
Pkg *ssa.Package
|
||||
SrcFuncs []*ssa.Function
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// Plundered from ssautil.BuildPackage.
|
||||
|
||||
// We must create a new Program for each Package because the
|
||||
// analysis API provides no place to hang a Program shared by
|
||||
// all Packages. Consequently, SSA Packages and Functions do not
|
||||
// have a canonical representation across an analysis session of
|
||||
// multiple packages. This is unlikely to be a problem in
|
||||
// practice because the analysis API essentially forces all
|
||||
// packages to be analysed independently, so any given call to
|
||||
// Analysis.Run on a package will see only SSA objects belonging
|
||||
// to a single Program.
|
||||
|
||||
// Some Analyzers may need GlobalDebug, in which case we'll have
|
||||
// to set it globally, but let's wait till we need it.
|
||||
mode := ssa.BuilderMode(0)
|
||||
|
||||
prog := ssa.NewProgram(pass.Fset, mode)
|
||||
|
||||
// Create SSA packages for all imports.
|
||||
// Order is not significant.
|
||||
created := make(map[*types.Package]bool)
|
||||
var createAll func(pkgs []*types.Package)
|
||||
createAll = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !created[p] {
|
||||
created[p] = true
|
||||
prog.CreatePackage(p, nil, nil, true)
|
||||
createAll(p.Imports())
|
||||
}
|
||||
}
|
||||
}
|
||||
createAll(pass.Pkg.Imports())
|
||||
|
||||
// Create and build the primary package.
|
||||
ssapkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)
|
||||
ssapkg.Build()
|
||||
|
||||
// Compute list of source functions, including literals,
|
||||
// in source order.
|
||||
var funcs []*ssa.Function
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if fdecl, ok := decl.(*ast.FuncDecl); ok {
|
||||
|
||||
// SSA will not build a Function
|
||||
// for a FuncDecl named blank.
|
||||
// That's arguably too strict but
|
||||
// relaxing it would break uniqueness of
|
||||
// names of package members.
|
||||
if fdecl.Name.Name == "_" {
|
||||
continue
|
||||
}
|
||||
|
||||
// (init functions have distinct Func
|
||||
// objects named "init" and distinct
|
||||
// ssa.Functions named "init#1", ...)
|
||||
|
||||
fn := pass.TypesInfo.Defs[fdecl.Name].(*types.Func)
|
||||
if fn == nil {
|
||||
panic(fn)
|
||||
}
|
||||
|
||||
f := ssapkg.Prog.FuncValue(fn)
|
||||
if f == nil {
|
||||
panic(fn)
|
||||
}
|
||||
|
||||
var addAnons func(f *ssa.Function)
|
||||
addAnons = func(f *ssa.Function) {
|
||||
funcs = append(funcs, f)
|
||||
for _, anon := range f.AnonFuncs {
|
||||
addAnons(anon)
|
||||
}
|
||||
}
|
||||
addAnons(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SSA{Pkg: ssapkg, SrcFuncs: funcs}, nil
|
||||
}
|
||||
29
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa_test.go
generated
vendored
Normal file
29
vendor/golang.org/x/tools/go/analysis/passes/buildssa/buildssa_test.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// 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 buildssa_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/buildssa"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
result := analysistest.Run(t, testdata, buildssa.Analyzer, "a")[0].Result
|
||||
|
||||
ssainfo := result.(*buildssa.SSA)
|
||||
got := fmt.Sprint(ssainfo.SrcFuncs)
|
||||
want := `[a.Fib (a.T).fib]`
|
||||
if got != want {
|
||||
t.Errorf("SSA.SrcFuncs = %s, want %s", got, want)
|
||||
for _, f := range ssainfo.SrcFuncs {
|
||||
f.WriteTo(os.Stderr)
|
||||
}
|
||||
}
|
||||
}
|
||||
16
vendor/golang.org/x/tools/go/analysis/passes/buildssa/testdata/src/a/a.go
generated
vendored
Normal file
16
vendor/golang.org/x/tools/go/analysis/passes/buildssa/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package a
|
||||
|
||||
func Fib(x int) int {
|
||||
if x < 2 {
|
||||
return x
|
||||
}
|
||||
return Fib(x-1) + Fib(x-2)
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
func (T) fib(x int) int { return Fib(x) }
|
||||
|
||||
func _() {
|
||||
print("hi")
|
||||
}
|
||||
159
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
generated
vendored
Normal file
159
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
// Copyright 2013 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 buildtag defines an Analyzer that checks build tags.
|
||||
package buildtag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "buildtag",
|
||||
Doc: "check that +build tags are well-formed and correctly located",
|
||||
Run: runBuildTag,
|
||||
}
|
||||
|
||||
func runBuildTag(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, f := range pass.Files {
|
||||
checkGoFile(pass, f)
|
||||
}
|
||||
for _, name := range pass.OtherFiles {
|
||||
if err := checkOtherFile(pass, name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkGoFile(pass *analysis.Pass, f *ast.File) {
|
||||
pastCutoff := false
|
||||
for _, group := range f.Comments {
|
||||
// A +build comment is ignored after or adjoining the package declaration.
|
||||
if group.End()+1 >= f.Package {
|
||||
pastCutoff = true
|
||||
}
|
||||
|
||||
// "+build" is ignored within or after a /*...*/ comment.
|
||||
if !strings.HasPrefix(group.List[0].Text, "//") {
|
||||
pastCutoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Check each line of a //-comment.
|
||||
for _, c := range group.List {
|
||||
if !strings.Contains(c.Text, "+build") {
|
||||
continue
|
||||
}
|
||||
if err := checkLine(c.Text, pastCutoff); err != nil {
|
||||
pass.Reportf(c.Pos(), "%s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkOtherFile(pass *analysis.Pass, filename string) error {
|
||||
content, tf, err := analysisutil.ReadFile(pass.Fset, filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We must look at the raw lines, as build tags may appear in non-Go
|
||||
// files such as assembly files.
|
||||
lines := bytes.SplitAfter(content, nl)
|
||||
|
||||
// Determine cutpoint where +build comments are no longer valid.
|
||||
// They are valid in leading // comments in the file followed by
|
||||
// a blank line.
|
||||
//
|
||||
// This must be done as a separate pass because of the
|
||||
// requirement that the comment be followed by a blank line.
|
||||
var cutoff int
|
||||
for i, line := range lines {
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, slashSlash) {
|
||||
if len(line) > 0 {
|
||||
break
|
||||
}
|
||||
cutoff = i
|
||||
}
|
||||
}
|
||||
|
||||
for i, line := range lines {
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, slashSlash) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Contains(line, []byte("+build")) {
|
||||
continue
|
||||
}
|
||||
if err := checkLine(string(line), i >= cutoff); err != nil {
|
||||
pass.Reportf(analysisutil.LineStart(tf, i+1), "%s", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkLine checks a line that starts with "//" and contains "+build".
|
||||
func checkLine(line string, pastCutoff bool) error {
|
||||
line = strings.TrimPrefix(line, "//")
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if strings.HasPrefix(line, "+build") {
|
||||
fields := strings.Fields(line)
|
||||
if fields[0] != "+build" {
|
||||
// Comment is something like +buildasdf not +build.
|
||||
return fmt.Errorf("possible malformed +build comment")
|
||||
}
|
||||
if pastCutoff {
|
||||
return fmt.Errorf("+build comment must appear before package clause and be followed by a blank line")
|
||||
}
|
||||
if err := checkArguments(fields); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Comment with +build but not at beginning.
|
||||
if !pastCutoff {
|
||||
return fmt.Errorf("possible malformed +build comment")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkArguments(fields []string) error {
|
||||
// The original version of this checker in vet could examine
|
||||
// files with malformed build tags that would cause the file to
|
||||
// be always ignored by "go build". However, drivers for the new
|
||||
// analysis API will analyze only the files selected to form a
|
||||
// package, so these checks will never fire.
|
||||
// TODO(adonovan): rethink this.
|
||||
|
||||
for _, arg := range fields[1:] {
|
||||
for _, elem := range strings.Split(arg, ",") {
|
||||
if strings.HasPrefix(elem, "!!") {
|
||||
return fmt.Errorf("invalid double negative in build constraint: %s", arg)
|
||||
}
|
||||
elem = strings.TrimPrefix(elem, "!")
|
||||
for _, c := range elem {
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||
return fmt.Errorf("invalid non-alphanumeric build constraint: %s", arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
nl = []byte("\n")
|
||||
slashSlash = []byte("//")
|
||||
)
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/buildtag/buildtag_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 buildtag_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/buildtag"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, buildtag.Analyzer, "a")
|
||||
}
|
||||
21
vendor/golang.org/x/tools/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
generated
vendored
Normal file
21
vendor/golang.org/x/tools/go/analysis/passes/buildtag/testdata/src/a/buildtag.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file contains tests for the buildtag checker.
|
||||
|
||||
// +builder // want `possible malformed \+build comment`
|
||||
// +build !ignore
|
||||
|
||||
// Mention +build // want `possible malformed \+build comment`
|
||||
|
||||
// +build nospace // want "build comment must appear before package clause and be followed by a blank line"
|
||||
package a
|
||||
|
||||
// +build toolate // want "build comment must appear before package clause and be followed by a blank line$"
|
||||
|
||||
var _ = 3
|
||||
|
||||
var _ = `
|
||||
// +build notacomment
|
||||
`
|
||||
390
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go
generated
vendored
Normal file
390
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go
generated
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
// Copyright 2015 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 cgocall defines an Analyzer that detects some violations of
|
||||
// the cgo pointer passing rules.
|
||||
package cgocall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
const debug = false
|
||||
|
||||
const doc = `detect some violations of the cgo pointer passing rules
|
||||
|
||||
Check for invalid cgo pointer passing.
|
||||
This looks for code that uses cgo to call C code passing values
|
||||
whose types are almost always invalid according to the cgo pointer
|
||||
sharing rules.
|
||||
Specifically, it warns about attempts to pass a Go chan, map, func,
|
||||
or slice to C, either directly, or via a pointer, array, or struct.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "cgocall",
|
||||
Doc: doc,
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
if imports(pass.Pkg, "runtime/cgo") == nil {
|
||||
return nil, nil // doesn't use cgo
|
||||
}
|
||||
|
||||
cgofiles, info, err := typeCheckCgoSourceFiles(pass.Fset, pass.Pkg, pass.Files, pass.TypesInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, f := range cgofiles {
|
||||
checkCgo(pass.Fset, f, info, pass.Reportf)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func checkCgo(fset *token.FileSet, f *ast.File, info *types.Info, reportf func(token.Pos, string, ...interface{})) {
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
call, ok := n.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// Is this a C.f() call?
|
||||
var name string
|
||||
if sel, ok := analysisutil.Unparen(call.Fun).(*ast.SelectorExpr); ok {
|
||||
if id, ok := sel.X.(*ast.Ident); ok && id.Name == "C" {
|
||||
name = sel.Sel.Name
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
return true // not a call we need to check
|
||||
}
|
||||
|
||||
// A call to C.CBytes passes a pointer but is always safe.
|
||||
if name == "CBytes" {
|
||||
return true
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("%s: call to C.%s", fset.Position(call.Lparen), name)
|
||||
}
|
||||
|
||||
for _, arg := range call.Args {
|
||||
if !typeOKForCgoCall(cgoBaseType(info, arg), make(map[types.Type]bool)) {
|
||||
reportf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
|
||||
break
|
||||
}
|
||||
|
||||
// Check for passing the address of a bad type.
|
||||
if conv, ok := arg.(*ast.CallExpr); ok && len(conv.Args) == 1 &&
|
||||
isUnsafePointer(info, conv.Fun) {
|
||||
arg = conv.Args[0]
|
||||
}
|
||||
if u, ok := arg.(*ast.UnaryExpr); ok && u.Op == token.AND {
|
||||
if !typeOKForCgoCall(cgoBaseType(info, u.X), make(map[types.Type]bool)) {
|
||||
reportf(arg.Pos(), "possibly passing Go type with embedded pointer to C")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// typeCheckCgoSourceFiles returns type-checked syntax trees for the raw
|
||||
// cgo files of a package (those that import "C"). Such files are not
|
||||
// Go, so there may be gaps in type information around C.f references.
|
||||
//
|
||||
// This checker was initially written in vet to inpect raw cgo source
|
||||
// files using partial type information. However, Analyzers in the new
|
||||
// analysis API are presented with the type-checked, "cooked" Go ASTs
|
||||
// resulting from cgo-processing files, so we must choose between
|
||||
// working with the cooked file generated by cgo (which was tried but
|
||||
// proved fragile) or locating the raw cgo file (e.g. from //line
|
||||
// directives) and working with that, as we now do.
|
||||
//
|
||||
// Specifically, we must type-check the raw cgo source files (or at
|
||||
// least the subtrees needed for this analyzer) in an environment that
|
||||
// simulates the rest of the already type-checked package.
|
||||
//
|
||||
// For example, for each raw cgo source file in the original package,
|
||||
// such as this one:
|
||||
//
|
||||
// package p
|
||||
// import "C"
|
||||
// import "fmt"
|
||||
// type T int
|
||||
// const k = 3
|
||||
// var x, y = fmt.Println()
|
||||
// func f() { ... }
|
||||
// func g() { ... C.malloc(k) ... }
|
||||
// func (T) f(int) string { ... }
|
||||
//
|
||||
// we synthesize a new ast.File, shown below, that dot-imports the
|
||||
// orginal "cooked" package using a special name ("·this·"), so that all
|
||||
// references to package members resolve correctly. (References to
|
||||
// unexported names cause an "unexported" error, which we ignore.)
|
||||
//
|
||||
// To avoid shadowing names imported from the cooked package,
|
||||
// package-level declarations in the new source file are modified so
|
||||
// that they do not declare any names.
|
||||
// (The cgocall analysis is concerned with uses, not declarations.)
|
||||
// Specifically, type declarations are discarded;
|
||||
// all names in each var and const declaration are blanked out;
|
||||
// each method is turned into a regular function by turning
|
||||
// the receiver into the first parameter;
|
||||
// and all functions are renamed to "_".
|
||||
//
|
||||
// package p
|
||||
// import . "·this·" // declares T, k, x, y, f, g, T.f
|
||||
// import "C"
|
||||
// import "fmt"
|
||||
// const _ = 3
|
||||
// var _, _ = fmt.Println()
|
||||
// func _() { ... }
|
||||
// func _() { ... C.malloc(k) ... }
|
||||
// func _(T, int) string { ... }
|
||||
//
|
||||
// In this way, the raw function bodies and const/var initializer
|
||||
// expressions are preserved but refer to the "cooked" objects imported
|
||||
// from "·this·", and none of the transformed package-level declarations
|
||||
// actually declares anything. In the example above, the reference to k
|
||||
// in the argument of the call to C.malloc resolves to "·this·".k, which
|
||||
// has an accurate type.
|
||||
//
|
||||
// This approach could in principle be generalized to more complex
|
||||
// analyses on raw cgo files. One could synthesize a "C" package so that
|
||||
// C.f would resolve to "·this·"._C_func_f, for example. But we have
|
||||
// limited ourselves here to preserving function bodies and initializer
|
||||
// expressions since that is all that the cgocall analyzer needs.
|
||||
//
|
||||
func typeCheckCgoSourceFiles(fset *token.FileSet, pkg *types.Package, files []*ast.File, info *types.Info) ([]*ast.File, *types.Info, error) {
|
||||
const thispkg = "·this·"
|
||||
|
||||
// Which files are cgo files?
|
||||
var cgoFiles []*ast.File
|
||||
importMap := map[string]*types.Package{thispkg: pkg}
|
||||
for _, raw := range files {
|
||||
// If f is a cgo-generated file, Position reports
|
||||
// the original file, honoring //line directives.
|
||||
filename := fset.Position(raw.Pos()).Filename
|
||||
f, err := parser.ParseFile(fset, filename, nil, parser.Mode(0))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("can't parse raw cgo file: %v", err)
|
||||
}
|
||||
found := false
|
||||
for _, spec := range f.Imports {
|
||||
if spec.Path.Value == `"C"` {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue // not a cgo file
|
||||
}
|
||||
|
||||
// Record the original import map.
|
||||
for _, spec := range raw.Imports {
|
||||
path, _ := strconv.Unquote(spec.Path.Value)
|
||||
importMap[path] = imported(info, spec)
|
||||
}
|
||||
|
||||
// Add special dot-import declaration:
|
||||
// import . "·this·"
|
||||
var decls []ast.Decl
|
||||
decls = append(decls, &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ImportSpec{
|
||||
Name: &ast.Ident{Name: "."},
|
||||
Path: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: strconv.Quote(thispkg),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Transform declarations from the raw cgo file.
|
||||
for _, decl := range f.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch decl.Tok {
|
||||
case token.TYPE:
|
||||
// Discard type declarations.
|
||||
continue
|
||||
case token.IMPORT:
|
||||
// Keep imports.
|
||||
case token.VAR, token.CONST:
|
||||
// Blank the declared var/const names.
|
||||
for _, spec := range decl.Specs {
|
||||
spec := spec.(*ast.ValueSpec)
|
||||
for i := range spec.Names {
|
||||
spec.Names[i].Name = "_"
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
// Blank the declared func name.
|
||||
decl.Name.Name = "_"
|
||||
|
||||
// Turn a method receiver: func (T) f(P) R {...}
|
||||
// into regular parameter: func _(T, P) R {...}
|
||||
if decl.Recv != nil {
|
||||
var params []*ast.Field
|
||||
params = append(params, decl.Recv.List...)
|
||||
params = append(params, decl.Type.Params.List...)
|
||||
decl.Type.Params.List = params
|
||||
decl.Recv = nil
|
||||
}
|
||||
}
|
||||
decls = append(decls, decl)
|
||||
}
|
||||
f.Decls = decls
|
||||
if debug {
|
||||
format.Node(os.Stderr, fset, f) // debugging
|
||||
}
|
||||
cgoFiles = append(cgoFiles, f)
|
||||
}
|
||||
if cgoFiles == nil {
|
||||
return nil, nil, nil // nothing to do (can't happen?)
|
||||
}
|
||||
|
||||
// Type-check the synthetic files.
|
||||
tc := &types.Config{
|
||||
FakeImportC: true,
|
||||
Importer: importerFunc(func(path string) (*types.Package, error) {
|
||||
return importMap[path], nil
|
||||
}),
|
||||
// TODO(adonovan): Sizes should probably be provided by analysis.Pass.
|
||||
Sizes: types.SizesFor("gc", build.Default.GOARCH),
|
||||
Error: func(error) {}, // ignore errors (e.g. unused import)
|
||||
}
|
||||
|
||||
// It's tempting to record the new types in the
|
||||
// existing pass.TypesInfo, but we don't own it.
|
||||
altInfo := &types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
}
|
||||
tc.Check(pkg.Path(), fset, cgoFiles, altInfo)
|
||||
|
||||
return cgoFiles, altInfo, nil
|
||||
}
|
||||
|
||||
// cgoBaseType tries to look through type conversions involving
|
||||
// unsafe.Pointer to find the real type. It converts:
|
||||
// unsafe.Pointer(x) => x
|
||||
// *(*unsafe.Pointer)(unsafe.Pointer(&x)) => x
|
||||
func cgoBaseType(info *types.Info, arg ast.Expr) types.Type {
|
||||
switch arg := arg.(type) {
|
||||
case *ast.CallExpr:
|
||||
if len(arg.Args) == 1 && isUnsafePointer(info, arg.Fun) {
|
||||
return cgoBaseType(info, arg.Args[0])
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
call, ok := arg.X.(*ast.CallExpr)
|
||||
if !ok || len(call.Args) != 1 {
|
||||
break
|
||||
}
|
||||
// Here arg is *f(v).
|
||||
t := info.Types[call.Fun].Type
|
||||
if t == nil {
|
||||
break
|
||||
}
|
||||
ptr, ok := t.Underlying().(*types.Pointer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// Here arg is *(*p)(v)
|
||||
elem, ok := ptr.Elem().Underlying().(*types.Basic)
|
||||
if !ok || elem.Kind() != types.UnsafePointer {
|
||||
break
|
||||
}
|
||||
// Here arg is *(*unsafe.Pointer)(v)
|
||||
call, ok = call.Args[0].(*ast.CallExpr)
|
||||
if !ok || len(call.Args) != 1 {
|
||||
break
|
||||
}
|
||||
// Here arg is *(*unsafe.Pointer)(f(v))
|
||||
if !isUnsafePointer(info, call.Fun) {
|
||||
break
|
||||
}
|
||||
// Here arg is *(*unsafe.Pointer)(unsafe.Pointer(v))
|
||||
u, ok := call.Args[0].(*ast.UnaryExpr)
|
||||
if !ok || u.Op != token.AND {
|
||||
break
|
||||
}
|
||||
// Here arg is *(*unsafe.Pointer)(unsafe.Pointer(&v))
|
||||
return cgoBaseType(info, u.X)
|
||||
}
|
||||
|
||||
return info.Types[arg].Type
|
||||
}
|
||||
|
||||
// typeOKForCgoCall reports whether the type of arg is OK to pass to a
|
||||
// C function using cgo. This is not true for Go types with embedded
|
||||
// pointers. m is used to avoid infinite recursion on recursive types.
|
||||
func typeOKForCgoCall(t types.Type, m map[types.Type]bool) bool {
|
||||
if t == nil || m[t] {
|
||||
return true
|
||||
}
|
||||
m[t] = true
|
||||
switch t := t.Underlying().(type) {
|
||||
case *types.Chan, *types.Map, *types.Signature, *types.Slice:
|
||||
return false
|
||||
case *types.Pointer:
|
||||
return typeOKForCgoCall(t.Elem(), m)
|
||||
case *types.Array:
|
||||
return typeOKForCgoCall(t.Elem(), m)
|
||||
case *types.Struct:
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
if !typeOKForCgoCall(t.Field(i).Type(), m) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isUnsafePointer(info *types.Info, e ast.Expr) bool {
|
||||
t := info.Types[e].Type
|
||||
return t != nil && t.Underlying() == types.Typ[types.UnsafePointer]
|
||||
}
|
||||
|
||||
type importerFunc func(path string) (*types.Package, error)
|
||||
|
||||
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
|
||||
|
||||
// TODO(adonovan): make this a library function or method of Info.
|
||||
func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
|
||||
obj, ok := info.Implicits[spec]
|
||||
if !ok {
|
||||
obj = info.Defs[spec.Name] // renaming import
|
||||
}
|
||||
return obj.(*types.PkgName).Imported()
|
||||
}
|
||||
|
||||
// imports reports whether pkg has path among its direct imports.
|
||||
// It returns the imported package if so, or nil if not.
|
||||
// TODO(adonovan): move to analysisutil.
|
||||
func imports(pkg *types.Package, path string) *types.Package {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == path {
|
||||
return imp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 cgocall_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/cgocall"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, cgocall.Analyzer, "a", "b", "c")
|
||||
}
|
||||
71
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo.go
generated
vendored
Normal file
71
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
// This file contains tests for the cgo checker.
|
||||
|
||||
package a
|
||||
|
||||
// void f(void *ptr) {}
|
||||
import "C"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func CgoTests() {
|
||||
var c chan bool
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&c))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&c)) // want "embedded pointer"
|
||||
|
||||
var m map[string]string
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&m))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&m)) // want "embedded pointer"
|
||||
|
||||
var f func()
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&f))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&f)) // want "embedded pointer"
|
||||
|
||||
var s []int
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&s)) // want "embedded pointer"
|
||||
|
||||
var a [1][]int
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&a)) // want "embedded pointer"
|
||||
|
||||
var st struct{ f []int }
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&st)) // want "embedded pointer"
|
||||
|
||||
var st3 S
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st3))) // want "embedded pointer"
|
||||
C.f(unsafe.Pointer(&st3)) // want "embedded pointer"
|
||||
|
||||
// The following cases are OK.
|
||||
var i int
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&i)))
|
||||
C.f(unsafe.Pointer(&i))
|
||||
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&s[0])))
|
||||
C.f(unsafe.Pointer(&s[0]))
|
||||
|
||||
var a2 [1]int
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&a2)))
|
||||
C.f(unsafe.Pointer(&a2))
|
||||
|
||||
var st2 struct{ i int }
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st2)))
|
||||
C.f(unsafe.Pointer(&st2))
|
||||
|
||||
var st4 S2
|
||||
C.f(*(*unsafe.Pointer)(unsafe.Pointer(&st4)))
|
||||
C.f(unsafe.Pointer(&st4))
|
||||
|
||||
type cgoStruct struct{ p *cgoStruct }
|
||||
C.f(unsafe.Pointer(&cgoStruct{}))
|
||||
|
||||
C.CBytes([]byte("hello"))
|
||||
}
|
||||
|
||||
type S struct{ slice []int }
|
||||
|
||||
type S2 struct{ int int }
|
||||
21
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo3.go
generated
vendored
Normal file
21
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/a/cgo3.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2017 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 a
|
||||
|
||||
// The purpose of this inherited test is unclear.
|
||||
|
||||
import "C"
|
||||
|
||||
const x = 1
|
||||
|
||||
var a, b = 1, 2
|
||||
|
||||
func F() {
|
||||
}
|
||||
|
||||
func FAD(int, string) bool {
|
||||
C.malloc(3)
|
||||
return true
|
||||
}
|
||||
20
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/b/b.go
generated
vendored
Normal file
20
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
// Test the cgo checker on a file that doesn't use cgo, but has an
|
||||
// import named "C".
|
||||
|
||||
package b
|
||||
|
||||
import C "fmt"
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
var f func()
|
||||
C.Println(unsafe.Pointer(&f))
|
||||
|
||||
// Passing a pointer (via a slice), but C is fmt, not cgo.
|
||||
C.Println([]int{3})
|
||||
}
|
||||
14
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/c/c.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/go/analysis/passes/cgocall/testdata/src/c/c.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Test the cgo checker on a file that doesn't use cgo.
|
||||
|
||||
package c
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Passing a pointer (via the slice), but C isn't cgo.
|
||||
var _ = C.f(unsafe.Pointer(new([]int)))
|
||||
|
||||
var C struct{ f func(interface{}) int }
|
||||
108
vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go
generated
vendored
Normal file
108
vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2012 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 composite defines an Analyzer that checks for unkeyed
|
||||
// composite literals.
|
||||
package composite
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `checked for unkeyed composite literals
|
||||
|
||||
This analyzer reports a diagnostic for composite literals of struct
|
||||
types imported from another package that do not use the field-keyed
|
||||
syntax. Such literals are fragile because the addition of a new field
|
||||
(even if unexported) to the struct will cause compilation to fail.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "composites",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
var whitelist = true
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.BoolVar(&whitelist, "whitelist", whitelist, "use composite white list; for testing only")
|
||||
}
|
||||
|
||||
// runUnkeyedLiteral checks if a composite literal is a struct literal with
|
||||
// unkeyed fields.
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.CompositeLit)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
cl := n.(*ast.CompositeLit)
|
||||
|
||||
typ := pass.TypesInfo.Types[cl].Type
|
||||
if typ == nil {
|
||||
// cannot determine composite literals' type, skip it
|
||||
return
|
||||
}
|
||||
typeName := typ.String()
|
||||
if whitelist && unkeyedLiteral[typeName] {
|
||||
// skip whitelisted types
|
||||
return
|
||||
}
|
||||
under := typ.Underlying()
|
||||
for {
|
||||
ptr, ok := under.(*types.Pointer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
under = ptr.Elem().Underlying()
|
||||
}
|
||||
if _, ok := under.(*types.Struct); !ok {
|
||||
// skip non-struct composite literals
|
||||
return
|
||||
}
|
||||
if isLocalType(pass, typ) {
|
||||
// allow unkeyed locally defined composite literal
|
||||
return
|
||||
}
|
||||
|
||||
// check if the CompositeLit contains an unkeyed field
|
||||
allKeyValue := true
|
||||
for _, e := range cl.Elts {
|
||||
if _, ok := e.(*ast.KeyValueExpr); !ok {
|
||||
allKeyValue = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allKeyValue {
|
||||
// all the composite literal fields are keyed
|
||||
return
|
||||
}
|
||||
|
||||
pass.Reportf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func isLocalType(pass *analysis.Pass, typ types.Type) bool {
|
||||
switch x := typ.(type) {
|
||||
case *types.Struct:
|
||||
// struct literals are local types
|
||||
return true
|
||||
case *types.Pointer:
|
||||
return isLocalType(pass, x.Elem())
|
||||
case *types.Named:
|
||||
// names in package foo are local to foo_test too
|
||||
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
|
||||
}
|
||||
return false
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/composite/composite_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/composite/composite_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 composite_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/composite"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, composite.Analyzer, "a")
|
||||
}
|
||||
121
vendor/golang.org/x/tools/go/analysis/passes/composite/testdata/src/a/a.go
generated
vendored
Normal file
121
vendor/golang.org/x/tools/go/analysis/passes/composite/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// This file contains the test for untagged struct literals.
|
||||
|
||||
package a
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"image"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var Okay1 = []string{
|
||||
"Name",
|
||||
"Usage",
|
||||
"DefValue",
|
||||
}
|
||||
|
||||
var Okay2 = map[string]bool{
|
||||
"Name": true,
|
||||
"Usage": true,
|
||||
"DefValue": true,
|
||||
}
|
||||
|
||||
var Okay3 = struct {
|
||||
X string
|
||||
Y string
|
||||
Z string
|
||||
}{
|
||||
"Name",
|
||||
"Usage",
|
||||
"DefValue",
|
||||
}
|
||||
|
||||
var Okay4 = []struct {
|
||||
A int
|
||||
B int
|
||||
}{
|
||||
{1, 2},
|
||||
{3, 4},
|
||||
}
|
||||
|
||||
type MyStruct struct {
|
||||
X string
|
||||
Y string
|
||||
Z string
|
||||
}
|
||||
|
||||
var Okay5 = &MyStruct{
|
||||
"Name",
|
||||
"Usage",
|
||||
"DefValue",
|
||||
}
|
||||
|
||||
var Okay6 = []MyStruct{
|
||||
{"foo", "bar", "baz"},
|
||||
{"aa", "bb", "cc"},
|
||||
}
|
||||
|
||||
var Okay7 = []*MyStruct{
|
||||
{"foo", "bar", "baz"},
|
||||
{"aa", "bb", "cc"},
|
||||
}
|
||||
|
||||
// Testing is awkward because we need to reference things from a separate package
|
||||
// to trigger the warnings.
|
||||
|
||||
var goodStructLiteral = flag.Flag{
|
||||
Name: "Name",
|
||||
Usage: "Usage",
|
||||
}
|
||||
var badStructLiteral = flag.Flag{ // want "unkeyed fields"
|
||||
"Name",
|
||||
"Usage",
|
||||
nil, // Value
|
||||
"DefValue",
|
||||
}
|
||||
|
||||
var delta [3]rune
|
||||
|
||||
// SpecialCase is a named slice of CaseRange to test issue 9171.
|
||||
var goodNamedSliceLiteral = unicode.SpecialCase{
|
||||
{Lo: 1, Hi: 2, Delta: delta},
|
||||
unicode.CaseRange{Lo: 1, Hi: 2, Delta: delta},
|
||||
}
|
||||
var badNamedSliceLiteral = unicode.SpecialCase{
|
||||
{1, 2, delta}, // want "unkeyed fields"
|
||||
unicode.CaseRange{1, 2, delta}, // want "unkeyed fields"
|
||||
}
|
||||
|
||||
// ErrorList is a named slice, so no warnings should be emitted.
|
||||
var goodScannerErrorList = scanner.ErrorList{
|
||||
&scanner.Error{Msg: "foobar"},
|
||||
}
|
||||
var badScannerErrorList = scanner.ErrorList{
|
||||
&scanner.Error{token.Position{}, "foobar"}, // want "unkeyed fields"
|
||||
}
|
||||
|
||||
// Check whitelisted structs: if vet is run with --compositewhitelist=false,
|
||||
// this line triggers an error.
|
||||
var whitelistedPoint = image.Point{1, 2}
|
||||
|
||||
// Do not check type from unknown package.
|
||||
// See issue 15408.
|
||||
var unknownPkgVar = unicode.NoSuchType{"foo", "bar"}
|
||||
|
||||
// A named pointer slice of CaseRange to test issue 23539. In
|
||||
// particular, we're interested in how some slice elements omit their
|
||||
// type.
|
||||
var goodNamedPointerSliceLiteral = []*unicode.CaseRange{
|
||||
{Lo: 1, Hi: 2},
|
||||
&unicode.CaseRange{Lo: 1, Hi: 2},
|
||||
}
|
||||
var badNamedPointerSliceLiteral = []*unicode.CaseRange{
|
||||
{1, 2, delta}, // want "unkeyed fields"
|
||||
&unicode.CaseRange{1, 2, delta}, // want "unkeyed fields"
|
||||
}
|
||||
33
vendor/golang.org/x/tools/go/analysis/passes/composite/whitelist.go
generated
vendored
Normal file
33
vendor/golang.org/x/tools/go/analysis/passes/composite/whitelist.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2013 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 composite
|
||||
|
||||
// unkeyedLiteral is a white list of types in the standard packages
|
||||
// that are used with unkeyed literals we deem to be acceptable.
|
||||
var unkeyedLiteral = map[string]bool{
|
||||
// These image and image/color struct types are frozen. We will never add fields to them.
|
||||
"image/color.Alpha16": true,
|
||||
"image/color.Alpha": true,
|
||||
"image/color.CMYK": true,
|
||||
"image/color.Gray16": true,
|
||||
"image/color.Gray": true,
|
||||
"image/color.NRGBA64": true,
|
||||
"image/color.NRGBA": true,
|
||||
"image/color.NYCbCrA": true,
|
||||
"image/color.RGBA64": true,
|
||||
"image/color.RGBA": true,
|
||||
"image/color.YCbCr": true,
|
||||
"image.Point": true,
|
||||
"image.Rectangle": true,
|
||||
"image.Uniform": true,
|
||||
|
||||
"unicode.Range16": true,
|
||||
|
||||
// These three structs are used in generated test main files,
|
||||
// but the generator can be trusted.
|
||||
"testing.InternalBenchmark": true,
|
||||
"testing.InternalExample": true,
|
||||
"testing.InternalTest": true,
|
||||
}
|
||||
300
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
generated
vendored
Normal file
300
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
generated
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
// Copyright 2013 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 copylock defines an Analyzer that checks for locks
|
||||
// erroneously passed by value.
|
||||
package copylock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for locks erroneously passed by value
|
||||
|
||||
Inadvertently copying a value containing a lock, such as sync.Mutex or
|
||||
sync.WaitGroup, may cause both copies to malfunction. Generally such
|
||||
values should be referred to through a pointer.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "copylocks",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
RunDespiteErrors: true,
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
(*ast.CallExpr)(nil),
|
||||
(*ast.CompositeLit)(nil),
|
||||
(*ast.FuncDecl)(nil),
|
||||
(*ast.FuncLit)(nil),
|
||||
(*ast.GenDecl)(nil),
|
||||
(*ast.RangeStmt)(nil),
|
||||
(*ast.ReturnStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(node ast.Node) {
|
||||
switch node := node.(type) {
|
||||
case *ast.RangeStmt:
|
||||
checkCopyLocksRange(pass, node)
|
||||
case *ast.FuncDecl:
|
||||
checkCopyLocksFunc(pass, node.Name.Name, node.Recv, node.Type)
|
||||
case *ast.FuncLit:
|
||||
checkCopyLocksFunc(pass, "func", nil, node.Type)
|
||||
case *ast.CallExpr:
|
||||
checkCopyLocksCallExpr(pass, node)
|
||||
case *ast.AssignStmt:
|
||||
checkCopyLocksAssign(pass, node)
|
||||
case *ast.GenDecl:
|
||||
checkCopyLocksGenDecl(pass, node)
|
||||
case *ast.CompositeLit:
|
||||
checkCopyLocksCompositeLit(pass, node)
|
||||
case *ast.ReturnStmt:
|
||||
checkCopyLocksReturnStmt(pass, node)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// checkCopyLocksAssign checks whether an assignment
|
||||
// copies a lock.
|
||||
func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {
|
||||
for i, x := range as.Rhs {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksGenDecl checks whether lock is copied
|
||||
// in variable declaration.
|
||||
func checkCopyLocksGenDecl(pass *analysis.Pass, gd *ast.GenDecl) {
|
||||
if gd.Tok != token.VAR {
|
||||
return
|
||||
}
|
||||
for _, spec := range gd.Specs {
|
||||
valueSpec := spec.(*ast.ValueSpec)
|
||||
for i, x := range valueSpec.Values {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksCompositeLit detects lock copy inside a composite literal
|
||||
func checkCopyLocksCompositeLit(pass *analysis.Pass, cl *ast.CompositeLit) {
|
||||
for _, x := range cl.Elts {
|
||||
if node, ok := x.(*ast.KeyValueExpr); ok {
|
||||
x = node.Value
|
||||
}
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksReturnStmt detects lock copy in return statement
|
||||
func checkCopyLocksReturnStmt(pass *analysis.Pass, rs *ast.ReturnStmt) {
|
||||
for _, x := range rs.Results {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "return copies lock value: %v", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksCallExpr detects lock copy in the arguments to a function call
|
||||
func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
|
||||
var id *ast.Ident
|
||||
switch fun := ce.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
id = fun
|
||||
case *ast.SelectorExpr:
|
||||
id = fun.Sel
|
||||
}
|
||||
if fun, ok := pass.TypesInfo.Uses[id].(*types.Builtin); ok {
|
||||
switch fun.Name() {
|
||||
case "new", "len", "cap", "Sizeof":
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, x := range ce.Args {
|
||||
if path := lockPathRhs(pass, x); path != nil {
|
||||
pass.Reportf(x.Pos(), "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCopyLocksFunc checks whether a function might
|
||||
// inadvertently copy a lock, by checking whether
|
||||
// its receiver, parameters, or return values
|
||||
// are locks.
|
||||
func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) {
|
||||
if recv != nil && len(recv.List) > 0 {
|
||||
expr := recv.List[0].Type
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||
pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path)
|
||||
}
|
||||
}
|
||||
|
||||
if typ.Params != nil {
|
||||
for _, field := range typ.Params.List {
|
||||
expr := field.Type
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||
pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't check typ.Results. If T has a Lock field it's OK to write
|
||||
// return T{}
|
||||
// because that is returning the zero value. Leave result checking
|
||||
// to the return statement.
|
||||
}
|
||||
|
||||
// checkCopyLocksRange checks whether a range statement
|
||||
// might inadvertently copy a lock by checking whether
|
||||
// any of the range variables are locks.
|
||||
func checkCopyLocksRange(pass *analysis.Pass, r *ast.RangeStmt) {
|
||||
checkCopyLocksRangeVar(pass, r.Tok, r.Key)
|
||||
checkCopyLocksRangeVar(pass, r.Tok, r.Value)
|
||||
}
|
||||
|
||||
func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
id, isId := e.(*ast.Ident)
|
||||
if isId && id.Name == "_" {
|
||||
return
|
||||
}
|
||||
|
||||
var typ types.Type
|
||||
if rtok == token.DEFINE {
|
||||
if !isId {
|
||||
return
|
||||
}
|
||||
obj := pass.TypesInfo.Defs[id]
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
typ = obj.Type()
|
||||
} else {
|
||||
typ = pass.TypesInfo.Types[e].Type
|
||||
}
|
||||
|
||||
if typ == nil {
|
||||
return
|
||||
}
|
||||
if path := lockPath(pass.Pkg, typ); path != nil {
|
||||
pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path)
|
||||
}
|
||||
}
|
||||
|
||||
type typePath []types.Type
|
||||
|
||||
// String pretty-prints a typePath.
|
||||
func (path typePath) String() string {
|
||||
n := len(path)
|
||||
var buf bytes.Buffer
|
||||
for i := range path {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, " contains ")
|
||||
}
|
||||
// The human-readable path is in reverse order, outermost to innermost.
|
||||
fmt.Fprint(&buf, path[n-i-1].String())
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath {
|
||||
if _, ok := x.(*ast.CompositeLit); ok {
|
||||
return nil
|
||||
}
|
||||
if _, ok := x.(*ast.CallExpr); ok {
|
||||
// A call may return a zero value.
|
||||
return nil
|
||||
}
|
||||
if star, ok := x.(*ast.StarExpr); ok {
|
||||
if _, ok := star.X.(*ast.CallExpr); ok {
|
||||
// A call may return a pointer to a zero value.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type)
|
||||
}
|
||||
|
||||
// lockPath returns a typePath describing the location of a lock value
|
||||
// contained in typ. If there is no contained lock, it returns nil.
|
||||
func lockPath(tpkg *types.Package, typ types.Type) typePath {
|
||||
if typ == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
atyp, ok := typ.Underlying().(*types.Array)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
typ = atyp.Elem()
|
||||
}
|
||||
|
||||
// We're only interested in the case in which the underlying
|
||||
// type is a struct. (Interfaces and pointers are safe to copy.)
|
||||
styp, ok := typ.Underlying().(*types.Struct)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We're looking for cases in which a pointer to this type
|
||||
// is a sync.Locker, but a value is not. This differentiates
|
||||
// embedded interfaces from embedded values.
|
||||
if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
|
||||
return []types.Type{typ}
|
||||
}
|
||||
|
||||
// In go1.10, sync.noCopy did not implement Locker.
|
||||
// (The Unlock method was added only in CL 121876.)
|
||||
// TODO(adonovan): remove workaround when we drop go1.10.
|
||||
if named, ok := typ.(*types.Named); ok &&
|
||||
named.Obj().Name() == "noCopy" &&
|
||||
named.Obj().Pkg().Path() == "sync" {
|
||||
return []types.Type{typ}
|
||||
}
|
||||
|
||||
nfields := styp.NumFields()
|
||||
for i := 0; i < nfields; i++ {
|
||||
ftyp := styp.Field(i).Type()
|
||||
subpath := lockPath(tpkg, ftyp)
|
||||
if subpath != nil {
|
||||
return append(subpath, typ)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var lockerType *types.Interface
|
||||
|
||||
// Construct a sync.Locker interface type.
|
||||
func init() {
|
||||
nullary := types.NewSignature(nil, nil, nil, false) // func()
|
||||
methods := []*types.Func{
|
||||
types.NewFunc(token.NoPos, nil, "Lock", nullary),
|
||||
types.NewFunc(token.NoPos, nil, "Unlock", nullary),
|
||||
}
|
||||
lockerType = types.NewInterface(methods, nil).Complete()
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 copylock_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/copylock"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, copylock.Analyzer, "a")
|
||||
}
|
||||
188
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock.go
generated
vendored
Normal file
188
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package a
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
. "unsafe"
|
||||
unsafe1 "unsafe"
|
||||
)
|
||||
|
||||
func OkFunc() {
|
||||
var x *sync.Mutex
|
||||
p := x
|
||||
var y sync.Mutex
|
||||
p = &y
|
||||
|
||||
var z = sync.Mutex{}
|
||||
w := sync.Mutex{}
|
||||
|
||||
w = sync.Mutex{}
|
||||
q := struct{ L sync.Mutex }{
|
||||
L: sync.Mutex{},
|
||||
}
|
||||
|
||||
yy := []Tlock{
|
||||
Tlock{},
|
||||
Tlock{
|
||||
once: sync.Once{},
|
||||
},
|
||||
}
|
||||
|
||||
nl := new(sync.Mutex)
|
||||
mx := make([]sync.Mutex, 10)
|
||||
xx := struct{ L *sync.Mutex }{
|
||||
L: new(sync.Mutex),
|
||||
}
|
||||
}
|
||||
|
||||
type Tlock struct {
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func BadFunc() {
|
||||
var x *sync.Mutex
|
||||
p := x
|
||||
var y sync.Mutex
|
||||
p = &y
|
||||
*p = *x // want `assignment copies lock value to \*p: sync.Mutex`
|
||||
|
||||
var t Tlock
|
||||
var tp *Tlock
|
||||
tp = &t
|
||||
*tp = t // want `assignment copies lock value to \*tp: a.Tlock contains sync.Once contains sync.Mutex`
|
||||
t = *tp // want "assignment copies lock value to t: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
|
||||
y := *x // want "assignment copies lock value to y: sync.Mutex"
|
||||
var z = t // want "variable declaration copies lock value to z: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
|
||||
w := struct{ L sync.Mutex }{
|
||||
L: *x, // want `literal copies lock value from \*x: sync.Mutex`
|
||||
}
|
||||
var q = map[int]Tlock{
|
||||
1: t, // want "literal copies lock value from t: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
2: *tp, // want `literal copies lock value from \*tp: a.Tlock contains sync.Once contains sync.Mutex`
|
||||
}
|
||||
yy := []Tlock{
|
||||
t, // want "literal copies lock value from t: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
*tp, // want `literal copies lock value from \*tp: a.Tlock contains sync.Once contains sync.Mutex`
|
||||
}
|
||||
|
||||
// override 'new' keyword
|
||||
new := func(interface{}) {}
|
||||
new(t) // want "call of new copies lock value: a.Tlock contains sync.Once contains sync.Mutex"
|
||||
|
||||
// copy of array of locks
|
||||
var muA [5]sync.Mutex
|
||||
muB := muA // want "assignment copies lock value to muB: sync.Mutex"
|
||||
muA = muB // want "assignment copies lock value to muA: sync.Mutex"
|
||||
muSlice := muA[:] // OK
|
||||
|
||||
// multidimensional array
|
||||
var mmuA [5][5]sync.Mutex
|
||||
mmuB := mmuA // want "assignment copies lock value to mmuB: sync.Mutex"
|
||||
mmuA = mmuB // want "assignment copies lock value to mmuA: sync.Mutex"
|
||||
mmuSlice := mmuA[:] // OK
|
||||
|
||||
// slice copy is ok
|
||||
var fmuA [5][][5]sync.Mutex
|
||||
fmuB := fmuA // OK
|
||||
fmuA = fmuB // OK
|
||||
fmuSlice := fmuA[:] // OK
|
||||
}
|
||||
|
||||
func LenAndCapOnLockArrays() {
|
||||
var a [5]sync.Mutex
|
||||
aLen := len(a) // OK
|
||||
aCap := cap(a) // OK
|
||||
|
||||
// override 'len' and 'cap' keywords
|
||||
|
||||
len := func(interface{}) {}
|
||||
len(a) // want "call of len copies lock value: sync.Mutex"
|
||||
|
||||
cap := func(interface{}) {}
|
||||
cap(a) // want "call of cap copies lock value: sync.Mutex"
|
||||
}
|
||||
|
||||
func SizeofMutex() {
|
||||
var mu sync.Mutex
|
||||
unsafe.Sizeof(mu) // OK
|
||||
unsafe1.Sizeof(mu) // OK
|
||||
Sizeof(mu) // OK
|
||||
unsafe := struct{ Sizeof func(interface{}) }{}
|
||||
unsafe.Sizeof(mu) // want "call of unsafe.Sizeof copies lock value: sync.Mutex"
|
||||
Sizeof := func(interface{}) {}
|
||||
Sizeof(mu) // want "call of Sizeof copies lock value: sync.Mutex"
|
||||
}
|
||||
|
||||
// SyncTypesCheck checks copying of sync.* types except sync.Mutex
|
||||
func SyncTypesCheck() {
|
||||
// sync.RWMutex copying
|
||||
var rwmuX sync.RWMutex
|
||||
var rwmuXX = sync.RWMutex{}
|
||||
rwmuX1 := new(sync.RWMutex)
|
||||
rwmuY := rwmuX // want "assignment copies lock value to rwmuY: sync.RWMutex"
|
||||
rwmuY = rwmuX // want "assignment copies lock value to rwmuY: sync.RWMutex"
|
||||
var rwmuYY = rwmuX // want "variable declaration copies lock value to rwmuYY: sync.RWMutex"
|
||||
rwmuP := &rwmuX
|
||||
rwmuZ := &sync.RWMutex{}
|
||||
|
||||
// sync.Cond copying
|
||||
var condX sync.Cond
|
||||
var condXX = sync.Cond{}
|
||||
condX1 := new(sync.Cond)
|
||||
condY := condX // want "assignment copies lock value to condY: sync.Cond contains sync.noCopy"
|
||||
condY = condX // want "assignment copies lock value to condY: sync.Cond contains sync.noCopy"
|
||||
var condYY = condX // want "variable declaration copies lock value to condYY: sync.Cond contains sync.noCopy"
|
||||
condP := &condX
|
||||
condZ := &sync.Cond{
|
||||
L: &sync.Mutex{},
|
||||
}
|
||||
condZ = sync.NewCond(&sync.Mutex{})
|
||||
|
||||
// sync.WaitGroup copying
|
||||
var wgX sync.WaitGroup
|
||||
var wgXX = sync.WaitGroup{}
|
||||
wgX1 := new(sync.WaitGroup)
|
||||
wgY := wgX // want "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy"
|
||||
wgY = wgX // want "assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy"
|
||||
var wgYY = wgX // want "variable declaration copies lock value to wgYY: sync.WaitGroup contains sync.noCopy"
|
||||
wgP := &wgX
|
||||
wgZ := &sync.WaitGroup{}
|
||||
|
||||
// sync.Pool copying
|
||||
var poolX sync.Pool
|
||||
var poolXX = sync.Pool{}
|
||||
poolX1 := new(sync.Pool)
|
||||
poolY := poolX // want "assignment copies lock value to poolY: sync.Pool contains sync.noCopy"
|
||||
poolY = poolX // want "assignment copies lock value to poolY: sync.Pool contains sync.noCopy"
|
||||
var poolYY = poolX // want "variable declaration copies lock value to poolYY: sync.Pool contains sync.noCopy"
|
||||
poolP := &poolX
|
||||
poolZ := &sync.Pool{}
|
||||
|
||||
// sync.Once copying
|
||||
var onceX sync.Once
|
||||
var onceXX = sync.Once{}
|
||||
onceX1 := new(sync.Once)
|
||||
onceY := onceX // want "assignment copies lock value to onceY: sync.Once contains sync.Mutex"
|
||||
onceY = onceX // want "assignment copies lock value to onceY: sync.Once contains sync.Mutex"
|
||||
var onceYY = onceX // want "variable declaration copies lock value to onceYY: sync.Once contains sync.Mutex"
|
||||
onceP := &onceX
|
||||
onceZ := &sync.Once{}
|
||||
}
|
||||
|
||||
// AtomicTypesCheck checks copying of sync/atomic types
|
||||
func AtomicTypesCheck() {
|
||||
// atomic.Value copying
|
||||
var vX atomic.Value
|
||||
var vXX = atomic.Value{}
|
||||
vX1 := new(atomic.Value)
|
||||
// These are OK because the value has not been used yet.
|
||||
// (And vet can't tell whether it has been used, so they're always OK.)
|
||||
vY := vX
|
||||
vY = vX
|
||||
var vYY = vX
|
||||
vP := &vX
|
||||
vZ := &atomic.Value{}
|
||||
}
|
||||
136
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_func.go
generated
vendored
Normal file
136
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_func.go
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file contains tests for the copylock checker's
|
||||
// function declaration analysis.
|
||||
|
||||
package a
|
||||
|
||||
import "sync"
|
||||
|
||||
func OkFunc(*sync.Mutex) {}
|
||||
func BadFunc(sync.Mutex) {} // want "BadFunc passes lock by value: sync.Mutex"
|
||||
func BadFunc2(sync.Map) {} // want "BadFunc2 passes lock by value: sync.Map contains sync.Mutex"
|
||||
func OkRet() *sync.Mutex {}
|
||||
func BadRet() sync.Mutex {} // Don't warn about results
|
||||
|
||||
var (
|
||||
OkClosure = func(*sync.Mutex) {}
|
||||
BadClosure = func(sync.Mutex) {} // want "func passes lock by value: sync.Mutex"
|
||||
BadClosure2 = func(sync.Map) {} // want "func passes lock by value: sync.Map contains sync.Mutex"
|
||||
)
|
||||
|
||||
type EmbeddedRWMutex struct {
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (*EmbeddedRWMutex) OkMeth() {}
|
||||
func (EmbeddedRWMutex) BadMeth() {} // want "BadMeth passes lock by value: a.EmbeddedRWMutex"
|
||||
func OkFunc(e *EmbeddedRWMutex) {}
|
||||
func BadFunc(EmbeddedRWMutex) {} // want "BadFunc passes lock by value: a.EmbeddedRWMutex"
|
||||
func OkRet() *EmbeddedRWMutex {}
|
||||
func BadRet() EmbeddedRWMutex {} // Don't warn about results
|
||||
|
||||
type FieldMutex struct {
|
||||
s sync.Mutex
|
||||
}
|
||||
|
||||
func (*FieldMutex) OkMeth() {}
|
||||
func (FieldMutex) BadMeth() {} // want "BadMeth passes lock by value: a.FieldMutex contains sync.Mutex"
|
||||
func OkFunc(*FieldMutex) {}
|
||||
func BadFunc(FieldMutex, int) {} // want "BadFunc passes lock by value: a.FieldMutex contains sync.Mutex"
|
||||
|
||||
type L0 struct {
|
||||
L1
|
||||
}
|
||||
|
||||
type L1 struct {
|
||||
l L2
|
||||
}
|
||||
|
||||
type L2 struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (*L0) Ok() {}
|
||||
func (L0) Bad() {} // want "Bad passes lock by value: a.L0 contains a.L1 contains a.L2"
|
||||
|
||||
type EmbeddedMutexPointer struct {
|
||||
s *sync.Mutex // safe to copy this pointer
|
||||
}
|
||||
|
||||
func (*EmbeddedMutexPointer) Ok() {}
|
||||
func (EmbeddedMutexPointer) AlsoOk() {}
|
||||
func StillOk(EmbeddedMutexPointer) {}
|
||||
func LookinGood() EmbeddedMutexPointer {}
|
||||
|
||||
type EmbeddedLocker struct {
|
||||
sync.Locker // safe to copy interface values
|
||||
}
|
||||
|
||||
func (*EmbeddedLocker) Ok() {}
|
||||
func (EmbeddedLocker) AlsoOk() {}
|
||||
|
||||
type CustomLock struct{}
|
||||
|
||||
func (*CustomLock) Lock() {}
|
||||
func (*CustomLock) Unlock() {}
|
||||
|
||||
func Ok(*CustomLock) {}
|
||||
func Bad(CustomLock) {} // want "Bad passes lock by value: a.CustomLock"
|
||||
|
||||
// Passing lock values into interface function arguments
|
||||
func FuncCallInterfaceArg(f func(a int, b interface{})) {
|
||||
var m sync.Mutex
|
||||
var t struct{ lock sync.Mutex }
|
||||
|
||||
f(1, "foo")
|
||||
f(2, &t)
|
||||
f(3, &sync.Mutex{})
|
||||
f(4, m) // want "call of f copies lock value: sync.Mutex"
|
||||
f(5, t) // want "call of f copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
|
||||
var fntab []func(t)
|
||||
fntab[0](t) // want "call of fntab.0. copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
|
||||
}
|
||||
|
||||
// Returning lock via interface value
|
||||
func ReturnViaInterface(x int) (int, interface{}) {
|
||||
var m sync.Mutex
|
||||
var t struct{ lock sync.Mutex }
|
||||
|
||||
switch x % 4 {
|
||||
case 0:
|
||||
return 0, "qwe"
|
||||
case 1:
|
||||
return 1, &sync.Mutex{}
|
||||
case 2:
|
||||
return 2, m // want "return copies lock value: sync.Mutex"
|
||||
default:
|
||||
return 3, t // want "return copies lock value: struct.lock sync.Mutex. contains sync.Mutex"
|
||||
}
|
||||
}
|
||||
|
||||
// Some cases that we don't warn about.
|
||||
|
||||
func AcceptedCases() {
|
||||
x := EmbeddedRwMutex{} // composite literal on RHS is OK (#16227)
|
||||
x = BadRet() // function call on RHS is OK (#16227)
|
||||
x = *OKRet() // indirection of function call on RHS is OK (#16227)
|
||||
}
|
||||
|
||||
// TODO: Unfortunate cases
|
||||
|
||||
// Non-ideal error message:
|
||||
// Since we're looking for Lock methods, sync.Once's underlying
|
||||
// sync.Mutex gets called out, but without any reference to the sync.Once.
|
||||
type LocalOnce sync.Once
|
||||
|
||||
func (LocalOnce) Bad() {} // want "Bad passes lock by value: a.LocalOnce contains sync.Mutex"
|
||||
|
||||
// False negative:
|
||||
// LocalMutex doesn't have a Lock method.
|
||||
// Nevertheless, it is probably a bad idea to pass it by value.
|
||||
type LocalMutex sync.Mutex
|
||||
|
||||
func (LocalMutex) Bad() {} // WANTED: An error here :(
|
||||
67
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_range.go
generated
vendored
Normal file
67
vendor/golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a/copylock_range.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This file contains tests for the copylock checker's
|
||||
// range statement analysis.
|
||||
|
||||
package a
|
||||
|
||||
import "sync"
|
||||
|
||||
func rangeMutex() {
|
||||
var mu sync.Mutex
|
||||
var i int
|
||||
|
||||
var s []sync.Mutex
|
||||
for range s {
|
||||
}
|
||||
for i = range s {
|
||||
}
|
||||
for i := range s {
|
||||
}
|
||||
for i, _ = range s {
|
||||
}
|
||||
for i, _ := range s {
|
||||
}
|
||||
for _, mu = range s { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for _, m := range s { // want "range var m copies lock: sync.Mutex"
|
||||
}
|
||||
for i, mu = range s { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for i, m := range s { // want "range var m copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
var a [3]sync.Mutex
|
||||
for _, m := range a { // want "range var m copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
var m map[sync.Mutex]sync.Mutex
|
||||
for k := range m { // want "range var k copies lock: sync.Mutex"
|
||||
}
|
||||
for mu, _ = range m { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for k, _ := range m { // want "range var k copies lock: sync.Mutex"
|
||||
}
|
||||
for _, mu = range m { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for _, v := range m { // want "range var v copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
var c chan sync.Mutex
|
||||
for range c {
|
||||
}
|
||||
for mu = range c { // want "range var mu copies lock: sync.Mutex"
|
||||
}
|
||||
for v := range c { // want "range var v copies lock: sync.Mutex"
|
||||
}
|
||||
|
||||
// Test non-idents in range variables
|
||||
var t struct {
|
||||
i int
|
||||
mu sync.Mutex
|
||||
}
|
||||
for t.i, t.mu = range s { // want "range var t.mu copies lock: sync.Mutex"
|
||||
}
|
||||
}
|
||||
225
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
Normal file
225
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
// 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 ctrlflow is an analysis that provides a syntactic
|
||||
// control-flow graph (CFG) for the body of a function.
|
||||
// It records whether a function cannot return.
|
||||
// By itself, it does not report any diagnostics.
|
||||
package ctrlflow
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/cfg"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "ctrlflow",
|
||||
Doc: "build a control-flow graph",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeOf(new(CFGs)),
|
||||
FactTypes: []analysis.Fact{new(noReturn)},
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
}
|
||||
|
||||
// noReturn is a fact indicating that a function does not return.
|
||||
type noReturn struct{}
|
||||
|
||||
func (*noReturn) AFact() {}
|
||||
|
||||
func (*noReturn) String() string { return "noReturn" }
|
||||
|
||||
// A CFGs holds the control-flow graphs
|
||||
// for all the functions of the current package.
|
||||
type CFGs struct {
|
||||
defs map[*ast.Ident]types.Object // from Pass.TypesInfo.Defs
|
||||
funcDecls map[*types.Func]*declInfo
|
||||
funcLits map[*ast.FuncLit]*litInfo
|
||||
pass *analysis.Pass // transient; nil after construction
|
||||
}
|
||||
|
||||
// CFGs has two maps: funcDecls for named functions and funcLits for
|
||||
// unnamed ones. Unlike funcLits, the funcDecls map is not keyed by its
|
||||
// syntax node, *ast.FuncDecl, because callMayReturn needs to do a
|
||||
// look-up by *types.Func, and you can get from an *ast.FuncDecl to a
|
||||
// *types.Func but not the other way.
|
||||
|
||||
type declInfo struct {
|
||||
decl *ast.FuncDecl
|
||||
cfg *cfg.CFG // iff decl.Body != nil
|
||||
started bool // to break cycles
|
||||
noReturn bool
|
||||
}
|
||||
|
||||
type litInfo struct {
|
||||
cfg *cfg.CFG
|
||||
noReturn bool
|
||||
}
|
||||
|
||||
// FuncDecl returns the control-flow graph for a named function.
|
||||
// It returns nil if decl.Body==nil.
|
||||
func (c *CFGs) FuncDecl(decl *ast.FuncDecl) *cfg.CFG {
|
||||
if decl.Body == nil {
|
||||
return nil
|
||||
}
|
||||
fn := c.defs[decl.Name].(*types.Func)
|
||||
return c.funcDecls[fn].cfg
|
||||
}
|
||||
|
||||
// FuncLit returns the control-flow graph for a literal function.
|
||||
func (c *CFGs) FuncLit(lit *ast.FuncLit) *cfg.CFG {
|
||||
return c.funcLits[lit].cfg
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
// Because CFG construction consumes and produces noReturn
|
||||
// facts, CFGs for exported FuncDecls must be built before 'run'
|
||||
// returns; we cannot construct them lazily.
|
||||
// (We could build CFGs for FuncLits lazily,
|
||||
// but the benefit is marginal.)
|
||||
|
||||
// Pass 1. Map types.Funcs to ast.FuncDecls in this package.
|
||||
funcDecls := make(map[*types.Func]*declInfo) // functions and methods
|
||||
funcLits := make(map[*ast.FuncLit]*litInfo)
|
||||
|
||||
var decls []*types.Func // keys(funcDecls), in order
|
||||
var lits []*ast.FuncLit // keys(funcLits), in order
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.FuncDecl)(nil),
|
||||
(*ast.FuncLit)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
switch n := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
fn := pass.TypesInfo.Defs[n.Name].(*types.Func)
|
||||
funcDecls[fn] = &declInfo{decl: n}
|
||||
decls = append(decls, fn)
|
||||
|
||||
case *ast.FuncLit:
|
||||
funcLits[n] = new(litInfo)
|
||||
lits = append(lits, n)
|
||||
}
|
||||
})
|
||||
|
||||
c := &CFGs{
|
||||
defs: pass.TypesInfo.Defs,
|
||||
funcDecls: funcDecls,
|
||||
funcLits: funcLits,
|
||||
pass: pass,
|
||||
}
|
||||
|
||||
// Pass 2. Build CFGs.
|
||||
|
||||
// Build CFGs for named functions.
|
||||
// Cycles in the static call graph are broken
|
||||
// arbitrarily but deterministically.
|
||||
// We create noReturn facts as discovered.
|
||||
for _, fn := range decls {
|
||||
c.buildDecl(fn, funcDecls[fn])
|
||||
}
|
||||
|
||||
// Build CFGs for literal functions.
|
||||
// These aren't relevant to facts (since they aren't named)
|
||||
// but are required for the CFGs.FuncLit API.
|
||||
for _, lit := range lits {
|
||||
li := funcLits[lit]
|
||||
if li.cfg == nil {
|
||||
li.cfg = cfg.New(lit.Body, c.callMayReturn)
|
||||
if !hasReachableReturn(li.cfg) {
|
||||
li.noReturn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All CFGs are now built.
|
||||
c.pass = nil
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// di.cfg may be nil on return.
|
||||
func (c *CFGs) buildDecl(fn *types.Func, di *declInfo) {
|
||||
// buildDecl may call itself recursively for the same function,
|
||||
// because cfg.New is passed the callMayReturn method, which
|
||||
// builds the CFG of the callee, leading to recursion.
|
||||
// The buildDecl call tree thus resembles the static call graph.
|
||||
// We mark each node when we start working on it to break cycles.
|
||||
|
||||
if !di.started { // break cycle
|
||||
di.started = true
|
||||
|
||||
if isIntrinsicNoReturn(fn) {
|
||||
di.noReturn = true
|
||||
}
|
||||
if di.decl.Body != nil {
|
||||
di.cfg = cfg.New(di.decl.Body, c.callMayReturn)
|
||||
if !hasReachableReturn(di.cfg) {
|
||||
di.noReturn = true
|
||||
}
|
||||
}
|
||||
if di.noReturn {
|
||||
c.pass.ExportObjectFact(fn, new(noReturn))
|
||||
}
|
||||
|
||||
// debugging
|
||||
if false {
|
||||
log.Printf("CFG for %s:\n%s (noreturn=%t)\n", fn, di.cfg.Format(c.pass.Fset), di.noReturn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// callMayReturn reports whether the called function may return.
|
||||
// It is passed to the CFG builder.
|
||||
func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
|
||||
if id, ok := call.Fun.(*ast.Ident); ok && c.pass.TypesInfo.Uses[id] == panicBuiltin {
|
||||
return false // panic never returns
|
||||
}
|
||||
|
||||
// Is this a static call?
|
||||
fn := typeutil.StaticCallee(c.pass.TypesInfo, call)
|
||||
if fn == nil {
|
||||
return true // callee not statically known; be conservative
|
||||
}
|
||||
|
||||
// Function or method declared in this package?
|
||||
if di, ok := c.funcDecls[fn]; ok {
|
||||
c.buildDecl(fn, di)
|
||||
return !di.noReturn
|
||||
}
|
||||
|
||||
// Not declared in this package.
|
||||
// Is there a fact from another package?
|
||||
return !c.pass.ImportObjectFact(fn, new(noReturn))
|
||||
}
|
||||
|
||||
var panicBuiltin = types.Universe.Lookup("panic").(*types.Builtin)
|
||||
|
||||
func hasReachableReturn(g *cfg.CFG) bool {
|
||||
for _, b := range g.Blocks {
|
||||
if b.Live && b.Return() != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isIntrinsicNoReturn reports whether a function intrinsically never
|
||||
// returns because it stops execution of the calling thread.
|
||||
// It is the base case in the recursion.
|
||||
func isIntrinsicNoReturn(fn *types.Func) bool {
|
||||
// Add functions here as the need arises, but don't allocate memory.
|
||||
path, name := fn.Pkg().Path(), fn.Name()
|
||||
return path == "syscall" && (name == "Exit" || name == "ExitProcess" || name == "ExitThread") ||
|
||||
path == "runtime" && name == "Goexit"
|
||||
}
|
||||
35
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow_test.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow_test.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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 ctrlflow_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/ctrlflow"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
|
||||
// load testdata/src/a/a.go
|
||||
results := analysistest.Run(t, testdata, ctrlflow.Analyzer, "a")
|
||||
|
||||
// Perform a minimal smoke test on
|
||||
// the result (CFG) computed by ctrlflow.
|
||||
for _, result := range results {
|
||||
cfgs := result.Result.(*ctrlflow.CFGs)
|
||||
|
||||
for _, decl := range result.Pass.Files[0].Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Body != nil {
|
||||
if cfgs.FuncDecl(decl) == nil {
|
||||
t.Errorf("%s: no CFG for func %s",
|
||||
result.Pass.Fset.Position(decl.Pos()), decl.Name.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/a/a.go
generated
vendored
Normal file
109
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package a
|
||||
|
||||
// This file tests facts produced by ctrlflow.
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"lib"
|
||||
)
|
||||
|
||||
var cond bool
|
||||
|
||||
func a() { // want a:"noReturn"
|
||||
if cond {
|
||||
b()
|
||||
} else {
|
||||
for {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func b() { // want b:"noReturn"
|
||||
select {}
|
||||
}
|
||||
|
||||
func f(x int) { // no fact here
|
||||
switch x {
|
||||
case 0:
|
||||
os.Exit(0)
|
||||
case 1:
|
||||
panic(0)
|
||||
}
|
||||
// default case returns
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
func (T) method1() { // want method1:"noReturn"
|
||||
a()
|
||||
}
|
||||
|
||||
func (T) method2() { // (may return)
|
||||
if cond {
|
||||
a()
|
||||
}
|
||||
}
|
||||
|
||||
// Checking for the noreturn fact associated with F ensures that
|
||||
// ctrlflow proved each of the listed functions was "noReturn".
|
||||
//
|
||||
func standardFunctions(x int) { // want standardFunctions:"noReturn"
|
||||
t := new(testing.T)
|
||||
switch x {
|
||||
case 0:
|
||||
t.FailNow()
|
||||
case 1:
|
||||
t.Fatal()
|
||||
case 2:
|
||||
t.Fatalf("")
|
||||
case 3:
|
||||
t.Skip()
|
||||
case 4:
|
||||
t.SkipNow()
|
||||
case 5:
|
||||
t.Skipf("")
|
||||
case 6:
|
||||
log.Fatal()
|
||||
case 7:
|
||||
log.Fatalf("")
|
||||
case 8:
|
||||
log.Fatalln()
|
||||
case 9:
|
||||
os.Exit(0)
|
||||
case 10:
|
||||
syscall.Exit(0)
|
||||
case 11:
|
||||
runtime.Goexit()
|
||||
case 12:
|
||||
log.Panic()
|
||||
case 13:
|
||||
log.Panicln()
|
||||
case 14:
|
||||
log.Panicf("")
|
||||
default:
|
||||
panic(0)
|
||||
}
|
||||
}
|
||||
|
||||
// False positives are possible.
|
||||
// This function is marked noReturn but in fact returns.
|
||||
//
|
||||
func spurious() { // want spurious:"noReturn"
|
||||
defer func() { recover() }()
|
||||
panic(nil)
|
||||
}
|
||||
|
||||
func noBody()
|
||||
|
||||
func g() {
|
||||
lib.CanReturn()
|
||||
}
|
||||
|
||||
func h() { // want h:"noReturn"
|
||||
lib.NoReturn()
|
||||
}
|
||||
8
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/lib/lib.go
generated
vendored
Normal file
8
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/testdata/src/lib/lib.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package lib
|
||||
|
||||
func CanReturn() {}
|
||||
|
||||
func NoReturn() {
|
||||
for {
|
||||
}
|
||||
}
|
||||
9
vendor/golang.org/x/tools/go/analysis/passes/findcall/cmd/findcall/main.go
generated
vendored
Normal file
9
vendor/golang.org/x/tools/go/analysis/passes/findcall/cmd/findcall/main.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// The findcall command runs the findcall analyzer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(findcall.Analyzer) }
|
||||
80
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go
generated
vendored
Normal file
80
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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.
|
||||
|
||||
// The findcall package defines an Analyzer that serves as a trivial
|
||||
// example and test of the Analysis API. It reports a diagnostic for
|
||||
// every call to a function or method of the name specified by its
|
||||
// -name flag.
|
||||
package findcall
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
const Doc = `find calls to a particular function
|
||||
|
||||
The findcall analysis reports calls to functions or methods
|
||||
of a particular name.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "findcall",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
RunDespiteErrors: true,
|
||||
FactTypes: []analysis.Fact{new(foundFact)},
|
||||
}
|
||||
|
||||
var name string // -name flag
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.StringVar(&name, "name", name, "name of the function to find")
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
for _, f := range pass.Files {
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
if call, ok := n.(*ast.CallExpr); ok {
|
||||
var id *ast.Ident
|
||||
switch fun := call.Fun.(type) {
|
||||
case *ast.Ident:
|
||||
id = fun
|
||||
case *ast.SelectorExpr:
|
||||
id = fun.Sel
|
||||
}
|
||||
if id != nil && !pass.TypesInfo.Types[id].IsType() && id.Name == name {
|
||||
pass.Reportf(call.Lparen, "call of %s(...)", id.Name)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Export a fact for each matching function.
|
||||
//
|
||||
// These facts are produced only to test the testing
|
||||
// infrastructure in the analysistest package.
|
||||
// They are not consumed by the findcall Analyzer
|
||||
// itself, as would happen in a more realistic example.
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.FuncDecl); ok && decl.Name.Name == name {
|
||||
if obj, ok := pass.TypesInfo.Defs[decl.Name].(*types.Func); ok {
|
||||
pass.ExportObjectFact(obj, new(foundFact))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// foundFact is a fact associated with functions that match -name.
|
||||
// We use it to exercise the fact machinery in tests.
|
||||
type foundFact struct{}
|
||||
|
||||
func (*foundFact) String() string { return "found" }
|
||||
func (*foundFact) AFact() {}
|
||||
64
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall_test.go
generated
vendored
Normal file
64
vendor/golang.org/x/tools/go/analysis/passes/findcall/findcall_test.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// 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 findcall_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/findcall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
findcall.Analyzer.Flags.Set("name", "println")
|
||||
}
|
||||
|
||||
// TestFromStringLiterals demonstrates how to test an analysis using
|
||||
// a table of string literals for each test case.
|
||||
//
|
||||
// Such tests are typically quite compact.
|
||||
func TestFromStringLiterals(t *testing.T) {
|
||||
|
||||
for _, test := range [...]struct {
|
||||
desc string
|
||||
pkgpath string
|
||||
files map[string]string
|
||||
}{
|
||||
{
|
||||
desc: "SimpleTest",
|
||||
pkgpath: "main",
|
||||
files: map[string]string{"main/main.go": `package main
|
||||
|
||||
func main() {
|
||||
println("hello") // want "call of println"
|
||||
print("goodbye") // not a call of println
|
||||
}`,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
dir, cleanup, err := analysistest.WriteFiles(test.files)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer cleanup()
|
||||
analysistest.Run(t, dir, findcall.Analyzer, test.pkgpath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFromFileSystem demonstrates how to test an analysis using input
|
||||
// files stored in the file system.
|
||||
//
|
||||
// These tests have the advantages that test data can be edited
|
||||
// directly, and that files named in error messages can be opened.
|
||||
// However, they tend to spread a small number of lines of text across a
|
||||
// rather deep directory hierarchy, and obscure similarities among
|
||||
// related tests, especially when tests involve multiple packages, or
|
||||
// multiple variants of a single scenario.
|
||||
func TestFromFileSystem(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, findcall.Analyzer, "a") // loads testdata/src/a/a.go.
|
||||
}
|
||||
6
vendor/golang.org/x/tools/go/analysis/passes/findcall/testdata/src/a/a.go
generated
vendored
Normal file
6
vendor/golang.org/x/tools/go/analysis/passes/findcall/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hi") // want "call of println"
|
||||
print("hi") // not a call of println
|
||||
}
|
||||
177
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go
generated
vendored
Normal file
177
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright 2016 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 httpresponse defines an Analyzer that checks for mistakes
|
||||
// using HTTP responses.
|
||||
package httpresponse
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for mistakes using HTTP responses
|
||||
|
||||
A common mistake when using the net/http package is to defer a function
|
||||
call to close the http.Response Body before checking the error that
|
||||
determines whether the response is valid:
|
||||
|
||||
resp, err := http.Head(url)
|
||||
defer resp.Body.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// (defer statement belongs here)
|
||||
|
||||
This checker helps uncover latent nil dereference bugs by reporting a
|
||||
diagnostic for such mistakes.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "httpresponse",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
// Fast path: if the package doesn't import net/http,
|
||||
// skip the traversal.
|
||||
if !imports(pass.Pkg, "net/http") {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.CallExpr)(nil),
|
||||
}
|
||||
inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
|
||||
if !push {
|
||||
return true
|
||||
}
|
||||
call := n.(*ast.CallExpr)
|
||||
if !isHTTPFuncOrMethodOnClient(pass.TypesInfo, call) {
|
||||
return true // the function call is not related to this check.
|
||||
}
|
||||
|
||||
// Find the innermost containing block, and get the list
|
||||
// of statements starting with the one containing call.
|
||||
stmts := restOfBlock(stack)
|
||||
if len(stmts) < 2 {
|
||||
return true // the call to the http function is the last statement of the block.
|
||||
}
|
||||
|
||||
asg, ok := stmts[0].(*ast.AssignStmt)
|
||||
if !ok {
|
||||
return true // the first statement is not assignment.
|
||||
}
|
||||
resp := rootIdent(asg.Lhs[0])
|
||||
if resp == nil {
|
||||
return true // could not find the http.Response in the assignment.
|
||||
}
|
||||
|
||||
def, ok := stmts[1].(*ast.DeferStmt)
|
||||
if !ok {
|
||||
return true // the following statement is not a defer.
|
||||
}
|
||||
root := rootIdent(def.Call.Fun)
|
||||
if root == nil {
|
||||
return true // could not find the receiver of the defer call.
|
||||
}
|
||||
|
||||
if resp.Obj == root.Obj {
|
||||
pass.Reportf(root.Pos(), "using %s before checking for errors", resp.Name)
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// isHTTPFuncOrMethodOnClient checks whether the given call expression is on
|
||||
// either a function of the net/http package or a method of http.Client that
|
||||
// returns (*http.Response, error).
|
||||
func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
|
||||
fun, _ := expr.Fun.(*ast.SelectorExpr)
|
||||
sig, _ := info.Types[fun].Type.(*types.Signature)
|
||||
if sig == nil {
|
||||
return false // the call is not of the form x.f()
|
||||
}
|
||||
|
||||
res := sig.Results()
|
||||
if res.Len() != 2 {
|
||||
return false // the function called does not return two values.
|
||||
}
|
||||
if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "net/http", "Response") {
|
||||
return false // the first return type is not *http.Response.
|
||||
}
|
||||
|
||||
errorType := types.Universe.Lookup("error").Type()
|
||||
if !types.Identical(res.At(1).Type(), errorType) {
|
||||
return false // the second return type is not error
|
||||
}
|
||||
|
||||
typ := info.Types[fun.X].Type
|
||||
if typ == nil {
|
||||
id, ok := fun.X.(*ast.Ident)
|
||||
return ok && id.Name == "http" // function in net/http package.
|
||||
}
|
||||
|
||||
if isNamedType(typ, "net/http", "Client") {
|
||||
return true // method on http.Client.
|
||||
}
|
||||
ptr, ok := typ.(*types.Pointer)
|
||||
return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
|
||||
}
|
||||
|
||||
// restOfBlock, given a traversal stack, finds the innermost containing
|
||||
// block and returns the suffix of its statements starting with the
|
||||
// current node (the last element of stack).
|
||||
func restOfBlock(stack []ast.Node) []ast.Stmt {
|
||||
for i := len(stack) - 1; i >= 0; i-- {
|
||||
if b, ok := stack[i].(*ast.BlockStmt); ok {
|
||||
for j, v := range b.List {
|
||||
if v == stack[i+1] {
|
||||
return b.List[j:]
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
|
||||
func rootIdent(n ast.Node) *ast.Ident {
|
||||
switch n := n.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
return rootIdent(n.X)
|
||||
case *ast.Ident:
|
||||
return n
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// isNamedType reports whether t is the named type path.name.
|
||||
func isNamedType(t types.Type, path, name string) bool {
|
||||
n, ok := t.(*types.Named)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
obj := n.Obj()
|
||||
return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
|
||||
}
|
||||
|
||||
func imports(pkg *types.Package, path string) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 httpresponse_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/httpresponse"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, httpresponse.Analyzer, "a")
|
||||
}
|
||||
85
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/testdata/src/a/a.go
generated
vendored
Normal file
85
vendor/golang.org/x/tools/go/analysis/passes/httpresponse/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package a
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func goodHTTPGet() {
|
||||
res, err := http.Get("http://foo.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
func badHTTPGet() {
|
||||
res, err := http.Get("http://foo.com")
|
||||
defer res.Body.Close() // want "using res before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func badHTTPHead() {
|
||||
res, err := http.Head("http://foo.com")
|
||||
defer res.Body.Close() // want "using res before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func goodClientGet() {
|
||||
client := http.DefaultClient
|
||||
res, err := client.Get("http://foo.com")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
}
|
||||
|
||||
func badClientPtrGet() {
|
||||
client := http.DefaultClient
|
||||
resp, err := client.Get("http://foo.com")
|
||||
defer resp.Body.Close() // want "using resp before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func badClientGet() {
|
||||
client := http.Client{}
|
||||
resp, err := client.Get("http://foo.com")
|
||||
defer resp.Body.Close() // want "using resp before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func badClientPtrDo() {
|
||||
client := http.DefaultClient
|
||||
req, err := http.NewRequest("GET", "http://foo.com", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
defer resp.Body.Close() // want "using resp before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func badClientDo() {
|
||||
var client http.Client
|
||||
req, err := http.NewRequest("GET", "http://foo.com", nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
defer resp.Body.Close() // want "using resp before checking for errors"
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
45
vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go
generated
vendored
Normal file
45
vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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 inspect defines an Analyzer that provides an AST inspector
|
||||
// (golang.org/x/tools/go/ast/inspect.Inspect) for the syntax trees of a
|
||||
// package. It is only a building block for other analyzers.
|
||||
//
|
||||
// Example of use in another analysis:
|
||||
//
|
||||
// import "golang.org/x/tools/go/analysis/passes/inspect"
|
||||
//
|
||||
// var Analyzer = &analysis.Analyzer{
|
||||
// ...
|
||||
// Requires: reflect.TypeOf(new(inspect.Analyzer)),
|
||||
// }
|
||||
//
|
||||
// func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
// inspect.Preorder(nil, func(n ast.Node) {
|
||||
// ...
|
||||
// })
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "inspect",
|
||||
Doc: "optimize AST traversal for later passes",
|
||||
Run: run,
|
||||
RunDespiteErrors: true,
|
||||
ResultType: reflect.TypeOf(new(inspector.Inspector)),
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
return inspector.New(pass.Files), nil
|
||||
}
|
||||
106
vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
generated
vendored
Normal file
106
vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go
generated
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Package analysisutil defines various helper functions
|
||||
// used by two or more packages beneath go/analysis.
|
||||
package analysisutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Format returns a string representation of the expression.
|
||||
func Format(fset *token.FileSet, x ast.Expr) string {
|
||||
var b bytes.Buffer
|
||||
printer.Fprint(&b, fset, x)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// HasSideEffects reports whether evaluation of e has side effects.
|
||||
func HasSideEffects(info *types.Info, e ast.Expr) bool {
|
||||
safe := true
|
||||
ast.Inspect(e, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
typVal := info.Types[n.Fun]
|
||||
switch {
|
||||
case typVal.IsType():
|
||||
// Type conversion, which is safe.
|
||||
case typVal.IsBuiltin():
|
||||
// Builtin func, conservatively assumed to not
|
||||
// be safe for now.
|
||||
safe = false
|
||||
return false
|
||||
default:
|
||||
// A non-builtin func or method call.
|
||||
// Conservatively assume that all of them have
|
||||
// side effects for now.
|
||||
safe = false
|
||||
return false
|
||||
}
|
||||
case *ast.UnaryExpr:
|
||||
if n.Op == token.ARROW {
|
||||
safe = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return !safe
|
||||
}
|
||||
|
||||
// Unparen returns e with any enclosing parentheses stripped.
|
||||
func Unparen(e ast.Expr) ast.Expr {
|
||||
for {
|
||||
p, ok := e.(*ast.ParenExpr)
|
||||
if !ok {
|
||||
return e
|
||||
}
|
||||
e = p.X
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFile reads a file and adds it to the FileSet
|
||||
// so that we can report errors against it using lineStart.
|
||||
func ReadFile(fset *token.FileSet, filename string) ([]byte, *token.File, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
tf := fset.AddFile(filename, -1, len(content))
|
||||
tf.SetLinesForContent(content)
|
||||
return content, tf, nil
|
||||
}
|
||||
|
||||
// LineStart returns the position of the start of the specified line
|
||||
// within file f, or NoPos if there is no line of that number.
|
||||
func LineStart(f *token.File, line int) token.Pos {
|
||||
// Use binary search to find the start offset of this line.
|
||||
//
|
||||
// TODO(adonovan): eventually replace this function with the
|
||||
// simpler and more efficient (*go/token.File).LineStart, added
|
||||
// in go1.12.
|
||||
|
||||
min := 0 // inclusive
|
||||
max := f.Size() // exclusive
|
||||
for {
|
||||
offset := (min + max) / 2
|
||||
pos := f.Pos(offset)
|
||||
posn := f.Position(pos)
|
||||
if posn.Line == line {
|
||||
return pos - (token.Pos(posn.Column) - 1)
|
||||
}
|
||||
|
||||
if min+1 >= max {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
if posn.Line < line {
|
||||
min = offset
|
||||
} else {
|
||||
max = offset
|
||||
}
|
||||
}
|
||||
}
|
||||
130
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go
generated
vendored
Normal file
130
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2012 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 loopclosure defines an Analyzer that checks for references to
|
||||
// enclosing loop variables from within nested functions.
|
||||
package loopclosure
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
// TODO(adonovan): also report an error for the following structure,
|
||||
// which is often used to ensure that deferred calls do not accumulate
|
||||
// in a loop:
|
||||
//
|
||||
// for i, x := range c {
|
||||
// func() {
|
||||
// ...reference to i or x...
|
||||
// }()
|
||||
// }
|
||||
|
||||
const Doc = `check references to loop variables from within nested functions
|
||||
|
||||
This analyzer checks for references to loop variables from within a
|
||||
function literal inside the loop body. It checks only instances where
|
||||
the function literal is called in a defer or go statement that is the
|
||||
last statement in the loop body, as otherwise we would need whole
|
||||
program analysis.
|
||||
|
||||
For example:
|
||||
|
||||
for i, v := range s {
|
||||
go func() {
|
||||
println(i, v) // not what you might expect
|
||||
}()
|
||||
}
|
||||
|
||||
See: https://golang.org/doc/go_faq.html#closures_and_goroutines`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "loopclosure",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.RangeStmt)(nil),
|
||||
(*ast.ForStmt)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
// Find the variables updated by the loop statement.
|
||||
var vars []*ast.Ident
|
||||
addVar := func(expr ast.Expr) {
|
||||
if id, ok := expr.(*ast.Ident); ok {
|
||||
vars = append(vars, id)
|
||||
}
|
||||
}
|
||||
var body *ast.BlockStmt
|
||||
switch n := n.(type) {
|
||||
case *ast.RangeStmt:
|
||||
body = n.Body
|
||||
addVar(n.Key)
|
||||
addVar(n.Value)
|
||||
case *ast.ForStmt:
|
||||
body = n.Body
|
||||
switch post := n.Post.(type) {
|
||||
case *ast.AssignStmt:
|
||||
// e.g. for p = head; p != nil; p = p.next
|
||||
for _, lhs := range post.Lhs {
|
||||
addVar(lhs)
|
||||
}
|
||||
case *ast.IncDecStmt:
|
||||
// e.g. for i := 0; i < n; i++
|
||||
addVar(post.X)
|
||||
}
|
||||
}
|
||||
if vars == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Inspect a go or defer statement
|
||||
// if it's the last one in the loop body.
|
||||
// (We give up if there are following statements,
|
||||
// because it's hard to prove go isn't followed by wait,
|
||||
// or defer by return.)
|
||||
if len(body.List) == 0 {
|
||||
return
|
||||
}
|
||||
var last *ast.CallExpr
|
||||
switch s := body.List[len(body.List)-1].(type) {
|
||||
case *ast.GoStmt:
|
||||
last = s.Call
|
||||
case *ast.DeferStmt:
|
||||
last = s.Call
|
||||
default:
|
||||
return
|
||||
}
|
||||
lit, ok := last.Fun.(*ast.FuncLit)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ast.Inspect(lit.Body, func(n ast.Node) bool {
|
||||
id, ok := n.(*ast.Ident)
|
||||
if !ok || id.Obj == nil {
|
||||
return true
|
||||
}
|
||||
if pass.TypesInfo.Types[id].Type == nil {
|
||||
// Not referring to a variable (e.g. struct field name)
|
||||
return true
|
||||
}
|
||||
for _, v := range vars {
|
||||
if v.Obj == id.Obj {
|
||||
pass.Reportf(id.Pos(), "loop variable %s captured by func literal",
|
||||
id.Name)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 loopclosure_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/loopclosure"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, loopclosure.Analyzer, "a")
|
||||
}
|
||||
90
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/testdata/src/a/a.go
generated
vendored
Normal file
90
vendor/golang.org/x/tools/go/analysis/passes/loopclosure/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// This file contains tests for the loopclosure checker.
|
||||
|
||||
package testdata
|
||||
|
||||
func _() {
|
||||
var s []int
|
||||
for i, v := range s {
|
||||
go func() {
|
||||
println(i) // want "loop variable i captured by func literal"
|
||||
println(v) // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i, v := range s {
|
||||
defer func() {
|
||||
println(i) // want "loop variable i captured by func literal"
|
||||
println(v) // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i := range s {
|
||||
go func() {
|
||||
println(i) // want "loop variable i captured by func literal"
|
||||
}()
|
||||
}
|
||||
for _, v := range s {
|
||||
go func() {
|
||||
println(v) // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i, v := range s {
|
||||
go func() {
|
||||
println(i, v)
|
||||
}()
|
||||
println("unfortunately, we don't catch the error above because of this statement")
|
||||
}
|
||||
for i, v := range s {
|
||||
go func(i, v int) {
|
||||
println(i, v)
|
||||
}(i, v)
|
||||
}
|
||||
for i, v := range s {
|
||||
i, v := i, v
|
||||
go func() {
|
||||
println(i, v)
|
||||
}()
|
||||
}
|
||||
// If the key of the range statement is not an identifier
|
||||
// the code should not panic (it used to).
|
||||
var x [2]int
|
||||
var f int
|
||||
for x[0], f = range s {
|
||||
go func() {
|
||||
_ = f // want "loop variable f captured by func literal"
|
||||
}()
|
||||
}
|
||||
type T struct {
|
||||
v int
|
||||
}
|
||||
for _, v := range s {
|
||||
go func() {
|
||||
_ = T{v: 1}
|
||||
_ = map[int]int{v: 1} // want "loop variable v captured by func literal"
|
||||
}()
|
||||
}
|
||||
|
||||
// ordinary for-loops
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
print(i) // want "loop variable i captured by func literal"
|
||||
}()
|
||||
}
|
||||
for i, j := 0, 1; i < 100; i, j = j, i+j {
|
||||
go func() {
|
||||
print(j) // want "loop variable j captured by func literal"
|
||||
}()
|
||||
}
|
||||
type cons struct {
|
||||
car int
|
||||
cdr *cons
|
||||
}
|
||||
var head *cons
|
||||
for p := head; p != nil; p = p.cdr {
|
||||
go func() {
|
||||
print(p.car) // want "loop variable p captured by func literal"
|
||||
}()
|
||||
}
|
||||
}
|
||||
10
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/cmd/lostcancel/main.go
generated
vendored
Normal file
10
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/cmd/lostcancel/main.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// The nilness command applies the golang.org/x/tools/go/analysis/passes/lostcancel
|
||||
// analysis to the specified packages of Go source code.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(lostcancel.Analyzer) }
|
||||
310
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go
generated
vendored
Normal file
310
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
// Copyright 2016 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 lostcancel defines an Analyzer that checks for failure to
|
||||
// call a context cancelation function.
|
||||
package lostcancel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/ctrlflow"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/cfg"
|
||||
)
|
||||
|
||||
const Doc = `check cancel func returned by context.WithCancel is called
|
||||
|
||||
The cancelation function returned by context.WithCancel, WithTimeout,
|
||||
and WithDeadline must be called or the new context will remain live
|
||||
until its parent context is cancelled.
|
||||
(The background context is never cancelled.)`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "lostcancel",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
Requires: []*analysis.Analyzer{
|
||||
inspect.Analyzer,
|
||||
ctrlflow.Analyzer,
|
||||
},
|
||||
}
|
||||
|
||||
const debug = false
|
||||
|
||||
var contextPackage = "context"
|
||||
|
||||
// checkLostCancel reports a failure to the call the cancel function
|
||||
// returned by context.WithCancel, either because the variable was
|
||||
// assigned to the blank identifier, or because there exists a
|
||||
// control-flow path from the call to a return statement and that path
|
||||
// does not "use" the cancel function. Any reference to the variable
|
||||
// counts as a use, even within a nested function literal.
|
||||
//
|
||||
// checkLostCancel analyzes a single named or literal function.
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// Fast path: bypass check if file doesn't use context.WithCancel.
|
||||
if !hasImport(pass.Pkg, contextPackage) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Call runFunc for each Func{Decl,Lit}.
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
nodeTypes := []ast.Node{
|
||||
(*ast.FuncLit)(nil),
|
||||
(*ast.FuncDecl)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeTypes, func(n ast.Node) {
|
||||
runFunc(pass, n)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func runFunc(pass *analysis.Pass, node ast.Node) {
|
||||
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
|
||||
cancelvars := make(map[*types.Var]ast.Node)
|
||||
|
||||
// TODO(adonovan): opt: refactor to make a single pass
|
||||
// over the AST using inspect.WithStack and node types
|
||||
// {FuncDecl,FuncLit,CallExpr,SelectorExpr}.
|
||||
|
||||
// Find the set of cancel vars to analyze.
|
||||
stack := make([]ast.Node, 0, 32)
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
switch n.(type) {
|
||||
case *ast.FuncLit:
|
||||
if len(stack) > 0 {
|
||||
return false // don't stray into nested functions
|
||||
}
|
||||
case nil:
|
||||
stack = stack[:len(stack)-1] // pop
|
||||
return true
|
||||
}
|
||||
stack = append(stack, n) // push
|
||||
|
||||
// Look for [{AssignStmt,ValueSpec} CallExpr SelectorExpr]:
|
||||
//
|
||||
// ctx, cancel := context.WithCancel(...)
|
||||
// ctx, cancel = context.WithCancel(...)
|
||||
// var ctx, cancel = context.WithCancel(...)
|
||||
//
|
||||
if isContextWithCancel(pass.TypesInfo, n) && isCall(stack[len(stack)-2]) {
|
||||
var id *ast.Ident // id of cancel var
|
||||
stmt := stack[len(stack)-3]
|
||||
switch stmt := stmt.(type) {
|
||||
case *ast.ValueSpec:
|
||||
if len(stmt.Names) > 1 {
|
||||
id = stmt.Names[1]
|
||||
}
|
||||
case *ast.AssignStmt:
|
||||
if len(stmt.Lhs) > 1 {
|
||||
id, _ = stmt.Lhs[1].(*ast.Ident)
|
||||
}
|
||||
}
|
||||
if id != nil {
|
||||
if id.Name == "_" {
|
||||
pass.Reportf(id.Pos(),
|
||||
"the cancel function returned by context.%s should be called, not discarded, to avoid a context leak",
|
||||
n.(*ast.SelectorExpr).Sel.Name)
|
||||
} else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok {
|
||||
cancelvars[v] = stmt
|
||||
} else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok {
|
||||
cancelvars[v] = stmt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
if len(cancelvars) == 0 {
|
||||
return // no need to inspect CFG
|
||||
}
|
||||
|
||||
// Obtain the CFG.
|
||||
cfgs := pass.ResultOf[ctrlflow.Analyzer].(*ctrlflow.CFGs)
|
||||
var g *cfg.CFG
|
||||
var sig *types.Signature
|
||||
switch node := node.(type) {
|
||||
case *ast.FuncDecl:
|
||||
sig, _ = pass.TypesInfo.Defs[node.Name].Type().(*types.Signature)
|
||||
if node.Name.Name == "main" && sig.Recv() == nil && pass.Pkg.Name() == "main" {
|
||||
// Returning from main.main terminates the process,
|
||||
// so there's no need to cancel contexts.
|
||||
return
|
||||
}
|
||||
g = cfgs.FuncDecl(node)
|
||||
|
||||
case *ast.FuncLit:
|
||||
sig, _ = pass.TypesInfo.Types[node.Type].Type.(*types.Signature)
|
||||
g = cfgs.FuncLit(node)
|
||||
}
|
||||
if sig == nil {
|
||||
return // missing type information
|
||||
}
|
||||
|
||||
// Print CFG.
|
||||
if debug {
|
||||
fmt.Println(g.Format(pass.Fset))
|
||||
}
|
||||
|
||||
// Examine the CFG for each variable in turn.
|
||||
// (It would be more efficient to analyze all cancelvars in a
|
||||
// single pass over the AST, but seldom is there more than one.)
|
||||
for v, stmt := range cancelvars {
|
||||
if ret := lostCancelPath(pass, g, v, stmt, sig); ret != nil {
|
||||
lineno := pass.Fset.Position(stmt.Pos()).Line
|
||||
pass.Reportf(stmt.Pos(), "the %s function is not used on all paths (possible context leak)", v.Name())
|
||||
pass.Reportf(ret.Pos(), "this return statement may be reached without using the %s var defined on line %d", v.Name(), lineno)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isCall(n ast.Node) bool { _, ok := n.(*ast.CallExpr); return ok }
|
||||
|
||||
func hasImport(pkg *types.Package, path string) bool {
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Path() == path {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isContextWithCancel reports whether n is one of the qualified identifiers
|
||||
// context.With{Cancel,Timeout,Deadline}.
|
||||
func isContextWithCancel(info *types.Info, n ast.Node) bool {
|
||||
if sel, ok := n.(*ast.SelectorExpr); ok {
|
||||
switch sel.Sel.Name {
|
||||
case "WithCancel", "WithTimeout", "WithDeadline":
|
||||
if x, ok := sel.X.(*ast.Ident); ok {
|
||||
if pkgname, ok := info.Uses[x].(*types.PkgName); ok {
|
||||
return pkgname.Imported().Path() == contextPackage
|
||||
}
|
||||
// Import failed, so we can't check package path.
|
||||
// Just check the local package name (heuristic).
|
||||
return x.Name == "context"
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// lostCancelPath finds a path through the CFG, from stmt (which defines
|
||||
// the 'cancel' variable v) to a return statement, that doesn't "use" v.
|
||||
// If it finds one, it returns the return statement (which may be synthetic).
|
||||
// sig is the function's type, if known.
|
||||
func lostCancelPath(pass *analysis.Pass, g *cfg.CFG, v *types.Var, stmt ast.Node, sig *types.Signature) *ast.ReturnStmt {
|
||||
vIsNamedResult := sig != nil && tupleContains(sig.Results(), v)
|
||||
|
||||
// uses reports whether stmts contain a "use" of variable v.
|
||||
uses := func(pass *analysis.Pass, v *types.Var, stmts []ast.Node) bool {
|
||||
found := false
|
||||
for _, stmt := range stmts {
|
||||
ast.Inspect(stmt, func(n ast.Node) bool {
|
||||
switch n := n.(type) {
|
||||
case *ast.Ident:
|
||||
if pass.TypesInfo.Uses[n] == v {
|
||||
found = true
|
||||
}
|
||||
case *ast.ReturnStmt:
|
||||
// A naked return statement counts as a use
|
||||
// of the named result variables.
|
||||
if n.Results == nil && vIsNamedResult {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return !found
|
||||
})
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// blockUses computes "uses" for each block, caching the result.
|
||||
memo := make(map[*cfg.Block]bool)
|
||||
blockUses := func(pass *analysis.Pass, v *types.Var, b *cfg.Block) bool {
|
||||
res, ok := memo[b]
|
||||
if !ok {
|
||||
res = uses(pass, v, b.Nodes)
|
||||
memo[b] = res
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Find the var's defining block in the CFG,
|
||||
// plus the rest of the statements of that block.
|
||||
var defblock *cfg.Block
|
||||
var rest []ast.Node
|
||||
outer:
|
||||
for _, b := range g.Blocks {
|
||||
for i, n := range b.Nodes {
|
||||
if n == stmt {
|
||||
defblock = b
|
||||
rest = b.Nodes[i+1:]
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
if defblock == nil {
|
||||
panic("internal error: can't find defining block for cancel var")
|
||||
}
|
||||
|
||||
// Is v "used" in the remainder of its defining block?
|
||||
if uses(pass, v, rest) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Does the defining block return without using v?
|
||||
if ret := defblock.Return(); ret != nil {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Search the CFG depth-first for a path, from defblock to a
|
||||
// return block, in which v is never "used".
|
||||
seen := make(map[*cfg.Block]bool)
|
||||
var search func(blocks []*cfg.Block) *ast.ReturnStmt
|
||||
search = func(blocks []*cfg.Block) *ast.ReturnStmt {
|
||||
for _, b := range blocks {
|
||||
if !seen[b] {
|
||||
seen[b] = true
|
||||
|
||||
// Prune the search if the block uses v.
|
||||
if blockUses(pass, v, b) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Found path to return statement?
|
||||
if ret := b.Return(); ret != nil {
|
||||
if debug {
|
||||
fmt.Printf("found path to return in block %s\n", b)
|
||||
}
|
||||
return ret // found
|
||||
}
|
||||
|
||||
// Recur
|
||||
if ret := search(b.Succs); ret != nil {
|
||||
if debug {
|
||||
fmt.Printf(" from block %s\n", b)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return search(defblock.Succs)
|
||||
}
|
||||
|
||||
func tupleContains(tuple *types.Tuple, v *types.Var) bool {
|
||||
for i := 0; i < tuple.Len(); i++ {
|
||||
if tuple.At(i) == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 lostcancel_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, lostcancel.Analyzer, "a", "b")
|
||||
}
|
||||
173
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/a/a.go
generated
vendored
Normal file
173
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2016 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 a
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bg = context.Background()
|
||||
|
||||
// Check the three functions and assignment forms (var, :=, =) we look for.
|
||||
// (Do these early: line numbers are fragile.)
|
||||
func _() {
|
||||
var _, cancel = context.WithCancel(bg) // want `the cancel function is not used on all paths \(possible context leak\)`
|
||||
if false {
|
||||
_ = cancel
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var defined on line 20"
|
||||
|
||||
func _() {
|
||||
_, cancel2 := context.WithDeadline(bg, time.Time{}) // want "the cancel2 function is not used..."
|
||||
if false {
|
||||
_ = cancel2
|
||||
}
|
||||
} // want "may be reached without using the cancel2 var defined on line 27"
|
||||
|
||||
func _() {
|
||||
var cancel3 func()
|
||||
_, cancel3 = context.WithTimeout(bg, 0) // want "function is not used..."
|
||||
if false {
|
||||
_ = cancel3
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel3 var defined on line 35"
|
||||
|
||||
func _() {
|
||||
ctx, _ := context.WithCancel(bg) // want "the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak"
|
||||
ctx, _ = context.WithTimeout(bg, 0) // want "the cancel function returned by context.WithTimeout should be called, not discarded, to avoid a context leak"
|
||||
ctx, _ = context.WithDeadline(bg, time.Time{}) // want "the cancel function returned by context.WithDeadline should be called, not discarded, to avoid a context leak"
|
||||
_ = ctx
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
defer cancel() // ok
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
if condition {
|
||||
cancel()
|
||||
}
|
||||
return // want "this return statement may be reached without using the cancel var"
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
if condition {
|
||||
cancel()
|
||||
} else {
|
||||
// ok: infinite loop
|
||||
for {
|
||||
print(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
if condition {
|
||||
cancel()
|
||||
} else {
|
||||
for i := 0; i < 10; i++ {
|
||||
print(0)
|
||||
}
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var"
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
// ok: used on all paths
|
||||
switch someInt {
|
||||
case 0:
|
||||
new(testing.T).FailNow()
|
||||
case 1:
|
||||
log.Fatal()
|
||||
case 2:
|
||||
cancel()
|
||||
case 3:
|
||||
print("hi")
|
||||
fallthrough
|
||||
default:
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func _() {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
switch someInt {
|
||||
case 0:
|
||||
new(testing.T).FailNow()
|
||||
case 1:
|
||||
log.Fatal()
|
||||
case 2:
|
||||
cancel()
|
||||
case 3:
|
||||
print("hi") // falls through to implicit return
|
||||
default:
|
||||
os.Exit(1)
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var"
|
||||
|
||||
func _(ch chan int) {
|
||||
_, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
select {
|
||||
case <-ch:
|
||||
new(testing.T).FailNow()
|
||||
case ch <- 2:
|
||||
print("hi") // falls through to implicit return
|
||||
case ch <- 1:
|
||||
cancel()
|
||||
default:
|
||||
os.Exit(1)
|
||||
}
|
||||
} // want "this return statement may be reached without using the cancel var"
|
||||
|
||||
func _(ch chan int) {
|
||||
_, cancel := context.WithCancel(bg)
|
||||
// A blocking select must execute one of its cases.
|
||||
select {
|
||||
case <-ch:
|
||||
panic(0)
|
||||
}
|
||||
if false {
|
||||
_ = cancel
|
||||
}
|
||||
}
|
||||
|
||||
func _() {
|
||||
go func() {
|
||||
ctx, cancel := context.WithCancel(bg) // want "not used on all paths"
|
||||
if false {
|
||||
_ = cancel
|
||||
}
|
||||
print(ctx)
|
||||
}() // want "may be reached without using the cancel var"
|
||||
}
|
||||
|
||||
var condition bool
|
||||
var someInt int
|
||||
|
||||
// Regression test for Go issue 16143.
|
||||
func _() {
|
||||
var x struct{ f func() }
|
||||
x.f()
|
||||
}
|
||||
|
||||
// Regression test for Go issue 16230.
|
||||
func _() (ctx context.Context, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(bg)
|
||||
return // a naked return counts as a load of the named result values
|
||||
}
|
||||
|
||||
// Same as above, but for literal function.
|
||||
var _ = func() (ctx context.Context, cancel func()) {
|
||||
ctx, cancel = context.WithCancel(bg)
|
||||
return
|
||||
}
|
||||
26
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/b/b.go
generated
vendored
Normal file
26
vendor/golang.org/x/tools/go/analysis/passes/lostcancel/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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 main
|
||||
|
||||
import "context"
|
||||
|
||||
// Return from main is handled specially.
|
||||
// Since the program exits, there's no need to call cancel.
|
||||
func main() {
|
||||
_, cancel := context.WithCancel(nil)
|
||||
if maybe {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func notMain() {
|
||||
_, cancel := context.WithCancel(nil) // want "cancel function.*not used"
|
||||
|
||||
if maybe {
|
||||
cancel()
|
||||
}
|
||||
} // want "return statement.*reached without using the cancel"
|
||||
|
||||
var maybe bool
|
||||
74
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
generated
vendored
Normal file
74
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2013 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 nilfunc defines an Analyzer that checks for useless
|
||||
// comparisons against nil.
|
||||
package nilfunc
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
const Doc = `check for useless comparisons between functions and nil
|
||||
|
||||
A useless comparison is one like f == nil as opposed to f() == nil.`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "nilfunc",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.BinaryExpr)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
e := n.(*ast.BinaryExpr)
|
||||
|
||||
// Only want == or != comparisons.
|
||||
if e.Op != token.EQL && e.Op != token.NEQ {
|
||||
return
|
||||
}
|
||||
|
||||
// Only want comparisons with a nil identifier on one side.
|
||||
var e2 ast.Expr
|
||||
switch {
|
||||
case pass.TypesInfo.Types[e.X].IsNil():
|
||||
e2 = e.Y
|
||||
case pass.TypesInfo.Types[e.Y].IsNil():
|
||||
e2 = e.X
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// Only want identifiers or selector expressions.
|
||||
var obj types.Object
|
||||
switch v := e2.(type) {
|
||||
case *ast.Ident:
|
||||
obj = pass.TypesInfo.Uses[v]
|
||||
case *ast.SelectorExpr:
|
||||
obj = pass.TypesInfo.Uses[v.Sel]
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// Only want functions.
|
||||
if _, ok := obj.(*types.Func); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
pass.Reportf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 nilfunc_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/nilfunc"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, nilfunc.Analyzer, "a")
|
||||
}
|
||||
35
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/testdata/src/a/a.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2013 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 a
|
||||
|
||||
func F() {}
|
||||
|
||||
type T struct {
|
||||
F func()
|
||||
}
|
||||
|
||||
func (T) M() {}
|
||||
|
||||
var Fv = F
|
||||
|
||||
func Comparison() {
|
||||
var t T
|
||||
var fn func()
|
||||
if fn == nil || Fv == nil || t.F == nil {
|
||||
// no error; these func vars or fields may be nil
|
||||
}
|
||||
if F == nil { // want "comparison of function F == nil is always false"
|
||||
panic("can't happen")
|
||||
}
|
||||
if t.M == nil { // want "comparison of function M == nil is always false"
|
||||
panic("can't happen")
|
||||
}
|
||||
if F != nil { // want "comparison of function F != nil is always true"
|
||||
if t.M != nil { // want "comparison of function M != nil is always true"
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("can't happen")
|
||||
}
|
||||
10
vendor/golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness/main.go
generated
vendored
Normal file
10
vendor/golang.org/x/tools/go/analysis/passes/nilness/cmd/nilness/main.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// The nilness command applies the golang.org/x/tools/go/analysis/passes/nilness
|
||||
// analysis to the specified packages of Go source code.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/nilness"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(nilness.Analyzer) }
|
||||
271
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go
generated
vendored
Normal file
271
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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 nilness inspects the control-flow graph of an SSA function
|
||||
// and reports errors such as nil pointer dereferences and degenerate
|
||||
// nil pointer comparisons.
|
||||
package nilness
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/buildssa"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
)
|
||||
|
||||
const Doc = `check for redundant or impossible nil comparisons
|
||||
|
||||
The nilness checker inspects the control-flow graph of each function in
|
||||
a package and reports nil pointer dereferences and degenerate nil
|
||||
pointers. A degenerate comparison is of the form x==nil or x!=nil where x
|
||||
is statically known to be nil or non-nil. These are often a mistake,
|
||||
especially in control flow related to errors.
|
||||
|
||||
This check reports conditions such as:
|
||||
|
||||
if f == nil { // impossible condition (f is a function)
|
||||
}
|
||||
|
||||
and:
|
||||
|
||||
p := &v
|
||||
...
|
||||
if p != nil { // tautological condition
|
||||
}
|
||||
|
||||
and:
|
||||
|
||||
if p == nil {
|
||||
print(*p) // nil dereference
|
||||
}
|
||||
`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "nilness",
|
||||
Doc: Doc,
|
||||
Run: run,
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
|
||||
for _, fn := range ssainput.SrcFuncs {
|
||||
runFunc(pass, fn)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func runFunc(pass *analysis.Pass, fn *ssa.Function) {
|
||||
reportf := func(category string, pos token.Pos, format string, args ...interface{}) {
|
||||
pass.Report(analysis.Diagnostic{
|
||||
Pos: pos,
|
||||
Category: category,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
})
|
||||
}
|
||||
|
||||
// notNil reports an error if v is provably nil.
|
||||
notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) {
|
||||
if nilnessOf(stack, v) == isnil {
|
||||
reportf("nilderef", instr.Pos(), "nil dereference in "+descr)
|
||||
}
|
||||
}
|
||||
|
||||
// visit visits reachable blocks of the CFG in dominance order,
|
||||
// maintaining a stack of dominating nilness facts.
|
||||
//
|
||||
// By traversing the dom tree, we can pop facts off the stack as
|
||||
// soon as we've visited a subtree. Had we traversed the CFG,
|
||||
// we would need to retain the set of facts for each block.
|
||||
seen := make([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i
|
||||
var visit func(b *ssa.BasicBlock, stack []fact)
|
||||
visit = func(b *ssa.BasicBlock, stack []fact) {
|
||||
if seen[b.Index] {
|
||||
return
|
||||
}
|
||||
seen[b.Index] = true
|
||||
|
||||
// Report nil dereferences.
|
||||
for _, instr := range b.Instrs {
|
||||
switch instr := instr.(type) {
|
||||
case ssa.CallInstruction:
|
||||
notNil(stack, instr, instr.Common().Value,
|
||||
instr.Common().Description())
|
||||
case *ssa.FieldAddr:
|
||||
notNil(stack, instr, instr.X, "field selection")
|
||||
case *ssa.IndexAddr:
|
||||
notNil(stack, instr, instr.X, "index operation")
|
||||
case *ssa.MapUpdate:
|
||||
notNil(stack, instr, instr.Map, "map update")
|
||||
case *ssa.Slice:
|
||||
// A nilcheck occurs in ptr[:] iff ptr is a pointer to an array.
|
||||
if _, ok := instr.X.Type().Underlying().(*types.Pointer); ok {
|
||||
notNil(stack, instr, instr.X, "slice operation")
|
||||
}
|
||||
case *ssa.Store:
|
||||
notNil(stack, instr, instr.Addr, "store")
|
||||
case *ssa.TypeAssert:
|
||||
notNil(stack, instr, instr.X, "type assertion")
|
||||
case *ssa.UnOp:
|
||||
if instr.Op == token.MUL { // *X
|
||||
notNil(stack, instr, instr.X, "load")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For nil comparison blocks, report an error if the condition
|
||||
// is degenerate, and push a nilness fact on the stack when
|
||||
// visiting its true and false successor blocks.
|
||||
if binop, tsucc, fsucc := eq(b); binop != nil {
|
||||
xnil := nilnessOf(stack, binop.X)
|
||||
ynil := nilnessOf(stack, binop.Y)
|
||||
|
||||
if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) {
|
||||
// Degenerate condition:
|
||||
// the nilness of both operands is known,
|
||||
// and at least one of them is nil.
|
||||
var adj string
|
||||
if (xnil == ynil) == (binop.Op == token.EQL) {
|
||||
adj = "tautological"
|
||||
} else {
|
||||
adj = "impossible"
|
||||
}
|
||||
reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil)
|
||||
|
||||
// If tsucc's or fsucc's sole incoming edge is impossible,
|
||||
// it is unreachable. Prune traversal of it and
|
||||
// all the blocks it dominates.
|
||||
// (We could be more precise with full dataflow
|
||||
// analysis of control-flow joins.)
|
||||
var skip *ssa.BasicBlock
|
||||
if xnil == ynil {
|
||||
skip = fsucc
|
||||
} else {
|
||||
skip = tsucc
|
||||
}
|
||||
for _, d := range b.Dominees() {
|
||||
if d == skip && len(d.Preds) == 1 {
|
||||
continue
|
||||
}
|
||||
visit(d, stack)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// "if x == nil" or "if nil == y" condition; x, y are unknown.
|
||||
if xnil == isnil || ynil == isnil {
|
||||
var f fact
|
||||
if xnil == isnil {
|
||||
// x is nil, y is unknown:
|
||||
// t successor learns y is nil.
|
||||
f = fact{binop.Y, isnil}
|
||||
} else {
|
||||
// x is nil, y is unknown:
|
||||
// t successor learns x is nil.
|
||||
f = fact{binop.X, isnil}
|
||||
}
|
||||
|
||||
for _, d := range b.Dominees() {
|
||||
// Successor blocks learn a fact
|
||||
// only at non-critical edges.
|
||||
// (We could do be more precise with full dataflow
|
||||
// analysis of control-flow joins.)
|
||||
s := stack
|
||||
if len(d.Preds) == 1 {
|
||||
if d == tsucc {
|
||||
s = append(s, f)
|
||||
} else if d == fsucc {
|
||||
s = append(s, f.negate())
|
||||
}
|
||||
}
|
||||
visit(d, s)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, d := range b.Dominees() {
|
||||
visit(d, stack)
|
||||
}
|
||||
}
|
||||
|
||||
// Visit the entry block. No need to visit fn.Recover.
|
||||
if fn.Blocks != nil {
|
||||
visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty
|
||||
}
|
||||
}
|
||||
|
||||
// A fact records that a block is dominated
|
||||
// by the condition v == nil or v != nil.
|
||||
type fact struct {
|
||||
value ssa.Value
|
||||
nilness nilness
|
||||
}
|
||||
|
||||
func (f fact) negate() fact { return fact{f.value, -f.nilness} }
|
||||
|
||||
type nilness int
|
||||
|
||||
const (
|
||||
isnonnil = -1
|
||||
unknown nilness = 0
|
||||
isnil = 1
|
||||
)
|
||||
|
||||
var nilnessStrings = []string{"non-nil", "unknown", "nil"}
|
||||
|
||||
func (n nilness) String() string { return nilnessStrings[n+1] }
|
||||
|
||||
// nilnessOf reports whether v is definitely nil, definitely not nil,
|
||||
// or unknown given the dominating stack of facts.
|
||||
func nilnessOf(stack []fact, v ssa.Value) nilness {
|
||||
// Is value intrinsically nil or non-nil?
|
||||
switch v := v.(type) {
|
||||
case *ssa.Alloc,
|
||||
*ssa.FieldAddr,
|
||||
*ssa.FreeVar,
|
||||
*ssa.Function,
|
||||
*ssa.Global,
|
||||
*ssa.IndexAddr,
|
||||
*ssa.MakeChan,
|
||||
*ssa.MakeClosure,
|
||||
*ssa.MakeInterface,
|
||||
*ssa.MakeMap,
|
||||
*ssa.MakeSlice:
|
||||
return isnonnil
|
||||
case *ssa.Const:
|
||||
if v.IsNil() {
|
||||
return isnil
|
||||
} else {
|
||||
return isnonnil
|
||||
}
|
||||
}
|
||||
|
||||
// Search dominating control-flow facts.
|
||||
for _, f := range stack {
|
||||
if f.value == v {
|
||||
return f.nilness
|
||||
}
|
||||
}
|
||||
return unknown
|
||||
}
|
||||
|
||||
// If b ends with an equality comparison, eq returns the operation and
|
||||
// its true (equal) and false (not equal) successors.
|
||||
func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) {
|
||||
if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok {
|
||||
if binop, ok := If.Cond.(*ssa.BinOp); ok {
|
||||
switch binop.Op {
|
||||
case token.EQL:
|
||||
return binop, b.Succs[0], b.Succs[1]
|
||||
case token.NEQ:
|
||||
return binop, b.Succs[1], b.Succs[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, nil, nil
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 nilness_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/nilness"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, nilness.Analyzer, "a")
|
||||
}
|
||||
91
vendor/golang.org/x/tools/go/analysis/passes/nilness/testdata/src/a/a.go
generated
vendored
Normal file
91
vendor/golang.org/x/tools/go/analysis/passes/nilness/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package a
|
||||
|
||||
type X struct{ f, g int }
|
||||
|
||||
func f(x, y *X) {
|
||||
if x == nil {
|
||||
print(x.f) // want "nil dereference in field selection"
|
||||
} else {
|
||||
print(x.f)
|
||||
}
|
||||
|
||||
if x == nil {
|
||||
if nil != y {
|
||||
print(1)
|
||||
panic(0)
|
||||
}
|
||||
x.f = 1 // want "nil dereference in field selection"
|
||||
y.f = 1 // want "nil dereference in field selection"
|
||||
}
|
||||
|
||||
var f func()
|
||||
if f == nil { // want "tautological condition: nil == nil"
|
||||
go f() // want "nil dereference in dynamic function call"
|
||||
} else {
|
||||
// This block is unreachable,
|
||||
// so we don't report an error for the
|
||||
// nil dereference in the call.
|
||||
defer f()
|
||||
}
|
||||
}
|
||||
|
||||
func f2(ptr *[3]int, i interface{}) {
|
||||
if ptr != nil {
|
||||
print(ptr[:])
|
||||
*ptr = [3]int{}
|
||||
print(*ptr)
|
||||
} else {
|
||||
print(ptr[:]) // want "nil dereference in slice operation"
|
||||
*ptr = [3]int{} // want "nil dereference in store"
|
||||
print(*ptr) // want "nil dereference in load"
|
||||
|
||||
if ptr != nil { // want "impossible condition: nil != nil"
|
||||
// Dominated by ptr==nil and ptr!=nil,
|
||||
// this block is unreachable.
|
||||
// We do not report errors within it.
|
||||
print(*ptr)
|
||||
}
|
||||
}
|
||||
|
||||
if i != nil {
|
||||
print(i.(interface{ f() }))
|
||||
} else {
|
||||
print(i.(interface{ f() })) // want "nil dereference in type assertion"
|
||||
}
|
||||
}
|
||||
|
||||
func g() error
|
||||
|
||||
func f3() error {
|
||||
err := g()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil && err.Error() == "foo" { // want "impossible condition: nil != nil"
|
||||
print(0)
|
||||
}
|
||||
ch := make(chan int)
|
||||
if ch == nil { // want "impossible condition: non-nil == nil"
|
||||
print(0)
|
||||
}
|
||||
if ch != nil { // want "tautological condition: non-nil != nil"
|
||||
print(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func h(err error, b bool) {
|
||||
if err != nil && b {
|
||||
return
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func i(*int) error {
|
||||
for {
|
||||
if err := g(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
127
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go
generated
vendored
Normal file
127
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// 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.
|
||||
|
||||
// The pkgfact package is a demonstration and test of the package fact
|
||||
// mechanism.
|
||||
//
|
||||
// The output of the pkgfact analysis is a set of key/values pairs
|
||||
// gathered from the analyzed package and its imported dependencies.
|
||||
// Each key/value pair comes from a top-level constant declaration
|
||||
// whose name starts and ends with "_". For example:
|
||||
//
|
||||
// package p
|
||||
//
|
||||
// const _greeting_ = "hello"
|
||||
// const _audience_ = "world"
|
||||
//
|
||||
// the pkgfact analysis output for package p would be:
|
||||
//
|
||||
// {"greeting": "hello", "audience": "world"}.
|
||||
//
|
||||
// In addition, the analysis reports a diagnostic at each import
|
||||
// showing which key/value pairs it contributes.
|
||||
package pkgfact
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "pkgfact",
|
||||
Doc: "gather name/value pairs from constant declarations",
|
||||
Run: run,
|
||||
FactTypes: []analysis.Fact{new(pairsFact)},
|
||||
ResultType: reflect.TypeOf(map[string]string{}),
|
||||
}
|
||||
|
||||
// A pairsFact is a package-level fact that records
|
||||
// an set of key=value strings accumulated from constant
|
||||
// declarations in this package and its dependencies.
|
||||
// Elements are ordered by keys, which are unique.
|
||||
type pairsFact []string
|
||||
|
||||
func (f *pairsFact) AFact() {}
|
||||
func (f *pairsFact) String() string { return "pairs(" + strings.Join(*f, ", ") + ")" }
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
result := make(map[string]string)
|
||||
|
||||
// At each import, print the fact from the imported
|
||||
// package and accumulate its information into the result.
|
||||
// (Warning: accumulation leads to quadratic growth of work.)
|
||||
doImport := func(spec *ast.ImportSpec) {
|
||||
pkg := imported(pass.TypesInfo, spec)
|
||||
var fact pairsFact
|
||||
if pass.ImportPackageFact(pkg, &fact) {
|
||||
for _, pair := range fact {
|
||||
eq := strings.IndexByte(pair, '=')
|
||||
result[pair[:eq]] = pair[1+eq:]
|
||||
}
|
||||
pass.Reportf(spec.Pos(), "%s", strings.Join(fact, " "))
|
||||
}
|
||||
}
|
||||
|
||||
// At each "const _name_ = value", add a fact into env.
|
||||
doConst := func(spec *ast.ValueSpec) {
|
||||
if len(spec.Names) == len(spec.Values) {
|
||||
for i := range spec.Names {
|
||||
name := spec.Names[i].Name
|
||||
if strings.HasPrefix(name, "_") && strings.HasSuffix(name, "_") {
|
||||
|
||||
if key := strings.Trim(name, "_"); key != "" {
|
||||
value := pass.TypesInfo.Types[spec.Values[i]].Value.String()
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range pass.Files {
|
||||
for _, decl := range f.Decls {
|
||||
if decl, ok := decl.(*ast.GenDecl); ok {
|
||||
for _, spec := range decl.Specs {
|
||||
switch decl.Tok {
|
||||
case token.IMPORT:
|
||||
doImport(spec.(*ast.ImportSpec))
|
||||
case token.CONST:
|
||||
doConst(spec.(*ast.ValueSpec))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort/deduplicate the result and save it as a package fact.
|
||||
keys := make([]string, 0, len(result))
|
||||
for key := range result {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
var fact pairsFact
|
||||
for _, key := range keys {
|
||||
fact = append(fact, fmt.Sprintf("%s=%s", key, result[key]))
|
||||
}
|
||||
if len(fact) > 0 {
|
||||
pass.ExportPackageFact(&fact)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
|
||||
obj, ok := info.Implicits[spec]
|
||||
if !ok {
|
||||
obj = info.Defs[spec.Name] // renaming import
|
||||
}
|
||||
return obj.(*types.PkgName).Imported()
|
||||
}
|
||||
17
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact_test.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact_test.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// 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 pkgfact_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/pkgfact"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, pkgfact.Analyzer, "c")
|
||||
}
|
||||
4
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/a/a.go
generated
vendored
Normal file
4
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package a
|
||||
|
||||
const _greeting_ = "hello"
|
||||
const _audience_ = "world"
|
||||
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/b/b.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package b
|
||||
|
||||
import _ "a"
|
||||
|
||||
const _pi_ = 3.14159
|
||||
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/c/c.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/go/analysis/passes/pkgfact/testdata/src/c/c.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// want package:`pairs\(audience="world", greeting="hello", pi=3.14159\)`
|
||||
|
||||
package c
|
||||
|
||||
import _ "b" // want `audience="world" greeting="hello" pi=3.14159`
|
||||
1017
vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
Normal file
1017
vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
14
vendor/golang.org/x/tools/go/analysis/passes/printf/printf_test.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/go/analysis/passes/printf/printf_test.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package printf_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/printf"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
printf.Analyzer.Flags.Set("funcs", "Warn,Warnf")
|
||||
analysistest.Run(t, testdata, printf.Analyzer, "a", "b")
|
||||
}
|
||||
710
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/a/a.go
generated
vendored
Normal file
710
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,710 @@
|
||||
// Copyright 2010 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.
|
||||
|
||||
// This file contains tests for the printf checker.
|
||||
|
||||
package a
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
logpkg "log" // renamed to make it harder to see
|
||||
"math"
|
||||
"os"
|
||||
"testing"
|
||||
"unsafe" // just for test case printing unsafe.Pointer
|
||||
|
||||
// For testing printf-like functions from external package.
|
||||
// "github.com/foobar/externalprintf"
|
||||
"b"
|
||||
)
|
||||
|
||||
func UnsafePointerPrintfTest() {
|
||||
var up unsafe.Pointer
|
||||
fmt.Printf("%p, %x %X", up, up, up)
|
||||
}
|
||||
|
||||
// Error methods that do not satisfy the Error interface and should be checked.
|
||||
type errorTest1 int
|
||||
|
||||
func (errorTest1) Error(...interface{}) string {
|
||||
return "hi"
|
||||
}
|
||||
|
||||
type errorTest2 int // Analogous to testing's *T type.
|
||||
func (errorTest2) Error(...interface{}) {
|
||||
}
|
||||
|
||||
type errorTest3 int
|
||||
|
||||
func (errorTest3) Error() { // No return value.
|
||||
}
|
||||
|
||||
type errorTest4 int
|
||||
|
||||
func (errorTest4) Error() int { // Different return type.
|
||||
return 3
|
||||
}
|
||||
|
||||
type errorTest5 int
|
||||
|
||||
func (errorTest5) error() { // niladic; don't complain if no args (was bug)
|
||||
}
|
||||
|
||||
// This function never executes, but it serves as a simple test for the program.
|
||||
// Test with make test.
|
||||
func PrintfTests() {
|
||||
var b bool
|
||||
var i int
|
||||
var r rune
|
||||
var s string
|
||||
var x float64
|
||||
var p *int
|
||||
var imap map[int]int
|
||||
var fslice []float64
|
||||
var c complex64
|
||||
// Some good format/argtypes
|
||||
fmt.Printf("")
|
||||
fmt.Printf("%b %b %b", 3, i, x)
|
||||
fmt.Printf("%c %c %c %c", 3, i, 'x', r)
|
||||
fmt.Printf("%d %d %d", 3, i, imap)
|
||||
fmt.Printf("%e %e %e %e", 3e9, x, fslice, c)
|
||||
fmt.Printf("%E %E %E %E", 3e9, x, fslice, c)
|
||||
fmt.Printf("%f %f %f %f", 3e9, x, fslice, c)
|
||||
fmt.Printf("%F %F %F %F", 3e9, x, fslice, c)
|
||||
fmt.Printf("%g %g %g %g", 3e9, x, fslice, c)
|
||||
fmt.Printf("%G %G %G %G", 3e9, x, fslice, c)
|
||||
fmt.Printf("%b %b %b %b", 3e9, x, fslice, c)
|
||||
fmt.Printf("%o %o", 3, i)
|
||||
fmt.Printf("%p", p)
|
||||
fmt.Printf("%q %q %q %q", 3, i, 'x', r)
|
||||
fmt.Printf("%s %s %s", "hi", s, []byte{65})
|
||||
fmt.Printf("%t %t", true, b)
|
||||
fmt.Printf("%T %T", 3, i)
|
||||
fmt.Printf("%U %U", 3, i)
|
||||
fmt.Printf("%v %v", 3, i)
|
||||
fmt.Printf("%x %x %x %x", 3, i, "hi", s)
|
||||
fmt.Printf("%X %X %X %X", 3, i, "hi", s)
|
||||
fmt.Printf("%.*s %d %g", 3, "hi", 23, 2.3)
|
||||
fmt.Printf("%s", &stringerv)
|
||||
fmt.Printf("%v", &stringerv)
|
||||
fmt.Printf("%T", &stringerv)
|
||||
fmt.Printf("%s", &embeddedStringerv)
|
||||
fmt.Printf("%v", &embeddedStringerv)
|
||||
fmt.Printf("%T", &embeddedStringerv)
|
||||
fmt.Printf("%v", notstringerv)
|
||||
fmt.Printf("%T", notstringerv)
|
||||
fmt.Printf("%q", stringerarrayv)
|
||||
fmt.Printf("%v", stringerarrayv)
|
||||
fmt.Printf("%s", stringerarrayv)
|
||||
fmt.Printf("%v", notstringerarrayv)
|
||||
fmt.Printf("%T", notstringerarrayv)
|
||||
fmt.Printf("%d", new(fmt.Formatter))
|
||||
fmt.Printf("%*%", 2) // Ridiculous but allowed.
|
||||
fmt.Printf("%s", interface{}(nil)) // Nothing useful we can say.
|
||||
|
||||
fmt.Printf("%g", 1+2i)
|
||||
fmt.Printf("%#e %#E %#f %#F %#g %#G", 1.2, 1.2, 1.2, 1.2, 1.2, 1.2) // OK since Go 1.9
|
||||
// Some bad format/argTypes
|
||||
fmt.Printf("%b", "hi") // want "Printf format %b has arg \x22hi\x22 of wrong type string"
|
||||
fmt.Printf("%t", c) // want "Printf format %t has arg c of wrong type complex64"
|
||||
fmt.Printf("%t", 1+2i) // want `Printf format %t has arg 1 \+ 2i of wrong type complex128`
|
||||
fmt.Printf("%c", 2.3) // want "Printf format %c has arg 2.3 of wrong type float64"
|
||||
fmt.Printf("%d", 2.3) // want "Printf format %d has arg 2.3 of wrong type float64"
|
||||
fmt.Printf("%e", "hi") // want `Printf format %e has arg "hi" of wrong type string`
|
||||
fmt.Printf("%E", true) // want "Printf format %E has arg true of wrong type bool"
|
||||
fmt.Printf("%f", "hi") // want "Printf format %f has arg \x22hi\x22 of wrong type string"
|
||||
fmt.Printf("%F", 'x') // want "Printf format %F has arg 'x' of wrong type rune"
|
||||
fmt.Printf("%g", "hi") // want `Printf format %g has arg "hi" of wrong type string`
|
||||
fmt.Printf("%g", imap) // want `Printf format %g has arg imap of wrong type map\[int\]int`
|
||||
fmt.Printf("%G", i) // want "Printf format %G has arg i of wrong type int"
|
||||
fmt.Printf("%o", x) // want "Printf format %o has arg x of wrong type float64"
|
||||
fmt.Printf("%p", nil) // want "Printf format %p has arg nil of wrong type untyped nil"
|
||||
fmt.Printf("%p", 23) // want "Printf format %p has arg 23 of wrong type int"
|
||||
fmt.Printf("%q", x) // want "Printf format %q has arg x of wrong type float64"
|
||||
fmt.Printf("%s", b) // want "Printf format %s has arg b of wrong type bool"
|
||||
fmt.Printf("%s", byte(65)) // want `Printf format %s has arg byte\(65\) of wrong type byte`
|
||||
fmt.Printf("%t", 23) // want "Printf format %t has arg 23 of wrong type int"
|
||||
fmt.Printf("%U", x) // want "Printf format %U has arg x of wrong type float64"
|
||||
fmt.Printf("%x", nil) // want "Printf format %x has arg nil of wrong type untyped nil"
|
||||
fmt.Printf("%X", 2.3) // want "Printf format %X has arg 2.3 of wrong type float64"
|
||||
fmt.Printf("%s", stringerv) // want "Printf format %s has arg stringerv of wrong type a.ptrStringer"
|
||||
fmt.Printf("%t", stringerv) // want "Printf format %t has arg stringerv of wrong type a.ptrStringer"
|
||||
fmt.Printf("%s", embeddedStringerv) // want "Printf format %s has arg embeddedStringerv of wrong type a.embeddedStringer"
|
||||
fmt.Printf("%t", embeddedStringerv) // want "Printf format %t has arg embeddedStringerv of wrong type a.embeddedStringer"
|
||||
fmt.Printf("%q", notstringerv) // want "Printf format %q has arg notstringerv of wrong type a.notstringer"
|
||||
fmt.Printf("%t", notstringerv) // want "Printf format %t has arg notstringerv of wrong type a.notstringer"
|
||||
fmt.Printf("%t", stringerarrayv) // want "Printf format %t has arg stringerarrayv of wrong type a.stringerarray"
|
||||
fmt.Printf("%t", notstringerarrayv) // want "Printf format %t has arg notstringerarrayv of wrong type a.notstringerarray"
|
||||
fmt.Printf("%q", notstringerarrayv) // want "Printf format %q has arg notstringerarrayv of wrong type a.notstringerarray"
|
||||
fmt.Printf("%d", BoolFormatter(true)) // want `Printf format %d has arg BoolFormatter\(true\) of wrong type a.BoolFormatter`
|
||||
fmt.Printf("%z", FormatterVal(true)) // correct (the type is responsible for formatting)
|
||||
fmt.Printf("%d", FormatterVal(true)) // correct (the type is responsible for formatting)
|
||||
fmt.Printf("%s", nonemptyinterface) // correct (the type is responsible for formatting)
|
||||
fmt.Printf("%.*s %d %6g", 3, "hi", 23, 'x') // want "Printf format %6g has arg 'x' of wrong type rune"
|
||||
fmt.Println() // not an error
|
||||
fmt.Println("%s", "hi") // want "Println call has possible formatting directive %s"
|
||||
fmt.Println("%v", "hi") // want "Println call has possible formatting directive %v"
|
||||
fmt.Println("%T", "hi") // want "Println call has possible formatting directive %T"
|
||||
fmt.Println("0.0%") // correct (trailing % couldn't be a formatting directive)
|
||||
fmt.Printf("%s", "hi", 3) // want "Printf call needs 1 arg but has 2 args"
|
||||
_ = fmt.Sprintf("%"+("s"), "hi", 3) // want "Sprintf call needs 1 arg but has 2 args"
|
||||
fmt.Printf("%s%%%d", "hi", 3) // correct
|
||||
fmt.Printf("%08s", "woo") // correct
|
||||
fmt.Printf("% 8s", "woo") // correct
|
||||
fmt.Printf("%.*d", 3, 3) // correct
|
||||
fmt.Printf("%.*d x", 3, 3, 3, 3) // want "Printf call needs 2 args but has 4 args"
|
||||
fmt.Printf("%.*d x", "hi", 3) // want `Printf format %.*d uses non-int "hi" as argument of \*`
|
||||
fmt.Printf("%.*d x", i, 3) // correct
|
||||
fmt.Printf("%.*d x", s, 3) // want `Printf format %.\*d uses non-int s as argument of \*`
|
||||
fmt.Printf("%*% x", 0.22) // want `Printf format %\*% uses non-int 0.22 as argument of \*`
|
||||
fmt.Printf("%q %q", multi()...) // ok
|
||||
fmt.Printf("%#q", `blah`) // ok
|
||||
// printf("now is the time", "buddy") // no error "printf call has arguments but no formatting directives"
|
||||
Printf("now is the time", "buddy") // want "Printf call has arguments but no formatting directives"
|
||||
Printf("hi") // ok
|
||||
const format = "%s %s\n"
|
||||
Printf(format, "hi", "there")
|
||||
Printf(format, "hi") // want "Printf format %s reads arg #2, but call has 1 arg$"
|
||||
Printf("%s %d %.3v %q", "str", 4) // want "Printf format %.3v reads arg #3, but call has 2 args"
|
||||
f := new(ptrStringer)
|
||||
f.Warn(0, "%s", "hello", 3) // want "Warn call has possible formatting directive %s"
|
||||
f.Warnf(0, "%s", "hello", 3) // want "Warnf call needs 1 arg but has 2 args"
|
||||
f.Warnf(0, "%r", "hello") // want "Warnf format %r has unknown verb r"
|
||||
f.Warnf(0, "%#s", "hello") // want "Warnf format %#s has unrecognized flag #"
|
||||
f.Warn2(0, "%s", "hello", 3) // want "Warn2 call has possible formatting directive %s"
|
||||
f.Warnf2(0, "%s", "hello", 3) // want "Warnf2 call needs 1 arg but has 2 args"
|
||||
f.Warnf2(0, "%r", "hello") // want "Warnf2 format %r has unknown verb r"
|
||||
f.Warnf2(0, "%#s", "hello") // want "Warnf2 format %#s has unrecognized flag #"
|
||||
f.Wrap(0, "%s", "hello", 3) // want "Wrap call has possible formatting directive %s"
|
||||
f.Wrapf(0, "%s", "hello", 3) // want "Wrapf call needs 1 arg but has 2 args"
|
||||
f.Wrapf(0, "%r", "hello") // want "Wrapf format %r has unknown verb r"
|
||||
f.Wrapf(0, "%#s", "hello") // want "Wrapf format %#s has unrecognized flag #"
|
||||
f.Wrap2(0, "%s", "hello", 3) // want "Wrap2 call has possible formatting directive %s"
|
||||
f.Wrapf2(0, "%s", "hello", 3) // want "Wrapf2 call needs 1 arg but has 2 args"
|
||||
f.Wrapf2(0, "%r", "hello") // want "Wrapf2 format %r has unknown verb r"
|
||||
f.Wrapf2(0, "%#s", "hello") // want "Wrapf2 format %#s has unrecognized flag #"
|
||||
fmt.Printf("%#s", FormatterVal(true)) // correct (the type is responsible for formatting)
|
||||
Printf("d%", 2) // want "Printf format % is missing verb at end of string"
|
||||
Printf("%d", percentDV)
|
||||
Printf("%d", &percentDV)
|
||||
Printf("%d", notPercentDV) // want "Printf format %d has arg notPercentDV of wrong type a.notPercentDStruct"
|
||||
Printf("%d", ¬PercentDV) // want `Printf format %d has arg ¬PercentDV of wrong type \*a.notPercentDStruct`
|
||||
Printf("%p", ¬PercentDV) // Works regardless: we print it as a pointer.
|
||||
Printf("%q", &percentDV) // want `Printf format %q has arg &percentDV of wrong type \*a.percentDStruct`
|
||||
Printf("%s", percentSV)
|
||||
Printf("%s", &percentSV)
|
||||
// Good argument reorderings.
|
||||
Printf("%[1]d", 3)
|
||||
Printf("%[1]*d", 3, 1)
|
||||
Printf("%[2]*[1]d", 1, 3)
|
||||
Printf("%[2]*.[1]*[3]d", 2, 3, 4)
|
||||
fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
|
||||
// Bad argument reorderings.
|
||||
Printf("%[xd", 3) // want `Printf format %\[xd is missing closing \]`
|
||||
Printf("%[x]d x", 3) // want `Printf format has invalid argument index \[x\]`
|
||||
Printf("%[3]*s x", "hi", 2) // want `Printf format has invalid argument index \[3\]`
|
||||
_ = fmt.Sprintf("%[3]d x", 2) // want `Sprintf format has invalid argument index \[3\]`
|
||||
Printf("%[2]*.[1]*[3]d x", 2, "hi", 4) // want `Printf format %\[2]\*\.\[1\]\*\[3\]d uses non-int \x22hi\x22 as argument of \*`
|
||||
Printf("%[0]s x", "arg1") // want `Printf format has invalid argument index \[0\]`
|
||||
Printf("%[0]d x", 1) // want `Printf format has invalid argument index \[0\]`
|
||||
// Something that satisfies the error interface.
|
||||
var e error
|
||||
fmt.Println(e.Error()) // ok
|
||||
// Something that looks like an error interface but isn't, such as the (*T).Error method
|
||||
// in the testing package.
|
||||
var et1 *testing.T
|
||||
et1.Error() // ok
|
||||
et1.Error("hi") // ok
|
||||
et1.Error("%d", 3) // want "Error call has possible formatting directive %d"
|
||||
et1.Errorf("%s", 1) // want "Errorf format %s has arg 1 of wrong type int"
|
||||
var et3 errorTest3
|
||||
et3.Error() // ok, not an error method.
|
||||
var et4 errorTest4
|
||||
et4.Error() // ok, not an error method.
|
||||
var et5 errorTest5
|
||||
et5.error() // ok, not an error method.
|
||||
// Interfaces can be used with any verb.
|
||||
var iface interface {
|
||||
ToTheMadness() bool // Method ToTheMadness usually returns false
|
||||
}
|
||||
fmt.Printf("%f", iface) // ok: fmt treats interfaces as transparent and iface may well have a float concrete type
|
||||
// Can't print a function.
|
||||
Printf("%d", someFunction) // want "Printf format %d arg someFunction is a func value, not called"
|
||||
Printf("%v", someFunction) // want "Printf format %v arg someFunction is a func value, not called"
|
||||
Println(someFunction) // want "Println arg someFunction is a func value, not called"
|
||||
Printf("%p", someFunction) // ok: maybe someone wants to see the pointer
|
||||
Printf("%T", someFunction) // ok: maybe someone wants to see the type
|
||||
// Bug: used to recur forever.
|
||||
Printf("%p %x", recursiveStructV, recursiveStructV.next)
|
||||
Printf("%p %x", recursiveStruct1V, recursiveStruct1V.next) // want `Printf format %x has arg recursiveStruct1V\.next of wrong type \*a\.RecursiveStruct2`
|
||||
Printf("%p %x", recursiveSliceV, recursiveSliceV)
|
||||
Printf("%p %x", recursiveMapV, recursiveMapV)
|
||||
// Special handling for Log.
|
||||
math.Log(3) // OK
|
||||
var t *testing.T
|
||||
t.Log("%d", 3) // want "Log call has possible formatting directive %d"
|
||||
t.Logf("%d", 3)
|
||||
t.Logf("%d", "hi") // want `Logf format %d has arg "hi" of wrong type string`
|
||||
|
||||
Errorf(1, "%d", 3) // OK
|
||||
Errorf(1, "%d", "hi") // want `Errorf format %d has arg "hi" of wrong type string`
|
||||
|
||||
// Multiple string arguments before variadic args
|
||||
errorf("WARNING", "foobar") // OK
|
||||
errorf("INFO", "s=%s, n=%d", "foo", 1) // OK
|
||||
errorf("ERROR", "%d") // want "errorf format %d reads arg #1, but call has 0 args"
|
||||
|
||||
var tb testing.TB
|
||||
tb.Errorf("%s", 1) // want "Errorf format %s has arg 1 of wrong type int"
|
||||
|
||||
// Printf from external package
|
||||
// externalprintf.Printf("%d", 42) // OK
|
||||
// externalprintf.Printf("foobar") // OK
|
||||
// level := 123
|
||||
// externalprintf.Logf(level, "%d", 42) // OK
|
||||
// externalprintf.Errorf(level, level, "foo %q bar", "foobar") // OK
|
||||
// externalprintf.Logf(level, "%d") // no error "Logf format %d reads arg #1, but call has 0 args"
|
||||
// var formatStr = "%s %s"
|
||||
// externalprintf.Sprintf(formatStr, "a", "b") // OK
|
||||
// externalprintf.Logf(level, formatStr, "a", "b") // OK
|
||||
|
||||
// user-defined Println-like functions
|
||||
ss := &someStruct{}
|
||||
ss.Log(someFunction, "foo") // OK
|
||||
ss.Error(someFunction, someFunction) // OK
|
||||
ss.Println() // OK
|
||||
ss.Println(1.234, "foo") // OK
|
||||
ss.Println(1, someFunction) // no error "Println arg someFunction is a func value, not called"
|
||||
ss.log(someFunction) // OK
|
||||
ss.log(someFunction, "bar", 1.33) // OK
|
||||
ss.log(someFunction, someFunction) // no error "log arg someFunction is a func value, not called"
|
||||
|
||||
// indexed arguments
|
||||
Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4) // OK
|
||||
Printf("%d %[0]d %d %[2]d x", 1, 2, 3, 4) // want `Printf format has invalid argument index \[0\]`
|
||||
Printf("%d %[3]d %d %[-2]d x", 1, 2, 3, 4) // want `Printf format has invalid argument index \[-2\]`
|
||||
Printf("%d %[3]d %d %[2234234234234]d x", 1, 2, 3, 4) // want `Printf format has invalid argument index \[2234234234234\]`
|
||||
Printf("%d %[3]d %-10d %[2]d x", 1, 2, 3) // want "Printf format %-10d reads arg #4, but call has 3 args"
|
||||
Printf("%[1][3]d x", 1, 2) // want `Printf format %\[1\]\[ has unknown verb \[`
|
||||
Printf("%[1]d x", 1, 2) // OK
|
||||
Printf("%d %[3]d %d %[2]d x", 1, 2, 3, 4, 5) // OK
|
||||
|
||||
// wrote Println but meant Fprintln
|
||||
Printf("%p\n", os.Stdout) // OK
|
||||
Println(os.Stdout, "hello") // want "Println does not take io.Writer but has first arg os.Stdout"
|
||||
|
||||
Printf(someString(), "hello") // OK
|
||||
|
||||
// Printf wrappers in package log should be detected automatically
|
||||
logpkg.Fatal("%d", 1) // want "Fatal call has possible formatting directive %d"
|
||||
logpkg.Fatalf("%d", "x") // want `Fatalf format %d has arg "x" of wrong type string`
|
||||
logpkg.Fatalln("%d", 1) // want "Fatalln call has possible formatting directive %d"
|
||||
logpkg.Panic("%d", 1) // want "Panic call has possible formatting directive %d"
|
||||
logpkg.Panicf("%d", "x") // want `Panicf format %d has arg "x" of wrong type string`
|
||||
logpkg.Panicln("%d", 1) // want "Panicln call has possible formatting directive %d"
|
||||
logpkg.Print("%d", 1) // want "Print call has possible formatting directive %d"
|
||||
logpkg.Printf("%d", "x") // want `Printf format %d has arg "x" of wrong type string`
|
||||
logpkg.Println("%d", 1) // want "Println call has possible formatting directive %d"
|
||||
|
||||
// Methods too.
|
||||
var l *logpkg.Logger
|
||||
l.Fatal("%d", 1) // want "Fatal call has possible formatting directive %d"
|
||||
l.Fatalf("%d", "x") // want `Fatalf format %d has arg "x" of wrong type string`
|
||||
l.Fatalln("%d", 1) // want "Fatalln call has possible formatting directive %d"
|
||||
l.Panic("%d", 1) // want "Panic call has possible formatting directive %d"
|
||||
l.Panicf("%d", "x") // want `Panicf format %d has arg "x" of wrong type string`
|
||||
l.Panicln("%d", 1) // want "Panicln call has possible formatting directive %d"
|
||||
l.Print("%d", 1) // want "Print call has possible formatting directive %d"
|
||||
l.Printf("%d", "x") // want `Printf format %d has arg "x" of wrong type string`
|
||||
l.Println("%d", 1) // want "Println call has possible formatting directive %d"
|
||||
|
||||
// Issue 26486
|
||||
dbg("", 1) // no error "call has arguments but no formatting directive"
|
||||
}
|
||||
|
||||
func someString() string { return "X" }
|
||||
|
||||
type someStruct struct{}
|
||||
|
||||
// Log is non-variadic user-define Println-like function.
|
||||
// Calls to this func must be skipped when checking
|
||||
// for Println-like arguments.
|
||||
func (ss *someStruct) Log(f func(), s string) {}
|
||||
|
||||
// Error is variadic user-define Println-like function.
|
||||
// Calls to this func mustn't be checked for Println-like arguments,
|
||||
// since variadic arguments type isn't interface{}.
|
||||
func (ss *someStruct) Error(args ...func()) {}
|
||||
|
||||
// Println is variadic user-defined Println-like function.
|
||||
// Calls to this func must be checked for Println-like arguments.
|
||||
func (ss *someStruct) Println(args ...interface{}) {}
|
||||
|
||||
// log is variadic user-defined Println-like function.
|
||||
// Calls to this func must be checked for Println-like arguments.
|
||||
func (ss *someStruct) log(f func(), args ...interface{}) {}
|
||||
|
||||
// A function we use as a function value; it has no other purpose.
|
||||
func someFunction() {}
|
||||
|
||||
// Printf is used by the test so we must declare it.
|
||||
func Printf(format string, args ...interface{}) { // want Printf:"printfWrapper"
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Println is used by the test so we must declare it.
|
||||
func Println(args ...interface{}) { // want Println:"printWrapper"
|
||||
fmt.Println(args...)
|
||||
}
|
||||
|
||||
// printf is used by the test so we must declare it.
|
||||
func printf(format string, args ...interface{}) { // want printf:"printfWrapper"
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Errorf is used by the test for a case in which the first parameter
|
||||
// is not a format string.
|
||||
func Errorf(i int, format string, args ...interface{}) { // want Errorf:"printfWrapper"
|
||||
_ = fmt.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// errorf is used by the test for a case in which the function accepts multiple
|
||||
// string parameters before variadic arguments
|
||||
func errorf(level, format string, args ...interface{}) { // want errorf:"printfWrapper"
|
||||
_ = fmt.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// multi is used by the test.
|
||||
func multi() []interface{} {
|
||||
panic("don't call - testing only")
|
||||
}
|
||||
|
||||
type stringer int
|
||||
|
||||
func (stringer) String() string { return "string" }
|
||||
|
||||
type ptrStringer float64
|
||||
|
||||
var stringerv ptrStringer
|
||||
|
||||
func (*ptrStringer) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Warn2(x int, args ...interface{}) string { // want Warn2:"printWrapper"
|
||||
return p.Warn(x, args...)
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Warnf2(x int, format string, args ...interface{}) string { // want Warnf2:"printfWrapper"
|
||||
return p.Warnf(x, format, args...)
|
||||
}
|
||||
|
||||
// During testing -printf.funcs flag matches Warn.
|
||||
func (*ptrStringer) Warn(x int, args ...interface{}) string {
|
||||
return "warn"
|
||||
}
|
||||
|
||||
// During testing -printf.funcs flag matches Warnf.
|
||||
func (*ptrStringer) Warnf(x int, format string, args ...interface{}) string {
|
||||
return "warnf"
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Wrap2(x int, args ...interface{}) string { // want Wrap2:"printWrapper"
|
||||
return p.Wrap(x, args...)
|
||||
}
|
||||
|
||||
func (p *ptrStringer) Wrapf2(x int, format string, args ...interface{}) string { // want Wrapf2:"printfWrapper"
|
||||
return p.Wrapf(x, format, args...)
|
||||
}
|
||||
|
||||
func (*ptrStringer) Wrap(x int, args ...interface{}) string { // want Wrap:"printWrapper"
|
||||
return fmt.Sprint(args...)
|
||||
}
|
||||
|
||||
func (*ptrStringer) Wrapf(x int, format string, args ...interface{}) string { // want Wrapf:"printfWrapper"
|
||||
return fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
func (*ptrStringer) BadWrap(x int, args ...interface{}) string {
|
||||
return fmt.Sprint(args) // want "missing ... in args forwarded to print-like function"
|
||||
}
|
||||
|
||||
func (*ptrStringer) BadWrapf(x int, format string, args ...interface{}) string {
|
||||
return fmt.Sprintf(format, args) // want "missing ... in args forwarded to printf-like function"
|
||||
}
|
||||
|
||||
func (*ptrStringer) WrapfFalsePositive(x int, arg1 string, arg2 ...interface{}) string {
|
||||
return fmt.Sprintf("%s %v", arg1, arg2)
|
||||
}
|
||||
|
||||
type embeddedStringer struct {
|
||||
foo string
|
||||
ptrStringer
|
||||
bar int
|
||||
}
|
||||
|
||||
var embeddedStringerv embeddedStringer
|
||||
|
||||
type notstringer struct {
|
||||
f float64
|
||||
}
|
||||
|
||||
var notstringerv notstringer
|
||||
|
||||
type stringerarray [4]float64
|
||||
|
||||
func (stringerarray) String() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
var stringerarrayv stringerarray
|
||||
|
||||
type notstringerarray [4]float64
|
||||
|
||||
var notstringerarrayv notstringerarray
|
||||
|
||||
var nonemptyinterface = interface {
|
||||
f()
|
||||
}(nil)
|
||||
|
||||
// A data type we can print with "%d".
|
||||
type percentDStruct struct {
|
||||
a int
|
||||
b []byte
|
||||
c *float64
|
||||
}
|
||||
|
||||
var percentDV percentDStruct
|
||||
|
||||
// A data type we cannot print correctly with "%d".
|
||||
type notPercentDStruct struct {
|
||||
a int
|
||||
b []byte
|
||||
c bool
|
||||
}
|
||||
|
||||
var notPercentDV notPercentDStruct
|
||||
|
||||
// A data type we can print with "%s".
|
||||
type percentSStruct struct {
|
||||
a string
|
||||
b []byte
|
||||
C stringerarray
|
||||
}
|
||||
|
||||
var percentSV percentSStruct
|
||||
|
||||
type recursiveStringer int
|
||||
|
||||
func (s recursiveStringer) String() string {
|
||||
_ = fmt.Sprintf("%d", s)
|
||||
_ = fmt.Sprintf("%#v", s)
|
||||
_ = fmt.Sprintf("%v", s) // want "Sprintf format %v with arg s causes recursive String method call"
|
||||
_ = fmt.Sprintf("%v", &s) // want "Sprintf format %v with arg &s causes recursive String method call"
|
||||
_ = fmt.Sprintf("%T", s) // ok; does not recursively call String
|
||||
return fmt.Sprintln(s) // want "Sprintln arg s causes recursive call to String method"
|
||||
}
|
||||
|
||||
type recursivePtrStringer int
|
||||
|
||||
func (p *recursivePtrStringer) String() string {
|
||||
_ = fmt.Sprintf("%v", *p)
|
||||
_ = fmt.Sprint(&p) // ok; prints address
|
||||
return fmt.Sprintln(p) // want "Sprintln arg p causes recursive call to String method"
|
||||
}
|
||||
|
||||
type cons struct {
|
||||
car int
|
||||
cdr *cons
|
||||
}
|
||||
|
||||
func (cons *cons) String() string {
|
||||
if cons == nil {
|
||||
return "nil"
|
||||
}
|
||||
_ = fmt.Sprint(cons.cdr) // don't want "recursive call" diagnostic
|
||||
return fmt.Sprintf("(%d . %v)", cons.car, cons.cdr) // don't want "recursive call" diagnostic
|
||||
}
|
||||
|
||||
type BoolFormatter bool
|
||||
|
||||
func (*BoolFormatter) Format(fmt.State, rune) {
|
||||
}
|
||||
|
||||
// Formatter with value receiver
|
||||
type FormatterVal bool
|
||||
|
||||
func (FormatterVal) Format(fmt.State, rune) {
|
||||
}
|
||||
|
||||
type RecursiveSlice []RecursiveSlice
|
||||
|
||||
var recursiveSliceV = &RecursiveSlice{}
|
||||
|
||||
type RecursiveMap map[int]RecursiveMap
|
||||
|
||||
var recursiveMapV = make(RecursiveMap)
|
||||
|
||||
type RecursiveStruct struct {
|
||||
next *RecursiveStruct
|
||||
}
|
||||
|
||||
var recursiveStructV = &RecursiveStruct{}
|
||||
|
||||
type RecursiveStruct1 struct {
|
||||
next *RecursiveStruct2
|
||||
}
|
||||
|
||||
type RecursiveStruct2 struct {
|
||||
next *RecursiveStruct1
|
||||
}
|
||||
|
||||
var recursiveStruct1V = &RecursiveStruct1{}
|
||||
|
||||
type unexportedInterface struct {
|
||||
f interface{}
|
||||
}
|
||||
|
||||
// Issue 17798: unexported ptrStringer cannot be formatted.
|
||||
type unexportedStringer struct {
|
||||
t ptrStringer
|
||||
}
|
||||
type unexportedStringerOtherFields struct {
|
||||
s string
|
||||
t ptrStringer
|
||||
S string
|
||||
}
|
||||
|
||||
// Issue 17798: unexported error cannot be formatted.
|
||||
type unexportedError struct {
|
||||
e error
|
||||
}
|
||||
type unexportedErrorOtherFields struct {
|
||||
s string
|
||||
e error
|
||||
S string
|
||||
}
|
||||
|
||||
type errorer struct{}
|
||||
|
||||
func (e errorer) Error() string { return "errorer" }
|
||||
|
||||
type unexportedCustomError struct {
|
||||
e errorer
|
||||
}
|
||||
|
||||
type errorInterface interface {
|
||||
error
|
||||
ExtraMethod()
|
||||
}
|
||||
|
||||
type unexportedErrorInterface struct {
|
||||
e errorInterface
|
||||
}
|
||||
|
||||
func UnexportedStringerOrError() {
|
||||
fmt.Printf("%s", unexportedInterface{"foo"}) // ok; prints {foo}
|
||||
fmt.Printf("%s", unexportedInterface{3}) // ok; we can't see the problem
|
||||
|
||||
us := unexportedStringer{}
|
||||
fmt.Printf("%s", us) // want "Printf format %s has arg us of wrong type a.unexportedStringer"
|
||||
fmt.Printf("%s", &us) // want "Printf format %s has arg &us of wrong type [*]a.unexportedStringer"
|
||||
|
||||
usf := unexportedStringerOtherFields{
|
||||
s: "foo",
|
||||
S: "bar",
|
||||
}
|
||||
fmt.Printf("%s", usf) // want "Printf format %s has arg usf of wrong type a.unexportedStringerOtherFields"
|
||||
fmt.Printf("%s", &usf) // want "Printf format %s has arg &usf of wrong type [*]a.unexportedStringerOtherFields"
|
||||
|
||||
ue := unexportedError{
|
||||
e: &errorer{},
|
||||
}
|
||||
fmt.Printf("%s", ue) // want "Printf format %s has arg ue of wrong type a.unexportedError"
|
||||
fmt.Printf("%s", &ue) // want "Printf format %s has arg &ue of wrong type [*]a.unexportedError"
|
||||
|
||||
uef := unexportedErrorOtherFields{
|
||||
s: "foo",
|
||||
e: &errorer{},
|
||||
S: "bar",
|
||||
}
|
||||
fmt.Printf("%s", uef) // want "Printf format %s has arg uef of wrong type a.unexportedErrorOtherFields"
|
||||
fmt.Printf("%s", &uef) // want "Printf format %s has arg &uef of wrong type [*]a.unexportedErrorOtherFields"
|
||||
|
||||
uce := unexportedCustomError{
|
||||
e: errorer{},
|
||||
}
|
||||
fmt.Printf("%s", uce) // want "Printf format %s has arg uce of wrong type a.unexportedCustomError"
|
||||
|
||||
uei := unexportedErrorInterface{}
|
||||
fmt.Printf("%s", uei) // want "Printf format %s has arg uei of wrong type a.unexportedErrorInterface"
|
||||
fmt.Println("foo\n", "bar") // not an error
|
||||
|
||||
fmt.Println("foo\n") // want "Println arg list ends with redundant newline"
|
||||
fmt.Println("foo\\n") // not an error
|
||||
fmt.Println(`foo\n`) // not an error
|
||||
|
||||
intSlice := []int{3, 4}
|
||||
fmt.Printf("%s", intSlice) // want `Printf format %s has arg intSlice of wrong type \[\]int`
|
||||
nonStringerArray := [1]unexportedStringer{{}}
|
||||
fmt.Printf("%s", nonStringerArray) // want `Printf format %s has arg nonStringerArray of wrong type \[1\]a.unexportedStringer`
|
||||
fmt.Printf("%s", []stringer{3, 4}) // not an error
|
||||
fmt.Printf("%s", [2]stringer{3, 4}) // not an error
|
||||
}
|
||||
|
||||
// TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11.
|
||||
// See issues 23598 and 23605.
|
||||
func DisableErrorForFlag0() {
|
||||
fmt.Printf("%0t", true)
|
||||
}
|
||||
|
||||
// Issue 26486.
|
||||
func dbg(format string, args ...interface{}) {
|
||||
if format == "" {
|
||||
format = "%v"
|
||||
}
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
func PointersToCompoundTypes() {
|
||||
stringSlice := []string{"a", "b"}
|
||||
fmt.Printf("%s", &stringSlice) // not an error
|
||||
|
||||
intSlice := []int{3, 4}
|
||||
fmt.Printf("%s", &intSlice) // want `Printf format %s has arg &intSlice of wrong type \*\[\]int`
|
||||
|
||||
stringArray := [2]string{"a", "b"}
|
||||
fmt.Printf("%s", &stringArray) // not an error
|
||||
|
||||
intArray := [2]int{3, 4}
|
||||
fmt.Printf("%s", &intArray) // want `Printf format %s has arg &intArray of wrong type \*\[2\]int`
|
||||
|
||||
stringStruct := struct{ F string }{"foo"}
|
||||
fmt.Printf("%s", &stringStruct) // not an error
|
||||
|
||||
intStruct := struct{ F int }{3}
|
||||
fmt.Printf("%s", &intStruct) // want `Printf format %s has arg &intStruct of wrong type \*struct{F int}`
|
||||
|
||||
stringMap := map[string]string{"foo": "bar"}
|
||||
fmt.Printf("%s", &stringMap) // not an error
|
||||
|
||||
intMap := map[int]int{3: 4}
|
||||
fmt.Printf("%s", &intMap) // want `Printf format %s has arg &intMap of wrong type \*map\[int\]int`
|
||||
|
||||
type T2 struct {
|
||||
X string
|
||||
}
|
||||
type T1 struct {
|
||||
X *T2
|
||||
}
|
||||
fmt.Printf("%s\n", T1{&T2{"x"}}) // want `Printf format %s has arg T1{&T2{.x.}} of wrong type a\.T1`
|
||||
}
|
||||
|
||||
// Printf wrappers from external package
|
||||
func externalPackage() {
|
||||
b.Wrapf("%s", 1) // want "Wrapf format %s has arg 1 of wrong type int"
|
||||
b.Wrap("%s", 1) // want "Wrap call has possible formatting directive %s"
|
||||
b.NoWrap("%s", 1)
|
||||
b.Wrapf2("%s", 1) // want "Wrapf2 format %s has arg 1 of wrong type int"
|
||||
}
|
||||
33
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/b/b.go
generated
vendored
Normal file
33
vendor/golang.org/x/tools/go/analysis/passes/printf/testdata/src/b/b.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package b
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Wrapf is a printf wrapper.
|
||||
func Wrapf(format string, args ...interface{}) { // want Wrapf:"printfWrapper"
|
||||
fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Wrap is a print wrapper.
|
||||
func Wrap(args ...interface{}) { // want Wrap:"printWrapper"
|
||||
fmt.Sprint(args...)
|
||||
}
|
||||
|
||||
// NoWrap is not a wrapper.
|
||||
func NoWrap(format string, args ...interface{}) {
|
||||
}
|
||||
|
||||
// Wrapf2 is another printf wrapper.
|
||||
func Wrapf2(format string, args ...interface{}) string { // want Wrapf2:"printfWrapper"
|
||||
|
||||
// This statement serves as an assertion that this function is a
|
||||
// printf wrapper and that calls to it should be checked
|
||||
// accordingly, even though the delegation below is obscured by
|
||||
// the "("+format+")" operations.
|
||||
if false {
|
||||
fmt.Sprintf(format, args...)
|
||||
}
|
||||
|
||||
// Effectively a printf delegation,
|
||||
// but the printf checker can't see it.
|
||||
return fmt.Sprintf("("+format+")", args...)
|
||||
}
|
||||
236
vendor/golang.org/x/tools/go/analysis/passes/printf/types.go
generated
vendored
Normal file
236
vendor/golang.org/x/tools/go/analysis/passes/printf/types.go
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
package printf
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
)
|
||||
|
||||
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
|
||||
|
||||
// matchArgType reports an error if printf verb t is not appropriate
|
||||
// for operand arg.
|
||||
//
|
||||
// typ is used only for recursive calls; external callers must supply nil.
|
||||
//
|
||||
// (Recursion arises from the compound types {map,chan,slice} which
|
||||
// may be printed with %d etc. if that is appropriate for their element
|
||||
// types.)
|
||||
func matchArgType(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr) bool {
|
||||
return matchArgTypeInternal(pass, t, typ, arg, make(map[types.Type]bool))
|
||||
}
|
||||
|
||||
// matchArgTypeInternal is the internal version of matchArgType. It carries a map
|
||||
// remembering what types are in progress so we don't recur when faced with recursive
|
||||
// types or mutually recursive types.
|
||||
func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
|
||||
// %v, %T accept any argument type.
|
||||
if t == anyType {
|
||||
return true
|
||||
}
|
||||
if typ == nil {
|
||||
// external call
|
||||
typ = pass.TypesInfo.Types[arg].Type
|
||||
if typ == nil {
|
||||
return true // probably a type check problem
|
||||
}
|
||||
}
|
||||
// If the type implements fmt.Formatter, we have nothing to check.
|
||||
if isFormatter(pass, typ) {
|
||||
return true
|
||||
}
|
||||
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
|
||||
if t&argString != 0 && isConvertibleToString(pass, typ) {
|
||||
return true
|
||||
}
|
||||
|
||||
typ = typ.Underlying()
|
||||
if inProgress[typ] {
|
||||
// We're already looking at this type. The call that started it will take care of it.
|
||||
return true
|
||||
}
|
||||
inProgress[typ] = true
|
||||
|
||||
switch typ := typ.(type) {
|
||||
case *types.Signature:
|
||||
return t&argPointer != 0
|
||||
|
||||
case *types.Map:
|
||||
// Recur: map[int]int matches %d.
|
||||
return t&argPointer != 0 ||
|
||||
(matchArgTypeInternal(pass, t, typ.Key(), arg, inProgress) && matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress))
|
||||
|
||||
case *types.Chan:
|
||||
return t&argPointer != 0
|
||||
|
||||
case *types.Array:
|
||||
// Same as slice.
|
||||
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
||||
return true // %s matches []byte
|
||||
}
|
||||
// Recur: []int matches %d.
|
||||
return t&argPointer != 0 || matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
|
||||
|
||||
case *types.Slice:
|
||||
// Same as array.
|
||||
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
||||
return true // %s matches []byte
|
||||
}
|
||||
// Recur: []int matches %d. But watch out for
|
||||
// type T []T
|
||||
// If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
|
||||
return t&argPointer != 0 || matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
|
||||
|
||||
case *types.Pointer:
|
||||
// Ugly, but dealing with an edge case: a known pointer to an invalid type,
|
||||
// probably something from a failed import.
|
||||
if typ.Elem().String() == "invalid type" {
|
||||
if false {
|
||||
pass.Reportf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", analysisutil.Format(pass.Fset, arg))
|
||||
}
|
||||
return true // special case
|
||||
}
|
||||
// If it's actually a pointer with %p, it prints as one.
|
||||
if t == argPointer {
|
||||
return true
|
||||
}
|
||||
|
||||
under := typ.Elem().Underlying()
|
||||
switch under.(type) {
|
||||
case *types.Struct: // see below
|
||||
case *types.Array: // see below
|
||||
case *types.Slice: // see below
|
||||
case *types.Map: // see below
|
||||
default:
|
||||
// Check whether the rest can print pointers.
|
||||
return t&argPointer != 0
|
||||
}
|
||||
// If it's a top-level pointer to a struct, array, slice, or
|
||||
// map, that's equivalent in our analysis to whether we can
|
||||
// print the type being pointed to. Pointers in nested levels
|
||||
// are not supported to minimize fmt running into loops.
|
||||
if len(inProgress) > 1 {
|
||||
return false
|
||||
}
|
||||
return matchArgTypeInternal(pass, t, under, arg, inProgress)
|
||||
|
||||
case *types.Struct:
|
||||
return matchStructArgType(pass, t, typ, arg, inProgress)
|
||||
|
||||
case *types.Interface:
|
||||
// There's little we can do.
|
||||
// Whether any particular verb is valid depends on the argument.
|
||||
// The user may have reasonable prior knowledge of the contents of the interface.
|
||||
return true
|
||||
|
||||
case *types.Basic:
|
||||
switch typ.Kind() {
|
||||
case types.UntypedBool,
|
||||
types.Bool:
|
||||
return t&argBool != 0
|
||||
|
||||
case types.UntypedInt,
|
||||
types.Int,
|
||||
types.Int8,
|
||||
types.Int16,
|
||||
types.Int32,
|
||||
types.Int64,
|
||||
types.Uint,
|
||||
types.Uint8,
|
||||
types.Uint16,
|
||||
types.Uint32,
|
||||
types.Uint64,
|
||||
types.Uintptr:
|
||||
return t&argInt != 0
|
||||
|
||||
case types.UntypedFloat,
|
||||
types.Float32,
|
||||
types.Float64:
|
||||
return t&argFloat != 0
|
||||
|
||||
case types.UntypedComplex,
|
||||
types.Complex64,
|
||||
types.Complex128:
|
||||
return t&argComplex != 0
|
||||
|
||||
case types.UntypedString,
|
||||
types.String:
|
||||
return t&argString != 0
|
||||
|
||||
case types.UnsafePointer:
|
||||
return t&(argPointer|argInt) != 0
|
||||
|
||||
case types.UntypedRune:
|
||||
return t&(argInt|argRune) != 0
|
||||
|
||||
case types.UntypedNil:
|
||||
return false
|
||||
|
||||
case types.Invalid:
|
||||
if false {
|
||||
pass.Reportf(arg.Pos(), "printf argument %v has invalid or unknown type", analysisutil.Format(pass.Fset, arg))
|
||||
}
|
||||
return true // Probably a type check problem.
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isConvertibleToString(pass *analysis.Pass, typ types.Type) bool {
|
||||
if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
|
||||
// We explicitly don't want untyped nil, which is
|
||||
// convertible to both of the interfaces below, as it
|
||||
// would just panic anyway.
|
||||
return false
|
||||
}
|
||||
if types.ConvertibleTo(typ, errorType) {
|
||||
return true // via .Error()
|
||||
}
|
||||
|
||||
// Does it implement fmt.Stringer?
|
||||
if obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "String"); obj != nil {
|
||||
if fn, ok := obj.(*types.Func); ok {
|
||||
sig := fn.Type().(*types.Signature)
|
||||
if sig.Params().Len() == 0 &&
|
||||
sig.Results().Len() == 1 &&
|
||||
sig.Results().At(0).Type() == types.Typ[types.String] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// hasBasicType reports whether x's type is a types.Basic with the given kind.
|
||||
func hasBasicType(pass *analysis.Pass, x ast.Expr, kind types.BasicKind) bool {
|
||||
t := pass.TypesInfo.Types[x].Type
|
||||
if t != nil {
|
||||
t = t.Underlying()
|
||||
}
|
||||
b, ok := t.(*types.Basic)
|
||||
return ok && b.Kind() == kind
|
||||
}
|
||||
|
||||
// matchStructArgType reports whether all the elements of the struct match the expected
|
||||
// type. For instance, with "%d" all the elements must be printable with the "%d" format.
|
||||
func matchStructArgType(pass *analysis.Pass, t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
typf := typ.Field(i)
|
||||
if !matchArgTypeInternal(pass, t, typf.Type(), arg, inProgress) {
|
||||
return false
|
||||
}
|
||||
if t&argString != 0 && !typf.Exported() && isConvertibleToString(pass, typf.Type()) {
|
||||
// Issue #17798: unexported Stringer or error cannot be properly fomatted.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var archSizes = types.SizesFor("gc", build.Default.GOARCH)
|
||||
9
vendor/golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow/main.go
generated
vendored
Normal file
9
vendor/golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow/main.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// The shadow command runs the shadow analyzer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/analysis/passes/shadow"
|
||||
"golang.org/x/tools/go/analysis/singlechecker"
|
||||
)
|
||||
|
||||
func main() { singlechecker.Main(shadow.Analyzer) }
|
||||
288
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow.go
generated
vendored
Normal file
288
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow.go
generated
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
// Copyright 2013 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 shadow
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
// NOTE: Experimental. Not part of the vet suite.
|
||||
|
||||
const Doc = `check for possible unintended shadowing of variables
|
||||
|
||||
This analyzer check for shadowed variables.
|
||||
A shadowed variable is a variable declared in an inner scope
|
||||
with the same name and type as a variable in an outer scope,
|
||||
and where the outer variable is mentioned after the inner one
|
||||
is declared.
|
||||
|
||||
(This definition can be refined; the module generates too many
|
||||
false positives and is not yet enabled by default.)
|
||||
|
||||
For example:
|
||||
|
||||
func BadRead(f *os.File, buf []byte) error {
|
||||
var err error
|
||||
for {
|
||||
n, err := f.Read(buf) // shadows the function variable 'err'
|
||||
if err != nil {
|
||||
break // causes return of wrong value
|
||||
}
|
||||
foo(buf)
|
||||
}
|
||||
return err
|
||||
}
|
||||
`
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "shadow",
|
||||
Doc: Doc,
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
Run: run,
|
||||
}
|
||||
|
||||
// flags
|
||||
var strict = false
|
||||
|
||||
func init() {
|
||||
Analyzer.Flags.BoolVar(&strict, "strict", strict, "whether to be strict about shadowing; can be noisy")
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
spans := make(map[types.Object]span)
|
||||
for id, obj := range pass.TypesInfo.Defs {
|
||||
// Ignore identifiers that don't denote objects
|
||||
// (package names, symbolic variables such as t
|
||||
// in t := x.(type) of type switch headers).
|
||||
if obj != nil {
|
||||
growSpan(spans, obj, id.Pos(), id.End())
|
||||
}
|
||||
}
|
||||
for id, obj := range pass.TypesInfo.Uses {
|
||||
growSpan(spans, obj, id.Pos(), id.End())
|
||||
}
|
||||
for node, obj := range pass.TypesInfo.Implicits {
|
||||
// A type switch with a short variable declaration
|
||||
// such as t := x.(type) doesn't declare the symbolic
|
||||
// variable (t in the example) at the switch header;
|
||||
// instead a new variable t (with specific type) is
|
||||
// declared implicitly for each case. Such variables
|
||||
// are found in the types.Info.Implicits (not Defs)
|
||||
// map. Add them here, assuming they are declared at
|
||||
// the type cases' colon ":".
|
||||
if cc, ok := node.(*ast.CaseClause); ok {
|
||||
growSpan(spans, obj, cc.Colon, cc.Colon)
|
||||
}
|
||||
}
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.AssignStmt)(nil),
|
||||
(*ast.GenDecl)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
switch n := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
checkShadowAssignment(pass, spans, n)
|
||||
case *ast.GenDecl:
|
||||
checkShadowDecl(pass, spans, n)
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// A span stores the minimum range of byte positions in the file in which a
|
||||
// given variable (types.Object) is mentioned. It is lexically defined: it spans
|
||||
// from the beginning of its first mention to the end of its last mention.
|
||||
// A variable is considered shadowed (if strict is off) only if the
|
||||
// shadowing variable is declared within the span of the shadowed variable.
|
||||
// In other words, if a variable is shadowed but not used after the shadowed
|
||||
// variable is declared, it is inconsequential and not worth complaining about.
|
||||
// This simple check dramatically reduces the nuisance rate for the shadowing
|
||||
// check, at least until something cleverer comes along.
|
||||
//
|
||||
// One wrinkle: A "naked return" is a silent use of a variable that the Span
|
||||
// will not capture, but the compilers catch naked returns of shadowed
|
||||
// variables so we don't need to.
|
||||
//
|
||||
// Cases this gets wrong (TODO):
|
||||
// - If a for loop's continuation statement mentions a variable redeclared in
|
||||
// the block, we should complain about it but don't.
|
||||
// - A variable declared inside a function literal can falsely be identified
|
||||
// as shadowing a variable in the outer function.
|
||||
//
|
||||
type span struct {
|
||||
min token.Pos
|
||||
max token.Pos
|
||||
}
|
||||
|
||||
// contains reports whether the position is inside the span.
|
||||
func (s span) contains(pos token.Pos) bool {
|
||||
return s.min <= pos && pos < s.max
|
||||
}
|
||||
|
||||
// growSpan expands the span for the object to contain the source range [pos, end).
|
||||
func growSpan(spans map[types.Object]span, obj types.Object, pos, end token.Pos) {
|
||||
if strict {
|
||||
return // No need
|
||||
}
|
||||
s, ok := spans[obj]
|
||||
if ok {
|
||||
if s.min > pos {
|
||||
s.min = pos
|
||||
}
|
||||
if s.max < end {
|
||||
s.max = end
|
||||
}
|
||||
} else {
|
||||
s = span{pos, end}
|
||||
}
|
||||
spans[obj] = s
|
||||
}
|
||||
|
||||
// checkShadowAssignment checks for shadowing in a short variable declaration.
|
||||
func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *ast.AssignStmt) {
|
||||
if a.Tok != token.DEFINE {
|
||||
return
|
||||
}
|
||||
if idiomaticShortRedecl(pass, a) {
|
||||
return
|
||||
}
|
||||
for _, expr := range a.Lhs {
|
||||
ident, ok := expr.(*ast.Ident)
|
||||
if !ok {
|
||||
pass.Reportf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
|
||||
return
|
||||
}
|
||||
checkShadowing(pass, spans, ident)
|
||||
}
|
||||
}
|
||||
|
||||
// idiomaticShortRedecl reports whether this short declaration can be ignored for
|
||||
// the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
|
||||
func idiomaticShortRedecl(pass *analysis.Pass, a *ast.AssignStmt) bool {
|
||||
// Don't complain about deliberate redeclarations of the form
|
||||
// i := i
|
||||
// Such constructs are idiomatic in range loops to create a new variable
|
||||
// for each iteration. Another example is
|
||||
// switch n := n.(type)
|
||||
if len(a.Rhs) != len(a.Lhs) {
|
||||
return false
|
||||
}
|
||||
// We know it's an assignment, so the LHS must be all identifiers. (We check anyway.)
|
||||
for i, expr := range a.Lhs {
|
||||
lhs, ok := expr.(*ast.Ident)
|
||||
if !ok {
|
||||
pass.Reportf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
|
||||
return true // Don't do any more processing.
|
||||
}
|
||||
switch rhs := a.Rhs[i].(type) {
|
||||
case *ast.Ident:
|
||||
if lhs.Name != rhs.Name {
|
||||
return false
|
||||
}
|
||||
case *ast.TypeAssertExpr:
|
||||
if id, ok := rhs.X.(*ast.Ident); ok {
|
||||
if lhs.Name != id.Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// idiomaticRedecl reports whether this declaration spec can be ignored for
|
||||
// the purposes of shadowing, that is, that any redeclarations it contains are deliberate.
|
||||
func idiomaticRedecl(d *ast.ValueSpec) bool {
|
||||
// Don't complain about deliberate redeclarations of the form
|
||||
// var i, j = i, j
|
||||
if len(d.Names) != len(d.Values) {
|
||||
return false
|
||||
}
|
||||
for i, lhs := range d.Names {
|
||||
if rhs, ok := d.Values[i].(*ast.Ident); ok {
|
||||
if lhs.Name != rhs.Name {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// checkShadowDecl checks for shadowing in a general variable declaration.
|
||||
func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.GenDecl) {
|
||||
if d.Tok != token.VAR {
|
||||
return
|
||||
}
|
||||
for _, spec := range d.Specs {
|
||||
valueSpec, ok := spec.(*ast.ValueSpec)
|
||||
if !ok {
|
||||
pass.Reportf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec")
|
||||
return
|
||||
}
|
||||
// Don't complain about deliberate redeclarations of the form
|
||||
// var i = i
|
||||
if idiomaticRedecl(valueSpec) {
|
||||
return
|
||||
}
|
||||
for _, ident := range valueSpec.Names {
|
||||
checkShadowing(pass, spans, ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkShadowing checks whether the identifier shadows an identifier in an outer scope.
|
||||
func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast.Ident) {
|
||||
if ident.Name == "_" {
|
||||
// Can't shadow the blank identifier.
|
||||
return
|
||||
}
|
||||
obj := pass.TypesInfo.Defs[ident]
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
// obj.Parent.Parent is the surrounding scope. If we can find another declaration
|
||||
// starting from there, we have a shadowed identifier.
|
||||
_, shadowed := obj.Parent().Parent().LookupParent(obj.Name(), obj.Pos())
|
||||
if shadowed == nil {
|
||||
return
|
||||
}
|
||||
// Don't complain if it's shadowing a universe-declared identifier; that's fine.
|
||||
if shadowed.Parent() == types.Universe {
|
||||
return
|
||||
}
|
||||
if strict {
|
||||
// The shadowed identifier must appear before this one to be an instance of shadowing.
|
||||
if shadowed.Pos() > ident.Pos() {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Don't complain if the span of validity of the shadowed identifier doesn't include
|
||||
// the shadowing identifier.
|
||||
span, ok := spans[shadowed]
|
||||
if !ok {
|
||||
pass.Reportf(ident.Pos(), "internal error: no range for %q", ident.Name)
|
||||
return
|
||||
}
|
||||
if !span.contains(ident.Pos()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Don't complain if the types differ: that implies the programmer really wants two different things.
|
||||
if types.Identical(obj.Type(), shadowed.Type()) {
|
||||
line := pass.Fset.Position(shadowed.Pos()).Line
|
||||
pass.Reportf(ident.Pos(), "declaration of %q shadows declaration at line %d", obj.Name(), line)
|
||||
}
|
||||
}
|
||||
13
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow_test.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/analysis/passes/shadow/shadow_test.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package shadow_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/analysis/analysistest"
|
||||
"golang.org/x/tools/go/analysis/passes/shadow"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testdata := analysistest.TestData()
|
||||
analysistest.Run(t, testdata, shadow.Analyzer, "a")
|
||||
}
|
||||
91
vendor/golang.org/x/tools/go/analysis/passes/shadow/testdata/src/a/a.go
generated
vendored
Normal file
91
vendor/golang.org/x/tools/go/analysis/passes/shadow/testdata/src/a/a.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
// This file contains tests for the shadowed variable checker.
|
||||
// Some of these errors are caught by the compiler (shadowed return parameters for example)
|
||||
// but are nonetheless useful tests.
|
||||
|
||||
package a
|
||||
|
||||
import "os"
|
||||
|
||||
func ShadowRead(f *os.File, buf []byte) (err error) {
|
||||
var x int
|
||||
if f != nil {
|
||||
err := 3 // OK - different type.
|
||||
_ = err
|
||||
}
|
||||
if f != nil {
|
||||
_, err := f.Read(buf) // want "declaration of .err. shadows declaration at line 13"
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i := 3 // OK
|
||||
_ = i
|
||||
}
|
||||
if f != nil {
|
||||
x := one() // want "declaration of .x. shadows declaration at line 14"
|
||||
var _, err = f.Read(buf) // want "declaration of .err. shadows declaration at line 13"
|
||||
if x == 1 && err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
i := i // OK: obviously intentional idiomatic redeclaration
|
||||
go func() {
|
||||
println(i)
|
||||
}()
|
||||
}
|
||||
var shadowTemp interface{}
|
||||
switch shadowTemp := shadowTemp.(type) { // OK: obviously intentional idiomatic redeclaration
|
||||
case int:
|
||||
println("OK")
|
||||
_ = shadowTemp
|
||||
}
|
||||
if shadowTemp := shadowTemp; true { // OK: obviously intentional idiomatic redeclaration
|
||||
var f *os.File // OK because f is not mentioned later in the function.
|
||||
// The declaration of x is a shadow because x is mentioned below.
|
||||
var x int // want "declaration of .x. shadows declaration at line 14"
|
||||
_, _, _ = x, f, shadowTemp
|
||||
}
|
||||
// Use a couple of variables to trigger shadowing errors.
|
||||
_, _ = err, x
|
||||
return
|
||||
}
|
||||
|
||||
func one() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Must not complain with an internal error for the
|
||||
// implicitly declared type switch variable v.
|
||||
func issue26725(x interface{}) int {
|
||||
switch v := x.(type) {
|
||||
case int, int32:
|
||||
if v, ok := x.(int); ok {
|
||||
return v
|
||||
}
|
||||
case int64:
|
||||
return int(v)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Verify that implicitly declared variables from
|
||||
// type switches are considered in shadowing analysis.
|
||||
func shadowTypeSwitch(a interface{}) {
|
||||
switch t := a.(type) {
|
||||
case int:
|
||||
{
|
||||
t := 0 // want "declaration of .t. shadows declaration at line 78"
|
||||
_ = t
|
||||
}
|
||||
_ = t
|
||||
case uint:
|
||||
{
|
||||
t := uint(0) // OK because t is not mentioned later in this function
|
||||
_ = t
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user