Two different unary operators chained on one operand
ID |
objectivec.correctness.multiple_unary_operator |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Suspicious Code |
Language |
Objective-C |
Description
Reports an operand carrying two different unary prefix operators in a row - !~value, !-value, ~!flag, ~-value, -!flag, -~value. Where the outer operator is an arithmetic -, the chain is only reported when it clearly starts an expression (after =, return, ( or ,), so a subtraction such as total - !flag is not reported. The same operator applied twice (!!, ~~) is reported by the no-double-operators rule instead, and occurrences inside a comment or a string literal are not reported.
Rationale
Each of these chains means something surprising. ~!flag complements a value that is only ever 0 or 1, producing -1 or -2, which is almost never what a flag is for. !~value is true only when value is exactly -1, and !-value is simply !value with a negation that changes nothing. -~value and ~-value are the arithmetic identities value + 1 and value - 1 written as bit tricks. In every case the reader has to reconstruct the operator precedence and the two’s-complement representation to recover an intent the expression could have stated directly - and the usual cause is a keystroke, a ! typed next to a ~, rather than a deliberate choice.
The following code illustrates the pattern detected by this rule:
@implementation BitReader
- (BOOL)isSentinel:(NSInteger)value {
// FLAGGED: Two different unary operators chained on one operand
return !~value;
}
- (NSInteger)clamp:(NSInteger)value { return value; }
@end
Remediation
Write the condition the chain was standing in for. Compare against the value you mean - value == -1 for !~value, !flag for !-flag - or use the plain arithmetic, value + 1 instead of -~value. Where a boolean and a bit mask are genuinely both in play, convert explicitly (flag ? kAllBits : 0) so the two domains stay separate.