@@ -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 } ;
@@ -442,58 +442,87 @@ impl LateLintPass<'_> for Diagnostics {
442
442
_ => return ,
443
443
} ;
444
444
445
- // Is the callee marked with `#[rustc_lint_diagnostics]`?
446
- let has_attr = ty:: Instance :: try_resolve ( cx. tcx , cx. param_env , def_id, fn_gen_args)
447
- . ok ( )
448
- . flatten ( )
449
- . is_some_and ( |inst| cx. tcx . has_attr ( inst. def_id ( ) , sym:: rustc_lint_diagnostics) ) ;
450
-
451
- // Closure: is the type `{D,Subd}iagMessage`?
452
- let is_diag_message = |ty : MiddleTy < ' _ > | {
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
- } ;
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
+ }
462
449
463
- // Does the callee have one or more `impl Into<{D,Subd}iagMessage>` parameters?
464
- 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
+ ) {
465
468
let fn_sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) . skip_binder ( ) ;
466
469
let predicates = cx. tcx . predicates_of ( def_id) . instantiate_identity ( cx. tcx ) . predicates ;
467
470
for ( i, & param_ty) in fn_sig. inputs ( ) . iter ( ) . enumerate ( ) {
468
- if let ty:: Param ( p ) = param_ty. kind ( ) {
471
+ if let ty:: Param ( sig_param ) = param_ty. kind ( ) {
469
472
// It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
470
473
for pred in predicates. iter ( ) {
471
474
if let Some ( trait_pred) = pred. as_trait_clause ( )
472
475
&& let trait_ref = trait_pred. skip_binder ( ) . trait_ref
473
476
&& trait_ref. self_ty ( ) == param_ty // correct predicate for the param?
474
477
&& cx. tcx . is_diagnostic_item ( sym:: Into , trait_ref. def_id )
475
478
&& let ty1 = trait_ref. args . type_at ( 1 )
476
- && is_diag_message ( ty1)
479
+ && Self :: is_diag_message ( cx , ty1)
477
480
{
478
- 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
+ }
479
496
}
480
497
}
481
498
}
482
499
}
500
+ }
483
501
484
- // Is the callee interesting?
485
- 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 {
486
513
return ;
487
- }
514
+ } ;
515
+ let has_attr = cx. tcx . has_attr ( inst. def_id ( ) , sym:: rustc_lint_diagnostics) ;
516
+ if !has_attr {
517
+ return ;
518
+ } ;
488
519
489
- // Is the parent method marked with `#[rustc_lint_diagnostics]`?
490
- let mut parent_has_attr = false ;
491
- 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) {
492
521
if let Some ( owner_did) = hir_id. as_owner ( )
493
522
&& cx. tcx . has_attr ( owner_did, sym:: rustc_lint_diagnostics)
494
523
{
495
- parent_has_attr = true ;
496
- break ;
524
+ // The parent method is marked with `#[rustc_lint_diagnostics]`
525
+ return ;
497
526
}
498
527
}
499
528
@@ -502,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
502
531
// - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
503
532
//
504
533
// Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
505
- if has_attr && !parent_has_attr {
506
- let mut is_inside_appropriate_impl = false ;
507
- for ( _hir_id, parent) in cx. tcx . hir ( ) . parent_iter ( expr. hir_id ) {
508
- debug ! ( ?parent) ;
509
- if let Node :: Item ( Item { kind : ItemKind :: Impl ( impl_) , .. } ) = parent
510
- && let Impl { of_trait : Some ( of_trait) , .. } = impl_
511
- && let Some ( def_id) = of_trait. trait_def_id ( )
512
- && let Some ( name) = cx. tcx . get_diagnostic_name ( def_id)
513
- && matches ! ( name, sym:: Diagnostic | sym:: Subdiagnostic | sym:: LintDiagnostic )
514
- {
515
- is_inside_appropriate_impl = true ;
516
- break ;
517
- }
518
- }
519
- debug ! ( ?is_inside_appropriate_impl) ;
520
- if !is_inside_appropriate_impl {
521
- 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 ;
522
545
}
523
546
}
524
-
525
- // Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
526
- // with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
527
- // `UNTRANSLATABLE_DIAGNOSTIC` lint.
528
- for ( param_i, param_i_p_name) in impl_into_diagnostic_message_params {
529
- // Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
530
- let ( arg_ty, arg_span) = arg_tys_and_spans[ param_i] ;
531
- let is_translatable = is_diag_message ( arg_ty)
532
- || matches ! ( arg_ty. kind( ) , ty:: Param ( p) if p. name == param_i_p_name) ;
533
- if !is_translatable {
534
- cx. emit_span_lint ( UNTRANSLATABLE_DIAGNOSTIC , arg_span, UntranslatableDiag ) ;
535
- }
547
+ debug ! ( ?is_inside_appropriate_impl) ;
548
+ if !is_inside_appropriate_impl {
549
+ cx. emit_span_lint ( DIAGNOSTIC_OUTSIDE_OF_IMPL , span, DiagOutOfImpl ) ;
536
550
}
537
551
}
538
552
}
0 commit comments