Auto merge of #14584 - Veykril:macro-def-err, r=Veykril

internal: Report item-level macro expansion syntax errors
This commit is contained in:
bors 2023-04-16 17:40:20 +00:00
commit 697b335fda
22 changed files with 327 additions and 325 deletions

View file

@ -21,7 +21,7 @@ use limit::Limit;
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use profile::Count; use profile::Count;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr}; use syntax::{ast, AstPtr, Parse, SyntaxNode, SyntaxNodePtr};
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
@ -137,7 +137,8 @@ impl Expander {
&mut self, &mut self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
macro_call: ast::MacroCall, macro_call: ast::MacroCall,
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> { ) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
// FIXME: within_limit should support this, instead of us having to extract the error
let mut unresolved_macro_err = None; let mut unresolved_macro_err = None;
let result = self.within_limit(db, |this| { let result = self.within_limit(db, |this| {
@ -146,22 +147,13 @@ impl Expander {
let resolver = let resolver =
|path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it)); |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
let mut err = None; match macro_call.as_call_id_with_errors(db, this.def_map.krate(), resolver) {
let call_id = match macro_call.as_call_id_with_errors(
db,
this.def_map.krate(),
resolver,
&mut |e| {
err.get_or_insert(e);
},
) {
Ok(call_id) => call_id, Ok(call_id) => call_id,
Err(resolve_err) => { Err(resolve_err) => {
unresolved_macro_err = Some(resolve_err); unresolved_macro_err = Some(resolve_err);
return ExpandResult { value: None, err: None }; ExpandResult { value: None, err: None }
} }
}; }
ExpandResult { value: call_id.ok(), err }
}); });
if let Some(err) = unresolved_macro_err { if let Some(err) = unresolved_macro_err {
@ -175,37 +167,37 @@ impl Expander {
&mut self, &mut self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
call_id: MacroCallId, call_id: MacroCallId,
) -> ExpandResult<Option<(Mark, T)>> { ) -> ExpandResult<Option<(Mark, Parse<T>)>> {
self.within_limit(db, |_this| ExpandResult::ok(Some(call_id))) self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
} }
fn enter_expand_inner( fn enter_expand_inner(
db: &dyn DefDatabase, db: &dyn DefDatabase,
call_id: MacroCallId, call_id: MacroCallId,
mut err: Option<ExpandError>, mut error: Option<ExpandError>,
) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> { ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
if err.is_none() { let file_id = call_id.as_file();
err = db.macro_expand_error(call_id); let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id);
if error.is_none() {
error = err;
} }
let file_id = call_id.as_file(); let parse = match value {
Some(it) => it,
let raw_node = match db.parse_or_expand_with_err(file_id) {
// FIXME: report parse errors
Some(it) => it.syntax_node(),
None => { None => {
// Only `None` if the macro expansion produced no usable AST. // Only `None` if the macro expansion produced no usable AST.
if err.is_none() { if error.is_none() {
tracing::warn!("no error despite `parse_or_expand` failing"); tracing::warn!("no error despite `parse_or_expand` failing");
} }
return ExpandResult::only_err(err.unwrap_or_else(|| { return ExpandResult::only_err(error.unwrap_or_else(|| {
ExpandError::Other("failed to parse macro invocation".into()) ExpandError::Other("failed to parse macro invocation".into())
})); }));
} }
}; };
ExpandResult { value: Some((file_id, raw_node)), err } ExpandResult { value: Some(InFile::new(file_id, parse)), err: error }
} }
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
@ -267,7 +259,7 @@ impl Expander {
&mut self, &mut self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
op: F, op: F,
) -> ExpandResult<Option<(Mark, T)>> ) -> ExpandResult<Option<(Mark, Parse<T>)>>
where where
F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>, F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
{ {
@ -294,15 +286,15 @@ impl Expander {
}; };
Self::enter_expand_inner(db, call_id, err).map(|value| { Self::enter_expand_inner(db, call_id, err).map(|value| {
value.and_then(|(new_file_id, node)| { value.and_then(|InFile { file_id, value }| {
let node = T::cast(node)?; let parse = value.cast::<T>()?;
self.recursion_depth += 1; self.recursion_depth += 1;
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id); self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id); let old_file_id = std::mem::replace(&mut self.current_file_id, file_id);
let mark = let mark =
Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") }; Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
Some((mark, node)) Some((mark, parse))
}) })
}) })
} }

View file

@ -824,7 +824,11 @@ impl ExprCollector<'_> {
self.db.ast_id_map(self.expander.current_file_id), self.db.ast_id_map(self.expander.current_file_id),
); );
let id = collector(self, Some(expansion)); if record_diagnostics {
// FIXME: Report parse errors here
}
let id = collector(self, Some(expansion.tree()));
self.ast_id_map = prev_ast_id_map; self.ast_id_map = prev_ast_id_map;
self.expander.exit(self.db, mark); self.expander.exit(self.db, mark);
id id

View file

