Operator set implemented incompletely
ID |
vbnet.correctness.operator_set_incomplete |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Type Design |
Language |
VB.NET |
Description
Reports a type that overloads one operator of an inverse arithmetic pair without the other - + without -, or * without /, in either direction. A type that declares both members of the pair is not reported, in either declaration order.
Rationale
Arithmetic operators are read as a set. Once a type supports a + b, callers reasonably expect a - b to exist as well, and code that has to compute one from the other ends up reaching past the type - unwrapping it to its underlying value, doing the arithmetic on that, and rebuilding the instance. That workaround bypasses whatever the type validates or normalises when it is constructed, which is usually exactly what the type exists to guarantee. The asymmetry also makes the type unusable in generic numeric code, and it commonly means the missing operator was simply overlooked rather than deliberately withheld.
The following code illustrates the pattern detected by this rule:
Public Value As Double
' FLAGGED: Operator set implemented incompletely
Public Shared Operator +(ByVal a As Reading, ByVal b As Reading) As Reading
Return New Reading With {.Value = a.Value + b.Value}
End Operator
End Structure
Public Structure Drift
Public Value As Double
' FLAGGED: Operator set implemented incompletely
Public Shared Operator -(ByVal a As Drift, ByVal b As Drift) As Drift
Return New Drift With {.Value = a.Value - b.Value}
End Operator
End Structure
Public Structure Scale
Public Factor As Double
' FLAGGED: Operator set implemented incompletely
Public Shared Operator *(ByVal a As Scale, ByVal k As Double) As Scale
Return New Scale With {.Factor = a.Factor * k}
End Operator
End Structure
Public Class Rate
Remediation
Add the missing operator as a Public Shared Operator alongside the one that already exists, keeping the same operand and return types so the pair composes, and route it through the same constructor or factory the existing operator uses so both go through the same validation. If the inverse genuinely has no meaning for the type - a monotonic counter or an accumulator, say - expose the intended arithmetic through a named method instead and drop the operator, rather than leaving callers a half-set they have to work around.