@@ -8,7 +8,7 @@ use rustc_hir::{
8
8
BinOp , BinOpKind , Expr , ExprKind , GenericArg , HirId , Impl , Item , ItemKind , Node , Pat , PatKind ,
9
9
Path , PathSegment , QPath , Ty , TyKind ,
10
10
} ;
11
- use rustc_middle:: ty:: { self , Ty as MiddleTy } ;
11
+ use rustc_middle:: ty:: { self , GenericArgsRef , Ty as MiddleTy } ;
12
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
13
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
14
14
use rustc_span:: symbol:: { kw, sym, Symbol } ;
@@ -415,14 +415,17 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE
415
415
416
416
impl LateLintPass < ' _ > for Diagnostics {
417
417
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
418
+ let collect_args_tys_and_spans = |args : & [ Expr < ' _ > ] , reserve_one_extra : bool | {
419
+ let mut result = Vec :: with_capacity ( args. len ( ) + usize:: from ( reserve_one_extra) ) ;
420
+ result. extend ( args. iter ( ) . map ( |arg| ( cx. typeck_results ( ) . expr_ty ( arg) , arg. span ) ) ) ;
421
+ result
422
+ } ;
418
423
// Only check function calls and method calls.
419
- let ( span, def_id, fn_gen_args, call_tys ) = match expr. kind {
424
+ let ( span, def_id, fn_gen_args, arg_tys_and_spans ) = match expr. kind {
420
425
ExprKind :: Call ( callee, args) => {
421
426
match cx. typeck_results ( ) . node_type ( callee. hir_id ) . kind ( ) {
422
427
& ty:: FnDef ( def_id, fn_gen_args) => {
423
- let call_tys: Vec < _ > =
424
- args. iter ( ) . map ( |arg| cx. typeck_results ( ) . expr_ty ( arg) ) . collect ( ) ;
425
- ( callee. span , def_id, fn_gen_args, call_tys)
428
+ ( callee. span , def_id, fn_gen_args, collect_args_tys_and_spans ( args, false ) )
426
429
}
427
430
_ => return , // occurs for fns passed as args
428
431
}
@@ -432,66 +435,94 @@ impl LateLintPass<'_> for Diagnostics {
432
435
else {
433
436
return ;
434
437
} ;
435
- let mut call_tys: Vec < _ > =
436
- args. iter ( ) . map ( |arg| cx. typeck_results ( ) . expr_ty ( arg) ) . collect ( ) ;
437
- call_tys. insert ( 0 , cx. tcx . types . self_param ) ; // dummy inserted for `self`
438
- ( span, def_id, fn_gen_args, call_tys)
438
+ let mut args = collect_args_tys_and_spans ( args, true ) ;
439
+ args. insert ( 0 , ( cx. tcx . types . self_param , _recv. span ) ) ; // dummy inserted for `self`
440
+ ( span, def_id, fn_gen_args, args)
439
441
}
440
442
_ => return ,
441
443
} ;
442
444
443
- // Is the callee marked with `#[rustc_lint_diagnostics]`?
444
- let has_attr = ty:: Instance :: try_resolve ( cx. tcx , cx. param_env , def_id, fn_gen_args)
445
- . ok ( )
446
- . flatten ( )
447
- . is_some_and ( |inst| cx. tcx . has_attr ( inst. def_id ( ) , sym:: rustc_lint_diagnostics) ) ;
448
-
449
- // Closure: is the type `{D,Subd}iagMessage`?
450
- let is_diag_message = |ty : MiddleTy < ' _ > | {
451
- if let Some ( adt_def) = ty. ty_adt_def ( )
452
- && let Some ( name) = cx. tcx . get_diagnostic_name ( adt_def. did ( ) )
453
- && matches ! ( name, sym:: DiagMessage | sym:: SubdiagMessage )
454
- {
455
- true
456
- } else {
457
- false
458
- }
459
- } ;
445
+ Self :: diagnostic_outside_of_impl ( cx, span, expr. hir_id , def_id, fn_gen_args) ;
446
+ Self :: untranslatable_diagnostic ( cx, def_id, & arg_tys_and_spans) ;
447
+ }
448
+ }
460
449
461
- // Does the callee have one or more `impl Into<{D,Subd}iagMessage>` parameters?
462
- let mut impl_into_diagnostic_message_params = vec ! [ ] ;
450
+ impl Diagnostics {
451
+ // Is the type `{D,Subd}iagMessage`?
452
+ fn is_diag_message < ' cx > ( cx : & LateContext < ' cx > , ty : MiddleTy < ' cx > ) -> bool {
453
+ if let Some ( adt_def) = ty. ty_adt_def ( )
454
+ && let Some ( name) = cx. tcx . get_diagnostic_name ( adt_def. did ( ) )
455
+ && matches ! ( name, sym:: DiagMessage | sym:: SubdiagMessage )
456
+ {
457
+ true
458
+ } else {
459
+ false
460
+ }
461
+ }
462
+
463
+ fn untranslatable_diagnostic < ' cx > (
464
+ cx : & LateContext < ' cx > ,
465
+ def_id : DefId ,
466
+ arg_tys_and_spans : & [ ( MiddleTy < ' cx > , Span ) ] ,
467
+ ) {
463
468
let fn_sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) . skip_binder ( ) ;
464
469
let predicates = cx. tcx . predicates_of ( def_id) . instantiate_identity ( cx. tcx ) . predicates ;
465
470
for ( i, & param_ty) in fn_sig. inputs ( ) . iter ( ) . enumerate ( ) {
466
- if let ty:: Param ( p ) = param_ty. kind ( ) {
471
+ if let ty:: Param ( sig_param ) = param_ty. kind ( ) {
467
472
// It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
468
473
for pred in predicates. iter ( ) {
469
474
if let Some ( trait_pred) = pred. as_trait_clause ( )
470
475
&& let trait_ref = trait_pred. skip_binder ( ) . trait_ref
471
476
&& trait_ref. self_ty ( ) == param_ty // correct predicate for the param?
472
477
&& cx. tcx . is_diagnostic_item ( sym:: Into , trait_ref. def_id )
473
478
&& let ty1 = trait_ref. args . type_at ( 1 )
474
- && is_diag_message ( ty1)
479
+ && Self :: is_diag_message ( cx , ty1)
475
480
{
476
- impl_into_diagnostic_message_params. push ( ( i, p. name ) ) ;
481
+ // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
482
+ // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
483
+ // `UNTRANSLATABLE_DIAGNOSTIC` lint.
484
+ let ( arg_ty, arg_span) = arg_tys_and_spans[ i] ;
485
+
486
+ // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
487
+ let is_translatable = Self :: is_diag_message ( cx, arg_ty)
488
+ || matches ! ( arg_ty. kind( ) , ty:: Param ( arg_param) if arg_param. name == sig_param. name) ;
489
+ if !is_translatable {
490
+ cx. emit_span_lint (
491
+ UNTRANSLATABLE_DIAGNOSTIC ,
492
+ arg_span,
493
+ UntranslatableDiag ,
494
+ ) ;
495
+ }
477
496
}
478
497
}
479
498
}
480
499
}
500
+ }
481
501
482
- // Is the callee interesting?
483
- if !has_attr && impl_into_diagnostic_message_params. is_empty ( ) {
502
+ fn diagnostic_outside_of_impl < ' cx > (
503
+ cx : & LateContext < ' cx > ,
504
+ span : Span ,
505
+ current_id : HirId ,
506
+ def_id : DefId ,
507
+ fn_gen_args : GenericArgsRef < ' cx > ,
508
+ ) {
509
+ // Is the callee marked with `#[rustc_lint_diagnostics]`?
510
+ let Some ( inst) =
511
+ ty:: Instance :: try_resolve ( cx. tcx , cx. param_env , def_id, fn_gen_args) . ok ( ) . flatten ( )
512
+ else {
484
513
return ;
485
- }
514
+ } ;
515
+ let has_attr = cx. tcx . has_attr ( inst. def_id ( ) , sym:: rustc_lint_diagnostics) ;
516
+ if !has_attr {
517
+ return ;
518
+ } ;
486
519
487
- // Is the parent method marked with `#[rustc_lint_diagnostics]`?
488
- let mut parent_has_attr = false ;
489
- for ( hir_id, _parent) in cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) {
520
+ for ( hir_id, _parent) in cx. tcx . hir ( ) . parent_iter ( current_id) {
490
521
if let Some ( owner_did) = hir_id. as_owner ( )
491
522
&& cx. tcx . has_attr ( owner_did, sym:: rustc_lint_diagnostics)
492
523
{
493
- parent_has_attr = true ;
494
- break ;
524
+ // The parent method is marked with `#[rustc_lint_diagnostics]`
525
+ return ;
495
526
}
496
527
}
497
528
@@ -500,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
500
531
// - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
501
532
//
502
533
// Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
503
- if has_attr && !parent_has_attr {
504
- let mut is_inside_appropriate_impl = false ;
505
- for ( _hir_id, parent) in cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) {
506
- debug ! ( ?parent) ;
507
- if let Node :: Item ( Item { kind : ItemKind :: Impl ( impl_) , .. } ) = parent
508
- && let Impl { of_trait : Some ( of_trait) , .. } = impl_
509
- && let Some ( def_id) = of_trait. trait_def_id ( )
510
- && let Some ( name) = cx. tcx . get_diagnostic_name ( def_id)
511
- && matches ! ( name, sym:: Diagnostic | sym:: Subdiagnostic | sym:: LintDiagnostic )
512
- {
513
- is_inside_appropriate_impl = true ;
514
- break ;
515
- }
516
- }
517
- debug ! ( ?is_inside_appropriate_impl) ;
518
- if !is_inside_appropriate_impl {
519
- cx. emit_span_lint ( DIAGNOSTIC_OUTSIDE_OF_IMPL , span, DiagOutOfImpl ) ;
534
+ let mut is_inside_appropriate_impl = false ;
535
+ for ( _hir_id, parent) in cx. tcx . hir ( ) . parent_iter ( current_id) {
536
+ debug ! ( ?parent) ;
537
+ if let Node :: Item ( Item { kind : ItemKind :: Impl ( impl_) , .. } ) = parent
538
+ && let Impl { of_trait : Some ( of_trait) , .. } = impl_
539
+ && let Some ( def_id) = of_trait. trait_def_id ( )
540
+ && let Some ( name) = cx. tcx . get_diagnostic_name ( def_id)
541
+ && matches ! ( name, sym:: Diagnostic | sym:: Subdiagnostic | sym:: LintDiagnostic )
542
+ {
543
+ is_inside_appropriate_impl = true ;
544
+ break ;
520
545
}
521
546
}
522
-
523
- // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
524
- // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
525
- // `UNTRANSLATABLE_DIAGNOSTIC` lint.
526
- for ( param_i, param_i_p_name) in impl_into_diagnostic_message_params {
527
- // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
528
- let arg_ty = call_tys[ param_i] ;
529
- let is_translatable = is_diag_message ( arg_ty)
530
- || matches ! ( arg_ty. kind( ) , ty:: Param ( p) if p. name == param_i_p_name) ;
531
- if !is_translatable {
532
- cx. emit_span_lint ( UNTRANSLATABLE_DIAGNOSTIC , span, UntranslatableDiag ) ;
533
- }
547
+ debug ! ( ?is_inside_appropriate_impl) ;
548
+ if !is_inside_appropriate_impl {
549
+ cx. emit_span_lint ( DIAGNOSTIC_OUTSIDE_OF_IMPL , span, DiagOutOfImpl ) ;
534
550
}
535
551
}
536
552
}
0 commit comments