@ -7,7 +7,7 @@ use std::sync::Arc;
use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind}; use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
use intern::Interned; use intern::Interned;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::ast; use syntax::{ast, Parse};
use crate::{ use crate::{
attr::Attrs, attr::Attrs,
@ -604,13 +604,10 @@ impl<'a> AssocItemCollector<'a> {
continue 'attrs; continue 'attrs;
} }
} }
match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
ExpandResult { value: Some((mark, _)), .. } => { let res = self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
self.collect_macro_items(mark); self.collect_macro_items(res, &|| loc.kind.clone());
continue 'items; continue 'items;
}
ExpandResult { .. } => {}
}
} }
} }
@ -641,22 +638,23 @@ impl<'a> AssocItemCollector<'a> {
self.items.push((item.name.clone(), def.into())); self.items.push((item.name.clone(), def.into()));
} }
AssocItem::MacroCall(call) => { AssocItem::MacroCall(call) => {
if let Some(root) = let file_id = self.expander.current_file_id();
self.db.parse_or_expand_with_err(self.expander.current_file_id()) let root = self.db.parse_or_expand(file_id);
{ if let Some(root) = root {
// FIXME: report parse errors
let root = root.syntax_node();
let call = &item_tree[call]; let call = &item_tree[call];
let ast_id_map = self.db.ast_id_map(self.expander.current_file_id()); let ast_id_map = self.db.ast_id_map(file_id);
let call = ast_id_map.get(call.ast_id).to_node(&root); let macro_call = ast_id_map.get(call.ast_id).to_node(&root);
let _cx = let _cx = stdx::panic_context::enter(format!(
stdx::panic_context::enter(format!("collect_items MacroCall: {call}")); "collect_items MacroCall: {macro_call}"
let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call); ));
if let Ok(res) =
if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res { self.expander.enter_expand::<ast::MacroItems>(self.db, macro_call)
self.collect_macro_items(mark); {
self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
ast_id: InFile::new(file_id, call.ast_id),
expand_to: hir_expand::ExpandTo::Items,
});
} }
} }
} }
@ -664,7 +662,28 @@ impl<'a> AssocItemCollector<'a> {
} }
} }
fn collect_macro_items(&mut self, mark: Mark) { fn collect_macro_items(
&mut self,
ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
) {
let Some((mark, parse)) = value else { return };
if let Some(err) = err {
self.inactive_diagnostics.push(DefDiagnostic::macro_error(
self.module_id.local_id,
error_call_kind(),
err.to_string(),
));
}
if let errors @ [_, ..] = parse.errors() {
self.inactive_diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
self.module_id.local_id,
error_call_kind(),
errors.into(),
));
}
let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None); let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
let item_tree = tree_id.item_tree(self.db); let item_tree = tree_id.item_tree(self.db);
let iter: SmallVec<[_; 2]> = let iter: SmallVec<[_; 2]> =

View file

@ -350,7 +350,7 @@ impl GenericParams {
match expander.enter_expand::<ast::Type>(db, macro_call) { match expander.enter_expand::<ast::Type>(db, macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
let ctx = expander.ctx(db); let ctx = expander.ctx(db);
let type_ref = TypeRef::from_ast(&ctx, expanded); let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
self.fill_implicit_impl_trait_args(db, expander, &type_ref); self.fill_implicit_impl_trait_args(db, expander, &type_ref);
expander.exit(db, mark); expander.exit(db, mark);
} }

View file

@ -101,6 +101,7 @@ pub struct ItemTree {
top_level: SmallVec<[ModItem; 1]>, top_level: SmallVec<[ModItem; 1]>,
attrs: FxHashMap<AttrOwner, RawAttrs>, attrs: FxHashMap<AttrOwner, RawAttrs>,
// FIXME: Remove this indirection, an item tree is almost always non-empty?
data: Option<Box<ItemTreeData>>, data: Option<Box<ItemTreeData>>,
} }

View file

@ -65,11 +65,11 @@ use hir_expand::{
builtin_attr_macro::BuiltinAttrExpander, builtin_attr_macro::BuiltinAttrExpander,
builtin_derive_macro::BuiltinDeriveExpander, builtin_derive_macro::BuiltinDeriveExpander,
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander}, builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, eager::expand_eager_macro,
hygiene::Hygiene, hygiene::Hygiene,
proc_macro::ProcMacroExpander, proc_macro::ProcMacroExpander,
AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
MacroDefKind, UnresolvedMacro, MacroDefId, MacroDefKind, UnresolvedMacro,
}; };
use item_tree::ExternBlock; use item_tree::ExternBlock;
use la_arena::Idx; use la_arena::Idx;
@ -795,7 +795,7 @@ pub trait AsMacroCall {
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> { ) -> Option<MacroCallId> {
self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok() self.as_call_id_with_errors(db, krate, resolver).ok()?.value
} }
fn as_call_id_with_errors( fn as_call_id_with_errors(
@ -803,8 +803,7 @@ pub trait AsMacroCall {
db: &dyn db::DefDatabase, db: &dyn db::DefDatabase,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
error_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
} }
impl AsMacroCall for InFile<&ast::MacroCall> { impl AsMacroCall for InFile<&ast::MacroCall> {
@ -813,30 +812,23 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
db: &dyn db::DefDatabase, db: &dyn db::DefDatabase,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
mut error_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
let expands_to = hir_expand::ExpandTo::from_call_site(self.value); let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
let h = Hygiene::new(db.upcast(), self.file_id); let h = Hygiene::new(db.upcast(), self.file_id);
let path = let path =
self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h)); self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
let path = match error_sink let Some(path) = path else {
.option(path, || ExpandError::Other("malformed macro invocation".into())) return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
{
Ok(path) => path,
Err(error) => {
return Ok(Err(error));
}
}; };
macro_call_as_call_id( macro_call_as_call_id_(
db, db,
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path), &AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
expands_to, expands_to,
krate, krate,
resolver, resolver,
error_sink,
) )
} }
} }
@ -860,21 +852,33 @@ fn macro_call_as_call_id(
expand_to: ExpandTo, expand_to: ExpandTo,
krate: CrateId, krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
error_sink: &mut dyn FnMut(ExpandError), ) -> Result<Option<MacroCallId>, UnresolvedMacro> {
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
}
fn macro_call_as_call_id_(
db: &dyn db::DefDatabase,
call: &AstIdWithPath<ast::MacroCall>,
expand_to: ExpandTo,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let def = let def =
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
let res = if let MacroDefKind::BuiltInEager(..) = def.kind { let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)? expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver)?
} else { } else {
Ok(def.as_lazy_macro( ExpandResult {
db.upcast(), value: Some(def.as_lazy_macro(
krate, db.upcast(),
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to }, krate,
)) MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
)),
err: None,
}
}; };
Ok(res) Ok(res)
} }

