From 841b0075e9e517dbae15440bf1786c851a744111 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Mar 2026 16:49:58 +0300 Subject: [PATCH 01/15] ast: Preserve the star symbol span in glob delegation items --- compiler/rustc_ast/src/ast.rs | 9 +++++++-- compiler/rustc_ast/src/visit.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state/item.rs | 10 ++++++++-- compiler/rustc_expand/src/expand.rs | 8 ++++---- compiler/rustc_parse/src/parser/item.rs | 13 +++++++++---- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 87633cade1b6a..9ce31549e59e4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3879,12 +3879,17 @@ pub struct Delegation { pub from_glob: bool, } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub enum DelegationSuffixes { + List(ThinVec<(Ident, Option)>), + Glob(Span), +} + #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct DelegationMac { pub qself: Option>, pub prefix: Path, - // Some for list delegation, and None for glob delegation. - pub suffixes: Option)>>, + pub suffixes: DelegationSuffixes, pub body: Option>, } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 6aa8d5f38ad24..ee4b1d1354300 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -430,6 +430,7 @@ macro_rules! common_visitor_and_walkers { Defaultness, Delegation, DelegationMac, + DelegationSuffixes, DelimArgs, DelimSpan, EnumDef, diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index e997fdf498202..cfa929ab27ca2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -435,7 +435,10 @@ impl<'a> State<'a> { &item.vis, &deleg.qself, &deleg.prefix, - deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), + match &deleg.suffixes { + ast::DelegationSuffixes::List(s) => DelegationKind::List(s), + ast::DelegationSuffixes::Glob(_) => DelegationKind::Glob, + }, &deleg.body, ), } @@ -641,7 +644,10 @@ impl<'a> State<'a> { vis, &deleg.qself, &deleg.prefix, - deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)), + match &deleg.suffixes { + ast::DelegationSuffixes::List(s) => DelegationKind::List(s), + ast::DelegationSuffixes::Glob(_) => DelegationKind::Glob, + }, &deleg.body, ), } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 4ed87fc83b232..fff159634da49 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -8,9 +8,9 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; use rustc_ast::{ self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrItemKind, AttrStyle, AttrVec, - DUMMY_NODE_ID, EarlyParsedAttribute, ExprKind, ForeignItemKind, HasAttrs, HasNodeId, Inline, - ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, PatKind, StmtKind, - TyKind, token, + DUMMY_NODE_ID, DelegationSuffixes, EarlyParsedAttribute, ExprKind, ForeignItemKind, HasAttrs, + HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, + PatKind, StmtKind, TyKind, token, }; use rustc_ast_pretty::pprust; use rustc_attr_parsing::parser::AllowExprMetavar; @@ -2365,7 +2365,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { res } None if let Some((deleg, item)) = node.delegation() => { - let Some(suffixes) = &deleg.suffixes else { + let DelegationSuffixes::List(suffixes) = &deleg.suffixes else { let traitless_qself = matches!(&deleg.qself, Some(qself) if qself.position == 0); let (item, of_trait) = match node.to_annotatable() { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index e2f0f8fee0d8d..e310bc106cad7 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -857,7 +857,7 @@ impl<'a> Parser<'a> { kind: AssocItemKind::DelegationMac(Box::new(DelegationMac { qself: None, prefix: of_trait.trait_ref.path.clone(), - suffixes: None, + suffixes: DelegationSuffixes::Glob(whole_reuse_span), body, })), })); @@ -879,10 +879,12 @@ impl<'a> Parser<'a> { Ok(if self.eat_path_sep() { let suffixes = if self.eat(exp!(Star)) { - None + DelegationSuffixes::Glob(self.prev_token.span) } else { let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?)); - Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0) + DelegationSuffixes::List( + self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0, + ) }; ItemKind::DelegationMac(Box::new(DelegationMac { @@ -1517,7 +1519,10 @@ impl<'a> Parser<'a> { let span = self.psess.source_map().guess_head_span(span); let descr = kind.descr(); let help = match kind { - ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => false, + ItemKind::DelegationMac(box DelegationMac { + suffixes: DelegationSuffixes::Glob(_), + .. + }) => false, _ => true, }; self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help }); From a47a7727a02eec130c92068127adf1d0e29672ca Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Mar 2026 17:30:57 +0300 Subject: [PATCH 02/15] expand: More precise location for glob delegation The span location of the last segment in the desugared path is inherited from the star symbol's span --- compiler/rustc_expand/src/base.rs | 11 +++++++-- compiler/rustc_resolve/src/macros.rs | 23 ++++++++++++------- .../delegation/impl-reuse-negative-traits.rs | 2 +- .../impl-reuse-negative-traits.stderr | 6 ++--- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 4f60ba2e9725c..7cdabae7c8464 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1019,18 +1019,24 @@ impl SyntaxExtension { pub fn glob_delegation( trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, edition: Edition, ) -> SyntaxExtension { struct GlobDelegationExpanderImpl { trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, } impl GlobDelegationExpander for GlobDelegationExpanderImpl { fn expand( &self, ecx: &mut ExtCtxt<'_>, ) -> ExpandResult)>, ()> { - match ecx.resolver.glob_delegation_suffixes(self.trait_def_id, self.impl_def_id) { + match ecx.resolver.glob_delegation_suffixes( + self.trait_def_id, + self.impl_def_id, + self.star_span, + ) { Ok(suffixes) => ExpandResult::Ready(suffixes), Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()), Err(Indeterminate) => ExpandResult::Retry(()), @@ -1038,7 +1044,7 @@ impl SyntaxExtension { } } - let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id }; + let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id, star_span }; SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Arc::new(expander)), edition) } @@ -1170,6 +1176,7 @@ pub trait ResolverExpand { &self, trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, ) -> Result)>, Indeterminate>; /// Record the name of an opaque `Ty::ImplTrait` pre-expansion so that it can be used diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 619e61211984d..f4f726d096aac 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -4,7 +4,7 @@ use std::mem; use std::sync::Arc; -use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, NodeId}; +use rustc_ast::{self as ast, Crate, DUMMY_NODE_ID, DelegationSuffixes, NodeId}; use rustc_ast_pretty::pprust; use rustc_attr_parsing::AttributeParser; use rustc_errors::{Applicability, DiagCtxtHandle, StashKey}; @@ -286,7 +286,8 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive), InvocationKind::GlobDelegation { ref item, .. } => { let ast::AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; - deleg_impl = Some(self.invocation_parent(invoc_id)); + let DelegationSuffixes::Glob(star_span) = deleg.suffixes else { unreachable!() }; + deleg_impl = Some((self.invocation_parent(invoc_id), star_span)); // It is sufficient to consider glob delegation a bang macro for now. (&deleg.prefix, MacroKind::Bang) } @@ -530,6 +531,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { &self, trait_def_id: DefId, impl_def_id: LocalDefId, + star_span: Span, ) -> Result)>, Indeterminate> { let target_trait = self.expect_module(trait_def_id); if !target_trait.unexpanded_invocations.borrow().is_empty() { @@ -549,13 +551,13 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { let mut idents = Vec::new(); target_trait.for_each_child(self, |this, ident, orig_ident_span, ns, _binding| { - // FIXME: Adjust hygiene for idents from globs, like for glob imports. if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) && overriding_keys.contains(&BindingKey::new(ident, ns)) { // The name is overridden, do not produce it from the glob delegation. } else { - idents.push((ident.orig(orig_ident_span), None)); + // FIXME: Adjust hygiene for idents from globs, like for glob imports. + idents.push((ident.orig(star_span.with_ctxt(orig_ident_span.ctxt())), None)); } }); Ok(idents) @@ -579,7 +581,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope: &ParentScope<'ra>, node_id: NodeId, force: bool, - deleg_impl: Option, + deleg_impl: Option<(LocalDefId, Span)>, invoc_in_mod_inert_attr: Option, suggestion_span: Option, ) -> Result<(Arc, Res), Indeterminate> { @@ -761,7 +763,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { kind: MacroKind, parent_scope: &ParentScope<'ra>, force: bool, - deleg_impl: Option, + deleg_impl: Option<(LocalDefId, Span)>, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, ignore_import: Option>, suggestion_span: Option, @@ -846,10 +848,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let res = res?; let ext = match deleg_impl { - Some(impl_def_id) => match res { + Some((impl_def_id, star_span)) => match res { def::Res::Def(DefKind::Trait, def_id) => { let edition = self.tcx.sess.edition(); - Some(Arc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition))) + Some(Arc::new(SyntaxExtension::glob_delegation( + def_id, + impl_def_id, + star_span, + edition, + ))) } _ => None, }, diff --git a/tests/ui/delegation/impl-reuse-negative-traits.rs b/tests/ui/delegation/impl-reuse-negative-traits.rs index 7bcbc82f03db8..8bc2b72708599 100644 --- a/tests/ui/delegation/impl-reuse-negative-traits.rs +++ b/tests/ui/delegation/impl-reuse-negative-traits.rs @@ -4,7 +4,6 @@ trait Trait { fn foo(&self); - //~^ ERROR negative impls cannot have any items [E0749] } struct S; @@ -15,5 +14,6 @@ impl Trait for S { struct F(S); reuse impl !Trait for F { &self.0 } +//~^ ERROR negative impls cannot have any items fn main() {} diff --git a/tests/ui/delegation/impl-reuse-negative-traits.stderr b/tests/ui/delegation/impl-reuse-negative-traits.stderr index 1be6ef715920d..7510fdd89d7c6 100644 --- a/tests/ui/delegation/impl-reuse-negative-traits.stderr +++ b/tests/ui/delegation/impl-reuse-negative-traits.stderr @@ -1,8 +1,8 @@ error[E0749]: negative impls cannot have any items - --> $DIR/impl-reuse-negative-traits.rs:6:8 + --> $DIR/impl-reuse-negative-traits.rs:16:1 | -LL | fn foo(&self); - | ^^^ +LL | reuse impl !Trait for F { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error From db6a037f844064c393cc9e2b963dcfb81b7480f9 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Mar 2026 19:05:12 +0300 Subject: [PATCH 03/15] delegation: Give declaration of `self` syntax context of the delegation body Instead of the last segment of the delegation path. `self` is something that introduced by the whole delegation item, not some specific part of it, and the last segment may need to have a different context for path resolution purposes. --- compiler/rustc_resolve/src/late.rs | 3 +- tests/ui/delegation/impl-reuse-pass.rs | 25 ------- .../ui/delegation/impl-reuse-self-hygiene.rs | 28 ++++++++ .../delegation/impl-reuse-self-hygiene.stderr | 68 +++++++++++++++++++ 4 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 tests/ui/delegation/impl-reuse-self-hygiene.rs create mode 100644 tests/ui/delegation/impl-reuse-self-hygiene.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b3a6fe95e5fa6..53dd8fb98c177 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3848,8 +3848,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { let Some(body) = &delegation.body else { return }; self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - let span = delegation.path.segments.last().unwrap().ident.span; - let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules()); + let ident = Ident::new(kw::SelfLower, body.span.normalize_to_macro_rules()); let res = Res::Local(delegation.id); this.innermost_rib_bindings(ValueNS).insert(ident, res); diff --git a/tests/ui/delegation/impl-reuse-pass.rs b/tests/ui/delegation/impl-reuse-pass.rs index 90060b03f9efd..578e8c08e7038 100644 --- a/tests/ui/delegation/impl-reuse-pass.rs +++ b/tests/ui/delegation/impl-reuse-pass.rs @@ -173,19 +173,6 @@ mod macros { macro_rules! m { () => { M } } reuse impl Trait for m!() { self_0_ref!(self) } - struct S1(u8); - macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } - one_line_reuse!(self); - - struct S2(u8); - macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } - one_line_reuse_expr!(self.0); - - struct S3(u8); - macro_rules! s3 { () => { S3 } } - macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } - one_line_reuse_expr2!(self.0); - fn f() { let s = S(1); s.foo(); @@ -194,18 +181,6 @@ mod macros { let m = M(41); m.foo(); m.bar(); - - let s1 = S1(2); - s1.foo(); - s1.bar(); - - let s2 = S2(4); - s2.foo(); - s2.bar(); - - let s3 = S3(5); - s3.foo(); - s3.bar(); } } diff --git a/tests/ui/delegation/impl-reuse-self-hygiene.rs b/tests/ui/delegation/impl-reuse-self-hygiene.rs new file mode 100644 index 0000000000000..b49e4419703a7 --- /dev/null +++ b/tests/ui/delegation/impl-reuse-self-hygiene.rs @@ -0,0 +1,28 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } +} + +struct S1(u8); +macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` +one_line_reuse!(self); + +struct S2(u8); +macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } +one_line_reuse_expr!(self.0); +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` + +struct S3(u8); +macro_rules! s3 { () => { S3 } } +macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } +one_line_reuse_expr2!(self.0); +//~^ ERROR expected value, found module `self` +//~| ERROR expected value, found module `self` + +fn main() {} diff --git a/tests/ui/delegation/impl-reuse-self-hygiene.stderr b/tests/ui/delegation/impl-reuse-self-hygiene.stderr new file mode 100644 index 0000000000000..ae93829809e4f --- /dev/null +++ b/tests/ui/delegation/impl-reuse-self-hygiene.stderr @@ -0,0 +1,68 @@ +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:10:76 + | +LL | macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } + | --------------------------^^^^^---- + | | | + | | `self` value is a keyword only available in methods with a `self` parameter + | `self` not allowed in an implementation +... +LL | one_line_reuse!(self); + | --------------------- in this macro invocation + | + = note: this error originates in the macro `one_line_reuse` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:10:76 + | +LL | macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } + | --------------------------^^^^^---- + | | | + | | `self` value is a keyword only available in methods with a `self` parameter + | `self` not allowed in an implementation +... +LL | one_line_reuse!(self); + | --------------------- in this macro invocation + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `one_line_reuse` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:17:22 + | +LL | macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } + | ------------------------------ `self` not allowed in an implementation +LL | one_line_reuse_expr!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:17:22 + | +LL | macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } + | ------------------------------ `self` not allowed in an implementation +LL | one_line_reuse_expr!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:24:23 + | +LL | macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } + | --------------------------------- `self` not allowed in an implementation +LL | one_line_reuse_expr2!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + +error[E0424]: expected value, found module `self` + --> $DIR/impl-reuse-self-hygiene.rs:24:23 + | +LL | macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } + | --------------------------------- `self` not allowed in an implementation +LL | one_line_reuse_expr2!(self.0); + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0424`. From 66cf18efbf0d98ccff2be6c291c1041986adf946 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 8 Apr 2026 14:59:53 +0300 Subject: [PATCH 04/15] Update some tests after rebase --- tests/ui/delegation/glob-glob-conflict.rs | 9 ++-- tests/ui/delegation/glob-glob-conflict.stderr | 50 +++++++++---------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/ui/delegation/glob-glob-conflict.rs b/tests/ui/delegation/glob-glob-conflict.rs index cb07a78b84fef..c1cd3f703e07f 100644 --- a/tests/ui/delegation/glob-glob-conflict.rs +++ b/tests/ui/delegation/glob-glob-conflict.rs @@ -3,13 +3,10 @@ trait Trait1 { fn method(&self) -> u8; - //~^ ERROR: this function takes 1 argument but 0 arguments were supplied - //~| ERROR: mismatched types } trait Trait2 { fn method(&self) -> u8; - //~^ ERROR: this function takes 1 argument but 0 arguments were supplied - //~| ERROR: mismatched types + } trait Trait { fn method(&self) -> u8; @@ -28,10 +25,14 @@ impl Trait2 for u8 { impl Trait for u8 { reuse Trait1::*; reuse Trait2::*; //~ ERROR duplicate definitions with name `method` + //~^ ERROR: this function takes 1 argument but 0 arguments were supplied + //~| ERROR: mismatched types } impl Trait for u16 { reuse Trait1::*; reuse Trait1::*; //~ ERROR duplicate definitions with name `method` + //~^ ERROR: this function takes 1 argument but 0 arguments were supplied + //~| ERROR: mismatched types } fn main() {} diff --git a/tests/ui/delegation/glob-glob-conflict.stderr b/tests/ui/delegation/glob-glob-conflict.stderr index 4259d71117b7b..f55ee333630c6 100644 --- a/tests/ui/delegation/glob-glob-conflict.stderr +++ b/tests/ui/delegation/glob-glob-conflict.stderr @@ -1,5 +1,5 @@ error[E0201]: duplicate definitions with name `method`: - --> $DIR/glob-glob-conflict.rs:30:5 + --> $DIR/glob-glob-conflict.rs:27:5 | LL | fn method(&self) -> u8; | ----------------------- item in trait @@ -10,7 +10,7 @@ LL | reuse Trait2::*; | ^^^^^^^^^^^^^^^^ duplicate definition error[E0201]: duplicate definitions with name `method`: - --> $DIR/glob-glob-conflict.rs:34:5 + --> $DIR/glob-glob-conflict.rs:33:5 | LL | fn method(&self) -> u8; | ----------------------- item in trait @@ -21,35 +21,35 @@ LL | reuse Trait1::*; | ^^^^^^^^^^^^^^^^ duplicate definition error[E0061]: this function takes 1 argument but 0 arguments were supplied - --> $DIR/glob-glob-conflict.rs:10:8 + --> $DIR/glob-glob-conflict.rs:27:19 | -LL | fn method(&self) -> u8; - | ^^^^^^ argument #1 of type `&_` is missing +LL | reuse Trait2::*; + | ^ argument #1 of type `&_` is missing | note: method defined here - --> $DIR/glob-glob-conflict.rs:10:8 + --> $DIR/glob-glob-conflict.rs:8:8 | LL | fn method(&self) -> u8; | ^^^^^^ ---- help: provide the argument | -LL | fn method(/* value */)(&self) -> u8; - | +++++++++++++ +LL | reuse Trait2::*(/* value */); + | +++++++++++++ error[E0308]: mismatched types - --> $DIR/glob-glob-conflict.rs:10:8 + --> $DIR/glob-glob-conflict.rs:27:19 | -LL | fn method(&self) -> u8; - | ^^^^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `u8` - | expected `()` because of default return type +LL | reuse Trait2::*; + | ^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `u8` + | expected `()` because of default return type error[E0061]: this function takes 1 argument but 0 arguments were supplied - --> $DIR/glob-glob-conflict.rs:5:8 + --> $DIR/glob-glob-conflict.rs:33:19 | -LL | fn method(&self) -> u8; - | ^^^^^^ argument #1 of type `&_` is missing +LL | reuse Trait1::*; + | ^ argument #1 of type `&_` is missing | note: method defined here --> $DIR/glob-glob-conflict.rs:5:8 @@ -58,17 +58,17 @@ LL | fn method(&self) -> u8; | ^^^^^^ ---- help: provide the argument | -LL | fn method(/* value */)(&self) -> u8; - | +++++++++++++ +LL | reuse Trait1::*(/* value */); + | +++++++++++++ error[E0308]: mismatched types - --> $DIR/glob-glob-conflict.rs:5:8 + --> $DIR/glob-glob-conflict.rs:33:19 | -LL | fn method(&self) -> u8; - | ^^^^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `u8` - | expected `()` because of default return type +LL | reuse Trait1::*; + | ^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `u8` + | expected `()` because of default return type error: aborting due to 6 previous errors From e756d1607d6af1e8a76e5b1e7bf8821db990772e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:45:33 +0100 Subject: [PATCH 05/15] Add `eii_impls` argument to `StaticItem` --- compiler/rustc_ast/src/ast.rs | 7 ++++ compiler/rustc_ast_lowering/src/item.rs | 13 +++++-- .../rustc_ast_pretty/src/pprust/state/item.rs | 38 +++++++++++++------ compiler/rustc_expand/src/build.rs | 1 + compiler/rustc_parse/src/parser/item.rs | 12 +++++- compiler/rustc_resolve/src/def_collector.rs | 1 + .../clippy/clippy_utils/src/ast_utils/mod.rs | 4 ++ 7 files changed, 60 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ae4989fcbc6c9..ad32fe7e488c1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3918,6 +3918,13 @@ pub struct StaticItem { pub mutability: Mutability, pub expr: Option>, pub define_opaque: Option>, + + /// This static is an implementation of an externally implementable item (EII). + /// This means, there was an EII declared somewhere and this static is the + /// implementation that should be used for the declaration. + /// + /// For statics, there may be at most one `EiiImpl`, but this is a `ThinVec` to make usages of this field nicer. + pub eii_impls: ThinVec, } #[derive(Clone, Encodable, Decodable, Debug, Walkable)] diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 072d4803bf459..d9c2c12113199 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -213,8 +213,14 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { i: &ItemKind, ) -> Vec { match i { - ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(), - ItemKind::Fn(box Fn { eii_impls, .. }) => { + ItemKind::Fn(box Fn { eii_impls, .. }) + | ItemKind::Static(box StaticItem { eii_impls, .. }) + if eii_impls.is_empty() => + { + Vec::new() + } + ItemKind::Fn(box Fn { eii_impls, .. }) + | ItemKind::Static(box StaticItem { eii_impls, .. }) => { vec![hir::Attribute::Parsed(AttributeKind::EiiImpls( eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(), ))] @@ -226,7 +232,6 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { ItemKind::ExternCrate(..) | ItemKind::Use(..) - | ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::ConstBlock(..) | ItemKind::Mod(..) @@ -302,6 +307,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { mutability: m, expr: e, define_opaque, + eii_impls: _, }) => { let ident = self.lower_ident(*ident); let ty = self @@ -817,6 +823,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { expr: _, safety, define_opaque, + eii_impls: _, }) => { let ty = self .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 3e9c596148343..201fa63bfa33c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -43,6 +43,7 @@ impl<'a> State<'a> { expr, safety, define_opaque, + eii_impls, }) => self.print_item_const( *ident, Some(*mutability), @@ -53,6 +54,7 @@ impl<'a> State<'a> { *safety, ast::Defaultness::Implicit, define_opaque.as_deref(), + eii_impls, ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { defaultness, @@ -93,8 +95,12 @@ impl<'a> State<'a> { safety: ast::Safety, defaultness: ast::Defaultness, define_opaque: Option<&[(ast::NodeId, ast::Path)]>, + eii_impls: &[EiiImpl], ) { self.print_define_opaques(define_opaque); + for eii_impl in eii_impls { + self.print_eii_impl(eii_impl); + } let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_safety(safety); @@ -191,6 +197,7 @@ impl<'a> State<'a> { mutability: mutbl, expr: body, define_opaque, + eii_impls, }) => { self.print_safety(*safety); self.print_item_const( @@ -203,6 +210,7 @@ impl<'a> State<'a> { ast::Safety::Default, ast::Defaultness::Implicit, define_opaque.as_deref(), + eii_impls, ); } ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => { @@ -234,6 +242,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), + &[], ); } ast::ItemKind::Fn(func) => { @@ -602,6 +611,7 @@ impl<'a> State<'a> { ast::Safety::Default, *defaultness, define_opaque.as_deref(), + &[], ); } ast::AssocItemKind::Type(box ast::TyAlias { @@ -703,18 +713,8 @@ impl<'a> State<'a> { self.print_define_opaques(define_opaque.as_deref()); - for EiiImpl { eii_macro_path, impl_safety, .. } in eii_impls { - self.word("#["); - if let Safety::Unsafe(..) = impl_safety { - self.word("unsafe"); - self.popen(); - } - self.print_path(eii_macro_path, false, 0); - if let Safety::Unsafe(..) = impl_safety { - self.pclose(); - } - self.word("]"); - self.hardbreak(); + for eii_impl in eii_impls { + self.print_eii_impl(eii_impl); } let body_cb_ib = body.as_ref().map(|body| (body, self.head(""))); @@ -741,6 +741,20 @@ impl<'a> State<'a> { } } + fn print_eii_impl(&mut self, eii: &ast::EiiImpl) { + self.word("#["); + if let Safety::Unsafe(..) = eii.impl_safety { + self.word("unsafe"); + self.popen(); + } + self.print_path(&eii.eii_macro_path, false, 0); + if let Safety::Unsafe(..) = eii.impl_safety { + self.pclose(); + } + self.word("]"); + self.hardbreak(); + } + fn print_define_opaques(&mut self, define_opaque: Option<&[(ast::NodeId, ast::Path)]>) { if let Some(define_opaque) = define_opaque { self.word("#[define_opaque("); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 05e6b78132aeb..01886a97f55a2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -715,6 +715,7 @@ impl<'a> ExtCtxt<'a> { mutability, expr: Some(expr), define_opaque: None, + eii_impls: Default::default(), } .into(), ), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index ab3683f598208..df85aa7d041a5 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1235,6 +1235,7 @@ impl<'a> Parser<'a> { mutability: _, expr, define_opaque, + eii_impls: _, }) => { self.dcx().emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { @@ -1503,6 +1504,7 @@ impl<'a> Parser<'a> { }, safety: Safety::Default, define_opaque: None, + eii_impls: ThinVec::default(), })) } _ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"), @@ -1636,7 +1638,15 @@ impl<'a> Parser<'a> { self.expect_semi()?; - let item = StaticItem { ident, ty, safety, mutability, expr, define_opaque: None }; + let item = StaticItem { + ident, + ty, + safety, + mutability, + expr, + define_opaque: None, + eii_impls: ThinVec::default(), + }; Ok(ItemKind::Static(Box::new(item))) } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 427a75c6bff4d..78dcb0620e5b1 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -274,6 +274,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { expr: _, safety, define_opaque: _, + eii_impls: _, }) => { let safety = match safety { ast::Safety::Unsafe(_) | ast::Safety::Default => hir::Safety::Unsafe, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index cfff7da60a6a4..c96c0649753fd 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -339,6 +339,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: le, safety: ls, define_opaque: _, + eii_impls: _, }), Static(box StaticItem { ident: ri, @@ -347,6 +348,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { expr: re, safety: rs, define_opaque: _, + eii_impls: _, }), ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()), ( @@ -540,6 +542,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: le, safety: ls, define_opaque: _, + eii_impls: _, }), Static(box StaticItem { ident: ri, @@ -548,6 +551,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { expr: re, safety: rs, define_opaque: _, + eii_impls: _, }), ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs, ( From 607b0620e2bfd8ee0c7ed42e83ec35aa88ed6984 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 27 Mar 2026 22:21:22 +0100 Subject: [PATCH 06/15] Analysis for externally implementable statics --- .../src/attributes/codegen_attrs.rs | 3 +- compiler/rustc_builtin_macros/src/eii.rs | 158 +++++++++++------- compiler/rustc_builtin_macros/src/errors.rs | 20 ++- .../src/check/compare_eii.rs | 132 +++++++++++++-- .../rustc_hir_analysis/src/check/wfcheck.rs | 37 +++- compiler/rustc_hir_analysis/src/errors.rs | 25 +++ compiler/rustc_passes/src/check_attr.rs | 4 +- compiler/rustc_passes/src/errors.rs | 4 +- compiler/rustc_resolve/src/late.rs | 43 +++-- 9 files changed, 328 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ff94cf50adf67..9625a1f04f26e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -727,7 +727,8 @@ pub(crate) struct RustcEiiForeignItemParser; impl NoArgsAttributeParser for RustcEiiForeignItemParser { const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]); + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem; } diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 7b651ed848288..9dab90b72a027 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -10,8 +10,8 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, - EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction, - EiiSharedMacroInStatementPosition, + EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, }; /// ```rust @@ -73,44 +73,63 @@ fn eii_( }); return vec![orig_item]; } else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { + ecx.dcx().emit_err(EiiSharedMacroTarget { span: eii_attr_span, name: path_to_string(&meta_item.path), }); return vec![orig_item]; }; - let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = - item.as_ref() - else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span: eii_attr_span, - name: path_to_string(&meta_item.path), - }); - return vec![Annotatable::Item(item)]; + let ast::Item { attrs, id: _, span: _, vis, kind, tokens: _ } = item.as_ref(); + let (item_span, foreign_item_name) = match kind { + ItemKind::Fn(func) => (func.sig.span, func.ident), + ItemKind::Static(stat) => { + // Statics with a default are not supported yet + if let Some(stat_body) = &stat.expr { + ecx.dcx().emit_err(EiiStaticDefault { + span: stat_body.span, + name: path_to_string(&meta_item.path), + }); + return vec![]; + } + // Statics must have an explicit name for the eii + if meta_item.is_word() { + ecx.dcx().emit_err(EiiStaticArgumentRequired { + span: eii_attr_span, + name: path_to_string(&meta_item.path), + }); + return vec![]; + } + (item.span, stat.ident) + } + _ => { + ecx.dcx().emit_err(EiiSharedMacroTarget { + span: eii_attr_span, + name: path_to_string(&meta_item.path), + }); + return vec![Annotatable::Item(item)]; + } }; + // only clone what we need let attrs = attrs.clone(); - let func = (**func).clone(); let vis = vis.clone(); let attrs_from_decl = filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); - let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else { + let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else { // we don't need to wrap in Annotatable::Stmt conditionally since // EII can't be used on items in statement position return vec![Annotatable::Item(item)]; }; - // span of the declaring item without attributes - let item_span = func.sig.span; - let foreign_item_name = func.ident; - let mut module_items = Vec::new(); - if func.body.is_some() { - module_items.push(generate_default_impl( + if let ItemKind::Fn(func) = kind + && func.body.is_some() + { + module_items.push(generate_default_func_impl( ecx, &func, impl_unsafe, @@ -125,7 +144,7 @@ fn eii_( ecx, eii_attr_span, item_span, - func, + kind, vis, &attrs_from_decl, )); @@ -148,11 +167,11 @@ fn eii_( /// declaration of the EII. fn name_for_impl_macro( ecx: &mut ExtCtxt<'_>, - func: &ast::Fn, + item_ident: Ident, meta_item: &MetaItem, ) -> Result { if meta_item.is_word() { - Ok(func.ident) + Ok(item_ident) } else if let Some([first]) = meta_item.meta_item_list() && let Some(m) = first.meta_item() && m.path.segments.len() == 1 @@ -190,7 +209,7 @@ fn filter_attrs_for_multiple_eii_attr( .collect() } -fn generate_default_impl( +fn generate_default_func_impl( ecx: &mut ExtCtxt<'_>, func: &ast::Fn, impl_unsafe: bool, @@ -257,7 +276,7 @@ fn generate_foreign_item( ecx: &mut ExtCtxt<'_>, eii_attr_span: Span, item_span: Span, - mut func: ast::Fn, + item_kind: &ItemKind, vis: Visibility, attrs_from_decl: &[Attribute], ) -> Box { @@ -268,30 +287,21 @@ fn generate_foreign_item( // This attribute makes sure that we later know that this foreign item's symbol should not be. foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span)); - let abi = match func.sig.header.ext { - // extern "X" fn => extern "X" {} - ast::Extern::Explicit(lit, _) => Some(lit), - // extern fn => extern {} - ast::Extern::Implicit(_) => None, - // fn => extern "Rust" {} - ast::Extern::None => Some(ast::StrLit { - symbol: sym::Rust, - suffix: None, - symbol_unescaped: sym::Rust, - style: ast::StrStyle::Cooked, - span: eii_attr_span, - }), + // We set the abi to the default "rust" abi, which can be overridden by `generate_foreign_func`, + // if a specific abi was specified on the EII function + let mut abi = Some(ast::StrLit { + symbol: sym::Rust, + suffix: None, + symbol_unescaped: sym::Rust, + style: ast::StrStyle::Cooked, + span: eii_attr_span, + }); + let foreign_kind = match item_kind { + ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi), + ItemKind::Static(stat) => generate_foreign_static(stat.clone()), + _ => unreachable!("Target was checked earlier"), }; - // ABI has been moved to the extern {} block, so we remove it from the fn item. - func.sig.header.ext = ast::Extern::None; - func.body = None; - - // And mark safe functions explicitly as `safe fn`. - if func.sig.header.safety == ast::Safety::Default { - func.sig.header.safety = ast::Safety::Safe(func.sig.span); - } - ecx.item( eii_attr_span, ThinVec::new(), @@ -304,13 +314,46 @@ fn generate_foreign_item( id: ast::DUMMY_NODE_ID, span: item_span, vis, - kind: ast::ForeignItemKind::Fn(Box::new(func.clone())), + kind: foreign_kind, tokens: None, })]), }), ) } +fn generate_foreign_func( + mut func: Box, + abi: &mut Option, +) -> ast::ForeignItemKind { + match func.sig.header.ext { + // extern "X" fn => extern "X" {} + ast::Extern::Explicit(lit, _) => *abi = Some(lit), + // extern fn => extern {} + ast::Extern::Implicit(_) => *abi = None, + // no abi was specified, so we keep the default + ast::Extern::None => {} + }; + + // ABI has been moved to the extern {} block, so we remove it from the fn item. + func.sig.header.ext = ast::Extern::None; + func.body = None; + + // And mark safe functions explicitly as `safe fn`. + if func.sig.header.safety == ast::Safety::Default { + func.sig.header.safety = ast::Safety::Safe(func.sig.span); + } + + ast::ForeignItemKind::Fn(func) +} + +fn generate_foreign_static(mut stat: Box) -> ast::ForeignItemKind { + if stat.safety == ast::Safety::Default { + stat.safety = ast::Safety::Safe(stat.ident.span); + } + + ast::ForeignItemKind::Static(stat) +} + /// Generate a stub macro (a bit like in core) that will roughly look like: /// /// ```rust, ignore, example @@ -453,19 +496,18 @@ pub(crate) fn eii_shared_macro( { item } else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span, - name: path_to_string(&meta_item.path), - }); + ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); return vec![item]; }; - let ItemKind::Fn(f) = &mut i.kind else { - ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { - span, - name: path_to_string(&meta_item.path), - }); - return vec![item]; + let eii_impls = match &mut i.kind { + ItemKind::Fn(func) => &mut func.eii_impls, + ItemKind::Static(stat) => &mut stat.eii_impls, + _ => { + ecx.dcx() + .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); + return vec![item]; + } }; let is_default = if meta_item.is_word() { @@ -483,7 +525,7 @@ pub(crate) fn eii_shared_macro( return vec![item]; }; - f.eii_impls.push(EiiImpl { + eii_impls.push(EiiImpl { node_id: DUMMY_NODE_ID, inner_span: meta_item.path.span, eii_macro_path: meta_item.path.clone(), diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b5ac84337465a..4b572c2f34ac0 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1117,8 +1117,24 @@ pub(crate) struct EiiExternTargetExpectedUnsafe { } #[derive(Diagnostic)] -#[diag("`#[{$name}]` is only valid on functions")] -pub(crate) struct EiiSharedMacroExpectedFunction { +#[diag("`#[{$name}]` is only valid on functions and statics")] +pub(crate) struct EiiSharedMacroTarget { + #[primary_span] + pub span: Span, + pub name: String, +} + +#[derive(Diagnostic)] +#[diag("`#[{$name}]` cannot be used on statics with a value")] +pub(crate) struct EiiStaticDefault { + #[primary_span] + pub span: Span, + pub name: String, +} + +#[derive(Diagnostic)] +#[diag("`#[{$name}]` requires the name as an explicit argument when used on a static")] +pub(crate) struct EiiStaticArgumentRequired { #[primary_span] pub span: Span, pub name: String, diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 29213058d1d5e..956e68773b796 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -15,7 +15,7 @@ use rustc_hir::{self as hir, FnSig, HirId, ItemKind, find_attr}; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCause, ObligationCauseCode}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypingMode}; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::InferCtxtRegionExt; @@ -26,7 +26,10 @@ use super::potentially_plural_count; use crate::check::compare_impl_item::{ CheckNumberOfEarlyBoundRegionsError, check_number_of_early_bound_regions, }; -use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; +use crate::errors::{ + EiiDefkindMismatch, EiiDefkindMismatchStaticMutability, EiiDefkindMismatchStaticSafety, + EiiWithGenerics, LifetimesOrBoundsMismatchOnEii, +}; /// Checks whether the signature of some `external_impl`, matches /// the signature of `declaration`, which it is supposed to be compatible @@ -38,14 +41,7 @@ pub(crate) fn compare_eii_function_types<'tcx>( eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { - // Error recovery can resolve the EII target to another value item with the same name, - // such as a tuple-struct constructor. Skip the comparison in that case and rely on the - // earlier name-resolution error instead of ICEing while building EII diagnostics. - // See . - if !is_foreign_function(tcx, foreign_item) { - return Ok(()); - } - + check_eii_target(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; check_is_structurally_compatible(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; let external_impl_span = tcx.def_span(external_impl); @@ -152,6 +148,118 @@ pub(crate) fn compare_eii_function_types<'tcx>( Ok(()) } +pub(crate) fn compare_eii_statics<'tcx>( + tcx: TyCtxt<'tcx>, + external_impl: LocalDefId, + external_impl_ty: Ty<'tcx>, + foreign_item: DefId, + eii_name: Symbol, + eii_attr_span: Span, +) -> Result<(), ErrorGuaranteed> { + check_eii_target(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; + + let external_impl_span = tcx.def_span(external_impl); + let cause = ObligationCause::new( + external_impl_span, + external_impl, + ObligationCauseCode::CompareEii { external_impl, declaration: foreign_item }, + ); + + let param_env = ParamEnv::empty(); + + let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); + let ocx = ObligationCtxt::new_with_diagnostics(infcx); + + let declaration_ty = tcx.type_of(foreign_item).instantiate_identity(); + debug!(?declaration_ty); + + // FIXME: Copied over from compare impl items, same issue: + // We'd want to keep more accurate spans than "the method signature" when + // processing the comparison between the trait and impl fn, but we sadly lose them + // and point at the whole signature when a trait bound or specific input or output + // type would be more appropriate. In other places we have a `Vec` + // corresponding to their `Vec`, but we don't have that here. + // Fixing this would improve the output of test `issue-83765.rs`. + let result = ocx.sup(&cause, param_env, declaration_ty, external_impl_ty); + + if let Err(terr) = result { + debug!(?external_impl_ty, ?declaration_ty, ?terr, "sub_types failed"); + + let mut diag = struct_span_code_err!( + tcx.dcx(), + cause.span, + E0806, + "static `{}` has a type that is incompatible with the declaration of `#[{eii_name}]`", + tcx.item_name(external_impl) + ); + diag.span_note(eii_attr_span, "expected this because of this attribute"); + + return Err(diag.emit()); + } + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.evaluate_obligations_error_on_ambiguity(); + if !errors.is_empty() { + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); + return Err(reported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let errors = infcx.resolve_regions(external_impl, param_env, []); + if !errors.is_empty() { + return Err(infcx + .tainted_by_errors() + .unwrap_or_else(|| infcx.err_ctxt().report_region_errors(external_impl, &errors))); + } + + Ok(()) +} + +fn check_eii_target( + tcx: TyCtxt<'_>, + external_impl: LocalDefId, + foreign_item: DefId, + eii_name: Symbol, + eii_attr_span: Span, +) -> Result<(), ErrorGuaranteed> { + // Error recovery can resolve the EII target to another value item with the same name, + // such as a tuple-struct constructor. Skip the comparison in that case and rely on the + // earlier name-resolution error instead of ICEing while building EII diagnostics. + // See . + if !tcx.is_foreign_item(foreign_item) { + return Err(tcx.dcx().delayed_bug("EII is a foreign item")); + } + let expected_kind = tcx.def_kind(foreign_item); + let actual_kind = tcx.def_kind(external_impl); + + match expected_kind { + // Correct target + _ if expected_kind == actual_kind => Ok(()), + DefKind::Static { mutability: m1, safety: s1, .. } + if let DefKind::Static { mutability: m2, safety: s2, .. } = actual_kind => + { + Err(if s1 != s2 { + tcx.dcx().emit_err(EiiDefkindMismatchStaticSafety { span: eii_attr_span, eii_name }) + } else if m1 != m2 { + tcx.dcx() + .emit_err(EiiDefkindMismatchStaticMutability { span: eii_attr_span, eii_name }) + } else { + unreachable!() + }) + } + // Not checked by attr target checking + DefKind::Fn | DefKind::Static { .. } => Err(tcx.dcx().emit_err(EiiDefkindMismatch { + span: eii_attr_span, + eii_name, + expected_kind: expected_kind.descr(foreign_item), + })), + // Checked by attr target checking + _ => Err(tcx.dcx().delayed_bug("Attribute should not be allowed by target checking")), + } +} + /// Checks a bunch of different properties of the impl/trait methods for /// compatibility, such as asyncness, number of argument, self receiver kind, /// and number of early- and late-bound generics. @@ -451,7 +559,3 @@ fn get_declaration_sig<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Option<&' let hir_id: HirId = tcx.local_def_id_to_hir_id(def_id); tcx.hir_fn_sig_by_hir_id(hir_id) } - -fn is_foreign_function(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.is_foreign_item(def_id) && matches!(tcx.def_kind(def_id), DefKind::Fn) -} diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c7b87db5971fa..88f8abaf19e75 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -41,7 +41,7 @@ use rustc_trait_selection::traits::{ }; use tracing::{debug, instrument}; -use super::compare_eii::compare_eii_function_types; +use super::compare_eii::{compare_eii_function_types, compare_eii_statics}; use crate::autoderef::Autoderef; use crate::constrained_generic_params::{Parameter, identify_constrained_generic_params}; use crate::errors; @@ -1208,7 +1208,7 @@ fn check_item_fn( decl: &hir::FnDecl<'_>, ) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, def_id, |wfcx| { - check_eiis(tcx, def_id); + check_eiis_fn(tcx, def_id); let sig = tcx.fn_sig(def_id).instantiate_identity(); check_fn_or_method(wfcx, sig, decl, def_id); @@ -1216,7 +1216,7 @@ fn check_item_fn( }) } -fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. for EiiImpl { resolution, span, .. } in @@ -1243,6 +1243,33 @@ fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { } } +fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) { + // does the function have an EiiImpl attribute? that contains the defid of a *macro* + // that was used to mark the implementation. This is a two step process. + for EiiImpl { resolution, span, .. } in + find_attr!(tcx, def_id, EiiImpls(impls) => impls).into_iter().flatten() + { + let (foreign_item, name) = match resolution { + EiiImplResolution::Macro(def_id) => { + // we expect this macro to have the `EiiMacroFor` attribute, that points to a function + // signature that we'd like to compare the function we're currently checking with + if let Some(foreign_item) = + find_attr!(tcx, *def_id, EiiDeclaration(EiiDecl {foreign_item: t, ..}) => *t) + { + (foreign_item, tcx.item_name(*def_id)) + } else { + tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); + continue; + } + } + EiiImplResolution::Known(decl) => (decl.foreign_item, decl.name.name), + EiiImplResolution::Error(_eg) => continue, + }; + + let _ = compare_eii_statics(tcx, def_id, ty, foreign_item, name, *span); + } +} + #[instrument(level = "debug", skip(tcx))] pub(crate) fn check_static_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -1251,6 +1278,10 @@ pub(crate) fn check_static_item<'tcx>( should_check_for_sync: bool, ) -> Result<(), ErrorGuaranteed> { enter_wf_checking_ctxt(tcx, item_id, |wfcx| { + if should_check_for_sync { + check_eiis_static(tcx, item_id, ty); + } + let span = tcx.ty_span(item_id); let loc = Some(WellFormedLoc::Ty(item_id)); let item_ty = wfcx.deeply_normalize(span, loc, ty); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c55b9e384c55b..4e56909260ad1 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1923,3 +1923,28 @@ pub(crate) struct ImplUnpinForPinProjectedType { pub adt_span: Span, pub adt_name: Symbol, } + +#[derive(Diagnostic)] +#[diag("`#[{$eii_name}]` must be used on a {$expected_kind}")] +pub(crate) struct EiiDefkindMismatch { + #[primary_span] + pub span: Span, + pub eii_name: Symbol, + pub expected_kind: &'static str, +} + +#[derive(Diagnostic)] +#[diag("mutability does not match with the definition of`#[{$eii_name}]`")] +pub(crate) struct EiiDefkindMismatchStaticMutability { + #[primary_span] + pub span: Span, + pub eii_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("safety does not match with the definition of`#[{$eii_name}]`")] +pub(crate) struct EiiDefkindMismatchStaticSafety { + #[primary_span] + pub span: Span, + pub eii_name: Symbol, +} diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 896e9d01777ff..4f81f91300307 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -551,9 +551,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls { match target { - Target::Fn => {} + Target::Fn | Target::Static => {} _ => { - self.dcx().emit_err(errors::EiiImplNotFunction { span: *span }); + self.dcx().emit_err(errors::EiiImplTarget { span: *span }); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 5de43f24b2dc6..9d4f7a3cbbea2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1175,8 +1175,8 @@ pub(crate) struct ReprAlignShouldBeAlignStatic { } #[derive(Diagnostic)] -#[diag("`eii_macro_for` is only valid on functions")] -pub(crate) struct EiiImplNotFunction { +#[diag("`eii_macro_for` is only valid on functions and statics")] +pub(crate) struct EiiImplTarget { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2c287045be7a9..540e7ec52f51c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1095,21 +1095,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc debug!("(resolving function) entering function"); if let FnKind::Fn(_, _, f) = fn_kind { - for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in &f.eii_impls - { - // See docs on the `known_eii_macro_resolution` field: - // if we already know the resolution statically, don't bother resolving it. - if let Some(target) = known_eii_macro_resolution { - self.smart_resolve_path( - *node_id, - &None, - &target.foreign_item, - PathSource::ExternItemImpl, - ); - } else { - self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); - } - } + self.resolve_eii(&f.eii_impls); } // Create a value rib for the function. @@ -2905,7 +2891,14 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.parent_scope.module = orig_module; } - ItemKind::Static(box ast::StaticItem { ident, ty, expr, define_opaque, .. }) => { + ItemKind::Static(box ast::StaticItem { + ident, + ty, + expr, + define_opaque, + eii_impls, + .. + }) => { self.with_static_rib(def_kind, |this| { this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| { this.visit_ty(ty); @@ -2917,6 +2910,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }); self.resolve_define_opaques(define_opaque); + self.resolve_eii(&eii_impls); } ItemKind::Const(box ast::ConstItem { @@ -5496,6 +5490,23 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } } } + + fn resolve_eii(&mut self, eii_impls: &[EiiImpl]) { + for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in eii_impls { + // See docs on the `known_eii_macro_resolution` field: + // if we already know the resolution statically, don't bother resolving it. + if let Some(target) = known_eii_macro_resolution { + self.smart_resolve_path( + *node_id, + &None, + &target.foreign_item, + PathSource::ExternItemImpl + ); + } else { + self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); + } + } + } } /// Walks the whole crate in DFS order, visiting each item, counting the declared number of From c7e194f215fc5e4273a12438eded7e79c95d9c57 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:47:34 +0100 Subject: [PATCH 07/15] Fix codegen for `add_static_aliases` --- compiler/rustc_codegen_llvm/src/mono_item.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 0783282bc6e56..2c0a6ff01018c 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -135,7 +135,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { let ty = self.get_type_of_global(aliasee); for (alias, linkage, visibility) in aliases { - let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); + let instance = Instance::mono(self.tcx, *alias); + let symbol_name = self.tcx.symbol_name(instance); tracing::debug!("STATIC ALIAS: {alias:?} {linkage:?} {visibility:?}"); let lldecl = llvm::add_alias( @@ -145,6 +146,13 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { aliasee, &CString::new(symbol_name.name).unwrap(), ); + // Add the alias name to the set of cached items, so there is no duplicate + // instance added to it during the normal `external static` codegen + let prev_entry = self.instances.borrow_mut().insert(instance, lldecl); + + // If there already was a previous entry, then `add_static_aliases` was called multiple times for the same `alias` + // which would result in incorrect codegen + assert!(prev_entry.is_none(), "An instance was already present for {instance:?}"); llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility)); llvm::set_linkage(lldecl, base::linkage_to_llvm(*linkage)); From fca29ada741148b90f3afafa2926c1447733376b Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Thu, 26 Mar 2026 16:47:53 +0100 Subject: [PATCH 08/15] Uitests for external statics --- tests/ui/eii/attribute_targets.stderr | 36 +++++++++---------- tests/ui/eii/default/call_default.rs | 2 +- tests/ui/eii/default/call_default_panics.rs | 2 +- tests/ui/eii/default/call_impl.rs | 2 +- tests/ui/eii/default/local_crate.rs | 2 +- tests/ui/eii/default/local_crate_explicit.rs | 2 +- tests/ui/eii/duplicate/duplicate1.rs | 2 +- tests/ui/eii/duplicate/duplicate2.rs | 2 +- tests/ui/eii/duplicate/duplicate3.rs | 2 +- tests/ui/eii/duplicate/multiple_impls.rs | 2 +- tests/ui/eii/error_statement_position.stderr | 2 +- tests/ui/eii/errors.rs | 6 ++-- tests/ui/eii/errors.stderr | 20 ++++------- tests/ui/eii/linking/codegen_cross_crate.rs | 2 +- tests/ui/eii/linking/codegen_single_crate.rs | 2 +- tests/ui/eii/linking/same-symbol.rs | 2 +- tests/ui/eii/shadow_builtin.rs | 17 +++++++++ tests/ui/eii/shadow_builtin.stderr | 26 ++++++++++++++ tests/ui/eii/static/argument_required.rs | 11 ++++++ tests/ui/eii/static/argument_required.stderr | 8 +++++ .../eii/static/auxiliary/cross_crate_decl.rs | 6 ++++ .../eii/static/auxiliary/cross_crate_def.rs | 9 +++++ tests/ui/eii/static/cross_crate_decl.rs | 21 +++++++++++ .../ui/eii/static/cross_crate_decl.run.stdout | 2 ++ tests/ui/eii/static/cross_crate_def.rs | 18 ++++++++++ .../ui/eii/static/cross_crate_def.run.stdout | 2 ++ tests/ui/eii/static/duplicate.rs | 25 +++++++++++++ tests/ui/eii/static/duplicate.stderr | 13 +++++++ tests/ui/eii/static/mismatch_fn_static.rs | 14 ++++++++ tests/ui/eii/static/mismatch_fn_static.stderr | 8 +++++ tests/ui/eii/static/mismatch_mut.rs | 21 +++++++++++ tests/ui/eii/static/mismatch_mut.stderr | 8 +++++ tests/ui/eii/static/mismatch_safety.rs | 21 +++++++++++ tests/ui/eii/static/mismatch_safety.stderr | 8 +++++ tests/ui/eii/static/mismatch_static_fn.rs | 16 +++++++++ tests/ui/eii/static/mismatch_static_fn.stderr | 8 +++++ tests/ui/eii/static/multiple_impls.rs | 21 +++++++++++ tests/ui/eii/static/multiple_impls.run.stdout | 1 + tests/ui/eii/static/mut.rs | 22 ++++++++++++ tests/ui/eii/static/mut.run.stdout | 2 ++ tests/ui/eii/static/same_address.rs | 21 +++++++++++ tests/ui/eii/static/simple.rs | 22 ++++++++++++ tests/ui/eii/static/simple.run.stdout | 2 ++ tests/ui/eii/static/subtype.rs | 18 ++++++++++ tests/ui/eii/static/subtype_wrong.rs | 17 +++++++++ tests/ui/eii/static/subtype_wrong.stderr | 12 +++++++ tests/ui/eii/static/wrong_ty.rs | 18 ++++++++++ tests/ui/eii/static/wrong_ty.stderr | 15 ++++++++ 48 files changed, 473 insertions(+), 48 deletions(-) create mode 100644 tests/ui/eii/shadow_builtin.rs create mode 100644 tests/ui/eii/shadow_builtin.stderr create mode 100644 tests/ui/eii/static/argument_required.rs create mode 100644 tests/ui/eii/static/argument_required.stderr create mode 100644 tests/ui/eii/static/auxiliary/cross_crate_decl.rs create mode 100644 tests/ui/eii/static/auxiliary/cross_crate_def.rs create mode 100644 tests/ui/eii/static/cross_crate_decl.rs create mode 100644 tests/ui/eii/static/cross_crate_decl.run.stdout create mode 100644 tests/ui/eii/static/cross_crate_def.rs create mode 100644 tests/ui/eii/static/cross_crate_def.run.stdout create mode 100644 tests/ui/eii/static/duplicate.rs create mode 100644 tests/ui/eii/static/duplicate.stderr create mode 100644 tests/ui/eii/static/mismatch_fn_static.rs create mode 100644 tests/ui/eii/static/mismatch_fn_static.stderr create mode 100644 tests/ui/eii/static/mismatch_mut.rs create mode 100644 tests/ui/eii/static/mismatch_mut.stderr create mode 100644 tests/ui/eii/static/mismatch_safety.rs create mode 100644 tests/ui/eii/static/mismatch_safety.stderr create mode 100644 tests/ui/eii/static/mismatch_static_fn.rs create mode 100644 tests/ui/eii/static/mismatch_static_fn.stderr create mode 100644 tests/ui/eii/static/multiple_impls.rs create mode 100644 tests/ui/eii/static/multiple_impls.run.stdout create mode 100644 tests/ui/eii/static/mut.rs create mode 100644 tests/ui/eii/static/mut.run.stdout create mode 100644 tests/ui/eii/static/same_address.rs create mode 100644 tests/ui/eii/static/simple.rs create mode 100644 tests/ui/eii/static/simple.run.stdout create mode 100644 tests/ui/eii/static/subtype.rs create mode 100644 tests/ui/eii/static/subtype_wrong.rs create mode 100644 tests/ui/eii/static/subtype_wrong.stderr create mode 100644 tests/ui/eii/static/wrong_ty.rs create mode 100644 tests/ui/eii/static/wrong_ty.stderr diff --git a/tests/ui/eii/attribute_targets.stderr b/tests/ui/eii/attribute_targets.stderr index bf04c323c95ca..8166609e19740 100644 --- a/tests/ui/eii/attribute_targets.stderr +++ b/tests/ui/eii/attribute_targets.stderr @@ -1,106 +1,106 @@ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:7:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:9:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:13:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:15:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:21:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:23:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:27:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:29:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:32:5 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:34:5 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:39:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:41:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:44:5 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:46:5 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:51:1 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:53:1 | LL | #[eii] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/attribute_targets.rs:56:5 | LL | #[foo] | ^^^^^^ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/attribute_targets.rs:58:5 | LL | #[eii] diff --git a/tests/ui/eii/default/call_default.rs b/tests/ui/eii/default/call_default.rs index b60a1dd0b2156..8806c7fa7d8ce 100644 --- a/tests/ui/eii/default/call_default.rs +++ b/tests/ui/eii/default/call_default.rs @@ -3,7 +3,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // When there's no explicit declaration, the default should be called from the declaring crate. diff --git a/tests/ui/eii/default/call_default_panics.rs b/tests/ui/eii/default/call_default_panics.rs index 96b2742aa8e0a..db664e0cbcb0d 100644 --- a/tests/ui/eii/default/call_default_panics.rs +++ b/tests/ui/eii/default/call_default_panics.rs @@ -5,7 +5,7 @@ //@ needs-unwind //@ exec-env:RUST_BACKTRACE=1 //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // A small test to make sure that unwinding works properly. // diff --git a/tests/ui/eii/default/call_impl.rs b/tests/ui/eii/default/call_impl.rs index b887697574892..1a972774beaeb 100644 --- a/tests/ui/eii/default/call_impl.rs +++ b/tests/ui/eii/default/call_impl.rs @@ -4,7 +4,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // When an explicit implementation is given in one dependency, and the declaration is in another, diff --git a/tests/ui/eii/default/local_crate.rs b/tests/ui/eii/default/local_crate.rs index d98c2fac4234e..fd4fd459c52fb 100644 --- a/tests/ui/eii/default/local_crate.rs +++ b/tests/ui/eii/default/local_crate.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // In the same crate, when there's no explicit declaration, the default should be called. diff --git a/tests/ui/eii/default/local_crate_explicit.rs b/tests/ui/eii/default/local_crate_explicit.rs index a4cc54fcd31fc..200905b8753a0 100644 --- a/tests/ui/eii/default/local_crate_explicit.rs +++ b/tests/ui/eii/default/local_crate_explicit.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests EIIs with default implementations. // In the same crate, the explicit implementation should get priority. diff --git a/tests/ui/eii/duplicate/duplicate1.rs b/tests/ui/eii/duplicate/duplicate1.rs index 3269778aca0c4..2128cac70eb30 100644 --- a/tests/ui/eii/duplicate/duplicate1.rs +++ b/tests/ui/eii/duplicate/duplicate1.rs @@ -2,7 +2,7 @@ //@ aux-build: impl1.rs //@ aux-build: impl2.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // tests that EIIs error properly, even if the conflicting implementations live in another crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate2.rs b/tests/ui/eii/duplicate/duplicate2.rs index 4c883d28d74a4..b0f1b1266e4ca 100644 --- a/tests/ui/eii/duplicate/duplicate2.rs +++ b/tests/ui/eii/duplicate/duplicate2.rs @@ -3,7 +3,7 @@ //@ aux-build: impl2.rs //@ aux-build: impl3.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests the error message when there are multiple implementations of an EII in many crates. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate3.rs b/tests/ui/eii/duplicate/duplicate3.rs index b046760745097..4b2b0fc111b58 100644 --- a/tests/ui/eii/duplicate/duplicate3.rs +++ b/tests/ui/eii/duplicate/duplicate3.rs @@ -4,7 +4,7 @@ //@ aux-build: impl3.rs //@ aux-build: impl4.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests the error message when there are multiple implementations of an EII in many crates. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/multiple_impls.rs b/tests/ui/eii/duplicate/multiple_impls.rs index c02c783223ac3..5ce2a27e16957 100644 --- a/tests/ui/eii/duplicate/multiple_impls.rs +++ b/tests/ui/eii/duplicate/multiple_impls.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether one function could implement two EIIs. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/error_statement_position.stderr b/tests/ui/eii/error_statement_position.stderr index f14e6c33e64f5..c6af18044db8d 100644 --- a/tests/ui/eii/error_statement_position.stderr +++ b/tests/ui/eii/error_statement_position.stderr @@ -1,4 +1,4 @@ -error: `#[eii]` is only valid on functions +error: `#[eii]` is only valid on functions and statics --> $DIR/error_statement_position.rs:8:5 | LL | #[eii] diff --git a/tests/ui/eii/errors.rs b/tests/ui/eii/errors.rs index 5fcf33336b402..bc6c17f463a78 100644 --- a/tests/ui/eii/errors.rs +++ b/tests/ui/eii/errors.rs @@ -25,11 +25,9 @@ unsafe extern "Rust" { safe fn bar(x: u64) -> u64; } -#[foo] //~ ERROR `#[foo]` is only valid on functions -static X: u64 = 4; -#[foo] //~ ERROR `#[foo]` is only valid on functions +#[foo] //~ ERROR `#[foo]` is only valid on functions and statics const Y: u64 = 4; -#[foo] //~ ERROR `#[foo]` is only valid on functions +#[foo] //~ ERROR `#[foo]` is only valid on functions and statics macro bar() {} #[foo()] diff --git a/tests/ui/eii/errors.stderr b/tests/ui/eii/errors.stderr index c7eb96a9c2190..553ae622cb36f 100644 --- a/tests/ui/eii/errors.stderr +++ b/tests/ui/eii/errors.stderr @@ -52,47 +52,41 @@ error: `#[eii_declaration(...)]` expects a list of one or two elements LL | #[eii_declaration = "unsafe"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/errors.rs:28:1 | LL | #[foo] | ^^^^^^ -error: `#[foo]` is only valid on functions +error: `#[foo]` is only valid on functions and statics --> $DIR/errors.rs:30:1 | LL | #[foo] | ^^^^^^ -error: `#[foo]` is only valid on functions - --> $DIR/errors.rs:32:1 - | -LL | #[foo] - | ^^^^^^ - error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:35:1 + --> $DIR/errors.rs:33:1 | LL | #[foo()] | ^^^^^^^^ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:37:1 + --> $DIR/errors.rs:35:1 | LL | #[foo(default, bar)] | ^^^^^^^^^^^^^^^^^^^^ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:39:1 + --> $DIR/errors.rs:37:1 | LL | #[foo("default")] | ^^^^^^^^^^^^^^^^^ error: `#[foo]` expected no arguments or a single argument: `#[foo(default)]` - --> $DIR/errors.rs:41:1 + --> $DIR/errors.rs:39:1 | LL | #[foo = "default"] | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error: aborting due to 14 previous errors diff --git a/tests/ui/eii/linking/codegen_cross_crate.rs b/tests/ui/eii/linking/codegen_cross_crate.rs index 2958a0f10521b..192aac5920704 100644 --- a/tests/ui/eii/linking/codegen_cross_crate.rs +++ b/tests/ui/eii/linking/codegen_cross_crate.rs @@ -3,7 +3,7 @@ //@ aux-build: codegen_cross_crate_other_crate.rs //@ compile-flags: -O //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether calling EIIs works with the declaration in another crate. diff --git a/tests/ui/eii/linking/codegen_single_crate.rs b/tests/ui/eii/linking/codegen_single_crate.rs index 8e85c354bba1c..d0e9c015da418 100644 --- a/tests/ui/eii/linking/codegen_single_crate.rs +++ b/tests/ui/eii/linking/codegen_single_crate.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether calling EIIs works with the declaration in the same crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/linking/same-symbol.rs b/tests/ui/eii/linking/same-symbol.rs index baf36ff4f5a0a..afba9b7750262 100644 --- a/tests/ui/eii/linking/same-symbol.rs +++ b/tests/ui/eii/linking/same-symbol.rs @@ -1,7 +1,7 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows #![feature(extern_item_impls)] diff --git a/tests/ui/eii/shadow_builtin.rs b/tests/ui/eii/shadow_builtin.rs new file mode 100644 index 0000000000000..5f619b79d01a4 --- /dev/null +++ b/tests/ui/eii/shadow_builtin.rs @@ -0,0 +1,17 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether calling EIIs works with the declaration in the same crate. +#![feature(extern_item_impls)] + +#[eii(inline)] +//~^ ERROR `#[inline]` required, but not found +fn test(x: u64); + +#[inline] +//~^ ERROR `inline` is ambiguous +fn test_impl(x: u64) { + println!("{x:?}") +} + +fn main() { } diff --git a/tests/ui/eii/shadow_builtin.stderr b/tests/ui/eii/shadow_builtin.stderr new file mode 100644 index 0000000000000..d48e66a18af20 --- /dev/null +++ b/tests/ui/eii/shadow_builtin.stderr @@ -0,0 +1,26 @@ +error[E0659]: `inline` is ambiguous + --> $DIR/shadow_builtin.rs:11:3 + | +LL | #[inline] + | ^^^^^^ ambiguous name + | + = note: ambiguous because of a name conflict with a builtin attribute + = note: `inline` could refer to a built-in attribute +note: `inline` could also refer to the attribute macro defined here + --> $DIR/shadow_builtin.rs:7:1 + | +LL | #[eii(inline)] + | ^^^^^^^^^^^^^^ + = help: use `crate::inline` to refer to this attribute macro unambiguously + +error: `#[inline]` required, but not found + --> $DIR/shadow_builtin.rs:7:7 + | +LL | #[eii(inline)] + | ^^^^^^ expected because `#[inline]` was declared here in crate `shadow_builtin` + | + = help: expected at least one implementation in crate `shadow_builtin` or any of its dependencies + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/eii/static/argument_required.rs b/tests/ui/eii/static/argument_required.rs new file mode 100644 index 0000000000000..114b8a35de5cf --- /dev/null +++ b/tests/ui/eii/static/argument_required.rs @@ -0,0 +1,11 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii] +//~^ ERROR `#[eii]` requires the name as an explicit argument when used on a static +static HELLO: u64; + +fn main() { } diff --git a/tests/ui/eii/static/argument_required.stderr b/tests/ui/eii/static/argument_required.stderr new file mode 100644 index 0000000000000..9e5ee398f77d8 --- /dev/null +++ b/tests/ui/eii/static/argument_required.stderr @@ -0,0 +1,8 @@ +error: `#[eii]` requires the name as an explicit argument when used on a static + --> $DIR/argument_required.rs:7:1 + | +LL | #[eii] + | ^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/auxiliary/cross_crate_decl.rs b/tests/ui/eii/static/auxiliary/cross_crate_decl.rs new file mode 100644 index 0000000000000..06b7daca22074 --- /dev/null +++ b/tests/ui/eii/static/auxiliary/cross_crate_decl.rs @@ -0,0 +1,6 @@ +//@ no-prefer-dynamic +#![crate_type = "rlib"] +#![feature(extern_item_impls)] + +#[eii(eii1)] +pub static DECL1: u64; diff --git a/tests/ui/eii/static/auxiliary/cross_crate_def.rs b/tests/ui/eii/static/auxiliary/cross_crate_def.rs new file mode 100644 index 0000000000000..70933440a62be --- /dev/null +++ b/tests/ui/eii/static/auxiliary/cross_crate_def.rs @@ -0,0 +1,9 @@ +//@ no-prefer-dynamic +#![crate_type = "rlib"] +#![feature(extern_item_impls)] + +#[eii(eii1)] +pub static DECL1: u64; + +#[eii1] +pub static EII1_IMPL: u64 = 5; diff --git a/tests/ui/eii/static/cross_crate_decl.rs b/tests/ui/eii/static/cross_crate_decl.rs new file mode 100644 index 0000000000000..63e3511198e1d --- /dev/null +++ b/tests/ui/eii/static/cross_crate_decl.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//@ aux-build: cross_crate_decl.rs +//@ compile-flags: -O +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether calling EIIs works with the declaration in another crate. + +extern crate cross_crate_decl as codegen; + +#[codegen::eii1] +static EII1_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{}", EII1_IMPL); + // through the alias + println!("{}", codegen::DECL1); +} diff --git a/tests/ui/eii/static/cross_crate_decl.run.stdout b/tests/ui/eii/static/cross_crate_decl.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/cross_crate_decl.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/cross_crate_def.rs b/tests/ui/eii/static/cross_crate_def.rs new file mode 100644 index 0000000000000..a0b6afbfd760b --- /dev/null +++ b/tests/ui/eii/static/cross_crate_def.rs @@ -0,0 +1,18 @@ +//@ run-pass +//@ check-run-results +//@ aux-build: cross_crate_def.rs +//@ compile-flags: -O +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether calling EIIs works with the declaration and definition in another crate. + +extern crate cross_crate_def as codegen; + +// what you would write: +fn main() { + // directly + println!("{}", codegen::EII1_IMPL); + // through the alias + println!("{}", codegen::DECL1); +} diff --git a/tests/ui/eii/static/cross_crate_def.run.stdout b/tests/ui/eii/static/cross_crate_def.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/cross_crate_def.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/duplicate.rs b/tests/ui/eii/static/duplicate.rs new file mode 100644 index 0000000000000..12b2e56c07e4e --- /dev/null +++ b/tests/ui/eii/static/duplicate.rs @@ -0,0 +1,25 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL1: u64 = 5; +//~^ ERROR multiple implementations of `#[hello]` + +#[hello] +static HELLO_IMPL2: u64 = 6; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL1}"); + println!("{HELLO_IMPL2}"); + + // through the alias + println!("{HELLO}"); +} diff --git a/tests/ui/eii/static/duplicate.stderr b/tests/ui/eii/static/duplicate.stderr new file mode 100644 index 0000000000000..270664c8c74c6 --- /dev/null +++ b/tests/ui/eii/static/duplicate.stderr @@ -0,0 +1,13 @@ +error: multiple implementations of `#[hello]` + --> $DIR/duplicate.rs:11:1 + | +LL | static HELLO_IMPL1: u64 = 5; + | ^^^^^^^^^^^^^^^^^^^^^^^ first implemented here in crate `duplicate` +... +LL | static HELLO_IMPL2: u64 = 6; + | ----------------------- also implemented here in crate `duplicate` + | + = help: an "externally implementable item" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_fn_static.rs b/tests/ui/eii/static/mismatch_fn_static.rs new file mode 100644 index 0000000000000..298fdca18d967 --- /dev/null +++ b/tests/ui/eii/static/mismatch_fn_static.rs @@ -0,0 +1,14 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +fn hello() -> u64; + +#[hello] +//~^ ERROR `#[hello]` must be used on a function +static HELLO_IMPL: u64 = 5; + +fn main() { } diff --git a/tests/ui/eii/static/mismatch_fn_static.stderr b/tests/ui/eii/static/mismatch_fn_static.stderr new file mode 100644 index 0000000000000..e8fa5f85b1f13 --- /dev/null +++ b/tests/ui/eii/static/mismatch_fn_static.stderr @@ -0,0 +1,8 @@ +error: `#[hello]` must be used on a function + --> $DIR/mismatch_fn_static.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_mut.rs b/tests/ui/eii/static/mismatch_mut.rs new file mode 100644 index 0000000000000..e99c948d0dccf --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static mut HELLO: u64; + +#[hello] +//~^ ERROR mutability does not match with the definition of`#[hello]` +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mismatch_mut.stderr b/tests/ui/eii/static/mismatch_mut.stderr new file mode 100644 index 0000000000000..a8438789c4049 --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut.stderr @@ -0,0 +1,8 @@ +error: mutability does not match with the definition of`#[hello]` + --> $DIR/mismatch_mut.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_safety.rs b/tests/ui/eii/static/mismatch_safety.rs new file mode 100644 index 0000000000000..9579cd68cb84b --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +unsafe static mut HELLO: u64; + +#[hello] +//~^ ERROR safety does not match with the definition of`#[hello]` +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mismatch_safety.stderr b/tests/ui/eii/static/mismatch_safety.stderr new file mode 100644 index 0000000000000..d4fa85778ae11 --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety.stderr @@ -0,0 +1,8 @@ +error: safety does not match with the definition of`#[hello]` + --> $DIR/mismatch_safety.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_static_fn.rs b/tests/ui/eii/static/mismatch_static_fn.rs new file mode 100644 index 0000000000000..cd9a8109dc339 --- /dev/null +++ b/tests/ui/eii/static/mismatch_static_fn.rs @@ -0,0 +1,16 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +//~^ ERROR `#[hello]` must be used on a static +fn hello_impl() -> u64 { + 5 +} + +fn main() { } diff --git a/tests/ui/eii/static/mismatch_static_fn.stderr b/tests/ui/eii/static/mismatch_static_fn.stderr new file mode 100644 index 0000000000000..639e3cfa3beb3 --- /dev/null +++ b/tests/ui/eii/static/mismatch_static_fn.stderr @@ -0,0 +1,8 @@ +error: `#[hello]` must be used on a static + --> $DIR/mismatch_static_fn.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs new file mode 100644 index 0000000000000..81adf22680f7f --- /dev/null +++ b/tests/ui/eii/static/multiple_impls.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether one function could implement two EIIs. +#![feature(extern_item_impls)] + +#[eii(a)] +static A: u64; + +#[eii(b)] +static B: u64; + +#[a] +#[b] +static IMPL: u64 = 5; + +fn main() { + println!("{A} {B} {IMPL}") +} diff --git a/tests/ui/eii/static/multiple_impls.run.stdout b/tests/ui/eii/static/multiple_impls.run.stdout new file mode 100644 index 0000000000000..58945c2b48291 --- /dev/null +++ b/tests/ui/eii/static/multiple_impls.run.stdout @@ -0,0 +1 @@ +5 5 5 diff --git a/tests/ui/eii/static/mut.rs b/tests/ui/eii/static/mut.rs new file mode 100644 index 0000000000000..49c36f72ab86f --- /dev/null +++ b/tests/ui/eii/static/mut.rs @@ -0,0 +1,22 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static mut HELLO: u64; + +#[hello] +static mut HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{}", unsafe { HELLO_IMPL }); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mut.run.stdout b/tests/ui/eii/static/mut.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/mut.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/same_address.rs b/tests/ui/eii/static/same_address.rs new file mode 100644 index 0000000000000..81de19406dc49 --- /dev/null +++ b/tests/ui/eii/static/same_address.rs @@ -0,0 +1,21 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs and their declarations share the same address +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + assert_eq!( + &HELLO as *const u64 as usize, + &HELLO_IMPL as *const u64 as usize, + ) +} diff --git a/tests/ui/eii/static/simple.rs b/tests/ui/eii/static/simple.rs new file mode 100644 index 0000000000000..661ab9b9835f2 --- /dev/null +++ b/tests/ui/eii/static/simple.rs @@ -0,0 +1,22 @@ +//@ run-pass +//@ check-run-results +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{HELLO}"); +} diff --git a/tests/ui/eii/static/simple.run.stdout b/tests/ui/eii/static/simple.run.stdout new file mode 100644 index 0000000000000..fd3c81a4d7631 --- /dev/null +++ b/tests/ui/eii/static/simple.run.stdout @@ -0,0 +1,2 @@ +5 +5 diff --git a/tests/ui/eii/static/subtype.rs b/tests/ui/eii/static/subtype.rs new file mode 100644 index 0000000000000..d98e94fa90322 --- /dev/null +++ b/tests/ui/eii/static/subtype.rs @@ -0,0 +1,18 @@ +//@ check-pass +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests that mismatching types of the declaration and definition are rejected +#![feature(extern_item_impls)] + +use std::ptr; + +#[eii(hello)] +static HELLO: for<'a> fn(&'a u8) -> &'a u8; + +#[hello] +static HELLO_IMPL: for<'a> fn(&'a u8) -> &'static u8 = |_| todo!(); + +fn main() { + +} diff --git a/tests/ui/eii/static/subtype_wrong.rs b/tests/ui/eii/static/subtype_wrong.rs new file mode 100644 index 0000000000000..964a3d767b197 --- /dev/null +++ b/tests/ui/eii/static/subtype_wrong.rs @@ -0,0 +1,17 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests that mismatching types of the declaration and definition are rejected +#![feature(extern_item_impls)] + +use std::ptr; + +#[eii(hello)] +static HELLO: for<'a> fn(&'a u8) -> &'static u8; + +#[hello] +static HELLO_IMPL: for<'a> fn(&'a u8) -> &'a u8 = |_| todo!(); +//~^ ERROR mismatched types + +fn main() { +} diff --git a/tests/ui/eii/static/subtype_wrong.stderr b/tests/ui/eii/static/subtype_wrong.stderr new file mode 100644 index 0000000000000..a20074947c15d --- /dev/null +++ b/tests/ui/eii/static/subtype_wrong.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/subtype_wrong.rs:13:1 + | +LL | static HELLO_IMPL: for<'a> fn(&'a u8) -> &'a u8 = |_| todo!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected fn pointer `for<'a> fn(&'a _) -> &'static _` + found fn pointer `for<'a> fn(&'a _) -> &'a _` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/eii/static/wrong_ty.rs b/tests/ui/eii/static/wrong_ty.rs new file mode 100644 index 0000000000000..beee0a5a0857b --- /dev/null +++ b/tests/ui/eii/static/wrong_ty.rs @@ -0,0 +1,18 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests that mismatching types of the declaration and definition are rejected +#![feature(extern_item_impls)] + +use std::ptr; + +#[eii(hello)] +static HELLO: u64; + +#[hello] +static HELLO_IMPL: bool = true; +//~^ ERROR static `HELLO_IMPL` has a type that is incompatible with the declaration of `#[hello]` [E0806] + +fn main() { + +} diff --git a/tests/ui/eii/static/wrong_ty.stderr b/tests/ui/eii/static/wrong_ty.stderr new file mode 100644 index 0000000000000..5095513527747 --- /dev/null +++ b/tests/ui/eii/static/wrong_ty.stderr @@ -0,0 +1,15 @@ +error[E0806]: static `HELLO_IMPL` has a type that is incompatible with the declaration of `#[hello]` + --> $DIR/wrong_ty.rs:13:1 + | +LL | static HELLO_IMPL: bool = true; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: expected this because of this attribute + --> $DIR/wrong_ty.rs:12:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0806`. From 59ed2459457eb7c95b2baa9dccdb74f63b9c0a80 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 10 Apr 2026 15:05:07 +0200 Subject: [PATCH 09/15] Reject mutable externally implementable statics --- compiler/rustc_builtin_macros/src/eii.rs | 14 ++++++++++++-- compiler/rustc_builtin_macros/src/errors.rs | 8 ++++++++ tests/ui/eii/static/mismatch_mut.rs | 1 + tests/ui/eii/static/mismatch_mut.stderr | 10 ++++++++-- tests/ui/eii/static/mismatch_mut2.rs | 21 +++++++++++++++++++++ tests/ui/eii/static/mismatch_mut2.stderr | 8 ++++++++ tests/ui/eii/static/mismatch_safety.rs | 2 +- tests/ui/eii/static/mismatch_safety2.rs | 21 +++++++++++++++++++++ tests/ui/eii/static/mismatch_safety2.stderr | 8 ++++++++ tests/ui/eii/static/mut.rs | 3 +-- tests/ui/eii/static/mut.stderr | 8 ++++++++ 11 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 tests/ui/eii/static/mismatch_mut2.rs create mode 100644 tests/ui/eii/static/mismatch_mut2.stderr create mode 100644 tests/ui/eii/static/mismatch_safety2.rs create mode 100644 tests/ui/eii/static/mismatch_safety2.stderr create mode 100644 tests/ui/eii/static/mut.stderr diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 9dab90b72a027..0cf9adfd4bcca 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -1,7 +1,8 @@ use rustc_ast::token::{Delimiter, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast::{ - Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Path, StmtKind, Visibility, ast, + Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Mutability, Path, StmtKind, + Visibility, ast, }; use rustc_ast_pretty::pprust::path_to_string; use rustc_expand::base::{Annotatable, ExtCtxt}; @@ -11,7 +12,7 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, - EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, EiiStaticMutable, }; /// ```rust @@ -100,6 +101,15 @@ fn eii_( }); return vec![]; } + + // Mut statics are currently not supported + if stat.mutability == Mutability::Mut { + ecx.dcx().emit_err(EiiStaticMutable { + span: eii_attr_span, + name: path_to_string(&meta_item.path), + }); + } + (item.span, stat.ident) } _ => { diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 4b572c2f34ac0..b210f93338d31 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1140,6 +1140,14 @@ pub(crate) struct EiiStaticArgumentRequired { pub name: String, } +#[derive(Diagnostic)] +#[diag("`#[{$name}]` cannot be used on mutable statics")] +pub(crate) struct EiiStaticMutable { + #[primary_span] + pub span: Span, + pub name: String, +} + #[derive(Diagnostic)] #[diag("`#[{$name}]` can only be used on functions inside a module")] pub(crate) struct EiiSharedMacroInStatementPosition { diff --git a/tests/ui/eii/static/mismatch_mut.rs b/tests/ui/eii/static/mismatch_mut.rs index e99c948d0dccf..87c2c4128aa5e 100644 --- a/tests/ui/eii/static/mismatch_mut.rs +++ b/tests/ui/eii/static/mismatch_mut.rs @@ -5,6 +5,7 @@ #![feature(extern_item_impls)] #[eii(hello)] +//~^ ERROR `#[eii]` cannot be used on mutable statics static mut HELLO: u64; #[hello] diff --git a/tests/ui/eii/static/mismatch_mut.stderr b/tests/ui/eii/static/mismatch_mut.stderr index a8438789c4049..537ac0de3c3a2 100644 --- a/tests/ui/eii/static/mismatch_mut.stderr +++ b/tests/ui/eii/static/mismatch_mut.stderr @@ -1,8 +1,14 @@ +error: `#[eii]` cannot be used on mutable statics + --> $DIR/mismatch_mut.rs:7:1 + | +LL | #[eii(hello)] + | ^^^^^^^^^^^^^ + error: mutability does not match with the definition of`#[hello]` - --> $DIR/mismatch_mut.rs:10:1 + --> $DIR/mismatch_mut.rs:11:1 | LL | #[hello] | ^^^^^^^^ -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/ui/eii/static/mismatch_mut2.rs b/tests/ui/eii/static/mismatch_mut2.rs new file mode 100644 index 0000000000000..ab525e418adeb --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut2.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +//~^ ERROR mutability does not match with the definition of`#[hello]` +static mut HELLO_IMPL: u64 = 5; + +// what you would write: +fn main() { + // directly + println!("{}", unsafe { HELLO_IMPL }); + + // through the alias + println!("{HELLO}"); +} diff --git a/tests/ui/eii/static/mismatch_mut2.stderr b/tests/ui/eii/static/mismatch_mut2.stderr new file mode 100644 index 0000000000000..6ac3df57697db --- /dev/null +++ b/tests/ui/eii/static/mismatch_mut2.stderr @@ -0,0 +1,8 @@ +error: mutability does not match with the definition of`#[hello]` + --> $DIR/mismatch_mut2.rs:10:1 + | +LL | #[hello] + | ^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mismatch_safety.rs b/tests/ui/eii/static/mismatch_safety.rs index 9579cd68cb84b..f30326b0755c0 100644 --- a/tests/ui/eii/static/mismatch_safety.rs +++ b/tests/ui/eii/static/mismatch_safety.rs @@ -5,7 +5,7 @@ #![feature(extern_item_impls)] #[eii(hello)] -unsafe static mut HELLO: u64; +unsafe static HELLO: u64; #[hello] //~^ ERROR safety does not match with the definition of`#[hello]` diff --git a/tests/ui/eii/static/mismatch_safety2.rs b/tests/ui/eii/static/mismatch_safety2.rs new file mode 100644 index 0000000000000..dea45c26292d8 --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety2.rs @@ -0,0 +1,21 @@ +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Tests whether EIIs work on statics +#![feature(extern_item_impls)] + +#[eii(hello)] +static HELLO: u64; + +#[hello] +unsafe static HELLO_IMPL: u64 = 5; +//~^ ERROR static items cannot be declared with `unsafe` safety qualifier outside of `extern` block + +// what you would write: +fn main() { + // directly + println!("{HELLO_IMPL}"); + + // through the alias + println!("{}", unsafe { HELLO }); +} diff --git a/tests/ui/eii/static/mismatch_safety2.stderr b/tests/ui/eii/static/mismatch_safety2.stderr new file mode 100644 index 0000000000000..6957a6202b614 --- /dev/null +++ b/tests/ui/eii/static/mismatch_safety2.stderr @@ -0,0 +1,8 @@ +error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block + --> $DIR/mismatch_safety2.rs:11:1 + | +LL | unsafe static HELLO_IMPL: u64 = 5; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/eii/static/mut.rs b/tests/ui/eii/static/mut.rs index 49c36f72ab86f..803ffc2297992 100644 --- a/tests/ui/eii/static/mut.rs +++ b/tests/ui/eii/static/mut.rs @@ -1,5 +1,3 @@ -//@ run-pass -//@ check-run-results //@ ignore-backends: gcc // FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows @@ -7,6 +5,7 @@ #![feature(extern_item_impls)] #[eii(hello)] +//~^ ERROR `#[eii]` cannot be used on mutable statics static mut HELLO: u64; #[hello] diff --git a/tests/ui/eii/static/mut.stderr b/tests/ui/eii/static/mut.stderr new file mode 100644 index 0000000000000..cd3a0ca23c7f4 --- /dev/null +++ b/tests/ui/eii/static/mut.stderr @@ -0,0 +1,8 @@ +error: `#[eii]` cannot be used on mutable statics + --> $DIR/mut.rs:7:1 + | +LL | #[eii(hello)] + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 1f2b0900a702d4947d8f11ea7b162c3af1626e3c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 10 Apr 2026 15:26:11 +0200 Subject: [PATCH 10/15] Reject multiple EII implementations on one static --- compiler/rustc_builtin_macros/src/eii.rs | 12 ++++++++++-- compiler/rustc_builtin_macros/src/errors.rs | 10 ++++++++++ tests/ui/eii/static/multiple_decls.rs | 11 +++++++++++ tests/ui/eii/static/multiple_decls.stderr | 14 ++++++++++++++ tests/ui/eii/static/multiple_impls.rs | 3 +-- tests/ui/eii/static/multiple_impls.stderr | 10 ++++++++++ 6 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 tests/ui/eii/static/multiple_decls.rs create mode 100644 tests/ui/eii/static/multiple_decls.stderr create mode 100644 tests/ui/eii/static/multiple_impls.stderr diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 0cf9adfd4bcca..fd0ef8500c6c3 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -12,7 +12,8 @@ use thin_vec::{ThinVec, thin_vec}; use crate::errors::{ EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition, - EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, EiiStaticMutable, + EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault, + EiiStaticMultipleImplementations, EiiStaticMutable, }; /// ```rust @@ -512,7 +513,14 @@ pub(crate) fn eii_shared_macro( let eii_impls = match &mut i.kind { ItemKind::Fn(func) => &mut func.eii_impls, - ItemKind::Static(stat) => &mut stat.eii_impls, + ItemKind::Static(stat) => { + if !stat.eii_impls.is_empty() { + // Reject multiple implementations on one static item + // because it might be unintuitive for libraries defining statics the defined statics may alias + ecx.dcx().emit_err(EiiStaticMultipleImplementations { span }); + } + &mut stat.eii_impls + } _ => { ecx.dcx() .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) }); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b210f93338d31..ad641beb87d98 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1124,6 +1124,16 @@ pub(crate) struct EiiSharedMacroTarget { pub name: String, } +#[derive(Diagnostic)] +#[diag("static cannot implement multiple EIIs")] +#[note( + "this is not allowed because multiple externally implementable statics that alias may be unintuitive" +)] +pub(crate) struct EiiStaticMultipleImplementations { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`#[{$name}]` cannot be used on statics with a value")] pub(crate) struct EiiStaticDefault { diff --git a/tests/ui/eii/static/multiple_decls.rs b/tests/ui/eii/static/multiple_decls.rs new file mode 100644 index 0000000000000..1913dc39e8b7c --- /dev/null +++ b/tests/ui/eii/static/multiple_decls.rs @@ -0,0 +1,11 @@ +#![feature(extern_item_impls)] + +const A: () = (); +#[eii(A)] +static A: u64; +//~^ ERROR the name `A` is defined multiple times + +#[A] +static A_IMPL: u64 = 5; + +fn main() {} diff --git a/tests/ui/eii/static/multiple_decls.stderr b/tests/ui/eii/static/multiple_decls.stderr new file mode 100644 index 0000000000000..b0b5da1aabe1b --- /dev/null +++ b/tests/ui/eii/static/multiple_decls.stderr @@ -0,0 +1,14 @@ +error[E0428]: the name `A` is defined multiple times + --> $DIR/multiple_decls.rs:5:1 + | +LL | const A: () = (); + | ----------------- previous definition of the value `A` here +LL | #[eii(A)] +LL | static A: u64; + | ^^^^^^^^^^^^^^ `A` redefined here + | + = note: `A` must be defined only once in the value namespace of this module + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0428`. diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs index 81adf22680f7f..8ad7d87040a36 100644 --- a/tests/ui/eii/static/multiple_impls.rs +++ b/tests/ui/eii/static/multiple_impls.rs @@ -1,5 +1,3 @@ -//@ run-pass -//@ check-run-results //@ ignore-backends: gcc // FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows @@ -14,6 +12,7 @@ static B: u64; #[a] #[b] +//~^ ERROR static cannot implement multiple EIIs static IMPL: u64 = 5; fn main() { diff --git a/tests/ui/eii/static/multiple_impls.stderr b/tests/ui/eii/static/multiple_impls.stderr new file mode 100644 index 0000000000000..b31331f2483f1 --- /dev/null +++ b/tests/ui/eii/static/multiple_impls.stderr @@ -0,0 +1,10 @@ +error: static cannot implement multiple EIIs + --> $DIR/multiple_impls.rs:14:1 + | +LL | #[b] + | ^^^^ + | + = note: this is not allowed because multiple externally implementable statics that alias may be unintuitive + +error: aborting due to 1 previous error + From 21925e9e2353a24e9a2fc9dd42a1e4c681eacdf9 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:24:04 +0200 Subject: [PATCH 11/15] Lazily check diagnostic namespace features --- .../src/attributes/diagnostic/on_const.rs | 1 + .../src/attributes/diagnostic/on_unknown.rs | 1 + compiler/rustc_resolve/src/macros.rs | 25 +++++++++++-------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs index def4069f6b477..23db854252a37 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_const.rs @@ -17,6 +17,7 @@ impl AttributeParser for OnConstParser { template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), |this, cx, args| { if !cx.features().diagnostic_on_const() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs return; } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs index bd5eb4cbf82c7..dcfba68a4cf8b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unknown.rs @@ -18,6 +18,7 @@ impl OnUnknownParser { mode: Mode, ) { if !cx.features().diagnostic_on_unknown() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs return; } let span = cx.attr_span; diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 67a896bdd7557..15244b7afb69f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -712,25 +712,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit(); } - let diagnostic_attributes: &[(Symbol, bool)] = &[ - (sym::on_unimplemented, true), - (sym::do_not_recommend, true), - (sym::on_move, true), - (sym::on_const, self.tcx.features().diagnostic_on_const()), - (sym::on_unknown, self.tcx.features().diagnostic_on_unknown()), + const DIAGNOSTIC_ATTRIBUTES: &[(Symbol, Option)] = &[ + (sym::on_unimplemented, None), + (sym::do_not_recommend, None), + (sym::on_move, None), + (sym::on_const, Some(sym::diagnostic_on_const)), + (sym::on_unknown, Some(sym::diagnostic_on_unknown)), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && !diagnostic_attributes - .iter() - .any(|(attr, stable)| *stable && attribute.ident.name == *attr) + && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, stable)| { + attribute.ident.name == *attr + && stable.is_none_or(|f| self.tcx.features().enabled(f)) + }) { let span = attribute.span(); - let candidates = diagnostic_attributes + let candidates = DIAGNOSTIC_ATTRIBUTES .iter() - .filter_map(|(sym, stable)| stable.then_some(*sym)) + .filter_map(|(sym, stable)| { + stable.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*sym) + }) .collect::>(); let typo = find_best_match_for_name(&candidates, attribute.ident.name, Some(5)) .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); From d69403abfc978657586e9ff85b71b988248b915e Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:42:04 +0200 Subject: [PATCH 12/15] Properly emit diagnostic for diagnostic::on_move being used without feature gate --- .../src/attributes/diagnostic/on_move.rs | 1 + compiler/rustc_resolve/src/macros.rs | 2 +- .../feature-gate-diagnostic-on-move.rs | 5 ++--- .../feature-gate-diagnostic-on-move.stderr | 16 ++++++++++++---- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs index 006b3b66658e0..a79b7d6afbcdc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -24,6 +24,7 @@ impl OnMoveParser { mode: Mode, ) { if !cx.features().diagnostic_on_move() { + // `UnknownDiagnosticAttribute` is emitted in rustc_resolve/macros.rs return; } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 15244b7afb69f..7769b6d815454 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -715,7 +715,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { const DIAGNOSTIC_ATTRIBUTES: &[(Symbol, Option)] = &[ (sym::on_unimplemented, None), (sym::do_not_recommend, None), - (sym::on_move, None), + (sym::on_move, Some(sym::diagnostic_on_move)), (sym::on_const, Some(sym::diagnostic_on_const)), (sym::on_unknown, Some(sym::diagnostic_on_unknown)), ]; diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs index a1f3b1fbbc860..a55a6260d6512 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs @@ -2,9 +2,8 @@ //! gate, but the fact that not adding the feature gate will cause the //! diagnostic to not emit the custom diagnostic message //! -#[diagnostic::on_move( - message = "Foo" -)] +#[diagnostic::on_move(message = "Foo")] +//~^ WARN unknown diagnostic attribute #[derive(Debug)] struct Foo; diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr index 9ba6f272cf92b..83d6448ed5704 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -1,5 +1,13 @@ +warning: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-on-move.rs:5:15 + | +LL | #[diagnostic::on_move(message = "Foo")] + | ^^^^^^^ + | + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + error[E0382]: use of moved value: `foo` - --> $DIR/feature-gate-diagnostic-on-move.rs:16:15 + --> $DIR/feature-gate-diagnostic-on-move.rs:15:15 | LL | let foo = Foo; | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait @@ -9,14 +17,14 @@ LL | let bar = foo; | ^^^ value used here after move | note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary - --> $DIR/feature-gate-diagnostic-on-move.rs:11:17 + --> $DIR/feature-gate-diagnostic-on-move.rs:10:17 | LL | fn takes_foo(_: Foo) {} | --------- ^^^ this parameter takes ownership of the value | | | in this function note: if `Foo` implemented `Clone`, you could clone the value - --> $DIR/feature-gate-diagnostic-on-move.rs:9:1 + --> $DIR/feature-gate-diagnostic-on-move.rs:8:1 | LL | struct Foo; | ^^^^^^^^^^ consider implementing `Clone` for this type @@ -24,6 +32,6 @@ LL | struct Foo; LL | takes_foo(foo); | --- you could clone this value -error: aborting due to 1 previous error +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0382`. From 55cd47658c8ae2e09011ccbfee773bd03b704263 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 01:47:07 +0200 Subject: [PATCH 13/15] Change edit distance to not suggest `on_move` and `on_const` against each other --- compiler/rustc_resolve/src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 7769b6d815454..f106d88a83206 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -735,7 +735,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { stable.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*sym) }) .collect::>(); - let typo = find_best_match_for_name(&candidates, attribute.ident.name, Some(5)) + let typo = find_best_match_for_name(&candidates, attribute.ident.name, None) .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); self.tcx.sess.psess.buffer_lint( From 106b16cd2d9e64b950924be6469fdc0854eb3515 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 12 Apr 2026 11:06:40 +0200 Subject: [PATCH 14/15] Add support for static EIIs in late resolution --- compiler/rustc_resolve/src/late.rs | 15 ++++++++++----- .../ui/eii/eii-declaration-not-fn-issue-152337.rs | 4 ++-- .../eii-declaration-not-fn-issue-152337.stderr | 8 ++++---- tests/ui/eii/static/multiple_decls.rs | 1 + tests/ui/eii/static/multiple_decls.stderr | 11 +++++++++-- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 540e7ec52f51c..56e26b1ac6cbe 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -530,9 +530,8 @@ impl PathSource<'_, '_, '_> { }, _ => "value", }, - PathSource::ReturnTypeNotation - | PathSource::Delegation - | PathSource::ExternItemImpl => "function", + PathSource::ReturnTypeNotation | PathSource::Delegation => "function", + PathSource::ExternItemImpl => "function or static", PathSource::PreciseCapturingArg(..) => "type or const parameter", PathSource::Macro => "macro", PathSource::Module => "module", @@ -625,7 +624,13 @@ impl PathSource<'_, '_, '_> { }, PathSource::Delegation => matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn, _)), PathSource::ExternItemImpl => { - matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..), _)) + matches!( + res, + Res::Def( + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::Static { .. }, + _ + ) + ) } PathSource::PreciseCapturingArg(ValueNS) => { matches!(res, Res::Def(DefKind::ConstParam, _)) @@ -5500,7 +5505,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { *node_id, &None, &target.foreign_item, - PathSource::ExternItemImpl + PathSource::ExternItemImpl, ); } else { self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); diff --git a/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs b/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs index 8564a5a748477..0d71f4854d344 100644 --- a/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs +++ b/tests/ui/eii/eii-declaration-not-fn-issue-152337.rs @@ -6,7 +6,7 @@ const A: () = (); #[eii] fn A() {} //~ ERROR the name `A` is defined multiple times -//~^ ERROR expected function, found constant -//~| ERROR expected function, found constant +//~^ ERROR expected function or static, found constant +//~| ERROR expected function or static, found constant fn main() {} diff --git a/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr b/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr index ea4ec604e7aa4..34998f33cc92e 100644 --- a/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr +++ b/tests/ui/eii/eii-declaration-not-fn-issue-152337.stderr @@ -9,17 +9,17 @@ LL | fn A() {} | = note: `A` must be defined only once in the value namespace of this module -error[E0423]: expected function, found constant `self::A` +error[E0423]: expected function or static, found constant `self::A` --> $DIR/eii-declaration-not-fn-issue-152337.rs:8:4 | LL | fn A() {} - | ^ not a function + | ^ not a function or static -error[E0423]: expected function, found constant `A` +error[E0423]: expected function or static, found constant `A` --> $DIR/eii-declaration-not-fn-issue-152337.rs:8:4 | LL | fn A() {} - | ^ not a function + | ^ not a function or static error: aborting due to 3 previous errors diff --git a/tests/ui/eii/static/multiple_decls.rs b/tests/ui/eii/static/multiple_decls.rs index 1913dc39e8b7c..791e2725087f1 100644 --- a/tests/ui/eii/static/multiple_decls.rs +++ b/tests/ui/eii/static/multiple_decls.rs @@ -4,6 +4,7 @@ const A: () = (); #[eii(A)] static A: u64; //~^ ERROR the name `A` is defined multiple times +//~| ERROR expected function or static, found constant `A` #[A] static A_IMPL: u64 = 5; diff --git a/tests/ui/eii/static/multiple_decls.stderr b/tests/ui/eii/static/multiple_decls.stderr index b0b5da1aabe1b..fcc5d93c5f993 100644 --- a/tests/ui/eii/static/multiple_decls.stderr +++ b/tests/ui/eii/static/multiple_decls.stderr @@ -9,6 +9,13 @@ LL | static A: u64; | = note: `A` must be defined only once in the value namespace of this module -error: aborting due to 1 previous error +error[E0423]: expected function or static, found constant `A` + --> $DIR/multiple_decls.rs:5:8 + | +LL | static A: u64; + | ^ not a function or static + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0428`. +Some errors have detailed explanations: E0423, E0428. +For more information about an error, try `rustc --explain E0423`. From 08a12dc1432edd57898f3cb6b154a72977937fde Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 12 Apr 2026 18:38:17 +0200 Subject: [PATCH 15/15] Add feature hint for unstable diagnostic attributes --- compiler/rustc_resolve/src/errors.rs | 26 +++++++------ compiler/rustc_resolve/src/macros.rs | 38 ++++++++++++++----- .../feature-gate-diagnostic-on-move.stderr | 1 + .../feature-gate-diagnostic-on-unknown.stderr | 1 + 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index bcc754fd984da..8c7bf61949a29 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1521,20 +1521,24 @@ pub(crate) struct RedundantImportVisibility { #[diag("unknown diagnostic attribute")] pub(crate) struct UnknownDiagnosticAttribute { #[subdiagnostic] - pub typo: Option, + pub help: Option, } #[derive(Subdiagnostic)] -#[suggestion( - "an attribute with a similar name exists", - style = "verbose", - code = "{typo_name}", - applicability = "machine-applicable" -)] -pub(crate) struct UnknownDiagnosticAttributeTypoSugg { - #[primary_span] - pub span: Span, - pub typo_name: Symbol, +pub(crate) enum UnknownDiagnosticAttributeHelp { + #[suggestion( + "an attribute with a similar name exists", + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" + )] + Typo { + #[primary_span] + span: Span, + typo_name: Symbol, + }, + #[help("add `#![feature({$feature})]` to the crate attributes to enable")] + UseFeature { feature: Symbol }, } // FIXME: Make this properly translatable. diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f106d88a83206..13bda9c98c0a7 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -723,26 +723,44 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments && namespace.ident.name == sym::diagnostic - && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, stable)| { + && !DIAGNOSTIC_ATTRIBUTES.iter().any(|(attr, feature)| { attribute.ident.name == *attr - && stable.is_none_or(|f| self.tcx.features().enabled(f)) + && feature.is_none_or(|f| self.tcx.features().enabled(f)) }) { + let name = attribute.ident.name; let span = attribute.span(); - let candidates = DIAGNOSTIC_ATTRIBUTES - .iter() - .filter_map(|(sym, stable)| { - stable.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*sym) + + let help = 'help: { + if self.tcx.sess.is_nightly_build() { + for (attr, feature) in DIAGNOSTIC_ATTRIBUTES { + if let Some(feature) = *feature + && *attr == name + { + break 'help Some(errors::UnknownDiagnosticAttributeHelp::UseFeature { + feature, + }); + } + } + } + + let candidates = DIAGNOSTIC_ATTRIBUTES + .iter() + .filter_map(|(attr, feature)| { + feature.is_none_or(|f| self.tcx.features().enabled(f)).then_some(*attr) + }) + .collect::>(); + + find_best_match_for_name(&candidates, name, None).map(|typo_name| { + errors::UnknownDiagnosticAttributeHelp::Typo { span, typo_name } }) - .collect::>(); - let typo = find_best_match_for_name(&candidates, attribute.ident.name, None) - .map(|typo_name| errors::UnknownDiagnosticAttributeTypoSugg { span, typo_name }); + }; self.tcx.sess.psess.buffer_lint( UNKNOWN_DIAGNOSTIC_ATTRIBUTES, span, node_id, - errors::UnknownDiagnosticAttribute { typo }, + errors::UnknownDiagnosticAttribute { help }, ); } diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr index 83d6448ed5704..593120edd1700 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -4,6 +4,7 @@ warning: unknown diagnostic attribute LL | #[diagnostic::on_move(message = "Foo")] | ^^^^^^^ | + = help: add `#![feature(diagnostic_on_move)]` to the crate attributes to enable = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default error[E0382]: use of moved value: `foo` diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr index f6d7ffadaceae..d9c8071339b75 100644 --- a/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unknown.stderr @@ -10,6 +10,7 @@ error: unknown diagnostic attribute LL | #[diagnostic::on_unknown(message = "Tada")] | ^^^^^^^^^^ | + = help: add `#![feature(diagnostic_on_unknown)]` to the crate attributes to enable note: the lint level is defined here --> $DIR/feature-gate-diagnostic-on-unknown.rs:1:9 |