Skip to content

Commit 8576cfa

Browse files
committed
Add warn-by-default lint against unclear fat pointer comparisons
1 parent 287ae4d commit 8576cfa

File tree

5 files changed

+303
-3
lines changed

5 files changed

+303
-3
lines changed

compiler/rustc_lint/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir
315315
316316
lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable
317317
318+
lint_unclear_fat_pointer_comparisons = unclear fat pointer comparison, the comparison includes metadata which may not be expected
319+
.addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses
320+
.addr_suggestion = use untyped pointers to only compare their addresses
321+
318322
lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
319323
.label = casting happend here
320324

compiler/rustc_lint/src/lints.rs

+39
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,45 @@ pub enum InvalidNanComparisonsSuggestion {
15851585
Spanless,
15861586
}
15871587

1588+
#[derive(LintDiagnostic)]
1589+
#[diag(lint_unclear_fat_pointer_comparisons)]
1590+
pub struct UnclearFatPointerComparisons {
1591+
#[subdiagnostic]
1592+
pub addr_metadata_suggestion: Option<UnclearFatPointerComparisonsAddrMetadataSuggestion>,
1593+
#[subdiagnostic]
1594+
pub addr_suggestion: UnclearFatPointerComparisonsAddrSuggestion,
1595+
}
1596+
1597+
#[derive(Subdiagnostic)]
1598+
#[multipart_suggestion(
1599+
lint_addr_metadata_suggestion,
1600+
style = "verbose",
1601+
applicability = "machine-applicable"
1602+
)]
1603+
pub struct UnclearFatPointerComparisonsAddrMetadataSuggestion {
1604+
#[suggestion_part(code = "!std::ptr::eq(")]
1605+
pub left_ne: Option<Span>,
1606+
#[suggestion_part(code = "std::ptr::eq(")]
1607+
pub left_eq: Option<Span>,
1608+
#[suggestion_part(code = ", ")]
1609+
pub middle: Span,
1610+
#[suggestion_part(code = ")")]
1611+
pub right: Span,
1612+
}
1613+
1614+
#[derive(Subdiagnostic)]
1615+
#[multipart_suggestion(
1616+
lint_addr_suggestion,
1617+
style = "verbose",
1618+
applicability = "machine-applicable"
1619+
)]
1620+
pub struct UnclearFatPointerComparisonsAddrSuggestion {
1621+
#[suggestion_part(code = " as *const ()")]
1622+
pub left: Span,
1623+
#[suggestion_part(code = " as *const ()")]
1624+
pub right: Span,
1625+
}
1626+
15881627
pub struct ImproperCTypes<'a> {
15891628
pub ty: Ty<'a>,
15901629
pub desc: &'a str,

compiler/rustc_lint/src/types.rs

+86-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use crate::{
55
InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
66
OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
77
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
8-
OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
8+
OverflowingUInt, RangeEndpointOutOfRange, UnclearFatPointerComparisons,
9+
UnclearFatPointerComparisonsAddrMetadataSuggestion,
10+
UnclearFatPointerComparisonsAddrSuggestion, UnusedComparisons, UseInclusiveRange,
911
VariantSizeDifferencesDiag,
1012
},
1113
};
@@ -17,10 +19,10 @@ use rustc_errors::DiagnosticMessage;
1719
use rustc_hir as hir;
1820
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
1921
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
20-
use rustc_middle::ty::GenericArgsRef;
2122
use rustc_middle::ty::{
2223
self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
2324
};
25+
use rustc_middle::ty::{GenericArgsRef, TypeAndMut};
2426
use rustc_span::def_id::LocalDefId;
2527
use rustc_span::source_map;
2628
use rustc_span::symbol::sym;
@@ -136,6 +138,37 @@ declare_lint! {
136138
"detects invalid floating point NaN comparisons"
137139
}
138140