View file

@ -125,21 +125,15 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) { for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
let macro_call = InFile::new(source.file_id, &macro_call); let macro_call = InFile::new(source.file_id, &macro_call);
let mut error = None; let res = macro_call
let macro_call_id = macro_call .as_call_id_with_errors(&db, krate, |path| {
.as_call_id_with_errors( resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
&db, })
krate,
|path| {
resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
},
&mut |err| error = Some(err),
)
.unwrap()
.unwrap(); .unwrap();
let macro_call_id = res.value.unwrap();
let macro_file = MacroFile { macro_call_id }; let macro_file = MacroFile { macro_call_id };
let mut expansion_result = db.parse_macro_expansion(macro_file); let mut expansion_result = db.parse_macro_expansion(macro_file);
expansion_result.err = expansion_result.err.or(error); expansion_result.err = expansion_result.err.or(res.err);
expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id))); expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id)));
} }

View file

@ -16,8 +16,8 @@ use hir_expand::{
builtin_fn_macro::find_builtin_macro, builtin_fn_macro::find_builtin_macro,
name::{name, AsName, Name}, name::{name, AsName, Name},
proc_macro::ProcMacroExpander, proc_macro::ProcMacroExpander,
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
MacroDefKind, MacroDefId, MacroDefKind,
}; };
use itertools::{izip, Itertools}; use itertools::{izip, Itertools};
use la_arena::Idx; use la_arena::Idx;
@ -1116,10 +1116,10 @@ impl DefCollector<'_> {
*expand_to, *expand_to,
self.def_map.krate, self.def_map.krate,
resolver_def_id, resolver_def_id,
&mut |_err| (),
); );
if let Ok(Ok(call_id)) = call_id { if let Ok(Some(call_id)) = call_id {
push_resolved(directive, call_id); push_resolved(directive, call_id);
res = ReachedFixedPoint::No; res = ReachedFixedPoint::No;
return false; return false;
} }
@ -1355,26 +1355,30 @@ impl DefCollector<'_> {
let file_id = macro_call_id.as_file(); let file_id = macro_call_id.as_file();
// First, fetch the raw expansion result for purposes of error reporting. This goes through // First, fetch the raw expansion result for purposes of error reporting. This goes through
// `macro_expand_error` to avoid depending on the full expansion result (to improve // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
// incrementality). // incrementality).
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id); let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
let err = self.db.macro_expand_error(macro_call_id);
if let Some(err) = err { if let Some(err) = err {
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
let diag = match err { let diag = match err {
// why is this reported here?
hir_expand::ExpandError::UnresolvedProcMacro(krate) => { hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
always!(krate == loc.def.krate); always!(krate == loc.def.krate);
// Missing proc macros are non-fatal, so they are handled specially.
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate) DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
} }
_ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()), _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
}; };
self.def_map.diagnostics.push(diag); self.def_map.diagnostics.push(diag);
} }
if let Some(errors) = value {
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
self.def_map.diagnostics.push(diag);
}
// Then, fetch and process the item tree. This will reuse the expansion result from above. // Then, fetch and process the item tree. This will reuse the expansion result from above.
let item_tree = self.db.file_item_tree(file_id); let item_tree = self.db.file_item_tree(file_id);
// FIXME: report parse errors for the macro expansion here
let mod_dir = self.mod_dirs[&module_id].clone(); let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector { ModCollector {
@ -1396,6 +1400,7 @@ impl DefCollector<'_> {
for directive in &self.unresolved_macros { for directive in &self.unresolved_macros {
match &directive.kind { match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => { MacroDirectiveKind::FnLike { ast_id, expand_to } => {
// 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( let macro_call_as_call_id = macro_call_as_call_id(
self.db, self.db,
ast_id, ast_id,
@ -1414,7 +1419,6 @@ impl DefCollector<'_> {
.take_macros() .take_macros()
.map(|it| macro_id_to_def_id(self.db, it)) .map(|it| macro_id_to_def_id(self.db, it))
}, },
&mut |_| (),
); );
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id { if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
@ -2112,8 +2116,7 @@ impl ModCollector<'_, '_> {
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path)); let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
// Case 1: try to resolve in legacy scope and expand macro_rules // Case 1: try to resolve in legacy scope and expand macro_rules
let mut error = None; if let Ok(res) = macro_call_as_call_id(
match macro_call_as_call_id(
self.def_collector.db, self.def_collector.db,
&ast_id, &ast_id,
mac.expand_to, mac.expand_to,
@ -2133,41 +2136,19 @@ impl ModCollector<'_, '_> {
) )
}) })
}, },
&mut |err| {
error.get_or_insert(err);
},
) { ) {
Ok(Ok(macro_call_id)) => { // Legacy macros need to be expanded immediately, so that any macros they produce
// Legacy macros need to be expanded immediately, so that any macros they produce // are in scope.
// are in scope. if let Some(val) = res {
self.def_collector.collect_macro_expansion( self.def_collector.collect_macro_expansion(
self.module_id, self.module_id,
macro_call_id, val,
self.macro_depth + 1, self.macro_depth + 1,
container, container,
); );
if let Some(err) = error {
self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
self.module_id,
MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
err.to_string(),
));
}
return;
} }
Ok(Err(_)) => {
// Built-in macro failed eager expansion.
self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( return;
self.module_id,
MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
error.unwrap().to_string(),
));
return;
}
Err(UnresolvedMacro { .. }) => (),
} }
// Case 2: resolve in module scope, expand during name resolution. // Case 2: resolve in module scope, expand during name resolution.

