package rule import ( "go/ast" "github.com/mgechev/revive/lint" ) // IdenticalBranchesRule warns on constant logical expressions. type IdenticalBranchesRule struct{} // Apply applies the rule to given file. func (*IdenticalBranchesRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure onFailure := func(failure lint.Failure) { failures = append(failures, failure) } astFile := file.AST w := &lintIdenticalBranches{astFile, onFailure} ast.Walk(w, astFile) return failures } // Name returns the rule name. func (*IdenticalBranchesRule) Name() string { return "identical-branches" } type lintIdenticalBranches struct { file *ast.File onFailure func(lint.Failure) } func (w *lintIdenticalBranches) Visit(node ast.Node) ast.Visitor { n, ok := node.(*ast.IfStmt) if !ok { return w } noElseBranch := n.Else == nil if noElseBranch { return w } branches := []*ast.BlockStmt{n.Body} elseBranch, ok := n.Else.(*ast.BlockStmt) if !ok { // if-else-if construction return w } branches = append(branches, elseBranch) if w.identicalBranches(branches) { w.newFailure(n, "both branches of the if are identical") } return w } func (lintIdenticalBranches) identicalBranches(branches []*ast.BlockStmt) bool { if len(branches) < 2 { return false // only one branch to compare thus we return } referenceBranch := gofmt(branches[0]) referenceBranchSize := len(branches[0].List) for i := 1; i < len(branches); i++ { currentBranch := branches[i] currentBranchSize := len(currentBranch.List) if currentBranchSize != referenceBranchSize || gofmt(currentBranch) != referenceBranch { return false } } return true } func (w lintIdenticalBranches) newFailure(node ast.Node, msg string) { w.onFailure(lint.Failure{ Confidence: 1, Node: node, Category: "logic", Failure: msg, }) }