From 46ce4afcb3022e4132bb5787cbd8a4b1a2581e01 Mon Sep 17 00:00:00 2001 From: darkraider01 Date: Tue, 13 Oct 2026 13:37:39 +0530 Subject: [PATCH] Move check_rustc_must_implement_one_of validation to attribute parser --- compiler/rustc_ast_lowering/src/lib.rs | 2 +- .../src/attributes/rustc_internal.rs | 49 ++++++- .../rustc_attr_parsing/src/diagnostics.rs | 38 ++++++ compiler/rustc_passes/src/check_attr.rs | 57 +------- compiler/rustc_passes/src/diagnostics.rs | 38 ------ .../rustc_must_implement_one_of_delegation.rs | 23 ++++ .../rustc_must_implement_one_of_macros.rs | 40 ++++++ .../rustc_must_implement_one_of_macros.stderr | 8 ++ .../rustc_must_implement_one_of_misuse.rs | 9 ++ .../rustc_must_implement_one_of_misuse.stderr | 126 ++++++++++++------ 10 files changed, 253 insertions(+), 137 deletions(-) create mode 100644 tests/ui/traits/default-method/rustc_must_implement_one_of_delegation.rs create mode 100644 tests/ui/traits/default-method/rustc_must_implement_one_of_macros.rs create mode 100644 tests/ui/traits/default-method/rustc_must_implement_one_of_macros.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index c68fcb18427c5..d0cdb57f51db0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -687,7 +687,7 @@ fn index_ast<'tcx>( mut_visit::walk_assoc_item(self, item, ctxt); match ctxt { visit::AssocCtxt::Trait => { - self.replace_with_dummy(item, AssocItemKind::MacCall, AstOwner::TraitItem) + self.insert(item.id, AstOwner::TraitItem(Box::new(item.clone()))); } visit::AssocCtxt::Impl { .. } => { self.replace_with_dummy(item, AssocItemKind::MacCall, AstOwner::ImplItem) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 52422a85041a4..af68d80dfdeba 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; -use rustc_ast::{GenericParamKind, ItemKind, LitIntType, LitKind, MetaItemLit}; +use rustc_ast::{AssocItemKind, GenericParamKind, ItemKind, LitIntType, LitKind, MetaItemLit}; use rustc_attr_ir::lang_items::LangItem; use rustc_attr_ir::{ BorrowckGraphvizFormatKind, CguFields, CguKind, RustcAssertVarianceKind, RustcCleanAttribute, - RustcCleanQueries, RustcMirKind, + RustcCleanQueries, RustcMirKind, find_attr, }; use rustc_data_structures::fx::FxHashMap; use rustc_feature::AttributeStability; @@ -86,6 +86,51 @@ impl SingleAttributeParser for RustcMustImplementOneOfParser { Some(AttributeKind::RustcMustImplementOneOf { attr_span: cx.attr_span, fn_names }) } + + fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) { + if cx.target != Target::Trait { + return; + } + + let item = cx.target_item.expect("missing AST target item for Target::Trait"); + let ItemKind::Trait(trait_) = &item.kind else { + panic!("expected trait AST target item for Target::Trait"); + }; + + let fn_names = + find_attr!(cx.parsed_attrs, RustcMustImplementOneOf { fn_names, .. } => fn_names) + .expect("missing parsed RustcMustImplementOneOf attribute in finalize_check"); + + for ident in fn_names { + let item = trait_.items.iter().find(|item| item.kind.ident() == Some(*ident)); + + match item { + Some(item) => match &item.kind { + AssocItemKind::Fn(f) => { + if f.body.is_none() { + cx.emit_err(diagnostics::FunctionNotHaveDefaultImplementation { + span: item.span, + note_span: attr_span, + }); + } + } + AssocItemKind::Delegation(_) => {} + _ => { + cx.emit_err(diagnostics::MustImplementNotFunction { + span: item.span, + span_note: diagnostics::MustImplementNotFunctionSpanNote { + span: attr_span, + }, + note: diagnostics::MustImplementNotFunctionNote {}, + }); + } + }, + None => { + cx.emit_err(diagnostics::FunctionNotFoundInTrait { span: ident.span }); + } + } + } + } } pub(crate) struct RustcNeverReturnsNullPtrParser; diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index b5bfbc9e3dc3b..d232afd809157 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -111,6 +111,44 @@ pub(crate) struct FunctionNamesDuplicated { pub spans: Vec, } +#[derive(Diagnostic)] +#[diag("function doesn't have a default implementation")] +pub(crate) struct FunctionNotHaveDefaultImplementation { + #[primary_span] + pub span: Span, + #[note("required by this annotation")] + pub note_span: Span, +} + +#[derive(Diagnostic)] +#[diag("not a function")] +pub(crate) struct MustImplementNotFunction { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub span_note: MustImplementNotFunctionSpanNote, + #[subdiagnostic] + pub note: MustImplementNotFunctionNote, +} + +#[derive(Subdiagnostic)] +#[note("required by this annotation")] +pub(crate) struct MustImplementNotFunctionSpanNote { + #[primary_span] + pub span: Span, +} + +#[derive(Subdiagnostic)] +#[note("all `#[rustc_must_implement_one_of]` arguments must be associated function names")] +pub(crate) struct MustImplementNotFunctionNote {} + +#[derive(Diagnostic)] +#[diag("function not found in this trait")] +pub(crate) struct FunctionNotFoundInTrait { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("unsafe attribute used without unsafe")] pub(crate) struct UnsafeAttrOutsideUnsafeLint { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ab951a2cc1ce9..ce4b88861d125 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -17,7 +17,6 @@ use rustc_attr_ir::{ OptimizeAttr, ReprAttr, RustcAssertVarianceKind, find_attr, }; use rustc_attr_parsing::AttributeParser; -use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::{DiagCtxtHandle, IntoDiagArg}; use rustc_feature::BUILTIN_ATTRIBUTE_SET; use rustc_hir::def::DefKind; @@ -40,7 +39,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode, Unnormalized, Variance}; use rustc_session::diagnostics::feature_err; use rustc_span::edition::Edition; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, bug, kw, span_bug, sym}; +use rustc_span::{DUMMY_SP, Span, Symbol, bug, kw, span_bug, sym}; use rustc_structures::CrateType; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; @@ -199,9 +198,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::MayDangle(attr_span) => self.check_may_dangle(hir_id, *attr_span), AttributeKind::Doc(attr) => self.check_doc_attrs(attr, hir_id, target), AttributeKind::EiiImpl(eii_impl) => self.check_eii_impl(eii_impl), - AttributeKind::RustcMustImplementOneOf { attr_span, fn_names } => { - self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id, target) - } AttributeKind::OnUnimplemented { directive } => { self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref()) } @@ -358,6 +354,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcMacroTransparency(_) => (), AttributeKind::RustcMain => (), AttributeKind::RustcMir(_) => (), + AttributeKind::RustcMustImplementOneOf { .. } => (), AttributeKind::RustcMustMatchExhaustively(..) => (), AttributeKind::RustcNeverReturnsNullPtr => (), AttributeKind::RustcNoImplicitAutorefs => (), @@ -456,56 +453,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }) } - fn check_rustc_must_implement_one_of( - &self, - attr_span: Span, - list: &ThinVec, - hir_id: HirId, - target: Target, - ) { - // Ignoring invalid targets because TyCtxt::associated_items emits bug if the target isn't valid - // the parser has already produced an error for the target being invalid - if !matches!(target, Target::Trait) { - return; - } - - let def_id = hir_id.owner.def_id; - - let items = self.tcx.associated_items(def_id); - // Check that all arguments of `#[rustc_must_implement_one_of]` reference - // functions in the trait with default implementations - for ident in list { - let item = items - .filter_by_name_unhygienic(ident.name) - .find(|item| item.ident(self.tcx) == *ident); - - match item { - Some(item) if matches!(item.kind, ty::AssocKind::Fn { .. }) => { - if !item.defaultness(self.tcx).has_value() { - self.tcx.dcx().emit_err( - diagnostics::FunctionNotHaveDefaultImplementation { - span: self.tcx.def_span(item.def_id), - note_span: attr_span, - }, - ); - } - } - Some(item) => { - self.dcx().emit_err(diagnostics::MustImplementNotFunction { - span: self.tcx.def_span(item.def_id), - span_note: diagnostics::MustImplementNotFunctionSpanNote { - span: attr_span, - }, - note: diagnostics::MustImplementNotFunctionNote {}, - }); - } - None => { - self.dcx().emit_err(diagnostics::FunctionNotFoundInTrait { span: ident.span }); - } - } - } - } - /// Checks that each externally implementable item (EII) implementation uses `unsafe` /// exactly when its declaration requires it. fn check_eii_impl(&self, eii_impl: &EiiImpl) { diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index e090fd0669cd8..6adceaab90fe2 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -955,44 +955,6 @@ pub(crate) struct EiiWithoutImpl { pub help: (), } -#[derive(Diagnostic)] -#[diag("function doesn't have a default implementation")] -pub(crate) struct FunctionNotHaveDefaultImplementation { - #[primary_span] - pub span: Span, - #[note("required by this annotation")] - pub note_span: Span, -} - -#[derive(Diagnostic)] -#[diag("not a function")] -pub(crate) struct MustImplementNotFunction { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub span_note: MustImplementNotFunctionSpanNote, - #[subdiagnostic] - pub note: MustImplementNotFunctionNote, -} - -#[derive(Subdiagnostic)] -#[note("required by this annotation")] -pub(crate) struct MustImplementNotFunctionSpanNote { - #[primary_span] - pub span: Span, -} - -#[derive(Subdiagnostic)] -#[note("all `#[rustc_must_implement_one_of]` arguments must be associated function names")] -pub(crate) struct MustImplementNotFunctionNote {} - -#[derive(Diagnostic)] -#[diag("function not found in this trait")] -pub(crate) struct FunctionNotFoundInTrait { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")] pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_delegation.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_delegation.rs new file mode 100644 index 0000000000000..a1f39bb33dc1a --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_delegation.rs @@ -0,0 +1,23 @@ +//@ check-pass + +#![feature(rustc_attrs)] +#![feature(fn_delegation)] + +mod to_reuse { + pub fn b() {} + pub fn c() {} +} + +#[rustc_must_implement_one_of(a, b)] +trait TraitDelegation { + fn a() {} + reuse to_reuse::b; +} + +#[rustc_must_implement_one_of(a, ren)] +trait TraitDelegationRenamed { + fn a() {} + reuse to_reuse::c as ren; +} + +fn main() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_macros.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_macros.rs new file mode 100644 index 0000000000000..f7a856e8ba8c9 --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_macros.rs @@ -0,0 +1,40 @@ +#![feature(rustc_attrs)] + +macro_rules! define_method { + ($name:ident) => { + fn $name() {} + }; +} + +#[rustc_must_implement_one_of(a, b)] +trait TraitMacro { + fn a() {} + define_method!(b); +} + +macro_rules! define_trait { + ($trait_name:ident, $m1:ident, $m2:ident) => { + #[rustc_must_implement_one_of($m1, $m2)] + trait $trait_name { + fn $m1() {} + fn $m2() {} + } + }; +} + +define_trait!(TraitMacroWhole, foo, bar); + +macro_rules! define_internal_b { + () => { + fn b() {} + }; +} + +#[rustc_must_implement_one_of(a, b)] +//~^ ERROR function not found in this trait +trait TraitHygiene { + fn a() {} + define_internal_b!(); +} + +fn main() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_macros.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of_macros.stderr new file mode 100644 index 0000000000000..041e708ce4c94 --- /dev/null +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_macros.stderr @@ -0,0 +1,8 @@ +error: function not found in this trait + --> $DIR/rustc_must_implement_one_of_macros.rs:33:34 + | +LL | #[rustc_must_implement_one_of(a, b)] + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs b/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs index e5f274df06a81..1b9be10df08a2 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs @@ -65,6 +65,15 @@ trait TrTwoDefaults { fn c(); //~ ERROR function doesn't have a default implementation } +#[rustc_must_implement_one_of(a, b, C, D, missing)] +//~^ ERROR function not found in this trait +trait TrMixed { + fn a() {} + fn b(); //~ ERROR function doesn't have a default implementation + const C: u8 = 1; //~ ERROR not a function + type D; //~ ERROR not a function +} + #[rustc_must_implement_one_of(abc, abc)] //~^ ERROR the `rustc_must_implement_one_of` attribute cannot be used on functions fn function() {} diff --git a/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr b/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr index 94a29d7ca0f50..f8e180cef86d6 100644 --- a/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr +++ b/tests/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr @@ -1,3 +1,21 @@ +error: function not found in this trait + --> $DIR/rustc_must_implement_one_of_misuse.rs:3:31 + | +LL | #[rustc_must_implement_one_of(a, b)] + | ^ + +error: function not found in this trait + --> $DIR/rustc_must_implement_one_of_misuse.rs:3:34 + | +LL | #[rustc_must_implement_one_of(a, b)] + | ^ + +error: function not found in this trait + --> $DIR/rustc_must_implement_one_of_misuse.rs:8:34 + | +LL | #[rustc_must_implement_one_of(a, b)] + | ^ + error[E0539]: malformed `rustc_must_implement_one_of` attribute input --> $DIR/rustc_must_implement_one_of_misuse.rs:14:3 | @@ -56,49 +74,11 @@ error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `,` LL | #[rustc_must_implement_one_of(,)] | ^ -error: the `rustc_must_implement_one_of` attribute cannot be used on functions - --> $DIR/rustc_must_implement_one_of_misuse.rs:68:3 - | -LL | #[rustc_must_implement_one_of(abc, abc)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | fn function() {} - | - = help: the `rustc_must_implement_one_of` attribute can only be applied to traits - -error: the `rustc_must_implement_one_of` attribute cannot be used on structs - --> $DIR/rustc_must_implement_one_of_misuse.rs:72:3 - | -LL | #[rustc_must_implement_one_of(abc, xyz)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | struct Struct {} - | - = help: the `rustc_must_implement_one_of` attribute can only be applied to traits - -error: function not found in this trait - --> $DIR/rustc_must_implement_one_of_misuse.rs:3:31 - | -LL | #[rustc_must_implement_one_of(a, b)] - | ^ - -error: function not found in this trait - --> $DIR/rustc_must_implement_one_of_misuse.rs:3:34 - | -LL | #[rustc_must_implement_one_of(a, b)] - | ^ - -error: function not found in this trait - --> $DIR/rustc_must_implement_one_of_misuse.rs:8:34 - | -LL | #[rustc_must_implement_one_of(a, b)] - | ^ - error: not a function --> $DIR/rustc_must_implement_one_of_misuse.rs:40:5 | LL | const A: u8 = 1; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | note: required by this annotation --> $DIR/rustc_must_implement_one_of_misuse.rs:38:1 @@ -111,7 +91,7 @@ error: not a function --> $DIR/rustc_must_implement_one_of_misuse.rs:42:5 | LL | type B; - | ^^^^^^ + | ^^^^^^^ | note: required by this annotation --> $DIR/rustc_must_implement_one_of_misuse.rs:38:1 @@ -168,6 +148,70 @@ note: required by this annotation LL | #[rustc_must_implement_one_of(a, b, c)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: function doesn't have a default implementation + --> $DIR/rustc_must_implement_one_of_misuse.rs:72:5 + | +LL | fn b(); + | ^^^^^^^ + | +note: required by this annotation + --> $DIR/rustc_must_implement_one_of_misuse.rs:68:1 + | +LL | #[rustc_must_implement_one_of(a, b, C, D, missing)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: not a function + --> $DIR/rustc_must_implement_one_of_misuse.rs:73:5 + | +LL | const C: u8 = 1; + | ^^^^^^^^^^^^^^^^ + | +note: required by this annotation + --> $DIR/rustc_must_implement_one_of_misuse.rs:68:1 + | +LL | #[rustc_must_implement_one_of(a, b, C, D, missing)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: all `#[rustc_must_implement_one_of]` arguments must be associated function names + +error: not a function + --> $DIR/rustc_must_implement_one_of_misuse.rs:74:5 + | +LL | type D; + | ^^^^^^^ + | +note: required by this annotation + --> $DIR/rustc_must_implement_one_of_misuse.rs:68:1 + | +LL | #[rustc_must_implement_one_of(a, b, C, D, missing)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: all `#[rustc_must_implement_one_of]` arguments must be associated function names + +error: function not found in this trait + --> $DIR/rustc_must_implement_one_of_misuse.rs:68:43 + | +LL | #[rustc_must_implement_one_of(a, b, C, D, missing)] + | ^^^^^^^ + +error: the `rustc_must_implement_one_of` attribute cannot be used on functions + --> $DIR/rustc_must_implement_one_of_misuse.rs:77:3 + | +LL | #[rustc_must_implement_one_of(abc, abc)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | fn function() {} + | + = help: the `rustc_must_implement_one_of` attribute can only be applied to traits + +error: the `rustc_must_implement_one_of` attribute cannot be used on structs + --> $DIR/rustc_must_implement_one_of_misuse.rs:81:3 + | +LL | #[rustc_must_implement_one_of(abc, xyz)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | struct Struct {} + | + = help: the `rustc_must_implement_one_of` attribute can only be applied to traits + +error: aborting due to 20 previous errors For more information about this error, try `rustc --explain E0539`.