Skip to content

Commit 28b65d3

Browse files
bors[bot]Jonas Schievink
and
Jonas Schievink
authored
Merge #11347
11347: fix: Fix resolution of eager macro contents r=jonas-schievink a=jonas-schievink Eager macros resolve and expand any contained macro invocations before they are expanded. The logic for this was previously pretty broken: any nameres failure would be reported as a generic macro expansion error, so this didn't work correctly with the fixed-point resolution loop. This manifested as spurious errors whenever a non-legacy macro was used in an eager macro (that means *any* path with more than one segment). After an intense staring contest with the abyss, this PR fixes the basic logic, but some bugs still remain (particularly around `$crate`). As a side-effect, this PR moves `ModPath` into `hir_expand`. bors r+ Co-authored-by: Jonas Schievink <[email protected]>
2 parents 1f0c20e + 35e5c3b commit 28b65d3

File tree

12 files changed

+375
-305
lines changed

12 files changed

+375
-305
lines changed

crates/hir_def/src/attr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ impl Attr {
713713
hygiene: &Hygiene,
714714
id: AttrId,
715715
) -> Option<Attr> {
716-
let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
716+
let path = Interned::new(ModPath::from_src(db.upcast(), ast.path()?, hygiene)?);
717717
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
718718
let value = match lit.kind() {
719719
ast::LiteralKind::String(string) => string.value()?.into(),

crates/hir_def/src/find_path.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ pub fn find_path_prefixed(
3535

3636
const MAX_PATH_LEN: usize = 15;
3737

38-
impl ModPath {
38+
trait ModPathExt {
39+
fn starts_with_std(&self) -> bool;
40+
fn can_start_with_std(&self) -> bool;
41+
}
42+
43+
impl ModPathExt for ModPath {
3944
fn starts_with_std(&self) -> bool {
4045
self.segments().first() == Some(&known::std)
4146
}

crates/hir_def/src/item_tree/lower.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ impl<'a> Ctx<'a> {
502502
}
503503

504504
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
505-
let path = Interned::new(ModPath::from_src(self.db, m.path()?, self.hygiene())?);
505+
let path = Interned::new(ModPath::from_src(self.db.upcast(), m.path()?, self.hygiene())?);
506506
let ast_id = self.source_ast_id_map.ast_id(m);
507507
let expand_to = hir_expand::ExpandTo::from_call_site(m);
508508
let res = MacroCall { path, ast_id, expand_to };
@@ -769,7 +769,7 @@ impl UseTreeLowering<'_> {
769769
// E.g. `use something::{inner}` (prefix is `None`, path is `something`)
770770
// or `use something::{path::{inner::{innerer}}}` (prefix is `something::path`, path is `inner`)
771771
Some(path) => {
772-
match ModPath::from_src(self.db, path, self.hygiene) {
772+
match ModPath::from_src(self.db.upcast(), path, self.hygiene) {
773773
Some(it) => Some(it),
774774
None => return None, // FIXME: report errors somewhere
775775
}
@@ -788,7 +788,7 @@ impl UseTreeLowering<'_> {
788788
} else {
789789
let is_glob = tree.star_token().is_some();
790790
let path = match tree.path() {
791-
Some(path) => Some(ModPath::from_src(self.db, path, self.hygiene)?),
791+
Some(path) => Some(ModPath::from_src(self.db.upcast(), path, self.hygiene)?),
792792
None => None,
793793
};
794794
let alias = tree.rename().map(|a| {

crates/hir_def/src/lib.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ use hir_expand::{
6464
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
6565
hygiene::Hygiene,
6666
AstId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
67+
UnresolvedMacro,
6768
};
6869
use item_tree::ExternBlock;
6970
use la_arena::Idx;
7071
use nameres::DefMap;
71-
use path::ModPath;
7272
use stdx::impl_from;
7373
use syntax::ast;
7474

@@ -677,7 +677,8 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
677677
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
678678
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
679679
let h = Hygiene::new(db.upcast(), self.file_id);
680-
let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
680+
let path =
681+
self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
681682

682683
let path = match error_sink
683684
.option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
@@ -712,11 +713,6 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
712713
}
713714
}
714715

715-
#[derive(Debug)]
716-
pub struct UnresolvedMacro {
717-
pub path: ModPath,
718-
}
719-
720716
fn macro_call_as_call_id(
721717
call: &AstIdWithPath<ast::MacroCall>,
722718
expand_to: ExpandTo,
@@ -730,16 +726,8 @@ fn macro_call_as_call_id(
730726

731727
let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
732728
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
733-
let hygiene = Hygiene::new(db.upcast(), call.ast_id.file_id);
734729

735-
expand_eager_macro(
736-
db.upcast(),
737-
krate,
738-
macro_call,
739-
def,
740-
&|path: ast::Path| resolver(path::ModPath::from_src(db, path, &hygiene)?),
741-
error_sink,
742-
)
730+
expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
743731
} else {
744732
Ok(def.as_lazy_macro(
745733
db.upcast(),

crates/hir_def/src/nameres/tests/macros.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,3 +1046,71 @@ m!(
10461046
"#]],
10471047
)
10481048
}
1049+
1050+
#[test]
1051+
fn eager_macro_correctly_resolves_contents() {
1052+
// Eager macros resolve any contained macros when expanded. This should work correctly with the
1053+
// usual name resolution rules, so both of these `include!`s should include the right file.
1054+
1055+
check(
1056+
r#"
1057+
//- /lib.rs
1058+
#[rustc_builtin_macro]
1059+
macro_rules! include { () => {} }
1060+
1061+
include!(inner_a!());
1062+
include!(crate::inner_b!());
1063+
1064+
#[macro_export]
1065+
macro_rules! inner_a {
1066+
() => { "inc_a.rs" };
1067+
}
1068+
#[macro_export]
1069+
macro_rules! inner_b {
1070+
() => { "inc_b.rs" };
1071+
}
1072+
//- /inc_a.rs
1073+
struct A;
1074+
//- /inc_b.rs
1075+
struct B;
1076+
"#,
1077+
expect![[r#"
1078+
crate
1079+
A: t v
1080+
B: t v
1081+
inner_a: m
1082+
inner_b: m
1083+
"#]],
1084+
);
1085+
}
1086+
1087+
#[test]
1088+
fn eager_macro_correctly_resolves_dollar_crate() {
1089+
check(
1090+
r#"
1091+
//- /lib.rs
1092+
#[rustc_builtin_macro]
1093+
macro_rules! include { () => {} }
1094+
1095+
#[macro_export]
1096+
macro_rules! inner {
1097+
() => { "inc.rs" };
1098+
}
1099+
1100+
macro_rules! m {
1101+
() => { include!($crate::inner!()); };
1102+
}
1103+
1104+
m!();
1105+
1106+
//- /inc.rs
1107+
struct A;
1108+
"#,
1109+
expect![[r#"
1110+
crate
1111+
inner: m
1112+
"#]],
1113+
);
1114+
1115+
// FIXME: This currently fails. The result should contain `A: t v`.
1116+
}

crates/hir_def/src/path.rs

Lines changed: 5 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,13 @@ use std::{
66
iter,
77
};
88

9-
use crate::{body::LowerCtx, db::DefDatabase, intern::Interned, type_ref::LifetimeRef};
10-
use base_db::CrateId;
11-
use hir_expand::{
12-
hygiene::Hygiene,
13-
name::{name, Name},
14-
};
9+
use crate::{body::LowerCtx, intern::Interned, type_ref::LifetimeRef};
10+
use hir_expand::name::{name, Name};
1511
use syntax::ast;
1612

1713
use crate::type_ref::{TypeBound, TypeRef};
1814

19-
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20-
pub struct ModPath {
21-
pub kind: PathKind,
22-
segments: Vec<Name>,
23-
}
24-
25-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
26-
pub enum PathKind {
27-
Plain,
28-
/// `self::` is `Super(0)`
29-
Super(u8),
30-
Crate,
31-
/// Absolute path (::foo)
32-
Abs,
33-
/// `$crate` from macro expansion
34-
DollarCrate(CrateId),
35-
}
15+
pub use hir_expand::mod_path::{path, ModPath, PathKind};
3616

3717
#[derive(Debug, Clone, PartialEq, Eq)]
3818
pub enum ImportAlias {
@@ -51,67 +31,6 @@ impl Display for ImportAlias {
5131
}
5232
}
5333

54-
impl ModPath {
55-
pub fn from_src(db: &dyn DefDatabase, path: ast::Path, hygiene: &Hygiene) -> Option<ModPath> {
56-
lower::convert_path(db, None, path, hygiene)
57-
}
58-
59-
pub fn from_segments(kind: PathKind, segments: impl IntoIterator<Item = Name>) -> ModPath {
60-
let segments = segments.into_iter().collect::<Vec<_>>();
61-
ModPath { kind, segments }
62-
}
63-
64-
/// Creates a `ModPath` from a `PathKind`, with no extra path segments.
65-
pub const fn from_kind(kind: PathKind) -> ModPath {
66-
ModPath { kind, segments: Vec::new() }
67-
}
68-
69-
pub fn segments(&self) -> &[Name] {
70-
&self.segments
71-
}
72-
73-
pub fn push_segment(&mut self, segment: Name) {
74-
self.segments.push(segment);
75-
}
76-
77-
pub fn pop_segment(&mut self) -> Option<Name> {
78-
self.segments.pop()
79-
}
80-
81-
/// Returns the number of segments in the path (counting special segments like `$crate` and
82-
/// `super`).
83-
pub fn len(&self) -> usize {
84-
self.segments.len()
85-
+ match self.kind {
86-
PathKind::Plain => 0,
87-
PathKind::Super(i) => i as usize,
88-
PathKind::Crate => 1,
89-
PathKind::Abs => 0,
90-
PathKind::DollarCrate(_) => 1,
91-
}
92-
}
93-
94-
pub fn is_ident(&self) -> bool {
95-
self.as_ident().is_some()
96-
}
97-
98-
pub fn is_self(&self) -> bool {
99-
self.kind == PathKind::Super(0) && self.segments.is_empty()
100-
}
101-
102-
/// If this path is a single identifier, like `foo`, return its name.
103-
pub fn as_ident(&self) -> Option<&Name> {
104-
if self.kind != PathKind::Plain {
105-
return None;
106-
}
107-
108-
match &*self.segments {
109-
[name] => Some(name),
110-
_ => None,
111-
}
112-
}
113-
}
114-
11534
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11635
pub struct Path {
11736
/// Type based path like `<T>::foo`.
@@ -185,10 +104,7 @@ impl Path {
185104
}
186105

187106
pub fn segments(&self) -> PathSegments<'_> {
188-
PathSegments {
189-
segments: self.mod_path.segments.as_slice(),
190-
generic_args: &self.generic_args,
191-
}
107+
PathSegments { segments: self.mod_path.segments(), generic_args: &self.generic_args }
192108
}
193109

194110
pub fn mod_path(&self) -> &ModPath {
@@ -203,7 +119,7 @@ impl Path {
203119
type_anchor: self.type_anchor.clone(),
204120
mod_path: Interned::new(ModPath::from_segments(
205121
self.mod_path.kind.clone(),
206-
self.mod_path.segments[..self.mod_path.segments.len() - 1].iter().cloned(),
122+
self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
207123
)),
208124
generic_args: self.generic_args[..self.generic_args.len() - 1].to_vec().into(),
209125
};
@@ -296,76 +212,3 @@ impl From<Name> for Box<Path> {
296212
Box::new(Path::from(name))
297213
}
298214
}
299-
300-
impl From<Name> for ModPath {
301-
fn from(name: Name) -> ModPath {
302-
ModPath::from_segments(PathKind::Plain, iter::once(name))
303-
}
304-
}
305-
306-
impl Display for ModPath {
307-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308-
let mut first_segment = true;
309-
let mut add_segment = |s| -> fmt::Result {
310-
if !first_segment {
311-
f.write_str("::")?;
312-
}
313-
first_segment = false;
314-
f.write_str(s)?;
315-
Ok(())
316-
};
317-
match self.kind {
318-
PathKind::Plain => {}
319-
PathKind::Super(0) => add_segment("self")?,
320-
PathKind::Super(n) => {
321-
for _ in 0..n {
322-
add_segment("super")?;
323-
}
324-
}
325-
PathKind::Crate => add_segment("crate")?,
326-
PathKind::Abs => add_segment("")?,
327-
PathKind::DollarCrate(_) => add_segment("$crate")?,
328-
}
329-
for segment in &self.segments {
330-
if !first_segment {
331-
f.write_str("::")?;
332-
}
333-
first_segment = false;
334-
write!(f, "{}", segment)?;
335-
}
336-
Ok(())
337-
}
338-
}
339-
340-
pub use hir_expand::name as __name;
341-
342-
#[macro_export]
343-
macro_rules! __known_path {
344-
(core::iter::IntoIterator) => {};
345-
(core::iter::Iterator) => {};
346-
(core::result::Result) => {};
347-
(core::option::Option) => {};
348-
(core::ops::Range) => {};
349-
(core::ops::RangeFrom) => {};
350-
(core::ops::RangeFull) => {};
351-
(core::ops::RangeTo) => {};
352-
(core::ops::RangeToInclusive) => {};
353-
(core::ops::RangeInclusive) => {};
354-
(core::future::Future) => {};
355-
(core::ops::Try) => {};
356-
($path:path) => {
357-
compile_error!("Please register your known path in the path module")
358-
};
359-
}
360-
361-
#[macro_export]
362-
macro_rules! __path {
363-
($start:ident $(:: $seg:ident)*) => ({
364-
$crate::__known_path!($start $(:: $seg)*);
365-
$crate::path::ModPath::from_segments($crate::path::PathKind::Abs, vec![
366-
$crate::path::__name![$start], $($crate::path::__name![$seg],)*
367-
])
368-
});
369-
}
370-
371-
pub use crate::__path as path;

0 commit comments

Comments
 (0)