Auto merge of #129365 - matthiaskrgr:rollup-ebwx6ya, r=matthiaskrgr · rust-lang/rust@739b1fd (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};
`
`@@ -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, ¶m_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
`}
`