]> Cypherpunks repositories - gostls13.git/commitdiff
gc: diagnose unused labels
authorRuss Cox <rsc@golang.org>
Tue, 15 Mar 2011 18:05:37 +0000 (14:05 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 15 Mar 2011 18:05:37 +0000 (14:05 -0400)
R=ken2
CC=golang-dev
https://golang.org/cl/4287047

src/cmd/5g/ggen.c
src/cmd/6g/ggen.c
src/cmd/8g/ggen.c
src/cmd/gc/dcl.c
src/cmd/gc/gen.c
src/cmd/gc/go.h
test/label.go [new file with mode: 0644]
test/label1.go [new file with mode: 0644]

index 182d7f147e16857efc370ff59a55c3ca816df40f..7197709d4dc7ac78788abb7ab71979324abd1545 100644 (file)
@@ -32,7 +32,7 @@ compile(Node *fn)
                return;
 
        // set up domain for labels
-       labellist = L;
+       clearlabels();
 
        lno = setlineno(fn);
 
index d9fa1793c6a72cd8109dc82f44d0f94c90c8a698..8d89fb164e0b707a41a1a6c4d90414d6ca8a3b64 100644 (file)
@@ -32,7 +32,7 @@ compile(Node *fn)
                return;
 
        // set up domain for labels
-       labellist = L;
+       clearlabels();
 
        lno = setlineno(fn);
 
index 4dcbd44895a658fbab111f69de331e57bb442ef8..8db55249346ec7c5094685b1a386ae42d5fb543d 100644 (file)
@@ -32,7 +32,7 @@ compile(Node *fn)
                return;
 
        // set up domain for labels
-       labellist = L;
+       clearlabels();
 
        lno = setlineno(fn);
 
index cbcdcbf8c706729e01e11d6f6f14564eba89b730..3089a23b0dbe468d7dce167b1ada235a2e6c9cdb 100644 (file)
@@ -22,7 +22,6 @@ dflag(void)
 /*
  * declaration stack & operations
  */
-static Sym*    dclstack;
 
 static void
 dcopy(Sym *a, Sym *b)
index 04af5a7bb149372d3bbc79a0eb6c85360dcdc5b7..8ad6c437de99122540a1902450e89ce92ff56edb 100644 (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:
index b071eb227336f131a192ef52dab49402b5f89710..39c316f2e53c1cbee143a244a042b9031d83946e 100644 (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);
diff --git a/test/label.go b/test/label.go
new file mode 100644 (file)
index 0000000..ab23123
--- /dev/null
@@ -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
+       }
+}
diff --git a/test/label1.go b/test/label1.go
new file mode 100644 (file)
index 0000000..bba63f2
--- /dev/null
@@ -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
+               }
+       }
+}