github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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  // This file contains the pieces of the tool that use typechecking from the go/types package.
     6  
     7  package main
     8  
     9  import (
    10  	"go/ast"
    11  	"go/importer"
    12  	"go/token"
    13  	"go/types"
    14  )
    15  
    16  // stdImporter is the importer we use to import packages.
    17  // It is created during initialization so that all packages
    18  // are imported by the same importer.
    19  var stdImporter = importer.Default()
    20  
    21  var (
    22  	errorType     *types.Interface
    23  	stringerType  *types.Interface // possibly nil
    24  	formatterType *types.Interface // possibly nil
    25  )
    26  
    27  func init() {
    28  	errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
    29  
    30  	if typ := importType("fmt", "Stringer"); typ != nil {
    31  		stringerType = typ.Underlying().(*types.Interface)
    32  	}
    33  
    34  	if typ := importType("fmt", "Formatter"); typ != nil {
    35  		formatterType = typ.Underlying().(*types.Interface)
    36  	}
    37  }
    38  
    39  // importType returns the type denoted by the qualified identifier
    40  // path.name, and adds the respective package to the imports map
    41  // as a side effect. In case of an error, importType returns nil.
    42  func importType(path, name string) types.Type {
    43  	pkg, err := stdImporter.Import(path)
    44  	if err != nil {
    45  		// This can happen if the package at path hasn't been compiled yet.
    46  		warnf("import failed: %v", err)
    47  		return nil
    48  	}
    49  	if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok {
    50  		return obj.Type()
    51  	}
    52  	warnf("invalid type name %q", name)
    53  	return nil
    54  }
    55  
    56  func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
    57  	pkg.defs = make(map[*ast.Ident]types.Object)
    58  	pkg.uses = make(map[*ast.Ident]types.Object)
    59  	pkg.selectors = make(map[*ast.SelectorExpr]*types.Selection)
    60  	pkg.spans = make(map[types.Object]Span)
    61  	pkg.types = make(map[ast.Expr]types.TypeAndValue)
    62  	config := types.Config{
    63  		// We use the same importer for all imports to ensure that
    64  		// everybody sees identical packages for the given paths.
    65  		Importer: stdImporter,
    66  		// By providing a Config with our own error function, it will continue
    67  		// past the first error. There is no need for that function to do anything.
    68  		Error: func(error) {},
    69  	}
    70  	info := &types.Info{
    71  		Selections: pkg.selectors,
    72  		Types:      pkg.types,
    73  		Defs:       pkg.defs,
    74  		Uses:       pkg.uses,
    75  	}
    76  	typesPkg, err := config.Check(pkg.path, fs, astFiles, info)
    77  	pkg.typesPkg = typesPkg
    78  	// update spans
    79  	for id, obj := range pkg.defs {
    80  		pkg.growSpan(id, obj)
    81  	}
    82  	for id, obj := range pkg.uses {
    83  		pkg.growSpan(id, obj)
    84  	}
    85  	return err
    86  }
    87  
    88  // isStruct reports whether the composite literal c is a struct.
    89  // If it is not (probably a struct), it returns a printable form of the type.
    90  func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
    91  	// Check that the CompositeLit's type is a slice or array (which needs no field keys), if possible.
    92  	typ := pkg.types[c].Type
    93  	// If it's a named type, pull out the underlying type. If it's not, the Underlying
    94  	// method returns the type itself.
    95  	actual := typ
    96  	if actual != nil {
    97  		actual = actual.Underlying()
    98  	}
    99  	if actual == nil {
   100  		// No type information available. Assume true, so we do the check.
   101  		return true, ""
   102  	}
   103  	switch actual.(type) {
   104  	case *types.Struct:
   105  		return true, typ.String()
   106  	default:
   107  		return false, ""
   108  	}
   109  }
   110  
   111  // matchArgType reports an error if printf verb t is not appropriate
   112  // for operand arg.
   113  //
   114  // typ is used only for recursive calls; external callers must supply nil.
   115  //
   116  // (Recursion arises from the compound types {map,chan,slice} which
   117  // may be printed with %d etc. if that is appropriate for their element
   118  // types.)
   119  func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool {
   120  	return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool))
   121  }
   122  
   123  // matchArgTypeInternal is the internal version of matchArgType. It carries a map
   124  // remembering what types are in progress so we don't recur when faced with recursive
   125  // types or mutually recursive types.
   126  func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
   127  	// %v, %T accept any argument type.
   128  	if t == anyType {
   129  		return true
   130  	}
   131  	if typ == nil {
   132  		// external call
   133  		typ = f.pkg.types[arg].Type
   134  		if typ == nil {
   135  			return true // probably a type check problem
   136  		}
   137  	}
   138  	// If the type implements fmt.Formatter, we have nothing to check.
   139  	// formatterTyp may be nil - be conservative and check for Format method in that case.
   140  	if formatterType != nil && types.Implements(typ, formatterType) || f.hasMethod(typ, "Format") {
   141  		return true
   142  	}
   143  	// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
   144  	if t&argString != 0 {
   145  		if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) {
   146  			return true
   147  		}
   148  	}
   149  
   150  	typ = typ.Underlying()
   151  	if inProgress[typ] {
   152  		// We're already looking at this type. The call that started it will take care of it.
   153  		return true
   154  	}
   155  	inProgress[typ] = true
   156  
   157  	switch typ := typ.(type) {
   158  	case *types.Signature:
   159  		return t&argPointer != 0
   160  
   161  	case *types.Map:
   162  		// Recur: map[int]int matches %d.
   163  		return t&argPointer != 0 ||
   164  			(f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress))
   165  
   166  	case *types.Chan:
   167  		return t&argPointer != 0
   168  
   169  	case *types.Array:
   170  		// Same as slice.
   171  		if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
   172  			return true // %s matches []byte
   173  		}
   174  		// Recur: []int matches %d.
   175  		return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress)
   176  
   177  	case *types.Slice:
   178  		// Same as array.
   179  		if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
   180  			return true // %s matches []byte
   181  		}
   182  		// Recur: []int matches %d. But watch out for
   183  		//	type T []T
   184  		// If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
   185  		return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)
   186  
   187  	case *types.Pointer:
   188  		// Ugly, but dealing with an edge case: a known pointer to an invalid type,
   189  		// probably something from a failed import.
   190  		if typ.Elem().String() == "invalid type" {
   191  			if *verbose {
   192  				f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg))
   193  			}
   194  			return true // special case
   195  		}
   196  		// If it's actually a pointer with %p, it prints as one.
   197  		if t == argPointer {
   198  			return true
   199  		}
   200  		// If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct.
   201  		if str, ok := typ.Elem().Underlying().(*types.Struct); ok {
   202  			return f.matchStructArgType(t, str, arg, inProgress)
   203  		}
   204  		// The rest can print with %p as pointers, or as integers with %x etc.
   205  		return t&(argInt|argPointer) != 0
   206  
   207  	case *types.Struct:
   208  		return f.matchStructArgType(t, typ, arg, inProgress)
   209  
   210  	case *types.Interface:
   211  		// If the static type of the argument is empty interface, there's little we can do.
   212  		// Example:
   213  		//	func f(x interface{}) { fmt.Printf("%s", x) }
   214  		// Whether x is valid for %s depends on the type of the argument to f. One day
   215  		// we will be able to do better. For now, we assume that empty interface is OK
   216  		// but non-empty interfaces, with Stringer and Error handled above, are errors.
   217  		return typ.NumMethods() == 0
   218  
   219  	case *types.Basic:
   220  		switch typ.Kind() {
   221  		case types.UntypedBool,
   222  			types.Bool:
   223  			return t&argBool != 0
   224  
   225  		case types.UntypedInt,
   226  			types.Int,
   227  			types.Int8,
   228  			types.Int16,
   229  			types.Int32,
   230  			types.Int64,
   231  			types.Uint,
   232  			types.Uint8,
   233  			types.Uint16,
   234  			types.Uint32,
   235  			types.Uint64,
   236  			types.Uintptr:
   237  			return t&argInt != 0
   238  
   239  		case types.UntypedFloat,
   240  			types.Float32,
   241  			types.Float64:
   242  			return t&argFloat != 0
   243  
   244  		case types.UntypedComplex,
   245  			types.Complex64,
   246  			types.Complex128:
   247  			return t&argComplex != 0
   248  
   249  		case types.UntypedString,
   250  			types.String:
   251  			return t&argString != 0
   252  
   253  		case types.UnsafePointer:
   254  			return t&(argPointer|argInt) != 0
   255  
   256  		case types.UntypedRune:
   257  			return t&(argInt|argRune) != 0
   258  
   259  		case types.UntypedNil:
   260  			return t&argPointer != 0 // TODO?
   261  
   262  		case types.Invalid:
   263  			if *verbose {
   264  				f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg))
   265  			}
   266  			return true // Probably a type check problem.
   267  		}
   268  		panic("unreachable")
   269  	}
   270  
   271  	return false
   272  }
   273  
   274  // hasBasicType reports whether x's type is a types.Basic with the given kind.
   275  func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
   276  	t := f.pkg.types[x].Type
   277  	if t != nil {
   278  		t = t.Underlying()
   279  	}
   280  	b, ok := t.(*types.Basic)
   281  	return ok && b.Kind() == kind
   282  }
   283  
   284  // matchStructArgType reports whether all the elements of the struct match the expected
   285  // type. For instance, with "%d" all the elements must be printable with the "%d" format.
   286  func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
   287  	for i := 0; i < typ.NumFields(); i++ {
   288  		if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) {
   289  			return false
   290  		}
   291  	}
   292  	return true
   293  }
   294  
   295  // numArgsInSignature tells how many formal arguments the function type
   296  // being called has.
   297  func (f *File) numArgsInSignature(call *ast.CallExpr) int {
   298  	// Check the type of the function or method declaration
   299  	typ := f.pkg.types[call.Fun].Type
   300  	if typ == nil {
   301  		return 0
   302  	}
   303  	// The type must be a signature, but be sure for safety.
   304  	sig, ok := typ.(*types.Signature)
   305  	if !ok {
   306  		return 0
   307  	}
   308  	return sig.Params().Len()
   309  }
   310  
   311  // isErrorMethodCall reports whether the call is of a method with signature
   312  //	func Error() string
   313  // where "string" is the universe's string type. We know the method is called "Error".
   314  func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
   315  	typ := f.pkg.types[call].Type
   316  	if typ != nil {
   317  		// We know it's called "Error", so just check the function signature
   318  		// (stringerType has exactly one method, String).
   319  		if stringerType != nil && stringerType.NumMethods() == 1 {
   320  			return types.Identical(f.pkg.types[call.Fun].Type, stringerType.Method(0).Type())
   321  		}
   322  	}
   323  	// Without types, we can still check by hand.
   324  	// Is it a selector expression? Otherwise it's a function call, not a method call.
   325  	sel, ok := call.Fun.(*ast.SelectorExpr)
   326  	if !ok {
   327  		return false
   328  	}
   329  	// The package is type-checked, so if there are no arguments, we're done.
   330  	if len(call.Args) > 0 {
   331  		return false
   332  	}
   333  	// Check the type of the method declaration
   334  	typ = f.pkg.types[sel].Type
   335  	if typ == nil {
   336  		return false
   337  	}
   338  	// The type must be a signature, but be sure for safety.
   339  	sig, ok := typ.(*types.Signature)
   340  	if !ok {
   341  		return false
   342  	}
   343  	// There must be a receiver for it to be a method call. Otherwise it is
   344  	// a function, not something that satisfies the error interface.
   345  	if sig.Recv() == nil {
   346  		return false
   347  	}
   348  	// There must be no arguments. Already verified by type checking, but be thorough.
   349  	if sig.Params().Len() > 0 {
   350  		return false
   351  	}
   352  	// Finally the real questions.
   353  	// There must be one result.
   354  	if sig.Results().Len() != 1 {
   355  		return false
   356  	}
   357  	// It must have return type "string" from the universe.
   358  	return sig.Results().At(0).Type() == types.Typ[types.String]
   359  }
   360  
   361  // hasMethod reports whether the type contains a method with the given name.
   362  // It is part of the workaround for Formatters and should be deleted when
   363  // that workaround is no longer necessary.
   364  // TODO: This could be better once issue 6259 is fixed.
   365  func (f *File) hasMethod(typ types.Type, name string) bool {
   366  	// assume we have an addressable variable of type typ
   367  	obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name)
   368  	_, ok := obj.(*types.Func)
   369  	return ok
   370  }