diff --git a/cmd/vet/copylock.go b/cmd/vet/copylock.go index b9df0c85..ebe17c87 100644 --- a/cmd/vet/copylock.go +++ b/cmd/vet/copylock.go @@ -8,9 +8,10 @@ package main import ( "bytes" - "code.google.com/p/go.tools/go/types" "fmt" "go/ast" + + "code.google.com/p/go.tools/go/types" ) // checkCopyLocks checks whether a function might @@ -24,7 +25,7 @@ func (f *File) checkCopyLocks(d *ast.FuncDecl) { if d.Recv != nil && len(d.Recv.List) > 0 { expr := d.Recv.List[0].Type - if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr]); path != nil { + if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil { f.Warnf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path) } } @@ -32,7 +33,7 @@ func (f *File) checkCopyLocks(d *ast.FuncDecl) { if d.Type.Params != nil { for _, field := range d.Type.Params.List { expr := field.Type - if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr]); path != nil { + if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil { f.Warnf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path) } } @@ -41,7 +42,7 @@ func (f *File) checkCopyLocks(d *ast.FuncDecl) { if d.Type.Results != nil { for _, field := range d.Type.Results.List { expr := field.Type - if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr]); path != nil { + if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil { f.Warnf(expr.Pos(), "%s returns Lock by value: %v", d.Name.Name, path) } } diff --git a/cmd/vet/main.go b/cmd/vet/main.go index e8a66492..f5d4daaa 100644 --- a/cmd/vet/main.go +++ b/cmd/vet/main.go @@ -21,7 +21,6 @@ import ( "strconv" "strings" - "code.google.com/p/go.tools/go/exact" _ "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) @@ -203,8 +202,7 @@ func doPackageDir(directory string) { type Package struct { path string idents map[*ast.Ident]types.Object - types map[ast.Expr]types.Type - values map[ast.Expr]exact.Value + types map[ast.Expr]types.TypeAndValue spans map[types.Object]Span files []*File typesPkg *types.Package @@ -459,7 +457,7 @@ func (f *File) prepStringerReceiver(d *ast.FuncDecl) { func (f *File) isStringer(d *ast.FuncDecl) bool { return d.Recv != nil && d.Name.Name == "String" && len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 && - f.pkg.types[d.Type.Results.List[0].Type] == types.Typ[types.String] + f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String] } // walkGenDecl walks a general declaration. diff --git a/cmd/vet/nilfunc.go b/cmd/vet/nilfunc.go index c6b33fdd..3efb2de3 100644 --- a/cmd/vet/nilfunc.go +++ b/cmd/vet/nilfunc.go @@ -59,5 +59,5 @@ func (f *File) checkNilFuncComparison(e *ast.BinaryExpr) { // isNil reports whether the provided expression is the built-in nil // identifier. func (f *File) isNil(e ast.Expr) bool { - return f.pkg.types[e] == types.Typ[types.UntypedNil] + return f.pkg.types[e].Type == types.Typ[types.UntypedNil] } diff --git a/cmd/vet/print.go b/cmd/vet/print.go index 9f4e37c1..52e14f8e 100644 --- a/cmd/vet/print.go +++ b/cmd/vet/print.go @@ -86,7 +86,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) { f.Warn(call.Pos(), "too few arguments in call to", name) return } - lit := f.pkg.values[call.Args[formatIndex]] + lit := f.pkg.types[call.Args[formatIndex]].Value if lit == nil { if *verbose { f.Warn(call.Pos(), "can't check non-constant format in call to", name) @@ -380,7 +380,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) { arg := call.Args[argNum] if !f.matchArgType(v.typ, nil, arg) { typeString := "" - if typ := f.pkg.types[arg]; typ != nil { + if typ := f.pkg.types[arg].Type; typ != nil { typeString = typ.String() } f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString) diff --git a/cmd/vet/types.go b/cmd/vet/types.go index 37e570fd..a5bdeea4 100644 --- a/cmd/vet/types.go +++ b/cmd/vet/types.go @@ -10,15 +10,13 @@ import ( "go/ast" "go/token" - "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/types" ) func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { pkg.idents = make(map[*ast.Ident]types.Object) pkg.spans = make(map[types.Object]Span) - pkg.types = make(map[ast.Expr]types.Type) - pkg.values = make(map[ast.Expr]exact.Value) + pkg.types = make(map[ast.Expr]types.TypeAndValue) // By providing a Config with our own error function, it will continue // past the first error. There is no need for that function to do anything. config := types.Config{ @@ -26,7 +24,6 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { } info := &types.Info{ Types: pkg.types, - Values: pkg.values, Objects: pkg.idents, } typesPkg, err := config.Check(pkg.path, fs, astFiles, info) @@ -42,7 +39,7 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { // If it is not (probably a struct), it returns a printable form of the type. func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) { // Check that the CompositeLit's type is a slice or array (which needs no field keys), if possible. - typ := pkg.types[c] + typ := pkg.types[c].Type // If it's a named type, pull out the underlying type. If it's not, the Underlying // method returns the type itself. actual := typ @@ -91,7 +88,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp } if typ == nil { // external call - typ = f.pkg.types[arg] + typ = f.pkg.types[arg].Type if typ == nil { return true // probably a type check problem } @@ -250,7 +247,7 @@ func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Ex // being called has. func (f *File) numArgsInSignature(call *ast.CallExpr) int { // Check the type of the function or method declaration - typ := f.pkg.types[call.Fun] + typ := f.pkg.types[call.Fun].Type if typ == nil { return 0 } @@ -266,10 +263,10 @@ func (f *File) numArgsInSignature(call *ast.CallExpr) int { // func Error() string // where "string" is the universe's string type. We know the method is called "Error". func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { - typ := f.pkg.types[call] + typ := f.pkg.types[call].Type if typ != nil { // We know it's called "Error", so just check the function signature. - return types.Identical(f.pkg.types[call.Fun], stringerMethodType) + return types.Identical(f.pkg.types[call.Fun].Type, stringerMethodType) } // Without types, we can still check by hand. // Is it a selector expression? Otherwise it's a function call, not a method call. @@ -282,7 +279,7 @@ func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { return false } // Check the type of the method declaration - typ = f.pkg.types[sel] + typ = f.pkg.types[sel].Type if typ == nil { return false } diff --git a/go/loader/loader.go b/go/loader/loader.go index cf5d914e..06c26031 100644 --- a/go/loader/loader.go +++ b/go/loader/loader.go @@ -107,7 +107,6 @@ import ( "strings" "code.google.com/p/go.tools/astutil" - "code.google.com/p/go.tools/go/exact" "code.google.com/p/go.tools/go/gcimporter" "code.google.com/p/go.tools/go/types" ) @@ -620,8 +619,7 @@ func (imp *importer) createPackage(path string, files ...*ast.File) *PackageInfo info := &PackageInfo{ Files: files, Info: types.Info{ - Types: make(map[ast.Expr]types.Type), - Values: make(map[ast.Expr]exact.Value), + Types: make(map[ast.Expr]types.TypeAndValue), Objects: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Scopes: make(map[ast.Node]*types.Scope), diff --git a/go/loader/pkginfo.go b/go/loader/pkginfo.go index ca25057e..bb43ff18 100644 --- a/go/loader/pkginfo.go +++ b/go/loader/pkginfo.go @@ -34,7 +34,7 @@ func (info *PackageInfo) String() string { // func (info *PackageInfo) TypeOf(e ast.Expr) types.Type { if t, ok := info.Types[e]; ok { - return t + return t.Type } // Defining ast.Idents (id := expr) get only Ident callbacks // but not Expr callbacks. @@ -49,7 +49,7 @@ func (info *PackageInfo) TypeOf(e ast.Expr) types.Type { // Precondition: e belongs to the package's ASTs. // func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value { - return info.Values[e] + return info.Types[e].Value } // ObjectOf returns the typechecker object denoted by the specified id. diff --git a/go/types/api.go b/go/types/api.go index 6403bf4f..99cf5d2d 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -14,11 +14,11 @@ // // Constant folding computes the exact constant value (exact.Value) for // every expression (ast.Expr) that is a compile-time constant. -// Use Info.Values for the results of constant folding. +// Use Info.Types[expr].Value for the results of constant folding. // // Type inference computes the type (Type) of every expression (ast.Expr) // and checks for compliance with the language specification. -// Use Info.Types for the results of type evaluation. +// Use Info.Types[expr].Type for the results of type inference. // package types @@ -115,22 +115,26 @@ type Config struct { // in a client of go/types will initialize DefaultImport to gcimporter.Import. var DefaultImport Importer +type TypeAndValue struct { + Type Type + Value exact.Value +} + // Info holds result type information for a type-checked package. // Only the information for which a map is provided is collected. // If the package has type errors, the collected information may // be incomplete. type Info struct { - // Types maps expressions to their types. Identifiers on the - // lhs of declarations are collected in Objects, not Types. + // Types maps expressions to their types, and for constant + // expressions, their values. + // Identifiers on the lhs of declarations are collected in + // Objects, not Types. // // For an expression denoting a predeclared built-in function // the recorded signature is call-site specific. If the call // result is not a constant, the recorded type is an argument- // specific signature. Otherwise, the recorded type is invalid. - Types map[ast.Expr]Type - - // Values maps constant expressions to their values. - Values map[ast.Expr]exact.Value + Types map[ast.Expr]TypeAndValue // Objects maps identifiers to their corresponding objects (including // package names, dots "." of dot-imports, and blank "_" identifiers). diff --git a/go/types/api_test.go b/go/types/api_test.go index 6742a3e2..147282bb 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -13,7 +13,6 @@ import ( "strings" "testing" - "code.google.com/p/go.tools/go/exact" _ "code.google.com/p/go.tools/go/gcimporter" . "code.google.com/p/go.tools/go/types" ) @@ -94,14 +93,13 @@ func TestValuesInfo(t *testing.T) { for _, test := range tests { info := Info{ - Types: make(map[ast.Expr]Type), - Values: make(map[ast.Expr]exact.Value), + Types: make(map[ast.Expr]TypeAndValue), } name := mustTypecheck(t, "ValuesInfo", test.src, &info) // look for constant expression var expr ast.Expr - for e := range info.Values { + for e := range info.Types { if ExprString(e) == test.expr { expr = e break @@ -111,15 +109,16 @@ func TestValuesInfo(t *testing.T) { t.Errorf("package %s: no expression found for %s", name, test.expr) continue } + tv := info.Types[expr] // check that type is correct - if got := info.Types[expr].String(); got != test.typ { + if got := tv.Type.String(); got != test.typ { t.Errorf("package %s: got type %s; want %s", name, got, test.typ) continue } // check that value is correct - if got := info.Values[expr].String(); got != test.val { + if got := tv.Value.String(); got != test.val { t.Errorf("package %s: got value %s; want %s", name, got, test.val) } } @@ -206,14 +205,14 @@ func TestTypesInfo(t *testing.T) { } for _, test := range tests { - info := Info{Types: make(map[ast.Expr]Type)} + info := Info{Types: make(map[ast.Expr]TypeAndValue)} name := mustTypecheck(t, "TypesInfo", test.src, &info) // look for expression type var typ Type - for e, t := range info.Types { + for e, tv := range info.Types { if ExprString(e) == test.expr { - typ = t + typ = tv.Type break } } diff --git a/go/types/builtins_test.go b/go/types/builtins_test.go index fecc531f..c33f63cb 100644 --- a/go/types/builtins_test.go +++ b/go/types/builtins_test.go @@ -134,7 +134,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) { var conf Config objects := make(map[*ast.Ident]Object) - types := make(map[ast.Expr]Type) + types := make(map[ast.Expr]TypeAndValue) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Objects: objects, Types: types}) if err != nil { t.Errorf("%s: %s", src0, err) @@ -144,7 +144,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) { // find called function n := 0 var fun ast.Expr - for x, _ := range types { + for x := range types { if call, _ := x.(*ast.CallExpr); call != nil { fun = call.Fun n++ @@ -158,7 +158,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) { // check recorded types for fun and descendents (may be parenthesized) for { // the recorded type for the built-in must match the wanted signature - typ := types[fun] + typ := types[fun].Type if typ == nil { t.Errorf("%s: no type recorded for %s", src0, ExprString(fun)) return diff --git a/go/types/check.go b/go/types/check.go index 734cbf60..c4694bec 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -92,14 +92,11 @@ func (check *checker) delay(f func()) { func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) { assert(x != nil && typ != nil) - if m := check.Types; m != nil { - m[x] = typ - } if val != nil { assert(isConstType(typ)) - if m := check.Values; m != nil { - m[x] = val - } + } + if m := check.Types; m != nil { + m[x] = TypeAndValue{typ, val} } } @@ -129,12 +126,14 @@ func (check *checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1])) if m := check.Types; m != nil { for { - assert(m[x] != nil) // should have been recorded already + tv := m[x] + assert(tv.Type != nil) // should have been recorded already pos := x.Pos() - m[x] = NewTuple( + tv.Type = NewTuple( NewVar(pos, check.pkg, "", a[0]), NewVar(pos, check.pkg, "", a[1]), ) + m[x] = tv // if x is a parenthesized expression (p.X), update p.X p, _ := x.(*ast.ParenExpr) if p == nil { @@ -248,7 +247,7 @@ func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File // TODO(gri) Consider doing this before and // after function body checking for smaller // map size and more immediate feedback. - if check.Types != nil || check.Values != nil { + if check.Types != nil { for x, info := range check.untyped { check.recordTypeAndValue(x, info.typ, info.val) } diff --git a/go/types/expr.go b/go/types/expr.go index f7d1ed9a..6b102d08 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -53,8 +53,7 @@ constant lhs must be representable as an integer. When an expression gets its final type, either on the way out from rawExpr, on the way down in updateExprType, or at the end of the type checker run, -the type (and constant value, if any) is recorded via Info.Types and Values, -if present. +the type (and constant value, if any) is recorded via Info.Types, if present. */ type opPredicates map[token.Token]func(Type) bool diff --git a/go/types/issues_test.go b/go/types/issues_test.go index 67d9e130..e61f0eff 100644 --- a/go/types/issues_test.go +++ b/go/types/issues_test.go @@ -48,13 +48,13 @@ var ( } var conf Config - types := make(map[ast.Expr]Type) + types := make(map[ast.Expr]TypeAndValue) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) if err != nil { t.Fatal(err) } - for x, typ := range types { + for x, tv := range types { var want Type switch x := x.(type) { case *ast.BasicLit: @@ -75,8 +75,8 @@ var ( want = Typ[UntypedNil] } } - if want != nil && !Identical(typ, want) { - t.Errorf("got %s; want %s", typ, want) + if want != nil && !Identical(tv.Type, want) { + t.Errorf("got %s; want %s", tv.Type, want) } } } @@ -96,7 +96,7 @@ func f() int { } var conf Config - types := make(map[ast.Expr]Type) + types := make(map[ast.Expr]TypeAndValue) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types}) if err != nil { t.Fatal(err) @@ -104,10 +104,10 @@ func f() int { want := Typ[Int] n := 0 - for x, got := range types { + for x, tv := range types { if _, ok := x.(*ast.CallExpr); ok { - if got != want { - t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), got, want) + if tv.Type != want { + t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want) } n++ }