Home | History | Annotate | Download | only in vet
      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 // matchArgType reports an error if printf verb t is not appropriate
     89 // for operand arg.
     90 //
     91 // typ is used only for recursive calls; external callers must supply nil.
     92 //
     93 // (Recursion arises from the compound types {map,chan,slice} which
     94 // may be printed with %d etc. if that is appropriate for their element
     95 // types.)
     96 func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool {
     97 	return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool))
     98 }
     99 
    100 // matchArgTypeInternal is the internal version of matchArgType. It carries a map
    101 // remembering what types are in progress so we don't recur when faced with recursive
    102 // types or mutually recursive types.
    103 func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
    104 	// %v, %T accept any argument type.
    105 	if t == anyType {
    106 		return true
    107 	}
    108 	if typ == nil {
    109 		// external call
    110 		typ = f.pkg.types[arg].Type
    111 		if typ == nil {
    112 			return true // probably a type check problem
    113 		}
    114 	}
    115 	// If the type implements fmt.Formatter, we have nothing to check.
    116 	if f.isFormatter(typ) {
    117 		return true
    118 	}
    119 	// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
    120 	if t&argString != 0 {
    121 		if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) {
    122 			return true
    123 		}
    124 	}
    125 
    126 	typ = typ.Underlying()
    127 	if inProgress[typ] {
    128 		// We're already looking at this type. The call that started it will take care of it.
    129 		return true
    130 	}
    131 	inProgress[typ] = true
    132 
    133 	switch typ := typ.(type) {
    134 	case *types.Signature:
    135 		return t&argPointer != 0
    136 
    137 	case *types.Map:
    138 		// Recur: map[int]int matches %d.
    139 		return t&argPointer != 0 ||
    140 			(f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress))
    141 
    142 	case *types.Chan:
    143 		return t&argPointer != 0
    144 
    145 	case *types.Array:
    146 		// Same as slice.
    147 		if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
    148 			return true // %s matches []byte
    149 		}
    150 		// Recur: []int matches %d.
    151 		return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress)
    152 
    153 	case *types.Slice:
    154 		// Same as array.
    155 		if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
    156 			return true // %s matches []byte
    157 		}
    158 		// Recur: []int matches %d. But watch out for
    159 		//	type T []T
    160 		// If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
    161 		return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)
    162 
    163 	case *types.Pointer:
    164 		// Ugly, but dealing with an edge case: a known pointer to an invalid type,
    165 		// probably something from a failed import.
    166 		if typ.Elem().String() == "invalid type" {
    167 			if *verbose {
    168 				f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg))
    169 			}
    170 			return true // special case
    171 		}
    172 		// If it's actually a pointer with %p, it prints as one.
    173 		if t == argPointer {
    174 			return true
    175 		}
    176 		// If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct.
    177 		if str, ok := typ.Elem().Underlying().(*types.Struct); ok {
    178 			return f.matchStructArgType(t, str, arg, inProgress)
    179 		}
    180 		// The rest can print with %p as pointers, or as integers with %x etc.
    181 		return t&(argInt|argPointer) != 0
    182 
    183 	case *types.Struct:
    184 		return f.matchStructArgType(t, typ, arg, inProgress)
    185 
    186 	case *types.Interface:
    187 		// There's little we can do.
    188 		// Whether any particular verb is valid depends on the argument.
    189 		// The user may have reasonable prior knowledge of the contents of the interface.
    190 		return true
    191 
    192 	case *types.Basic:
    193 		switch typ.Kind() {
    194 		case types.UntypedBool,
    195 			types.Bool:
    196 			return t&argBool != 0
    197 
    198 		case types.UntypedInt,
    199 			types.Int,
    200 			types.Int8,
    201 			types.Int16,
    202 			types.Int32,
    203 			types.Int64,
    204 			types.Uint,
    205 			types.Uint8,
    206 			types.Uint16,
    207 			types.Uint32,
    208 			types.Uint64,
    209 			types.Uintptr:
    210 			return t&argInt != 0
    211 
    212 		case types.UntypedFloat,
    213 			types.Float32,
    214 			types.Float64:
    215 			return t&argFloat != 0
    216 
    217 		case types.UntypedComplex,
    218 			types.Complex64,
    219 			types.Complex128:
    220 			return t&argComplex != 0
    221 
    222 		case types.UntypedString,
    223 			types.String:
    224 			return t&argString != 0
    225 
    226 		case types.UnsafePointer:
    227 			return t&(argPointer|argInt) != 0
    228 
    229 		case types.UntypedRune:
    230 			return t&(argInt|argRune) != 0
    231 
    232 		case types.UntypedNil:
    233 			return t&argPointer != 0 // TODO?
    234 
    235 		case types.Invalid:
    236 			if *verbose {
    237 				f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg))
    238 			}
    239 			return true // Probably a type check problem.
    240 		}
    241 		panic("unreachable")
    242 	}
    243 
    244 	return false
    245 }
    246 
    247 // hasBasicType reports whether x's type is a types.Basic with the given kind.
    248 func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
    249 	t := f.pkg.types[x].Type
    250 	if t != nil {
    251 		t = t.Underlying()
    252 	}
    253 	b, ok := t.(*types.Basic)
    254 	return ok && b.Kind() == kind
    255 }
    256 
    257 // matchStructArgType reports whether all the elements of the struct match the expected
    258 // type. For instance, with "%d" all the elements must be printable with the "%d" format.
    259 func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
    260 	for i := 0; i < typ.NumFields(); i++ {
    261 		if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) {
    262 			return false
    263 		}
    264 	}
    265 	return true
    266 }
    267 
    268 // hasMethod reports whether the type contains a method with the given name.
    269 // It is part of the workaround for Formatters and should be deleted when
    270 // that workaround is no longer necessary.
    271 // TODO: This could be better once issue 6259 is fixed.
    272 func (f *File) hasMethod(typ types.Type, name string) bool {
    273 	// assume we have an addressable variable of type typ
    274 	obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name)
    275 	_, ok := obj.(*types.Func)
    276 	return ok
    277 }
    278