Repository: rust-lang/rust-analyzer
Pull request #16845 (2024-03-15 13:02:02)

Show compilation progress in test explorer
Fix part of #16827


Patch:
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index b815c9b73e..f84852b262 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -715,7 +715,7 @@ impl<'a> AssocItemCollector<'a> {
             }
             AssocItem::MacroCall(call) => {
                 let file_id = self.expander.current_file_id();
-                let MacroCall { ast_id, expand_to, ctxt, ref path } = item_tree[call];
+                let MacroCall { ast_id, expand_to, call_site, ref path } = item_tree[call];
                 let module = self.expander.module.local_id;
 
                 let resolver = |path| {
@@ -734,7 +734,7 @@ impl<'a> AssocItemCollector<'a> {
                 match macro_call_as_call_id(
                     self.db.upcast(),
                     &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
-                    ctxt,
+                    call_site,
                     expand_to,
                     self.expander.module.krate(),
                     resolver,
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 585e93ce21..eb665f1941 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -49,7 +49,7 @@ use intern::Interned;
 use la_arena::{Arena, Idx, IdxRange, RawIdx};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
-use span::{AstIdNode, FileAstId, SyntaxContextId};
+use span::{AstIdNode, FileAstId, Span};
 use stdx::never;
 use syntax::{ast, match_ast, SyntaxKind};
 use triomphe::Arc;
@@ -790,7 +790,7 @@ pub struct MacroCall {
     pub path: Interned<ModPath>,
     pub ast_id: FileAstId<ast::MacroCall>,
     pub expand_to: ExpandTo,
-    pub ctxt: SyntaxContextId,
+    pub call_site: Span,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index f02163cbe4..350593d7dc 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -567,7 +567,7 @@ impl<'a> Ctx<'a> {
         })?);
         let ast_id = self.source_ast_id_map.ast_id(m);
         let expand_to = hir_expand::ExpandTo::from_call_site(m);
-        let res = MacroCall { path, ast_id, expand_to, ctxt: span_map.span_for_range(range).ctx };
+        let res = MacroCall { path, ast_id, expand_to, call_site: span_map.span_for_range(range) };
         Some(id(self.data().macro_calls.alloc(res)))
     }
 
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 953bf6b85d..87c90a4c6a 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -487,12 +487,12 @@ impl Printer<'_> {
                 }
             }
             ModItem::MacroCall(it) => {
-                let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it];
+                let MacroCall { path, ast_id, expand_to, call_site } = &self.tree[it];
                 let _ = writeln!(
                     self,
-                    "// AstId: {:?}, SyntaxContext: {}, ExpandTo: {:?}",
+                    "// AstId: {:?}, Span: {}, ExpandTo: {:?}",
                     ast_id.erase().into_raw(),
-                    ctxt,
+                    call_site,
                     expand_to
                 );
                 wln!(self, "{}!(...);", path.display(self.db.upcast()));
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 828842de7e..6ff350c75a 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -90,7 +90,7 @@ use hir_expand::{
 use item_tree::ExternBlock;
 use la_arena::Idx;
 use nameres::DefMap;
-use span::{AstIdNode, FileAstId, FileId, SyntaxContextId};
+use span::{AstIdNode, FileAstId, FileId, Span};
 use stdx::impl_from;
 use syntax::{ast, AstNode};
 
@@ -1357,7 +1357,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
         macro_call_as_call_id_with_eager(
             db,
             &AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
-            call_site.ctx,
+            call_site,
             expands_to,
             krate,
             resolver,
@@ -1382,7 +1382,7 @@ impl<T: AstIdNode> AstIdWithPath<T> {
 fn macro_call_as_call_id(
     db: &dyn ExpandDatabase,
     call: &AstIdWithPath<ast::MacroCall>,
-    call_site: SyntaxContextId,
+    call_site: Span,
     expand_to: ExpandTo,
     krate: CrateId,
     resolver: impl Fn(path::ModPath) -> Option<MacroDefId> + Copy,
@@ -1394,7 +1394,7 @@ fn macro_call_as_call_id(
 fn macro_call_as_call_id_with_eager(
     db: &dyn ExpandDatabase,
     call: &AstIdWithPath<ast::MacroCall>,
-    call_site: SyntaxContextId,
+    call_site: Span,
     expand_to: ExpandTo,
     krate: CrateId,
     resolver: impl FnOnce(path::ModPath) -> Option<MacroDefId>,
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs
index 662c80edf3..25744e9570 100644
--- a/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -5,7 +5,7 @@ use hir_expand::{
     attrs::{Attr, AttrId, AttrInput},
     MacroCallId, MacroCallKind, MacroDefId,
 };
-use span::SyntaxContextId;
+use span::Span;
 use syntax::{ast, SmolStr};
 use triomphe::Arc;
 
@@ -109,7 +109,7 @@ pub(super) fn attr_macro_as_call_id(
     let arg = match macro_attr.input.as_deref() {
         Some(AttrInput::TokenTree(tt)) => {
             let mut tt = tt.as_ref().clone();
-            tt.delimiter.kind = tt::DelimiterKind::Invisible;
+            tt.delimiter = tt::Delimiter::invisible_spanned(macro_attr.span);
             Some(tt)
         }
 
@@ -124,7 +124,7 @@ pub(super) fn attr_macro_as_call_id(
             attr_args: arg.map(Arc::new),
             invoc_attr_index: macro_attr.id,
         },
-        macro_attr.ctxt,
+        macro_attr.span,
     )
 }
 
@@ -133,7 +133,7 @@ pub(super) fn derive_macro_as_call_id(
     item_attr: &AstIdWithPath<ast::Adt>,
     derive_attr_index: AttrId,
     derive_pos: u32,
-    call_site: SyntaxContextId,
+    call_site: Span,
     krate: CrateId,
     resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>,
 ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> {
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 3d026447fb..593a88af69 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -230,13 +230,13 @@ enum MacroDirectiveKind {
     FnLike {
         ast_id: AstIdWithPath<ast::MacroCall>,
         expand_to: ExpandTo,
-        ctxt: SyntaxContextId,
+        call_site: Span,
     },
     Derive {
         ast_id: AstIdWithPath<ast::Adt>,
         derive_attr: AttrId,
         derive_pos: usize,
-        ctxt: SyntaxContextId,
+        call_site: Span,
     },
     Attr {
         ast_id: AstIdWithPath<ast::Item>,
@@ -1126,7 +1126,7 @@ impl DefCollector<'_> {
             let resolver_def_id = |path| resolver(path).map(|(_, it)| it);
 
             match &directive.kind {
-                MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
+                MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => {
                     let call_id = macro_call_as_call_id(
                         self.db.upcast(),
                         ast_id,
@@ -1146,7 +1146,7 @@ impl DefCollector<'_> {
                         return Resolved::Yes;
                     }
                 }
-                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: call_site } => {
+                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site } => {
                     let id = derive_macro_as_call_id(
                         self.db,
                         ast_id,
@@ -1266,7 +1266,7 @@ impl DefCollector<'_> {
                                             ast_id,
                                             derive_attr: attr.id,
                                             derive_pos: idx,
-                                            ctxt: call_site.ctx,
+                                            call_site,
                                         },
                                         container: directive.container,
                                     });
@@ -1428,7 +1428,7 @@ impl DefCollector<'_> {
 
         for directive in &self.unresolved_macros {
             match &directive.kind {
-                MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt: call_site } => {
+                MacroDirectiveKind::FnLike { ast_id, expand_to, call_site } => {
                     // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
                     let macro_call_as_call_id = macro_call_as_call_id(
                         self.db.upcast(),
@@ -1460,7 +1460,7 @@ impl DefCollector<'_> {
                         ));
                     }
                 }
-                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, ctxt: _ } => {
+                MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos, call_site: _ } => {
                     self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
                         directive.module_id,
                         MacroCallKind::Derive {
@@ -2289,7 +2289,7 @@ impl ModCollector<'_, '_> {
 
     fn collect_macro_call(
         &mut self,
-        &MacroCall { ref path, ast_id, expand_to, ctxt }: &MacroCall,
+        &MacroCall { ref path, ast_id, expand_to, call_site }: &MacroCall,
         container: ItemContainerId,
     ) {
         let ast_id = AstIdWithPath::new(self.file_id(), ast_id, ModPath::clone(path));
@@ -2303,7 +2303,7 @@ impl ModCollector<'_, '_> {
         if let Ok(res) = macro_call_as_call_id_with_eager(
             db.upcast(),
             &ast_id,
-            ctxt,
+            call_site,
             expand_to,
             self.def_collector.def_map.krate,
             |path| {
@@ -2361,7 +2361,7 @@ impl ModCollector<'_, '_> {
         self.def_collector.unresolved_macros.push(MacroDirective {
             module_id: self.module_id,
             depth: self.macro_depth + 1,
-            kind: MacroDirectiveKind::FnLike { ast_id, expand_to, ctxt },
+            kind: MacroDirectiveKind::FnLike { ast_id, expand_to, call_site },
             container,
         });
     }
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index af3ecdcd5e..686a3ad192 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -7,7 +7,7 @@ use either::Either;
 use intern::Interned;
 use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
 use smallvec::{smallvec, SmallVec};
-use span::{Span, SyntaxContextId};
+use span::Span;
 use syntax::{ast, match_ast, AstNode, AstToken, SmolStr, SyntaxNode};
 use triomphe::Arc;
 
@@ -53,7 +53,7 @@ impl RawAttrs {
                 id,
                 input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
                 path: Interned::new(ModPath::from(crate::name!(doc))),
-                ctxt: span_map.span_for_range(comment.syntax().text_range()).ctx,
+                span: span_map.span_for_range(comment.syntax().text_range()),
             }),
         });
         let entries: Arc<[Attr]> = Arc::from_iter(entries);
@@ -173,7 +173,7 @@ pub struct Attr {
     pub id: AttrId,
     pub path: Interned<ModPath>,
     pub input: Option<Interned<AttrInput>>,
-    pub ctxt: SyntaxContextId,
+    pub span: Span,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -219,11 +219,11 @@ impl Attr {
         } else {
             None
         };
-        Some(Attr { id, path, input, ctxt: span.ctx })
+        Some(Attr { id, path, input, span })
     }
 
     fn from_tt(db: &dyn ExpandDatabase, tt: &[tt::TokenTree], id: AttrId) -> Option<Attr> {
-        let ctxt = tt.first()?.first_span().ctx;
+        let span = tt.first()?.first_span();
         let path_end = tt
             .iter()
             .position(|tt| {
@@ -255,7 +255,7 @@ impl Attr {
             }
             _ => None,
         };
-        Some(Attr { id, path, input, ctxt })
+        Some(Attr { id, path, input, span })
     }
 
     pub fn path(&self) -> &ModPath {
diff --git a/crates/hir-expand/src/builtin_attr_macro.rs b/crates/hir-expand/src/builtin_attr_macro.rs
index 9ff29b484d..64295f64dc 100644
--- a/crates/hir-expand/src/builtin_attr_macro.rs
+++ b/crates/hir-expand/src/builtin_attr_macro.rs
@@ -11,7 +11,7 @@ macro_rules! register_builtin {
         }
 
         impl BuiltinAttrExpander {
-            pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree, Span) -> ExpandResult<tt::Subtree>  {
+            pub fn expander(&self) -> fn (&dyn ExpandDatabase, MacroCallId, &tt::Subtree) -> ExpandResult<tt::Subtree>  {
                 match *self {
                     $( BuiltinAttrExpander::$variant => $expand, )*
                 }
@@ -34,9 +34,8 @@ impl BuiltinAttrExpander {
         db: &dyn ExpandDatabase,
         id: MacroCallId,
         tt: &tt::Subtree,
-        span: Span,
     ) -> ExpandResult<tt::Subtree> {
-        self.expander()(db, id, tt, span)
+        self.expander()(db, id, tt)
     }
 
     pub fn is_derive(self) -> bool {
@@ -72,7 +71,6 @@ fn dummy_attr_expand(
     _db: &dyn ExpandDatabase,
     _id: MacroCallId,
     tt: &tt::Subtree,
-    _span: Span,
 ) -> ExpandResult<tt::Subtree> {
     ExpandResult::ok(tt.clone())
 }
@@ -102,7 +100,6 @@ fn derive_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
     tt: &tt::Subtree,
-    span: Span,
 ) -> ExpandResult<tt::Subtree> {
     let loc = db.lookup_intern_macro_call(id);
     let derives = match &loc.kind {
@@ -110,10 +107,13 @@ fn derive_expand(
             attr_args
         }
         _ => {
-            return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan { open: span, close: span }))
+            return ExpandResult::ok(tt::Subtree::empty(tt::DelimSpan {
+                open: loc.call_site,
+                close: loc.call_site,
+            }))
         }
     };
-    pseudo_derive_attr_expansion(tt, derives, span)
+    pseudo_derive_attr_expansion(tt, derives, loc.call_site)
 }
 
 pub fn pseudo_derive_attr_expansion(
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index 528038a9cc..66dec7d89e 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -50,8 +50,8 @@ impl BuiltinDeriveExpander {
         db: &dyn ExpandDatabase,
         id: MacroCallId,
         tt: &tt::Subtree,
-        span: Span,
     ) -> ExpandResult<tt::Subtree> {
+        let span = db.lookup_intern_macro_call(id).call_site;
         let span = span_with_def_site_ctxt(db, span, id);
         self.expander()(span, tt)
     }
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 9fb6a0b234..6f3c01ba8c 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -62,8 +62,8 @@ impl BuiltinFnLikeExpander {
         db: &dyn ExpandDatabase,
         id: MacroCallId,
         tt: &tt::Subtree,
-        span: Span,
     ) -> ExpandResult<tt::Subtree> {
+        let span = db.lookup_intern_macro_call(id).call_site;
         let span = span_with_def_site_ctxt(db, span, id);
         self.expander()(db, id, tt, span)
     }
@@ -75,8 +75,8 @@ impl EagerExpander {
         db: &dyn ExpandDatabase,
         id: MacroCallId,
         tt: &tt::Subtree,
-        span: Span,
     ) -> ExpandResult<tt::Subtree> {
+        let span = db.lookup_intern_macro_call(id).call_site;
         let span = span_with_def_site_ctxt(db, span, id);
         self.expander()(db, id, tt, span)
     }
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index ec68f2f96e..51f6da76cb 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -3,7 +3,7 @@
 use base_db::{salsa, CrateId, FileId, SourceDatabase};
 use either::Either;
 use limit::Limit;
-use mbe::syntax_node_to_token_tree;
+use mbe::{syntax_node_to_token_tree, ValueResult};
 use rustc_hash::FxHashSet;
 use span::{AstIdMap, Span, SyntaxContextData, SyntaxContextId};
 use syntax::{ast, AstNode, Parse, SyntaxElement, SyntaxError, SyntaxNode, SyntaxToken, T};
@@ -98,7 +98,10 @@ pub trait ExpandDatabase: SourceDatabase {
     /// Lowers syntactic macro call to a token tree representation. That's a firewall
     /// query, only typing in the macro call itself changes the returned
     /// subtree.
-    fn macro_arg(&self, id: MacroCallId) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
+    fn macro_arg(
+        &self,
+        id: MacroCallId,
+    ) -> ValueResult<(Arc<tt::Subtree>, SyntaxFixupUndoInfo), Arc<Box<[SyntaxError]>>>;
     /// Fetches the expander for this macro.
     #[salsa::transparent]
     #[salsa::invoke(TokenExpander::macro_expander)]
@@ -144,16 +147,14 @@ pub fn expand_speculative(
     let span_map = RealSpanMap::absolute(FileId::BOGUS);
     let span_map = SpanMapRef::RealSpanMap(&span_map);
 
-    let (_, _, span) = db.macro_arg(actual_macro_call);
-
     // Build the subtree and token mapping for the speculative args
     let (mut tt, undo_info) = match loc.kind {
         MacroCallKind::FnLike { .. } => (
-            mbe::syntax_node_to_token_tree(speculative_args, span_map, span),
+            mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site),
             SyntaxFixupUndoInfo::NONE,
         ),
         MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => (
-            mbe::syntax_node_to_token_tree(speculative_args, span_map, span),
+            mbe::syntax_node_to_token_tree(speculative_args, span_map, loc.call_site),
             SyntaxFixupUndoInfo::NONE,
         ),
         MacroCallKind::Derive { derive_attr_index: index, .. }
@@ -161,15 +162,12 @@ pub fn expand_speculative(
             let censor = if let MacroCallKind::Derive { .. } = loc.kind {
                 censor_derive_input(index, &ast::Adt::cast(speculative_args.clone())?)
             } else {
-                attr_source(index, &ast::Item::cast(speculative_args.clone())?)
-                    .into_iter()
-                    .map(|it| it.syntax().clone().into())
-                    .collect()
+                censor_attr_input(index, &ast::Item::cast(speculative_args.clone())?)
             };
 
             let censor_cfg =
                 cfg_process::process_cfg_attrs(speculative_args, &loc, db).unwrap_or_default();
-            let mut fixups = fixup::fixup_syntax(span_map, speculative_args, span);
+            let mut fixups = fixup::fixup_syntax(span_map, speculative_args, loc.call_site);
             fixups.append.retain(|it, _| match it {
                 syntax::NodeOrToken::Token(_) => true,
                 it => !censor.contains(it) && !censor_cfg.contains(it),
@@ -183,7 +181,7 @@ pub fn expand_speculative(
                     span_map,
                     fixups.append,
                     fixups.remove,
-                    span,
+                    loc.call_site,
                 ),
                 fixups.undo_info,
             )
@@ -205,8 +203,9 @@ pub fn expand_speculative(
             }?;
             match attr.token_tree() {
                 Some(token_tree) => {
-                    let mut tree = syntax_node_to_token_tree(token_tree.syntax(), span_map, span);
-                    tree.delimiter = tt::Delimiter::invisible_spanned(span);
+                    let mut tree =
+                        syntax_node_to_token_tree(token_tree.syntax(), span_map, loc.call_site);
+                    tree.delimiter = tt::Delimiter::invisible_spanned(loc.call_site);
 
                     Some(tree)
                 }
@@ -220,8 +219,8 @@ pub fn expand_speculative(
     // Otherwise the expand query will fetch the non speculative attribute args and pass those instead.
     let mut speculative_expansion = match loc.def.kind {
         MacroDefKind::ProcMacro(expander, _, ast) => {
+            tt.delimiter = tt::Delimiter::invisible_spanned(loc.call_site);
             let span = db.proc_macro_span(ast);
-            tt.delimiter = tt::Delimiter::invisible_spanned(span);
             expander.expand(
                 db,
                 loc.def.krate,
@@ -234,21 +233,22 @@ pub fn expand_speculative(
             )
         }
         MacroDefKind::BuiltInAttr(BuiltinAttrExpander::Derive, _) => {
-            pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, span)
-        }
-        MacroDefKind::Declarative(it) => {
-            db.decl_macro_expander(loc.krate, it).expand_unhygienic(db, tt, loc.def.krate, span)
-        }
-        MacroDefKind::BuiltIn(it, _) => {
-            it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
+            pseudo_derive_attr_expansion(&tt, attr_arg.as_ref()?, loc.call_site)
         }
+        MacroDefKind::Declarative(it) => db.decl_macro_expander(loc.krate, it).expand_unhygienic(
+            db,
+            tt,
+            loc.def.krate,
+            loc.call_site,
+        ),
+        MacroDefKind::BuiltIn(it, _) => it.expand(db, actual_macro_call, &tt).map_err(Into::into),
         MacroDefKind::BuiltInDerive(it, ..) => {
-            it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
+            it.expand(db, actual_macro_call, &tt).map_err(Into::into)
         }
         MacroDefKind::BuiltInEager(it, _) => {
-            it.expand(db, actual_macro_call, &tt, span).map_err(Into::into)
+            it.expand(db, actual_macro_call, &tt).map_err(Into::into)
         }
-        MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt, span),
+        MacroDefKind::BuiltInAttr(it, _) => it.expand(db, actual_macro_call, &tt),
     };
 
     let expand_to = loc.expand_to();
@@ -344,7 +344,7 @@ pub(crate) fn parse_with_map(
 fn macro_arg(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
-) -> (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span) {
+) -> ValueResult<(Arc<tt::Subtree>, SyntaxFixupUndoInfo), Arc<Box<[SyntaxError]>>> {
     let loc = db.lookup_intern_macro_call(id);
 
     if let MacroCallLoc {
@@ -353,33 +353,38 @@ fn macro_arg(
         ..
     } = &loc
     {
-        return (eager.arg.clone(), SyntaxFixupUndoInfo::NONE, eager.span);
+        return ValueResult::ok((eager.arg.clone(), SyntaxFixupUndoInfo::NONE));
     }
 
     let (parse, map) = parse_with_map(db, loc.kind.file_id());
     let root = parse.syntax_node();
 
-    let (censor, item_node, span) = match loc.kind {
+    let (censor, item_node) = match loc.kind {
         MacroCallKind::FnLike { ast_id, .. } => {
-            let node = &ast_id.to_ptr(db).to_node(&root);
-            let path_range = node
-                .path()
-                .map_or_else(|| node.syntax().text_range(), |path| path.syntax().text_range());
-            let span = map.span_for_range(path_range);
-
             let dummy_tt = |kind| {
                 (
                     Arc::new(tt::Subtree {
-                        delimiter: tt::Delimiter { open: span, close: span, kind },
+                        delimiter: tt::Delimiter {
+                            open: loc.call_site,
+                            close: loc.call_site,
+                            kind,
+                        },
                         token_trees: Box::default(),
                     }),
                     SyntaxFixupUndoInfo::default(),
-                    span,
                 )
             };
 
+            let node = &ast_id.to_ptr(db).to_node(&root);
+            let offset = node.syntax().text_range().start();
             let Some(tt) = node.token_tree() else {
-                return dummy_tt(tt::DelimiterKind::Invisible);
+                return ValueResult::new(
+                    dummy_tt(tt::DelimiterKind::Invisible),
+                    Arc::new(Box::new([SyntaxError::new_at_offset(
+                        "missing token tree".to_owned(),
+                        offset,
+                    )])),
+                );
             };
             let first = tt.left_delimiter_token().map(|it| it.kind()).unwrap_or(T!['(']);
             let last = tt.right_delimiter_token().map(|it| it.kind()).unwrap_or(T![.]);
@@ -404,46 +409,48 @@ fn macro_arg(
                     T!['{'] => tt::DelimiterKind::Brace,
                     _ => tt::DelimiterKind::Invisible,
                 };
-                return dummy_tt(kind);
+                return ValueResult::new(
+                    dummy_tt(kind),
+                    Arc::new(Box::new([SyntaxError::new_at_offset(
+                        "mismatched delimiters".to_owned(),
+                        offset,
+                    )])),
+                );
             }
 
-            let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), span);
+            let mut tt = mbe::syntax_node_to_token_tree(tt.syntax(), map.as_ref(), loc.call_site);
             if loc.def.is_proc_macro() {
                 // proc macros expect their inputs without parentheses, MBEs expect it with them included
                 tt.delimiter.kind = tt::DelimiterKind::Invisible;
             }
-            return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
+            let val = (Arc::new(tt), SyntaxFixupUndoInfo::NONE);
+            return if matches!(loc.def.kind, MacroDefKind::BuiltInEager(..)) {
+                match parse.errors() {
+                    errors if errors.is_empty() => ValueResult::ok(val),
+                    errors => ValueResult::new(
+                        val,
+                        // Box::<[_]>::from(res.errors()), not stable yet
+                        Arc::new(errors.to_vec().into_boxed_slice()),
+                    ),
+                }
+            } else {
+                ValueResult::ok(val)
+            };
         }
         MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
             let node = ast_id.to_ptr(db).to_node(&root);
-            let censor_derive_input = censor_derive_input(derive_attr_index, &node);
-            let item_node = node.into();
-            let attr_source = attr_source(derive_attr_index, &item_node);
-            // FIXME: This is wrong, this should point to the path of the derive attribute`
-            let span =
-                map.span_for_range(attr_source.as_ref().and_then(|it| it.path()).map_or_else(
-                    || item_node.syntax().text_range(),
-                    |it| it.syntax().text_range(),
-                ));
-            (censor_derive_input, item_node, span)
+            (censor_derive_input(derive_attr_index, &node), node.into())
         }
         MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
             let node = ast_id.to_ptr(db).to_node(&root);
-            let attr_source = attr_source(invoc_attr_index, &node);
-            let span = map.span_for_range(
-                attr_source
-                    .as_ref()
-                    .and_then(|it| it.path())
-                    .map_or_else(|| node.syntax().text_range(), |it| it.syntax().text_range()),
-            );
-            (attr_source.into_iter().map(|it| it.syntax().clone().into()).collect(), node, span)
+            (censor_attr_input(invoc_attr_index, &node), node)
         }
     };
 
     let (mut tt, undo_info) = {
         let syntax = item_node.syntax();
         let censor_cfg = cfg_process::process_cfg_attrs(syntax, &loc, db).unwrap_or_default();
-        let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, span);
+        let mut fixups = fixup::fixup_syntax(map.as_ref(), syntax, loc.call_site);
         fixups.append.retain(|it, _| match it {
             syntax::NodeOrToken::Token(_) => true,
             it => !censor.contains(it) && !censor_cfg.contains(it),
@@ -457,7 +464,7 @@ fn macro_arg(
                 map,
                 fixups.append,
                 fixups.remove,
-                span,
+                loc.call_site,
             ),
             fixups.undo_info,
         )
@@ -468,11 +475,11 @@ fn macro_arg(
         tt.delimiter.kind = tt::DelimiterKind::Invisible;
     }
 
-    (Arc::new(tt), undo_info, span)
+    ValueResult::ok((Arc::new(tt), undo_info))
 }
 
 // FIXME: Censoring info should be calculated by the caller! Namely by name resolution
-/// Derives expect all `#[derive(..)]` invocations up to (and including) the currently invoked one to be stripped
+/// Derives expect all `#[derive(..)]` invocations up to the currently invoked one to be stripped
 fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet<SyntaxElement> {
     // FIXME: handle `cfg_attr`
     cov_mark::hit!(derive_censoring);
@@ -489,11 +496,16 @@ fn censor_derive_input(derive_attr_index: AttrId, node: &ast::Adt) -> FxHashSet<
         .collect()
 }
 
-/// Attributes expect the invoking attribute to be stripped
-fn attr_source(invoc_attr_index: AttrId, node: &ast::Item) -> Option<ast::Attr> {
+/// Attributes expect the invoking attribute to be stripped\
+fn censor_attr_input(invoc_attr_index: AttrId, node: &ast::Item) -> FxHashSet<SyntaxElement> {
     // FIXME: handle `cfg_attr`
     cov_mark::hit!(attribute_macro_attr_censoring);
-    collect_attrs(node).nth(invoc_attr_index.ast_index()).and_then(|(_, attr)| Either::left(attr))
+    collect_attrs(node)
+        .nth(invoc_attr_index.ast_index())
+        .and_then(|(_, attr)| Either::left(attr))
+        .map(|attr| attr.syntax().clone().into())
+        .into_iter()
+        .collect()
 }
 
 impl TokenExpander {
@@ -523,54 +535,70 @@ fn macro_expand(
 ) -> ExpandResult<CowArc<tt::Subtree>> {
     let _p = tracing::span!(tracing::Level::INFO, "macro_expand").entered();
 
-    let (ExpandResult { value: tt, err }, span) = match loc.def.kind {
+    let ExpandResult { value: tt, err } = match loc.def.kind {
         MacroDefKind::ProcMacro(..) => return db.expand_proc_macro(macro_call_id).map(CowArc::Arc),
         _ => {
-            let (macro_arg, undo_info, span) = db.macro_arg(macro_call_id);
+            let ValueResult { value: (macro_arg, undo_info), err } = db.macro_arg(macro_call_id);
+            let format_parse_err = |err: Arc<Box<[SyntaxError]>>| {
+                let mut buf = String::new();
+                for err in &**err {
+                    use std::fmt::Write;
+                    _ = write!(buf, "{}, ", err);
+                }
+                buf.pop();
+                buf.pop();
+                ExpandError::other(buf)
+            };
 
             let arg = &*macro_arg;
-            let res =
-                match loc.def.kind {
-                    MacroDefKind::Declarative(id) => db
-                        .decl_macro_expander(loc.def.krate, id)
-                        .expand(db, arg.clone(), macro_call_id, span),
-                    MacroDefKind::BuiltIn(it, _) => {
-                        it.expand(db, macro_call_id, arg, span).map_err(Into::into)
-                    }
-                    MacroDefKind::BuiltInDerive(it, _) => {
-                        it.expand(db, macro_call_id, arg, span).map_err(Into::into)
-                    }
-                    MacroDefKind::BuiltInEager(it, _) => {
-                        // This might look a bit odd, but we do not expand the inputs to eager macros here.
-                        // Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
-                        // That kind of expansion uses the ast id map of an eager macros input though which goes through
-                        // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
-                        // will end up going through here again, whereas we want to just want to inspect the raw input.
-                        // As such we just return the input subtree here.
-                        let eager = match &loc.kind {
-                            MacroCallKind::FnLike { eager: None, .. } => {
-                                return ExpandResult::ok(CowArc::Arc(macro_arg.clone()));
-                            }
-                            MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
-                            _ => None,
-                        };
-
-                        let mut res = it.expand(db, macro_call_id, arg, span).map_err(Into::into);
-
-                        if let Some(EagerCallInfo { error, .. }) = eager {
-                            // FIXME: We should report both errors!
-                            res.err = error.clone().or(res.err);
+            let res = match loc.def.kind {
+                MacroDefKind::Declarative(id) => {
+                    db.decl_macro_expander(loc.def.krate, id).expand(db, arg.clone(), macro_call_id)
+                }
+                MacroDefKind::BuiltIn(it, _) => {
+                    it.expand(db, macro_call_id, arg).map_err(Into::into)
+                }
+                MacroDefKind::BuiltInDerive(it, _) => {
+                    it.expand(db, macro_call_id, arg).map_err(Into::into)
+                }
+                MacroDefKind::BuiltInEager(it, _) => {
+                    // This might look a bit odd, but we do not expand the inputs to eager macros here.
+                    // Eager macros inputs are expanded, well, eagerly when we collect the macro calls.
+                    // That kind of expansion uses the ast id map of an eager macros input though which goes through
+                    // the HirFileId machinery. As eager macro inputs are assigned a macro file id that query
+                    // will end up going through here again, whereas we want to just want to inspect the raw input.
+                    // As such we just return the input subtree here.
+                    let eager = match &loc.kind {
+                        MacroCallKind::FnLike { eager: None, .. } => {
+                            return ExpandResult {
+                                value: CowArc::Arc(macro_arg.clone()),
+                                err: err.map(format_parse_err),
+                            };
                         }
-                        res
-                    }
-                    MacroDefKind::BuiltInAttr(it, _) => {
-                        let mut res = it.expand(db, macro_call_id, arg, span);
-                        fixup::reverse_fixups(&mut res.value, &undo_info);
-                        res
+                        MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
+                        _ => None,
+                    };
+
+                    let mut res = it.expand(db, macro_call_id, arg).map_err(Into::into);
+
+                    if let Some(EagerCallInfo { error, .. }) = eager {
+                        // FIXME: We should report both errors!
+                        res.err = error.clone().or(res.err);
                     }
-                    _ => unreachable!(),
-                };
-            (ExpandResult { value: res.value, err: res.err }, span)
+                    res
+                }
+                MacroDefKind::BuiltInAttr(it, _) => {
+                    let mut res = it.expand(db, macro_call_id, arg);
+                    fixup::reverse_fixups(&mut res.value, &undo_info);
+                    res
+                }
+                _ => unreachable!(),
+            };
+            ExpandResult {
+                value: res.value,
+                // if the arg had parse errors, show them instead of the expansion errors
+                err: err.map(format_parse_err).or(res.err),
+            }
         }
     };
 
@@ -580,7 +608,7 @@ fn macro_expand(
         if let Err(value) = check_tt_count(&tt) {
             return value.map(|()| {
                 CowArc::Owned(tt::Subtree {
-                    delimiter: tt::Delimiter::invisible_spanned(span),
+                    delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
                     token_trees: Box::new([]),
                 })
             });
@@ -603,7 +631,7 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
 
 fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
     let loc = db.lookup_intern_macro_call(id);
-    let (macro_arg, undo_info, span) = db.macro_arg(id);
+    let (macro_arg, undo_info) = db.macro_arg(id).value;
 
     let (expander, ast) = match loc.def.kind {
         MacroDefKind::ProcMacro(expander, _, ast) => (expander, ast),
@@ -615,25 +643,23 @@ fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<A
         _ => None,
     };
 
-    let ExpandResult { value: mut tt, err } = {
-        let span = db.proc_macro_span(ast);
-        expander.expand(
-            db,
-            loc.def.krate,
-            loc.krate,
-            &macro_arg,
-            attr_arg,
-            span_with_def_site_ctxt(db, span, id),
-            span_with_call_site_ctxt(db, span, id),
-            span_with_mixed_site_ctxt(db, span, id),
-        )
-    };
+    let span = db.proc_macro_span(ast);
+    let ExpandResult { value: mut tt, err } = expander.expand(
+        db,
+        loc.def.krate,
+        loc.krate,
+        &macro_arg,
+        attr_arg,
+        span_with_def_site_ctxt(db, span, id),
+        span_with_call_site_ctxt(db, span, id),
+        span_with_mixed_site_ctxt(db, span, id),
+    );
 
     // Set a hard limit for the expanded tt
     if let Err(value) = check_tt_count(&tt) {
         return value.map(|()| {
             Arc::new(tt::Subtree {
-                delimiter: tt::Delimiter::invisible_spanned(span),
+                delimiter: tt::Delimiter::invisible_spanned(loc.call_site),
                 token_trees: Box::new([]),
             })
         });
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index 33643c0272..6874336cd2 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -29,7 +29,6 @@ impl DeclarativeMacroExpander {
         db: &dyn ExpandDatabase,
         tt: tt::Subtree,
         call_id: MacroCallId,
-        span: Span,
     ) -> ExpandResult<tt::Subtree> {
         let loc = db.lookup_intern_macro_call(call_id);
         let toolchain = db.toolchain(loc.def.krate);
@@ -46,7 +45,7 @@ impl DeclarativeMacroExpander {
         });
         match self.mac.err() {
             Some(_) => ExpandResult::new(
-                tt::Subtree::empty(tt::DelimSpan { open: span, close: span }),
+                tt::Subtree::empty(tt::DelimSpan { open: loc.call_site, close: loc.call_site }),
                 ExpandError::MacroDefinition,
             ),
             None => self
@@ -55,7 +54,7 @@ impl DeclarativeMacroExpander {
                     &tt,
                     |s| s.ctx = apply_mark(db, s.ctx, call_id, self.transparency),
                     new_meta_vars,
-                    span,
+                    loc.call_site,
                 )
                 .map_err(Into::into),
         }
diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs
index 8b147c88c1..4524463e63 100644
--- a/crates/hir-expand/src/eager.rs
+++ b/crates/hir-expand/src/eager.rs
@@ -19,7 +19,7 @@
 //!
 //! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
 use base_db::CrateId;
-use span::SyntaxContextId;
+use span::Span;
 use syntax::{ted, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent};
 use triomphe::Arc;
 
@@ -37,7 +37,7 @@ pub fn expand_eager_macro_input(
     macro_call: &ast::MacroCall,
     ast_id: AstId<ast::MacroCall>,
     def: MacroDefId,
-    call_site: SyntaxContextId,
+    call_site: Span,
     resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
 ) -> ExpandResult<Option<MacroCallId>> {
     let expand_to = ExpandTo::from_call_site(macro_call);
@@ -50,10 +50,9 @@ pub fn expand_eager_macro_input(
         def,
         krate,
         kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None },
-        ctxt: call_site,
+        call_site,
     }
     .intern(db);
-    let (_, _, span) = db.macro_arg(arg_id);
     let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
         db.parse_macro_expansion(arg_id.as_macro_file());
 
@@ -80,7 +79,7 @@ pub fn expand_eager_macro_input(
         return ExpandResult { value: None, err };
     };
 
-    let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, span);
+    let mut subtree = mbe::syntax_node_to_token_tree(&expanded_eager_input, arg_map, call_site);
 
     subtree.delimiter.kind = crate::tt::DelimiterKind::Invisible;
 
@@ -94,10 +93,9 @@ pub fn expand_eager_macro_input(
                 arg: Arc::new(subtree),
                 arg_id,
                 error: err.clone(),
-                span,
             })),
         },
-        ctxt: call_site,
+        call_site,
     };
 
     ExpandResult { value: Some(loc.intern(db)), err }
@@ -109,7 +107,7 @@ fn lazy_expand(
     macro_call: &ast::MacroCall,
     ast_id: AstId<ast::MacroCall>,
     krate: CrateId,
-    call_site: SyntaxContextId,
+    call_site: Span,
 ) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
     let expand_to = ExpandTo::from_call_site(macro_call);
     let id = def.make_call(
@@ -131,7 +129,7 @@ fn eager_macro_recur(
     mut offset: TextSize,
     curr: InFile<SyntaxNode>,
     krate: CrateId,
-    call_site: SyntaxContextId,
+    call_site: Span,
     macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
 ) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
     let original = curr.value.clone_for_update();
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index 959595afb5..b44feb5ebe 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -3,7 +3,7 @@
 
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::SmallVec;
-use span::{ErasedFileAstId, Span, SpanAnchor, FIXUP_ERASED_FILE_AST_ID_MARKER};
+use span::{ErasedFileAstId, Span, SpanAnchor, SpanData, FIXUP_ERASED_FILE_AST_ID_MARKER};
 use stdx::never;
 use syntax::{
     ast::{self, AstNode, HasLoopBody},
@@ -57,7 +57,7 @@ pub(crate) fn fixup_syntax(
     let dummy_range = FIXUP_DUMMY_RANGE;
     let fake_span = |range| {
         let span = span_map.span_for_range(range);
-        Span {
+        SpanData {
             range: dummy_range,
             anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
             ctx: span.ctx,
@@ -76,7 +76,7 @@ pub(crate) fn fixup_syntax(
             let span = span_map.span_for_range(node_range);
             let replacement = Leaf::Ident(Ident {
                 text: "__ra_fixup".into(),
-                span: Span {
+                span: SpanData {
                     range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
                     anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
                     ctx: span.ctx,
@@ -305,8 +305,8 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo)
         tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
             || tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
     ) {
-        tt.delimiter.close = Span::DUMMY;
-        tt.delimiter.open = Span::DUMMY;
+        tt.delimiter.close = SpanData::DUMMY;
+        tt.delimiter.open = SpanData::DUMMY;
     }
     reverse_fixups_(tt, undo_info);
 }
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index 097e760c70..ac2bab280d 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -65,7 +65,7 @@ pub(super) fn apply_mark(
         return apply_mark_internal(db, ctxt, call_id, transparency);
     }
 
-    let call_site_ctxt = db.lookup_intern_macro_call(call_id).ctxt;
+    let call_site_ctxt = db.lookup_intern_macro_call(call_id).call_site.ctx;
     let mut call_site_ctxt = if transparency == Transparency::SemiTransparent {
         call_site_ctxt.normalize_to_macros_2_0(db)
     } else {
@@ -205,10 +205,11 @@ pub(crate) fn dump_syntax_contexts(db: &dyn ExpandDatabase) -> String {
         let id = e.key;
         let expn_data = e.value.as_ref().unwrap();
         s.push_str(&format!(
-            "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, kind: {:?}",
+            "\n{:?}: parent: {:?}, call_site_ctxt: {:?}, def_site_ctxt: {:?}, kind: {:?}",
             id,
             expn_data.kind.file_id(),
-            expn_data.ctxt,
+            expn_data.call_site,
+            SyntaxContextId::ROOT, // FIXME expn_data.def_site,
             expn_data.kind.descr(),
         ));
     }
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 5d4f7dc146..a03d9a60d9 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -171,7 +171,8 @@ pub struct MacroCallLoc {
     pub def: MacroDefId,
     pub krate: CrateId,
     pub kind: MacroCallKind,
-    pub ctxt: SyntaxContextId,
+    // FIXME: Spans while relative to an anchor, are still rather unstable
+    pub call_site: Span,
 }
 impl_intern_value_trivial!(MacroCallLoc);
 
@@ -201,8 +202,6 @@ pub struct EagerCallInfo {
     /// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
     arg_id: MacroCallId,
     error: Option<ExpandError>,
-    /// TODO: Doc
-    span: Span,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -430,9 +429,9 @@ impl MacroDefId {
         db: &dyn ExpandDatabase,
         krate: CrateId,
         kind: MacroCallKind,
-        ctxt: SyntaxContextId,
+        call_site: Span,
     ) -> MacroCallId {
-        MacroCallLoc { def: self, krate, kind, ctxt }.intern(db)
+        MacroCallLoc { def: self, krate, kind, call_site }.intern(db)
     }
 
     pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
@@ -806,7 +805,7 @@ impl ExpansionInfo {
         let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
         let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
 
-        let (macro_arg, _, _) = db.macro_arg(macro_file.macro_call_id);
+        let (macro_arg, _) = db.macro_arg(macro_file.macro_call_id).value;
 
         let def = loc.def.ast_id().left().and_then(|id| {
             let def_tt = match id.to_node(db) {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 9796009cb4..02f9d5f31a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -697,7 +697,7 @@ impl<'db> SemanticsImpl<'db> {
             };
 
             // get mapped token in the include! macro file
-            let span = span::Span {
+            let span = span::SpanData {
                 range: token.text_range(),
                 anchor: span::SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
                 ctx: SyntaxContextId::ROOT,
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index ac7f071178..d946ecc1ca 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -1,7 +1,6 @@
 //! This module add real world mbe example for benchmark tests
 
 use rustc_hash::FxHashMap;
-use span::Span;
 use syntax::{
     ast::{self, HasName},
     AstNode, SmolStr,
@@ -10,7 +9,7 @@ use test_utils::{bench, bench_fixture, skip_slow_tests};
 
 use crate::{
     parser::{MetaVarKind, Op, RepeatKind, Separator},
-    syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanMap, DUMMY,
+    syntax_node_to_token_tree, DeclarativeMacro, DummyTestSpanData, DummyTestSpanMap, DUMMY,
 };
 
 #[test]
@@ -51,14 +50,14 @@ fn benchmark_expand_macro_rules() {
     assert_eq!(hash, 69413);
 }
 
-fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro<Span>> {
+fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro<DummyTestSpanData>> {
     macro_rules_fixtures_tt()
         .into_iter()
         .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true, true)))
         .collect()
 }
 
-fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<Span>> {
+fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<DummyTestSpanData>> {
     let fixture = bench_fixture::numerous_macro_rules();
     let source_file = ast::SourceFile::parse(&fixture).ok().unwrap();
 
@@ -80,8 +79,8 @@ fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<Span>> {
 
 /// Generate random invocation fixtures from rules
 fn invocation_fixtures(
-    rules: &FxHashMap<String, DeclarativeMacro<Span>>,
-) -> Vec<(String, tt::Subtree<Span>)> {
+    rules: &FxHashMap<String, DeclarativeMacro<DummyTestSpanData>>,
+) -> Vec<(String, tt::Subtree<DummyTestSpanData>)> {
     let mut seed = 123456789;
     let mut res = Vec::new();
 
@@ -129,8 +128,8 @@ fn invocation_fixtures(
     return res;
 
     fn collect_from_op(
-        op: &Op<Span>,
-        token_trees: &mut Vec<tt::TokenTree<Span>>,
+        op: &Op<DummyTestSpanData>,
+        token_trees: &mut Vec<tt::TokenTree<DummyTestSpanData>>,
         seed: &mut usize,
     ) {
         return match op {
@@ -222,19 +221,19 @@ fn invocation_fixtures(
             *seed = usize::wrapping_add(usize::wrapping_mul(*seed, a), c);
             *seed
         }
-        fn make_ident(ident: &str) -> tt::TokenTree<Span> {
+        fn make_ident(ident: &str) -> tt::TokenTree<DummyTestSpanData> {
             tt::Leaf::Ident(tt::Ident { span: DUMMY, text: SmolStr::new(ident) }).into()
         }
-        fn make_punct(char: char) -> tt::TokenTree<Span> {
+        fn make_punct(char: char) -> tt::TokenTree<DummyTestSpanData> {
             tt::Leaf::Punct(tt::Punct { span: DUMMY, char, spacing: tt::Spacing::Alone }).into()
         }
-        fn make_literal(lit: &str) -> tt::TokenTree<Span> {
+        fn make_literal(lit: &str) -> tt::TokenTree<DummyTestSpanData> {
             tt::Leaf::Literal(tt::Literal { span: DUMMY, text: SmolStr::new(lit) }).into()
         }
         fn make_subtree(
             kind: tt::DelimiterKind,
-            token_trees: Option<Vec<tt::TokenTree<Span>>>,
-        ) -> tt::TokenTree<Span> {
+            token_trees: Option<Vec<tt::TokenTree<DummyTestSpanData>>>,
+        ) -> tt::TokenTree<DummyTestSpanData> {
             tt::Subtree {
                 delimiter: tt::Delimiter { open: DUMMY, close: DUMMY, kind },
                 token_trees: token_trees.map(Vec::into_boxed_slice).unwrap_or_default(),
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 57d6082dd7..cb8f8dff50 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -41,30 +41,32 @@ impl<S: Span, SM: SpanMapper<S>> SpanMapper<S> for &SM {
 /// Dummy things for testing where spans don't matter.
 pub(crate) mod dummy_test_span_utils {
 
-    use span::{Span, SyntaxContextId};
-
     use super::*;
 
-    pub const DUMMY: Span = Span {
+    pub type DummyTestSpanData = span::SpanData<DummyTestSyntaxContext>;
+    pub const DUMMY: DummyTestSpanData = span::SpanData {
         range: TextRange::empty(TextSize::new(0)),
         anchor: span::SpanAnchor {
             file_id: span::FileId::BOGUS,
             ast_id: span::ROOT_ERASED_FILE_AST_ID,
         },
-        ctx: SyntaxContextId::ROOT,
+        ctx: DummyTestSyntaxContext,
     };
 
+    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
+    pub struct DummyTestSyntaxContext;
+
     pub struct DummyTestSpanMap;
 
-    impl SpanMapper<Span> for DummyTestSpanMap {
-        fn span_for(&self, range: syntax::TextRange) -> Span {
-            Span {
+    impl SpanMapper<span::SpanData<DummyTestSyntaxContext>> for DummyTestSpanMap {
+        fn span_for(&self, range: syntax::TextRange) -> span::SpanData<DummyTestSyntaxContext> {
+            span::SpanData {
                 range,
                 anchor: span::SpanAnchor {
                     file_id: span::FileId::BOGUS,
                     ast_id: span::ROOT_ERASED_FILE_AST_ID,
                 },
-                ctx: SyntaxContextId::ROOT,
+                ctx: DummyTestSyntaxContext,
             }
         }
     }
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 86ab652f8e..710ce7f8ac 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -234,6 +234,13 @@ impl Notification for EndRunTest {
     const METHOD: &'static str = "experimental/endRunTest";
 }
 
+pub enum AppendOutputToRunTest {}
+
+impl Notification for AppendOutputToRunTest {
+    type Params = String;
+    const METHOD: &'static str = "experimental/appendOutputToRunTest";
+}
+
 pub enum AbortRunTest {}
 
 impl Notification for AbortRunTest {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index e106a85c6e..ffe56e4143 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -799,6 +799,9 @@ impl GlobalState {
                 self.send_notification::<lsp_ext::EndRunTest>(());
                 self.test_run_session = None;
             }
+            flycheck::CargoTestMessage::Custom { text } => {
+                self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);
+            }
         }
     }
 
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs
index 6b849ce373..a29e5369a6 100644
--- a/crates/span/src/lib.rs
+++ b/crates/span/src/lib.rs
@@ -44,9 +44,6 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
 
 pub type Span = SpanData<SyntaxContextId>;
 
-/// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs
-/// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental
-/// friendly.
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct SpanData<Ctx> {
     /// The text range of this span, relative to the anchor.
@@ -87,7 +84,7 @@ impl<Ctx: Copy> SpanData<Ctx> {
 
 impl Span {
     #[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
-    pub const DUMMY: Self = Self {
+    pub const DUMMY: Self = SpanData {
         range: TextRange::empty(TextSize::new(0)),
         anchor: SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID },
         ctx: SyntaxContextId::ROOT,
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index af5b4e51ef..cf9ad5fe04 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp/ext.rs hash: 6bc140531b403717
+lsp/ext.rs hash: 61f485497d6e8e88
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -509,6 +509,13 @@ interface ChangeTestStateParams {
 }
 ```
 
+**Method:** `experimental/appendOutputToRunTest`
+
+**Notification:** `string`
+
+This notification is used for reporting messages independent of any single test and related to the run session
+in general, e.g. cargo compiling progress messages or warnings.
+
 ## Open External Documentation
 
 This request is sent from the client to the server to obtain web and local URL(s) for documentation related to the symbol under the cursor, if available.
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 31ac3d9413..ca8106371b 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -100,6 +100,9 @@ export const discoveredTests = new lc.NotificationType<DiscoverTestResults>(
 export const runTest = new lc.RequestType<RunTestParams, void, void>("experimental/runTest");
 export const abortRunTest = new lc.NotificationType0("experimental/abortRunTest");
 export const endRunTest = new lc.NotificationType0("experimental/endRunTest");
+export const appendOutputToRunTest = new lc.NotificationType<string>(
+    "experimental/appendOutputToRunTest",
+);
 export const changeTestState = new lc.NotificationType<ChangeTestStateParams>(
     "experimental/changeTestState",
 );


Test patch:
diff --git a/crates/flycheck/src/test_runner.rs b/crates/flycheck/src/test_runner.rs
index 6dac5899ee..31378716b3 100644
--- a/crates/flycheck/src/test_runner.rs
+++ b/crates/flycheck/src/test_runner.rs
@@ -28,19 +28,20 @@ pub enum CargoTestMessage {
     },
     Suite,
     Finished,
+    Custom {
+        text: String,
+    },
 }
 
 impl ParseFromLine for CargoTestMessage {
-    fn from_line(line: &str, error: &mut String) -> Option<Self> {
+    fn from_line(line: &str, _: &mut String) -> Option<Self> {
         let mut deserializer = serde_json::Deserializer::from_str(line);
         deserializer.disable_recursion_limit();
         if let Ok(message) = CargoTestMessage::deserialize(&mut deserializer) {
             return Some(message);
         }
 
-        error.push_str(line);
-        error.push('\n');
-        None
+        Some(CargoTestMessage::Custom { text: line.to_owned() })
     }
 
     fn from_eof() -> Option<Self> {
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 48da876ac1..b294d288ac 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -278,7 +278,7 @@ m!();
             // AstId: 2
             pub macro m2 { ... }
 
-            // AstId: 3, SyntaxContext: 0, ExpandTo: Items
+            // AstId: 3, Span: 0:3@0..1#0, ExpandTo: Items
             m!(...);
         "#]],
     );
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 23d8b023b8..63f211022c 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -33,7 +33,7 @@ m!(&k");
 "#,
         expect![[r#"
 macro_rules! m { ($i:literal) => {}; }
-/* error: expected literal */"#]],
+/* error: mismatched delimiters */"#]],
     );
 }
 
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index fb5797d6e5..362c189f6a 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
 macro_rules! m2 { ($x:ident) => {} }
 
 /* error: macro definition has parse errors */
-/* error: expected ident */
+/* error: mismatched delimiters */
 "#]],
     )
 }
diff --git a/crates/mbe/src/syntax_bridge/tests.rs b/crates/mbe/src/syntax_bridge/tests.rs
index a261b1d431..11d1a72879 100644
--- a/crates/mbe/src/syntax_bridge/tests.rs
+++ b/crates/mbe/src/syntax_bridge/tests.rs
@@ -1,5 +1,4 @@
 use rustc_hash::FxHashMap;
-use span::Span;
 use syntax::{ast, AstNode};
 use test_utils::extract_annotations;
 use tt::{
@@ -7,7 +6,7 @@ use tt::{
     Leaf, Punct, Spacing,
 };
 
-use crate::{syntax_node_to_token_tree, DummyTestSpanMap, DUMMY};
+use crate::{syntax_node_to_token_tree, DummyTestSpanData, DummyTestSpanMap, DUMMY};
 
 fn check_punct_spacing(fixture: &str) {
     let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
@@ -29,7 +28,7 @@ fn check_punct_spacing(fixture: &str) {
     while !cursor.eof() {
         while let Some(token_tree) = cursor.token_tree() {
             if let TokenTreeRef::Leaf(
-                Leaf::Punct(Punct { spacing, span: Span { range, .. }, .. }),
+                Leaf::Punct(Punct { spacing, span: DummyTestSpanData { range, .. }, .. }),
                 _,
             ) = token_tree
             {
diff --git a/editors/code/src/test_explorer.ts b/editors/code/src/test_explorer.ts
index 2f0b4d5b5c..ac4ffb1926 100644
--- a/editors/code/src/test_explorer.ts
+++ b/editors/code/src/test_explorer.ts
@@ -141,6 +141,12 @@ export const prepareTestExplorer = (
         }),
     );
 
+    ctx.pushClientCleanup(
+        client.onNotification(ra.appendOutputToRunTest, (output) => {
+            currentTestRun!.appendOutput(`${output}\r\n`);
+        }),
+    );
+
     ctx.pushClientCleanup(
         client.onNotification(ra.changeTestState, (results) => {
             const test = idToTestMap.get(results.testId)!;