github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/vet/types.go (about)

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build gotypes
     6  
     7  // This file contains the pieces of the tool that require the go/types package.
     8  // To compile this file, you must first run
     9  //  $ go get code.google.com/p/go.exp/go/types
    10  
    11  package main
    12  
    13  import (
    14  	"go/ast"
    15  	"go/token"
    16  
    17  	"code.google.com/p/go.exp/go/types"
    18  )
    19  
    20  // Type is equivalent to go/types.Type. Repeating it here allows us to avoid
    21  // depending on the go/types package.
    22  type Type interface {
    23  	String() string
    24  }
    25  
    26  func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
    27  	pkg.types = make(map[ast.Expr]Type)
    28  	pkg.values = make(map[ast.Expr]interface{})
    29  	exprFn := func(x ast.Expr, typ types.Type, val interface{}) {
    30  		pkg.types[x] = typ
    31  		if val != nil {
    32  			pkg.values[x] = val
    33  		}
    34  	}
    35  	// By providing the Context with our own error function, it will continue
    36  	// past the first error. There is no need for that function to do anything.
    37  	context := types.Context{
    38  		Expr:  exprFn,
    39  		Error: func(error) {},
    40  	}
    41  	_, err := context.Check(fs, astFiles)
    42  	return err
    43  }
    44  
    45  // isStruct reports whether the composite literal c is a struct.
    46  // If it is not (probably a struct), it returns a printable form of the type.
    47  func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
    48  	// Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
    49  	typ := pkg.types[c]
    50  	// If it's a named type, pull out the underlying type.
    51  	actual := typ
    52  	if namedType, ok := typ.(*types.NamedType); ok {
    53  		actual = namedType.Underlying
    54  	}
    55  	if actual == nil {
    56  		// No type information available. Assume true, so we do the check.
    57  		return true, ""
    58  	}
    59  	switch actual.(type) {
    60  	case *types.Struct:
    61  		return true, typ.String()
    62  	default:
    63  		return false, ""
    64  	}
    65  }
    66  
    67  func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
    68  	// TODO: for now, we can only test builtin types and untyped constants.
    69  	typ := f.pkg.types[arg]
    70  	if typ == nil {
    71  		return true
    72  	}
    73  	basic, ok := typ.(*types.Basic)
    74  	if !ok {
    75  		return true
    76  	}
    77  	switch basic.Kind {
    78  	case types.Bool:
    79  		return t&argBool != 0
    80  	case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
    81  		fallthrough
    82  	case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
    83  		return t&argInt != 0
    84  	case types.Float32, types.Float64, types.Complex64, types.Complex128:
    85  		return t&argFloat != 0
    86  	case types.String:
    87  		return t&argString != 0
    88  	case types.UnsafePointer:
    89  		return t&(argPointer|argInt) != 0
    90  	case types.UntypedBool:
    91  		return t&argBool != 0
    92  	case types.UntypedComplex:
    93  		return t&argFloat != 0
    94  	case types.UntypedFloat:
    95  		// If it's integral, we can use an int format.
    96  		switch f.pkg.values[arg].(type) {
    97  		case int, int8, int16, int32, int64:
    98  			return t&(argInt|argFloat) != 0
    99  		case uint, uint8, uint16, uint32, uint64:
   100  			return t&(argInt|argFloat) != 0
   101  		}
   102  		return t&argFloat != 0
   103  	case types.UntypedInt:
   104  		return t&argInt != 0
   105  	case types.UntypedRune:
   106  		return t&(argInt|argRune) != 0
   107  	case types.UntypedString:
   108  		return t&argString != 0
   109  	case types.UntypedNil:
   110  		return t&argPointer != 0 // TODO?
   111  	case types.Invalid:
   112  		if *verbose {
   113  			f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
   114  		}
   115  		return true // Probably a type check problem.
   116  	}
   117  	return false
   118  }
   119  
   120  // numArgsInSignature tells how many formal arguments the function type
   121  // being called has.
   122  func (f *File) numArgsInSignature(call *ast.CallExpr) int {
   123  	// Check the type of the function or method declaration
   124  	typ := f.pkg.types[call.Fun]
   125  	if typ == nil {
   126  		return 0
   127  	}
   128  	// The type must be a signature, but be sure for safety.
   129  	sig, ok := typ.(*types.Signature)
   130  	if !ok {
   131  		return 0
   132  	}
   133  	return len(sig.Params)
   134  }
   135  
   136  // isErrorMethodCall reports whether the call is of a method with signature
   137  //	func Error() string
   138  // where "string" is the universe's string type. We know the method is called "Error".
   139  func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
   140  	// Is it a selector expression? Otherwise it's a function call, not a method call.
   141  	sel, ok := call.Fun.(*ast.SelectorExpr)
   142  	if !ok {
   143  		return false
   144  	}
   145  	// The package is type-checked, so if there are no arguments, we're done.
   146  	if len(call.Args) > 0 {
   147  		return false
   148  	}
   149  	// Check the type of the method declaration
   150  	typ := f.pkg.types[sel]
   151  	if typ == nil {
   152  		return false
   153  	}
   154  	// The type must be a signature, but be sure for safety.
   155  	sig, ok := typ.(*types.Signature)
   156  	if !ok {
   157  		return false
   158  	}
   159  	// There must be a receiver for it to be a method call. Otherwise it is
   160  	// a function, not something that satisfies the error interface.
   161  	if sig.Recv == nil {
   162  		return false
   163  	}
   164  	// There must be no arguments. Already verified by type checking, but be thorough.
   165  	if len(sig.Params) > 0 {
   166  		return false
   167  	}
   168  	// Finally the real questions.
   169  	// There must be one result.
   170  	if len(sig.Results) != 1 {
   171  		return false
   172  	}
   173  	// It must have return type "string" from the universe.
   174  	result := sig.Results[0].Type
   175  	if types.IsIdentical(result, types.Typ[types.String]) {
   176  		return true
   177  	}
   178  	return false
   179  }