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 7package types 8 9import ( 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 "". 17func (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 // calling the predeclared (possibly parenthesized) panic() function is terminating 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 79func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool { 80 // trailing empty statements are permitted - skip them 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 // all statements are empty 87} 88 89func (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// TODO(gri) For nested breakable statements, the current implementation of hasBreak 104// will traverse the same subtree repeatedly, once for each label. Replace 105// with a single-pass label/break matching phase. 106 107// hasBreak reports if s is or contains a break statement 108// referring to the label-ed statement or implicit-ly the 109// closest outer breakable statement. 110func 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 // no chance 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 177func 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