Skip to content

Commit e94a4ee

Browse files
committed
Refactor: diagnostic_outside_of_impl, untranslatable_diagnostic
1. Decouple them. 2. Make logic around `diagnostic_outside_of_impl`'s early exits simpler. 3. Make `untranslatable_diagnostic` run one loop instead of two and not allocate an intermediate vec. 4. Overall, reduce the amount of code executed when the lints do not end up firing.
1 parent 3cc2a6f commit e94a4ee

File tree

1 file changed

+74
-60
lines changed

1 file changed

+74
-60
lines changed

Diff for: compiler/rustc_lint/src/internal.rs

+74-60
Original file line numberDiff line numberDiff line change
@@ -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};
1212
use rustc_session::{declare_lint_pass, declare_tool_lint};
1313
use rustc_span::hygiene::{ExpnKind, MacroKind};
1414
use 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

Comments
 (0)