@@ -8,7 +8,7 @@ use rustc_hir::{
88 BinOp , BinOpKind , Expr , ExprKind , GenericArg , HirId , Impl , Item , ItemKind , Node , Pat , PatKind ,
99 Path , PathSegment , QPath , Ty , TyKind ,
1010} ;
11- use rustc_middle:: ty:: { self , Ty as MiddleTy } ;
11+ use rustc_middle:: ty:: { self , GenericArgsRef , Ty as MiddleTy } ;
1212use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1313use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
1414use rustc_span:: symbol:: { kw, sym, Symbol } ;
@@ -442,58 +442,87 @@ impl LateLintPass<'_> for Diagnostics {
442442 _ => return ,
443443 } ;
444444
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+ }
462449
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+ ) {
465468 let fn_sig = cx. tcx . fn_sig ( def_id) . instantiate_identity ( ) . skip_binder ( ) ;
466469 let predicates = cx. tcx . predicates_of ( def_id) . instantiate_identity ( cx. tcx ) . predicates ;
467470 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 ( ) {
469472 // It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
470473 for pred in predicates. iter ( ) {
471474 if let Some ( trait_pred) = pred. as_trait_clause ( )
472475 && let trait_ref = trait_pred. skip_binder ( ) . trait_ref
473476 && trait_ref. self_ty ( ) == param_ty // correct predicate for the param?
474477 && cx. tcx . is_diagnostic_item ( sym:: Into , trait_ref. def_id )
475478 && let ty1 = trait_ref. args . type_at ( 1 )
476- && is_diag_message ( ty1)
479+ && Self :: is_diag_message ( cx , ty1)
477480 {
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+ }
479496 }
480497 }
481498 }
482499 }
500+ }
483501
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 {
486513 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+ } ;
488519
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) {
492521 if let Some ( owner_did) = hir_id. as_owner ( )
493522 && cx. tcx . has_attr ( owner_did, sym:: rustc_lint_diagnostics)
494523 {
495- parent_has_attr = true ;
496- break ;
524+ // The parent method is marked with `#[rustc_lint_diagnostics]`
525+ return ;
497526 }
498527 }
499528
@@ -502,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
502531 // - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
503532 //
504533 // 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 ;
522545 }
523546 }
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 ) ;
536550 }
537551 }
538552}
0 commit comments