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, &param_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}iagMessageor 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}iagMessageor 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

`}

`