package ifelse import ( "fmt" "go/ast" "go/token" ) // Branch contains information about a branch within an if-else chain. type Branch struct { BranchKind Call // The function called at the end for kind Panic or Exit. block []ast.Stmt } // BlockBranch gets the Branch of an ast.BlockStmt. func BlockBranch(block *ast.BlockStmt) Branch { blockLen := len(block.List) if blockLen == 0 { return Empty.Branch() } branch := StmtBranch(block.List[blockLen-1]) branch.block = block.List return branch } // StmtBranch gets the Branch of an ast.Stmt. func StmtBranch(stmt ast.Stmt) Branch { switch stmt := stmt.(type) { case *ast.ReturnStmt: return Return.Branch() case *ast.BlockStmt: return BlockBranch(stmt) case *ast.BranchStmt: switch stmt.Tok { case token.BREAK: return Break.Branch() case token.CONTINUE: return Continue.Branch() case token.GOTO: return Goto.Branch() } case *ast.ExprStmt: fn, ok := ExprCall(stmt) if !ok { break } kind, ok := DeviatingFuncs[fn] if ok { return Branch{BranchKind: kind, Call: fn} } case *ast.EmptyStmt: return Empty.Branch() case *ast.LabeledStmt: return StmtBranch(stmt.Stmt) } return Regular.Branch() } // String returns a brief string representation func (b Branch) String() string { switch b.BranchKind { case Empty: return "{ }" case Regular: return "{ ... }" case Panic, Exit: return fmt.Sprintf("{ ... %v() }", b.Call) } return fmt.Sprintf("{ ... %v }", b.BranchKind) } // LongString returns a longer form string representation func (b Branch) LongString() string { switch b.BranchKind { case Panic, Exit: return fmt.Sprintf("call to %v function", b.Call) } return b.BranchKind.LongString() } // HasDecls returns whether the branch has any top-level declarations func (b Branch) HasDecls() bool { for _, stmt := range b.block { switch stmt := stmt.(type) { case *ast.DeclStmt: return true case *ast.AssignStmt: if stmt.Tok == token.DEFINE { return true } } } return false } // IsShort returns whether the branch is empty or consists of a single statement func (b Branch) IsShort() bool { switch len(b.block) { case 0: return true case 1: return isShortStmt(b.block[0]) } return false } func isShortStmt(stmt ast.Stmt) bool { switch stmt.(type) { case *ast.BlockStmt, *ast.IfStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt: return false } return true }