1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:04:46 -07:00
go/cmd/vet/types.go
Rob Pike 331c428e76 go.tools/cmd/vet: add check for shadowed variables
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
2013-06-21 11:27:53 -07:00

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])
}