golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
- golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04
+ golang.org/x/tools v0.0.0-20190509153222-73554e0f7805
)
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04 h1:SRYGE+BqJRgY8JH4p2NmwTPeuREKqKYw5IuEmthTHKQ=
-golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190509153222-73554e0f7805 h1:1ufBXAsTpUhSmmPXEEs5PrGQSfnBhsjAd2SmVhp9xrY=
+golang.org/x/tools v0.0.0-20190509153222-73554e0f7805/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
// See comments for ExportObjectFact.
ExportPackageFact func(fact Fact)
+ // AllPackageFacts returns a new slice containing all package facts in unspecified order.
+ // WARNING: This is an experimental API and may change in the future.
+ AllPackageFacts func() []PackageFact
+
+ // AllObjectFacts returns a new slice containing all object facts in unspecified order.
+ // WARNING: This is an experimental API and may change in the future.
+ AllObjectFacts func() []ObjectFact
+
/* Further fields may be added in future. */
// For example, suggested or applied refactorings.
}
+// PackageFact is a package together with an associated fact.
+// WARNING: This is an experimental API and may change in the future.
+type PackageFact struct {
+ Package *types.Package
+ Fact Fact
+}
+
+// ObjectFact is an object together with an associated fact.
+// WARNING: This is an experimental API and may change in the future.
+type ObjectFact struct {
+ Object types.Object
+ Fact Fact
+}
+
// 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{}) {
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\)`)
+ 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*(.*))?`)
fnName string
localSize, argSize int
wroteSP bool
+ noframe bool
haveRetArg bool
retLine []int
)
}
}
+ // Ignore comments and commented-out code.
+ if i := strings.Index(line, "//"); i >= 0 {
+ line = line[:i]
+ }
+
if m := asmTEXT.FindStringSubmatch(line); m != nil {
flushRet()
if arch == "" {
// 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)
+ // 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
localSize += archDef.intSize
}
argSize, _ = strconv.Atoi(m[5])
- if fn == nil && !strings.Contains(fnName, "<>") {
+ noframe = strings.Contains(flag, "NOFRAME")
+ if fn == nil && !strings.Contains(fnName, "<>") && !noframe {
badf("function %s missing Go declaration", fnName)
}
wroteSP = false
continue
}
- if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) {
+ if strings.Contains(line, ", "+archDef.stack) || strings.Contains(line, ",\t"+archDef.stack) || strings.Contains(line, "NOP "+archDef.stack) || strings.Contains(line, "NOP\t"+archDef.stack) {
wroteSP = true
continue
}
+ if arch == "wasm" && strings.Contains(line, "CallImport") {
+ // CallImport is a call out to magic that can write the result.
+ haveRetArg = true
+ }
+
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
- if m[3] != archDef.stack || wroteSP {
+ if m[3] != archDef.stack || wroteSP || noframe {
continue
}
off := 0
}
continue
}
- asmCheckVar(badf, fn, line, m[0], off, v)
+ asmCheckVar(badf, fn, line, m[0], off, v, archDef)
}
}
flushRet()
}
// asmCheckVar checks a single variable reference.
-func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar) {
+func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr string, off int, v *asmVar, archDef *asmArch) {
m := asmOpcode.FindStringSubmatch(line)
if m == nil {
if !strings.HasPrefix(strings.TrimSpace(line), "//") {
return
}
+ addr := strings.HasPrefix(expr, "$")
+
// Determine operand sizes from instruction.
// Typically the suffix suffices, but there are exceptions.
var src, dst, kind asmKind
// They just take the address of it.
case "386.LEAL":
dst = 4
+ addr = true
case "amd64.LEAQ":
dst = 8
+ addr = true
case "amd64p32.LEAL":
dst = 4
+ addr = true
default:
switch fn.arch.name {
case "386", "amd64":
vs = v.inner[0].size
vt = v.inner[0].typ
}
+ if addr {
+ vk = asmKind(archDef.ptrSize)
+ vs = archDef.ptrSize
+ vt = "address"
+ }
if off != v.off {
var inner bytes.Buffer
// 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.
+// If the variable's scope is larger than the function
+// containing the assignment, we assume that other uses exist.
//
// checkLostCancel analyzes a single named or literal function.
func run(pass *analysis.Pass) (interface{}, error) {
}
func runFunc(pass *analysis.Pass, node ast.Node) {
+ // Find scope of function node
+ var funcScope *types.Scope
+ switch v := node.(type) {
+ case *ast.FuncLit:
+ funcScope = pass.TypesInfo.Scopes[v.Type]
+ case *ast.FuncDecl:
+ funcScope = pass.TypesInfo.Scopes[v.Type]
+ }
+
// Maps each cancel variable to its defining ValueSpec/AssignStmt.
cancelvars := make(map[*types.Var]ast.Node)
"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
+ // If the cancel variable is defined outside function scope,
+ // do not analyze it.
+ if funcScope.Contains(v.Pos()) {
+ cancelvars[v] = stmt
+ }
} else if v, ok := pass.TypesInfo.Defs[id].(*types.Var); ok {
cancelvars[v] = stmt
}
return false
}
- // Is it the receiver r, or &r?
- recv := stringMethod.Type().(*types.Signature).Recv()
- if recv == nil {
+ sig := stringMethod.Type().(*types.Signature)
+ if !isStringer(sig) {
return false
}
+
+ // Is it the receiver r, or &r?
if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND {
e = u.X // strip off & from &r
}
if id, ok := e.(*ast.Ident); ok {
- return pass.TypesInfo.Uses[id] == recv
+ return pass.TypesInfo.Uses[id] == sig.Recv()
}
return false
}
+// isStringer reports whether the method signature matches the String() definition in fmt.Stringer.
+func isStringer(sig *types.Signature) bool {
+ return sig.Params().Len() == 0 &&
+ sig.Results().Len() == 1 &&
+ sig.Results().At(0).Type() == types.Typ[types.String]
+}
+
// isFunctionValue reports whether the expression is a function as opposed to a function call.
// It is almost always a mistake to print a function value.
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
args := sign.Params()
results := sign.Results()
+ // Special case: WriteTo with more than one argument,
+ // not trying at all to implement io.WriterTo,
+ // comes up often enough to skip.
+ if id.Name == "WriteTo" && args.Len() > 1 {
+ return
+ }
+
// Do the =s (if any) all match?
if !matchParams(pass, expect.args, args, "=") || !matchParams(pass, expect.results, results, "=") {
return
// checkCanonicalFieldTag checks a single struct field tag.
func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *map[[2]string]token.Pos) {
+ switch pass.Pkg.Path() {
+ case "encoding/json", "encoding/xml":
+ // These packages know how to use their own APIs.
+ // Sometimes they are testing what happens to incorrect programs.
+ return
+ }
+
for _, key := range checkTagDups {
checkTagDuplicates(pass, tag, key, field, field, seen)
}
}
func run(pass *analysis.Pass) (interface{}, error) {
+ switch pass.Pkg.Path() {
+ case "encoding/gob", "encoding/json", "encoding/xml":
+ // These packages know how to use their own APIs.
+ // Sometimes they are testing what happens to incorrect programs.
+ return nil, nil
+ }
+
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
# golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82
golang.org/x/sys/unix
golang.org/x/sys/windows
-# golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04
+# golang.org/x/tools v0.0.0-20190509153222-73554e0f7805
golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/internal/analysisflags
golang.org/x/tools/go/analysis/internal/facts
vetTool := filepath.Join(tmpdir, "vet")
vetCmd = []string{
vetTool,
- "-nilness=0", // expensive, uses SSA
+ // "-nilness=0", // expensive, uses SSA
}
cmd := exec.Command(cmdGoPath, "build", "-o", vetTool, "golang.org/x/tools/go/analysis/cmd/vet")