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