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