mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-29 06:23:25 +00:00
Auto merge of #14584 - Veykril:macro-def-err, r=Veykril
internal: Report item-level macro expansion syntax errors
This commit is contained in:
commit
697b335fda
22 changed files with 327 additions and 325 deletions
|
@ -21,7 +21,7 @@ use limit::Limit;
|
|||
use once_cell::unsync::OnceCell;
|
||||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||
use syntax::{ast, AstPtr, Parse, SyntaxNode, SyntaxNodePtr};
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
|
@ -137,7 +137,8 @@ impl Expander {
|
|||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
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 result = self.within_limit(db, |this| {
|
||||
|
@ -146,22 +147,13 @@ impl Expander {
|
|||
let resolver =
|
||||
|path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
|
||||
|
||||
let mut err = None;
|
||||
let call_id = match macro_call.as_call_id_with_errors(
|
||||
db,
|
||||
this.def_map.krate(),
|
||||
resolver,
|
||||
&mut |e| {
|
||||
err.get_or_insert(e);
|
||||
},
|
||||
) {
|
||||
match macro_call.as_call_id_with_errors(db, this.def_map.krate(), resolver) {
|
||||
Ok(call_id) => call_id,
|
||||
Err(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 {
|
||||
|
@ -175,37 +167,37 @@ impl Expander {
|
|||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
) -> ExpandResult<Option<(Mark, T)>> {
|
||||
) -> ExpandResult<Option<(Mark, Parse<T>)>> {
|
||||
self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
|
||||
}
|
||||
|
||||
fn enter_expand_inner(
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
mut err: Option<ExpandError>,
|
||||
) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> {
|
||||
if err.is_none() {
|
||||
err = db.macro_expand_error(call_id);
|
||||
mut error: Option<ExpandError>,
|
||||
) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
|
||||
let file_id = call_id.as_file();
|
||||
let ExpandResult { value, err } = db.parse_or_expand_with_err(file_id);
|
||||
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
}
|
||||
|
||||
let file_id = call_id.as_file();
|
||||
|
||||
let raw_node = match db.parse_or_expand_with_err(file_id) {
|
||||
// FIXME: report parse errors
|
||||
Some(it) => it.syntax_node(),
|
||||
let parse = match value {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
// 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");
|
||||
}
|
||||
|
||||
return ExpandResult::only_err(err.unwrap_or_else(|| {
|
||||
return ExpandResult::only_err(error.unwrap_or_else(|| {
|
||||
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) {
|
||||
|
@ -267,7 +259,7 @@ impl Expander {
|
|||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
op: F,
|
||||
) -> ExpandResult<Option<(Mark, T)>>
|
||||
) -> ExpandResult<Option<(Mark, Parse<T>)>>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
|
||||
{
|
||||
|
@ -294,15 +286,15 @@ impl Expander {
|
|||
};
|
||||
|
||||
Self::enter_expand_inner(db, call_id, err).map(|value| {
|
||||
value.and_then(|(new_file_id, node)| {
|
||||
let node = T::cast(node)?;
|
||||
value.and_then(|InFile { file_id, value }| {
|
||||
let parse = value.cast::<T>()?;
|
||||
|
||||
self.recursion_depth += 1;
|
||||
self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id);
|
||||
let old_file_id = std::mem::replace(&mut self.current_file_id, 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, file_id);
|
||||
let mark =
|
||||
Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
|
||||
Some((mark, node))
|
||||
Some((mark, parse))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -824,7 +824,11 @@ impl ExprCollector<'_> {
|
|||
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.expander.exit(self.db, mark);
|
||||
id
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
|||
use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
|
||||
use intern::Interned;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::ast;
|
||||
use syntax::{ast, Parse};
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
|
@ -604,14 +604,11 @@ impl<'a> AssocItemCollector<'a> {
|
|||
continue 'attrs;
|
||||
}
|
||||
}
|
||||
match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
|
||||
ExpandResult { value: Some((mark, _)), .. } => {
|
||||
self.collect_macro_items(mark);
|
||||
|
||||
let res = self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
|
||||
self.collect_macro_items(res, &|| loc.kind.clone());
|
||||
continue 'items;
|
||||
}
|
||||
ExpandResult { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match item {
|
||||
|
@ -641,22 +638,23 @@ impl<'a> AssocItemCollector<'a> {
|
|||
self.items.push((item.name.clone(), def.into()));
|
||||
}
|
||||
AssocItem::MacroCall(call) => {
|
||||
if let Some(root) =
|
||||
self.db.parse_or_expand_with_err(self.expander.current_file_id())
|
||||
{
|
||||
// FIXME: report parse errors
|
||||
let root = root.syntax_node();
|
||||
|
||||
let file_id = self.expander.current_file_id();
|
||||
let root = self.db.parse_or_expand(file_id);
|
||||
if let Some(root) = root {
|
||||
let call = &item_tree[call];
|
||||
|
||||
let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
|
||||
let call = ast_id_map.get(call.ast_id).to_node(&root);
|
||||
let _cx =
|
||||
stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
|
||||
let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
|
||||
|
||||
if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {
|
||||
self.collect_macro_items(mark);
|
||||
let ast_id_map = self.db.ast_id_map(file_id);
|
||||
let macro_call = ast_id_map.get(call.ast_id).to_node(&root);
|
||||
let _cx = stdx::panic_context::enter(format!(
|
||||
"collect_items MacroCall: {macro_call}"
|
||||
));
|
||||
if let Ok(res) =
|
||||
self.expander.enter_expand::<ast::MacroItems>(self.db, macro_call)
|
||||
{
|
||||
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 item_tree = tree_id.item_tree(self.db);
|
||||
let iter: SmallVec<[_; 2]> =
|
||||
|
|
|
@ -350,7 +350,7 @@ impl GenericParams {
|
|||
match expander.enter_expand::<ast::Type>(db, macro_call) {
|
||||
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
|
||||
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);
|
||||
expander.exit(db, mark);
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ pub struct ItemTree {
|
|||
top_level: SmallVec<[ModItem; 1]>,
|
||||
attrs: FxHashMap<AttrOwner, RawAttrs>,
|
||||
|
||||
// FIXME: Remove this indirection, an item tree is almost always non-empty?
|
||||
data: Option<Box<ItemTreeData>>,
|
||||
}
|
||||
|
||||
|
|
|
@ -65,11 +65,11 @@ use hir_expand::{
|
|||
builtin_attr_macro::BuiltinAttrExpander,
|
||||
builtin_derive_macro::BuiltinDeriveExpander,
|
||||
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
|
||||
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
|
||||
eager::expand_eager_macro,
|
||||
hygiene::Hygiene,
|
||||
proc_macro::ProcMacroExpander,
|
||||
AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
|
||||
MacroDefKind, UnresolvedMacro,
|
||||
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
|
||||
MacroDefId, MacroDefKind, UnresolvedMacro,
|
||||
};
|
||||
use item_tree::ExternBlock;
|
||||
use la_arena::Idx;
|
||||
|
@ -795,7 +795,7 @@ pub trait AsMacroCall {
|
|||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
) -> 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(
|
||||
|
@ -803,8 +803,7 @@ pub trait AsMacroCall {
|
|||
db: &dyn db::DefDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
error_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
|
||||
}
|
||||
|
||||
impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||
|
@ -813,30 +812,23 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
|||
db: &dyn db::DefDatabase,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
mut error_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
|
||||
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 h = Hygiene::new(db.upcast(), self.file_id);
|
||||
let path =
|
||||
self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
|
||||
|
||||
let path = match error_sink
|
||||
.option(path, || ExpandError::Other("malformed macro invocation".into()))
|
||||
{
|
||||
Ok(path) => path,
|
||||
Err(error) => {
|
||||
return Ok(Err(error));
|
||||
}
|
||||
let Some(path) = path else {
|
||||
return Ok(ExpandResult::only_err(ExpandError::Other("malformed macro invocation".into())));
|
||||
};
|
||||
|
||||
macro_call_as_call_id(
|
||||
macro_call_as_call_id_(
|
||||
db,
|
||||
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
|
||||
expands_to,
|
||||
krate,
|
||||
resolver,
|
||||
error_sink,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -860,21 +852,33 @@ fn macro_call_as_call_id(
|
|||
expand_to: ExpandTo,
|
||||
krate: CrateId,
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
error_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<Option<MacroCallId>, 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 =
|
||||
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
||||
|
||||
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()));
|
||||
|
||||
expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
|
||||
expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver)?
|
||||
} else {
|
||||
Ok(def.as_lazy_macro(
|
||||
ExpandResult {
|
||||
value: Some(def.as_lazy_macro(
|
||||
db.upcast(),
|
||||
krate,
|
||||
MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
|
||||
))
|
||||
)),
|
||||
err: None,
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
let macro_call = InFile::new(source.file_id, ¯o_call);
|
||||
let mut error = None;
|
||||
let macro_call_id = macro_call
|
||||
.as_call_id_with_errors(
|
||||
&db,
|
||||
krate,
|
||||
|path| {
|
||||
let res = macro_call
|
||||
.as_call_id_with_errors(&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();
|
||||
let macro_call_id = res.value.unwrap();
|
||||
let macro_file = MacroFile { macro_call_id };
|
||||
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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ use hir_expand::{
|
|||
builtin_fn_macro::find_builtin_macro,
|
||||
name::{name, AsName, Name},
|
||||
proc_macro::ProcMacroExpander,
|
||||
ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
|
||||
MacroDefKind,
|
||||
ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
|
||||
MacroDefId, MacroDefKind,
|
||||
};
|
||||
use itertools::{izip, Itertools};
|
||||
use la_arena::Idx;
|
||||
|
@ -1116,10 +1116,10 @@ impl DefCollector<'_> {
|
|||
*expand_to,
|
||||
self.def_map.krate,
|
||||
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);
|
||||
|
||||
res = ReachedFixedPoint::No;
|
||||
return false;
|
||||
}
|
||||
|
@ -1355,26 +1355,30 @@ impl DefCollector<'_> {
|
|||
let file_id = macro_call_id.as_file();
|
||||
|
||||
// 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).
|
||||
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
|
||||
let err = self.db.macro_expand_error(macro_call_id);
|
||||
let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
|
||||
if let Some(err) = err {
|
||||
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
|
||||
let diag = match err {
|
||||
// why is this reported here?
|
||||
hir_expand::ExpandError::UnresolvedProcMacro(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::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);
|
||||
}
|
||||
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.
|
||||
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();
|
||||
ModCollector {
|
||||
|
@ -1396,6 +1400,7 @@ impl DefCollector<'_> {
|
|||
for directive in &self.unresolved_macros {
|
||||
match &directive.kind {
|
||||
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(
|
||||
self.db,
|
||||
ast_id,
|
||||
|
@ -1414,7 +1419,6 @@ impl DefCollector<'_> {
|
|||
.take_macros()
|
||||
.map(|it| macro_id_to_def_id(self.db, it))
|
||||
},
|
||||
&mut |_| (),
|
||||
);
|
||||
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
|
||||
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));
|
||||
|
||||
// Case 1: try to resolve in legacy scope and expand macro_rules
|
||||
let mut error = None;
|
||||
match macro_call_as_call_id(
|
||||
if let Ok(res) = macro_call_as_call_id(
|
||||
self.def_collector.db,
|
||||
&ast_id,
|
||||
mac.expand_to,
|
||||
|
@ -2133,42 +2136,20 @@ 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
|
||||
// are in scope.
|
||||
if let Some(val) = res {
|
||||
self.def_collector.collect_macro_expansion(
|
||||
self.module_id,
|
||||
macro_call_id,
|
||||
val,
|
||||
self.macro_depth + 1,
|
||||
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(
|
||||
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.
|
||||
self.def_collector.unresolved_macros.push(MacroDirective {
|
||||
|
|
|
@ -4,7 +4,10 @@ use base_db::CrateId;
|
|||
use cfg::{CfgExpr, CfgOptions};
|
||||
use hir_expand::{attrs::AttrId, MacroCallKind};
|
||||
use la_arena::Idx;
|
||||
use syntax::ast::{self, AnyHasAttrs};
|
||||
use syntax::{
|
||||
ast::{self, AnyHasAttrs},
|
||||
SyntaxError,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
item_tree::{self, ItemTreeId},
|
||||
|
@ -29,6 +32,8 @@ pub enum DefDiagnosticKind {
|
|||
|
||||
MacroError { ast: MacroCallKind, message: String },
|
||||
|
||||
MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
|
||||
|
||||
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
|
||||
|
||||
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
|
||||
|
@ -91,7 +96,7 @@ impl DefDiagnostic {
|
|||
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
|
||||
}
|
||||
|
||||
pub(super) fn macro_error(
|
||||
pub(crate) fn macro_error(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
message: String,
|
||||
|
@ -99,6 +104,20 @@ impl DefDiagnostic {
|
|||
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(
|
||||
container: LocalModuleId,
|
||||
ast: MacroCallKind,
|
||||
|
|
|
@ -140,7 +140,7 @@ m!(Z);
|
|||
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
|
||||
assert_eq!(n_recalculated_item_trees, 6);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use mbe::syntax_node_to_token_tree;
|
|||
use rustc_hash::FxHashSet;
|
||||
use syntax::{
|
||||
ast::{self, HasAttrs, HasDocComments},
|
||||
AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
|
||||
AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -100,7 +100,10 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
#[salsa::transparent]
|
||||
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
|
||||
#[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.
|
||||
fn parse_macro_expansion(
|
||||
&self,
|
||||
|
@ -129,15 +132,18 @@ pub trait ExpandDatabase: SourceDatabase {
|
|||
/// just fetches procedural ones.
|
||||
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>>>;
|
||||
/// Special case of the previous query for procedural macros. We can't LRU
|
||||
/// proc macros, since they are not deterministic in general, and
|
||||
/// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
|
||||
/// heroically debugged this once!
|
||||
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
|
||||
/// Firewall query that returns the error from the `macro_expand` query.
|
||||
fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
|
||||
/// Firewall query that returns the errors from the `parse_macro_expansion` query.
|
||||
fn parse_macro_expansion_error(
|
||||
&self,
|
||||
macro_call: MacroCallId,
|
||||
) -> ExpandResult<Option<Box<[SyntaxError]>>>;
|
||||
|
||||
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(
|
||||
db: &dyn ExpandDatabase,
|
||||
file_id: HirFileId,
|
||||
) -> Option<Parse<SyntaxNode>> {
|
||||
) -> ExpandResult<Option<Parse<SyntaxNode>>> {
|
||||
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) => {
|
||||
db.parse_macro_expansion(macro_file).value.map(|(parse, _)| parse)
|
||||
db.parse_macro_expansion(macro_file).map(|it| it.map(|(parse, _)| parse))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -279,6 +285,7 @@ fn parse_macro_expansion(
|
|||
let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id);
|
||||
|
||||
if let Some(err) = &err {
|
||||
if tracing::enabled!(tracing::Level::DEBUG) {
|
||||
// Note:
|
||||
// The final goal we would like to make all parse_macro success,
|
||||
// such that the following log will not call anyway.
|
||||
|
@ -286,8 +293,9 @@ fn parse_macro_expansion(
|
|||
let node = loc.kind.to_node(db);
|
||||
|
||||
// collect parent information for warning log
|
||||
let parents =
|
||||
std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db))
|
||||
let parents = std::iter::successors(loc.kind.file_id().call_node(db), |it| {
|
||||
it.file_id.call_node(db)
|
||||
})
|
||||
.map(|n| format!("{:#}", n.value))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
@ -299,6 +307,7 @@ fn parse_macro_expansion(
|
|||
parents
|
||||
);
|
||||
}
|
||||
}
|
||||
let tt = match value {
|
||||
Some(tt) => tt,
|
||||
None => return ExpandResult { value: None, err },
|
||||
|
@ -442,14 +451,14 @@ fn macro_def(
|
|||
fn macro_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
// FIXME: Remove the OPtion if possible
|
||||
) -> ExpandResult<Option<Arc<tt::Subtree>>> {
|
||||
let _p = profile::span("macro_expand");
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||
if let Some(eager) = &loc.eager {
|
||||
return ExpandResult {
|
||||
value: Some(eager.arg_or_expansion.clone()),
|
||||
// FIXME: There could be errors here!
|
||||
err: None,
|
||||
err: eager.error.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -466,7 +475,8 @@ fn macro_expand(
|
|||
Ok(it) => it,
|
||||
// FIXME: This is weird -- we effectively report macro *definition*
|
||||
// errors lazily, when we try to expand the macro. Instead, they should
|
||||
// be reported at the definition site (when we construct a def map).
|
||||
// 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) => {
|
||||
return ExpandResult::only_err(ExpandError::Other(
|
||||
format!("invalid macro definition: {err}").into(),
|
||||
|
@ -492,8 +502,12 @@ fn macro_expand(
|
|||
ExpandResult { value: Some(Arc::new(tt)), err }
|
||||
}
|
||||
|
||||
fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
|
||||
db.macro_expand(macro_call).err
|
||||
fn parse_macro_expansion_error(
|
||||
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> {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use base_db::CrateId;
|
||||
use syntax::{ted, SyntaxNode};
|
||||
use syntax::{ted, Parse, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
ast::{self, AstNode},
|
||||
|
@ -32,77 +32,16 @@ use crate::{
|
|||
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(
|
||||
db: &dyn ExpandDatabase,
|
||||
krate: CrateId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
def: MacroDefId,
|
||||
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
diagnostic_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<ExpandResult<Option<MacroCallId>>, 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 parsed_args = macro_call
|
||||
.value
|
||||
|
@ -121,48 +60,44 @@ pub fn expand_eager_macro(
|
|||
let arg_id = db.intern_macro_call(MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(EagerCallInfo {
|
||||
eager: Some(Box::new(EagerCallInfo {
|
||||
arg_or_expansion: Arc::new(parsed_args.clone()),
|
||||
included_file: None,
|
||||
}),
|
||||
error: None,
|
||||
})),
|
||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
|
||||
});
|
||||
|
||||
let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
|
||||
let result = match eager_macro_recur(
|
||||
let ExpandResult { value, mut err } = eager_macro_recur(
|
||||
db,
|
||||
&hygiene,
|
||||
InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
|
||||
krate,
|
||||
resolver,
|
||||
diagnostic_sink,
|
||||
) {
|
||||
Ok(Ok(it)) => it,
|
||||
Ok(Err(err)) => return Ok(Err(err)),
|
||||
Err(err) => return Err(err),
|
||||
)?;
|
||||
let Some(value ) = value else {
|
||||
return Ok(ExpandResult { value: None, 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);
|
||||
if let Some(err) = res.err {
|
||||
diagnostic_sink(err);
|
||||
if err.is_none() {
|
||||
err = res.err;
|
||||
}
|
||||
|
||||
let loc = MacroCallLoc {
|
||||
def,
|
||||
krate,
|
||||
eager: Some(EagerCallInfo {
|
||||
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(Ok(db.intern_macro_call(loc)))
|
||||
} else {
|
||||
panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
|
||||
}
|
||||
Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
|
||||
}
|
||||
|
||||
fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
|
||||
|
@ -176,7 +111,7 @@ fn lazy_expand(
|
|||
def: &MacroDefId,
|
||||
macro_call: InFile<ast::MacroCall>,
|
||||
krate: CrateId,
|
||||
) -> ExpandResult<Option<InFile<SyntaxNode>>> {
|
||||
) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
|
||||
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
||||
|
||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||
|
@ -186,13 +121,8 @@ fn lazy_expand(
|
|||
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
|
||||
);
|
||||
|
||||
let err = db.macro_expand_error(id);
|
||||
let value =
|
||||
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 }
|
||||
db.parse_or_expand_with_err(id.as_file())
|
||||
.map(|parse| parse.map(|parse| InFile::new(id.as_file(), parse)))
|
||||
}
|
||||
|
||||
fn eager_macro_recur(
|
||||
|
@ -201,23 +131,25 @@ fn eager_macro_recur(
|
|||
curr: InFile<SyntaxNode>,
|
||||
krate: CrateId,
|
||||
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||
mut diagnostic_sink: &mut dyn FnMut(ExpandError),
|
||||
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
|
||||
) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
|
||||
let original = curr.value.clone_for_update();
|
||||
|
||||
let children = original.descendants().filter_map(ast::MacroCall::cast);
|
||||
let mut replacements = Vec::new();
|
||||
|
||||
// Note: We only report a single error inside of eager expansions
|
||||
let mut error = None;
|
||||
|
||||
// Collect replacement
|
||||
for child in children {
|
||||
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
|
||||
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
|
||||
None => {
|
||||
diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
|
||||
error = Some(ExpandError::Other("malformed macro invocation".into()));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let insert = match def.kind {
|
||||
let ExpandResult { value, err } = match def.kind {
|
||||
MacroDefKind::BuiltInEager(..) => {
|
||||
let id = match expand_eager_macro(
|
||||
db,
|
||||
|
@ -225,45 +157,55 @@ fn eager_macro_recur(
|
|||
curr.with_value(child.clone()),
|
||||
def,
|
||||
macro_resolver,
|
||||
diagnostic_sink,
|
||||
) {
|
||||
Ok(Ok(it)) => it,
|
||||
Ok(Err(err)) => return Ok(Err(err)),
|
||||
Ok(it) => it,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
db.parse_or_expand(id.as_file())
|
||||
.expect("successful macro expansion should be parseable")
|
||||
.clone_for_update()
|
||||
id.map(|call| {
|
||||
call.and_then(|call| db.parse_or_expand(call.as_file()))
|
||||
.map(|it| it.clone_for_update())
|
||||
})
|
||||
}
|
||||
MacroDefKind::Declarative(_)
|
||||
| MacroDefKind::BuiltIn(..)
|
||||
| MacroDefKind::BuiltInAttr(..)
|
||||
| MacroDefKind::BuiltInDerive(..)
|
||||
| MacroDefKind::ProcMacro(..) => {
|
||||
let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
|
||||
let val = match diagnostic_sink.expand_result_option(res) {
|
||||
Ok(it) => it,
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
let ExpandResult { value, err } =
|
||||
lazy_expand(db, &def, curr.with_value(child.clone()), krate);
|
||||
|
||||
match value {
|
||||
Some(val) => {
|
||||
// replace macro inside
|
||||
let hygiene = Hygiene::new(db, val.file_id);
|
||||
match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) {
|
||||
Ok(Ok(it)) => it,
|
||||
Ok(Err(err)) => return Ok(Err(err)),
|
||||
Err(err) => return Err(err),
|
||||
let ExpandResult { value, err: error } = eager_macro_recur(
|
||||
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
|
||||
if child.syntax() == &original {
|
||||
return Ok(Ok(insert));
|
||||
return Ok(ExpandResult { value, err: error });
|
||||
}
|
||||
|
||||
if let Some(insert) = value {
|
||||
replacements.push((child, insert));
|
||||
}
|
||||
}
|
||||
|
||||
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
|
||||
Ok(Ok(original))
|
||||
Ok(ExpandResult { value: Some(original), err: error })
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ use crate::{
|
|||
|
||||
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum ExpandError {
|
||||
UnresolvedProcMacro(CrateId),
|
||||
Mbe(mbe::ExpandError),
|
||||
|
@ -114,7 +114,7 @@ impl_intern_key!(MacroCallId);
|
|||
pub struct MacroCallLoc {
|
||||
pub def: MacroDefId,
|
||||
pub(crate) krate: CrateId,
|
||||
eager: Option<EagerCallInfo>,
|
||||
eager: Option<Box<EagerCallInfo>>,
|
||||
pub kind: MacroCallKind,
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ struct EagerCallInfo {
|
|||
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
|
||||
arg_or_expansion: Arc<tt::Subtree>,
|
||||
included_file: Option<(FileId, TokenMap)>,
|
||||
error: Option<ExpandError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -206,8 +207,8 @@ impl HirFileId {
|
|||
HirFileIdRepr::FileId(id) => break id,
|
||||
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
|
||||
file_id = match loc.eager {
|
||||
Some(EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
|
||||
file_id = match loc.eager.as_deref() {
|
||||
Some(&EagerCallInfo { included_file: Some((file, _)), .. }) => file.into(),
|
||||
_ => loc.kind.file_id(),
|
||||
};
|
||||
}
|
||||
|
@ -320,7 +321,7 @@ impl HirFileId {
|
|||
match self.macro_file() {
|
||||
Some(macro_file) => {
|
||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||
matches!(loc.eager, Some(EagerCallInfo { included_file: Some(..), .. }))
|
||||
matches!(loc.eager.as_deref(), Some(EagerCallInfo { included_file: Some(..), .. }))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
@ -381,7 +381,8 @@ impl<'a> TyLoweringContext<'a> {
|
|||
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
|
||||
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
|
||||
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);
|
||||
let ty = self.lower_ty(&type_ref);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
pub use hir_def::db::*;
|
||||
pub use hir_expand::db::{
|
||||
AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery,
|
||||
InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery,
|
||||
MacroExpandQuery, ParseMacroExpansionQuery,
|
||||
InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery,
|
||||
ParseMacroExpansionQuery,
|
||||
};
|
||||
pub use hir_ty::db::*;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use cfg::{CfgExpr, CfgOptions};
|
|||
use either::Either;
|
||||
use hir_def::path::ModPath;
|
||||
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};
|
||||
|
||||
|
@ -38,8 +38,9 @@ diagnostics![
|
|||
IncorrectCase,
|
||||
InvalidDeriveTarget,
|
||||
IncoherentImpl,
|
||||
MacroError,
|
||||
MacroDefError,
|
||||
MacroError,
|
||||
MacroExpansionParseError,
|
||||
MalformedDerive,
|
||||
MismatchedArgCount,
|
||||
MissingFields,
|
||||
|
@ -132,6 +133,13 @@ pub struct MacroError {
|
|||
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)]
|
||||
pub struct MacroDefError {
|
||||
pub node: InFile<AstPtr<ast::Macro>>,
|
||||
|
|
|
@ -87,12 +87,12 @@ pub use crate::{
|
|||
attrs::{HasAttrs, Namespace},
|
||||
diagnostics::{
|
||||
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
|
||||
IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MalformedDerive,
|
||||
MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField,
|
||||
PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch,
|
||||
UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate,
|
||||
UnresolvedField, UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall,
|
||||
UnresolvedModule, UnresolvedProcMacro, UnusedMut,
|
||||
IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
|
||||
MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
|
||||
NeedMut, NoSuchField, PrivateAssocItem, PrivateField, ReplaceFilterMapNextWithFindMap,
|
||||
TypeMismatch, UndeclaredLabel, UnimplementedBuiltinMacro, UnreachableLabel,
|
||||
UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
|
||||
UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
|
||||
},
|
||||
has_source::HasSource,
|
||||
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
|
||||
|
@ -753,7 +753,6 @@ fn emit_def_diagnostic_(
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
|
||||
let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db);
|
||||
acc.push(
|
||||
|
@ -761,7 +760,6 @@ fn emit_def_diagnostic_(
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
|
||||
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
|
||||
acc.push(
|
||||
|
@ -774,12 +772,16 @@ fn emit_def_diagnostic_(
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
DefDiagnosticKind::MacroError { ast, message } => {
|
||||
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
|
||||
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 } => {
|
||||
let node = ast.to_node(db.upcast());
|
||||
// Must have a name, otherwise we wouldn't emit it.
|
||||
|
|
|
@ -80,7 +80,6 @@ impl RootDatabase {
|
|||
hir::db::MacroDefQuery
|
||||
hir::db::MacroExpandQuery
|
||||
hir::db::ExpandProcMacroQuery
|
||||
hir::db::MacroExpandErrorQuery
|
||||
hir::db::HygieneFrameQuery
|
||||
|
||||
// DefDatabase
|
||||
|
|
|
@ -152,7 +152,6 @@ impl RootDatabase {
|
|||
let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
|
||||
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::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
|
||||
}
|
||||
|
||||
pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
|
||||
|
@ -167,12 +166,6 @@ impl RootDatabase {
|
|||
.copied()
|
||||
.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 {
|
||||
($( $module:ident :: $query:ident )*) => {$(
|
||||
|
@ -201,7 +194,6 @@ impl RootDatabase {
|
|||
hir_db::MacroDefQuery
|
||||
// hir_db::MacroExpandQuery
|
||||
hir_db::ExpandProcMacroQuery
|
||||
hir_db::MacroExpandErrorQuery
|
||||
hir_db::HygieneFrameQuery
|
||||
|
||||
// DefDatabase
|
||||
|
|
|
@ -238,6 +238,22 @@ fn f() {
|
|||
//^^^ 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
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -265,6 +265,19 @@ pub fn diagnostics(
|
|||
AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
|
||||
AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_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::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
|
||||
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
|
||||
|
|
|
@ -69,7 +69,7 @@ impl fmt::Display for ParseError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub enum ExpandError {
|
||||
BindingError(Box<Box<str>>),
|
||||
LeftoverTokens,
|
||||
|
|
Loading…
Reference in a new issue