about summary refs log tree commit diff
path: root/compiler/rustc_lint/src/precedence.rs
diff options
context:
space:
mode:
authorUrgau <urgau@numericable.fr>2024-02-20 21:40:25 +0100
committerUrgau <urgau@numericable.fr>2024-07-11 00:46:47 +0200
commitde560c30659281e8c91a48579f907097efe22adf (patch)
tree2e27c209275e367550e5c0dba76cfb0e5ea263b5 /compiler/rustc_lint/src/precedence.rs
parent0c81f94b9a6207fb1fc080caa83584dea2d71fc6 (diff)
downloadrust-de560c30659281e8c91a48579f907097efe22adf.tar.gz
rust-de560c30659281e8c91a48579f907097efe22adf.zip
Implement `ambiguous_negative_literals` lint
Diffstat (limited to 'compiler/rustc_lint/src/precedence.rs')
-rw-r--r--compiler/rustc_lint/src/precedence.rs70
1 files changed, 70 insertions, 0 deletions
diff --git a/compiler/rustc_lint/src/precedence.rs b/compiler/rustc_lint/src/precedence.rs
new file mode 100644
index 00000000000..eb2ba397277
--- /dev/null
+++ b/compiler/rustc_lint/src/precedence.rs
@@ -0,0 +1,70 @@
+use rustc_ast::token::LitKind;
+use rustc_ast::{Expr, ExprKind, MethodCall, UnOp};
+use rustc_session::{declare_lint, declare_lint_pass};
+
+use crate::lints::{
+    AmbiguousNegativeLiteralsCurrentBehaviorSuggestion, AmbiguousNegativeLiteralsDiag,
+    AmbiguousNegativeLiteralsNegativeLiteralSuggestion,
+};
+use crate::{EarlyContext, EarlyLintPass, LintContext};
+
+declare_lint! {
+    /// The `ambiguous_negative_literals` lint checks for cases that are
+    /// confusing between a negative literal and a negation that's not part
+    /// of the literal.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// # #![allow(unused)]
+    /// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Method calls take precedence over unary precedence. Setting the
+    /// precedence explicitly makes the code clearer and avoid potential bugs.
+    pub AMBIGUOUS_NEGATIVE_LITERALS,
+    Deny,
+    "ambiguous negative literals operations",
+    report_in_external_macro
+}
+
+declare_lint_pass!(Precedence => [AMBIGUOUS_NEGATIVE_LITERALS]);
+
+impl EarlyLintPass for Precedence {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind else {
+            return;
+        };
+
+        let mut arg = operand;
+        let mut at_least_one = false;
+        while let ExprKind::MethodCall(box MethodCall { receiver, .. }) = &arg.kind {
+            at_least_one = true;
+            arg = receiver;
+        }
+
+        if at_least_one
+            && let ExprKind::Lit(lit) = &arg.kind
+            && let LitKind::Integer | LitKind::Float = &lit.kind
+        {
+            cx.emit_span_lint(
+                AMBIGUOUS_NEGATIVE_LITERALS,
+                expr.span,
+                AmbiguousNegativeLiteralsDiag {
+                    negative_literal: AmbiguousNegativeLiteralsNegativeLiteralSuggestion {
+                        start_span: expr.span.shrink_to_lo(),
+                        end_span: arg.span.shrink_to_hi(),
+                    },
+                    current_behavior: AmbiguousNegativeLiteralsCurrentBehaviorSuggestion {
+                        start_span: operand.span.shrink_to_lo(),
+                        end_span: operand.span.shrink_to_hi(),
+                    },
+                },
+            );
+        }
+    }
+}