Expression Too Complex
ID |
csharp.expression_too_complex |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Complexity |
Language |
CSharp |
Tags |
complexity, control-flow, readability |
Description
Reports one expression that combines more short-circuit and conditional operators than a reader
can follow. The complexity of an expression is the number of &&, || and ternary ?:
operators it contains; a count above the threshold — maxLogicalOperators, 3 by default — is
reported.
Rationale
A dense condition is hard to read not because it is long but because every operator doubles the
number of truth-value combinations the reader has to keep in mind. Precedence makes it worse:
&& binds tighter than ||, so a || b && c does not group the way the eye reads it, and the
grouping is invisible without parentheses. This is exactly where a wrong term or a missing
negation survives review — nothing in the layout marks the operand that is wrong, and every unit
test that happens to exercise one path passes.
Only the outermost expression of a group is reported, so a single dense condition produces one
issue rather than one per nesting level. A lambda or anonymous-method body counts as its own
expression, so an inline predicate is measured on its own rather than inflating the expression it
appears in. Bitwise & and | are not counted: they do not short-circuit and are covered
separately. Comparisons are not counted either — they carry no branching of their own.
This is an expression-level measure and is deliberately separate from the function-level cyclomatic complexity rule. A function can sit comfortably inside its complexity budget and still hold one unreadable condition, and a function with many simple branches can exceed the function-level budget without containing a single complex expression.
public class Gate
{
public bool CanEnter(bool member, bool paid, bool banned, bool invited, bool staff)
{
if (member && paid && !banned && invited && staff) // FLAW — 4 operators
{
return true;
}
if (member && paid && !banned) // OK — 2 operators
{
return true;
}
return false;
}
public bool Readable(bool member, bool paid, bool banned, bool invited, bool staff)
{
bool inGoodStanding = member && paid && !banned; // OK — named, 2 operators
bool hasOverride = invited || staff; // OK — named, 1 operator
return inGoodStanding || hasOverride; // OK — 1 operator
}
}
Remediation
Give the intermediate conditions names. Assigning each part of the condition to a well-named
local turns the operator soup into a sentence, costs nothing at run time thanks to
short-circuiting being preserved by the compiler in most shapes, and makes the wrong term visible.
For a condition that belongs to a domain concept, promote it to a small predicate method or a
property on the type it interrogates. Where the expression selects among several results rather
than testing one thing, a switch expression usually reads better than stacked ternaries.
Configuration
| Property | Default | Description |
|---|---|---|
|
|
Number of short-circuit and conditional operators ( |