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 )
12
13
14 func (check *Checker) labels(body *ast.BlockStmt) {
15
16 all := NewScope(nil, body.Pos(), body.End(), "label")
17
18 fwdJumps := check.blockBranches(all, nil, nil, body.List)
19
20
21
22
23
24 for _, jmp := range fwdJumps {
25 var msg string
26 var code Code
27 name := jmp.Label.Name
28 if alt := all.Lookup(name); alt != nil {
29 msg = "goto %s jumps into block"
30 alt.(*Label).used = true
31 code = JumpIntoBlock
32
33 } else {
34 msg = "label %s not declared"
35 code = UndeclaredLabel
36 name = quote(name)
37 }
38 check.errorf(jmp.Label, code, msg, name)
39 }
40
41
42 for name, obj := range all.elems {
43 obj = resolve(name, obj)
44 if lbl := obj.(*Label); !lbl.used {
45 check.softErrorf(lbl, UnusedLabel, "label %s declared and not used", quote(lbl.name))
46 }
47 }
48 }
49
50
51 type block struct {
52 parent *block
53 lstmt *ast.LabeledStmt
54 labels map[string]*ast.LabeledStmt
55 }
56
57
58
59 func (b *block) insert(s *ast.LabeledStmt) {
60 name := s.Label.Name
61 if debug {
62 assert(b.gotoTarget(name) == nil)
63 }
64 labels := b.labels
65 if labels == nil {
66 labels = make(map[string]*ast.LabeledStmt)
67 b.labels = labels
68 }
69 labels[name] = s
70 }
71
72
73
74 func (b *block) gotoTarget(name string) *ast.LabeledStmt {
75 for s := b; s != nil; s = s.parent {
76 if t := s.labels[name]; t != nil {
77 return t
78 }
79 }
80 return nil
81 }
82
83
84
85 func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
86 for s := b; s != nil; s = s.parent {
87 if t := s.lstmt; t != nil && t.Label.Name == name {
88 return t
89 }
90 }
91 return nil
92 }
93
94
95
96
97 func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
98 b := &block{parent: parent, lstmt: lstmt}
99
100 var (
101 varDeclPos token.Pos
102 fwdJumps, badJumps []*ast.BranchStmt
103 )
104
105
106
107
108 recordVarDecl := func(pos token.Pos) {
109 varDeclPos = pos
110 badJumps = append(badJumps[:0], fwdJumps...)
111 }
112
113 jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
114 if varDeclPos.IsValid() {
115 for _, bad := range badJumps {
116 if jmp == bad {
117 return true
118 }
119 }
120 }
121 return false
122 }
123
124 blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
125
126
127 fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
128 }
129
130 var stmtBranches func(ast.Stmt)
131 stmtBranches = func(s ast.Stmt) {
132 switch s := s.(type) {
133 case *ast.DeclStmt:
134 if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
135 recordVarDecl(d.Pos())
136 }
137
138 case *ast.LabeledStmt:
139
140 if name := s.Label.Name; name != "_" {
141 lbl := NewLabel(s.Label.Pos(), check.pkg, name)
142 if alt := all.Insert(lbl); alt != nil {
143 err := check.newError(DuplicateLabel)
144 err.soft = true
145 err.addf(lbl, "label %s already declared", quote(name))
146 err.addAltDecl(alt)
147 err.report()
148
149 } else {
150 b.insert(s)
151 check.recordDef(s.Label, lbl)
152 }
153
154 i := 0
155 for _, jmp := range fwdJumps {
156 if jmp.Label.Name == name {
157
158 lbl.used = true
159 check.recordUse(jmp.Label, lbl)
160 if jumpsOverVarDecl(jmp) {
161 check.softErrorf(
162 jmp.Label,
163 JumpOverDecl,
164 "goto %s jumps over variable declaration at line %d",
165 name,
166 check.fset.Position(varDeclPos).Line,
167 )
168
169 }
170 } else {
171
172 fwdJumps[i] = jmp
173 i++
174 }
175 }
176 fwdJumps = fwdJumps[:i]
177 lstmt = s
178 }
179 stmtBranches(s.Stmt)
180
181 case *ast.BranchStmt:
182 if s.Label == nil {
183 return
184 }
185
186
187 name := s.Label.Name
188 switch s.Tok {
189 case token.BREAK:
190
191
192
193 valid := false
194 if t := b.enclosingTarget(name); t != nil {
195 switch t.Stmt.(type) {
196 case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
197 valid = true
198 }
199 }
200 if !valid {
201 check.errorf(s.Label, MisplacedLabel, "invalid break label %s", quote(name))
202 return
203 }
204
205 case token.CONTINUE:
206
207
208 valid := false
209 if t := b.enclosingTarget(name); t != nil {
210 switch t.Stmt.(type) {
211 case *ast.ForStmt, *ast.RangeStmt:
212 valid = true
213 }
214 }
215 if !valid {
216 check.errorf(s.Label, MisplacedLabel, "invalid continue label %s", quote(name))
217 return
218 }
219
220 case token.GOTO:
221 if b.gotoTarget(name) == nil {
222
223 fwdJumps = append(fwdJumps, s)
224 return
225 }
226
227 default:
228 check.errorf(s, InvalidSyntaxTree, "branch statement: %s %s", s.Tok, name)
229 return
230 }
231
232
233 obj := all.Lookup(name)
234 obj.(*Label).used = true
235 check.recordUse(s.Label, obj)
236
237 case *ast.AssignStmt:
238 if s.Tok == token.DEFINE {
239 recordVarDecl(s.Pos())
240 }
241
242 case *ast.BlockStmt:
243 blockBranches(lstmt, s.List)
244
245 case *ast.IfStmt:
246 stmtBranches(s.Body)
247 if s.Else != nil {
248 stmtBranches(s.Else)
249 }
250
251 case *ast.CaseClause:
252 blockBranches(nil, s.Body)
253
254 case *ast.SwitchStmt:
255 stmtBranches(s.Body)
256
257 case *ast.TypeSwitchStmt:
258 stmtBranches(s.Body)
259
260 case *ast.CommClause:
261 blockBranches(nil, s.Body)
262
263 case *ast.SelectStmt:
264 stmtBranches(s.Body)
265
266 case *ast.ForStmt:
267 stmtBranches(s.Body)
268
269 case *ast.RangeStmt:
270 stmtBranches(s.Body)
271 }
272 }
273
274 for _, s := range list {
275 stmtBranches(s)
276 }
277
278 return fwdJumps
279 }
280
View as plain text