141+
declare_lint! {
142+
/// The `unclear_fat_pointer_comparisons` lint checks comparison
143+
/// of `*const/*mut ?Sized` as the operands.
144+
///
145+
/// ### Example
146+
///
147+
/// ```rust
148+
/// # struct A;
149+
/// # struct B;
150+
///
151+
/// # trait T {}
152+
/// # impl T for A {}
153+
/// # impl T for B {}
154+
///
155+
/// let ab = (A, B);
156+
/// let a = &ab.0 as *const dyn T;
157+
/// let b = &ab.1 as *const dyn T;
158+
///
159+
/// let _ = a == b;
160+
/// ```
161+
///
162+
/// {{produces}}
163+
///
164+
/// ### Explanation
165+
///
166+
/// The comparison include metadata which is not guaranteed to be equivalent.
167+
UNCLEAR_FAT_POINTER_COMPARISONS,
168+
Warn,
169+
"detects unclear fat pointer comparisons"
170+
}
171+
139172
#[derive(Copy, Clone)]
140173
pub struct TypeLimits {
141174
/// Id of the last visited negated expression
@@ -144,7 +177,12 @@ pub struct TypeLimits {
144177
negated_expr_span: Option<Span>,
145178
}
146179

147-
impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
180+
impl_lint_pass!(TypeLimits => [
181+
UNUSED_COMPARISONS,
182+
OVERFLOWING_LITERALS,
183+
INVALID_NAN_COMPARISONS,
184+
UNCLEAR_FAT_POINTER_COMPARISONS
185+
]);
148186

149187
impl TypeLimits {
150188
pub fn new() -> TypeLimits {
@@ -620,6 +658,50 @@ fn lint_nan<'tcx>(
620658
cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint);
621659
}
622660

661+
fn lint_fat_pointer<'tcx>(
662+
cx: &LateContext<'tcx>,
663+
e: &'tcx hir::Expr<'tcx>,
664+
binop: hir::BinOp,
665+
l: &'tcx hir::Expr<'tcx>,
666+
r: &'tcx hir::Expr<'tcx>,
667+
) {
668+
let Some(l_ty) = cx.typeck_results().expr_ty_adjusted_opt(l) else {
669+
return;
670+
};
671+
let Some(r_ty) = cx.typeck_results().expr_ty_adjusted_opt(r) else {
672+
return;
673+
};
674+
675+
let is_ptr_unsized = |ty: Ty<'tcx>| -> bool {
676+
match ty.kind() {
677+
ty::RawPtr(TypeAndMut { mutbl: _, ty }) => !ty.is_sized(cx.tcx, cx.param_env),
678+
_ => false,
679+
}
680+
};
681+
682+
if !is_ptr_unsized(l_ty) || !is_ptr_unsized(r_ty) {
683+
return;
684+
}
685+
686+
cx.emit_spanned_lint(
687+
UNCLEAR_FAT_POINTER_COMPARISONS,
688+
e.span,
689+
UnclearFatPointerComparisons {
690+
addr_metadata_suggestion: matches!(binop.node, hir::BinOpKind::Eq | hir::BinOpKind::Ne)
691+
.then(|| UnclearFatPointerComparisonsAddrMetadataSuggestion {
692+
left_ne: (binop.node == hir::BinOpKind::Ne).then(|| l.span.shrink_to_lo()),
693+
left_eq: (binop.node != hir::BinOpKind::Ne).then(|| l.span.shrink_to_lo()),
694+
middle: l.span.shrink_to_hi().until(r.span.shrink_to_lo()),
695+
right: r.span.shrink_to_hi(),
696+
}),
697+
addr_suggestion: UnclearFatPointerComparisonsAddrSuggestion {
698+
left: l.span.shrink_to_hi(),
699+
right: r.span.shrink_to_hi(),
700+
},
701+
},
702+
);
703+
}
704+
623705
impl<'tcx> LateLintPass<'tcx> for TypeLimits {
624706
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
625707
match e.kind {
@@ -636,6 +718,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
636718
cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
637719
} else {
638720
lint_nan(cx, e, binop, l, r);
721+
lint_fat_pointer(cx, e, binop, l, r);
639722
}
640723
}
641724
}
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// check-pass
2+
3+
use std::rc::Rc;
4+
use std::sync::Arc;
5+
6+
struct A;
7+
struct B;
8+
9+
trait T {}
10+
impl T for A {}
11+
impl T for B {}
12+
13+
fn main() {
14+
let ab = (A, B);
15+
let a = &ab.0 as *const dyn T;
16+
let b = &ab.1 as *const dyn T;
17+
18+
let _ = a == b;
19+
//~^ WARN unclear fat pointer comparison
20+
let _ = a != b;
21+
//~^ WARN unclear fat pointer comparison
22+
let _ = a < b;
23+
//~^ WARN unclear fat pointer comparison
24+
let _ = a <= b;
25+
//~^ WARN unclear fat pointer comparison
26+
let _ = a > b;
27+
//~^ WARN unclear fat pointer comparison
28+
let _ = a >= b;
29+
//~^ WARN unclear fat pointer comparison
30+
31+
let s = "" as *const str;
32+
let _ = s == s;
33+
//~^ WARN unclear fat pointer comparison
34+
35+
let s = &[8, 7][..] as *const [i32];
36+
let _ = s == s;
37+
//~^ WARN unclear fat pointer comparison
38+
39+
fn cmp<T: ?Sized>(a: *mut T, b: *mut T) -> bool {
40+
a == b
41+
//~^ WARN unclear fat pointer comparison
42+
}
43+
44+
let _ = std::ptr::eq(a, b);
45+
let _ = a as *const () == b as *const ();
46+
47+
let a: Rc<dyn std::fmt::Debug> = Rc::new(1);
48+
Rc::ptr_eq(&a, &a);
49+
50+
let a: Arc<dyn std::fmt::Debug> = Arc::new(1);
51+
Arc::ptr_eq(&a, &a);
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
2+
--> $DIR/fat_pointer_comparisons.rs:18:13
3+
|
4+
LL | let _ = a == b;
5+
| ^^^^^^
6+
|
7+
= note: `#[warn(unclear_fat_pointer_comparisons)]` on by default
8+
help: use explicit `std::ptr::eq` method to compare metadata and addresses
9+
|
10+
LL | let _ = std::ptr::eq(a, b);
11+
| +++++++++++++ ~ +
12+
help: use untyped pointers to only compare their addresses
13+
|
14+
LL | let _ = a as *const () == b as *const ();
15+
| ++++++++++++ ++++++++++++
16+
17+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
18+
--> $DIR/fat_pointer_comparisons.rs:20:13
19+
|
20+
LL | let _ = a != b;
21+
| ^^^^^^
22+
|
23+
help: use explicit `std::ptr::eq` method to compare metadata and addresses
24+
|
25+
LL | let _ = !std::ptr::eq(a, b);
26+
| ++++++++++++++ ~ +
27+
help: use untyped pointers to only compare their addresses
28+
|
29+
LL | let _ = a as *const () != b as *const ();
30+
| ++++++++++++ ++++++++++++
31+
32+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
33+
--> $DIR/fat_pointer_comparisons.rs:22:13
34+
|
35+
LL | let _ = a < b;
36+
| ^^^^^
37+
|
38+
help: use untyped pointers to only compare their addresses
39+
|
40+
LL | let _ = a as *const () < b as *const ();
41+
| ++++++++++++ ++++++++++++
42+
43+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
44+
--> $DIR/fat_pointer_comparisons.rs:24:13
45+
|
46+
LL | let _ = a <= b;
47+
| ^^^^^^
48+
|
49+
help: use untyped pointers to only compare their addresses
50+
|
51+
LL | let _ = a as *const () <= b as *const ();
52+
| ++++++++++++ ++++++++++++
53+
54+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
55+
--> $DIR/fat_pointer_comparisons.rs:26:13
56+
|
57+
LL | let _ = a > b;
58+
| ^^^^^
59+
|
60+
help: use untyped pointers to only compare their addresses
61+
|
62+
LL | let _ = a as *const () > b as *const ();
63+
| ++++++++++++ ++++++++++++
64+
65+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
66+
--> $DIR/fat_pointer_comparisons.rs:28:13
67+
|
68+
LL | let _ = a >= b;
69+
| ^^^^^^
70+
|
71+
help: use untyped pointers to only compare their addresses
72+
|
73+
LL | let _ = a as *const () >= b as *const ();
74+
| ++++++++++++ ++++++++++++
75+
76+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
77+
--> $DIR/fat_pointer_comparisons.rs:32:13
78+
|
79+
LL | let _ = s == s;
80+
| ^^^^^^
81+
|
82+
help: use explicit `std::ptr::eq` method to compare metadata and addresses
83+
|
84+
LL | let _ = std::ptr::eq(s, s);
85+
| +++++++++++++ ~ +
86+
help: use untyped pointers to only compare their addresses
87+
|
88+
LL | let _ = s as *const () == s as *const ();
89+
| ++++++++++++ ++++++++++++
90+
91+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
92+
--> $DIR/fat_pointer_comparisons.rs:36:13
93+
|
94+
LL | let _ = s == s;
95+
| ^^^^^^
96+
|
97+
help: use explicit `std::ptr::eq` method to compare metadata and addresses
98+
|
99+
LL | let _ = std::ptr::eq(s, s);
100+
| +++++++++++++ ~ +
101+
help: use untyped pointers to only compare their addresses
102+
|
103+
LL | let _ = s as *const () == s as *const ();
104+
| ++++++++++++ ++++++++++++
105+
106+
warning: unclear fat pointer comparison, the comparison includes metadata which may not be expected
107+
--> $DIR/fat_pointer_comparisons.rs:40:9
108+
|
109+
LL | a == b
110+
| ^^^^^^
111+
|
112+
help: use explicit `std::ptr::eq` method to compare metadata and addresses
113+
|
114+
LL | std::ptr::eq(a, b)
115+
| +++++++++++++ ~ +
116+
help: use untyped pointers to only compare their addresses
117+
|
118+
LL | a as *const () == b as *const ()
119+
| ++++++++++++ ++++++++++++
120+
121+
warning: 9 warnings emitted
122+

0 commit comments

Comments
 (0)