Duplicate Branches
ID |
go.duplicate_branches |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
Go |
Tags |
code-style, duplicate-code |
Description
Reports a conditional structure whose branches all run exactly the same code: an if / else
pair or an if / else if / else chain where every body is identical, or a switch where every
case, default included, is identical.
Requiring every branch to match — rather than any two of them — is what keeps the rule quiet across the whole chain and the switch. Two arms of a long dispatch chain sharing a body is a deliberate idiom; all of them sharing a body is always a defect, whatever the body’s size.
The rest of the scope is deliberately narrow too:
-
The chain must be complete — an
if / else ifwith no trailingelseis not a candidate, because falling through the whole chain does something different from entering any branch. Aswitchmust likewise carry adefault, and hold at least two clauses. -
Empty bodies are left alone: an empty branch may be a deliberate no-op, and an empty
ifbody is already reported bygo.empty_if_body. -
Type switches are excluded. In
switch t := v.(type)the guard variable has a different static type in each case, so two textually identical bodies do not run the same code.
Branch bodies are compared over the parsed token sequence, so comments and formatting differences between branches are ignored.
Rationale
When every branch does the same thing, the condition decides nothing. Either the structure is dead weight that should collapse to its body, or — far more often — a branch was copy-pasted and the edit that was supposed to follow never happened. The second reading is the dangerous one: the code looks like it handles each case separately, reviewers read the shape rather than the bodies, and one case is silently mishandled.
func describe(kind int, count int) string {
if kind == 1 { // FLAW — every branch of the chain runs the same code
log.Printf("kind %d", kind)
return "handled"
} else if kind == 2 {
log.Printf("kind %d", kind)
return "handled"
} else {
log.Printf("kind %d", kind)
return "handled"
}
}
Remediation
Decide which reading applies. If the branches were meant to differ, write the behaviour each case actually needs. If they were not, drop the conditional and keep the body.
func describe(kind int, count int) string {
switch kind {
case 1:
return "first"
case 2:
return "second"
default:
return "unknown"
}
}