Skip to content

Commit 7946ec0

Browse files
committed
Add warn-by-default lint against pointer trait comparisons
1 parent 287ae4d commit 7946ec0

File tree

5 files changed

+249
-6
lines changed

5 files changed

+249
-6
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_invalid_pointer_trait_comparisons = incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
319+
.addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses
320+
.addr_suggestion = convert to untyped pointers so that only their addresses are compared
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_invalid_pointer_trait_comparisons)]
1590+
pub struct InvalidPointerTraitComparisons {
1591+
#[subdiagnostic]
1592+
pub addr_metadata_suggestion: Option<InvalidPointerTraitComparisonsAddrMetadataSuggestion>,
1593+
#[subdiagnostic]
1594+
pub addr_suggestion: InvalidPointerTraitComparisonsAddrSuggestion,
1595+
}
1596+
1597+
#[derive(Subdiagnostic)]
1598+
#[multipart_suggestion(
1599+
lint_addr_metadata_suggestion,
1600+
style = "verbose",
1601+
applicability = "machine-applicable"
1602+
)]
1603+
pub struct InvalidPointerTraitComparisonsAddrMetadataSuggestion {
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 InvalidPointerTraitComparisonsAddrSuggestion {
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

+90-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ use crate::{
33
lints::{
44
AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
55
InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
6-
OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
7-
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
8-
OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
9-
VariantSizeDifferencesDiag,
6+
InvalidPointerTraitComparisons, InvalidPointerTraitComparisonsAddrMetadataSuggestion,
7+
InvalidPointerTraitComparisonsAddrSuggestion, OnlyCastu8ToChar, OverflowingBinHex,
8+
OverflowingBinHexSign, OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt,
9+
OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange,
10+
UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag,
1011
},
1112
};
1213
use crate::{LateContext, LateLintPass, LintContext};
@@ -17,10 +18,10 @@ use rustc_errors::DiagnosticMessage;
1718
use rustc_hir as hir;
1819
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
1920
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
20-
use rustc_middle::ty::GenericArgsRef;
2121
use rustc_middle::ty::{
2222
self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
2323
};
24+
use rustc_middle::ty::{GenericArgsRef, TypeAndMut};
2425
use rustc_span::def_id::LocalDefId;
2526
use rustc_span::source_map;
2627
use rustc_span::symbol::sym;
@@ -136,6 +137,37 @@ declare_lint! {
136137
"detects invalid floating point NaN comparisons"
137138
}
138139

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

147-
impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
179+
impl_lint_pass!(TypeLimits => [
180+
UNUSED_COMPARISONS,
181+
OVERFLOWING_LITERALS,
182+
INVALID_NAN_COMPARISONS,
183+
INVALID_POINTER_TRAIT_COMPARISONS
184+
]);
148185

149186
impl TypeLimits {
150187
pub fn new() -> TypeLimits {
@@ -620,6 +657,52 @@ fn lint_nan<'tcx>(
620657
cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint);
621658
}
622659

660+
fn lint_pointer_trait<'tcx>(
661+
cx: &LateContext<'tcx>,
662+
e: &'tcx hir::Expr<'tcx>,
663+
binop: hir::BinOp,
664+
l: &'tcx hir::Expr<'tcx>,
665+
r: &'tcx hir::Expr<'tcx>,
666+
) {
667+
let Some(l_ty) = cx.typeck_results().expr_ty_adjusted_opt(l) else {
668+
return;
669+
};
670+
let Some(r_ty) = cx.typeck_results().expr_ty_adjusted_opt(r) else {
671+
return;
672+
};
673+
674+
fn is_ptr_trait(ty: Ty<'_>) -> bool {
675+
match ty.kind() {
676+
ty::RawPtr(TypeAndMut { mutbl: _, ty }) => {
677+
matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))
678+
}
679+
_ => false,
680+
}
681+
}
682+
683+
if !is_ptr_trait(l_ty) || !is_ptr_trait(r_ty) {
684+
return;
685+
}
686+
687+
cx.emit_spanned_lint(
688+
INVALID_POINTER_TRAIT_COMPARISONS,
689+
e.span,
690+
InvalidPointerTraitComparisons {
691+
addr_metadata_suggestion: matches!(binop.node, hir::BinOpKind::Eq | hir::BinOpKind::Ne)
692+
.then(|| InvalidPointerTraitComparisonsAddrMetadataSuggestion {
693+
left_ne: (binop.node == hir::BinOpKind::Ne).then(|| l.span.shrink_to_lo()),
694+
left_eq: (binop.node != hir::BinOpKind::Ne).then(|| l.span.shrink_to_lo()),
695+
middle: l.span.shrink_to_hi().until(r.span.shrink_to_lo()),
696+
right: r.span.shrink_to_hi(),
697+
}),
698+
addr_suggestion: InvalidPointerTraitComparisonsAddrSuggestion {
699+
left: l.span.shrink_to_hi(),
700+
right: r.span.shrink_to_hi(),
701+
},
702+
},
703+
);
704+
}
705+
623706
impl<'tcx> LateLintPass<'tcx> for TypeLimits {
624707
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
625708
match e.kind {
@@ -636,6 +719,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
636719
cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
637720
} else {
638721
lint_nan(cx, e, binop, l, r);
722+
lint_pointer_trait(cx, e, binop, l, r);
639723
}
640724
}
641725
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 incorrect pointer trait comparison
20+
let _ = a != b;
21+
//~^ WARN incorrect pointer trait comparison
22+
let _ = a < b;
23+
//~^ WARN incorrect pointer trait comparison
24+
let _ = a <= b;
25+
//~^ WARN incorrect pointer trait comparison
26+
let _ = a > b;
27+
//~^ WARN incorrect pointer trait comparison
28+
let _ = a >= b;
29+
//~^ WARN incorrect pointer trait comparison
30+
31+
let _ = std::ptr::eq(a, b);
32+
let _ = a as *const () == b as *const ();
33+
34+
let a: Rc<dyn std::fmt::Debug> = Rc::new(1);
35+
Rc::ptr_eq(&a, &a);
36+
37+
let a: Arc<dyn std::fmt::Debug> = Arc::new(1);
38+
Arc::ptr_eq(&a, &a);
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
warning: incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
2+
--> $DIR/pointer_trait_comparisons.rs:18:13
3+
|
4+
LL | let _ = a == b;
5+
| ^^^^^^
6+
|
7+
= note: `#[warn(invalid_pointer_trait_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: convert to untyped pointers so that only their addresses are compared
13+
|
14+
LL | let _ = a as *const () == b as *const ();
15+
| ++++++++++++ ++++++++++++
16+
17+
warning: incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
18+
--> $DIR/pointer_trait_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: convert to untyped pointers so that only their addresses are compared
28+
|
29+
LL | let _ = a as *const () != b as *const ();
30+
| ++++++++++++ ++++++++++++
31+
32+
warning: incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
33+
--> $DIR/pointer_trait_comparisons.rs:22:13
34+
|
35+
LL | let _ = a < b;
36+
| ^^^^^
37+
|
38+
help: convert to untyped pointers so that only their addresses are compared
39+
|
40+
LL | let _ = a as *const () < b as *const ();
41+
| ++++++++++++ ++++++++++++
42+
43+
warning: incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
44+
--> $DIR/pointer_trait_comparisons.rs:24:13
45+
|
46+
LL | let _ = a <= b;
47+
| ^^^^^^
48+
|
49+
help: convert to untyped pointers so that only their addresses are compared
50+
|
51+
LL | let _ = a as *const () <= b as *const ();
52+
| ++++++++++++ ++++++++++++
53+
54+
warning: incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
55+
--> $DIR/pointer_trait_comparisons.rs:26:13
56+
|
57+
LL | let _ = a > b;
58+
| ^^^^^
59+
|
60+
help: convert to untyped pointers so that only their addresses are compared
61+
|
62+
LL | let _ = a as *const () > b as *const ();
63+
| ++++++++++++ ++++++++++++
64+
65+
warning: incorrect pointer trait comparison, the comparison include metadata which is not guaranteed to be equivalent
66+
--> $DIR/pointer_trait_comparisons.rs:28:13
67+
|
68+
LL | let _ = a >= b;
69+
| ^^^^^^
70+
|
71+
help: convert to untyped pointers so that only their addresses are compared
72+
|
73+
LL | let _ = a as *const () >= b as *const ();
74+
| ++++++++++++ ++++++++++++
75+
76+
warning: 6 warnings emitted
77+

0 commit comments

Comments
 (0)