// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file implements isTerminating.

package types

import (
	
	
)

// isTerminating reports if s is a terminating statement.
// If s is labeled, label is the label name; otherwise s
// is "".
func ( *Checker) ( ast.Stmt,  string) bool {
	switch s := .(type) {
	default:
		unreachable()

	case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
		*ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
		*ast.RangeStmt:
		// no chance

	case *ast.LabeledStmt:
		return .(.Stmt, .Label.Name)

	case *ast.ExprStmt:
		// calling the predeclared (possibly parenthesized) panic() function is terminating
		if ,  := unparen(.X).(*ast.CallExpr);  && .isPanic[] {
			return true
		}

	case *ast.ReturnStmt:
		return true

	case *ast.BranchStmt:
		if .Tok == token.GOTO || .Tok == token.FALLTHROUGH {
			return true
		}

	case *ast.BlockStmt:
		return .isTerminatingList(.List, "")

	case *ast.IfStmt:
		if .Else != nil &&
			.(.Body, "") &&
			.(.Else, "") {
			return true
		}

	case *ast.SwitchStmt:
		return .isTerminatingSwitch(.Body, )

	case *ast.TypeSwitchStmt:
		return .isTerminatingSwitch(.Body, )

	case *ast.SelectStmt:
		for ,  := range .Body.List {
			 := .(*ast.CommClause)
			if !.isTerminatingList(.Body, "") || hasBreakList(.Body, , true) {
				return false
			}

		}
		return true

	case *ast.ForStmt:
		if .Cond == nil && !hasBreak(.Body, , true) {
			return true
		}
	}

	return false
}

func ( *Checker) ( []ast.Stmt,  string) bool {
	// trailing empty statements are permitted - skip them
	for  := len() - 1;  >= 0; -- {
		if ,  := [].(*ast.EmptyStmt); ! {
			return .isTerminating([], )
		}
	}
	return false // all statements are empty
}

func ( *Checker) ( *ast.BlockStmt,  string) bool {
	 := false
	for ,  := range .List {
		 := .(*ast.CaseClause)
		if .List == nil {
			 = true
		}
		if !.isTerminatingList(.Body, "") || hasBreakList(.Body, , true) {
			return false
		}
	}
	return 
}

// TODO(gri) For nested breakable statements, the current implementation of hasBreak
//	     will traverse the same subtree repeatedly, once for each label. Replace
//           with a single-pass label/break matching phase.

// hasBreak reports if s is or contains a break statement
// referring to the label-ed statement or implicit-ly the
// closest outer breakable statement.
func hasBreak( ast.Stmt,  string,  bool) bool {
	switch 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:
		// no chance

	case *ast.LabeledStmt:
		return (.Stmt, , )

	case *ast.BranchStmt:
		if .Tok == token.BREAK {
			if .Label == nil {
				return 
			}
			if .Label.Name ==  {
				return true
			}
		}

	case *ast.BlockStmt:
		return hasBreakList(.List, , )

	case *ast.IfStmt:
		if (.Body, , ) ||
			.Else != nil && (.Else, , ) {
			return true
		}

	case *ast.CaseClause:
		return hasBreakList(.Body, , )

	case *ast.SwitchStmt:
		if  != "" && (.Body, , false) {
			return true
		}

	case *ast.TypeSwitchStmt:
		if  != "" && (.Body, , false) {
			return true
		}

	case *ast.CommClause:
		return hasBreakList(.Body, , )

	case *ast.SelectStmt:
		if  != "" && (.Body, , false) {
			return true
		}

	case *ast.ForStmt:
		if  != "" && (.Body, , false) {
			return true
		}

	case *ast.RangeStmt:
		if  != "" && (.Body, , false) {
			return true
		}
	}

	return false
}

func hasBreakList( []ast.Stmt,  string,  bool) bool {
	for ,  := range  {
		if hasBreak(, , ) {
			return true
		}
	}
	return false
}