gc: detect type switch variable not used cases.

Fixes #873
Fixes #2162

R=rsc
CC=golang-dev
https://golang.org/cl/5341043
This commit is contained in:
Luuk van Dijk 2011-11-04 17:03:50 +01:00
parent f2dc50b48d
commit aac144b120
11 changed files with 82 additions and 31 deletions

View file

@ -418,9 +418,7 @@ simple_stmt:
| expr_list LCOLAS expr_list | expr_list LCOLAS expr_list
{ {
if($3->n->op == OTYPESW) { if($3->n->op == OTYPESW) {
Node *n; $$ = nod(OTYPESW, N, $3->n->right);
n = N;
if($3->next != nil) if($3->next != nil)
yyerror("expr.(type) must be alone in list"); yyerror("expr.(type) must be alone in list");
if($1->next != nil) if($1->next != nil)
@ -428,8 +426,7 @@ simple_stmt:
else if($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME) else if($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME)
yyerror("invalid variable name %N in type switch", $1->n); yyerror("invalid variable name %N in type switch", $1->n);
else else
n = $1->n; $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname.
$$ = nod(OTYPESW, n, $3->n->right);
break; break;
} }
$$ = colas($1, $3); $$ = colas($1, $3);
@ -448,7 +445,7 @@ simple_stmt:
case: case:
LCASE expr_or_type_list ':' LCASE expr_or_type_list ':'
{ {
Node *n; Node *n, *nn;
// will be converted to OCASE // will be converted to OCASE
// right will point to next case // right will point to next case
@ -458,12 +455,13 @@ case:
$$->list = $2; $$->list = $2;
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) { if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// type switch - declare variable // type switch - declare variable
n = newname(n->sym); nn = newname(n->sym);
n->used = 1; // TODO(rsc): better job here declare(nn, dclcontext);
declare(n, dclcontext); $$->nname = nn;
$$->nname = n;
// keep track of the instances for reporting unused
nn->defn = typesw->right;
} }
break;
} }
| LCASE expr_or_type_list '=' expr ':' | LCASE expr_or_type_list '=' expr ':'
{ {
@ -494,16 +492,18 @@ case:
} }
| LDEFAULT ':' | LDEFAULT ':'
{ {
Node *n; Node *n, *nn;
markdcl(); markdcl();
$$ = nod(OXCASE, N, N); $$ = nod(OXCASE, N, N);
if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) { if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
// type switch - declare variable // type switch - declare variable
n = newname(n->sym); nn = newname(n->sym);
n->used = 1; // TODO(rsc): better job here declare(nn, dclcontext);
declare(n, dclcontext); $$->nname = nn;
$$->nname = n;
// keep track of the instances for reporting unused
nn->defn = typesw->right;
} }
} }

View file

@ -63,7 +63,6 @@ walk(Node *fn)
{ {
char s[50]; char s[50];
NodeList *l; NodeList *l;
Node *n;
int lno; int lno;
curfn = fn; curfn = fn;
@ -77,15 +76,33 @@ walk(Node *fn)
yyerror("function ends without a return statement"); yyerror("function ends without a return statement");
lno = lineno; lno = lineno;
// Final typecheck for any unused variables.
// It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
for(l=fn->dcl; l; l=l->next)
if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO)
typecheck(&l->n, Erv | Easgn);
// Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
for(l=fn->dcl; l; l=l->next)
if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used)
l->n->defn->left->used++;
for(l=fn->dcl; l; l=l->next) { for(l=fn->dcl; l; l=l->next) {
n = l->n; if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used)
if(n->op != ONAME || n->class != PAUTO)
continue; continue;
lineno = n->lineno; if(l->n->defn && l->n->defn->op == OTYPESW) {
typecheck(&n, Erv | Easgn); // only needed for unused variables if(l->n->defn->left->used)
if(!n->used && n->sym->name[0] != '&' && !nsyntaxerrors) continue;
yyerror("%S declared and not used", n->sym); lineno = l->n->defn->left->lineno;
} yyerror("%S declared and not used", l->n->sym);
l->n->defn->left->used = 1; // suppress repeats
} else {
lineno = l->n->lineno;
yyerror("%S declared and not used", l->n->sym);
}
}
lineno = lno; lineno = lno;
if(nerrors != 0) if(nerrors != 0)
return; return;

View file

@ -617,7 +617,7 @@ func (p *Parser) Skip() error {
if err != nil { if err != nil {
return err return err
} }
switch t := tok.(type) { switch tok.(type) {
case StartElement: case StartElement:
if err := p.Skip(); err != nil { if err := p.Skip(); err != nil {
return err return err

View file

@ -131,7 +131,7 @@ func (x Const) Match(y Const) (u, v Const) {
// otherwise the result is invalid. // otherwise the result is invalid.
func (x Const) Convert(typ *Type) Const { func (x Const) Convert(typ *Type) Const {
// TODO(gri) implement this // TODO(gri) implement this
switch x := x.val.(type) { switch x.val.(type) {
case bool: case bool:
case *big.Int: case *big.Int:
case *big.Rat: case *big.Rat:

View file

@ -1131,7 +1131,7 @@ func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
// checkExpr checks that x is an expression (and not a type). // checkExpr checks that x is an expression (and not a type).
func (p *parser) checkExpr(x ast.Expr) ast.Expr { func (p *parser) checkExpr(x ast.Expr) ast.Expr {
switch t := unparen(x).(type) { switch unparen(x).(type) {
case *ast.BadExpr: case *ast.BadExpr:
case *ast.Ident: case *ast.Ident:
case *ast.BasicLit: case *ast.BasicLit:

View file

@ -20,7 +20,7 @@ type Getter interface {
func f1(p Empty) { func f1(p Empty) {
switch x := p.(type) { switch x := p.(type) {
default: println("failed to match interface"); os.Exit(1); default: println("failed to match interface", x); os.Exit(1);
case Getter: break; case Getter: break;
} }

View file

@ -12,7 +12,7 @@ func main() {
// and worse, compiled the wrong code // and worse, compiled the wrong code
// for one of them. // for one of them.
var x interface{}; var x interface{};
switch v := x.(type) { switch x.(type) {
case func(int): case func(int):
case func(f int): // ERROR "duplicate" case func(f int): // ERROR "duplicate"
} }

View file

@ -7,7 +7,7 @@
package main package main
func main() { func main() {
var v interface{} = 0; var v interface{} = 0;
switch x := v.(type) { switch v.(type) {
case int: case int:
fallthrough; // ERROR "fallthrough" fallthrough; // ERROR "fallthrough"
default: default:

View file

@ -80,7 +80,7 @@ func main() {
case 2: case 2:
i = 3.14 i = 3.14
} }
switch k := i.(type) { switch i.(type) {
case p0.T: case p0.T:
if j != 0 { if j != 0 {
println("type switch p0.T") println("type switch p0.T")

View file

@ -15,5 +15,7 @@ func foo(t interface{}, c chan int) {
case <-c: case <-c:
// bug was: internal compiler error: var without type, init: v // bug was: internal compiler error: var without type, init: v
} }
default:
_ = v
} }
} }

32
test/fixedbugs/bug373.go Normal file
View file

@ -0,0 +1,32 @@
// errchk $G $D/$F.go
// Copyright 2011 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.
// Issue 873, 2162
package foo
func f(x interface{}) {
switch t := x.(type) { // ERROR "declared and not used"
case int:
}
}
func g(x interface{}) {
switch t := x.(type) {
case int:
case float32:
println(t)
}
}
func h(x interface{}) {
switch t := x.(type) {
case int:
case float32:
default:
println(t)
}
}