Use Short Circuit Logic
ID |
csharp.use_short_circuit_logic |
Severity |
critical |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
CSharp |
Tags |
code_smell, control-flow, suspicious-operator |
Description
Reports the non-short-circuit boolean operators & and | applied to boolean operands, where
the short-circuit forms && and || were intended.
Rationale
& and | evaluate both operands, always. && and || stop as soon as the result is decided.
That difference is the whole point of a guard clause: if (s != null & s.Length > 0) still
reads s.Length when s is null and throws the exact exception the null test was written to
avoid. The same applies to any right-hand side with side effects — a call that mutates state, a
lazily initialised property, an expensive lookup — all of which run even when the left-hand side
has already settled the answer.
What the rule looks at is the operands, not the position. A boolean & is just as wrong in an
assignment, in a return, or nested inside a larger && chain as it is in an if condition, so
the same expression is reported wherever it appears.
To avoid flagging deliberate bit manipulation, every operand must be established as boolean.
Syntax settles the common shapes — a comparison, a type test, a negation, a boolean literal, a
variable declared bool — and the remaining operands are resolved by type, which is what brings
in boolean-returning methods and properties. Because the test is on the resolved type, integer
work such as int i = 1 | 2 or the bit test (flags & Mask) != 0 is left alone, as is any
expression whose operand types cannot be resolved.
public class Validator
{
public bool HasContent(string s)
{
if (s != null & s.Length > 0) // FLAW — s.Length runs even when s is null
{
return true;
}
return false;
}
public bool Ready(bool loaded, bool failed)
{
while (loaded & !failed) // FLAW — both operands always evaluated
{
Step();
}
return true;
}
public bool Complete(string s)
{
bool valid = s != null & s.Length > 0; // FLAW — not a condition, same defect
return valid;
}
public bool Available()
{
return IsReady() | IsLoaded(); // FLAW — both calls always run
}
public bool Safe(string s)
{
return s != null && s.Length > 0; // OK — short-circuits on null
}
public bool BitTest(int flags, int mask)
{
return (flags & mask) != 0; // OK — bit manipulation on integers
}
private bool IsReady() { return true; }
private bool IsLoaded() { return true; }
private static void Step() { }
}