Source file
src/go/types/labels.go
1
2
3
4
5 package types
6
7 import (
8 "go/ast"
9 "go/token"
10 . "internal/types/errors"
11 "slices"
12 )
13
14
15 func (check *Checker) labels(body *ast.BlockStmt) {
16
17 all := NewScope(nil, body.Pos(), body.End(), "label")
18
19 fwdJumps := check.blockBranches(all, nil, nil, body.List)
20
21
22
23
24
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
33 } else {
34 msg = "label %s not declared"
35 code = UndeclaredLabel
36 }
37 check.errorf(jmp.Label, code, msg, name)
38 }
39
40
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
50 type block struct {
51 parent *block
52 lstmt *ast.LabeledStmt
53 labels map[string]*ast.LabeledStmt
54 }
55
56
57
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
72
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
83
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
94
95
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
105
106
107 recordVarDecl := func(pos token.Pos) {
108 varDeclPos = pos
109 badJumps = append(badJumps[:0], fwdJumps...)
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
118
119 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
120 }
121
122 var stmtBranches func(ast.Stmt)
123 stmtBranches = func(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
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
141 } else {
142 b.insert(s)
143 check.recordDef(s.Label, lbl)
144 }
145
146 i := 0
147 for _, jmp := range fwdJumps {
148 if jmp.Label.Name == name {
149
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
161 }
162 } else {
163
164 fwdJumps[i] = jmp
165 i++
166 }
167 }
168 fwdJumps = fwdJumps[:i]
169 lstmt = s
170 }
171 stmtBranches(s.Stmt)
172
173 case *ast.BranchStmt:
174 if s.Label == nil {
175 return
176 }
177
178
179 name := s.Label.Name
180 switch s.Tok {
181 case token.BREAK:
182
183
184
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
199
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
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
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(s.Body)
239 if s.Else != nil {
240 stmtBranches(s.Else)
241 }
242
243 case *ast.CaseClause:
244 blockBranches(nil, s.Body)
245
246 case *ast.SwitchStmt:
247 stmtBranches(s.Body)
248
249 case *ast.TypeSwitchStmt:
250 stmtBranches(s.Body)
251
252 case *ast.CommClause:
253 blockBranches(nil, s.Body)
254
255 case *ast.SelectStmt:
256 stmtBranches(s.Body)
257
258 case *ast.ForStmt:
259 stmtBranches(s.Body)
260
261 case *ast.RangeStmt:
262 stmtBranches(s.Body)
263 }
264 }
265
266 for _, s := range list {
267 stmtBranches(s)
268 }
269
270 return fwdJumps
271 }
272
View as plain text