mirror of https://github.com/rust-lang/rust.git
delegation: Implement glob delegation
This commit is contained in:
parent
7ac6c2fc68
commit
22d0b1ee18
|
@ -3158,13 +3158,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>>,
|
||||
}
|
||||
|
||||
|
@ -3291,7 +3294,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>),
|
||||
}
|
||||
|
@ -3372,7 +3375,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,10 +1183,12 @@ impl NoopVisitItemKind for ItemKind {
|
|||
ItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
vis.visit_qself(qself);
|
||||
vis.visit_path(prefix);
|
||||
for (ident, rename) in suffixes {
|
||||
vis.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
vis.visit_ident(rename);
|
||||
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 {
|
||||
|
@ -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,10 +1248,12 @@ impl NoopVisitItemKind for AssocItemKind {
|
|||
AssocItemKind::DelegationMac(box DelegationMac { qself, prefix, suffixes, body }) => {
|
||||
visitor.visit_qself(qself);
|
||||
visitor.visit_path(prefix);
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(rename);
|
||||
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 {
|
||||
|
|
|
@ -398,7 +398,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));
|
||||
}
|
||||
|
@ -411,10 +418,12 @@ impl WalkItemKind for ItemKind {
|
|||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
try_visit!(visitor.visit_path(prefix, item.id));
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
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);
|
||||
|
@ -828,7 +837,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));
|
||||
}
|
||||
|
@ -841,10 +857,12 @@ impl WalkItemKind for AssocItemKind {
|
|||
try_visit!(visitor.visit_ty(&qself.ty));
|
||||
}
|
||||
try_visit!(visitor.visit_path(prefix, item.id));
|
||||
for (ident, rename) in suffixes {
|
||||
visitor.visit_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
visitor.visit_ident(*rename);
|
||||
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,21 +620,28 @@ impl<'a> State<'a> {
|
|||
} else {
|
||||
self.print_path(path, false, 0);
|
||||
}
|
||||
if let Some(suffixes) = suffixes {
|
||||
self.word("::");
|
||||
self.word("{");
|
||||
for (i, (ident, rename)) in suffixes.iter().enumerate() {
|
||||
self.print_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
self.nbsp();
|
||||
self.word_nbsp("as");
|
||||
self.print_ident(*rename);
|
||||
}
|
||||
if i != suffixes.len() - 1 {
|
||||
self.word_space(",");
|
||||
match kind {
|
||||
DelegationKind::Single => {}
|
||||
DelegationKind::List(suffixes) => {
|
||||
self.word("::");
|
||||
self.word("{");
|
||||
for (i, (ident, rename)) in suffixes.iter().enumerate() {
|
||||
self.print_ident(*ident);
|
||||
if let Some(rename) = rename {
|
||||
self.nbsp();
|
||||
self.word_nbsp("as");
|
||||
self.print_ident(*rename);
|
||||
}
|
||||
if i != suffixes.len() - 1 {
|
||||
self.word_space(",");
|
||||
}
|
||||
}
|
||||
self.word("}");
|
||||
}
|
||||
DelegationKind::Glob => {
|
||||
self.word("::");
|
||||
self.word("*");
|
||||
}
|
||||
self.word("}");
|
||||
}
|
||||
if let Some(body) = body {
|
||||
self.nbsp();
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
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>();
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
let ns = match item.kind {
|
||||
AssocItemKind::Const(..) | AssocItemKind::Delegation(..) | AssocItemKind::Fn(..) => {
|
||||
ValueNS
|
||||
}
|
||||
AssocItemKind::Type(..) => TypeNS,
|
||||
AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
|
||||
};
|
||||
if ctxt == AssocCtxt::Trait {
|
||||
let ns = match item.kind {
|
||||
AssocItemKind::Const(..)
|
||||
| AssocItemKind::Delegation(..)
|
||||
| AssocItemKind::Fn(..) => ValueNS,
|
||||
AssocItemKind::Type(..) => TypeNS,
|
||||
AssocItemKind::MacCall(_) | AssocItemKind::DelegationMac(..) => bug!(), // handled above
|
||||
};
|
||||
|
||||
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);
|
||||
|
|
|
@ -3281,17 +3281,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
|||
}
|
||||
self.visit_path(&delegation.path, delegation.id);
|
||||
if let Some(body) = &delegation.body {
|
||||
// `PatBoundCtx` is not necessary in this context
|
||||
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
|
||||
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
|
||||
// `PatBoundCtx` is not necessary in this context
|
||||
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
|
||||
|
||||
let span = delegation.path.segments.last().unwrap().ident.span;
|
||||
self.fresh_binding(
|
||||
Ident::new(kw::SelfLower, span),
|
||||
delegation.id,
|
||||
PatternSource::FnParam,
|
||||
&mut bindings,
|
||||
);
|
||||
self.visit_block(body);
|
||||
let span = delegation.path.segments.last().unwrap().ident.span;
|
||||
this.fresh_binding(
|
||||
Ident::new(kw::SelfLower, span),
|
||||
delegation.id,
|
||||
PatternSource::FnParam,
|
||||
&mut bindings,
|
||||
);
|
||||
this.visit_block(body);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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() {}
|
|
@ -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
|
||||
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -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();
|
||||
}
|
|
@ -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() {}
|
|
@ -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`.
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -14,9 +14,9 @@ struct S(u8);
|
|||
|
||||
// Macro expansion works inside delegation items.
|
||||
macro_rules! u8 { () => { u8 } }
|
||||
macro_rules! self_0 { () => { &self.0 } }
|
||||
macro_rules! self_0 { ($self:ident) => { &$self.0 } }
|
||||
impl Trait for S {
|
||||
reuse <u8!() as Trait>::{foo, bar} { self_0!() }
|
||||
reuse <u8!() as Trait>::{foo, bar} { self_0!(self) }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
Loading…
Reference in New Issue