1
0
mirror of https://github.com/golang/go synced 2024-11-23 21:50:08 -07:00

gc: diagnose unused labels

R=ken2
CC=golang-dev
https://golang.org/cl/4287047
This commit is contained in:
Russ Cox 2011-03-15 14:05:37 -04:00
parent e72156b316
commit 66f09fd459
8 changed files with 251 additions and 67 deletions

View File

@ -32,7 +32,7 @@ compile(Node *fn)
return;
// set up domain for labels
labellist = L;
clearlabels();
lno = setlineno(fn);

View File

@ -32,7 +32,7 @@ compile(Node *fn)
return;
// set up domain for labels
labellist = L;
clearlabels();
lno = setlineno(fn);

View File

@ -32,7 +32,7 @@ compile(Node *fn)
return;
// set up domain for labels
labellist = L;
clearlabels();
lno = setlineno(fn);

View File

@ -22,7 +22,6 @@ dflag(void)
/*
* declaration stack & operations
*/
static Sym* dclstack;
static void
dcopy(Sym *a, Sym *b)

View File

@ -64,62 +64,83 @@ allocparams(void)
lineno = lno;
}
void
clearlabels(void)
{
Label *l;
for(l=labellist; l!=L; l=l->link)
l->sym->label = L;
labellist = L;
lastlabel = L;
}
static void
newlab(int op, Sym *s, Node *stmt)
newlab(int op, Node *nlab, Node *stmt)
{
Label *lab;
Sym *s;
int32 lno;
s = nlab->left->sym;
lno = nlab->left->lineno;
lab = mal(sizeof(*lab));
lab->link = labellist;
labellist = lab;
if(lastlabel == nil)
labellist = lab;
else
lastlabel->link = lab;
lastlabel = lab;
lab->lineno = lno;
lab->sym = s;
lab->op = op;
lab->label = pc;
lab->stmt = stmt;
if(op == OLABEL) {
if(s->label != L) {
lineno = lno;
yyerror("label %S already defined at %L", s, s->label->lineno);
} else
s->label = lab;
}
}
void
checklabels(void)
{
Label *l, *m;
Label *l;
Sym *s;
int lno;
// // print the label list
// for(l=labellist; l!=L; l=l->link) {
// print("lab %O %S\n", l->op, l->sym);
// }
lno = lineno;
// resolve goto using syms
for(l=labellist; l!=L; l=l->link) {
switch(l->op) {
case OLABEL:
// these are definitions -
switch(l->op) {
case OGOTO:
s = l->sym;
for(m=labellist; m!=L; m=m->link) {
if(m->sym != s)
continue;
switch(m->op) {
case OLABEL:
// these are definitions -
// look for redefinitions
if(l != m)
yyerror("label %S redefined", s);
break;
case OGOTO:
// these are references -
// patch to definition
patch(m->label, l->label);
m->sym = S; // mark done
break;
}
if(s->label == L) {
lineno = l->lineno;
yyerror("label %S not defined", s);
break;
}
s->label->used = 1;
patch(l->label, s->label->label);
break;
}
}
// diagnostic for all undefined references
for(l=labellist; l!=L; l=l->link)
if(l->op == OGOTO && l->sym != S)
yyerror("label %S not defined", l->sym);
// diagnose unused labels
for(l=labellist; l!=L; l=l->link) {
if(l->op == OLABEL && !l->used) {
lineno = l->lineno;
yyerror("label %S defined and not used", l->sym);
}
}
lineno = lno;
}
/*
@ -171,7 +192,7 @@ gen(Node *n)
// insert no-op so that
// L:; for { }
// does not treat L as a label for the loop.
if(labellist && labellist->label == p3)
if(lastlabel != L && lastlabel->label == p3)
gused(N);
break;
@ -180,26 +201,27 @@ gen(Node *n)
break;
case OLABEL:
newlab(OLABEL, n->left->sym, n->right);
newlab(OLABEL, n, n->right);
break;
case OGOTO:
newlab(OGOTO, n->left->sym, N);
newlab(OGOTO, n, N);
gjmp(P);
break;
case OBREAK:
if(n->left != N) {
for(lab=labellist; lab!=L; lab=lab->link) {
if(lab->sym == n->left->sym) {
if(lab->breakpc == P)
yyerror("invalid break label %S", n->left->sym);
gjmp(lab->breakpc);
goto donebreak;
}
}
if(lab == L)
lab = n->left->sym->label;
if(lab == L) {
yyerror("break label not defined: %S", n->left->sym);
break;
}
lab->used = 1;
if(lab->breakpc == P) {
yyerror("invalid break label %S", n->left->sym);
break;
}
gjmp(lab->breakpc);
break;
}
if(breakpc == P) {
@ -207,30 +229,28 @@ gen(Node *n)
break;
}
gjmp(breakpc);
donebreak:
break;
case OCONTINUE:
if(n->left != N) {
for(lab=labellist; lab!=L; lab=lab->link) {
if(lab->sym == n->left->sym) {
if(lab->continpc == P)
yyerror("invalid continue label %S", n->left->sym);
gjmp(lab->continpc);
goto donecont;
}
}
if(lab == L)
lab = n->left->sym->label;
if(lab == L) {
yyerror("continue label not defined: %S", n->left->sym);
break;
}
lab->used = 1;
if(lab->continpc == P) {
yyerror("invalid continue label %S", n->left->sym);
break;
}
gjmp(lab->continpc);
break;
}
if(continpc == P) {
yyerror("continue is not in a loop");
break;
}
gjmp(continpc);
donecont:
break;
case OFOR:
@ -241,10 +261,11 @@ gen(Node *n)
continpc = pc;
// define break and continue labels
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) {
if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n) {
lab->breakpc = breakpc;
lab->continpc = continpc;
}
} else
lab = L;
gen(n->nincr); // contin: incr
patch(p1, pc); // test:
@ -254,6 +275,10 @@ gen(Node *n)
patch(breakpc, pc); // done:
continpc = scontin;
breakpc = sbreak;
if(lab) {
lab->breakpc = P;
lab->continpc = P;
}
break;
case OIF:
@ -274,13 +299,17 @@ gen(Node *n)
breakpc = gjmp(P); // break: goto done
// define break label
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
lab->breakpc = breakpc;
else
lab = L;
patch(p1, pc); // test:
genlist(n->nbody); // switch(test) body
patch(breakpc, pc); // done:
breakpc = sbreak;
if(lab != L)
lab->breakpc = P;
break;
case OSELECT:
@ -289,13 +318,17 @@ gen(Node *n)
breakpc = gjmp(P); // break: goto done
// define break label
if((lab = labellist) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
if((lab = lastlabel) != L && lab->label == p3 && lab->op == OLABEL && lab->stmt == n)
lab->breakpc = breakpc;
else
lab = L;
patch(p1, pc); // test:
genlist(n->nbody); // select() body
patch(breakpc, pc); // done:
breakpc = sbreak;
if(lab != L)
lab->breakpc = P;
break;
case OASOP:

View File

@ -138,6 +138,7 @@ typedef struct Sym Sym;
typedef struct Node Node;
typedef struct NodeList NodeList;
typedef struct Type Type;
typedef struct Label Label;
struct Type
{
@ -302,11 +303,14 @@ struct Sym
Pkg* pkg;
char* name; // variable name
Node* def; // definition: ONAME OTYPE OPACK or OLITERAL
Label* label; // corresponding label (ephemeral)
int32 block; // blocknumber to catch redeclaration
int32 lastlineno; // last declaration for diagnostic
};
#define S ((Sym*)0)
EXTERN Sym* dclstack;
struct Pkg
{
char* name;
@ -619,20 +623,22 @@ struct Magic
typedef struct Prog Prog;
typedef struct Label Label;
struct Label
{
uchar op; // OGOTO/OLABEL
uchar used;
Sym* sym;
Node* stmt;
Prog* label; // pointer to code
Prog* breakpc; // pointer to code
Prog* continpc; // pointer to code
Label* link;
int32 lineno;
};
#define L ((Label*)0)
EXTERN Label* labellist;
EXTERN Label* lastlabel;
/*
* note this is the runtime representation
@ -900,6 +906,7 @@ void allocparams(void);
void cgen_as(Node *nl, Node *nr);
void cgen_callmeth(Node *n, int proc);
void checklabels(void);
void clearlabels(void);
int dotoffset(Node *n, int *oary, Node **nn);
void gen(Node *n);
void genlist(NodeList *l);

60
test/label.go Normal file
View File

@ -0,0 +1,60 @@
// errchk $G -e $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.
// Pass 1 label errors.
package main
var x int
func f() {
L1: // ERROR "label L1 defined and not used"
for {
}
L2: // ERROR "label L2 defined and not used"
select {
}
L3: // ERROR "label L3 defined and not used"
switch {
}
L4: // ERROR "label L4 defined and not used"
if true {
}
L5: // ERROR "label L5 defined and not used"
f()
L6:
f()
L6: // ERROR "label L6 already defined at"
f()
if x == 20 {
goto L6
}
L7:
for {
break L7
}
L8:
for {
if x == 21 {
continue L8
}
}
L9:
switch {
case true:
break L9
defalt: // ERROR "label defalt defined and not used"
}
L10:
select {
default:
break L10
}
}

85
test/label1.go Normal file
View File

@ -0,0 +1,85 @@
// errchk $G -e $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.
// Pass 2 label errors.
package main
var x int
func f() {
L1:
for {
if x == 0 {
break L1
}
if x == 1 {
continue L1
}
goto L1
}
L2:
select {
default:
if x == 0 {
break L2
}
if x == 1 {
continue L2 // ERROR "invalid continue label L2"
}
goto L2
}
L3:
switch {
case x > 10:
if x == 11 {
break L3
}
if x == 12 {
continue L3 // ERROR "invalid continue label L3"
}
goto L3
}
L4:
if true {
if x == 13 {
break L4 // ERROR "invalid break label L4"
}
if x == 14 {
continue L4 // ERROR "invalid continue label L4"
}
if x == 15 {
goto L4
}
}
L5:
f()
if x == 16 {
break L5 // ERROR "invalid break label L5"
}
if x == 17 {
continue L5 // ERROR "invalid continue label L5"
}
if x == 18 {
goto L5
}
for {
if x == 19 {
break L1 // ERROR "invalid break label L1"
}
if x == 20 {
continue L1 // ERROR "invalid continue label L1"
}
if x == 21 {
goto L1
}
}
}