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, &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() {

`

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}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

`+

}

`

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}iagMessageor 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

`}

`