mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Rollup merge of #124135 - petrochenkov:deleglob, r=fmease
delegation: Implement glob delegation Support delegating to all trait methods in one go. Overriding globs with explicit definitions is also supported. The implementation is generally based on the design from https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2020869823, but unlike with list delegation in https://github.com/rust-lang/rust/pull/123413 we cannot expand glob delegation eagerly. We have to enqueue it into the queue of unexpanded macros (most other macros are processed this way too), and then a glob delegation waits in that queue until its trait path is resolved, and enough code expands to generate the identifier list produced from the glob. Glob delegation is only allowed in impls, and can only point to traits. Supporting it in other places gives very little practical benefit, but significantly raises the implementation complexity. Part of https://github.com/rust-lang/rust/issues/118212.
This commit is contained in:
commit
f8ce1cfbf5
@ -3161,13 +3161,16 @@ pub struct Delegation {
|
||||
pub path: Path,
|
||||
pub rename: Option<Ident>,
|
||||
pub body: Option<P<Block>>,
|
||||
/// The item was expanded from a glob delegation item.
|
||||
pub from_glob: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct DelegationMac {
|
||||
pub qself: Option<P<QSelf>>,
|
||||
pub prefix: Path,
|
||||
pub suffixes: ThinVec<(Ident, Option<Ident>)>,
|
||||
// Some for list delegation, and None for glob delegation.
|
||||
pub suffixes: Option<ThinVec<(Ident, Option<Ident>)>>,
|
||||
pub body: Option<P<Block>>,
|
||||
}
|
||||
|
||||
@ -3294,7 +3297,7 @@ pub enum ItemKind {
|
||||
///
|
||||
/// E.g. `reuse <Type as Trait>::name { target_expr_template }`.
|
||||
Delegation(Box<Delegation>),
|
||||
/// A list delegation item (`reuse prefix::{a, b, c}`).
|
||||
/// A list or glob delegation item (`reuse prefix::{a, b, c}`, `reuse prefix::*`).
|
||||
/// Treated similarly to a macro call and expanded early.
|
||||
DelegationMac(Box<DelegationMac>),
|
||||
}
|
||||
@ -3375,7 +3378,7 @@ pub enum AssocItemKind {
|
||||
MacCall(P<MacCall>),
|
||||
/// An associated delegation item.
|
||||
Delegation(Box<Delegation>),
|
||||
/// An associated delegation item list.
|
||||
/// An associated list or glob delegation item.
|
||||
DelegationMac(Box<DelegationMac>),
|
||||
}
|
||||
|
||||
|
@ -1162,7 +1162,14 @@ impl NoopVisitItemKind for ItemKind {
|
||||
}
|
||||
ItemKind::MacCall(m) => vis.visit_mac_call(m),
|
||||
ItemKind::MacroDef(def) => vis.visit_macro_def(def),
|
||||
ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
ItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
vis.visit_id(id);
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(path);
|
||||
@ -1176,12 +1183,14 @@ impl NoopVisitItemKind for ItemKind {
|
||||
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(prefix);
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
vis.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
vis.visit_ident(rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
vis.visit_block(body);
|
||||
}
|
||||
@ -1218,7 +1227,14 @@ impl NoopVisitItemKind for AssocItemKind {
|
||||
visit_opt(ty, |ty| visitor.visit_ty(ty));
|
||||
}
|
||||
AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
|
||||
AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
AssocItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
visitor.visit_id(id);
|
||||
visitor.visit_qself(qself);
|
||||
visitor.visit_path(path);
|
||||
@ -1232,12 +1248,14 @@ impl NoopVisitItemKind for AssocItemKind {
|
||||
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
visitor.visit_qself(qself);
|
||||
visitor.visit_path(prefix);
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
visitor.visit_block(body);
|
||||
}
|
||||
|
@ -408,7 +408,14 @@ impl WalkItemKind for ItemKind {
|
||||
}
|
||||
ItemKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
|
||||
ItemKind::MacroDef(ts) => try_visit!(visitor.visit_mac_def(ts, item.id)),
|
||||
ItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
ItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
if let Some(qself) = qself {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
@ -421,12 +428,14 @@ impl WalkItemKind for ItemKind {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
try_visit!(visitor.visit_path(prefix, item.id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
}
|
||||
@ -837,7 +846,14 @@ impl WalkItemKind for AssocItemKind {
|
||||
AssocItemKind::MacCall(mac) => {
|
||||
try_visit!(visitor.visit_mac_call(mac));
|
||||
}
|
||||
AssocItemKind::Delegation(box Delegation { id, qself, path, rename, body }) => {
|
||||
AssocItemKind::Delegation(box Delegation {
|
||||
id,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body,
|
||||
from_glob: _,
|
||||
}) => {
|
||||
if let Some(qself) = qself {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
@ -850,12 +866,14 @@ impl WalkItemKind for AssocItemKind {
|
||||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
try_visit!(visitor.visit_path(prefix, item.id));
|
||||
if let Some(suffixes) = suffixes {
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
}
|
||||
}
|
||||
}
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,12 @@ use rustc_ast::ptr::P;
|
||||
use rustc_ast::ModKind;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
enum DelegationKind<'a> {
|
||||
Single,
|
||||
List(&'a [(Ident, Option<Ident>)]),
|
||||
Glob,
|
||||
}
|
||||
|
||||
fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
|
||||
format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s)
|
||||
}
|
||||
@ -387,7 +393,7 @@ impl<'a> State<'a> {
|
||||
&item.vis,
|
||||
&deleg.qself,
|
||||
&deleg.path,
|
||||
None,
|
||||
DelegationKind::Single,
|
||||
&deleg.body,
|
||||
),
|
||||
ast::ItemKind::DelegationMac(deleg) => self.print_delegation(
|
||||
@ -395,7 +401,7 @@ impl<'a> State<'a> {
|
||||
&item.vis,
|
||||
&deleg.qself,
|
||||
&deleg.prefix,
|
||||
Some(&deleg.suffixes),
|
||||
deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)),
|
||||
&deleg.body,
|
||||
),
|
||||
}
|
||||
@ -579,7 +585,7 @@ impl<'a> State<'a> {
|
||||
vis,
|
||||
&deleg.qself,
|
||||
&deleg.path,
|
||||
None,
|
||||
DelegationKind::Single,
|
||||
&deleg.body,
|
||||
),
|
||||
ast::AssocItemKind::DelegationMac(deleg) => self.print_delegation(
|
||||
@ -587,20 +593,20 @@ impl<'a> State<'a> {
|
||||
vis,
|
||||
&deleg.qself,
|
||||
&deleg.prefix,
|
||||
Some(&deleg.suffixes),
|
||||
deleg.suffixes.as_ref().map_or(DelegationKind::Glob, |s| DelegationKind::List(s)),
|
||||
&deleg.body,
|
||||
),
|
||||
}
|
||||
self.ann.post(self, AnnNode::SubItem(id))
|
||||
}
|
||||
|
||||
pub(crate) fn print_delegation(
|
||||
fn print_delegation(
|
||||
&mut self,
|
||||
attrs: &[ast::Attribute],
|
||||
vis: &ast::Visibility,
|
||||
qself: &Option<P<ast::QSelf>>,
|
||||
path: &ast::Path,
|
||||
suffixes: Option<&[(Ident, Option<Ident>)]>,
|
||||
kind: DelegationKind<'_>,
|
||||
body: &Option<P<ast::Block>>,
|
||||
) {
|
||||
if body.is_some() {
|
||||
@ -614,7 +620,9 @@ impl<'a> State<'a> {
|
||||
} else {
|
||||
self.print_path(path, false, 0);
|
||||
}
|
||||
if let Some(suffixes) = suffixes {
|
||||
match kind {
|
||||
DelegationKind::Single => {}
|
||||
DelegationKind::List(suffixes) => {
|
||||
self.word("::");
|
||||
self.word("{");
|
||||
for (i, (ident, rename)) in suffixes.iter().enumerate() {
|
||||
@ -630,6 +638,11 @@ impl<'a> State<'a> {
|
||||
}
|
||||
self.word("}");
|
||||
}
|
||||
DelegationKind::Glob => {
|
||||
self.word("::");
|
||||
self.word("*");
|
||||
}
|
||||
}
|
||||
if let Some(body) = body {
|
||||
self.nbsp();
|
||||
self.print_block_with_attrs(body, attrs);
|
||||
|
@ -35,8 +35,8 @@ expand_duplicate_matcher_binding = duplicate matcher binding
|
||||
.label = duplicate binding
|
||||
.label2 = previous binding
|
||||
|
||||
expand_empty_delegation_list =
|
||||
empty list delegation is not supported
|
||||
expand_empty_delegation_mac =
|
||||
empty {$kind} delegation is not supported
|
||||
|
||||
expand_expected_paren_or_brace =
|
||||
expected `(` or `{"{"}`, found `{$token}`
|
||||
@ -58,6 +58,9 @@ expand_feature_removed =
|
||||
.label = feature has been removed
|
||||
.reason = {$reason}
|
||||
|
||||
expand_glob_delegation_outside_impls =
|
||||
glob delegation is only supported in impls
|
||||
|
||||
expand_helper_attribute_name_invalid =
|
||||
`{$name}` cannot be a name of derive helper attribute
|
||||
|
||||
|
@ -357,6 +357,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GlobDelegationExpander {
|
||||
fn expand(&self, ecx: &mut ExtCtxt<'_>) -> ExpandResult<Vec<(Ident, Option<Ident>)>, ()>;
|
||||
}
|
||||
|
||||
// Use a macro because forwarding to a simple function has type system issues
|
||||
macro_rules! make_stmts_default {
|
||||
($me:expr) => {
|
||||
@ -714,6 +718,9 @@ pub enum SyntaxExtensionKind {
|
||||
/// The produced AST fragment is appended to the input AST fragment.
|
||||
Box<dyn MultiItemModifier + sync::DynSync + sync::DynSend>,
|
||||
),
|
||||
|
||||
/// A glob delegation.
|
||||
GlobDelegation(Box<dyn GlobDelegationExpander + sync::DynSync + sync::DynSend>),
|
||||
}
|
||||
|
||||
/// A struct representing a macro definition in "lowered" form ready for expansion.
|
||||
@ -748,7 +755,9 @@ impl SyntaxExtension {
|
||||
/// Returns which kind of macro calls this syntax extension.
|
||||
pub fn macro_kind(&self) -> MacroKind {
|
||||
match self.kind {
|
||||
SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) => MacroKind::Bang,
|
||||
SyntaxExtensionKind::Bang(..)
|
||||
| SyntaxExtensionKind::LegacyBang(..)
|
||||
| SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang,
|
||||
SyntaxExtensionKind::Attr(..)
|
||||
| SyntaxExtensionKind::LegacyAttr(..)
|
||||
| SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr,
|
||||
@ -922,6 +931,32 @@ impl SyntaxExtension {
|
||||
SyntaxExtension::default(SyntaxExtensionKind::NonMacroAttr, edition)
|
||||
}
|
||||
|
||||
pub fn glob_delegation(
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
edition: Edition,
|
||||
) -> SyntaxExtension {
|
||||
struct GlobDelegationExpanderImpl {
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
}
|
||||
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) {
|
||||
Ok(suffixes) => ExpandResult::Ready(suffixes),
|
||||
Err(Indeterminate) if ecx.force_mode => ExpandResult::Ready(Vec::new()),
|
||||
Err(Indeterminate) => ExpandResult::Retry(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let expander = GlobDelegationExpanderImpl { trait_def_id, impl_def_id };
|
||||
SyntaxExtension::default(SyntaxExtensionKind::GlobDelegation(Box::new(expander)), edition)
|
||||
}
|
||||
|
||||
pub fn expn_data(
|
||||
&self,
|
||||
parent: LocalExpnId,
|
||||
@ -1030,6 +1065,16 @@ pub trait ResolverExpand {
|
||||
|
||||
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
|
||||
fn registered_tools(&self) -> &RegisteredTools;
|
||||
|
||||
/// Mark this invocation id as a glob delegation.
|
||||
fn register_glob_delegation(&mut self, invoc_id: LocalExpnId);
|
||||
|
||||
/// Names of specific methods to which glob delegation expands.
|
||||
fn glob_delegation_suffixes(
|
||||
&mut self,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate>;
|
||||
}
|
||||
|
||||
pub trait LintStoreExpand {
|
||||
|
@ -435,8 +435,16 @@ pub struct ExpectedParenOrBrace<'a> {
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_empty_delegation_list)]
|
||||
pub(crate) struct EmptyDelegationList {
|
||||
#[diag(expand_empty_delegation_mac)]
|
||||
pub(crate) struct EmptyDelegationMac {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub kind: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_glob_delegation_outside_impls)]
|
||||
pub(crate) struct GlobDelegationOutsideImpls {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::base::*;
|
||||
use crate::config::StripUnconfigured;
|
||||
use crate::errors::{
|
||||
EmptyDelegationList, IncompleteParse, RecursionLimitReached, RemoveExprNotSupported,
|
||||
RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
|
||||
EmptyDelegationMac, GlobDelegationOutsideImpls, IncompleteParse, RecursionLimitReached,
|
||||
RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, WrongFragmentKind,
|
||||
};
|
||||
use crate::mbe::diagnostics::annotate_err_with_kind;
|
||||
use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod};
|
||||
@ -343,6 +343,9 @@ pub enum InvocationKind {
|
||||
is_const: bool,
|
||||
item: Annotatable,
|
||||
},
|
||||
GlobDelegation {
|
||||
item: P<ast::AssocItem>,
|
||||
},
|
||||
}
|
||||
|
||||
impl InvocationKind {
|
||||
@ -370,6 +373,7 @@ impl Invocation {
|
||||
InvocationKind::Bang { span, .. } => *span,
|
||||
InvocationKind::Attr { attr, .. } => attr.span,
|
||||
InvocationKind::Derive { path, .. } => path.span,
|
||||
InvocationKind::GlobDelegation { item } => item.span,
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,6 +382,7 @@ impl Invocation {
|
||||
InvocationKind::Bang { span, .. } => span,
|
||||
InvocationKind::Attr { attr, .. } => &mut attr.span,
|
||||
InvocationKind::Derive { path, .. } => &mut path.span,
|
||||
InvocationKind::GlobDelegation { item } => &mut item.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -800,6 +805,36 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
InvocationKind::GlobDelegation { item } => {
|
||||
let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() };
|
||||
let suffixes = match ext {
|
||||
SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx)
|
||||
{
|
||||
ExpandResult::Ready(suffixes) => suffixes,
|
||||
ExpandResult::Retry(()) => {
|
||||
// Reassemble the original invocation for retrying.
|
||||
return ExpandResult::Retry(Invocation {
|
||||
kind: InvocationKind::GlobDelegation { item },
|
||||
..invoc
|
||||
});
|
||||
}
|
||||
},
|
||||
SyntaxExtensionKind::LegacyBang(..) => {
|
||||
let msg = "expanded a dummy glob delegation";
|
||||
let guar = self.cx.dcx().span_delayed_bug(span, msg);
|
||||
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
type Node = AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>;
|
||||
let single_delegations = build_single_delegations::<Node>(
|
||||
self.cx, deleg, &item, &suffixes, item.span, true,
|
||||
);
|
||||
fragment_kind.expect_from_annotatables(
|
||||
single_delegations.map(|item| Annotatable::ImplItem(P(item))),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1067,7 +1102,7 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
|
||||
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
|
||||
unreachable!()
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
None
|
||||
}
|
||||
fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind {
|
||||
@ -1126,7 +1161,7 @@ impl InvocationCollectorNode for P<ast::Item> {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.kind {
|
||||
ItemKind::DelegationMac(deleg) => Some((deleg, self)),
|
||||
_ => None,
|
||||
@ -1270,7 +1305,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag>
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.wrapped.kind {
|
||||
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
|
||||
_ => None,
|
||||
@ -1311,7 +1346,7 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.wrapped.kind {
|
||||
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
|
||||
_ => None,
|
||||
@ -1487,7 +1522,7 @@ impl InvocationCollectorNode for ast::Stmt {
|
||||
};
|
||||
(mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No })
|
||||
}
|
||||
fn delegation_list(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
|
||||
match &self.kind {
|
||||
StmtKind::Item(item) => match &item.kind {
|
||||
ItemKind::DelegationMac(deleg) => Some((deleg, item)),
|
||||
@ -1684,6 +1719,44 @@ impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag>
|
||||
}
|
||||
}
|
||||
|
||||
fn build_single_delegations<'a, Node: InvocationCollectorNode>(
|
||||
ecx: &ExtCtxt<'_>,
|
||||
deleg: &'a ast::DelegationMac,
|
||||
item: &'a ast::Item<Node::ItemKind>,
|
||||
suffixes: &'a [(Ident, Option<Ident>)],
|
||||
item_span: Span,
|
||||
from_glob: bool,
|
||||
) -> impl Iterator<Item = ast::Item<Node::ItemKind>> + 'a {
|
||||
if suffixes.is_empty() {
|
||||
// Report an error for now, to avoid keeping stem for resolution and
|
||||
// stability checks.
|
||||
let kind = String::from(if from_glob { "glob" } else { "list" });
|
||||
ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind });
|
||||
}
|
||||
|
||||
suffixes.iter().map(move |&(ident, rename)| {
|
||||
let mut path = deleg.prefix.clone();
|
||||
path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None });
|
||||
|
||||
ast::Item {
|
||||
attrs: item.attrs.clone(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: if from_glob { item_span } else { ident.span },
|
||||
vis: item.vis.clone(),
|
||||
ident: rename.unwrap_or(ident),
|
||||
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
qself: deleg.qself.clone(),
|
||||
path,
|
||||
rename,
|
||||
body: deleg.body.clone(),
|
||||
from_glob,
|
||||
})),
|
||||
tokens: None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct InvocationCollector<'a, 'b> {
|
||||
cx: &'a mut ExtCtxt<'b>,
|
||||
invocations: Vec<(Invocation, Option<Lrc<SyntaxExtension>>)>,
|
||||
@ -1702,6 +1775,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
|
||||
fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment {
|
||||
let expn_id = LocalExpnId::fresh_empty();
|
||||
if matches!(kind, InvocationKind::GlobDelegation { .. }) {
|
||||
// In resolver we need to know which invocation ids are delegations early,
|
||||
// before their `ExpnData` is filled.
|
||||
self.cx.resolver.register_glob_delegation(expn_id);
|
||||
}
|
||||
let vis = kind.placeholder_visibility();
|
||||
self.invocations.push((
|
||||
Invocation {
|
||||
@ -1734,6 +1812,14 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
self.collect(kind, InvocationKind::Attr { attr, pos, item, derives })
|
||||
}
|
||||
|
||||
fn collect_glob_delegation(
|
||||
&mut self,
|
||||
item: P<ast::AssocItem>,
|
||||
kind: AstFragmentKind,
|
||||
) -> AstFragment {
|
||||
self.collect(kind, InvocationKind::GlobDelegation { item })
|
||||
}
|
||||
|
||||
/// If `item` is an attribute invocation, remove the attribute and return it together with
|
||||
/// its position and derives following it. We have to collect the derives in order to resolve
|
||||
/// legacy derive helpers (helpers written before derives that introduce them).
|
||||
@ -1901,37 +1987,27 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
Node::post_flat_map_node_collect_bang(&mut res, add_semicolon);
|
||||
res
|
||||
}
|
||||
None if let Some((deleg, item)) = node.delegation_list() => {
|
||||
if deleg.suffixes.is_empty() {
|
||||
// Report an error for now, to avoid keeping stem for resolution and
|
||||
// stability checks.
|
||||
self.cx.dcx().emit_err(EmptyDelegationList { span: item.span });
|
||||
None if let Some((deleg, item)) = node.delegation() => {
|
||||
let Some(suffixes) = &deleg.suffixes else {
|
||||
let item = match node.to_annotatable() {
|
||||
Annotatable::ImplItem(item) => item,
|
||||
ann @ (Annotatable::Item(_)
|
||||
| Annotatable::TraitItem(_)
|
||||
| Annotatable::Stmt(_)) => {
|
||||
let span = ann.span();
|
||||
self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span });
|
||||
return Default::default();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
return self.collect_glob_delegation(item, Node::KIND).make_ast::<Node>();
|
||||
};
|
||||
|
||||
Node::flatten_outputs(deleg.suffixes.iter().map(|&(ident, rename)| {
|
||||
let mut path = deleg.prefix.clone();
|
||||
path.segments.push(ast::PathSegment {
|
||||
ident,
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
args: None,
|
||||
});
|
||||
|
||||
let mut item = Node::from_item(ast::Item {
|
||||
attrs: item.attrs.clone(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: ident.span,
|
||||
vis: item.vis.clone(),
|
||||
ident: rename.unwrap_or(ident),
|
||||
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
qself: deleg.qself.clone(),
|
||||
path,
|
||||
rename,
|
||||
body: deleg.body.clone(),
|
||||
})),
|
||||
tokens: None,
|
||||
});
|
||||
|
||||
let single_delegations = build_single_delegations::<Node>(
|
||||
self.cx, deleg, item, suffixes, item.span, false,
|
||||
);
|
||||
Node::flatten_outputs(single_delegations.map(|item| {
|
||||
let mut item = Node::from_item(item);
|
||||
assign_id!(self, item.node_id_mut(), || item.noop_flat_map(self))
|
||||
}))
|
||||
}
|
||||
@ -1983,7 +2059,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
|
||||
self.collect_bang(mac, Node::KIND).make_ast::<Node>()
|
||||
})
|
||||
}
|
||||
None if node.delegation_list().is_some() => unreachable!(),
|
||||
None if node.delegation().is_some() => unreachable!(),
|
||||
None => {
|
||||
assign_id!(self, node.node_id_mut(), || node.noop_visit(self))
|
||||
}
|
||||
|
@ -2699,12 +2699,13 @@ pub(crate) struct SingleColonImportPath {
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_bad_item_kind)]
|
||||
#[help]
|
||||
pub(crate) struct BadItemKind {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: &'static str,
|
||||
pub ctx: &'static str,
|
||||
#[help]
|
||||
pub help: Option<()>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
@ -707,15 +707,25 @@ impl<'a> Parser<'a> {
|
||||
};
|
||||
|
||||
let (ident, item_kind) = if self.eat(&token::PathSep) {
|
||||
let (suffixes, _) = self.parse_delim_comma_seq(Delimiter::Brace, |p| {
|
||||
Ok((p.parse_path_segment_ident()?, rename(p)?))
|
||||
})?;
|
||||
let suffixes = if self.eat(&token::BinOp(token::Star)) {
|
||||
None
|
||||
} else {
|
||||
let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?));
|
||||
Some(self.parse_delim_comma_seq(Delimiter::Brace, parse_suffix)?.0)
|
||||
};
|
||||
let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? };
|
||||
(Ident::empty(), ItemKind::DelegationMac(Box::new(deleg)))
|
||||
} else {
|
||||
let rename = rename(self)?;
|
||||
let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
|
||||
let deleg = Delegation { id: DUMMY_NODE_ID, qself, path, rename, body: body(self)? };
|
||||
let deleg = Delegation {
|
||||
id: DUMMY_NODE_ID,
|
||||
qself,
|
||||
path,
|
||||
rename,
|
||||
body: body(self)?,
|
||||
from_glob: false,
|
||||
};
|
||||
(ident, ItemKind::Delegation(Box::new(deleg)))
|
||||
};
|
||||
|
||||
@ -1237,7 +1247,11 @@ impl<'a> Parser<'a> {
|
||||
// FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`)
|
||||
let span = self.psess.source_map().guess_head_span(span);
|
||||
let descr = kind.descr();
|
||||
self.dcx().emit_err(errors::BadItemKind { span, descr, ctx });
|
||||
let help = match kind {
|
||||
ItemKind::DelegationMac(deleg) if deleg.suffixes.is_none() => None,
|
||||
_ => Some(()),
|
||||
};
|
||||
self.dcx().emit_err(errors::BadItemKind { span, descr, ctx, help });
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
|
||||
use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
|
||||
use rustc_attr as attr;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_expand::base::ResolverExpand;
|
||||
use rustc_expand::expand::AstFragment;
|
||||
use rustc_hir::def::{self, *};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
|
||||
@ -1358,6 +1359,14 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
self.visit_invoc_in_module(item.id);
|
||||
}
|
||||
AssocCtxt::Impl => {
|
||||
let invoc_id = item.id.placeholder_to_expn_id();
|
||||
if !self.r.glob_delegation_invoc_ids.contains(&invoc_id) {
|
||||
self.r
|
||||
.impl_unexpanded_invocations
|
||||
.entry(self.r.invocation_parent(invoc_id))
|
||||
.or_default()
|
||||
.insert(invoc_id);
|
||||
}
|
||||
self.visit_invoc(item.id);
|
||||
}
|
||||
}
|
||||
@ -1379,18 +1388,21 @@ impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
self.r.feed_visibility(feed, vis);
|
||||
}
|
||||
|
||||
if ctxt == AssocCtxt::Trait {
|
||||
let ns = match item.kind {
|
||||
AssocItemKind::Const(..)
|
||||
| AssocItemKind::Delegation(..)
|
||||
| AssocItemKind::Fn(..) => ValueNS,
|
||||
AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => {
|
||||
ValueNS
|
||||
}
|
||||
AssocItemKind::Type(..) => TypeNS,
|
||||
AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
|
||||
};
|
||||
|
||||
if ctxt == AssocCtxt::Trait {
|
||||
let parent = self.parent_scope.module;
|
||||
let expansion = self.parent_scope.expansion;
|
||||
self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
|
||||
} else if !matches!(&item.kind, AssocItemKind::Delegation(deleg) if deleg.from_glob) {
|
||||
let impl_def_id = self.r.tcx.local_parent(local_def_id);
|
||||
let key = BindingKey::new(item.ident.normalize_to_macros_2_0(), ns);
|
||||
self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
|
||||
}
|
||||
|
||||
visit::walk_assoc_item(self, item, ctxt);
|
||||
|
@ -144,8 +144,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
}
|
||||
ItemKind::GlobalAsm(..) => DefKind::GlobalAsm,
|
||||
ItemKind::Use(..) => return visit::walk_item(self, i),
|
||||
ItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
|
||||
ItemKind::DelegationMac(..) => unreachable!(),
|
||||
ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => {
|
||||
return self.visit_macro_invoc(i.id);
|
||||
}
|
||||
};
|
||||
let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span);
|
||||
|
||||
@ -294,8 +295,9 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn,
|
||||
AssocItemKind::Const(..) => DefKind::AssocConst,
|
||||
AssocItemKind::Type(..) => DefKind::AssocTy,
|
||||
AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
|
||||
AssocItemKind::DelegationMac(..) => unreachable!(),
|
||||
AssocItemKind::MacCall(..) | AssocItemKind::DelegationMac(..) => {
|
||||
return self.visit_macro_invoc(i.id);
|
||||
}
|
||||
};
|
||||
|
||||
let def = self.create_def(i.id, i.ident.name, def_kind, i.span);
|
||||
|
@ -1089,7 +1089,7 @@ pub struct Resolver<'a, 'tcx> {
|
||||
single_segment_macro_resolutions:
|
||||
Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
|
||||
multi_segment_macro_resolutions:
|
||||
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>,
|
||||
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>, Namespace)>,
|
||||
builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
|
||||
/// `derive(Copy)` marks items they are applied to so they are treated specially later.
|
||||
/// Derive macros cannot modify the item themselves and have to store the markers in the global
|
||||
@ -1163,6 +1163,15 @@ pub struct Resolver<'a, 'tcx> {
|
||||
doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
|
||||
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
|
||||
all_macro_rules: FxHashMap<Symbol, Res>,
|
||||
|
||||
/// Invocation ids of all glob delegations.
|
||||
glob_delegation_invoc_ids: FxHashSet<LocalExpnId>,
|
||||
/// Analogue of module `unexpanded_invocations` but in trait impls, excluding glob delegations.
|
||||
/// Needed because glob delegations wait for all other neighboring macros to expand.
|
||||
impl_unexpanded_invocations: FxHashMap<LocalDefId, FxHashSet<LocalExpnId>>,
|
||||
/// Simplified analogue of module `resolutions` but in trait impls, excluding glob delegations.
|
||||
/// Needed because glob delegations exclude explicitly defined names.
|
||||
impl_binding_keys: FxHashMap<LocalDefId, FxHashSet<BindingKey>>,
|
||||
}
|
||||
|
||||
/// Nothing really interesting here; it just provides memory for the rest of the crate.
|
||||
@ -1504,6 +1513,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
doc_link_traits_in_scope: Default::default(),
|
||||
all_macro_rules: Default::default(),
|
||||
delegation_fn_sigs: Default::default(),
|
||||
glob_delegation_invoc_ids: Default::default(),
|
||||
impl_unexpanded_invocations: Default::default(),
|
||||
impl_binding_keys: Default::default(),
|
||||
};
|
||||
|
||||
let root_parent_scope = ParentScope::module(graph_root, &resolver);
|
||||
|
@ -5,7 +5,7 @@ use crate::errors::CannotDetermineMacroResolution;
|
||||
use crate::errors::{self, AddAsNonDerive, CannotFindIdentInThisScope};
|
||||
use crate::errors::{MacroExpectedFound, RemoveSurroundingDerive};
|
||||
use crate::Namespace::*;
|
||||
use crate::{BuiltinMacroState, Determinacy, MacroData, Used};
|
||||
use crate::{BindingKey, BuiltinMacroState, Determinacy, MacroData, Used};
|
||||
use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
|
||||
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
|
||||
use rustc_ast::expand::StrippedCfgItem;
|
||||
@ -198,6 +198,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
|
||||
|
||||
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
|
||||
if let Some(unexpanded_invocations) =
|
||||
self.impl_unexpanded_invocations.get_mut(&self.invocation_parent(expansion))
|
||||
{
|
||||
unexpanded_invocations.remove(&expansion);
|
||||
}
|
||||
}
|
||||
|
||||
fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) {
|
||||
@ -262,15 +267,21 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let (path, kind, inner_attr, derives) = match invoc.kind {
|
||||
InvocationKind::Attr { ref attr, ref derives, .. } => (
|
||||
&attr.get_normal_item().path,
|
||||
MacroKind::Attr,
|
||||
attr.style == ast::AttrStyle::Inner,
|
||||
self.arenas.alloc_ast_paths(derives),
|
||||
),
|
||||
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]),
|
||||
InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]),
|
||||
let (mut derives, mut inner_attr, mut deleg_impl) = (&[][..], false, None);
|
||||
let (path, kind) = match invoc.kind {
|
||||
InvocationKind::Attr { ref attr, derives: ref attr_derives, .. } => {
|
||||
derives = self.arenas.alloc_ast_paths(attr_derives);
|
||||
inner_attr = attr.style == ast::AttrStyle::Inner;
|
||||
(&attr.get_normal_item().path, MacroKind::Attr)
|
||||
}
|
||||
InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang),
|
||||
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));
|
||||
// It is sufficient to consider glob delegation a bang macro for now.
|
||||
(&deleg.prefix, MacroKind::Bang)
|
||||
}
|
||||
};
|
||||
|
||||
// Derives are not included when `invocations` are collected, so we have to add them here.
|
||||
@ -286,10 +297,11 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
node_id,
|
||||
force,
|
||||
soft_custom_inner_attributes_gate(path, invoc),
|
||||
deleg_impl,
|
||||
)?;
|
||||
|
||||
let span = invoc.span();
|
||||
let def_id = res.opt_def_id();
|
||||
let def_id = if deleg_impl.is_some() { None } else { res.opt_def_id() };
|
||||
invoc_id.set_expn_data(
|
||||
ext.expn_data(
|
||||
parent_scope.expansion,
|
||||
@ -452,6 +464,45 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
|
||||
fn registered_tools(&self) -> &RegisteredTools {
|
||||
self.registered_tools
|
||||
}
|
||||
|
||||
fn register_glob_delegation(&mut self, invoc_id: LocalExpnId) {
|
||||
self.glob_delegation_invoc_ids.insert(invoc_id);
|
||||
}
|
||||
|
||||
fn glob_delegation_suffixes(
|
||||
&mut self,
|
||||
trait_def_id: DefId,
|
||||
impl_def_id: LocalDefId,
|
||||
) -> Result<Vec<(Ident, Option<Ident>)>, Indeterminate> {
|
||||
let target_trait = self.expect_module(trait_def_id);
|
||||
if !target_trait.unexpanded_invocations.borrow().is_empty() {
|
||||
return Err(Indeterminate);
|
||||
}
|
||||
// FIXME: Instead of waiting try generating all trait methods, and pruning
|
||||
// the shadowed ones a bit later, e.g. when all macro expansion completes.
|
||||
// Pros: expansion will be stuck less (but only in exotic cases), the implementation may be
|
||||
// less hacky.
|
||||
// Cons: More code is generated just to be deleted later, deleting already created `DefId`s
|
||||
// may be nontrivial.
|
||||
if let Some(unexpanded_invocations) = self.impl_unexpanded_invocations.get(&impl_def_id)
|
||||
&& !unexpanded_invocations.is_empty()
|
||||
{
|
||||
return Err(Indeterminate);
|
||||
}
|
||||
|
||||
let mut idents = Vec::new();
|
||||
target_trait.for_each_child(self, |this, ident, 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.normalize_to_macros_2_0(), ns))
|
||||
{
|
||||
// The name is overridden, do not produce it from the glob delegation.
|
||||
} else {
|
||||
idents.push((ident, None));
|
||||
}
|
||||
});
|
||||
Ok(idents)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
@ -468,15 +519,40 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
node_id: NodeId,
|
||||
force: bool,
|
||||
soft_custom_inner_attributes_gate: bool,
|
||||
deleg_impl: Option<LocalDefId>,
|
||||
) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> {
|
||||
let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force)
|
||||
{
|
||||
let (ext, res) = match self.resolve_macro_or_delegation_path(
|
||||
path,
|
||||
Some(kind),
|
||||
parent_scope,
|
||||
true,
|
||||
force,
|
||||
deleg_impl,
|
||||
) {
|
||||
Ok((Some(ext), res)) => (ext, res),
|
||||
Ok((None, res)) => (self.dummy_ext(kind), res),
|
||||
Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err),
|
||||
Err(Determinacy::Undetermined) => return Err(Indeterminate),
|
||||
};
|
||||
|
||||
// Everything below is irrelevant to glob delegation, take a shortcut.
|
||||
if deleg_impl.is_some() {
|
||||
if !matches!(res, Res::Err | Res::Def(DefKind::Trait, _)) {
|
||||
self.dcx().emit_err(MacroExpectedFound {
|
||||
span: path.span,
|
||||
expected: "trait",
|
||||
article: "a",
|
||||
found: res.descr(),
|
||||
macro_path: &pprust::path_to_string(path),
|
||||
remove_surrounding_derive: None,
|
||||
add_as_non_derive: None,
|
||||
});
|
||||
return Ok((self.dummy_ext(kind), Res::Err));
|
||||
}
|
||||
|
||||
return Ok((ext, res));
|
||||
}
|
||||
|
||||
// Report errors for the resolved macro.
|
||||
for segment in &path.segments {
|
||||
if let Some(args) = &segment.args {
|
||||
@ -605,12 +681,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
parent_scope: &ParentScope<'a>,
|
||||
trace: bool,
|
||||
force: bool,
|
||||
) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
|
||||
self.resolve_macro_or_delegation_path(path, kind, parent_scope, trace, force, None)
|
||||
}
|
||||
|
||||
fn resolve_macro_or_delegation_path(
|
||||
&mut self,
|
||||
path: &ast::Path,
|
||||
kind: Option<MacroKind>,
|
||||
parent_scope: &ParentScope<'a>,
|
||||
trace: bool,
|
||||
force: bool,
|
||||
deleg_impl: Option<LocalDefId>,
|
||||
) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
|
||||
let path_span = path.span;
|
||||
let mut path = Segment::from_path(path);
|
||||
|
||||
// Possibly apply the macro helper hack
|
||||
if kind == Some(MacroKind::Bang)
|
||||
if deleg_impl.is_none()
|
||||
&& kind == Some(MacroKind::Bang)
|
||||
&& path.len() == 1
|
||||
&& path[0].ident.span.ctxt().outer_expn_data().local_inner_macros
|
||||
{
|
||||
@ -618,13 +707,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
path.insert(0, Segment::from_ident(root));
|
||||
}
|
||||
|
||||
let res = if path.len() > 1 {
|
||||
let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) {
|
||||
let res = if deleg_impl.is_some() || path.len() > 1 {
|
||||
let ns = if deleg_impl.is_some() { TypeNS } else { MacroNS };
|
||||
let res = match self.maybe_resolve_path(&path, Some(ns), parent_scope) {
|
||||
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res),
|
||||
PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
|
||||
PathResult::NonModule(..)
|
||||
| PathResult::Indeterminate
|
||||
| PathResult::Failed { .. } => Err(Determinacy::Determined),
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
|
||||
Ok(module.res().unwrap())
|
||||
}
|
||||
PathResult::Module(..) => unreachable!(),
|
||||
};
|
||||
|
||||
@ -636,6 +729,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
kind,
|
||||
*parent_scope,
|
||||
res.ok(),
|
||||
ns,
|
||||
));
|
||||
}
|
||||
|
||||
@ -670,7 +764,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
res
|
||||
};
|
||||
|
||||
res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res))
|
||||
let res = res?;
|
||||
let ext = match deleg_impl {
|
||||
Some(impl_def_id) => match res {
|
||||
def::Res::Def(DefKind::Trait, def_id) => {
|
||||
let edition = self.tcx.sess.edition();
|
||||
Some(Lrc::new(SyntaxExtension::glob_delegation(def_id, impl_def_id, edition)))
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
None => self.get_macro(res).map(|macro_data| macro_data.ext.clone()),
|
||||
};
|
||||
Ok((ext, res))
|
||||
}
|
||||
|
||||
pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) {
|
||||
@ -706,14 +811,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
};
|
||||
|
||||
let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions);
|
||||
for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions {
|
||||
for (mut path, path_span, kind, parent_scope, initial_res, ns) in macro_resolutions {
|
||||
// FIXME: Path resolution will ICE if segment IDs present.
|
||||
for seg in &mut path {
|
||||
seg.id = None;
|
||||
}
|
||||
match self.resolve_path(
|
||||
&path,
|
||||
Some(MacroNS),
|
||||
Some(ns),
|
||||
&parent_scope,
|
||||
Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
|
||||
None,
|
||||
@ -721,6 +826,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => {
|
||||
check_consistency(self, &path, path_span, kind, initial_res, res)
|
||||
}
|
||||
// This may be a trait for glob delegation expansions.
|
||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => check_consistency(
|
||||
self,
|
||||
&path,
|
||||
path_span,
|
||||
kind,
|
||||
initial_res,
|
||||
module.res().unwrap(),
|
||||
),
|
||||
path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => {
|
||||
let mut suggestion = None;
|
||||
let (span, label, module) =
|
||||
|
32
tests/ui/delegation/body-identity-glob.rs
Normal file
32
tests/ui/delegation/body-identity-glob.rs
Normal file
@ -0,0 +1,32 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) {}
|
||||
fn bar(&self) {}
|
||||
}
|
||||
|
||||
impl Trait for u8 {}
|
||||
|
||||
struct S(u8);
|
||||
|
||||
mod to_import {
|
||||
pub fn check(arg: &u8) -> &u8 { arg }
|
||||
}
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::* {
|
||||
use to_import::check;
|
||||
|
||||
let _arr = Some(self.0).map(|x| [x * 2; 3]);
|
||||
check(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(0);
|
||||
s.foo();
|
||||
s.bar();
|
||||
}
|
11
tests/ui/delegation/empty-glob.rs
Normal file
11
tests/ui/delegation/empty-glob.rs
Normal file
@ -0,0 +1,11 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {}
|
||||
|
||||
struct S;
|
||||
impl S {
|
||||
reuse Trait::*; //~ ERROR empty glob delegation is not supported
|
||||
}
|
||||
|
||||
fn main() {}
|
8
tests/ui/delegation/empty-glob.stderr
Normal file
8
tests/ui/delegation/empty-glob.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: empty glob delegation is not supported
|
||||
--> $DIR/empty-glob.rs:8:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
12
tests/ui/delegation/glob-bad-path.rs
Normal file
12
tests/ui/delegation/glob-bad-path.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {}
|
||||
struct S;
|
||||
|
||||
impl Trait for u8 {
|
||||
reuse unresolved::*; //~ ERROR failed to resolve: use of undeclared crate or module `unresolved`
|
||||
reuse S::*; //~ ERROR expected trait, found struct `S`
|
||||
}
|
||||
|
||||
fn main() {}
|
15
tests/ui/delegation/glob-bad-path.stderr
Normal file
15
tests/ui/delegation/glob-bad-path.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error: expected trait, found struct `S`
|
||||
--> $DIR/glob-bad-path.rs:9:11
|
||||
|
|
||||
LL | reuse S::*;
|
||||
| ^ not a trait
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `unresolved`
|
||||
--> $DIR/glob-bad-path.rs:8:11
|
||||
|
|
||||
LL | reuse unresolved::*;
|
||||
| ^^^^^^^^^^ use of undeclared crate or module `unresolved`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
33
tests/ui/delegation/glob-glob-conflict.rs
Normal file
33
tests/ui/delegation/glob-glob-conflict.rs
Normal file
@ -0,0 +1,33 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait1 {
|
||||
fn method(&self) -> u8;
|
||||
}
|
||||
trait Trait2 {
|
||||
fn method(&self) -> u8;
|
||||
}
|
||||
trait Trait {
|
||||
fn method(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait1 for u8 {
|
||||
fn method(&self) -> u8 { 0 }
|
||||
}
|
||||
impl Trait1 for u16 {
|
||||
fn method(&self) -> u8 { 1 }
|
||||
}
|
||||
impl Trait2 for u8 {
|
||||
fn method(&self) -> u8 { 2 }
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
reuse Trait1::*;
|
||||
reuse Trait2::*; //~ ERROR duplicate definitions with name `method`
|
||||
}
|
||||
impl Trait for u16 {
|
||||
reuse Trait1::*;
|
||||
reuse Trait1::*; //~ ERROR duplicate definitions with name `method`
|
||||
}
|
||||
|
||||
fn main() {}
|
25
tests/ui/delegation/glob-glob-conflict.stderr
Normal file
25
tests/ui/delegation/glob-glob-conflict.stderr
Normal file
@ -0,0 +1,25 @@
|
||||
error[E0201]: duplicate definitions with name `method`:
|
||||
--> $DIR/glob-glob-conflict.rs:26:5
|
||||
|
|
||||
LL | fn method(&self) -> u8;
|
||||
| ----------------------- item in trait
|
||||
...
|
||||
LL | reuse Trait1::*;
|
||||
| ---------------- previous definition here
|
||||
LL | reuse Trait2::*;
|
||||
| ^^^^^^^^^^^^^^^^ duplicate definition
|
||||
|
||||
error[E0201]: duplicate definitions with name `method`:
|
||||
--> $DIR/glob-glob-conflict.rs:30:5
|
||||
|
|
||||
LL | fn method(&self) -> u8;
|
||||
| ----------------------- item in trait
|
||||
...
|
||||
LL | reuse Trait1::*;
|
||||
| ---------------- previous definition here
|
||||
LL | reuse Trait1::*;
|
||||
| ^^^^^^^^^^^^^^^^ duplicate definition
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0201`.
|
36
tests/ui/delegation/glob-glob.rs
Normal file
36
tests/ui/delegation/glob-glob.rs
Normal file
@ -0,0 +1,36 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
mod inner {
|
||||
pub trait TraitFoo {
|
||||
fn foo(&self) -> u8;
|
||||
}
|
||||
pub trait TraitBar {
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl TraitFoo for u8 {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
}
|
||||
impl TraitBar for u8 {
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
}
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8;
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
reuse inner::TraitFoo::*;
|
||||
reuse inner::TraitBar::*;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let u = 0u8;
|
||||
u.foo();
|
||||
u.bar();
|
||||
}
|
38
tests/ui/delegation/glob-non-fn.rs
Normal file
38
tests/ui/delegation/glob-non-fn.rs
Normal file
@ -0,0 +1,38 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn method(&self);
|
||||
const CONST: u8;
|
||||
type Type;
|
||||
#[allow(non_camel_case_types)]
|
||||
type method;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
fn method(&self) {}
|
||||
const CONST: u8 = 0;
|
||||
type Type = u8;
|
||||
type method = u8;
|
||||
}
|
||||
|
||||
struct Good(u8);
|
||||
impl Trait for Good {
|
||||
reuse Trait::* { &self.0 }
|
||||
// Explicit definitions for non-delegatable items.
|
||||
const CONST: u8 = 0;
|
||||
type Type = u8;
|
||||
type method = u8;
|
||||
}
|
||||
|
||||
struct Bad(u8);
|
||||
impl Trait for Bad { //~ ERROR not all trait items implemented, missing: `CONST`, `Type`, `method`
|
||||
reuse Trait::* { &self.0 }
|
||||
//~^ ERROR item `CONST` is an associated method, which doesn't match its trait `Trait`
|
||||
//~| ERROR item `Type` is an associated method, which doesn't match its trait `Trait`
|
||||
//~| ERROR duplicate definitions with name `method`
|
||||
//~| ERROR expected function, found associated constant `Trait::CONST`
|
||||
//~| ERROR expected function, found associated type `Trait::Type`
|
||||
}
|
||||
|
||||
fn main() {}
|
62
tests/ui/delegation/glob-non-fn.stderr
Normal file
62
tests/ui/delegation/glob-non-fn.stderr
Normal file
@ -0,0 +1,62 @@
|
||||
error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait`
|
||||
--> $DIR/glob-non-fn.rs:30:5
|
||||
|
|
||||
LL | const CONST: u8;
|
||||
| ---------------- item in trait
|
||||
...
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait
|
||||
|
||||
error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait`
|
||||
--> $DIR/glob-non-fn.rs:30:5
|
||||
|
|
||||
LL | type Type;
|
||||
| ---------- item in trait
|
||||
...
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait
|
||||
|
||||
error[E0201]: duplicate definitions with name `method`:
|
||||
--> $DIR/glob-non-fn.rs:30:5
|
||||
|
|
||||
LL | fn method(&self);
|
||||
| ----------------- item in trait
|
||||
...
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| duplicate definition
|
||||
| previous definition here
|
||||
|
||||
error[E0423]: expected function, found associated constant `Trait::CONST`
|
||||
--> $DIR/glob-non-fn.rs:30:11
|
||||
|
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^ not a function
|
||||
|
||||
error[E0423]: expected function, found associated type `Trait::Type`
|
||||
--> $DIR/glob-non-fn.rs:30:11
|
||||
|
|
||||
LL | reuse Trait::* { &self.0 }
|
||||
| ^^^^^
|
||||
|
|
||||
= note: can't use a type alias as a constructor
|
||||
|
||||
error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method`
|
||||
--> $DIR/glob-non-fn.rs:29:1
|
||||
|
|
||||
LL | const CONST: u8;
|
||||
| --------------- `CONST` from trait
|
||||
LL | type Type;
|
||||
| --------- `Type` from trait
|
||||
LL | #[allow(non_camel_case_types)]
|
||||
LL | type method;
|
||||
| ----------- `method` from trait
|
||||
...
|
||||
LL | impl Trait for Bad {
|
||||
| ^^^^^^^^^^^^^^^^^^ missing `CONST`, `Type`, `method` in implementation
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0046, E0201, E0324, E0423.
|
||||
For more information about an error, try `rustc --explain E0046`.
|
20
tests/ui/delegation/glob-non-impl.rs
Normal file
20
tests/ui/delegation/glob-non-impl.rs
Normal file
@ -0,0 +1,20 @@
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn method() {}
|
||||
}
|
||||
|
||||
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
|
||||
|
||||
trait OtherTrait {
|
||||
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
|
||||
}
|
||||
|
||||
extern {
|
||||
reuse Trait::*; //~ ERROR delegation is not supported in `extern` blocks
|
||||
}
|
||||
|
||||
fn main() {
|
||||
reuse Trait::*; //~ ERROR glob delegation is only supported in impls
|
||||
}
|
26
tests/ui/delegation/glob-non-impl.stderr
Normal file
26
tests/ui/delegation/glob-non-impl.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
error: delegation is not supported in `extern` blocks
|
||||
--> $DIR/glob-non-impl.rs:15:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: glob delegation is only supported in impls
|
||||
--> $DIR/glob-non-impl.rs:8:1
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: glob delegation is only supported in impls
|
||||
--> $DIR/glob-non-impl.rs:11:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: glob delegation is only supported in impls
|
||||
--> $DIR/glob-non-impl.rs:19:5
|
||||
|
|
||||
LL | reuse Trait::*;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
37
tests/ui/delegation/glob-override.rs
Normal file
37
tests/ui/delegation/glob-override.rs
Normal file
@ -0,0 +1,37 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8;
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
|
||||
struct S(u8);
|
||||
struct Z(u8);
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::* { &self.0 }
|
||||
fn bar(&self) -> u8 { 2 }
|
||||
}
|
||||
|
||||
impl Trait for Z {
|
||||
reuse Trait::* { &self.0 }
|
||||
reuse Trait::bar { &self.0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(2);
|
||||
s.foo();
|
||||
s.bar();
|
||||
|
||||
let z = Z(2);
|
||||
z.foo();
|
||||
z.bar();
|
||||
}
|
35
tests/ui/delegation/glob.rs
Normal file
35
tests/ui/delegation/glob.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8;
|
||||
fn bar(&self) -> u8;
|
||||
}
|
||||
|
||||
impl Trait for u8 {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
|
||||
struct S(u8);
|
||||
struct Z(u8);
|
||||
|
||||
impl Trait for S {
|
||||
reuse Trait::* { &self.0 }
|
||||
}
|
||||
|
||||
impl Trait for Z {
|
||||
reuse <u8 as Trait>::* { &self.0 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(2);
|
||||
s.foo();
|
||||
s.bar();
|
||||
|
||||
let z = Z(3);
|
||||
z.foo();
|
||||
z.bar();
|
||||
}
|
26
tests/ui/delegation/macro-inside-glob.rs
Normal file
26
tests/ui/delegation/macro-inside-glob.rs
Normal file
@ -0,0 +1,26 @@
|
||||
//@ check-pass
|
||||
|
||||
#![feature(fn_delegation)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
trait Trait {
|
||||
fn foo(&self) -> u8 { 0 }
|
||||
fn bar(&self) -> u8 { 1 }
|
||||
}
|
||||
|
||||
impl Trait for u8 {}
|
||||
|
||||
struct S(u8);
|
||||
|
||||
// Macro expansion works inside delegation items.
|
||||
macro_rules! u8 { () => { u8 } }
|
||||
macro_rules! self_0 { ($self:ident) => { &$self.0 } }
|
||||
impl Trait for S {
|
||||
reuse <u8!() as Trait>::* { self_0!(self) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = S(2);
|
||||
s.foo();
|
||||
s.bar();
|
||||
}
|
Loading…
Reference in New Issue
Block a user