@@ -23,7 +23,9 @@ use crate::lints::{
23
23
AmbiguousWidePointerComparisons , AmbiguousWidePointerComparisonsAddrMetadataSuggestion ,
24
24
AmbiguousWidePointerComparisonsAddrSuggestion , AtomicOrderingFence , AtomicOrderingLoad ,
25
25
AtomicOrderingStore , ImproperCTypes , InvalidAtomicOrderingDiag , InvalidNanComparisons ,
26
- InvalidNanComparisonsSuggestion , UnusedComparisons , VariantSizeDifferencesDiag ,
26
+ InvalidNanComparisonsSuggestion , UnpredictableFunctionPointerComparisons ,
27
+ UnpredictableFunctionPointerComparisonsSuggestion , UnusedComparisons ,
28
+ VariantSizeDifferencesDiag ,
27
29
} ;
28
30
use crate :: { LateContext , LateLintPass , LintContext , fluent_generated as fluent} ;
29
31
@@ -166,6 +168,35 @@ declare_lint! {
166
168
"detects ambiguous wide pointer comparisons"
167
169
}
168
170
171
+ declare_lint ! {
172
+ /// The `unpredictable_function_pointer_comparisons` lint checks comparison
173
+ /// of function pointer as the operands.
174
+ ///
175
+ /// ### Example
176
+ ///
177
+ /// ```rust
178
+ /// fn a() {}
179
+ /// fn b() {}
180
+ ///
181
+ /// let f: fn() = a;
182
+ /// let g: fn() = b;
183
+ ///
184
+ /// let _ = f == g;
185
+ /// ```
186
+ ///
187
+ /// {{produces}}
188
+ ///
189
+ /// ### Explanation
190
+ ///
191
+ /// Function pointers comparisons do not produce meaningful result since
192
+ /// they are never guaranteed to be unique and could vary between different
193
+ /// code generation units. Furthermore, different functions could have the
194
+ /// same address after being merged together.
195
+ UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS ,
196
+ Warn ,
197
+ "detects unpredictable function pointer comparisons"
198
+ }
199
+
169
200
#[ derive( Copy , Clone , Default ) ]
170
201
pub ( crate ) struct TypeLimits {
171
202
/// Id of the last visited negated expression
@@ -178,7 +209,8 @@ impl_lint_pass!(TypeLimits => [
178
209
UNUSED_COMPARISONS ,
179
210
OVERFLOWING_LITERALS ,
180
211
INVALID_NAN_COMPARISONS ,
181
- AMBIGUOUS_WIDE_POINTER_COMPARISONS
212
+ AMBIGUOUS_WIDE_POINTER_COMPARISONS ,
213
+ UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
182
214
] ) ;
183
215
184
216
impl TypeLimits {
@@ -255,7 +287,7 @@ fn lint_nan<'tcx>(
255
287
cx. emit_span_lint ( INVALID_NAN_COMPARISONS , e. span , lint) ;
256
288
}
257
289
258
- #[ derive( Debug , PartialEq ) ]
290
+ #[ derive( Debug , PartialEq , Copy , Clone ) ]
259
291
enum ComparisonOp {
260
292
BinOp ( hir:: BinOpKind ) ,
261
293
Other ,
@@ -383,6 +415,100 @@ fn lint_wide_pointer<'tcx>(
383
415
) ;
384
416
}
385
417
418
+ fn lint_fn_pointer < ' tcx > (
419
+ cx : & LateContext < ' tcx > ,
420
+ e : & ' tcx hir:: Expr < ' tcx > ,
421
+ cmpop : ComparisonOp ,
422
+ l : & ' tcx hir:: Expr < ' tcx > ,
423
+ r : & ' tcx hir:: Expr < ' tcx > ,
424
+ ) {
425
+ let peel_refs = |mut ty : Ty < ' tcx > | -> ( Ty < ' tcx > , usize ) {
426
+ let mut refs = 0 ;
427
+
428
+ while let ty:: Ref ( _, inner_ty, _) = ty. kind ( ) {
429
+ ty = * inner_ty;
430
+ refs += 1 ;
431
+ }
432
+
433
+ ( ty, refs)
434
+ } ;
435
+
436
+ // Left and right operands can have borrows, remove them
437
+ let l = l. peel_borrows ( ) ;
438
+ let r = r. peel_borrows ( ) ;
439
+
440
+ let Some ( l_ty) = cx. typeck_results ( ) . expr_ty_opt ( l) else { return } ;
441
+ let Some ( r_ty) = cx. typeck_results ( ) . expr_ty_opt ( r) else { return } ;
442
+
443
+ // Remove any references as `==` will deref through them (and count the
444
+ // number of references removed, for latter).
445
+ let ( l_ty, l_ty_refs) = peel_refs ( l_ty) ;
446
+ let ( r_ty, r_ty_refs) = peel_refs ( r_ty) ;
447
+
448
+ if !l_ty. is_fn ( ) || !r_ty. is_fn ( ) {
449
+ return ;
450
+ }
451
+
452
+ // Let's try to suggest `ptr::fn_addr_eq` if/when possible.
453
+
454
+ let is_eq_ne = matches ! ( cmpop, ComparisonOp :: BinOp ( hir:: BinOpKind :: Eq | hir:: BinOpKind :: Ne ) ) ;
455
+
456
+ if !is_eq_ne {
457
+ // Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning.
458
+ return cx. emit_span_lint (
459
+ UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS ,
460
+ e. span ,
461
+ UnpredictableFunctionPointerComparisons :: Warn ,
462
+ ) ;
463
+ }
464
+
465
+ let ( Some ( l_span) , Some ( r_span) ) =
466
+ ( l. span . find_ancestor_inside ( e. span ) , r. span . find_ancestor_inside ( e. span ) )
467
+ else {
468
+ // No appropriate spans for the left and right operands, just show the warning.
469
+ return cx. emit_span_lint (
470
+ UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS ,
471
+ e. span ,
472
+ UnpredictableFunctionPointerComparisons :: Warn ,
473
+ ) ;
474
+ } ;
475
+
476
+ let ne = if cmpop == ComparisonOp :: BinOp ( hir:: BinOpKind :: Ne ) { "!" } else { "" } ;
477
+
478
+ // `ptr::fn_addr_eq` only works with raw pointer, deref any references.
479
+ let deref_left = & * "*" . repeat ( l_ty_refs) ;
480
+ let deref_right = & * "*" . repeat ( r_ty_refs) ;
481
+
482
+ let left = e. span . shrink_to_lo ( ) . until ( l_span. shrink_to_lo ( ) ) ;
483
+ let middle = l_span. shrink_to_hi ( ) . until ( r_span. shrink_to_lo ( ) ) ;
484
+ let right = r_span. shrink_to_hi ( ) . until ( e. span . shrink_to_hi ( ) ) ;
485
+
486
+ // We only check for a right cast as `FnDef` == `FnPtr` is not possible,
487
+ // only `FnPtr == FnDef` is possible.
488
+ let cast_right = if !r_ty. is_fn_ptr ( ) {
489
+ let fn_sig = r_ty. fn_sig ( cx. tcx ) ;
490
+ format ! ( " as {fn_sig}" )
491
+ } else {
492
+ String :: new ( )
493
+ } ;
494
+
495
+ cx. emit_span_lint (
496
+ UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS ,
497
+ e. span ,
498
+ UnpredictableFunctionPointerComparisons :: Suggestion {
499
+ sugg : UnpredictableFunctionPointerComparisonsSuggestion {
500
+ ne,
501
+ deref_left,
502
+ deref_right,
503
+ left,
504
+ middle,
505
+ right,
506
+ cast_right,
507
+ } ,
508
+ } ,
509
+ ) ;
510
+ }
511
+
386
512
impl < ' tcx > LateLintPass < ' tcx > for TypeLimits {
387
513
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx hir:: Expr < ' tcx > ) {
388
514
match e. kind {
@@ -399,7 +525,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
399
525
cx. emit_span_lint ( UNUSED_COMPARISONS , e. span , UnusedComparisons ) ;
400
526
} else {
401
527
lint_nan ( cx, e, binop, l, r) ;
402
- lint_wide_pointer ( cx, e, ComparisonOp :: BinOp ( binop. node ) , l, r) ;
528
+ let cmpop = ComparisonOp :: BinOp ( binop. node ) ;
529
+ lint_wide_pointer ( cx, e, cmpop, l, r) ;
530
+ lint_fn_pointer ( cx, e, cmpop, l, r) ;
403
531
}
404
532
}
405
533
}
@@ -411,13 +539,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
411
539
&& let Some ( cmpop) = diag_item_cmpop ( diag_item) =>
412
540
{
413
541
lint_wide_pointer ( cx, e, cmpop, l, r) ;
542
+ lint_fn_pointer ( cx, e, cmpop, l, r) ;
414
543
}
415
544
hir:: ExprKind :: MethodCall ( _, l, [ r] , _)
416
545
if let Some ( def_id) = cx. typeck_results ( ) . type_dependent_def_id ( e. hir_id )
417
546
&& let Some ( diag_item) = cx. tcx . get_diagnostic_name ( def_id)
418
547
&& let Some ( cmpop) = diag_item_cmpop ( diag_item) =>
419
548
{
420
549
lint_wide_pointer ( cx, e, cmpop, l, r) ;
550
+ lint_fn_pointer ( cx, e, cmpop, l, r) ;
421
551
}
422
552
_ => { }
423
553
} ;
0 commit comments