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 5package types 6 7import ( 8 "go/ast" 9 "go/token" 10) 11 12// labels checks correct label use in body. 13func (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. 47type 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. 55func (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. 70func (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. 81func (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). 93func (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