11use crate :: {
22 fluent_generated as fluent,
33 lints:: {
4- AtomicOrderingFence , AtomicOrderingLoad , AtomicOrderingStore , ImproperCTypes ,
5- InvalidAtomicOrderingDiag , InvalidNanComparisons , InvalidNanComparisonsSuggestion ,
6- OnlyCastu8ToChar , OverflowingBinHex , OverflowingBinHexSign , OverflowingBinHexSignBitSub ,
7- OverflowingBinHexSub , OverflowingInt , OverflowingIntHelp , OverflowingLiteral ,
8- OverflowingUInt , RangeEndpointOutOfRange , UnusedComparisons , UseInclusiveRange ,
9- VariantSizeDifferencesDiag ,
4+ AmbiguousWidePointerComparisons , AmbiguousWidePointerComparisonsAddrMetadataSuggestion ,
5+ AmbiguousWidePointerComparisonsAddrSuggestion , AtomicOrderingFence , AtomicOrderingLoad ,
6+ AtomicOrderingStore , ImproperCTypes , InvalidAtomicOrderingDiag , InvalidNanComparisons ,
7+ InvalidNanComparisonsSuggestion , OnlyCastu8ToChar , OverflowingBinHex ,
8+ OverflowingBinHexSign , OverflowingBinHexSignBitSub , OverflowingBinHexSub , OverflowingInt ,
9+ OverflowingIntHelp , OverflowingLiteral , OverflowingUInt , RangeEndpointOutOfRange ,
10+ UnusedComparisons , UseInclusiveRange , VariantSizeDifferencesDiag ,
1011 } ,
1112} ;
1213use crate :: { LateContext , LateLintPass , LintContext } ;
@@ -17,17 +18,18 @@ use rustc_errors::DiagnosticMessage;
1718use rustc_hir as hir;
1819use rustc_hir:: { is_range_literal, Expr , ExprKind , Node } ;
1920use rustc_middle:: ty:: layout:: { IntegerExt , LayoutOf , SizeSkeleton } ;
20- use rustc_middle:: ty:: GenericArgsRef ;
2121use rustc_middle:: ty:: {
2222 self , AdtKind , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitableExt ,
2323} ;
24+ use rustc_middle:: ty:: { GenericArgsRef , TypeAndMut } ;
2425use rustc_span:: def_id:: LocalDefId ;
2526use rustc_span:: source_map;
2627use rustc_span:: symbol:: sym;
2728use rustc_span:: { Span , Symbol } ;
2829use rustc_target:: abi:: { Abi , Size , WrappingRange } ;
2930use rustc_target:: abi:: { Integer , TagEncoding , Variants } ;
3031use rustc_target:: spec:: abi:: Abi as SpecAbi ;
32+ use rustc_type_ir:: DynKind ;
3133
3234use std:: iter;
3335use std:: ops:: ControlFlow ;
@@ -136,6 +138,37 @@ declare_lint! {
136138 "detects invalid floating point NaN comparisons"
137139}
138140
141+ declare_lint ! {
142+ /// The `ambiguous_wide_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 includes metadata which may not be expected.
167+ AMBIGUOUS_WIDE_POINTER_COMPARISONS ,
168+ Warn ,
169+ "detects ambiguous wide pointer comparisons"
170+ }
171+
139172#[ derive( Copy , Clone ) ]
140173pub 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+ AMBIGUOUS_WIDE_POINTER_COMPARISONS
185+ ] ) ;
148186
149187impl TypeLimits {
150188 pub fn new ( ) -> TypeLimits {
@@ -620,6 +658,106 @@ fn lint_nan<'tcx>(
620658 cx. emit_spanned_lint ( INVALID_NAN_COMPARISONS , e. span , lint) ;
621659}
622660
661+ fn lint_wide_pointer < ' tcx > (
662+ cx : & LateContext < ' tcx > ,
663+ e : & ' tcx hir:: Expr < ' tcx > ,
664+ binop : hir:: BinOpKind ,
665+ l : & ' tcx hir:: Expr < ' tcx > ,
666+ r : & ' tcx hir:: Expr < ' tcx > ,
667+ ) {
668+ let ptr_unsized = |mut ty : Ty < ' tcx > | -> Option < ( usize , bool ) > {
669+ let mut refs = 0 ;
670+ // here we remove any "implicit" references and count the number
671+ // of them to correctly suggest the right number of deref
672+ while let ty:: Ref ( _, inner_ty, _) = ty. kind ( ) {
673+ ty = * inner_ty;
674+ refs += 1 ;
675+ }
676+ match ty. kind ( ) {
677+ ty:: RawPtr ( TypeAndMut { mutbl : _, ty } ) => ( !ty. is_sized ( cx. tcx , cx. param_env ) )
678+ . then ( || ( refs, matches ! ( ty. kind( ) , ty:: Dynamic ( _, _, DynKind :: Dyn ) ) ) ) ,
679+ _ => None ,
680+ }
681+ } ;
682+
683+ // PartialEq::{eq,ne} takes references, remove any explicit references
684+ let l = l. peel_borrows ( ) ;
685+ let r = r. peel_borrows ( ) ;
686+
687+ let Some ( l_ty) = cx. typeck_results ( ) . expr_ty_opt ( l) else {
688+ return ;
689+ } ;
690+ let Some ( r_ty) = cx. typeck_results ( ) . expr_ty_opt ( r) else {
691+ return ;
692+ } ;
693+
694+ let Some ( ( l_ty_refs, l_inner_ty_is_dyn) ) = ptr_unsized ( l_ty) else {
695+ return ;
696+ } ;
697+ let Some ( ( r_ty_refs, r_inner_ty_is_dyn) ) = ptr_unsized ( r_ty) else {
698+ return ;
699+ } ;
700+
701+ let ( Some ( l_span) , Some ( r_span) ) =
702+ ( l. span . find_ancestor_inside ( e. span ) , r. span . find_ancestor_inside ( e. span ) )
703+ else {
704+ return cx. emit_spanned_lint (
705+ AMBIGUOUS_WIDE_POINTER_COMPARISONS ,
706+ e. span ,
707+ AmbiguousWidePointerComparisons :: Spanless ,
708+ ) ;
709+ } ;
710+
711+ let ne = if binop == hir:: BinOpKind :: Ne { "!" } else { "" } ;
712+ let is_eq_ne = matches ! ( binop, hir:: BinOpKind :: Eq | hir:: BinOpKind :: Ne ) ;
713+ let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn;
714+
715+ let left = e. span . shrink_to_lo ( ) . until ( l_span. shrink_to_lo ( ) ) ;
716+ let middle = l_span. shrink_to_hi ( ) . until ( r_span. shrink_to_lo ( ) ) ;
717+ let right = r_span. shrink_to_hi ( ) . until ( e. span . shrink_to_hi ( ) ) ;
718+
719+ let deref_left = & * "*" . repeat ( l_ty_refs) ;
720+ let deref_right = & * "*" . repeat ( r_ty_refs) ;
721+
722+ cx. emit_spanned_lint (
723+ AMBIGUOUS_WIDE_POINTER_COMPARISONS ,
724+ e. span ,
725+ AmbiguousWidePointerComparisons :: Spanful {
726+ addr_metadata_suggestion : ( is_eq_ne && !is_dyn_comparison) . then ( || {
727+ AmbiguousWidePointerComparisonsAddrMetadataSuggestion {
728+ ne,
729+ deref_left,
730+ deref_right,
731+ left,
732+ middle,
733+ right,
734+ }
735+ } ) ,
736+ addr_suggestion : if is_eq_ne {
737+ AmbiguousWidePointerComparisonsAddrSuggestion :: AddrEq {
738+ ne,
739+ deref_left,
740+ deref_right,
741+ left,
742+ middle,
743+ right,
744+ }
745+ } else {
746+ AmbiguousWidePointerComparisonsAddrSuggestion :: Cast {
747+ deref_left,
748+ deref_right,
749+ // those two Options are required for correctness as having
750+ // an empty span and an empty suggestion is not permitted
751+ left_before : ( l_ty_refs != 0 ) . then_some ( left) ,
752+ right_before : ( r_ty_refs != 0 ) . then ( || r_span. shrink_to_lo ( ) ) ,
753+ left : l_span. shrink_to_hi ( ) ,
754+ right,
755+ }
756+ } ,
757+ } ,
758+ ) ;
759+ }
760+
623761impl < ' tcx > LateLintPass < ' tcx > for TypeLimits {
624762 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx hir:: Expr < ' tcx > ) {
625763 match e. kind {
@@ -636,10 +774,26 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
636774 cx. emit_spanned_lint ( UNUSED_COMPARISONS , e. span , UnusedComparisons ) ;
637775 } else {
638776 lint_nan ( cx, e, binop, l, r) ;
777+ lint_wide_pointer ( cx, e, binop. node , l, r) ;
639778 }
640779 }
641780 }
642781 hir:: ExprKind :: Lit ( lit) => lint_literal ( cx, self , e, lit) ,
782+ hir:: ExprKind :: Call ( path, [ l, r] )
783+ if let ExprKind :: Path ( ref qpath) = path. kind
784+ && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
785+ && let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
786+ && let Some ( binop) = partialeq_binop ( diag_item) =>
787+ {
788+ lint_wide_pointer ( cx, e, binop, l, r) ;
789+ }
790+ hir:: ExprKind :: MethodCall ( _, l, [ r] , _)
791+ if let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id )
792+ && let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
793+ && let Some ( binop) = partialeq_binop ( diag_item) =>
794+ {
795+ lint_wide_pointer ( cx, e, binop, l, r) ;
796+ }
643797 _ => { }
644798 } ;
645799
@@ -722,6 +876,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
722876 | hir:: BinOpKind :: Gt
723877 )
724878 }
879+
880+ fn partialeq_binop ( diag_item : Symbol ) -> Option < hir:: BinOpKind > {
881+ if diag_item == sym:: cmp_partialeq_eq {
882+ Some ( hir:: BinOpKind :: Eq )
883+ } else if diag_item == sym:: cmp_partialeq_ne {
884+ Some ( hir:: BinOpKind :: Ne )
885+ } else {
886+ None
887+ }
888+ }
725889 }
726890}
727891
0 commit comments