Refactor: diagnostic_outside_of_impl
, untranslatable_diagnostic
· rust-lang/rust@e94a4ee (original) (raw)
`@@ -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:🪥:{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, ¶m_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
`}
`