...

Source file src/pkg/go/types/labels.go

     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	}
   269	

View as plain text