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/build"
     12 	"go/importer"
     13 	"go/token"
     14 	"go/types"
     15 )
     16 
     17 // stdImporter is the importer we use to import packages.
     18 // It is shared so that all packages are imported by the same importer.
     19 var stdImporter types.Importer
     20 
     21 var (
     22 	errorType     *types.Interface
     23 	stringerType  *types.Interface // possibly nil
     24 	formatterType *types.Interface // possibly nil
     25 )
     26 
     27 func inittypes() {
     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 	if typ := importType("fmt", "Formatter"); typ != nil {
     34 		formatterType = typ.Underlying().(*types.Interface)
     35 	}
     36 }
     37 
     38 // isNamedType reports whether t is the named type path.name.
     39 func isNamedType(t types.Type, path, name string) bool {
     40 	n, ok := t.(*types.Named)
     41 	if !ok {
     42 		return false
     43 	}
     44 	obj := n.Obj()
     45 	return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
     46 }
     47 
     48 // importType returns the type denoted by the qualified identifier
     49 // path.name, and adds the respective package to the imports map
     50 // as a side effect. In case of an error, importType returns nil.
     51 func importType(path, name string) types.Type {
     52 	pkg, err := stdImporter.Import(path)
     53 	if err != nil {
     54 		// This can happen if the package at path hasn't been compiled yet.
     55 		warnf("import failed: %v", err)
     56 		return nil
     57 	}
     58 	if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok {
     59 		return obj.Type()
     60 	}
     61 	warnf("invalid type name %q", name)
     62 	return nil
     63 }
     64 
     65 func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) []error {
     66 	if stdImporter == nil {
     67 		if *source {
     68 			stdImporter = importer.For("source", nil)
     69 		} else {
     70 			stdImporter = importer.Default()
     71 		}
     72 		inittypes()
     73 	}
     74 	pkg.defs = make(map[*ast.Ident]types.Object)
     75 	pkg.uses = make(map[*ast.Ident]types.Object)
     76 	pkg.selectors = make(map[*ast.SelectorExpr]*types.Selection)
     77 	pkg.spans = make(map[types.Object]Span)
     78 	pkg.types = make(map[ast.Expr]types.TypeAndValue)
     79 
     80 	var allErrors []error
     81 	config := types.Config{
     82 		// We use the same importer for all imports to ensure that
     83 		// everybody sees identical packages for the given paths.
     84 		Importer: stdImporter,
     85 		// By providing a Config with our own error function, it will continue
     86 		// past the first error. We collect them all for printing later.
     87 		Error: func(e error) {
     88 			allErrors = append(allErrors, e)
     89 		},
     90 
     91 		Sizes: archSizes,
     92 	}
     93 	info := &types.Info{
     94 		Selections: pkg.selectors,
     95 		Types:      pkg.types,
     96 		Defs:       pkg.defs,
     97 		Uses:       pkg.uses,
     98 	}
     99 	typesPkg, err := config.Check(pkg.path, fs, astFiles, info)
    100 	if len(allErrors) == 0 && err != nil {
    101 		allErrors = append(allErrors, err)
    102 	}
    103 	pkg.typesPkg = typesPkg
    104 	// update spans
    105 	for id, obj := range pkg.defs {
    106 		pkg.growSpan(id, obj)
    107 	}
    108 	for id, obj := range pkg.uses {
    109 		pkg.growSpan(id, obj)
    110 	}
    111 	return allErrors
    112 }
    113 
    114 // matchArgType reports an error if printf verb t is not appropriate
    115 // for operand arg.
    116 //
    117 // typ is used only for recursive calls; external callers must supply nil.
    118 //
    119 // (Recursion arises from the compound types {map,chan,slice} which
    120 // may be printed with %d etc. if that is appropriate for their element
    121 // types.)
    122 func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool {
    123 	return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool))
    124 }
    125 
    126 // matchArgTypeInternal is the internal version of matchArgType. It carries a map
    127 // remembering what types are in progress so we don't recur when faced with recursive
    128 // types or mutually recursive types.
    129 func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
    130 	// %v, %T accept any argument type.
    131 	if t == anyType {
    132 		return true
    133 	}
    134 	if typ == nil {
    135 		// external call
    136 		typ = f.pkg.types[arg].Type
    137 		if typ == nil {
    138 			return true // probably a type check problem
    139 		}
    140 	}
    141 	// If the type implements fmt.Formatter, we have nothing to check.
    142 	if f.isFormatter(typ) {
    143 		return true
    144 	}
    145 	// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
    146 	if t&argString != 0 && isConvertibleToString(typ) {
    147 		return true
    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(), 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 		// There's little we can do.
    212 		// Whether any particular verb is valid depends on the argument.
    213 		// The user may have reasonable prior knowledge of the contents of the interface.
    214 		return true
    215 
    216 	case *types.Basic:
    217 		switch typ.Kind() {
    218 		case types.UntypedBool,
    219 			types.Bool:
    220 			return t&argBool != 0
    221 
    222 		case types.UntypedInt,
    223 			types.Int,
    224 			types.Int8,
    225 			types.Int16,
    226 			types.Int32,
    227 			types.Int64,
    228 			types.Uint,
    229 			types.Uint8,
    230 			types.Uint16,
    231 			types.Uint32,
    232 			types.Uint64,
    233 			types.Uintptr:
    234 			return t&argInt != 0
    235 
    236 		case types.UntypedFloat,
    237 			types.Float32,
    238 			types.Float64:
    239 			return t&argFloat != 0
    240 
    241 		case types.UntypedComplex,
    242 			types.Complex64,
    243 			types.Complex128:
    244 			return t&argComplex != 0
    245 
    246 		case types.UntypedString,
    247 			types.String:
    248 			return t&argString != 0
    249 
    250 		case types.UnsafePointer:
    251 			return t&(argPointer|argInt) != 0
    252 
    253 		case types.UntypedRune:
    254 			return t&(argInt|argRune) != 0
    255 
    256 		case types.UntypedNil:
    257 			return t&argPointer != 0 // TODO?
    258 
    259 		case types.Invalid:
    260 			if *verbose {
    261 				f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg))
    262 			}
    263 			return true // Probably a type check problem.
    264 		}
    265 		panic("unreachable")
    266 	}
    267 
    268 	return false
    269 }
    270 
    271 func isConvertibleToString(typ types.Type) bool {
    272 	if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
    273 		// We explicitly don't want untyped nil, which is
    274 		// convertible to both of the interfaces below, as it
    275 		// would just panic anyway.
    276 		return false
    277 	}
    278 	if types.ConvertibleTo(typ, errorType) {
    279 		return true // via .Error()
    280 	}
    281 	if stringerType != nil && types.ConvertibleTo(typ, stringerType) {
    282 		return true // via .String()
    283 	}
    284 	return false
    285 }
    286 
    287 // hasBasicType reports whether x's type is a types.Basic with the given kind.
    288 func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool {
    289 	t := f.pkg.types[x].Type
    290 	if t != nil {
    291 		t = t.Underlying()
    292 	}
    293 	b, ok := t.(*types.Basic)
    294 	return ok && b.Kind() == kind
    295 }
    296 
    297 // matchStructArgType reports whether all the elements of the struct match the expected
    298 // type. For instance, with "%d" all the elements must be printable with the "%d" format.
    299 func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
    300 	for i := 0; i < typ.NumFields(); i++ {
    301 		typf := typ.Field(i)
    302 		if !f.matchArgTypeInternal(t, typf.Type(), arg, inProgress) {
    303 			return false
    304 		}
    305 		if t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
    306 			// Issue #17798: unexported Stringer or error cannot be properly fomatted.
    307 			return false
    308 		}
    309 	}
    310 	return true
    311 }
    312 
    313 // hasMethod reports whether the type contains a method with the given name.
    314 // It is part of the workaround for Formatters and should be deleted when
    315 // that workaround is no longer necessary.
    316 // TODO: This could be better once issue 6259 is fixed.
    317 func (f *File) hasMethod(typ types.Type, name string) bool {
    318 	// assume we have an addressable variable of type typ
    319 	obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name)
    320 	_, ok := obj.(*types.Func)
    321 	return ok
    322 }
    323 
    324 var archSizes = types.SizesFor("gc", build.Default.GOARCH)
    325