about summary refs log tree commit diff
diff options
context:
space:
mode:
authorKalle Wachsmuth <kalle.wachsmuth@gmail.com>2024-09-01 19:22:35 +0200
committerKalle Wachsmuth <kalle.wachsmuth@gmail.com>2025-01-26 12:18:33 +0100
commitc1dcbebd0b4f721cead9d0f7ad63b88d8effed43 (patch)
tree6a5c390f9e0fca799549e62067161e31f9ec4e84
parent9e316f34720a27477c79fdb5e0193554bd4d3fb5 (diff)
downloadrust-c1dcbebd0b4f721cead9d0f7ad63b88d8effed43.tar.gz
rust-c1dcbebd0b4f721cead9d0f7ad63b88d8effed43.zip
implement lint `double_negations`
-rw-r--r--compiler/rustc_lint/messages.ftl5
-rw-r--r--compiler/rustc_lint/src/builtin.rs75
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint/src/lints.rs18
-rw-r--r--tests/ui/lint/lint-double-negations.rs9
-rw-r--r--tests/ui/lint/lint-double-negations.stderr42
-rw-r--r--tests/ui/lint/lint-type-overflow2.rs1
-rw-r--r--tests/ui/lint/lint-type-overflow2.stderr24
8 files changed, 159 insertions, 16 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 0c1bd08474f..55c6a122d35 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -76,6 +76,11 @@ lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$rea
 lint_builtin_deref_nullptr = dereferencing a null pointer
     .label = this code causes undefined behavior when executed
 
+lint_builtin_double_negations = use of a double negation
+    .note = the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+    .note_decrement = use `-= 1` if you meant to decrement the value
+    .add_parens_suggestion = add parentheses for clarity
+
 lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated
     .suggestion = use `..=` for an inclusive range
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 3da9ef3ff7c..c03de687a33 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -49,16 +49,16 @@ use rustc_trait_selection::traits::{self};
 use crate::errors::BuiltinEllipsisInclusiveRangePatterns;
 use crate::lints::{
     BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
-    BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr,
-    BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
-    BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
-    BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
-    BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes,
-    BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed,
-    BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller,
-    BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
-    BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
-    BuiltinWhileTrue, InvalidAsmLabel,
+    BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr, BuiltinDoubleNegations,
+    BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint,
+    BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote,
+    BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures,
+    BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
+    BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
+    BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
+    BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub,
+    BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
+    BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
 };
 use crate::nonstandard_style::{MethodLateContext, method_context};
 use crate::{
@@ -1568,6 +1568,58 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
     }
 }
 
