package types
import (
"go/ast"
"go/token"
)
func (check *Checker ) isTerminating (s ast .Stmt , label string ) bool {
switch s := s .(type ) {
default :
unreachable ()
case *ast .BadStmt , *ast .DeclStmt , *ast .EmptyStmt , *ast .SendStmt ,
*ast .IncDecStmt , *ast .AssignStmt , *ast .GoStmt , *ast .DeferStmt ,
*ast .RangeStmt :
case *ast .LabeledStmt :
return check .isTerminating (s .Stmt , s .Label .Name )
case *ast .ExprStmt :
if call , ok := unparen (s .X ).(*ast .CallExpr ); ok && check .isPanic [call ] {
return true
}
case *ast .ReturnStmt :
return true
case *ast .BranchStmt :
if s .Tok == token .GOTO || s .Tok == token .FALLTHROUGH {
return true
}
case *ast .BlockStmt :
return check .isTerminatingList (s .List , "" )
case *ast .IfStmt :
if s .Else != nil &&
check .isTerminating (s .Body , "" ) &&
check .isTerminating (s .Else , "" ) {
return true
}
case *ast .SwitchStmt :
return check .isTerminatingSwitch (s .Body , label )
case *ast .TypeSwitchStmt :
return check .isTerminatingSwitch (s .Body , label )
case *ast .SelectStmt :
for _ , s := range s .Body .List {
cc := s .(*ast .CommClause )
if !check .isTerminatingList (cc .Body , "" ) || hasBreakList (cc .Body , label , true ) {
return false
}
}
return true
case *ast .ForStmt :
if s .Cond == nil && !hasBreak (s .Body , label , true ) {
return true
}
}
return false
}
func (check *Checker ) isTerminatingList (list []ast .Stmt , label string ) bool {
for i := len (list ) - 1 ; i >= 0 ; i -- {
if _ , ok := list [i ].(*ast .EmptyStmt ); !ok {
return check .isTerminating (list [i ], label )
}
}
return false
}
func (check *Checker ) isTerminatingSwitch (body *ast .BlockStmt , label string ) bool {
hasDefault := false
for _ , s := range body .List {
cc := s .(*ast .CaseClause )
if cc .List == nil {
hasDefault = true
}
if !check .isTerminatingList (cc .Body , "" ) || hasBreakList (cc .Body , label , true ) {
return false
}
}
return hasDefault
}
func hasBreak(s ast .Stmt , label string , implicit bool ) bool {
switch s := s .(type ) {
default :
unreachable ()
case *ast .BadStmt , *ast .DeclStmt , *ast .EmptyStmt , *ast .ExprStmt ,
*ast .SendStmt , *ast .IncDecStmt , *ast .AssignStmt , *ast .GoStmt ,
*ast .DeferStmt , *ast .ReturnStmt :
case *ast .LabeledStmt :
return hasBreak (s .Stmt , label , implicit )
case *ast .BranchStmt :
if s .Tok == token .BREAK {
if s .Label == nil {
return implicit
}
if s .Label .Name == label {
return true
}
}
case *ast .BlockStmt :
return hasBreakList (s .List , label , implicit )
case *ast .IfStmt :
if hasBreak (s .Body , label , implicit ) ||
s .Else != nil && hasBreak (s .Else , label , implicit ) {
return true
}
case *ast .CaseClause :
return hasBreakList (s .Body , label , implicit )
case *ast .SwitchStmt :
if label != "" && hasBreak (s .Body , label , false ) {
return true
}
case *ast .TypeSwitchStmt :
if label != "" && hasBreak (s .Body , label , false ) {
return true
}
case *ast .CommClause :
return hasBreakList (s .Body , label , implicit )
case *ast .SelectStmt :
if label != "" && hasBreak (s .Body , label , false ) {
return true
}
case *ast .ForStmt :
if label != "" && hasBreak (s .Body , label , false ) {
return true
}
case *ast .RangeStmt :
if label != "" && hasBreak (s .Body , label , false ) {
return true
}
}
return false
}
func hasBreakList(list []ast .Stmt , label string , implicit bool ) bool {
for _ , s := range list {
if hasBreak (s , label , implicit ) {
return true
}
}
return false
}
The pages are generated with Golds v0.6.9-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .