Skip to content

Commit 0ba1e14

Browse files
committed
Implement ambiguous unary precedence lint
1 parent ccb1415 commit 0ba1e14

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed

compiler/rustc_lint/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,9 @@ lint_tykind = usage of `ty::TyKind`
527527
lint_tykind_kind = usage of `ty::TyKind::<kind>`
528528
.suggestion = try using `ty::<kind>` directly
529529
530+
lint_unary_precedence = unary operator `-` has lower precedence than method calls which might be unexpected
531+
.suggestion = use parentheses to clarify your intent
532+
530533
lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
531534
.label = argument has type `{$arg_ty}`
532535
.suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value

compiler/rustc_lint/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ mod noop_method_call;
7575
mod opaque_hidden_inferred_bound;
7676
mod pass_by_value;
7777
mod passes;
78+
mod precedence;
7879
mod ptr_nulls;
7980
mod redundant_semicolon;
8081
mod reference_casting;
@@ -109,6 +110,7 @@ use nonstandard_style::*;
109110
use noop_method_call::*;
110111
use opaque_hidden_inferred_bound::*;
111112
use pass_by_value::*;
113+
use precedence::*;
112114
use ptr_nulls::*;
113115
use redundant_semicolon::*;
114116
use reference_casting::*;
@@ -171,6 +173,7 @@ early_lint_methods!(
171173
IncompleteInternalFeatures: IncompleteInternalFeatures,
172174
RedundantSemicolons: RedundantSemicolons,
173175
UnusedDocComment: UnusedDocComment,
176+
Precedence: Precedence,
174177
]
175178
]
176179
);

compiler/rustc_lint/src/lints.rs

+17
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,23 @@ pub struct SuspiciousDoubleRefCloneDiag<'a> {
12931293
pub ty: Ty<'a>,
12941294
}
12951295

1296+
// precedence.rs
1297+
#[derive(LintDiagnostic)]
1298+
#[diag(lint_unary_precedence)]
1299+
pub struct UnaryPrecedenceDiag {
1300+
#[subdiagnostic]
1301+
pub suggestion: UnaryPrecedenceSuggestion,
1302+
}
1303+
1304+
#[derive(Subdiagnostic)]
1305+
#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
1306+
pub struct UnaryPrecedenceSuggestion {
1307+
#[suggestion_part(code = "(")]
1308+
pub start_span: Span,
1309+
#[suggestion_part(code = ")")]
1310+
pub end_span: Span,
1311+
}
1312+
12961313
// pass_by_value.rs
12971314
#[derive(LintDiagnostic)]
12981315
#[diag(lint_pass_by_value)]

compiler/rustc_lint/src/precedence.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
use rustc_ast::token::LitKind;
2+
use rustc_ast::{Expr, ExprKind, MethodCall, UnOp};
3+
4+
use crate::lints::{UnaryPrecedenceDiag, UnaryPrecedenceSuggestion};
5+
use crate::{EarlyContext, EarlyLintPass, LintContext};
6+
7+
declare_lint! {
8+
/// The `ambiguous_unary_precedence` lint checks for use the negative
9+
/// unary operator with a literal and method calls.
10+
///
11+
/// ### Example
12+
///
13+
/// ```rust,compile_fail
14+
/// # #![allow(unused)]
15+
/// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1
16+
/// ```
17+
///
18+
/// {{produces}}
19+
///
20+
/// ### Explanation
21+
///
22+
/// Unary operations take precedence on binary operations and method
23+
/// calls take precedence over unary precedence. Setting the precedence
24+
/// explicitly makes the code clearer and avoid potential bugs.
25+
pub AMBIGUOUS_UNARY_PRECEDENCE,
26+
Deny,
27+
"operations where precedence may be unclear",
28+
report_in_external_macro
29+
}
30+
31+
declare_lint_pass!(Precedence => [AMBIGUOUS_UNARY_PRECEDENCE]);
32+
33+
impl EarlyLintPass for Precedence {
34+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
35+
let ExprKind::Unary(UnOp::Neg, operand) = &expr.kind else {
36+
return;
37+
};
38+
39+
let mut arg = operand;
40+
let mut at_least_one = false;
41+
while let ExprKind::MethodCall(box MethodCall { receiver, .. }) = &arg.kind {
42+
at_least_one = true;
43+
arg = receiver;
44+
}
45+
46+
if at_least_one
47+
&& let ExprKind::Lit(lit) = &arg.kind
48+
&& let LitKind::Integer | LitKind::Float = &lit.kind
49+
{
50+
cx.emit_span_lint(
51+
AMBIGUOUS_UNARY_PRECEDENCE,
52+
expr.span,
53+
UnaryPrecedenceDiag {
54+
suggestion: UnaryPrecedenceSuggestion {
55+
start_span: operand.span.shrink_to_lo(),
56+
end_span: operand.span.shrink_to_hi(),
57+
},
58+
},
59+
);
60+
}
61+
}
62+
}

tests/ui/lint/unary_precedence.fixed

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@ check-fail
2+
//@ run-rustfix
3+
4+
fn main() {
5+
let _ = -(1i32.abs());
6+
//~^ ERROR unary operator `-` has lower precedence than method call
7+
let _ = -(1f32.abs());
8+
//~^ ERROR unary operator `-` has lower precedence than method call
9+
let _ = -(1f64.asin());
10+
//~^ ERROR unary operator `-` has lower precedence than method call
11+
let _ = -(1f64.asinh());
12+
//~^ ERROR unary operator `-` has lower precedence than method call
13+
let _ = -(1f64.tan());
14+
//~^ ERROR unary operator `-` has lower precedence than method call
15+
let _ = -(1f64.tanh());
16+
//~^ ERROR unary operator `-` has lower precedence than method call
17+
let _ = -(1.0_f64.cos().cos());
18+
//~^ ERROR unary operator `-` has lower precedence than method call
19+
let _ = -(1.0_f64.cos().sin());
20+
//~^ ERROR unary operator `-` has lower precedence than method call
21+
let _ = -(1.0_f64.sin().cos());
22+
//~^ ERROR unary operator `-` has lower precedence than method call
23+
let _ = -(1f64.sin().sin());
24+
//~^ ERROR unary operator `-` has lower precedence than method call
25+
26+
dbg!( -(1.0_f32.cos()) );
27+
//~^ ERROR unary operator `-` has lower precedence than method call
28+
29+
// should not warn
30+
let _ = (-1i32).abs();
31+
let _ = (-1f32).abs();
32+
let _ = -(1i32).abs();
33+
let _ = -(1f32).abs();
34+
let _ = -(1i32.abs());
35+
let _ = -(1f32.abs());
36+
}

tests/ui/lint/unary_precedence.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@ check-fail
2+
//@ run-rustfix
3+
4+
fn main() {
5+
let _ = -1i32.abs();
6+
//~^ ERROR unary operator `-` has lower precedence than method call
7+
let _ = -1f32.abs();
8+
//~^ ERROR unary operator `-` has lower precedence than method call
9+
let _ = -1f64.asin();
10+
//~^ ERROR unary operator `-` has lower precedence than method call
11+
let _ = -1f64.asinh();
12+
//~^ ERROR unary operator `-` has lower precedence than method call
13+
let _ = -1f64.tan();
14+
//~^ ERROR unary operator `-` has lower precedence than method call
15+
let _ = -1f64.tanh();
16+
//~^ ERROR unary operator `-` has lower precedence than method call
17+
let _ = -1.0_f64.cos().cos();
18+
//~^ ERROR unary operator `-` has lower precedence than method call
19+
let _ = -1.0_f64.cos().sin();
20+
//~^ ERROR unary operator `-` has lower precedence than method call
21+
let _ = -1.0_f64.sin().cos();
22+
//~^ ERROR unary operator `-` has lower precedence than method call
23+
let _ = -1f64.sin().sin();
24+
//~^ ERROR unary operator `-` has lower precedence than method call
25+
26+
dbg!( -1.0_f32.cos() );
27+
//~^ ERROR unary operator `-` has lower precedence than method call
28+
29+
// should not warn
30+
let _ = (-1i32).abs();
31+
let _ = (-1f32).abs();
32+
let _ = -(1i32).abs();
33+
let _ = -(1f32).abs();
34+
let _ = -(1i32.abs());
35+
let _ = -(1f32.abs());
36+
}

tests/ui/lint/unary_precedence.stderr

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
error: unary operator `-` has lower precedence than method calls which might be unexpected
2+
--> $DIR/unary_precedence.rs:5:13
3+
|
4+
LL | let _ = -1i32.abs();
5+
| ^^^^^^^^^^^
6+
|
7+
= note: `#[deny(ambiguous_unary_precedence)]` on by default
8+
help: use parentheses to clarify your intent
9+
|
10+
LL | let _ = -(1i32.abs());
11+
| + +
12+
13+
error: unary operator `-` has lower precedence than method calls which might be unexpected
14+
--> $DIR/unary_precedence.rs:7:13
15+
|
16+
LL | let _ = -1f32.abs();
17+
| ^^^^^^^^^^^
18+
|
19+
help: use parentheses to clarify your intent
20+
|
21+
LL | let _ = -(1f32.abs());
22+
| + +
23+
24+
error: unary operator `-` has lower precedence than method calls which might be unexpected
25+
--> $DIR/unary_precedence.rs:9:13
26+
|
27+
LL | let _ = -1f64.asin();
28+
| ^^^^^^^^^^^^
29+
|
30+
help: use parentheses to clarify your intent
31+
|
32+
LL | let _ = -(1f64.asin());
33+
| + +
34+
35+
error: unary operator `-` has lower precedence than method calls which might be unexpected
36+
--> $DIR/unary_precedence.rs:11:13
37+
|
38+
LL | let _ = -1f64.asinh();
39+
| ^^^^^^^^^^^^^
40+
|
41+
help: use parentheses to clarify your intent
42+
|
43+
LL | let _ = -(1f64.asinh());
44+
| + +
45+
46+
error: unary operator `-` has lower precedence than method calls which might be unexpected
47+
--> $DIR/unary_precedence.rs:13:13
48+
|
49+
LL | let _ = -1f64.tan();
50+
| ^^^^^^^^^^^
51+
|
52+
help: use parentheses to clarify your intent
53+
|
54+
LL | let _ = -(1f64.tan());
55+
| + +
56+
57+
error: unary operator `-` has lower precedence than method calls which might be unexpected
58+
--> $DIR/unary_precedence.rs:15:13
59+
|
60+
LL | let _ = -1f64.tanh();
61+
| ^^^^^^^^^^^^
62+
|
63+
help: use parentheses to clarify your intent
64+
|
65+
LL | let _ = -(1f64.tanh());
66+
| + +
67+
68+
error: unary operator `-` has lower precedence than method calls which might be unexpected
69+
--> $DIR/unary_precedence.rs:17:13
70+
|
71+
LL | let _ = -1.0_f64.cos().cos();
72+
| ^^^^^^^^^^^^^^^^^^^^
73+
|
74+
help: use parentheses to clarify your intent
75+
|
76+
LL | let _ = -(1.0_f64.cos().cos());
77+
| + +
78+
79+
error: unary operator `-` has lower precedence than method calls which might be unexpected
80+
--> $DIR/unary_precedence.rs:19:13
81+
|
82+
LL | let _ = -1.0_f64.cos().sin();
83+
| ^^^^^^^^^^^^^^^^^^^^
84+
|
85+
help: use parentheses to clarify your intent
86+
|
87+
LL | let _ = -(1.0_f64.cos().sin());
88+
| + +
89+
90+
error: unary operator `-` has lower precedence than method calls which might be unexpected
91+
--> $DIR/unary_precedence.rs:21:13
92+
|
93+
LL | let _ = -1.0_f64.sin().cos();
94+
| ^^^^^^^^^^^^^^^^^^^^
95+
|
96+
help: use parentheses to clarify your intent
97+
|
98+
LL | let _ = -(1.0_f64.sin().cos());
99+
| + +
100+
101+
error: unary operator `-` has lower precedence than method calls which might be unexpected
102+
--> $DIR/unary_precedence.rs:23:13
103+
|
104+
LL | let _ = -1f64.sin().sin();
105+
| ^^^^^^^^^^^^^^^^^
106+
|
107+
help: use parentheses to clarify your intent
108+
|
109+
LL | let _ = -(1f64.sin().sin());
110+
| + +
111+
112+
error: unary operator `-` has lower precedence than method calls which might be unexpected
113+
--> $DIR/unary_precedence.rs:26:11
114+
|
115+
LL | dbg!( -1.0_f32.cos() );
116+
| ^^^^^^^^^^^^^^
117+
|
118+
help: use parentheses to clarify your intent
119+
|
120+
LL | dbg!( -(1.0_f32.cos()) );
121+
| + +
122+
123+
error: aborting due to 11 previous errors
124+

0 commit comments

Comments
 (0)