Skip to content

Commit c1dcbeb

Browse files
committed
implement lint double_negations
1 parent 9e316f3 commit c1dcbeb

File tree

8 files changed

+159
-16
lines changed

8 files changed

+159
-16
lines changed

compiler/rustc_lint/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$rea
7676
lint_builtin_deref_nullptr = dereferencing a null pointer
7777
.label = this code causes undefined behavior when executed
7878
79+
lint_builtin_double_negations = use of a double negation
80+
.note = the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
81+
.note_decrement = use `-= 1` if you meant to decrement the value
82+
.add_parens_suggestion = add parentheses for clarity
83+
7984
lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated
8085
.suggestion = use `..=` for an inclusive range
8186

compiler/rustc_lint/src/builtin.rs

+64-11
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ use rustc_trait_selection::traits::{self};
4949
use crate::errors::BuiltinEllipsisInclusiveRangePatterns;
5050
use crate::lints::{
5151
BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
52-
BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr,
53-
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
54-
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
55-
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
56-
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes,
57-
BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed,
58-
BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller,
59-
BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
60-
BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
61-
BuiltinWhileTrue, InvalidAsmLabel,
52+
BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr, BuiltinDoubleNegations,
53+
BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint,
54+
BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote,
55+
BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures,
56+
BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
57+
BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
58+
BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
59+
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub,
60+
BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
61+
BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
6262
};
6363
use crate::nonstandard_style::{MethodLateContext, method_context};
6464
use crate::{
@@ -1568,6 +1568,58 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
15681568
}
15691569
}
15701570

1571+
declare_lint! {
1572+
/// The `double_negations` lint detects expressions of the form `--x`.
1573+
///
1574+
/// ### Example
1575+
///
1576+
/// ```rust
1577+
/// fn main() {
1578+
/// let x = 1;
1579+
/// let _b = --x;
1580+
/// }
1581+
/// ```
1582+
///
1583+
/// {{produces}}
1584+
///
1585+
/// ### Explanation
1586+
///
1587+
/// Negating something twice is usually the same as not negating it at all.
1588+
/// However, a double negation in Rust can easily be confused with the
1589+
/// prefix decrement operator that exists in many languages derived from C.
1590+
/// Use `-(-x)` if you really wanted to negate the value twice.
1591+
///
1592+
/// To decrement a value, use `x -= 1` instead.
1593+
pub DOUBLE_NEGATIONS,
1594+
Warn,
1595+
"detects expressions of the form `--x`"
1596+
}
1597+
1598+
declare_lint_pass!(
1599+
/// Lint for expressions of the form `--x` that can be confused with C's
1600+
/// prefix decrement operator.
1601+
DoubleNegations => [DOUBLE_NEGATIONS]
1602+
);
1603+
1604+
impl EarlyLintPass for DoubleNegations {
1605+
#[inline]
1606+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
1607+
// only lint on the innermost `--` in a chain of `-` operators,
1608+
// even if there are 3 or more negations
1609+
if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind
1610+
&& let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind
1611+
&& !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _))
1612+
{
1613+
cx.emit_span_lint(DOUBLE_NEGATIONS, expr.span, BuiltinDoubleNegations {
1614+
add_parens: BuiltinDoubleNegationsAddParens {
1615+
start_span: inner.span.shrink_to_lo(),
1616+
end_span: inner.span.shrink_to_hi(),
1617+
},
1618+
});
1619+
}
1620+
}
1621+
}
1622+
15711623
declare_lint_pass!(
15721624
/// Does nothing as a lint pass, but registers some `Lint`s
15731625
/// which are used by other parts of the compiler.
@@ -1586,7 +1638,8 @@ declare_lint_pass!(
15861638
UNSTABLE_FEATURES,
15871639
UNREACHABLE_PUB,
15881640
TYPE_ALIAS_BOUNDS,
1589-
TRIVIAL_BOUNDS
1641+
TRIVIAL_BOUNDS,
1642+
DOUBLE_NEGATIONS
15901643
]
15911644
);
15921645

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ early_lint_methods!(
181181
UnusedDocComment: UnusedDocComment,
182182
Expr2024: Expr2024,
183183
Precedence: Precedence,
184+
DoubleNegations: DoubleNegations,
184185
]
185186
]
186187
);

compiler/rustc_lint/src/lints.rs

+18
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,24 @@ pub(crate) struct BuiltinTrivialBounds<'a> {
331331
pub predicate: Clause<'a>,
332332
}
333333

