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