github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/src/go/types/return.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // This file implements isTerminating. 6 7 package types 8 9 import ( 10 "go/ast" 11 "go/token" 12 ) 13 14 // isTerminating reports if s is a terminating statement. 15 // If s is labeled, label is the label name; otherwise s 16 // is "". 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 // no chance 26 27 case *ast.LabeledStmt: 28 return check.isTerminating(s.Stmt, s.Label.Name) 29 30 case *ast.ExprStmt: 31 // the predeclared (possibly parenthesized) panic() function is terminating 32 if call, _ := unparen(s.X).(*ast.CallExpr); call != nil { 33 if id, _ := call.Fun.(*ast.Ident); id != nil { 34 if _, obj := check.scope.LookupParent(id.Name, token.NoPos); obj != nil { 35 if b, _ := obj.(*Builtin); b != nil && b.id == _Panic { 36 return true 37 } 38 } 39 } 40 } 41 42 case *ast.ReturnStmt: 43 return true 44 45 case *ast.BranchStmt: 46 if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH { 47 return true 48 } 49 50 case *ast.BlockStmt: 51 return check.isTerminatingList(s.List, "") 52 53 case *ast.IfStmt: 54 if s.Else != nil && 55 check.isTerminating(s.Body, "") && 56 check.isTerminating(s.Else, "") { 57 return true 58 } 59 60 case *ast.SwitchStmt: 61 return check.isTerminatingSwitch(s.Body, label) 62 63 case *ast.TypeSwitchStmt: 64 return check.isTerminatingSwitch(s.Body, label) 65 66 case *ast.SelectStmt: 67 for _, s := range s.Body.List { 68 cc := s.(*ast.CommClause) 69 if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { 70 return false 71 } 72 73 } 74 return true 75 76 case *ast.ForStmt: 77 if s.Cond == nil && !hasBreak(s.Body, label, true) { 78 return true 79 } 80 } 81 82 return false 83 } 84 85 func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool { 86 // trailing empty statements are permitted - skip them 87 for i := len(list) - 1; i >= 0; i-- { 88 if _, ok := list[i].(*ast.EmptyStmt); !ok { 89 return check.isTerminating(list[i], label) 90 } 91 } 92 return false // all statements are empty 93 } 94 95 func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool { 96 hasDefault := false 97 for _, s := range body.List { 98 cc := s.(*ast.CaseClause) 99 if cc.List == nil { 100 hasDefault = true 101 } 102 if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { 103 return false 104 } 105 } 106 return hasDefault 107 } 108 109 // TODO(gri) For nested breakable statements, the current implementation of hasBreak 110 // will traverse the same subtree repeatedly, once for each label. Replace 111 // with a single-pass label/break matching phase. 112 113 // hasBreak reports if s is or contains a break statement 114 // referring to the label-ed statement or implicit-ly the 115 // closest outer breakable statement. 116 func hasBreak(s ast.Stmt, label string, implicit bool) bool { 117 switch s := s.(type) { 118 default: 119 unreachable() 120 121 case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt, 122 *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, 123 *ast.DeferStmt, *ast.ReturnStmt: 124 // no chance 125 126 case *ast.LabeledStmt: 127 return hasBreak(s.Stmt, label, implicit) 128 129 case *ast.BranchStmt: 130 if s.Tok == token.BREAK { 131 if s.Label == nil { 132 return implicit 133 } 134 if s.Label.Name == label { 135 return true 136 } 137 } 138 139 case *ast.BlockStmt: 140 return hasBreakList(s.List, label, implicit) 141 142 case *ast.IfStmt: 143 if hasBreak(s.Body, label, implicit) || 144 s.Else != nil && hasBreak(s.Else, label, implicit) { 145 return true 146 } 147 148 case *ast.CaseClause: 149 return hasBreakList(s.Body, label, implicit) 150 151 case *ast.SwitchStmt: 152 if label != "" && hasBreak(s.Body, label, false) { 153 return true 154 } 155 156 case *ast.TypeSwitchStmt: 157 if label != "" && hasBreak(s.Body, label, false) { 158 return true 159 } 160 161 case *ast.CommClause: 162 return hasBreakList(s.Body, label, implicit) 163 164 case *ast.SelectStmt: 165 if label != "" && hasBreak(s.Body, label, false) { 166 return true 167 } 168 169 case *ast.ForStmt: 170 if label != "" && hasBreak(s.Body, label, false) { 171 return true 172 } 173 174 case *ast.RangeStmt: 175 if label != "" && hasBreak(s.Body, label, false) { 176 return true 177 } 178 } 179 180 return false 181 } 182 183 func hasBreakList(list []ast.Stmt, label string, implicit bool) bool { 184 for _, s := range list { 185 if hasBreak(s, label, implicit) { 186 return true 187 } 188 } 189 return false 190 }