1
2
3
4
5 package syntax
6
7 import "fmt"
8
9
10
11
12
13
14
15
16 func checkBranches(body *BlockStmt, errh ErrorHandler) {
17 if body == nil {
18 return
19 }
20
21
22 ls := &labelScope{errh: errh}
23 fwdGotos := ls.blockBranches(nil, targets{}, nil, body.Pos(), body.List)
24
25
26
27
28 for _, fwd := range fwdGotos {
29 name := fwd.Label.Value
30 if l := ls.labels[name]; l != nil {
31 l.used = true
32 ls.err(fwd.Label.Pos(), "goto %s jumps into block starting at %s", name, l.parent.start)
33 } else {
34 ls.err(fwd.Label.Pos(), "label %s not defined", name)
35 }
36 }
37
38
39 for _, l := range ls.labels {
40 if !l.used {
41 l := l.lstmt.Label
42 ls.err(l.Pos(), "label %s defined and not used", l.Value)
43 }
44 }
45 }
46
47 type labelScope struct {
48 errh ErrorHandler
49 labels map[string]*label
50 }
51
52 type label struct {
53 parent *block
54 lstmt *LabeledStmt
55 used bool
56 }
57
58 type block struct {
59 parent *block
60 start Pos
61 lstmt *LabeledStmt
62 }
63
64 func (ls *labelScope) err(pos Pos, format string, args ...interface{}) {
65 ls.errh(Error{pos, fmt.Sprintf(format, args...)})
66 }
67
68
69
70
71 func (ls *labelScope) declare(b *block, s *LabeledStmt) *label {
72 name := s.Label.Value
73 labels := ls.labels
74 if labels == nil {
75 labels = make(map[string]*label)
76 ls.labels = labels
77 } else if alt := labels[name]; alt != nil {
78 ls.err(s.Label.Pos(), "label %s already defined at %s", name, alt.lstmt.Label.Pos().String())
79 return alt
80 }
81 l := &label{b, s, false}
82 labels[name] = l
83 return l
84 }
85
86
87
88
89 func (ls *labelScope) gotoTarget(b *block, name string) *LabeledStmt {
90 if l := ls.labels[name]; l != nil {
91 l.used = true
92 for ; b != nil; b = b.parent {
93 if l.parent == b {
94 return l.lstmt
95 }
96 }
97 }
98 return nil
99 }
100
101 var invalid = new(LabeledStmt)
102
103
104
105
106 func (ls *labelScope) enclosingTarget(b *block, name string) *LabeledStmt {
107 if l := ls.labels[name]; l != nil {
108 l.used = true
109 for ; b != nil; b = b.parent {
110 if l.lstmt == b.lstmt {
111 return l.lstmt
112 }
113 }
114 return invalid
115 }
116 return nil
117 }
118
119
120
121 type targets struct {
122 breaks Stmt
123 continues *ForStmt
124 caseIndex int
125 }
126
127
128
129
130
131 func (ls *labelScope) blockBranches(parent *block, ctxt targets, lstmt *LabeledStmt, start Pos, body []Stmt) []*BranchStmt {
132 b := &block{parent: parent, start: start, lstmt: lstmt}
133
134 var varPos Pos
135 var varName Expr
136 var fwdGotos, badGotos []*BranchStmt
137
138 recordVarDecl := func(pos Pos, name Expr) {
139 varPos = pos
140 varName = name
141
142
143
144
145 badGotos = append(badGotos[:0], fwdGotos...)
146 }
147
148 jumpsOverVarDecl := func(fwd *BranchStmt) bool {
149 if varPos.IsKnown() {
150 for _, bad := range badGotos {
151 if fwd == bad {
152 return true
153 }
154 }
155 }
156 return false
157 }
158
159 innerBlock := func(ctxt targets, start Pos, body []Stmt) {
160
161
162 fwdGotos = append(fwdGotos, ls.blockBranches(b, ctxt, lstmt, start, body)...)
163 }
164
165
166
167 stmtList := trimTrailingEmptyStmts(body)
168 for stmtIndex, stmt := range stmtList {
169 lstmt = nil
170 L:
171 switch s := stmt.(type) {
172 case *DeclStmt:
173 for _, d := range s.DeclList {
174 if v, ok := d.(*VarDecl); ok {
175 recordVarDecl(v.Pos(), v.NameList[0])
176 break
177 }
178 }
179
180 case *LabeledStmt:
181
182 if name := s.Label.Value; name != "_" {
183 l := ls.declare(b, s)
184
185 i := 0
186 for _, fwd := range fwdGotos {
187 if fwd.Label.Value == name {
188 fwd.Target = s
189 l.used = true
190 if jumpsOverVarDecl(fwd) {
191 ls.err(
192 fwd.Label.Pos(),
193 "goto %s jumps over declaration of %s at %s",
194 name, String(varName), varPos,
195 )
196 }
197 } else {
198
199 fwdGotos[i] = fwd
200 i++
201 }
202 }
203 fwdGotos = fwdGotos[:i]
204 lstmt = s
205 }
206
207 stmt = s.Stmt
208 goto L
209
210 case *BranchStmt:
211
212 if s.Label == nil {
213 switch s.Tok {
214 case _Break:
215 if t := ctxt.breaks; t != nil {
216 s.Target = t
217 } else {
218 ls.err(s.Pos(), "break is not in a loop, switch, or select")
219 }
220 case _Continue:
221 if t := ctxt.continues; t != nil {
222 s.Target = t
223 } else {
224 ls.err(s.Pos(), "continue is not in a loop")
225 }
226 case _Fallthrough:
227 msg := "fallthrough statement out of place"
228 if t, _ := ctxt.breaks.(*SwitchStmt); t != nil {
229 if _, ok := t.Tag.(*TypeSwitchGuard); ok {
230 msg = "cannot fallthrough in type switch"
231 } else if ctxt.caseIndex < 0 || stmtIndex+1 < len(stmtList) {
232
233
234 } else if ctxt.caseIndex+1 == len(t.Body) {
235 msg = "cannot fallthrough final case in switch"
236 } else {
237 break
238 }
239 }
240 ls.err(s.Pos(), msg)
241 case _Goto:
242 fallthrough
243 default:
244 panic("invalid BranchStmt")
245 }
246 break
247 }
248
249
250 name := s.Label.Value
251 switch s.Tok {
252 case _Break:
253
254
255
256 if t := ls.enclosingTarget(b, name); t != nil {
257 switch t := t.Stmt.(type) {
258 case *SwitchStmt, *SelectStmt, *ForStmt:
259 s.Target = t
260 default:
261 ls.err(s.Label.Pos(), "invalid break label %s", name)
262 }
263 } else {
264 ls.err(s.Label.Pos(), "break label not defined: %s", name)
265 }
266
267 case _Continue:
268
269
270 if t := ls.enclosingTarget(b, name); t != nil {
271 if t, ok := t.Stmt.(*ForStmt); ok {
272 s.Target = t
273 } else {
274 ls.err(s.Label.Pos(), "invalid continue label %s", name)
275 }
276 } else {
277 ls.err(s.Label.Pos(), "continue label not defined: %s", name)
278 }
279
280 case _Goto:
281 if t := ls.gotoTarget(b, name); t != nil {
282 s.Target = t
283 } else {
284
285 fwdGotos = append(fwdGotos, s)
286 }
287
288 case _Fallthrough:
289 fallthrough
290 default:
291 panic("invalid BranchStmt")
292 }
293
294 case *AssignStmt:
295 if s.Op == Def {
296 recordVarDecl(s.Pos(), s.Lhs)
297 }
298
299 case *BlockStmt:
300 inner := targets{ctxt.breaks, ctxt.continues, -1}
301 innerBlock(inner, s.Pos(), s.List)
302
303 case *IfStmt:
304 inner := targets{ctxt.breaks, ctxt.continues, -1}
305 innerBlock(inner, s.Then.Pos(), s.Then.List)
306 if s.Else != nil {
307 innerBlock(inner, s.Else.Pos(), []Stmt{s.Else})
308 }
309
310 case *ForStmt:
311 inner := targets{s, s, -1}
312 innerBlock(inner, s.Body.Pos(), s.Body.List)
313
314 case *SwitchStmt:
315 inner := targets{s, ctxt.continues, -1}
316 for i, cc := range s.Body {
317 inner.caseIndex = i
318 innerBlock(inner, cc.Pos(), cc.Body)
319 }
320
321 case *SelectStmt:
322 inner := targets{s, ctxt.continues, -1}
323 for _, cc := range s.Body {
324 innerBlock(inner, cc.Pos(), cc.Body)
325 }
326 }
327 }
328
329 return fwdGotos
330 }
331
332 func trimTrailingEmptyStmts(list []Stmt) []Stmt {
333 for i := len(list); i > 0; i-- {
334 if _, ok := list[i-1].(*EmptyStmt); !ok {
335 return list[:i]
336 }
337 }
338 return nil
339 }
340
View as plain text