Source file
src/go/types/return.go
1
2
3
4
5
6
7 package types
8
9 import (
10 "go/ast"
11 "go/token"
12 )
13
14
15
16
17 func (check *Checker) isTerminating(s ast.Stmt, label string) bool {
18 switch s := s.(type) {
19 default:
20 unreachable()
21
22 case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
23 *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
24 *ast.RangeStmt:
25
26
27 case *ast.LabeledStmt:
28 return check.isTerminating(s.Stmt, s.Label.Name)
29
30 case *ast.ExprStmt:
31
32 if call, ok := unparen(s.X).(*ast.CallExpr); ok && check.isPanic[call] {
33 return true
34 }
35
36 case *ast.ReturnStmt:
37 return true
38
39 case *ast.BranchStmt:
40 if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH {
41 return true
42 }
43
44 case *ast.BlockStmt:
45 return check.isTerminatingList(s.List, "")
46
47 case *ast.IfStmt:
48 if s.Else != nil &&
49 check.isTerminating(s.Body, "") &&
50 check.isTerminating(s.Else, "") {
51 return true
52 }
53
54 case *ast.SwitchStmt:
55 return check.isTerminatingSwitch(s.Body, label)
56
57 case *ast.TypeSwitchStmt:
58 return check.isTerminatingSwitch(s.Body, label)
59
60 case *ast.SelectStmt:
61 for _, s := range s.Body.List {
62 cc := s.(*ast.CommClause)
63 if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
64 return false
65 }
66
67 }
68 return true
69
70 case *ast.ForStmt:
71 if s.Cond == nil && !hasBreak(s.Body, label, true) {
72 return true
73 }
74 }
75
76 return false
77 }
78
79 func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool {
80
81 for i := len(list) - 1; i >= 0; i-- {
82 if _, ok := list[i].(*ast.EmptyStmt); !ok {
83 return check.isTerminating(list[i], label)
84 }
85 }
86 return false
87 }
88
89 func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
90 hasDefault := false
91 for _, s := range body.List {
92 cc := s.(*ast.CaseClause)
93 if cc.List == nil {
94 hasDefault = true
95 }
96 if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
97 return false
98 }
99 }
100 return hasDefault
101 }
102
103
104
105
106
107
108
109
110 func hasBreak(s ast.Stmt, label string, implicit bool) bool {
111 switch s := s.(type) {
112 default:
113 unreachable()
114
115 case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt,
116 *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt,
117 *ast.DeferStmt, *ast.ReturnStmt:
118
119
120 case *ast.LabeledStmt:
121 return hasBreak(s.Stmt, label, implicit)
122
123 case *ast.BranchStmt:
124 if s.Tok == token.BREAK {
125 if s.Label == nil {
126 return implicit
127 }
128 if s.Label.Name == label {
129 return true
130 }
131 }
132
133 case *ast.BlockStmt:
134 return hasBreakList(s.List, label, implicit)
135
136 case *ast.IfStmt:
137 if hasBreak(s.Body, label, implicit) ||
138 s.Else != nil && hasBreak(s.Else, label, implicit) {
139 return true
140 }
141
142 case *ast.CaseClause:
143 return hasBreakList(s.Body, label, implicit)
144
145 case *ast.SwitchStmt:
146 if label != "" && hasBreak(s.Body, label, false) {
147 return true
148 }
149
150 case *ast.TypeSwitchStmt:
151 if label != "" && hasBreak(s.Body, label, false) {
152 return true
153 }
154
155 case *ast.CommClause:
156 return hasBreakList(s.Body, label, implicit)
157
158 case *ast.SelectStmt:
159 if label != "" && hasBreak(s.Body, label, false) {
160 return true
161 }
162
163 case *ast.ForStmt:
164 if label != "" && hasBreak(s.Body, label, false) {
165 return true
166 }
167
168 case *ast.RangeStmt:
169 if label != "" && hasBreak(s.Body, label, false) {
170 return true
171 }
172 }
173
174 return false
175 }
176
177 func hasBreakList(list []ast.Stmt, label string, implicit bool) bool {
178 for _, s := range list {
179 if hasBreak(s, label, implicit) {
180 return true
181 }
182 }
183 return false
184 }
185
View as plain text