Rollup merge of #154049 - petrochenkov:deleglobspan, r=jackh726

delegation: Track more precise spans for glob delegations

The last commit also fixes a macro hygiene issue with `self` in delegations found in https://github.com/rust-lang/rust/pull/154002.
This commit is contained in:
Jonathan Brouwer
2026-04-14 16:29:31 +02:00
committed by GitHub
15 changed files with 184 additions and 82 deletions
+7 -2
View File
@@ -3901,12 +3901,17 @@ pub struct Delegation {
pub from_glob: bool,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum DelegationSuffixes {
List(ThinVec<(Ident, Option<Ident>)>),
Glob(Span),
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct DelegationMac {
pub qself: Option<Box<QSelf>>,
pub prefix: Path,
// Some for list delegation, and None for glob delegation.
pub suffixes: Option<ThinVec<(Ident, Option<Ident>)>>,
pub suffixes: DelegationSuffixes,
pub body: Option<Box<Block>>,
}
+1
View File
@@ -430,6 +430,7 @@ pub fn ctxt(&self) -> Option<FnCtxt> {
Defaultness,
Delegation,
DelegationMac,
DelegationSuffixes,
DelimArgs,
DelimSpan,
EnumDef,
@@ -444,7 +444,10 @@ pub(crate) fn print_item(&mut self, item: &ast::Item) {
&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,
),
}
@@ -651,7 +654,10 @@ pub(crate) fn print_assoc_item(&mut self, item: &ast::AssocItem) {
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,
),
}
+9 -2
View File
@@ -1019,18 +1019,24 @@ pub fn non_macro_attr(edition: Edition) -> 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<Vec<(Ident, Option<Ident>)>, ()> {
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 @@ fn expand(
}
}
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 @@ fn glob_delegation_suffixes(
&self,
trait_def_id: DefId,
impl_def_id: LocalDefId,
star_span: Span,
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>;
/// Record the name of an opaque `Ty::ImplTrait` pre-expansion so that it can be used
+4 -4
View File
@@ -8,9 +8,9 @@
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;
@@ -2401,7 +2401,7 @@ fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
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() {
+9 -4
View File
@@ -857,7 +857,7 @@ fn parse_impl_delegation(
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 @@ fn parse_path_like_delegation(&mut self) -> PResult<'a, ItemKind> {
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 {
@@ -1519,7 +1521,10 @@ fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &'static str)
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 });
+1 -2
View File
@@ -3872,8 +3872,7 @@ fn resolve_delegation(
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);
+15 -8
View File
@@ -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 @@ fn resolve_macro_invocation(
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 @@ fn glob_delegation_suffixes(
&self,
trait_def_id: DefId,
impl_def_id: LocalDefId,
star_span: Span,
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> {
let target_trait = self.expect_module(trait_def_id);
if !target_trait.unexpanded_invocations.borrow().is_empty() {
@@ -549,13 +551,13 @@ fn glob_delegation_suffixes(
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 @@ fn smart_resolve_macro_path(
parent_scope: &ParentScope<'ra>,
node_id: NodeId,
force: bool,
deleg_impl: Option<LocalDefId>,
deleg_impl: Option<(LocalDefId, Span)>,
invoc_in_mod_inert_attr: Option<LocalDefId>,
suggestion_span: Option<Span>,
) -> Result<(Arc<SyntaxExtension>, Res), Indeterminate> {
@@ -792,7 +794,7 @@ fn resolve_macro_or_delegation_path<'r>(
kind: MacroKind,
parent_scope: &ParentScope<'ra>,
force: bool,
deleg_impl: Option<LocalDefId>,
deleg_impl: Option<(LocalDefId, Span)>,
invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>,
ignore_import: Option<Import<'ra>>,
suggestion_span: Option<Span>,
@@ -877,10 +879,15 @@ fn resolve_macro_or_delegation_path<'r>(
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,
},
+5 -4
View File
@@ -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 @@ fn method(&self) -> u8 { 2 }
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() {}
+25 -25
View File
@@ -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
@@ -4,7 +4,6 @@
trait Trait {
fn foo(&self);
//~^ ERROR negative impls cannot have any items [E0749]
}
struct S;
@@ -15,5 +14,6 @@ fn foo(&self) {}
struct F(S);
reuse impl !Trait for F { &self.0 }
//~^ ERROR negative impls cannot have any items
fn main() {}
@@ -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
-25
View File
@@ -173,19 +173,6 @@ macro_rules! self_0_ref { ($self:ident) => { &$self.0 } }
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 @@ fn f() {
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();
}
}
@@ -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() {}
@@ -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`.