github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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 n := len(list) 87 return n > 0 && check.isTerminating(list[n-1], label) 88 } 89 90 func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool { 91 hasDefault := false 92 for _, s := range body.List { 93 cc := s.(*ast.CaseClause) 94 if cc.List == nil { 95 hasDefault = true 96 } 97 if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) { 98 return false 99 } 100 } 101 return hasDefault 102 } 103 104 // TODO(gri) For nested breakable statements, the current implementation of hasBreak 105 // will traverse the same subtree repeatedly, once for each label. Replace 106 // with a single-pass label/break matching phase. 107 108 // hasBreak reports if s is or contains a break statement 109 // referring to the label-ed statement or implicit-ly the 110 // closest outer breakable statement. 111 func hasBreak(s ast.Stmt, label string, implicit bool) bool { 112 switch s := s.(type) { 113 default: 114 unreachable() 115 116 case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt, 117 *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, 118 *ast.DeferStmt, *ast.ReturnStmt: 119 // no chance 120 121 case *ast.LabeledStmt: 122 return hasBreak(s.Stmt, label, implicit) 123 124 case *ast.BranchStmt: 125 if s.Tok == token.BREAK { 126 if s.Label == nil { 127 return implicit 128 } 129 if s.Label.Name == label { 130 return true 131 } 132 } 133 134 case *ast.BlockStmt: 135 return hasBreakList(s.List, label, implicit) 136 137 case *ast.IfStmt: 138 if hasBreak(s.Body, label, implicit) || 139 s.Else != nil && hasBreak(s.Else, label, implicit) { 140 return true 141 } 142 143 case *ast.CaseClause: 144 return hasBreakList(s.Body, label, implicit) 145 146 case *ast.SwitchStmt: 147 if label != "" && hasBreak(s.Body, label, false) { 148 return true 149 } 150 151 case *ast.TypeSwitchStmt: 152 if label != "" && hasBreak(s.Body, label, false) { 153 return true 154 } 155 156 case *ast.CommClause: 157 return hasBreakList(s.Body, label, implicit) 158 159 case *ast.SelectStmt: 160 if label != "" && hasBreak(s.Body, label, false) { 161 return true 162 } 163 164 case *ast.ForStmt: 165 if label != "" && hasBreak(s.Body, label, false) { 166 return true 167 } 168 169 case *ast.RangeStmt: 170 if label != "" && hasBreak(s.Body, label, false) { 171 return true 172 } 173 } 174 175 return false 176 } 177 178 func hasBreakList(list []ast.Stmt, label string, implicit bool) bool { 179 for _, s := range list { 180 if hasBreak(s, label, implicit) { 181 return true 182 } 183 } 184 return false 185 }