github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/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/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)