mirror of
https://github.com/golang/go
synced 2024-11-18 18:44:42 -07:00
331c428e76
Experimental feature. It's too noisy yet to be enabled by default, so it must be enabled explicitly by go tool vet -shadow *.go or go tool vet -shadow directory (The go command does not know about the -shadow flag.) Fixes golang/go#5634. R=golang-dev, gri CC=golang-dev https://golang.org/cl/10409047
200 lines
5.9 KiB
Go
200 lines
5.9 KiB
Go
// 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 the pieces of the tool that use typechecking from the go/types package.
|
|
|
|
package main
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
|
|
"code.google.com/p/go.tools/go/exact"
|
|
"code.google.com/p/go.tools/go/types"
|
|
)
|
|
|
|
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
|
pkg.idents = make(map[*ast.Ident]types.Object)
|
|
pkg.spans = make(map[types.Object]Span)
|
|
pkg.types = make(map[ast.Expr]types.Type)
|
|
pkg.values = make(map[ast.Expr]exact.Value)
|
|
exprFn := func(x ast.Expr, typ types.Type, val exact.Value) {
|
|
pkg.types[x] = typ
|
|
if val != nil {
|
|
pkg.values[x] = val
|
|
}
|
|
}
|
|
identFn := func(id *ast.Ident, obj types.Object) {
|
|
pkg.idents[id] = obj
|
|
pkg.growSpan(id, obj)
|
|
}
|
|
// By providing the Context with our own error function, it will continue
|
|
// past the first error. There is no need for that function to do anything.
|
|
context := types.Context{
|
|
Ident: identFn,
|
|
Expr: exprFn,
|
|
Error: func(error) {},
|
|
}
|
|
_, err := context.Check(pkg.path, fs, astFiles...)
|
|
return err
|
|
}
|
|
|
|
// isStruct reports whether the composite literal c is a struct.
|
|
// If it is not (probably a struct), it returns a printable form of the type.
|
|
func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
|
|
// Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
|
|
typ := pkg.types[c]
|
|
// If it's a named type, pull out the underlying type. If it's not, the Underlying
|
|
// method returns the type itself.
|
|
actual := typ
|
|
if actual != nil {
|
|
actual = actual.Underlying()
|
|
}
|
|
if actual == nil {
|
|
// No type information available. Assume true, so we do the check.
|
|
return true, ""
|
|
}
|
|
switch actual.(type) {
|
|
case *types.Struct:
|
|
return true, typ.String()
|
|
default:
|
|
return false, ""
|
|
}
|
|
}
|
|
|
|
func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
|
|
// TODO: for now, we can only test builtin types, untyped constants, and Stringer/Errors.
|
|
typ := f.pkg.types[arg]
|
|
if typ == nil {
|
|
return true
|
|
}
|
|
// If we can use a string and this is a named type, does it implement the Stringer or Error interface?
|
|
// TODO: Simplify when we have the IsAssignableTo predicate in go/types.
|
|
if named, ok := typ.(*types.Named); ok && t&argString != 0 {
|
|
for i := 0; i < named.NumMethods(); i++ {
|
|
method := named.Method(i)
|
|
// Method must be either String or Error.
|
|
if method.Name() != "String" && method.Name() != "Error" {
|
|
continue
|
|
}
|
|
sig := method.Type().(*types.Signature)
|
|
// There must be zero arguments and one return.
|
|
if sig.Params().Len() != 0 || sig.Results().Len() != 1 {
|
|
continue
|
|
}
|
|
// Result must be string.
|
|
if !isUniverseString(sig.Results().At(0).Type()) {
|
|
continue
|
|
}
|
|
// It's a Stringer and we can print it.
|
|
return true
|
|
}
|
|
}
|
|
basic, ok := typ.Underlying().(*types.Basic)
|
|
if !ok {
|
|
return true
|
|
}
|
|
switch basic.Kind() {
|
|
case types.Bool:
|
|
return t&argBool != 0
|
|
case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
|
|
fallthrough
|
|
case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
|
|
return t&argInt != 0
|
|
case types.Float32, types.Float64, types.Complex64, types.Complex128:
|
|
return t&argFloat != 0
|
|
case types.String:
|
|
return t&argString != 0
|
|
case types.UnsafePointer:
|
|
return t&(argPointer|argInt) != 0
|
|
case types.UntypedBool:
|
|
return t&argBool != 0
|
|
case types.UntypedComplex:
|
|
return t&argFloat != 0
|
|
case types.UntypedFloat:
|
|
// If it's integral, we can use an int format.
|
|
switch f.pkg.values[arg].Kind() {
|
|
case exact.Int:
|
|
return t&(argInt|argFloat) != 0
|
|
}
|
|
return t&argFloat != 0
|
|
case types.UntypedInt:
|
|
return t&argInt != 0
|
|
case types.UntypedRune:
|
|
return t&(argInt|argRune) != 0
|
|
case types.UntypedString:
|
|
return t&argString != 0
|
|
case types.UntypedNil:
|
|
return t&argPointer != 0 // TODO?
|
|
case types.Invalid:
|
|
if *verbose {
|
|
f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
|
|
}
|
|
return true // Probably a type check problem.
|
|
}
|
|
return false
|
|
}
|
|
|
|
// numArgsInSignature tells how many formal arguments the function type
|
|
// being called has.
|
|
func (f *File) numArgsInSignature(call *ast.CallExpr) int {
|
|
// Check the type of the function or method declaration
|
|
typ := f.pkg.types[call.Fun]
|
|
if typ == nil {
|
|
return 0
|
|
}
|
|
// The type must be a signature, but be sure for safety.
|
|
sig, ok := typ.(*types.Signature)
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return sig.Params().Len()
|
|
}
|
|
|
|
// isErrorMethodCall reports whether the call is of a method with signature
|
|
// func Error() string
|
|
// where "string" is the universe's string type. We know the method is called "Error".
|
|
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
|
// Is it a selector expression? Otherwise it's a function call, not a method call.
|
|
sel, ok := call.Fun.(*ast.SelectorExpr)
|
|
if !ok {
|
|
return false
|
|
}
|
|
// The package is type-checked, so if there are no arguments, we're done.
|
|
if len(call.Args) > 0 {
|
|
return false
|
|
}
|
|
// Check the type of the method declaration
|
|
typ := f.pkg.types[sel]
|
|
if typ == nil {
|
|
return false
|
|
}
|
|
// The type must be a signature, but be sure for safety.
|
|
sig, ok := typ.(*types.Signature)
|
|
if !ok {
|
|
return false
|
|
}
|
|
// There must be a receiver for it to be a method call. Otherwise it is
|
|
// a function, not something that satisfies the error interface.
|
|
if sig.Recv == nil {
|
|
return false
|
|
}
|
|
// There must be no arguments. Already verified by type checking, but be thorough.
|
|
if sig.Params().Len() > 0 {
|
|
return false
|
|
}
|
|
// Finally the real questions.
|
|
// There must be one result.
|
|
if sig.Results().Len() != 1 {
|
|
return false
|
|
}
|
|
// It must have return type "string" from the universe.
|
|
return isUniverseString(sig.Results().At(0).Type())
|
|
}
|
|
|
|
// isUniverseString reports whether the type is the predeclared type "string".
|
|
func isUniverseString(typ types.Type) bool {
|
|
return types.IsIdentical(typ, types.Typ[types.String])
|
|
}
|