+declare_lint! {
+    /// The `double_negations` lint detects expressions of the form `--x`.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn main() {
+    ///     let x = 1;
+    ///     let _b = --x;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Negating something twice is usually the same as not negating it at all.
+    /// However, a double negation in Rust can easily be confused with the
+    /// prefix decrement operator that exists in many languages derived from C.
+    /// Use `-(-x)` if you really wanted to negate the value twice.
+    ///
+    /// To decrement a value, use `x -= 1` instead.
+    pub DOUBLE_NEGATIONS,
+    Warn,
+    "detects expressions of the form `--x`"
+}
+
+declare_lint_pass!(
+    /// Lint for expressions of the form `--x` that can be confused with C's
+    /// prefix decrement operator.
+    DoubleNegations => [DOUBLE_NEGATIONS]
+);
+
+impl EarlyLintPass for DoubleNegations {
+    #[inline]
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
+        // only lint on the innermost `--` in a chain of `-` operators,
+        // even if there are 3 or more negations
+        if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind
+            && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind
+            && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _))
+        {
+            cx.emit_span_lint(DOUBLE_NEGATIONS, expr.span, BuiltinDoubleNegations {
+                add_parens: BuiltinDoubleNegationsAddParens {
+                    start_span: inner.span.shrink_to_lo(),
+                    end_span: inner.span.shrink_to_hi(),
+                },
+            });
+        }
+    }
+}
+
 declare_lint_pass!(
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// which are used by other parts of the compiler.
@@ -1586,7 +1638,8 @@ declare_lint_pass!(
         UNSTABLE_FEATURES,
         UNREACHABLE_PUB,
         TYPE_ALIAS_BOUNDS,
-        TRIVIAL_BOUNDS
+        TRIVIAL_BOUNDS,
+        DOUBLE_NEGATIONS
     ]
 );
 
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 1465c2cff7b..83a168c3f44 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -181,6 +181,7 @@ early_lint_methods!(
             UnusedDocComment: UnusedDocComment,
             Expr2024: Expr2024,
             Precedence: Precedence,
+            DoubleNegations: DoubleNegations,
         ]
     ]
 );
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 3163bc8a300..677fc86a235 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -332,6 +332,24 @@ pub(crate) struct BuiltinTrivialBounds<'a> {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(lint_builtin_double_negations)]
+#[note(lint_note)]
+#[note(lint_note_decrement)]
+pub(crate) struct BuiltinDoubleNegations {
+    #[subdiagnostic]
+    pub add_parens: BuiltinDoubleNegationsAddParens,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(lint_add_parens_suggestion, applicability = "maybe-incorrect")]
+pub(crate) struct BuiltinDoubleNegationsAddParens {
+    #[suggestion_part(code = "(")]
+    pub start_span: Span,
+    #[suggestion_part(code = ")")]
+    pub end_span: Span,
+}
+
+#[derive(LintDiagnostic)]
 pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint {
     #[diag(lint_builtin_ellipsis_inclusive_range_patterns)]
     Parenthesise {
diff --git a/tests/ui/lint/lint-double-negations.rs b/tests/ui/lint/lint-double-negations.rs
new file mode 100644
index 00000000000..43e61dd7c13
--- /dev/null
+++ b/tests/ui/lint/lint-double-negations.rs
@@ -0,0 +1,9 @@
+//@ check-pass
+fn main() {
+    let x = 1;
+    -x;
+    -(-x);
+    --x; //~ WARN use of a double negation
+    ---x; //~ WARN use of a double negation
+    let _y = --(-x); //~ WARN use of a double negation
+}
diff --git a/tests/ui/lint/lint-double-negations.stderr b/tests/ui/lint/lint-double-negations.stderr
new file mode 100644
index 00000000000..9367f74be54
--- /dev/null
+++ b/tests/ui/lint/lint-double-negations.stderr
@@ -0,0 +1,42 @@
+warning: use of a double negation
+  --> $DIR/lint-double-negations.rs:6:5
+   |
+LL |     --x;
+   |     ^^^
+   |
+   = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+   = note: use `-= 1` if you meant to decrement the value
+   = note: `#[warn(double_negations)]` on by default
+help: add parentheses for clarity
+   |
+LL |     -(-x);
+   |      +  +
+
+warning: use of a double negation
+  --> $DIR/lint-double-negations.rs:7:6
+   |
+LL |     ---x;
+   |      ^^^
+   |
+   = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+   = note: use `-= 1` if you meant to decrement the value
+help: add parentheses for clarity
+   |
+LL |     --(-x);
+   |       +  +
+
+warning: use of a double negation
+  --> $DIR/lint-double-negations.rs:8:14
+   |
+LL |     let _y = --(-x);
+   |              ^^^^^^
+   |
+   = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+   = note: use `-= 1` if you meant to decrement the value
+help: add parentheses for clarity
+   |
+LL |     let _y = -(-(-x));
+   |               +     +
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/lint/lint-type-overflow2.rs b/tests/ui/lint/lint-type-overflow2.rs
index f007b45b847..ac7420326c8 100644
--- a/tests/ui/lint/lint-type-overflow2.rs
+++ b/tests/ui/lint/lint-type-overflow2.rs
@@ -4,6 +4,7 @@
 
 fn main() {
     let x2: i8 = --128; //~ ERROR literal out of range for `i8`
+    //~| WARN use of a double negation
 
     let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
     let x =  3.40282357e+38_f32; //~ ERROR literal out of range for `f32`
diff --git a/tests/ui/lint/lint-type-overflow2.stderr b/tests/ui/lint/lint-type-overflow2.stderr
index eb593d062f2..2cfb18e9fe9 100644
--- a/tests/ui/lint/lint-type-overflow2.stderr
+++ b/tests/ui/lint/lint-type-overflow2.stderr
@@ -1,3 +1,17 @@
+warning: use of a double negation
+  --> $DIR/lint-type-overflow2.rs:6:18
+   |
+LL |     let x2: i8 = --128;
+   |                  ^^^^^
+   |
+   = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages
+   = note: use `-= 1` if you meant to decrement the value
+   = note: `#[warn(double_negations)]` on by default
+help: add parentheses for clarity
+   |
+LL |     let x2: i8 = -(-128);
+   |                   +    +
+
 error: literal out of range for `i8`
   --> $DIR/lint-type-overflow2.rs:6:20
    |
@@ -13,7 +27,7 @@ LL | #![deny(overflowing_literals)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: literal out of range for `f32`
-  --> $DIR/lint-type-overflow2.rs:8:14
+  --> $DIR/lint-type-overflow2.rs:9:14
    |
 LL |     let x = -3.40282357e+38_f32;
    |              ^^^^^^^^^^^^^^^^^^
@@ -21,7 +35,7 @@ LL |     let x = -3.40282357e+38_f32;
    = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
 
 error: literal out of range for `f32`
-  --> $DIR/lint-type-overflow2.rs:9:14
+  --> $DIR/lint-type-overflow2.rs:10:14
    |
 LL |     let x =  3.40282357e+38_f32;
    |              ^^^^^^^^^^^^^^^^^^
@@ -29,7 +43,7 @@ LL |     let x =  3.40282357e+38_f32;
    = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
 
 error: literal out of range for `f64`
-  --> $DIR/lint-type-overflow2.rs:10:14
+  --> $DIR/lint-type-overflow2.rs:11:14
    |
 LL |     let x = -1.7976931348623159e+308_f64;
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -37,12 +51,12 @@ LL |     let x = -1.7976931348623159e+308_f64;
    = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
 
 error: literal out of range for `f64`
-  --> $DIR/lint-type-overflow2.rs:11:14
+  --> $DIR/lint-type-overflow2.rs:12:14
    |
 LL |     let x =  1.7976931348623159e+308_f64;
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
 
-error: aborting due to 5 previous errors
+error: aborting due to 5 previous errors; 1 warning emitted