334+
#[derive(LintDiagnostic)]
335+
#[diag(lint_builtin_double_negations)]
336+
#[note(lint_note)]
337+
#[note(lint_note_decrement)]
338+
pub(crate) struct BuiltinDoubleNegations {
339+
#[subdiagnostic]
340+
pub add_parens: BuiltinDoubleNegationsAddParens,
341+
}
342+
343+
#[derive(Subdiagnostic)]
344+
#[multipart_suggestion(lint_add_parens_suggestion, applicability = "maybe-incorrect")]
345+
pub(crate) struct BuiltinDoubleNegationsAddParens {
346+
#[suggestion_part(code = "(")]
347+
pub start_span: Span,
348+
#[suggestion_part(code = ")")]
349+
pub end_span: Span,
350+
}
351+
334352
#[derive(LintDiagnostic)]
335353
pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint {
336354
#[diag(lint_builtin_ellipsis_inclusive_range_patterns)]
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ check-pass
2+
fn main() {
3+
let x = 1;
4+
-x;
5+
-(-x);
6+
--x; //~ WARN use of a double negation
7+
---x; //~ WARN use of a double negation
8+
let _y = --(-x); //~ WARN use of a double negation
9+
}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
warning: use of a double negation
2+
--> $DIR/lint-double-negations.rs:6:5
3+
|
4+
LL | --x;
5+
| ^^^
6+
|
7+
= note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
8+
= note: use `-= 1` if you meant to decrement the value
9+
= note: `#[warn(double_negations)]` on by default
10+
help: add parentheses for clarity
11+
|
12+
LL | -(-x);
13+
| + +
14+
15+
warning: use of a double negation
16+
--> $DIR/lint-double-negations.rs:7:6
17+
|
18+
LL | ---x;
19+
| ^^^
20+
|
21+
= note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
22+
= note: use `-= 1` if you meant to decrement the value
23+
help: add parentheses for clarity
24+
|
25+
LL | --(-x);
26+
| + +
27+
28+
warning: use of a double negation
29+
--> $DIR/lint-double-negations.rs:8:14
30+
|
31+
LL | let _y = --(-x);
32+
| ^^^^^^
33+
|
34+
= note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
35+
= note: use `-= 1` if you meant to decrement the value
36+
help: add parentheses for clarity
37+
|
38+
LL | let _y = -(-(-x));
39+
| + +
40+
41+
warning: 3 warnings emitted
42+

tests/ui/lint/lint-type-overflow2.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
fn main() {
66
let x2: i8 = --128; //~ ERROR literal out of range for `i8`
7+
//~| WARN use of a double negation
78

89
let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
910
let x = 3.40282357e+38_f32; //~ ERROR literal out of range for `f32`

tests/ui/lint/lint-type-overflow2.stderr

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
warning: use of a double negation
2+
--> $DIR/lint-type-overflow2.rs:6:18
3+
|
4+
LL | let x2: i8 = --128;
5+
| ^^^^^
6+
|
7+
= note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
8+
= note: use `-= 1` if you meant to decrement the value
9+
= note: `#[warn(double_negations)]` on by default
10+
help: add parentheses for clarity
11+
|
12+
LL | let x2: i8 = -(-128);
13+
| + +
14+
115
error: literal out of range for `i8`
216
--> $DIR/lint-type-overflow2.rs:6:20
317
|
@@ -13,36 +27,36 @@ LL | #![deny(overflowing_literals)]
1327
| ^^^^^^^^^^^^^^^^^^^^
1428

1529
error: literal out of range for `f32`
16-
--> $DIR/lint-type-overflow2.rs:8:14
30+
--> $DIR/lint-type-overflow2.rs:9:14
1731
|
1832
LL | let x = -3.40282357e+38_f32;
1933
| ^^^^^^^^^^^^^^^^^^
2034
|
2135
= note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
2236

2337
error: literal out of range for `f32`
24-
--> $DIR/lint-type-overflow2.rs:9:14
38+
--> $DIR/lint-type-overflow2.rs:10:14
2539
|
2640
LL | let x = 3.40282357e+38_f32;
2741
| ^^^^^^^^^^^^^^^^^^
2842
|
2943
= note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
3044

3145
error: literal out of range for `f64`
32-
--> $DIR/lint-type-overflow2.rs:10:14
46+
--> $DIR/lint-type-overflow2.rs:11:14
3347
|
3448
LL | let x = -1.7976931348623159e+308_f64;
3549
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
3650
|
3751
= note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
3852

3953
error: literal out of range for `f64`
40-
--> $DIR/lint-type-overflow2.rs:11:14
54+
--> $DIR/lint-type-overflow2.rs:12:14
4155
|
4256
LL | let x = 1.7976931348623159e+308_f64;
4357
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
4458
|
4559
= note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
4660

47-
error: aborting due to 5 previous errors
61+
error: aborting due to 5 previous errors; 1 warning emitted
4862

0 commit comments

Comments
 (0)