github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/go/types/labels.go (about)

     1  // Copyright 2013 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types
     6  
     7  import (
     8  	"go/ast"
     9  	"go/token"
    10  )
    11  
    12  // labels checks correct label use in body.
    13  func (check *Checker) labels(body *ast.BlockStmt) {
    14  	// set of all labels in this body
    15  	all := NewScope(nil, body.Pos(), body.End(), "label")
    16  
    17  	fwdJumps := check.blockBranches(all, nil, nil, body.List)
    18  
    19  	// If there are any forward jumps left, no label was found for
    20  	// the corresponding goto statements. Either those labels were
    21  	// never defined, or they are inside blocks and not reachable
    22  	// for the respective gotos.
    23  	for _, jmp := range fwdJumps {
    24  		var msg string
    25  		name := jmp.Label.Name
    26  		if alt := all.Lookup(name); alt != nil {
    27  			msg = "goto %s jumps into block"
    28  			alt.(*Label).used = true // avoid another error
    29  		} else {
    30  			msg = "label %s not declared"
    31  		}
    32  		check.errorf(jmp.Label.Pos(), msg, name)
    33  	}
    34  
    35  	// spec: "It is illegal to define a label that is never used."
    36  	for _, obj := range all.elems {
    37  		if lbl := obj.(*Label); !lbl.used {
    38  			check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
    39  		}
    40  	}
    41  }
    42  
    43  // A block tracks label declarations in a block and its enclosing blocks.
    44  type block struct {
    45  	parent *block                      // enclosing block
    46  	lstmt  *ast.LabeledStmt            // labeled statement to which this block belongs, or nil
    47  	labels map[string]*ast.LabeledStmt // allocated lazily
    48  }
    49  
    50  // insert records a new label declaration for the current block.
    51  // The label must not have been declared before in any block.
    52  func (b *block) insert(s *ast.LabeledStmt) {
    53  	name := s.Label.Name
    54  	if debug {
    55  		assert(b.gotoTarget(name) == nil)
    56  	}
    57  	labels := b.labels
    58  	if labels == nil {
    59  		labels = make(map[string]*ast.LabeledStmt)
    60  		b.labels = labels
    61  	}
    62  	labels[name] = s
    63  }
    64  
    65  // gotoTarget returns the labeled statement in the current
    66  // or an enclosing block with the given label name, or nil.
    67  func (b *block) gotoTarget(name string) *ast.LabeledStmt {
    68  	for s := b; s != nil; s = s.parent {
    69  		if t := s.labels[name]; t != nil {
    70  			return t
    71  		}
    72  	}
    73  	return nil
    74  }
    75  
    76  // enclosingTarget returns the innermost enclosing labeled
    77  // statement with the given label name, or nil.
    78  func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
    79  	for s := b; s != nil; s = s.parent {
    80  		if t := s.lstmt; t != nil && t.Label.Name == name {
    81  			return t
    82  		}
    83  	}
    84  	return nil
    85  }
    86  
    87  // blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
    88  // all is the scope of all declared labels, parent the set of labels declared in the immediately
    89  // enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
    90  func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
    91  	b := &block{parent: parent, lstmt: lstmt}
    92  
    93  	var (
    94  		varDeclPos         token.Pos
    95  		fwdJumps, badJumps []*ast.BranchStmt
    96  	)
    97  
    98  	// All forward jumps jumping over a variable declaration are possibly
    99  	// invalid (they may still jump out of the block and be ok).
   100  	// recordVarDecl records them for the given position.
   101  	recordVarDecl := func(pos token.Pos) {
   102  		varDeclPos = pos
   103  		badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
   104  	}
   105  
   106  	jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
   107  		if varDeclPos.IsValid() {
   108  			for _, bad := range badJumps {
   109  				if jmp == bad {
   110  					return true
   111  				}
   112  			}
   113  		}
   114  		return false
   115  	}
   116  
   117  	blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
   118  		// Unresolved forward jumps inside the nested block
   119  		// become forward jumps in the current block.
   120  		fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
   121  	}
   122  
   123  	var stmtBranches func(ast.Stmt)
   124  	stmtBranches = func(s ast.Stmt) {
   125  		switch s := s.(type) {
   126  		case *ast.DeclStmt:
   127  			if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
   128  				recordVarDecl(d.Pos())
   129  			}
   130  
   131  		case *ast.LabeledStmt:
   132  			// declare non-blank label
   133  			if name := s.Label.Name; name != "_" {
   134  				lbl := NewLabel(s.Label.Pos(), check.pkg, name)
   135  				if alt := all.Insert(lbl); alt != nil {
   136  					check.softErrorf(lbl.pos, "label %s already declared", name)
   137  					check.reportAltDecl(alt)
   138  					// ok to continue
   139  				} else {
   140  					b.insert(s)
   141  					check.recordDef(s.Label, lbl)
   142  				}
   143  				// resolve matching forward jumps and remove them from fwdJumps
   144  				i := 0
   145  				for _, jmp := range fwdJumps {
   146  					if jmp.Label.Name == name {
   147  						// match
   148  						lbl.used = true
   149  						check.recordUse(jmp.Label, lbl)
   150  						if jumpsOverVarDecl(jmp) {
   151  							check.softErrorf(
   152  								jmp.Label.Pos(),
   153  								"goto %s jumps over variable declaration at line %d",
   154  								name,
   155  								check.fset.Position(varDeclPos).Line,
   156  							)
   157  							// ok to continue
   158  						}
   159  					} else {
   160  						// no match - record new forward jump
   161  						fwdJumps[i] = jmp
   162  						i++
   163  					}
   164  				}
   165  				fwdJumps = fwdJumps[:i]
   166  				lstmt = s
   167  			}
   168  			stmtBranches(s.Stmt)
   169  
   170  		case *ast.BranchStmt:
   171  			if s.Label == nil {
   172  				return // checked in 1st pass (check.stmt)
   173  			}
   174  
   175  			// determine and validate target
   176  			name := s.Label.Name
   177  			switch s.Tok {
   178  			case token.BREAK:
   179  				// spec: "If there is a label, it must be that of an enclosing
   180  				// "for", "switch", or "select" statement, and that is the one
   181  				// whose execution terminates."
   182  				valid := false
   183  				if t := b.enclosingTarget(name); t != nil {
   184  					switch t.Stmt.(type) {
   185  					case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
   186  						valid = true
   187  					}
   188  				}
   189  				if !valid {
   190  					check.errorf(s.Label.Pos(), "invalid break label %s", name)
   191  					return
   192  				}
   193  
   194  			case token.CONTINUE:
   195  				// spec: "If there is a label, it must be that of an enclosing
   196  				// "for" statement, and that is the one whose execution advances."
   197  				valid := false
   198  				if t := b.enclosingTarget(name); t != nil {
   199  					switch t.Stmt.(type) {
   200  					case *ast.ForStmt, *ast.RangeStmt:
   201  						valid = true
   202  					}
   203  				}
   204  				if !valid {
   205  					check.errorf(s.Label.Pos(), "invalid continue label %s", name)
   206  					return
   207  				}
   208  
   209  			case token.GOTO:
   210  				if b.gotoTarget(name) == nil {
   211  					// label may be declared later - add branch to forward jumps
   212  					fwdJumps = append(fwdJumps, s)
   213  					return
   214  				}
   215  
   216  			default:
   217  				check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
   218  				return
   219  			}
   220  
   221  			// record label use
   222  			obj := all.Lookup(name)
   223  			obj.(*Label).used = true
   224  			check.recordUse(s.Label, obj)
   225  
   226  		case *ast.AssignStmt:
   227  			if s.Tok == token.DEFINE {
   228  				recordVarDecl(s.Pos())
   229  			}
   230  
   231  		case *ast.BlockStmt:
   232  			blockBranches(lstmt, s.List)
   233  
   234  		case *ast.IfStmt:
   235  			stmtBranches(s.Body)
   236  			if s.Else != nil {
   237  				stmtBranches(s.Else)
   238  			}
   239  
   240  		case *ast.CaseClause:
   241  			blockBranches(nil, s.Body)
   242  
   243  		case *ast.SwitchStmt:
   244  			stmtBranches(s.Body)
   245  
   246  		case *ast.TypeSwitchStmt:
   247  			stmtBranches(s.Body)
   248  
   249  		case *ast.CommClause:
   250  			blockBranches(nil, s.Body)
   251  
   252  		case *ast.SelectStmt:
   253  			stmtBranches(s.Body)
   254  
   255  		case *ast.ForStmt:
   256  			stmtBranches(s.Body)
   257  
   258  		case *ast.RangeStmt:
   259  			stmtBranches(s.Body)
   260  		}
   261  	}
   262  
   263  	for _, s := range list {
   264  		stmtBranches(s)
   265  	}
   266  
   267  	return fwdJumps
   268  }