about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/expander.rs10
-rw-r--r--crates/hir-def/src/lib.rs6
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs10
-rw-r--r--crates/hir-expand/src/builtin_derive_macro.rs8
-rw-r--r--crates/hir-expand/src/builtin_fn_macro.rs140
-rw-r--r--crates/hir-expand/src/db.rs108
-rw-r--r--crates/hir-expand/src/eager.rs84
-rw-r--r--crates/hir-expand/src/lib.rs72
-rw-r--r--crates/hir-expand/src/proc_macro.rs11
-rw-r--r--crates/ide-db/src/test_data/test_doc_alias.txt42
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt134
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_strings.html3
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs3
13 files changed, 355 insertions, 276 deletions
diff --git a/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs
index 34ed1e72f20..31653916f41 100644
--- a/crates/hir-def/src/expander.rs
+++ b/crates/hir-def/src/expander.rs
@@ -113,10 +113,10 @@ impl Expander {
         call_id: MacroCallId,
         error: Option<ExpandError>,
     ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
-        let file_id = call_id.as_file();
-        let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id);
+        let macro_file = call_id.as_macro_file();
+        let ExpandResult { value, err } = db.parse_macro_expansion(macro_file);
 
-        ExpandResult { value: Some(InFile::new(file_id, value)), err: error.or(err) }
+        ExpandResult { value: Some(InFile::new(macro_file.into(), value.0)), err: error.or(err) }
     }
 
     pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
@@ -179,8 +179,8 @@ impl Expander {
         } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
             self.recursion_depth = u32::MAX;
             cov_mark::hit!(your_stack_belongs_to_me);
