diff options
| author | Urgau <urgau@numericable.fr> | 2024-02-20 21:40:25 +0100 |
|---|---|---|
| committer | Urgau <urgau@numericable.fr> | 2024-07-11 00:46:47 +0200 |
| commit | de560c30659281e8c91a48579f907097efe22adf (patch) | |
| tree | 2e27c209275e367550e5c0dba76cfb0e5ea263b5 /compiler/rustc_lint/src/precedence.rs | |
| parent | 0c81f94b9a6207fb1fc080caa83584dea2d71fc6 (diff) | |
| download | rust-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.rs | 70 |
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(), + }, + }, + ); + } + } +} |
