8462: Expand macros at type position r=jonas-schievink a=cynecx



Co-authored-by: cynecx <me@cynecx.net>
This commit is contained in:
bors[bot] 2021-04-19 13:01:30 +00:00 committed by GitHub
commit e4f7f1e1bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 391 additions and 76 deletions

View File

@ -6,10 +6,11 @@
use base_db::{FileId, FileRange};
use hir_def::{
body,
resolver::{self, HasResolver, Resolver, TypeNs},
AsMacroCall, FunctionId, TraitId, VariantId,
};
use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
use hir_expand::{name::AsName, ExpansionInfo};
use hir_ty::associated_type_shorthand_candidates;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
@ -853,8 +854,8 @@ pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
let hygiene = Hygiene::new(self.db.upcast(), self.file_id);
let path = Path::from_src(path.clone(), &hygiene)?;
let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
let path = Path::from_src(path.clone(), &ctx)?;
resolve_hir_path(self.db, &self.resolver, &path)
}
}

View File

@ -9,6 +9,7 @@
use hir_def::{
body::{
self,
scope::{ExprScopes, ScopeId},
Body, BodySourceMap,
},
@ -202,8 +203,8 @@ pub(crate) fn resolve_macro_call(
db: &dyn HirDatabase,
macro_call: InFile<&ast::MacroCall>,
) -> Option<MacroDef> {
let hygiene = Hygiene::new(db.upcast(), macro_call.file_id);
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &hygiene))?;
let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into())
}
@ -281,7 +282,9 @@ pub(crate) fn resolve_path(
}
// This must be a normal source file rather than macro file.
let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?;
let hygiene = Hygiene::new(db.upcast(), self.file_id);
let ctx = body::LowerCtx::with_hygiene(&hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?;
// Case where path is a qualifier of another path, e.g. foo::bar::Baz where we
// trying to resolve foo::bar.

View File

@ -21,7 +21,7 @@
use rustc_hash::FxHashMap;
use syntax::{ast, AstNode, AstPtr};
pub(crate) use lower::LowerCtx;
pub use lower::LowerCtx;
use crate::{
attr::{Attrs, RawAttrs},
@ -37,13 +37,15 @@
/// A subset of Expander that only deals with cfg attributes. We only need it to
/// avoid cyclic queries in crate def map during enum processing.
#[derive(Debug)]
pub(crate) struct CfgExpander {
cfg_options: CfgOptions,
hygiene: Hygiene,
krate: CrateId,
}
pub(crate) struct Expander {
#[derive(Debug)]
pub struct Expander {
cfg_expander: CfgExpander,
def_map: Arc<DefMap>,
current_file_id: HirFileId,
@ -80,11 +82,7 @@ pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::Attrs
}
impl Expander {
pub(crate) fn new(
db: &dyn DefDatabase,
current_file_id: HirFileId,
module: ModuleId,
) -> Expander {
pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
let def_map = module.def_map(db);
let ast_id_map = db.ast_id_map(current_file_id);
@ -98,7 +96,7 @@ pub(crate) fn new(
}
}
pub(crate) fn enter_expand<T: ast::AstNode>(
pub fn enter_expand<T: ast::AstNode>(
&mut self,
db: &dyn DefDatabase,
macro_call: ast::MacroCall,
@ -170,7 +168,7 @@ pub(crate) fn enter_expand<T: ast::AstNode>(
Ok(ExpandResult { value: Some((mark, node)), err })
}
pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
self.current_file_id = mark.file_id;
self.ast_id_map = mem::take(&mut mark.ast_id_map);
@ -190,8 +188,13 @@ pub(crate) fn cfg_options(&self) -> &CfgOptions {
&self.cfg_expander.cfg_options
}
pub fn current_file_id(&self) -> HirFileId {
self.current_file_id
}
fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
Path::from_src(path, &self.cfg_expander.hygiene)
let ctx = LowerCtx::with_hygiene(&self.cfg_expander.hygiene);
Path::from_src(path, &ctx)
}
fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
@ -204,7 +207,8 @@ fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
}
}
pub(crate) struct Mark {
#[derive(Debug)]
pub struct Mark {
file_id: HirFileId,
ast_id_map: Arc<AstIdMap>,
bomb: DropBomb,

View File

@ -1,10 +1,11 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
use std::mem;
use std::{mem, sync::Arc};
use either::Either;
use hir_expand::{
ast_id_map::{AstIdMap, FileAstId},
hygiene::Hygiene,
name::{name, AsName, Name},
ExpandError, HirFileId,
@ -39,20 +40,39 @@
use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
pub(crate) struct LowerCtx {
pub struct LowerCtx {
hygiene: Hygiene,
file_id: Option<HirFileId>,
source_ast_id_map: Option<Arc<AstIdMap>>,
}
impl LowerCtx {
pub(crate) fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
LowerCtx { hygiene: Hygiene::new(db.upcast(), file_id) }
pub fn new(db: &dyn DefDatabase, file_id: HirFileId) -> Self {
LowerCtx {
hygiene: Hygiene::new(db.upcast(), file_id),
file_id: Some(file_id),
source_ast_id_map: Some(db.ast_id_map(file_id)),
}
}
pub(crate) fn with_hygiene(hygiene: &Hygiene) -> Self {
LowerCtx { hygiene: hygiene.clone() }
pub fn with_hygiene(hygiene: &Hygiene) -> Self {
LowerCtx { hygiene: hygiene.clone(), file_id: None, source_ast_id_map: None }
}
pub(crate) fn hygiene(&self) -> &Hygiene {
&self.hygiene
}
pub(crate) fn file_id(&self) -> HirFileId {
self.file_id.unwrap()
}
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
Path::from_src(ast, &self.hygiene)
Path::from_src(ast, self)
}
pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<FileAstId<N>> {
self.source_ast_id_map.as_ref().map(|ast_id_map| ast_id_map.ast_id(item))
}
}

View File

@ -104,6 +104,11 @@ pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) ->
// items and expanded during block DefMap computation
return Default::default();
},
ast::Type(ty) => {
// Types can contain inner items. We return an empty item tree in this case, but
// still need to collect inner items.
ctx.lower_inner_items(ty.syntax())
},
ast::Expr(e) => {
// Macros can expand to expressions. We return an empty item tree in this case, but
// still need to collect inner items.

View File

@ -688,6 +688,7 @@ fn new(file_id: HirFileId, ast_id: FileAstId<T>, path: path::ModPath) -> AstIdWi
}
}
#[derive(Debug)]
pub struct UnresolvedMacro {
pub path: ModPath,
}

View File

@ -48,7 +48,8 @@ pub enum ImportAlias {
impl ModPath {
pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
lower::lower_path(path, hygiene).map(|it| (*it.mod_path).clone())
let ctx = LowerCtx::with_hygiene(hygiene);
lower::lower_path(path, &ctx).map(|it| (*it.mod_path).clone())
}
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
@ -167,8 +168,8 @@ pub enum GenericArg {
impl Path {
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// It correctly handles `$crate` based path from macro call.
pub fn from_src(path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
lower::lower_path(path, hygiene)
pub fn from_src(path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
lower::lower_path(path, ctx)
}
/// Converts a known mod path to `Path`.

View File

@ -6,10 +6,7 @@
use std::sync::Arc;
use either::Either;
use hir_expand::{
hygiene::Hygiene,
name::{name, AsName},
};
use hir_expand::name::{name, AsName};
use syntax::ast::{self, AstNode, TypeBoundsOwner};
use super::AssociatedTypeBinding;
@ -23,12 +20,12 @@
/// Converts an `ast::Path` to `Path`. Works with use trees.
/// It correctly handles `$crate` based path from macro call.
pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path> {
pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut type_anchor = None;
let mut segments = Vec::new();
let mut generic_args = Vec::new();
let ctx = LowerCtx::with_hygiene(hygiene);
let hygiene = ctx.hygiene();
loop {
let segment = path.segment()?;
@ -43,10 +40,10 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
Either::Left(name) => {
let args = segment
.generic_arg_list()
.and_then(|it| lower_generic_args(&ctx, it))
.and_then(|it| lower_generic_args(ctx, it))
.or_else(|| {
lower_generic_args_from_fn_path(
&ctx,
ctx,
segment.param_list(),
segment.ret_type(),
)
@ -64,7 +61,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
ast::PathSegmentKind::Type { type_ref, trait_ref } => {
assert!(path.qualifier().is_none()); // this can only occur at the first segment
let self_type = TypeRef::from_ast(&ctx, type_ref?);
let self_type = TypeRef::from_ast(ctx, type_ref?);
match trait_ref {
// <T>::foo
@ -74,7 +71,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
let path = Path::from_src(trait_ref.path()?, hygiene)?;
let path = Path::from_src(trait_ref.path()?, ctx)?;
let mod_path = (*path.mod_path).clone();
let num_segments = path.mod_path.segments.len();
kind = mod_path.kind;

View File

@ -1,6 +1,7 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
use hir_expand::name::Name;
use hir_expand::{name::Name, AstId, InFile};
use syntax::ast;
use crate::{body::LowerCtx, path::Path};
@ -68,6 +69,7 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Option<Self> {
}
}
}
/// Compare ty::Ty
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub enum TypeRef {
@ -84,6 +86,7 @@ pub enum TypeRef {
// For
ImplTrait(Vec<TypeBound>),
DynTrait(Vec<TypeBound>),
Macro(AstId<ast::MacroCall>),
Error,
}
@ -116,7 +119,7 @@ pub enum TypeBound {
impl TypeRef {
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
pub fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
match node {
ast::Type::ParenType(inner) => TypeRef::from_ast_opt(&ctx, inner.ty()),
ast::Type::TupleType(inner) => {
@ -176,8 +179,13 @@ pub(crate) fn from_ast(ctx: &LowerCtx, node: ast::Type) -> Self {
ast::Type::DynTraitType(inner) => {
TypeRef::DynTrait(type_bounds_from_ast(ctx, inner.type_bound_list()))
}
// FIXME: Macros in type position are not yet supported.
ast::Type::MacroType(_) => TypeRef::Error,
ast::Type::MacroType(mt) => match mt.macro_call() {
Some(mc) => ctx
.ast_id(&mc)
.map(|mc| TypeRef::Macro(InFile::new(ctx.file_id(), mc)))
.unwrap_or(TypeRef::Error),
None => TypeRef::Error,
},
}
}
@ -215,7 +223,7 @@ fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) {
}
}
TypeRef::Path(path) => go_path(path, f),
TypeRef::Never | TypeRef::Placeholder | TypeRef::Error => {}
TypeRef::Never | TypeRef::Placeholder | TypeRef::Macro(_) | TypeRef::Error => {}
};
}

View File

@ -440,6 +440,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
MACRO_STMTS => FragmentKind::Statements,
MACRO_PAT => FragmentKind::Pattern,
MACRO_TYPE => FragmentKind::Type,
ITEM_LIST => FragmentKind::Items,
LET_STMT => {
// FIXME: Handle LHS Pattern

View File

@ -31,6 +31,7 @@
use std::sync::Arc;
use syntax::{algo::SyntaxRewriter, SyntaxNode};
#[derive(Debug)]
pub struct ErrorEmitted {
_private: (),
}

View File

@ -9,6 +9,7 @@
use chalk_ir::BoundVar;
use hir_def::{
body,
db::DefDatabase,
find_path,
generics::TypeParamProvenance,
@ -18,7 +19,7 @@
visibility::Visibility,
AssocContainerId, Lookup, ModuleId, TraitId,
};
use hir_expand::name::Name;
use hir_expand::{hygiene::Hygiene, name::Name};
use crate::{
const_from_placeholder_idx, db::HirDatabase, from_assoc_type_id, from_foreign_def_id,
@ -997,6 +998,18 @@ fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
write!(f, "dyn ")?;
f.write_joined(bounds, " + ")?;
}
TypeRef::Macro(macro_call) => {
let macro_call = macro_call.to_node(f.db.upcast());
let ctx = body::LowerCtx::with_hygiene(&Hygiene::new_unhygienic());
match macro_call.path() {
Some(path) => match Path::from_src(path, &ctx) {
Some(path) => path.hir_fmt(f)?,
None => write!(f, "{{macro}}")?,
},
None => write!(f, "{{macro}}")?,
}
write!(f, "!(..)")?;
}
TypeRef::Error => write!(f, "{{error}}")?,
}
Ok(())

View File

@ -5,12 +5,14 @@
//! - Building the type for an item: This happens through the `type_for_def` query.
//!
//! This usually involves resolving names, collecting generic arguments etc.
use std::cell::{Cell, RefCell};
use std::{iter, sync::Arc};
use base_db::CrateId;
use chalk_ir::{cast::Cast, fold::Shift, interner::HasInterner, Mutability, Safety};
use hir_def::{
adt::StructKind,
body::{Expander, LowerCtx},
builtin_type::BuiltinType,
generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
path::{GenericArg, Path, PathSegment, PathSegments},
@ -20,10 +22,11 @@
GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
TypeAliasId, TypeParamId, UnionId, VariantId,
};
use hir_expand::name::Name;
use hir_expand::{name::Name, ExpandResult};
use la_arena::ArenaMap;
use smallvec::SmallVec;
use stdx::impl_from;
use syntax::ast;
use crate::{
db::HirDatabase,
@ -50,7 +53,7 @@ pub struct TyLoweringContext<'a> {
/// possible currently, so this should be fine for now.
pub type_param_mode: TypeParamLoweringMode,
pub impl_trait_mode: ImplTraitLoweringMode,
impl_trait_counter: std::cell::Cell<u16>,
impl_trait_counter: Cell<u16>,
/// When turning `impl Trait` into opaque types, we have to collect the
/// bounds at the same time to get the IDs correct (without becoming too
/// complicated). I don't like using interior mutability (as for the
@ -59,16 +62,17 @@ pub struct TyLoweringContext<'a> {
/// we're grouping the mutable data (the counter and this field) together
/// with the immutable context (the references to the DB and resolver).
/// Splitting this up would be a possible fix.
opaque_type_data: std::cell::RefCell<Vec<ReturnTypeImplTrait>>,
opaque_type_data: RefCell<Vec<ReturnTypeImplTrait>>,
expander: RefCell<Option<Expander>>,
}
impl<'a> TyLoweringContext<'a> {
pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
let impl_trait_counter = std::cell::Cell::new(0);
let impl_trait_counter = Cell::new(0);
let impl_trait_mode = ImplTraitLoweringMode::Disallowed;
let type_param_mode = TypeParamLoweringMode::Placeholder;
let in_binders = DebruijnIndex::INNERMOST;
let opaque_type_data = std::cell::RefCell::new(Vec::new());
let opaque_type_data = RefCell::new(Vec::new());
Self {
db,
resolver,
@ -77,6 +81,7 @@ pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
impl_trait_counter,
type_param_mode,
opaque_type_data,
expander: RefCell::new(None),
}
}
@ -86,15 +91,18 @@ pub fn with_debruijn<T>(
f: impl FnOnce(&TyLoweringContext) -> T,
) -> T {
let opaque_ty_data_vec = self.opaque_type_data.replace(Vec::new());
let expander = self.expander.replace(None);
let new_ctx = Self {
in_binders: debruijn,
impl_trait_counter: std::cell::Cell::new(self.impl_trait_counter.get()),
opaque_type_data: std::cell::RefCell::new(opaque_ty_data_vec),
impl_trait_counter: Cell::new(self.impl_trait_counter.get()),
opaque_type_data: RefCell::new(opaque_ty_data_vec),
expander: RefCell::new(expander),
..*self
};
let result = f(&new_ctx);
self.impl_trait_counter.set(new_ctx.impl_trait_counter.get());
self.opaque_type_data.replace(new_ctx.opaque_type_data.into_inner());
self.expander.replace(new_ctx.expander.into_inner());
result
}
@ -287,6 +295,53 @@ pub fn lower_ty_ext(&self, type_ref: &TypeRef) -> (Ty, Option<TypeNs>) {
}
}
}
TypeRef::Macro(macro_call) => {
let (expander, recursion_start) = {
let mut expander = self.expander.borrow_mut();
if expander.is_some() {
(Some(expander), false)
} else {
if let Some(module_id) = self.resolver.module() {
*expander = Some(Expander::new(
self.db.upcast(),
macro_call.file_id,
module_id,
));
(Some(expander), true)
} else {
(None, false)
}
}
};
let ty = if let Some(mut expander) = expander {
let expander_mut = expander.as_mut().unwrap();
let macro_call = macro_call.to_node(self.db.upcast());
match expander_mut.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
let ctx =
LowerCtx::new(self.db.upcast(), expander_mut.current_file_id());
let type_ref = TypeRef::from_ast(&ctx, expanded);
drop(expander);
let ty = self.lower_ty(&type_ref);
self.expander
.borrow_mut()
.as_mut()
.unwrap()
.exit(self.db.upcast(), mark);
Some(ty)
}
_ => None,
}
} else {
None
};
if recursion_start {
*self.expander.borrow_mut() = None;
}
ty.unwrap_or_else(|| TyKind::Error.intern(&Interner))
}
TypeRef::Error => TyKind::Error.intern(&Interner),
};
(ty, res)

View File

@ -1074,3 +1074,202 @@ fn main() {
"#]],
);
}
#[test]
fn macro_in_type_alias_position() {
check_infer(
r#"
macro_rules! U32 {
() => { u32 };
}
trait Foo {
type Ty;
}
impl<T> Foo for T {
type Ty = U32!();
}
type TayTo = U32!();
fn testy() {
let a: <() as Foo>::Ty;
let b: TayTo;
}
"#,
expect![[r#"
147..196 '{ ...yTo; }': ()
157..158 'a': u32
185..186 'b': u32
"#]],
);
}
#[test]
fn nested_macro_in_type_alias_position() {
check_infer(
r#"
macro_rules! U32Inner2 {
() => { u32 };
}
macro_rules! U32Inner1 {
() => { U32Inner2!() };
}
macro_rules! U32 {
() => { U32Inner1!() };
}
trait Foo {
type Ty;
}
impl<T> Foo for T {
type Ty = U32!();
}
type TayTo = U32!();
fn testy() {
let a: <() as Foo>::Ty;
let b: TayTo;
}
"#,
expect![[r#"
259..308 '{ ...yTo; }': ()
269..270 'a': u32
297..298 'b': u32
"#]],
);
}
#[test]
fn macros_in_type_alias_position_generics() {
check_infer(
r#"
struct Foo<A, B>(A, B);
macro_rules! U32 {
() => { u32 };
}
macro_rules! Bar {
() => { Foo<U32!(), U32!()> };
}
trait Moo {
type Ty;
}
impl<T> Moo for T {
type Ty = Bar!();
}
type TayTo = Bar!();
fn main() {
let a: <() as Moo>::Ty;
let b: TayTo;
}
"#,
expect![[r#"
228..277 '{ ...yTo; }': ()
238..239 'a': Foo<u32, u32>
266..267 'b': Foo<u32, u32>
"#]],
);
}
#[test]
fn macros_in_type_position() {
check_infer(
r#"
struct Foo<A, B>(A, B);
macro_rules! U32 {
() => { u32 };
}
macro_rules! Bar {
() => { Foo<U32!(), U32!()> };
}
fn main() {
let a: Bar!();
}
"#,
expect![[r#"
133..155 '{ ...!(); }': ()
143..144 'a': Foo<u32, u32>
"#]],
);
}
#[test]
fn macros_in_type_generics() {
check_infer(
r#"
struct Foo<A, B>(A, B);
macro_rules! U32 {
() => { u32 };
}
macro_rules! Bar {
() => { Foo<U32!(), U32!()> };
}
trait Moo {
type Ty;
}
impl<T> Moo for T {
type Ty = Foo<Bar!(), Bar!()>;
}
type TayTo = Foo<Bar!(), U32!()>;
fn main() {
let a: <() as Moo>::Ty;
let b: TayTo;
}
"#,
expect![[r#"
254..303 '{ ...yTo; }': ()
264..265 'a': Foo<Foo<u32, u32>, Foo<u32, u32>>
292..293 'b': Foo<Foo<u32, u32>, u32>
"#]],
);
}
#[test]
fn infinitely_recursive_macro_type() {
check_infer(
r#"
struct Bar<T, X>(T, X);
macro_rules! Foo {
() => { Foo!() }
}
macro_rules! U32 {
() => { u32 }
}
type A = Foo!();
type B = Bar<Foo!(), U32!()>;
fn main() {
let a: A;
let b: B;
}
"#,
expect![[r#"
166..197 '{ ...: B; }': ()
176..177 'a': {unknown}
190..191 'b': Bar<{unknown}, u32>
"#]],
);
}

View File

@ -283,17 +283,21 @@ pub(super) fn path_type(p: &mut Parser) {
// type B = crate::foo!();
fn path_or_macro_type_(p: &mut Parser, allow_bounds: bool) {
assert!(paths::is_path_start(p));
let r = p.start();
let m = p.start();
paths::type_path(p);
let kind = if p.at(T![!]) && !p.at(T![!=]) {
items::macro_call_after_excl(p);
MACRO_CALL
m.complete(p, MACRO_CALL);
MACRO_TYPE
} else {
m.abandon(p);
PATH_TYPE
};
let path = m.complete(p, kind);
let path = r.complete(p, kind);
if allow_bounds {
opt_type_bounds_as_dyn_trait_type(p, path);
@ -319,7 +323,7 @@ pub(super) fn path_type_(p: &mut Parser, allow_bounds: bool) {
fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser, type_marker: CompletedMarker) {
assert!(matches!(
type_marker.kind(),
SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_CALL
SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE
));
if !p.at(T![+]) {
return;

View File

@ -7,15 +7,16 @@ SOURCE_FILE@0..41
WHITESPACE@6..7 " "
EQ@7..8 "="
WHITESPACE@8..9 " "
MACRO_CALL@9..15
PATH@9..12
PATH_SEGMENT@9..12
NAME_REF@9..12
IDENT@9..12 "foo"
BANG@12..13 "!"
TOKEN_TREE@13..15
L_PAREN@13..14 "("
R_PAREN@14..15 ")"
MACRO_TYPE@9..15
MACRO_CALL@9..15
PATH@9..12
PATH_SEGMENT@9..12
NAME_REF@9..12
IDENT@9..12 "foo"
BANG@12..13 "!"
TOKEN_TREE@13..15
L_PAREN@13..14 "("
R_PAREN@14..15 ")"
SEMICOLON@15..16 ";"
WHITESPACE@16..17 "\n"
TYPE_ALIAS@17..40
@ -26,19 +27,20 @@ SOURCE_FILE@0..41
WHITESPACE@23..24 " "
EQ@24..25 "="
WHITESPACE@25..26 " "
MACRO_CALL@26..39
PATH@26..36
PATH@26..31
PATH_SEGMENT@26..31
NAME_REF@26..31
CRATE_KW@26..31 "crate"
COLON2@31..33 "::"
PATH_SEGMENT@33..36
NAME_REF@33..36
IDENT@33..36 "foo"
BANG@36..37 "!"
TOKEN_TREE@37..39
L_PAREN@37..38 "("
R_PAREN@38..39 ")"
MACRO_TYPE@26..39
MACRO_CALL@26..39
PATH@26..36
PATH@26..31
PATH_SEGMENT@26..31
NAME_REF@26..31
CRATE_KW@26..31 "crate"
COLON2@31..33 "::"
PATH_SEGMENT@33..36
NAME_REF@33..36
IDENT@33..36 "foo"
BANG@36..37 "!"
TOKEN_TREE@37..39
L_PAREN@37..38 "("
R_PAREN@38..39 ")"
SEMICOLON@39..40 ";"
WHITESPACE@40..41 "\n"