View file

@ -4,7 +4,10 @@ use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions}; use cfg::{CfgExpr, CfgOptions};
use hir_expand::{attrs::AttrId, MacroCallKind}; use hir_expand::{attrs::AttrId, MacroCallKind};
use la_arena::Idx; use la_arena::Idx;
use syntax::ast::{self, AnyHasAttrs}; use syntax::{
ast::{self, AnyHasAttrs},
SyntaxError,
};
use crate::{ use crate::{
item_tree::{self, ItemTreeId}, item_tree::{self, ItemTreeId},
@ -29,6 +32,8 @@ pub enum DefDiagnosticKind {
MacroError { ast: MacroCallKind, message: String }, MacroError { ast: MacroCallKind, message: String },
MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> }, UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize }, InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
@ -91,7 +96,7 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } } Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
} }
pub(super) fn macro_error( pub(crate) fn macro_error(
container: LocalModuleId, container: LocalModuleId,
ast: MacroCallKind, ast: MacroCallKind,
message: String, message: String,
@ -99,6 +104,20 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } } Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
} }
pub(crate) fn macro_expansion_parse_error(
container: LocalModuleId,
ast: MacroCallKind,
errors: &[SyntaxError],
) -> Self {
Self {
in_module: container,
kind: DefDiagnosticKind::MacroExpansionParseError {
ast,
errors: errors.to_vec().into_boxed_slice(),
},
}
}
pub(super) fn unresolved_macro_call( pub(super) fn unresolved_macro_call(
container: LocalModuleId, container: LocalModuleId,
ast: MacroCallKind, ast: MacroCallKind,

View file

@ -140,7 +140,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 6); assert_eq!(n_recalculated_item_trees, 6);
let n_reparsed_macros = let n_reparsed_macros =
events.iter().filter(|it| it.contains("parse_macro_expansion")).count(); events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 3); assert_eq!(n_reparsed_macros, 3);
} }

View file

