...

Source file src/go/types/labels.go

Documentation: go/types

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

View as plain text