diff --git a/go/types/api_test.go b/go/types/api_test.go index 056dbcdf..9293b24a 100644 --- a/go/types/api_test.go +++ b/go/types/api_test.go @@ -132,7 +132,7 @@ func TestScopesInfo(t *testing.T) { {`package p; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{ "file:", "func:t", "type switch:t", }}, - {`package p; func _(t interface{}) { switch x := t.(type) { case int: } }`, []string{ + {`package p; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{ "file:", "func:t", "type switch:", "case:x", // x implicitly declared }}, {`package p; func _() { select{} }`, []string{ diff --git a/go/types/assignments.go b/go/types/assignments.go index 2f44dede..06fb2abf 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -111,8 +111,11 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type { return nil } - // Don't evaluate lhs if it is the (possibly parenthesized) blank identifier. - if ident, _ := unparen(lhs).(*ast.Ident); ident != nil && ident.Name == "_" { + // Determine if the lhs is a (possibly parenthesized) identifier. + ident, _ := unparen(lhs).(*ast.Ident) + + // Don't evaluate lhs if it is the blank identifier. + if ident != nil && ident.Name == "_" { check.recordObject(ident, nil) // If the lhs is untyped, determine the default type. // The spec is unclear about this, but gc appears to @@ -131,8 +134,26 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type { return typ } + // If the lhs is an identifier denoting a variable v, this assignment + // is not a 'use' of v. Remember current value of v.used and restore + // after evaluating the lhs via check.expr. + var v *Var + var v_used bool + if ident != nil { + if obj := check.topScope.LookupParent(ident.Name); obj != nil { + v, _ = obj.(*Var) + if v != nil { + v_used = v.used + } + } + } + var z operand check.expr(&z, lhs) + if v != nil { + v.used = v_used // restore v.used + } + if z.mode == invalid || z.typ == Typ[Invalid] { return nil } diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index 7e08a23e..2e6f80b8 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -490,7 +490,7 @@ func (p *gcParser) parseField() (*Var, string) { if p.tok == scanner.String { tag = p.expect(scanner.String) } - return NewFieldVar(token.NoPos, pkg, name, typ, anonymous), tag + return NewField(token.NoPos, pkg, name, typ, anonymous), tag } // StructType = "struct" "{" [ FieldList ] "}" . diff --git a/go/types/objects.go b/go/types/objects.go index 6dd8c97f..117e8c27 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -13,8 +13,7 @@ import ( "code.google.com/p/go.tools/go/exact" ) -// TODO(gri) All objects looks very similar now. Maybe just have single Object struct with an object kind? -// TODO(gri) Document factory, accessor methods, and fields. +// TODO(gri) Document factory, accessor methods, and fields. General clean-up. // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. @@ -162,19 +161,25 @@ func (obj *TypeName) String() string { return obj.toString("type", obj.typ.Under type Var struct { object - anonymous bool // if set, this variable is an anonymous struct field, and name is the type name + anonymous bool // if set, the variable is an anonymous struct field, and name is the type name visited bool // for initialization cycle detection + used bool // if set, the variable was 'used' } func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var { - return &Var{object{nil, pos, pkg, name, typ}, false, false} + return &Var{object: object{nil, pos, pkg, name, typ}} } -func NewFieldVar(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { - return &Var{object{nil, pos, pkg, name, typ}, anonymous, false} +func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var { + return &Var{object: object{nil, pos, pkg, name, typ}, used: true} // parameters are always 'used' +} + +func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var { + return &Var{object: object{nil, pos, pkg, name, typ}, anonymous: anonymous} } func (obj *Var) Anonymous() bool { return obj.anonymous } +func (obj *Var) Used() bool { return obj.used } func (obj *Var) String() string { return obj.toString("var", obj.typ) } // A Func represents a declared function, concrete method, or abstract diff --git a/go/types/resolver.go b/go/types/resolver.go index 0c0ea5cc..20a764de 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -381,13 +381,34 @@ func (check *checker) resolveFiles(files []*ast.File) { } fmt.Println("---", s) } - check.topScope = f.sig.scope // open the function scope + + check.topScope = f.sig.scope // open function scope check.funcSig = f.sig check.stmtList(f.body.List, false) if f.sig.results.Len() > 0 && !check.isTerminating(f.body, "") { check.errorf(f.body.Rbrace, "missing return") } } + + // Phase 5: Check for declared but not used packages and variables. + + // Note: must happen after checking all functions because closures may affect outer scopes + for _, f := range check.funcList { + // spec: "Implementation restriction: A compiler may make it illegal to + // declare a variable inside a function body if the variable is never used." + check.usage(f.sig.scope) + } +} + +func (check *checker) usage(scope *Scope) { + for _, obj := range scope.elems { + if v, _ := obj.(*Var); v != nil && !v.used { + check.errorf(v.pos, "%s declared but not used", v.name) + } + } + for _, scope := range scope.children { + check.usage(scope) + } } // objDecl type-checks the declaration of obj in its respective file scope. diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go index d53244ac..9125b9e8 100644 --- a/go/types/resolver_test.go +++ b/go/types/resolver_test.go @@ -46,10 +46,11 @@ var sources = []string{ func (T) _() {} var i I var _ = i.m - func _(s []int) { for i, x := range s {} } + func _(s []int) { for i, x := range s { _, _ = i, x } } func _(x interface{}) { switch x := x.(type) { case int: + _ = x } } `, diff --git a/go/types/stmt.go b/go/types/stmt.go index 8b097801..8e8f5242 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -423,6 +423,14 @@ func (check *checker) stmt(s ast.Stmt, fallthroughOk bool) { T = x.typ } obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T) + // For now we mark all implicitly declared variables as used. If we don't, + // we will get an error for each implicitly declared but unused variable, + // even if there are others belonging to the same type switch which are used. + // The right solution will count any use of an implicit variable in this + // switch as a use for all of them, but we cannot make that decision until + // we have seen all code, including possibly nested closures. + // TODO(gri) Fix this! + obj.used = true check.declareObj(check.topScope, nil, obj) check.recordImplicit(clause, obj) } diff --git a/go/types/testdata/builtins.src b/go/types/testdata/builtins.src index 6a41c34f..8dcfdf8e 100644 --- a/go/types/testdata/builtins.src +++ b/go/types/testdata/builtins.src @@ -11,27 +11,26 @@ import "unsafe" func _append() { var x int var s []byte - _0 := append /* ERROR "argument" */ () - _1 := append("foo" /* ERROR "not a typed slice" */) - _2 := append(nil /* ERROR "not a typed slice" */, s) - _3 := append(x /* ERROR "not a typed slice" */, s) - _4 := append(s) + _ = append /* ERROR "argument" */ () + _ = append("foo" /* ERROR "not a typed slice" */) + _ = append(nil /* ERROR "not a typed slice" */, s) + _ = append(x /* ERROR "not a typed slice" */, s) + _ = append(s) append /* ERROR "not used" */ (s) } func _cap() { var a [10]bool var p *[20]int - var s []int var c chan string - _0 := cap /* ERROR "argument" */ () - _1 := cap /* ERROR "argument" */ (1, 2) - _2 := cap(42 /* ERROR "invalid" */) + _ = cap /* ERROR "argument" */ () + _ = cap /* ERROR "argument" */ (1, 2) + _ = cap(42 /* ERROR "invalid" */) const _3 = cap(a) assert(_3 == 10) const _4 = cap(p) assert(_4 == 20) - _5 := cap(c) + _ = cap(c) cap /* ERROR "not used" */ (c) // issue 4744 @@ -54,7 +53,6 @@ func _complex() { var f32 float32 var f64 float64 var c64 complex64 - var c128 complex128 _ = complex /* ERROR "argument" */ () _ = complex /* ERROR "argument" */ (1) _ = complex(true /* ERROR "invalid argument" */ , 0) @@ -116,6 +114,7 @@ func _copy() { n1 := copy(s, a[0:]) // n1 == 6, s == []int{0, 1, 2, 3, 4, 5} n2 := copy(s, s[2:]) // n2 == 4, s == []int{2, 3, 4, 5, 4, 5} n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello") + _, _, _ = n1, n2, n3 } func _delete() { @@ -149,13 +148,13 @@ func _imag() { f32 = imag /* ERROR "cannot assign" */ (c128) f64 = imag /* ERROR "cannot assign" */ (c64) imag /* ERROR "not used" */ (c64) + _, _ = f32, f64 } func _len() { const c = "foobar" var a [10]bool var p *[20]int - var s []int var m map[string]complex128 _ = len /* ERROR "argument" */ () _ = len /* ERROR "argument" */ (1, 2) @@ -292,6 +291,7 @@ func _real() { f32 = real /* ERROR "cannot assign" */ (c128) f64 = real /* ERROR "cannot assign" */ (c64) real /* ERROR "not used" */ (c64) + _, _ = f32, f64 } func _recover() { diff --git a/go/types/testdata/const0.src b/go/types/testdata/const0.src index b0a83279..6515e2aa 100644 --- a/go/types/testdata/const0.src +++ b/go/types/testdata/const0.src @@ -270,4 +270,5 @@ func _() { iota := 123 const x = iota /* ERROR "is not constant" */ var y = iota + _ = y } diff --git a/go/types/testdata/decls0.src b/go/types/testdata/decls0.src index d0b44fd1..b2e3b928 100644 --- a/go/types/testdata/decls0.src +++ b/go/types/testdata/decls0.src @@ -61,7 +61,7 @@ func init /* ERROR "missing function body" */ () func _() { const init = 0 } func _() { type init int } -func _() { var init int } +func _() { var init int; _ = init } // invalid array types type ( @@ -231,4 +231,5 @@ func _() { var i BlankI var x BlankT i = x /* ERROR "cannot assign" */ + _ = i } diff --git a/go/types/testdata/decls1.src b/go/types/testdata/decls1.src index a881e773..56c88333 100644 --- a/go/types/testdata/decls1.src +++ b/go/types/testdata/decls1.src @@ -110,6 +110,10 @@ func _() { m2a, m2b, m2c /* ERROR "missing init expr for m2c" */ = 1, 2 m3a, m3b = 1, 2, 3 /* ERROR "extra init expr 3" */ ) + + _, _ = m1a, m1b + _, _, _ = m2a, m2b, m2c + _, _ = m3a, m3b } // Declaration of parameters and results diff --git a/go/types/testdata/expr2.src b/go/types/testdata/expr2.src index 674be400..85bc2fed 100644 --- a/go/types/testdata/expr2.src +++ b/go/types/testdata/expr2.src @@ -14,6 +14,7 @@ func _bool() { var b bool var x, y float32 b = x < y + _ = b _ = struct{b bool}{x < y} } diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index a91c7ea7..7e924843 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -17,10 +17,14 @@ func indexes() { _ = a[- /* ERROR "negative" */ 1] _ = a[- /* ERROR "negative" */ 1 :] _ = a[: - /* ERROR "negative" */ 1] + var a0 int a0 = a[0] + _ = a0 var a1 int32 a1 = a /* ERROR "cannot assign" */ [1] + _ = a1 + _ = a[9] _ = a[10 /* ERROR "index .* out of bounds" */ ] _ = a[1 /* ERROR "overflows" */ <<100] @@ -67,8 +71,10 @@ func indexes() { _ = t[: - /* ERROR "negative" */ 1] var t0 byte t0 = t[0] + _ = t0 var t1 rune t1 = t /* ERROR "cannot assign" */ [2] + _ = t1 _ = ("foo" + "bar")[5] _ = ("foo" + "bar")[6 /* ERROR "index .* out of bounds" */ ] @@ -78,10 +84,12 @@ func indexes() { _ = c[: - /* ERROR "negative" */ 1] var c0 byte c0 = c[0] + _ = c0 var c2 float32 c2 = c /* ERROR "cannot assign" */ [2] _ = c[3 /* ERROR "index .* out of bounds" */ ] _ = ""[0 /* ERROR "index .* out of bounds" */ ] + _ = c2 _ = s[1<<30] // no compile-time error here @@ -94,6 +102,7 @@ func indexes() { ss = "foo"[i:j] ms = "foo" /* ERROR "cannot assign" */ [1:2] ms = "foo" /* ERROR "cannot assign" */ [i:j] + _, _ = ss, ms } type T struct { @@ -111,6 +120,7 @@ func method_expressions() { var f func(*T) = T /* ERROR "not in method set" */ .m var g func(*T) = (*T).m + _, _ = f, g _ = T /* ERROR "has no method" */ .y _ = ( /* ERROR "has no method" */ *T).y @@ -195,8 +205,10 @@ func array_literals() { var a14 [4]int a13 = a1 a14 = a1 /* ERROR "cannot assign" */ + _, _ = a13, a14 a2 := [...]int{- /* ERROR "negative" */ 1: 0} + _ = a2 a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4} assert(len(a3) == 5) // somewhat arbitrary @@ -295,6 +307,7 @@ func type_asserts() { var e interface{} var ok bool x, ok = e.(int) + _ = ok var t I _ = t /* ERROR "use of .* outside type switch" */ .(type) diff --git a/go/types/testdata/shifts.src b/go/types/testdata/shifts.src index cdc57e70..6c6d8f0f 100644 --- a/go/types/testdata/shifts.src +++ b/go/types/testdata/shifts.src @@ -65,6 +65,7 @@ func shifts2() { v float32 = 1 /* ERROR "must be integer" */ <> s) + _, _, _ = u, v, x } func shifts4() { @@ -176,7 +178,6 @@ func shifts6() { _ = append(b, 1.0< 0 { // named parameter for _, name := range field.Names { - par := NewVar(name.Pos(), check.pkg, name.Name, typ) + par := NewParam(name.Pos(), check.pkg, name.Name, typ) check.declareObj(scope, name, par) params = append(params, par) } } else { // anonymous parameter - par := NewVar(ftype.Pos(), check.pkg, "", typ) + par := NewParam(ftype.Pos(), check.pkg, "", typ) check.recordImplicit(field, par) params = append(params, par) } @@ -474,7 +475,7 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ tags = append(tags, tag) } - fld := NewFieldVar(pos, check.pkg, name, typ, anonymous) + fld := NewField(pos, check.pkg, name, typ, anonymous) check.declareFld(&fset, ident, fld) fields = append(fields, fld) }