@ -9,7 +9,7 @@ use mbe::syntax_node_to_token_tree;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ use syntax::{
ast::{self, HasAttrs, HasDocComments}, ast::{self, HasAttrs, HasDocComments},
AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T, AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
}; };
use crate::{ use crate::{
@ -100,7 +100,10 @@ pub trait ExpandDatabase: SourceDatabase {
#[salsa::transparent] #[salsa::transparent]
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>; fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
#[salsa::transparent] #[salsa::transparent]
fn parse_or_expand_with_err(&self, file_id: HirFileId) -> Option<Parse<SyntaxNode>>; fn parse_or_expand_with_err(
&self,
file_id: HirFileId,
) -> ExpandResult<Option<Parse<SyntaxNode>>>;
/// Implementation for the macro case. /// Implementation for the macro case.
fn parse_macro_expansion( fn parse_macro_expansion(
&self, &self,
@ -129,15 +132,18 @@ pub trait ExpandDatabase: SourceDatabase {
/// just fetches procedural ones. /// just fetches procedural ones.
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>; fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
/// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory) /// Expand macro call to a token tree.
fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>; fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
/// Special case of the previous query for procedural macros. We can't LRU /// Special case of the previous query for procedural macros. We can't LRU
/// proc macros, since they are not deterministic in general, and /// proc macros, since they are not deterministic in general, and
/// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
/// heroically debugged this once! /// heroically debugged this once!
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>; fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
/// Firewall query that returns the error from the `macro_expand` query. /// Firewall query that returns the errors from the `parse_macro_expansion` query.
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>; fn parse_macro_expansion_error(
&self,
macro_call: MacroCallId,
) -> ExpandResult<Option<Box<[SyntaxError]>>>;
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>; fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
} }
@ -262,11 +268,11 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<Syntax
fn parse_or_expand_with_err( fn parse_or_expand_with_err(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
file_id: HirFileId, file_id: HirFileId,
) -> Option<Parse<SyntaxNode>> { ) -> ExpandResult<Option<Parse<SyntaxNode>>> {
match file_id.repr() { match file_id.repr() {
HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).to_syntax()), HirFileIdRepr::FileId(file_id) => ExpandResult::ok(Some(db.parse(file_id).to_syntax())),
HirFileIdRepr::MacroFile(macro_file) => { HirFileIdRepr::MacroFile(macro_file) => {
db.parse_macro_expansion(macro_file).value.map(|(parse, _)| parse) db.parse_macro_expansion(macro_file).map(|it| it.map(|(parse, _)| parse))
} }
} }
} }
@ -279,25 +285,28 @@ fn parse_macro_expansion(
let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id); let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id);
if let Some(err) = &err { if let Some(err) = &err {
// Note: if tracing::enabled!(tracing::Level::DEBUG) {
// The final goal we would like to make all parse_macro success, // Note:
// such that the following log will not call anyway. // The final goal we would like to make all parse_macro success,
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); // such that the following log will not call anyway.
let node = loc.kind.to_node(db); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let node = loc.kind.to_node(db);
// collect parent information for warning log // collect parent information for warning log
let parents = let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db)) it.file_id.call_node(db)
.map(|n| format!("{:#}", n.value)) })
.collect::<Vec<_>>() .map(|n| format!("{:#}", n.value))
.join("\n"); .collect::<Vec<_>>()
.join("\n");
tracing::debug!( tracing::debug!(
"fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}", "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
err, err,
node.value, node.value,
parents parents
); );
}
} }
let tt = match value { let tt = match value {
Some(tt) => tt, Some(tt) => tt,
@ -442,14 +451,14 @@ fn macro_def(
fn macro_expand( fn macro_expand(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
id: MacroCallId, id: MacroCallId,
// FIXME: Remove the OPtion if possible
) -> ExpandResult<Option<Arc<tt::Subtree>>> { ) -> ExpandResult<Option<Arc<tt::Subtree>>> {
let _p = profile::span("macro_expand"); let _p = profile::span("macro_expand");
let loc: MacroCallLoc = db.lookup_intern_macro_call(id); let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
if let Some(eager) = &loc.eager { if let Some(eager) = &loc.eager {
return ExpandResult { return ExpandResult {
value: Some(eager.arg_or_expansion.clone()), value: Some(eager.arg_or_expansion.clone()),
// FIXME: There could be errors here! err: eager.error.clone(),
err: None,
}; };
} }
@ -466,7 +475,8 @@ fn macro_expand(
Ok(it) => it, Ok(it) => it,
// FIXME: This is weird -- we effectively report macro *definition* // FIXME: This is weird -- we effectively report macro *definition*
// errors lazily, when we try to expand the macro. Instead, they should // errors lazily, when we try to expand the macro. Instead, they should
// be reported at the definition site (when we construct a def map). // 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)
Err(err) => { Err(err) => {
return ExpandResult::only_err(ExpandError::Other( return ExpandResult::only_err(ExpandError::Other(
format!("invalid macro definition: {err}").into(), format!("invalid macro definition: {err}").into(),
@ -492,8 +502,12 @@ fn macro_expand(
ExpandResult { value: Some(Arc::new(tt)), err } ExpandResult { value: Some(Arc::new(tt)), err }
} }
fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> { fn parse_macro_expansion_error(
db.macro_expand(macro_call).err db: &dyn ExpandDatabase,
macro_call_id: MacroCallId,
) -> ExpandResult<Option<Box<[SyntaxError]>>> {
db.parse_macro_expansion(MacroFile { macro_call_id })
.map(|it| it.map(|(it, _)| it.errors().to_vec().into_boxed_slice()))
} }
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> { fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {

View file

@ -21,7 +21,7 @@
use std::sync::Arc; use std::sync::Arc;
use base_db::CrateId; use base_db::CrateId;
use syntax::{ted, SyntaxNode}; use syntax::{ted, Parse, SyntaxNode};
use crate::{ use crate::{
ast::{self, AstNode}, ast::{self, AstNode},
@ -32,77 +32,16 @@ use crate::{
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro, MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
}; };
#[derive(Debug)]
pub struct ErrorEmitted {
_private: (),
}
pub trait ErrorSink {
fn emit(&mut self, err: ExpandError);
fn option<T>(
&mut self,
opt: Option<T>,
error: impl FnOnce() -> ExpandError,
) -> Result<T, ErrorEmitted> {
match opt {
Some(it) => Ok(it),
None => {
self.emit(error());
Err(ErrorEmitted { _private: () })
}
}
}
fn option_with<T>(
&mut self,
opt: impl FnOnce() -> Option<T>,
error: impl FnOnce() -> ExpandError,
) -> Result<T, ErrorEmitted> {
self.option(opt(), error)
}
fn result<T>(&mut self, res: Result<T, ExpandError>) -> Result<T, ErrorEmitted> {
match res {
Ok(it) => Ok(it),
Err(e) => {
self.emit(e);
Err(ErrorEmitted { _private: () })
}
}
}
fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
match (res.value, res.err) {
(None, Some(err)) => {
self.emit(err);
Err(ErrorEmitted { _private: () })
}
(Some(value), opt_err) => {
if let Some(err) = opt_err {
self.emit(err);
}
Ok(value)
}
(None, None) => unreachable!("`ExpandResult` without value or error"),
}
}
}
impl ErrorSink for &'_ mut dyn FnMut(ExpandError) {
fn emit(&mut self, err: ExpandError) {
self(err);
}
}
pub fn expand_eager_macro( pub fn expand_eager_macro(
db: &dyn ExpandDatabase, db: &dyn ExpandDatabase,
krate: CrateId, krate: CrateId,
macro_call: InFile<ast::MacroCall>, macro_call: InFile<ast::MacroCall>,
def: MacroDefId, def: MacroDefId,
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
diagnostic_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { let MacroDefKind::BuiltInEager(eager, _) = def.kind else {
panic!("called `expand_eager_macro` on non-eager macro def {def:?}")
};
let hygiene = Hygiene::new(db, macro_call.file_id); let hygiene = Hygiene::new(db, macro_call.file_id);
let parsed_args = macro_call let parsed_args = macro_call
.value .value
@ -121,48 +60,44 @@ pub fn expand_eager_macro(
let arg_id = db.intern_macro_call(MacroCallLoc { let arg_id = db.intern_macro_call(MacroCallLoc {
def, def,
krate, krate,
eager: Some(EagerCallInfo { eager: Some(Box::new(EagerCallInfo {
arg_or_expansion: Arc::new(parsed_args.clone()), arg_or_expansion: Arc::new(parsed_args.clone()),
included_file: None, included_file: None,
}), error: None,
})),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr }, 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 parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
let result = match eager_macro_recur( let ExpandResult { value, mut err } = eager_macro_recur(
db, db,
&hygiene, &hygiene,
InFile::new(arg_id.as_file(), parsed_args.syntax_node()), InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
krate, krate,
resolver, resolver,
diagnostic_sink, )?;
) { let Some(value ) = value else {
Ok(Ok(it)) => it, return Ok(ExpandResult { value: None, err })
Ok(Err(err)) => return Ok(Err(err)),
Err(err) => return Err(err),
}; };
let subtree = to_subtree(&result); let subtree = to_subtree(&value);
if let MacroDefKind::BuiltInEager(eager, _) = def.kind { let res = eager.expand(db, arg_id, &subtree);
let res = eager.expand(db, arg_id, &subtree); if err.is_none() {
if let Some(err) = res.err { err = res.err;
diagnostic_sink(err);
}
let loc = MacroCallLoc {
def,
krate,
eager: Some(EagerCallInfo {
arg_or_expansion: Arc::new(res.value.subtree),
included_file: res.value.included_file,
}),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
};
Ok(Ok(db.intern_macro_call(loc)))
} else {
panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
} }
let loc = MacroCallLoc {
def,
krate,
eager: Some(Box::new(EagerCallInfo {
arg_or_expansion: Arc::new(res.value.subtree),
included_file: res.value.included_file,
error: err.clone(),
})),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
};
Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
} }
fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree { fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
@ -176,7 +111,7 @@ fn lazy_expand(
def: &MacroDefId, def: &MacroDefId,
macro_call: InFile<ast::MacroCall>, macro_call: InFile<ast::MacroCall>,
krate: CrateId, krate: CrateId,
) -> ExpandResult<Option<InFile<SyntaxNode>>> { ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
let expand_to = ExpandTo::from_call_site(&macro_call.value); let expand_to = ExpandTo::from_call_site(&macro_call.value);
@ -186,13 +121,8 @@ fn lazy_expand(
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to }, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
); );
let err = db.macro_expand_error(id); db.parse_or_expand_with_err(id.as_file())
let value = .map(|parse| parse.map(|parse| InFile::new(id.as_file(), parse)))
db.parse_or_expand_with_err(id.as_file()).map(|node| InFile::new(id.as_file(), node));
// FIXME: report parse errors
let value = value.map(|it| it.map(|it| it.syntax_node()));
ExpandResult { value, err }
} }
fn eager_macro_recur( fn eager_macro_recur(
@ -201,23 +131,25 @@ fn eager_macro_recur(
curr: InFile<SyntaxNode>, curr: InFile<SyntaxNode>,
krate: CrateId, krate: CrateId,
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>, macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
mut diagnostic_sink: &mut dyn FnMut(ExpandError), ) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
let original = curr.value.clone_for_update(); let original = curr.value.clone_for_update();
let children = original.descendants().filter_map(ast::MacroCall::cast); let children = original.descendants().filter_map(ast::MacroCall::cast);
let mut replacements = Vec::new(); let mut replacements = Vec::new();
// Note: We only report a single error inside of eager expansions
let mut error = None;
// Collect replacement // Collect replacement
for child in children { for child in children {
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) { let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?, Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
None => { None => {
diagnostic_sink(ExpandError::Other("malformed macro invocation".into())); error = Some(ExpandError::Other("malformed macro invocation".into()));
continue; continue;
} }
}; };
let insert = match def.kind { let ExpandResult { value, err } = match def.kind {
MacroDefKind::BuiltInEager(..) => { MacroDefKind::BuiltInEager(..) => {
let id = match expand_eager_macro( let id = match expand_eager_macro(
db, db,
@ -225,45 +157,55 @@ fn eager_macro_recur(
curr.with_value(child.clone()), curr.with_value(child.clone()),
def, def,
macro_resolver, macro_resolver,
diagnostic_sink,
) { ) {
Ok(Ok(it)) => it, Ok(it) => it,
Ok(Err(err)) => return Ok(Err(err)),
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
db.parse_or_expand(id.as_file()) id.map(|call| {
.expect("successful macro expansion should be parseable") call.and_then(|call| db.parse_or_expand(call.as_file()))
.clone_for_update() .map(|it| it.clone_for_update())
})
} }
MacroDefKind::Declarative(_) MacroDefKind::Declarative(_)
| MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltIn(..)
| MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInAttr(..)
| MacroDefKind::BuiltInDerive(..) | MacroDefKind::BuiltInDerive(..)
| MacroDefKind::ProcMacro(..) => { | MacroDefKind::ProcMacro(..) => {
let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate); let ExpandResult { value, err } =
let val = match diagnostic_sink.expand_result_option(res) { lazy_expand(db, &def, curr.with_value(child.clone()), krate);
Ok(it) => it,
Err(err) => return Ok(Err(err)),
};
// replace macro inside match value {
let hygiene = Hygiene::new(db, val.file_id); Some(val) => {
match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) { // replace macro inside
Ok(Ok(it)) => it, let hygiene = Hygiene::new(db, val.file_id);
Ok(Err(err)) => return Ok(Err(err)), let ExpandResult { value, err: error } = eager_macro_recur(
Err(err) => return Err(err), db,
&hygiene,
// FIXME: We discard parse errors here
val.map(|it| it.syntax_node()),
krate,
macro_resolver,
)?;
let err = if err.is_none() { error } else { err };
ExpandResult { value, err }
}
None => ExpandResult { value: None, err },
} }
} }
}; };
if err.is_some() {
error = err;
}
// check if the whole original syntax is replaced // check if the whole original syntax is replaced
if child.syntax() == &original { if child.syntax() == &original {
return Ok(Ok(insert)); return Ok(ExpandResult { value, err: error });
} }
replacements.push((child, insert)); if let Some(insert) = value {
replacements.push((child, insert));
}
} }
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
Ok(Ok(original)) Ok(ExpandResult { value: Some(original), err: error })
} }

View file

@ -52,7 +52,7 @@ use crate::{
pub type ExpandResult<T> = ValueResult<T, ExpandError>; pub type ExpandResult<T> = ValueResult<T, ExpandError>;
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError { pub enum ExpandError {
UnresolvedProcMacro(CrateId), UnresolvedProcMacro(CrateId),
Mbe(mbe::ExpandError), Mbe(mbe::ExpandError),
@ -114,7 +114,7 @@ impl_intern_key!(MacroCallId);
pub struct MacroCallLoc { pub struct MacroCallLoc {
pub def: MacroDefId, pub def: MacroDefId,
pub(crate) krate: CrateId, pub(crate) krate: CrateId,
eager: Option<EagerCallInfo>, eager: Option<Box<EagerCallInfo>>,
pub kind: MacroCallKind, pub kind: MacroCallKind,
} }
@ -141,6 +141,7 @@ struct EagerCallInfo {
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro! /// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
arg_or_expansion: Arc<tt::Subtree>, arg_or_expansion: Arc<tt::Subtree>,
included_file: Option<(FileId, TokenMap)>, included_file: Option<(FileId, TokenMap)>,
error: Option<ExpandError>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -206,8 +207,8 @@ impl HirFileId {
HirFileIdRepr::FileId(id) => break id, HirFileIdRepr::FileId(id) => break id,
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => { HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
file_id = match loc.eager { file_id = match loc.eager.as_deref() {
Some(EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(), Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
_ => loc.kind.file_id(), _ => loc.kind.file_id(),
}; };
} }
@ -320,7 +321,7 @@ impl HirFileId {
match self.macro_file() { match self.macro_file() {
Some(macro_file) => { Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id); let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.eager, Some(EagerCallInfo { included_file: Some(..), .. })) matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. }))
} }
_ => false, _ => false,
} }

View file

@ -381,7 +381,8 @@ impl<'a> TyLoweringContext<'a> {
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) { match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => { Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
let ctx = expander.ctx(self.db.upcast()); let ctx = expander.ctx(self.db.upcast());
let type_ref = TypeRef::from_ast(&ctx, expanded); // FIXME: Report syntax errors in expansion here
let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
drop(expander); drop(expander);
let ty = self.lower_ty(&type_ref); let ty = self.lower_ty(&type_ref);

View file

@ -6,8 +6,8 @@
pub use hir_def::db::*; pub use hir_def::db::*;
pub use hir_expand::db::{ pub use hir_expand::db::{
AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery, AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery,
InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery, InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery,
MacroExpandQuery, ParseMacroExpansionQuery, ParseMacroExpansionQuery,
}; };
pub use hir_ty::db::*; pub use hir_ty::db::*;

View file

@ -10,7 +10,7 @@ use cfg::{CfgExpr, CfgOptions};
use either::Either; use either::Either;
use hir_def::path::ModPath; use hir_def::path::ModPath;
use hir_expand::{name::Name, HirFileId, InFile}; use hir_expand::{name::Name, HirFileId, InFile};
use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
use crate::{AssocItem, Field, Local, MacroKind, Type}; use crate::{AssocItem, Field, Local, MacroKind, Type};
@ -38,8 +38,9 @@ diagnostics![
IncorrectCase, IncorrectCase,
InvalidDeriveTarget, InvalidDeriveTarget,
IncoherentImpl, IncoherentImpl,
MacroError,
MacroDefError, MacroDefError,
MacroError,
MacroExpansionParseError,
MalformedDerive, MalformedDerive,
MismatchedArgCount, MismatchedArgCount,
MissingFields, MissingFields,
@ -132,6 +133,13 @@ pub struct MacroError {
pub message: String, pub message: String,
} }
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MacroExpansionParseError {
pub node: InFile<SyntaxNodePtr>,
pub precise_location: Option<TextRange>,
pub errors: Box<[SyntaxError]>,
}
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub struct MacroDefError { pub struct MacroDefError {
pub node: InFile<AstPtr<ast::Macro>>, pub node: InFile<AstPtr<ast::Macro>>,

View file

@ -87,12 +87,12 @@ pub use crate::{
attrs::{HasAttrs, Namespace}, attrs::{HasAttrs, Namespace},
diagnostics::{ diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MalformedDerive, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap,
UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel,
UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
UnresolvedModule, UnresolvedProcMacro, UnusedMut, UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@ -753,7 +753,6 @@ fn emit_def_diagnostic_(
.into(), .into(),
); );
} }
DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => { DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db); let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db);
acc.push( acc.push(
@ -761,7 +760,6 @@ fn emit_def_diagnostic_(
.into(), .into(),
); );
} }
DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db); let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push( acc.push(
@ -774,12 +772,16 @@ fn emit_def_diagnostic_(
.into(), .into(),
); );
} }
DefDiagnosticKind::MacroError { ast, message } => { DefDiagnosticKind::MacroError { ast, message } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db); let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push(MacroError { node, precise_location, message: message.clone() }.into()); acc.push(MacroError { node, precise_location, message: message.clone() }.into());
} }
DefDiagnosticKind::MacroExpansionParseError { ast, errors } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push(
MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(),
);
}
DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
let node = ast.to_node(db.upcast()); let node = ast.to_node(db.upcast());
// Must have a name, otherwise we wouldn't emit it. // Must have a name, otherwise we wouldn't emit it.

View file

@ -80,7 +80,6 @@ impl RootDatabase {
hir::db::MacroDefQuery hir::db::MacroDefQuery
hir::db::MacroExpandQuery hir::db::MacroExpandQuery
hir::db::ExpandProcMacroQuery hir::db::ExpandProcMacroQuery
hir::db::MacroExpandErrorQuery
hir::db::HygieneFrameQuery hir::db::HygieneFrameQuery
// DefDatabase // DefDatabase

View file

@ -152,7 +152,6 @@ impl RootDatabase {
let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP); let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity); base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity); hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
} }
pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) { pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
@ -167,12 +166,6 @@ impl RootDatabase {
.copied() .copied()
.unwrap_or(base_db::DEFAULT_LRU_CAP), .unwrap_or(base_db::DEFAULT_LRU_CAP),
); );
hir_db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(
lru_capacities
.get(stringify!(MacroExpandQuery))
.copied()
.unwrap_or(base_db::DEFAULT_LRU_CAP),
);
macro_rules! update_lru_capacity_per_query { macro_rules! update_lru_capacity_per_query {
($( $module:ident :: $query:ident )*) => {$( ($( $module:ident :: $query:ident )*) => {$(
@ -201,7 +194,6 @@ impl RootDatabase {
hir_db::MacroDefQuery hir_db::MacroDefQuery
// hir_db::MacroExpandQuery // hir_db::MacroExpandQuery
hir_db::ExpandProcMacroQuery hir_db::ExpandProcMacroQuery
hir_db::MacroExpandErrorQuery
hir_db::HygieneFrameQuery hir_db::HygieneFrameQuery
// DefDatabase // DefDatabase

View file

@ -238,6 +238,22 @@ fn f() {
//^^^ error: invalid macro definition: expected subtree //^^^ error: invalid macro definition: expected subtree
} }
"#,
)
}
#[test]
fn expansion_syntax_diagnostic() {
check_diagnostics(
r#"
macro_rules! foo {
() => { struct; };
}
fn f() {
foo!();
//^^^ error: Syntax Error in Expansion: expected a name
}
"#, "#,
) )
} }

View file

@ -265,6 +265,19 @@ pub fn diagnostics(
AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d),
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
AnyDiagnostic::MacroExpansionParseError(d) => {
res.extend(d.errors.iter().take(32).map(|err| {
{
Diagnostic::new(
"syntax-error",
format!("Syntax Error in Expansion: {err}"),
ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
)
}
.experimental()
}));
continue;
},
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),

View file

@ -69,7 +69,7 @@ impl fmt::Display for ParseError {
} }
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError { pub enum ExpandError {
BindingError(Box<Box<str>>), BindingError(Box<Box<str>>),
LeftoverTokens, LeftoverTokens,