| // Copyright 2020 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package typesinternal provides helpful operators for dealing with | |
| // go/types: | |
| // | |
| // - operators for querying typed syntax trees (e.g. [Imports], [IsFunctionNamed]); | |
| // - functions for converting types to strings or syntax (e.g. [TypeExpr], FileQualifier]); | |
| // - helpers for working with the [go/types] API (e.g. [NewTypesInfo]); | |
| // - access to internal go/types APIs that are not yet | |
| // exported (e.g. [SetUsesCgo], [ErrorCodeStartEnd]); and | |
| // - common algorithms related to types (e.g. [TooNewStdSymbols]). | |
| // | |
| // See also: | |
| // - [golang.org/x/tools/internal/astutil], for operations on untyped syntax; | |
| // - [golang.org/x/tools/internal/analysisinernal], for helpers for analyzers; | |
| // - [golang.org/x/tools/internal/refactor], for operators to compute text edits. | |
| package typesinternal | |
| import ( | |
| "go/ast" | |
| "go/token" | |
| "go/types" | |
| "iter" | |
| "reflect" | |
| "golang.org/x/tools/go/ast/inspector" | |
| ) | |
| func SetUsesCgo(conf *types.Config) bool { | |
| v := reflect.ValueOf(conf).Elem() | |
| f := v.FieldByName("go115UsesCgo") | |
| if !f.IsValid() { | |
| f = v.FieldByName("UsesCgo") | |
| if !f.IsValid() { | |
| return false | |
| } | |
| } | |
| *(*bool)(f.Addr().UnsafePointer()) = true | |
| return true | |
| } | |
| // ErrorCodeStartEnd extracts additional information from types.Error values | |
| // generated by Go version 1.16 and later: the error code, start position, and | |
| // end position. If all positions are valid, start <= err.Pos <= end. | |
| // | |
| // If the data could not be read, the final result parameter will be false. | |
| // | |
| // TODO(adonovan): eliminate start/end when proposal #71803 is accepted. | |
| func ErrorCodeStartEnd(err types.Error) (code ErrorCode, start, end token.Pos, ok bool) { | |
| var data [3]int | |
| // By coincidence all of these fields are ints, which simplifies things. | |
| v := reflect.ValueOf(err) | |
| for i, name := range []string{"go116code", "go116start", "go116end"} { | |
| f := v.FieldByName(name) | |
| if !f.IsValid() { | |
| return 0, 0, 0, false | |
| } | |
| data[i] = int(f.Int()) | |
| } | |
| return ErrorCode(data[0]), token.Pos(data[1]), token.Pos(data[2]), true | |
| } | |
| // NameRelativeTo returns a types.Qualifier that qualifies members of | |
| // all packages other than pkg, using only the package name. | |
| // (By contrast, [types.RelativeTo] uses the complete package path, | |
| // which is often excessive.) | |
| // | |
| // If pkg is nil, it is equivalent to [*types.Package.Name]. | |
| // | |
| // TODO(adonovan): all uses of this with TypeString should be | |
| // eliminated when https://go.dev/issues/75604 is resolved. | |
| func NameRelativeTo(pkg *types.Package) types.Qualifier { | |
| return func(other *types.Package) string { | |
| if pkg != nil && pkg == other { | |
| return "" // same package; unqualified | |
| } | |
| return other.Name() | |
| } | |
| } | |
| // TypeNameFor returns the type name symbol for the specified type, if | |
| // it is a [*types.Alias], [*types.Named], [*types.TypeParam], or a | |
| // [*types.Basic] representing a type. | |
| // | |
| // For all other types, and for Basic types representing a builtin, | |
| // constant, or nil, it returns nil. Be careful not to convert the | |
| // resulting nil pointer to a [types.Object]! | |
| // | |
| // If t is the type of a constant, it may be an "untyped" type, which | |
| // has no TypeName. To access the name of such types (e.g. "untyped | |
| // int"), use [types.Basic.Name]. | |
| func TypeNameFor(t types.Type) *types.TypeName { | |
| switch t := t.(type) { | |
| case *types.Alias: | |
| return t.Obj() | |
| case *types.Named: | |
| return t.Obj() | |
| case *types.TypeParam: | |
| return t.Obj() | |
| case *types.Basic: | |
| // See issues #71886 and #66890 for some history. | |
| if tname, ok := types.Universe.Lookup(t.Name()).(*types.TypeName); ok { | |
| return tname | |
| } | |
| } | |
| return nil | |
| } | |
| // A NamedOrAlias is a [types.Type] that is named (as | |
| // defined by the spec) and capable of bearing type parameters: it | |
| // abstracts aliases ([types.Alias]) and defined types | |
| // ([types.Named]). | |
| // | |
| // Every type declared by an explicit "type" declaration is a | |
| // NamedOrAlias. (Built-in type symbols may additionally | |
| // have type [types.Basic], which is not a NamedOrAlias, | |
| // though the spec regards them as "named"; see [TypeNameFor].) | |
| // | |
| // NamedOrAlias cannot expose the Origin method, because | |
| // [types.Alias.Origin] and [types.Named.Origin] have different | |
| // (covariant) result types; use [Origin] instead. | |
| type NamedOrAlias interface { | |
| types.Type | |
| Obj() *types.TypeName | |
| TypeArgs() *types.TypeList | |
| TypeParams() *types.TypeParamList | |
| SetTypeParams(tparams []*types.TypeParam) | |
| } | |
| var ( | |
| _ NamedOrAlias = (*types.Alias)(nil) | |
| _ NamedOrAlias = (*types.Named)(nil) | |
| ) | |
| // Origin returns the generic type of the Named or Alias type t if it | |
| // is instantiated, otherwise it returns t. | |
| func Origin(t NamedOrAlias) NamedOrAlias { | |
| switch t := t.(type) { | |
| case *types.Alias: | |
| return t.Origin() | |
| case *types.Named: | |
| return t.Origin() | |
| } | |
| return t | |
| } | |
| // IsPackageLevel reports whether obj is a package-level symbol. | |
| func IsPackageLevel(obj types.Object) bool { | |
| return obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() | |
| } | |
| // NewTypesInfo returns a *types.Info with all maps populated. | |
| func NewTypesInfo() *types.Info { | |
| return &types.Info{ | |
| Types: map[ast.Expr]types.TypeAndValue{}, | |
| Instances: map[*ast.Ident]types.Instance{}, | |
| Defs: map[*ast.Ident]types.Object{}, | |
| Uses: map[*ast.Ident]types.Object{}, | |
| Implicits: map[ast.Node]types.Object{}, | |
| Selections: map[*ast.SelectorExpr]*types.Selection{}, | |
| Scopes: map[ast.Node]*types.Scope{}, | |
| FileVersions: map[*ast.File]string{}, | |
| } | |
| } | |
| // EnclosingScope returns the innermost block logically enclosing the cursor. | |
| func EnclosingScope(info *types.Info, cur inspector.Cursor) *types.Scope { | |
| for cur := range cur.Enclosing() { | |
| n := cur.Node() | |
| // A function's Scope is associated with its FuncType. | |
| switch f := n.(type) { | |
| case *ast.FuncDecl: | |
| n = f.Type | |
| case *ast.FuncLit: | |
| n = f.Type | |
| } | |
| if b := info.Scopes[n]; b != nil { | |
| return b | |
| } | |
| } | |
| panic("no Scope for *ast.File") | |
| } | |
| // Imports reports whether path is imported by pkg. | |
| func Imports(pkg *types.Package, path string) bool { | |
| for _, imp := range pkg.Imports() { | |
| if imp.Path() == path { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| // ObjectKind returns a description of the object's kind. | |
| // | |
| // from objectKind in go/types | |
| func ObjectKind(obj types.Object) string { | |
| switch obj := obj.(type) { | |
| case *types.PkgName: | |
| return "package name" | |
| case *types.Const: | |
| return "constant" | |
| case *types.TypeName: | |
| if obj.IsAlias() { | |
| return "type alias" | |
| } else if _, ok := obj.Type().(*types.TypeParam); ok { | |
| return "type parameter" | |
| } else { | |
| return "defined type" | |
| } | |
| case *types.Var: | |
| switch obj.Kind() { | |
| case types.PackageVar: | |
| return "package-level variable" | |
| case types.LocalVar: | |
| return "local variable" | |
| case types.RecvVar: | |
| return "receiver" | |
| case types.ParamVar: | |
| return "parameter" | |
| case types.ResultVar: | |
| return "result variable" | |
| case types.FieldVar: | |
| return "struct field" | |
| } | |
| case *types.Func: | |
| if obj.Signature().Recv() != nil { | |
| return "method" | |
| } else { | |
| return "function" | |
| } | |
| case *types.Label: | |
| return "label" | |
| case *types.Builtin: | |
| return "built-in function" | |
| case *types.Nil: | |
| return "untyped nil" | |
| } | |
| return "unknown symbol" | |
| } | |
| // ImplicitFieldSelections returns the sequence of implicit embedded fields | |
| // traversed by the given selection. It skips the final leaf field or method. | |
| // The boolean component indicates whether the traversal traversed a pointer. | |
| func ImplicitFieldSelections(seln *types.Selection) iter.Seq2[*types.Var, bool] { | |
| return func(yield func(*types.Var, bool) bool) { | |
| var ( | |
| t = seln.Recv() | |
| indices = seln.Index() | |
| ) | |
| for _, idx := range indices[:len(indices)-1] { | |
| ptr, isPtr := t.Underlying().(*types.Pointer) | |
| if isPtr { | |
| t = ptr.Elem() | |
| } | |
| structType, ok := t.Underlying().(*types.Struct) | |
| if !ok { | |
| break | |
| } | |
| field := structType.Field(idx) | |
| if !yield(field, isPtr) { | |
| break | |
| } | |
| t = field.Type() | |
| } | |
| } | |
| } | |
| // TupleOf returns a tuple type with the specified list of (unnamed) | |
| // var types. Neither the zero nor one cases are special. | |
| func TupleOf(elems ...types.Type) *types.Tuple { | |
| params := make([]*types.Var, len(elems)) | |
| for i, elem := range elems { | |
| params[i] = types.NewParam(token.NoPos, nil, "", elem) | |
| } | |
| return types.NewTuple(params...) | |
| } |