-            return ExpandResult::only_err(ExpandError::Other(
-                "reached recursion limit during macro expansion".into(),
+            return ExpandResult::only_err(ExpandError::other(
+                "reached recursion limit during macro expansion",
             ));
         }
 
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 9cd3dfd6f7c..02593609e73 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -71,7 +71,7 @@ use hir_expand::{
     builtin_derive_macro::BuiltinDeriveExpander,
     builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
-    eager::expand_eager_macro,
+    eager::expand_eager_macro_input,
     hygiene::Hygiene,
     proc_macro::ProcMacroExpander,
     AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
@@ -865,7 +865,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
         let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
 
         let Some(path) = path else {
-            return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
+            return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
         };
 
         macro_call_as_call_id_(
@@ -913,7 +913,7 @@ fn macro_call_as_call_id_(
 
     let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
         let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
-        expand_eager_macro(db, krate, macro_call, def, &resolver)?
+        expand_eager_macro_input(db, krate, macro_call, def, &resolver)?
     } else {
         ExpandResult {
             value: Some(def.as_lazy_macro(
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 541b0ad7066..07d9baa5897 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -79,7 +79,7 @@ fn main() { env!("TEST_ENV_VAR"); }
 #[rustc_builtin_macro]
 macro_rules! env {() => {}}
 
-fn main() { "__RA_UNIMPLEMENTED__"; }
+fn main() { "UNRESOLVED_ENV_VAR"; }
 "##]],
     );
 }
@@ -442,10 +442,6 @@ macro_rules! surprise {
     () => { "s" };
 }
 
-macro_rules! stuff {
-    ($string:expr) => { concat!($string) };
-}
-
 fn main() { concat!(surprise!()); }
 "##,
         expect![[r##"
@@ -456,10 +452,6 @@ macro_rules! surprise {
     () => { "s" };
 }
 
-macro_rules! stuff {
-    ($string:expr) => { concat!($string) };
-}
-
 fn main() { "s"; }
 "##]],
     );
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index da34f50660a..976cd6a4dde 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -194,15 +194,15 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
     let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
     let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
         debug!("derive node didn't parse");
-        ExpandError::Other("invalid item definition".into())
+        ExpandError::other("invalid item definition")
     })?;
     let item = macro_items.items().next().ok_or_else(|| {
         debug!("no module item parsed");
-        ExpandError::Other("no item found".into())
+        ExpandError::other("no item found")
     })?;
     let adt = ast::Adt::cast(item.syntax().clone()).ok_or_else(|| {
         debug!("expected adt, found: {:?}", item);
-        ExpandError::Other("expected struct, enum or union".into())
+        ExpandError::other("expected struct, enum or union")
     })?;
     let (name, generic_param_list, shape) = match &adt {
         ast::Adt::Struct(it) => (
@@ -305,7 +305,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
 fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
     let name = name.ok_or_else(|| {
         debug!("parsed item has no name");
-        ExpandError::Other("missing name".into())
+        ExpandError::other("missing name")
     })?;
     let name_token_id =
         token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 5f47fd90c5c..a9f0c154b02 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -14,7 +14,8 @@ use syntax::{
 };
 
 use crate::{
-    db::ExpandDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc,
+    db::ExpandDatabase, name, quote, tt, EagerCallInfo, ExpandError, ExpandResult, MacroCallId,
+    MacroCallLoc,
 };
 
 macro_rules! register_builtin {
@@ -49,7 +50,7 @@ macro_rules! register_builtin {
                 db: &dyn ExpandDatabase,
                 arg_id: MacroCallId,
                 tt: &tt::Subtree,
-            ) -> ExpandResult<ExpandedEager> {
+            ) -> ExpandResult<tt::Subtree> {
                 let expander = match *self {
                     $( EagerExpander::$e_kind => $e_expand, )*
                 };
@@ -67,16 +68,9 @@ macro_rules! register_builtin {
     };
 }
 
-#[derive(Debug)]
-pub struct ExpandedEager {
-    pub(crate) subtree: tt::Subtree,
-    /// The included file ID of the include macro.
-    pub(crate) included_file: Option<(FileId, TokenMap)>,
-}
-
-impl ExpandedEager {
-    fn new(subtree: tt::Subtree) -> Self {
-        ExpandedEager { subtree, included_file: None }
+impl EagerExpander {
+    pub fn is_include(&self) -> bool {
+        matches!(self, EagerExpander::Include)
     }
 }
 
@@ -237,18 +231,16 @@ fn format_args_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     format_args_expand_general(db, id, tt, "")
-        .map(|x| ExpandedEager { subtree: x, included_file: None })
 }
 
 fn format_args_nl_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     format_args_expand_general(db, id, tt, "\\n")
-        .map(|x| ExpandedEager { subtree: x, included_file: None })
 }
 
 fn format_args_expand_general(
@@ -509,23 +501,23 @@ fn compile_error_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let err = match &*tt.token_trees {
         [tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
-            Some(unquoted) => ExpandError::Other(unquoted.into()),
-            None => ExpandError::Other("`compile_error!` argument must be a string".into()),
+            Some(unquoted) => ExpandError::other(unquoted),
+            None => ExpandError::other("`compile_error!` argument must be a string"),
         },
-        _ => ExpandError::Other("`compile_error!` argument must be a string".into()),
+        _ => ExpandError::other("`compile_error!` argument must be a string"),
     };
 
-    ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) }
+    ExpandResult { value: quote! {}, err: Some(err) }
 }
 
 fn concat_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let mut err = None;
     let mut text = String::new();
     for (i, mut t) in tt.token_trees.iter().enumerate() {
@@ -564,14 +556,14 @@ fn concat_expand(
             }
         }
     }
-    ExpandResult { value: ExpandedEager::new(quote!(#text)), err }
+    ExpandResult { value: quote!(#text), err }
 }
 
 fn concat_bytes_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let mut bytes = Vec::new();
     let mut err = None;
     for (i, t) in tt.token_trees.iter().enumerate() {
@@ -604,7 +596,7 @@ fn concat_bytes_expand(
         }
     }
     let ident = tt::Ident { text: bytes.join(", ").into(), span: tt::TokenId::unspecified() };
-    ExpandResult { value: ExpandedEager::new(quote!([#ident])), err }
+    ExpandResult { value: quote!([#ident]), err }
 }
 
 fn concat_bytes_expand_subtree(
@@ -637,7 +629,7 @@ fn concat_idents_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let mut err = None;
     let mut ident = String::new();
     for (i, t) in tt.token_trees.iter().enumerate() {
@@ -652,7 +644,7 @@ fn concat_idents_expand(
         }
     }
     let ident = tt::Ident { text: ident.into(), span: tt::TokenId::unspecified() };
-    ExpandResult { value: ExpandedEager::new(quote!(#ident)), err }
+    ExpandResult { value: quote!(#ident), err }
 }
 
 fn relative_file(
@@ -665,10 +657,10 @@ fn relative_file(
     let path = AnchoredPath { anchor: call_site, path: path_str };
     let res = db
         .resolve_path(path)
-        .ok_or_else(|| ExpandError::Other(format!("failed to load file `{path_str}`").into()))?;
+        .ok_or_else(|| ExpandError::other(format!("failed to load file `{path_str}`")))?;
     // Prevent include itself
     if res == call_site && !allow_recursion {
-        Err(ExpandError::Other(format!("recursive inclusion of `{path_str}`").into()))
+        Err(ExpandError::other(format!("recursive inclusion of `{path_str}`")))
     } else {
         Ok(res)
     }
@@ -687,38 +679,37 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
 fn include_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
-    tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
-    let res = (|| {
-        let path = parse_string(tt)?;
-        let file_id = relative_file(db, arg_id, &path, false)?;
-
-        let (subtree, map) =
-            parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
-        Ok((subtree, map, file_id))
-    })();
-
-    match res {
-        Ok((subtree, map, file_id)) => {
-            ExpandResult::ok(ExpandedEager { subtree, included_file: Some((file_id, map)) })
-        }
-        Err(e) => ExpandResult::new(
-            ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-            e,
-        ),
+    _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+    match db.include_expand(arg_id) {
+        Ok((res, _)) => ExpandResult::ok(res.0.clone()),
+        Err(e) => ExpandResult::new(tt::Subtree::empty(), e),
     }
 }
 
+pub(crate) fn include_arg_to_tt(
+    db: &dyn ExpandDatabase,
+    arg_id: MacroCallId,
+) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> {
+    let loc = db.lookup_intern_macro_call(arg_id);
+    let Some(EagerCallInfo {arg, arg_id: Some(arg_id), .. }) = loc.eager.as_deref() else {
+        panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager);
+    };
+    let path = parse_string(&arg.0)?;
+    let file_id = relative_file(db, *arg_id, &path, false)?;
+
+    let (subtree, map) =
+        parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
+    Ok((triomphe::Arc::new((subtree, map)), file_id))
+}
+
 fn include_bytes_expand(
     _db: &dyn ExpandDatabase,
     _arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     if let Err(e) = parse_string(tt) {
-        return ExpandResult::new(
-            ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-            e,
-        );
+        return ExpandResult::new(tt::Subtree::empty(), e);
     }
 
     // FIXME: actually read the file here if the user asked for macro expansion
@@ -729,22 +720,17 @@ fn include_bytes_expand(
             span: tt::TokenId::unspecified(),
         }))],
     };
-    ExpandResult::ok(ExpandedEager::new(res))
+    ExpandResult::ok(res)
 }
 
 fn include_str_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let path = match parse_string(tt) {
         Ok(it) => it,
-        Err(e) => {
-            return ExpandResult::new(
-                ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-                e,
-            )
-        }
+        Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
 
     // FIXME: we're not able to read excluded files (which is most of them because
@@ -754,14 +740,14 @@ fn include_str_expand(
     let file_id = match relative_file(db, arg_id, &path, true) {
         Ok(file_id) => file_id,
         Err(_) => {
-            return ExpandResult::ok(ExpandedEager::new(quote!("")));
+            return ExpandResult::ok(quote!(""));
         }
     };
 
     let text = db.file_text(file_id);
     let text = &*text;
 
-    ExpandResult::ok(ExpandedEager::new(quote!(#text)))
+    ExpandResult::ok(quote!(#text))
 }
 
 fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
@@ -773,15 +759,10 @@ fn env_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let key = match parse_string(tt) {
         Ok(it) => it,
-        Err(e) => {
-            return ExpandResult::new(
-                ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-                e,
-            )
-        }
+        Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
 
     let mut err = None;
@@ -789,35 +770,28 @@ fn env_expand(
         // The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
         // unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
         if key == "OUT_DIR" {
-            err = Some(ExpandError::Other(
-                r#"`OUT_DIR` not set, enable "build scripts" to fix"#.into(),
-            ));
+            err = Some(ExpandError::other(r#"`OUT_DIR` not set, enable "build scripts" to fix"#));
         }
 
         // If the variable is unset, still return a dummy string to help type inference along.
         // We cannot use an empty string here, because for
         // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
         // `include!("foo.rs"), which might go to infinite loop
-        "__RA_UNIMPLEMENTED__".to_string()
+        "UNRESOLVED_ENV_VAR".to_string()
     });
     let expanded = quote! { #s };
 
-    ExpandResult { value: ExpandedEager::new(expanded), err }
+    ExpandResult { value: expanded, err }
 }
 
 fn option_env_expand(
     db: &dyn ExpandDatabase,
     arg_id: MacroCallId,
     tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
     let key = match parse_string(tt) {
         Ok(it) => it,
-        Err(e) => {
-            return ExpandResult::new(
-                ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
-                e,
-            )
-        }
+        Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
     };
     // FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
     let expanded = match get_env_inner(db, arg_id, &key) {
@@ -825,5 +799,5 @@ fn option_env_expand(
         Some(s) => quote! { ::core::option::Option::Some(#s) },
     };
 
-    ExpandResult::ok(ExpandedEager::new(expanded))
+    ExpandResult::ok(expanded)
 }
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 965dfa824d8..78b2db7306b 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -14,9 +14,9 @@ use triomphe::Arc;
 use crate::{
     ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
     builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
-    BuiltinDeriveExpander, BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId,
-    HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
-    ProcMacroExpander,
+    BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult,
+    ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
+    MacroDefKind, MacroFile, ProcMacroExpander,
 };
 
 /// Total limit on the number of tokens produced by any macro invocation.
@@ -53,9 +53,7 @@ impl TokenExpander {
         match self {
             TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
             TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
-            TokenExpander::BuiltinEager(it) => {
-                it.expand(db, id, tt).map_err(Into::into).map(|res| res.subtree)
-            }
+            TokenExpander::BuiltinEager(it) => it.expand(db, id, tt).map_err(Into::into),
             TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
             TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
             TokenExpander::ProcMacro(_) => {
@@ -132,6 +130,14 @@ pub trait ExpandDatabase: SourceDatabase {
     /// Expand macro call to a token tree.
     // This query is LRU cached
     fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
+    #[salsa::invoke(crate::builtin_fn_macro::include_arg_to_tt)]
+    fn include_expand(
+        &self,
+        arg_id: MacroCallId,
+    ) -> Result<
+        (triomphe::Arc<(::tt::Subtree<::tt::TokenId>, mbe::TokenMap)>, base_db::FileId),
+        ExpandError,
+    >;
     /// Special case of the previous query for procedural macros. We can't LRU
     /// proc macros, since they are not deterministic in general, and
     /// non-determinism breaks salsa in a very, very, very bad way.
@@ -281,31 +287,6 @@ fn parse_macro_expansion(
     let _p = profile::span("parse_macro_expansion");
     let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
 
-    if let Some(err) = &err {
-        if tracing::enabled!(tracing::Level::DEBUG) {
-            // Note:
-            // The final goal we would like to make all parse_macro success,
-            // such that the following log will not call anyway.
-            let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
-            let node = loc.to_node(db);
-
-            // collect parent information for warning log
-            let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
-                it.file_id.call_node(db)
-            })
-            .map(|n| format!("{:#}", n.value))
-            .collect::<Vec<_>>()
-            .join("\n");
-
-            tracing::debug!(
-                "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
-                err,
-                node.value,
-                parents
-            );
-        }
-    }
-
     let expand_to = macro_expand_to(db, macro_file.macro_call_id);
 
     tracing::debug!("expanded = {}", tt.as_debug_string());
@@ -320,9 +301,14 @@ fn macro_arg(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
 ) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
-    let arg = db.macro_arg_text(id)?;
     let loc = db.lookup_intern_macro_call(id);
 
+    if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() {
+        return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())));
+    }
+
+    let arg = db.macro_arg_text(id)?;
+
     let node = SyntaxNode::new_root(arg);
     let censor = censor_for_macro_input(&loc, &node);
     let mut fixups = fixup::fixup_syntax(&node);
@@ -398,7 +384,17 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode>
             return None;
         }
     }
-    Some(arg.green().into())
+    if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() {
+        Some(
+            mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr)
+                .0
+                .syntax_node()
+                .green()
+                .into(),
+        )
+    } else {
+        Some(arg.green().into())
+    }
 }
 
 fn macro_def(
@@ -445,23 +441,21 @@ fn macro_def(
 fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
     let _p = profile::span("macro_expand");
     let loc = db.lookup_intern_macro_call(id);
-    if let Some(eager) = &loc.eager {
-        return ExpandResult { value: eager.arg_or_expansion.clone(), err: eager.error.clone() };
+    if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() {
+        // This is an input expansion for an eager macro. These are already pre-expanded
+        return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() };
     }
-
     let expander = match db.macro_def(loc.def) {
         Ok(it) => it,
-        // FIXME: This is weird -- we effectively report macro *definition*
-        // errors lazily, when we try to expand the macro. Instead, they should
-        // be reported at the definition site when we construct a def map.
-        // (Note we do report them also at the definition site in the late diagnostic pass)
+        // FIXME: We should make sure to enforce a variant that invalid macro
+        // definitions do not get expanders that could reach this call path!
         Err(err) => {
             return ExpandResult {
                 value: Arc::new(tt::Subtree {
                     delimiter: tt::Delimiter::UNSPECIFIED,
                     token_trees: vec![],
                 }),
-                err: Some(ExpandError::Other(format!("invalid macro definition: {err}").into())),
+                err: Some(ExpandError::other(format!("invalid macro definition: {err}"))),
             }
         }
     };
@@ -473,13 +467,21 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
                     token_trees: Vec::new(),
                 },
             ),
-            err: Some(ExpandError::Other(
+            // FIXME: We should make sure to enforce a variant that invalid macro
+            // calls do not reach this call path!
+            err: Some(ExpandError::other(
                 "invalid token tree"
-                .into(),
             )),
         };
     };
-    let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
+    let (arg_tt, arg_tm, undo_info) = &*macro_arg;
+    let ExpandResult { value: mut tt, mut err } = expander.expand(db, id, arg_tt);
+
+    if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() {
+        // FIXME: We should report both errors!
+        err = error.clone().or(err);
+    }
+
     // Set a hard limit for the expanded tt
     let count = tt.count();
     if TOKEN_LIMIT.check(count).is_err() {
@@ -488,18 +490,15 @@ fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt
                 delimiter: tt::Delimiter::UNSPECIFIED,
                 token_trees: vec![],
             }),
-            err: Some(ExpandError::Other(
-                format!(
-                    "macro invocation exceeds token limit: produced {} tokens, limit is {}",
-                    count,
-                    TOKEN_LIMIT.inner(),
-                )
-                .into(),
-            )),
+            err: Some(ExpandError::other(format!(
+                "macro invocation exceeds token limit: produced {} tokens, limit is {}",
+                count,
+                TOKEN_LIMIT.inner(),
+            ))),
         };
     }
 
-    fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
+    fixup::reverse_fixups(&mut tt, arg_tm, undo_info);
 
     ExpandResult { value: Arc::new(tt), err }
 }
@@ -520,9 +519,8 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<t
                 delimiter: tt::Delimiter::UNSPECIFIED,
                 token_trees: Vec::new(),
             },
-            err: Some(ExpandError::Other(
+            err: Some(ExpandError::other(
                 "invalid token tree"
-                .into(),
             )),
         };
     };
diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs
index 59a92ff0ab6..7ee3fd375f6 100644
--- a/crates/hir-expand/src/eager.rs
+++ b/crates/hir-expand/src/eager.rs
@@ -31,22 +31,24 @@ use crate::{
     MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
 };
 
-pub fn expand_eager_macro(
+pub fn expand_eager_macro_input(
     db: &dyn ExpandDatabase,
     krate: CrateId,
     macro_call: InFile<ast::MacroCall>,
     def: MacroDefId,
     resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
 ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
-    let MacroDefKind::BuiltInEager(eager, _) = def.kind else {
-        panic!("called `expand_eager_macro` on non-eager macro def {def:?}")
+    assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..)));
+    let token_tree = macro_call.value.token_tree();
+
+    let Some(token_tree) = token_tree else {
+        return Ok(ExpandResult { value: None, err:
+            Some(ExpandError::other(
+                "invalid token tree"
+            )),
+        });
     };
-    let hygiene = Hygiene::new(db, macro_call.file_id);
-    let parsed_args = macro_call
-        .value
-        .token_tree()
-        .map(|tt| mbe::syntax_node_to_token_tree(tt.syntax()).0)
-        .unwrap_or_else(tt::Subtree::empty);
+    let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax());
 
     let ast_map = db.ast_id_map(macro_call.file_id);
     let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(&macro_call.value));
@@ -60,41 +62,40 @@ pub fn expand_eager_macro(
         def,
         krate,
         eager: Some(Box::new(EagerCallInfo {
-            arg_or_expansion: Arc::new(parsed_args.clone()),
-            included_file: None,
+            arg: Arc::new((parsed_args, arg_token_map)),
+            arg_id: None,
             error: None,
         })),
         kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
     });
-
-    let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
-    let ExpandResult { value, mut err } = eager_macro_recur(
+    let arg_as_expr = match db.macro_arg_text(arg_id) {
+        Some(it) => it,
+        None => {
+            return Ok(ExpandResult {
+                value: None,
+                err: Some(ExpandError::other("invalid token tree")),
+            })
+        }
+    };
+    let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
         db,
-        &hygiene,
-        InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
+        &Hygiene::new(db, macro_call.file_id),
+        InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
         krate,
         resolver,
     )?;
-    let Some(value ) = value else {
+    let Some(expanded_eager_input) = expanded_eager_input else {
         return Ok(ExpandResult { value: None, err })
     };
-    let subtree = {
-        let mut subtree = mbe::syntax_node_to_token_tree(&value).0;
-        subtree.delimiter = crate::tt::Delimiter::unspecified();
-        subtree
-    };
-
-    let res = eager.expand(db, arg_id, &subtree);
-    if err.is_none() {
-        err = res.err;
-    }
+    let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
+    subtree.delimiter = crate::tt::Delimiter::unspecified();
 
     let loc = MacroCallLoc {
         def,
         krate,
         eager: Some(Box::new(EagerCallInfo {
-            arg_or_expansion: Arc::new(res.value.subtree),
-            included_file: res.value.included_file,
+            arg: Arc::new((subtree, token_map)),
+            arg_id: Some(arg_id),
             error: err.clone(),
         })),
         kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
@@ -118,8 +119,9 @@ fn lazy_expand(
         MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
     );
 
-    let file_id = id.as_file();
-    db.parse_or_expand_with_err(file_id).map(|parse| InFile::new(file_id, parse))
+    let macro_file = id.as_macro_file();
+
+    db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
 }
 
 fn eager_macro_recur(
@@ -142,13 +144,13 @@ fn eager_macro_recur(
         let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
             Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
             None => {
-                error = Some(ExpandError::Other("malformed macro invocation".into()));
+                error = Some(ExpandError::other("malformed macro invocation"));
                 continue;
             }
         };
         let ExpandResult { value, err } = match def.kind {
             MacroDefKind::BuiltInEager(..) => {
-                let id = match expand_eager_macro(
+                let ExpandResult { value, err } = match expand_eager_macro_input(
                     db,
                     krate,
                     curr.with_value(child.clone()),
@@ -158,9 +160,17 @@ fn eager_macro_recur(
                     Ok(it) => it,
                     Err(err) => return Err(err),
                 };
-                id.map(|call| {
-                    call.map(|call| db.parse_or_expand(call.as_file()).clone_for_update())
-                })
+                match value {
+                    Some(call) => {
+                        let ExpandResult { value, err: err2 } =
+                            db.parse_macro_expansion(call.as_macro_file());
+                        ExpandResult {
+                            value: Some(value.0.syntax_node().clone_for_update()),
+                            err: err.or(err2),
+                        }
+                    }
+                    None => ExpandResult { value: None, err },
+                }
             }
             MacroDefKind::Declarative(_)
             | MacroDefKind::BuiltIn(..)
@@ -180,7 +190,7 @@ fn eager_macro_recur(
                     krate,
                     macro_resolver,
                 )?;
-                let err = if err.is_none() { error } else { err };
+                let err = err.or(error);
                 ExpandResult { value, err }
             }
         };
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index c8373778d32..e0c199328ef 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -58,7 +58,13 @@ pub enum ExpandError {
     UnresolvedProcMacro(CrateId),
     Mbe(mbe::ExpandError),
     RecursionOverflowPoisoned,
-    Other(Box<str>),
+    Other(Box<Box<str>>),
+}
+
+impl ExpandError {
+    pub fn other(msg: impl Into<Box<str>>) -> Self {
+        ExpandError::Other(Box::new(msg.into()))
+    }
 }
 
 impl From<mbe::ExpandError> for ExpandError {
@@ -97,9 +103,15 @@ impl fmt::Display for ExpandError {
 /// The two variants are encoded in a single u32 which are differentiated by the MSB.
 /// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
 /// `MacroCallId`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct HirFileId(u32);
 
+impl fmt::Debug for HirFileId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.repr().fmt(f)
+    }
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct MacroFile {
     pub macro_call_id: MacroCallId,
@@ -115,6 +127,7 @@ impl_intern_key!(MacroCallId);
 pub struct MacroCallLoc {
     pub def: MacroDefId,
     pub(crate) krate: CrateId,
+    /// Some if `def` is a builtin eager macro.
     eager: Option<Box<EagerCallInfo>>,
     pub kind: MacroCallKind,
 }
@@ -140,8 +153,10 @@ pub enum MacroDefKind {
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 struct EagerCallInfo {
     /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
-    arg_or_expansion: Arc<tt::Subtree>,
-    included_file: Option<(FileId, TokenMap)>,
+    arg: Arc<(tt::Subtree, TokenMap)>,
+    /// call id of the eager macro's input file. If this is none, macro call containing this call info
+    /// is an eager macro's input, otherwise it is its output.
+    arg_id: Option<MacroCallId>,
     error: Option<ExpandError>,
 }
 
@@ -206,10 +221,15 @@ impl HirFileId {
                 HirFileIdRepr::FileId(id) => break id,
                 HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
                     let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
-                    file_id = match loc.eager.as_deref() {
-                        Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
+                    let is_include_expansion = loc.def.is_include()
+                        && matches!(
+                            loc.eager.as_deref(),
+                            Some(EagerCallInfo { arg_id: Some(_), .. })
+                        );
+                    file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) {
+                        Some(Ok((_, file))) => file.into(),
                         _ => loc.kind.file_id(),
-                    };
+                    }
                 }
             }
         }
@@ -325,7 +345,17 @@ impl HirFileId {
         match self.macro_file() {
             Some(macro_file) => {
                 let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
-                matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. }))
+                loc.def.is_include()
+            }
+            _ => false,
+        }
+    }
+
+    pub fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
+        match self.macro_file() {
+            Some(macro_file) => {
+                let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
+                matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. }))
             }
             _ => false,
         }
@@ -423,6 +453,10 @@ impl MacroDefId {
     pub fn is_attribute_derive(&self) -> bool {
         matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
     }
+
+    pub fn is_include(&self) -> bool {
+        matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
+    }
 }
 
 impl MacroCallLoc {
@@ -569,6 +603,10 @@ impl MacroCallId {
     pub fn as_file(self) -> HirFileId {
         MacroFile { macro_call_id: self }.into()
     }
+
+    pub fn as_macro_file(self) -> MacroFile {
+        MacroFile { macro_call_id: self }
+    }
 }
 
 /// ExpansionInfo mainly describes how to map text range between src and expanded macro
@@ -662,7 +700,7 @@ impl ExpansionInfo {
 
         let token_id = match token_id_in_attr_input {
             Some(token_id) => token_id,
-            // the token is not inside an attribute's input so do the lookup in the macro_arg as usual
+            // the token is not inside `an attribute's input so do the lookup in the macro_arg as usual
             None => {
                 let relative_range =
                     token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
@@ -694,14 +732,18 @@ impl ExpansionInfo {
         let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
         let loc = db.lookup_intern_macro_call(call_id);
 
-        if let Some((file, map)) = loc.eager.and_then(|e| e.included_file) {
-            // Special case: map tokens from `include!` expansions to the included file
-            let range = map.first_range_by_token(token_id, token.value.kind())?;
-            let source = db.parse(file);
+        // Special case: map tokens from `include!` expansions to the included file
+        if loc.def.is_include()
+            && matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. }))
+        {
+            if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) {
+                let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?;
+                let source = db.parse(file_id);
 
-            let token = source.syntax_node().covering_element(range).into_token()?;
+                let token = source.syntax_node().covering_element(range).into_token()?;
 
-            return Some((InFile::new(file.into(), token), Origin::Call));
+                return Some((InFile::new(file_id.into(), token), Origin::Call));
+            }
         }
 
         // Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
diff --git a/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs
index c9539210abf..41675c630dc 100644
--- a/crates/hir-expand/src/proc_macro.rs
+++ b/crates/hir-expand/src/proc_macro.rs
@@ -46,7 +46,7 @@ impl ProcMacroExpander {
                         never!("Non-dummy expander even though there are no proc macros");
                         return ExpandResult::new(
                             tt::Subtree::empty(),
-                            ExpandError::Other("Internal error".into()),
+                            ExpandError::other("Internal error"),
                         );
                     }
                 };
@@ -60,7 +60,7 @@ impl ProcMacroExpander {
                         );
                         return ExpandResult::new(
                             tt::Subtree::empty(),
-                            ExpandError::Other("Internal error".into()),
+                            ExpandError::other("Internal error"),
                         );
                     }
                 };
@@ -75,14 +75,11 @@ impl ProcMacroExpander {
                         ProcMacroExpansionError::System(text)
                             if proc_macro.kind == ProcMacroKind::Attr =>
                         {
-                            ExpandResult {
-                                value: tt.clone(),
-                                err: Some(ExpandError::Other(text.into())),
-                            }
+                            ExpandResult { value: tt.clone(), err: Some(ExpandError::other(text)) }
                         }
                         ProcMacroExpansionError::System(text)
                         | ProcMacroExpansionError::Panic(text) => {
-                            ExpandResult::new(tt::Subtree::empty(), ExpandError::Other(text.into()))
+                            ExpandResult::new(tt::Subtree::empty(), ExpandError::other(text))
                         }
                     },
                 }
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt
index 77714efa350..7834c66033c 100644
--- a/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -20,8 +20,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -47,8 +49,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -74,8 +78,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -101,8 +107,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -128,8 +136,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -155,8 +165,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -182,8 +194,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index b5adfc13d96..1a00e29384e 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -18,8 +18,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: TYPE_ALIAS,
@@ -43,8 +45,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: CONST,
@@ -68,8 +72,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: CONST,
@@ -95,8 +101,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: ENUM,
@@ -122,8 +130,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MACRO_DEF,
@@ -147,8 +157,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STATIC,
@@ -174,8 +186,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -201,8 +215,12 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        2147483648,
+                    hir_file_id: MacroFile(
+                        MacroFile {
+                            macro_call_id: MacroCallId(
+                                0,
+                            ),
+                        },
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -228,8 +246,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -257,8 +277,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -286,8 +308,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -311,8 +335,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: TRAIT,
@@ -338,8 +364,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: UNION,
@@ -365,8 +393,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MODULE,
@@ -392,8 +422,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MODULE,
@@ -419,8 +451,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MACRO_RULES,
@@ -444,8 +478,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: FN,
@@ -471,8 +507,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: MACRO_RULES,
@@ -496,8 +534,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: FN,
@@ -521,8 +561,10 @@
                     },
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: FN,
@@ -561,8 +603,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        0,
+                    hir_file_id: FileId(
+                        FileId(
+                            0,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
@@ -599,8 +643,10 @@
                     ),
                 ),
                 loc: DeclarationLocation {
-                    hir_file_id: HirFileId(
-                        1,
+                    hir_file_id: FileId(
+                        FileId(
+                            1,
+                        ),
                     ),
                     ptr: SyntaxNodePtr {
                         kind: STRUCT,
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 327e1502d19..fa374b04f19 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -86,6 +86,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">assert</span> <span class="brace">{</span><span class="brace">}</span>
 <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">asm</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">concat</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
     <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
@@ -172,4 +174,5 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="operator macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 887d18b989a..497992f684c 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -432,6 +432,8 @@ macro_rules! panic {}
 macro_rules! assert {}
 #[rustc_builtin_macro]
 macro_rules! asm {}
+#[rustc_builtin_macro]
+macro_rules! concat {}
 
 macro_rules! toho {
     () => ($crate::panic!("not yet implemented"));
@@ -518,6 +520,7 @@ fn main() {
     toho!("{}fmt", 0);
     asm!("mov eax, {0}");
     format_args!(concat!("{}"), "{}");
+    format_args!("{}", format_args!("{}", 0));
 }"#,
         expect_file!["./test_data/highlight_strings.html"],
         false,