mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-05 19:58:32 +00:00
Merge #6700
6700: More macro diagnostics improvements r=jonas-schievink a=jonas-schievink This threads macro expansion errors through `eager.rs` and the `AsMacroCall` trait, improving macro diagnostics emitted during body lowering. Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
d46fce88f5
@ -120,18 +120,24 @@ impl Expander {
|
|||||||
self.resolve_path_as_macro(db, &path)
|
self.resolve_path_as_macro(db, &path)
|
||||||
};
|
};
|
||||||
|
|
||||||
let call_id = match macro_call.as_call_id(db, self.crate_def_map.krate, resolver) {
|
let mut err = None;
|
||||||
|
let call_id =
|
||||||
|
macro_call.as_call_id_with_errors(db, self.crate_def_map.krate, resolver, &mut |e| {
|
||||||
|
err.get_or_insert(e);
|
||||||
|
});
|
||||||
|
let call_id = match call_id {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
// FIXME: this can mean other things too, but `as_call_id` doesn't provide enough
|
if err.is_none() {
|
||||||
// info.
|
eprintln!("no error despite `as_call_id_with_errors` returning `None`");
|
||||||
return ExpandResult::only_err(mbe::ExpandError::Other(
|
}
|
||||||
"failed to parse or resolve macro invocation".into(),
|
return ExpandResult { value: None, err };
|
||||||
));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let err = db.macro_expand_error(call_id);
|
if err.is_none() {
|
||||||
|
err = db.macro_expand_error(call_id);
|
||||||
|
}
|
||||||
|
|
||||||
let file_id = call_id.as_file();
|
let file_id = call_id.as_file();
|
||||||
|
|
||||||
|
@ -78,21 +78,41 @@ fn f() {
|
|||||||
fn macro_diag_builtin() {
|
fn macro_diag_builtin() {
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! env {}
|
||||||
|
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! include {}
|
||||||
|
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! compile_error {}
|
||||||
|
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
macro_rules! format_args {
|
||||||
|
() => {}
|
||||||
|
}
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
// Test a handful of built-in (eager) macros:
|
// Test a handful of built-in (eager) macros:
|
||||||
|
|
||||||
include!(invalid);
|
include!(invalid);
|
||||||
//^^^^^^^^^^^^^^^^^ failed to parse or resolve macro invocation
|
//^^^^^^^^^^^^^^^^^ could not convert tokens
|
||||||
include!("does not exist");
|
include!("does not exist");
|
||||||
//^^^^^^^^^^^^^^^^^^^^^^^^^^ failed to parse or resolve macro invocation
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^ could not convert tokens
|
||||||
|
|
||||||
env!(invalid);
|
env!(invalid);
|
||||||
//^^^^^^^^^^^^^ failed to parse or resolve macro invocation
|
//^^^^^^^^^^^^^ could not convert tokens
|
||||||
|
|
||||||
|
env!("OUT_DIR");
|
||||||
|
//^^^^^^^^^^^^^^^ `OUT_DIR` not set, enable "load out dirs from check" to fix
|
||||||
|
|
||||||
|
compile_error!("compile_error works");
|
||||||
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `compile_error!` called: compile_error works
|
||||||
|
|
||||||
// Lazy:
|
// Lazy:
|
||||||
|
|
||||||
format_args!();
|
format_args!();
|
||||||
//^^^^^^^^^^^^^^ failed to parse or resolve macro invocation
|
//^^^^^^^^^^^^^^ no rule matches input tokens
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
@ -465,21 +465,37 @@ 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>,
|
||||||
) -> Option<MacroCallId>;
|
) -> Option<MacroCallId> {
|
||||||
}
|
self.as_call_id_with_errors(db, krate, resolver, &mut |_| ())
|
||||||
|
}
|
||||||
|
|
||||||
impl AsMacroCall for InFile<&ast::MacroCall> {
|
fn as_call_id_with_errors(
|
||||||
fn as_call_id(
|
|
||||||
&self,
|
&self,
|
||||||
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(mbe::ExpandError),
|
||||||
|
) -> Option<MacroCallId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
|
fn as_call_id_with_errors(
|
||||||
|
&self,
|
||||||
|
db: &dyn db::DefDatabase,
|
||||||
|
krate: CrateId,
|
||||||
|
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||||
|
error_sink: &mut dyn FnMut(mbe::ExpandError),
|
||||||
) -> Option<MacroCallId> {
|
) -> Option<MacroCallId> {
|
||||||
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 = path::ModPath::from_src(self.value.path()?, &h)?;
|
let path = self.value.path().and_then(|path| path::ModPath::from_src(path, &h));
|
||||||
|
|
||||||
AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
|
if path.is_none() {
|
||||||
|
error_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
AstIdWithPath::new(ast_id.file_id, ast_id.value, path?)
|
||||||
|
.as_call_id_with_errors(db, krate, resolver, error_sink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,22 +513,32 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
|
impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
|
||||||
fn as_call_id(
|
fn as_call_id_with_errors(
|
||||||
&self,
|
&self,
|
||||||
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(mbe::ExpandError),
|
||||||
) -> Option<MacroCallId> {
|
) -> Option<MacroCallId> {
|
||||||
let def: MacroDefId = resolver(self.path.clone())?;
|
let def: MacroDefId = resolver(self.path.clone()).or_else(|| {
|
||||||
|
error_sink(mbe::ExpandError::Other("could not resolve macro".into()));
|
||||||
|
None
|
||||||
|
})?;
|
||||||
|
|
||||||
if let MacroDefKind::BuiltInEager(_) = def.kind {
|
if let MacroDefKind::BuiltInEager(_) = def.kind {
|
||||||
let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast()));
|
let macro_call = InFile::new(self.ast_id.file_id, self.ast_id.to_node(db.upcast()));
|
||||||
let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
|
let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
|
expand_eager_macro(
|
||||||
resolver(path::ModPath::from_src(path, &hygiene)?)
|
db.upcast(),
|
||||||
})?
|
krate,
|
||||||
|
macro_call,
|
||||||
|
def,
|
||||||
|
&|path: ast::Path| resolver(path::ModPath::from_src(path, &hygiene)?),
|
||||||
|
error_sink,
|
||||||
|
)
|
||||||
|
.ok()?
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -522,13 +548,18 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsMacroCall for AstIdWithPath<ast::Item> {
|
impl AsMacroCall for AstIdWithPath<ast::Item> {
|
||||||
fn as_call_id(
|
fn as_call_id_with_errors(
|
||||||
&self,
|
&self,
|
||||||
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(mbe::ExpandError),
|
||||||
) -> Option<MacroCallId> {
|
) -> Option<MacroCallId> {
|
||||||
let def = resolver(self.path.clone())?;
|
let def: MacroDefId = resolver(self.path.clone()).or_else(|| {
|
||||||
|
error_sink(mbe::ExpandError::Other("could not resolve macro".into()));
|
||||||
|
None
|
||||||
|
})?;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
def.as_lazy_macro(
|
def.as_lazy_macro(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
|
@ -86,7 +86,6 @@ pub fn find_builtin_macro(
|
|||||||
register_builtin! {
|
register_builtin! {
|
||||||
LAZY:
|
LAZY:
|
||||||
(column, Column) => column_expand,
|
(column, Column) => column_expand,
|
||||||
(compile_error, CompileError) => compile_error_expand,
|
|
||||||
(file, File) => file_expand,
|
(file, File) => file_expand,
|
||||||
(line, Line) => line_expand,
|
(line, Line) => line_expand,
|
||||||
(assert, Assert) => assert_expand,
|
(assert, Assert) => assert_expand,
|
||||||
@ -97,6 +96,7 @@ register_builtin! {
|
|||||||
(format_args_nl, FormatArgsNl) => format_args_expand,
|
(format_args_nl, FormatArgsNl) => format_args_expand,
|
||||||
|
|
||||||
EAGER:
|
EAGER:
|
||||||
|
(compile_error, CompileError) => compile_error_expand,
|
||||||
(concat, Concat) => concat_expand,
|
(concat, Concat) => concat_expand,
|
||||||
(include, Include) => include_expand,
|
(include, Include) => include_expand,
|
||||||
(include_bytes, IncludeBytes) => include_bytes_expand,
|
(include_bytes, IncludeBytes) => include_bytes_expand,
|
||||||
@ -213,25 +213,6 @@ fn file_expand(
|
|||||||
ExpandResult::ok(expanded)
|
ExpandResult::ok(expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_error_expand(
|
|
||||||
_db: &dyn AstDatabase,
|
|
||||||
_id: LazyMacroId,
|
|
||||||
tt: &tt::Subtree,
|
|
||||||
) -> ExpandResult<tt::Subtree> {
|
|
||||||
if tt.count() == 1 {
|
|
||||||
if let tt::TokenTree::Leaf(tt::Leaf::Literal(it)) = &tt.token_trees[0] {
|
|
||||||
let s = it.text.as_str();
|
|
||||||
if s.contains('"') {
|
|
||||||
return ExpandResult::ok(quote! { loop { #it }});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ExpandResult::only_err(mbe::ExpandError::BindingError(
|
|
||||||
"`compile_error!` argument be a string".into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_args_expand(
|
fn format_args_expand(
|
||||||
_db: &dyn AstDatabase,
|
_db: &dyn AstDatabase,
|
||||||
_id: LazyMacroId,
|
_id: LazyMacroId,
|
||||||
@ -280,6 +261,30 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
|
|||||||
token.value().map(|it| it.into_owned())
|
token.value().map(|it| it.into_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_error_expand(
|
||||||
|
_db: &dyn AstDatabase,
|
||||||
|
_id: EagerMacroId,
|
||||||
|
tt: &tt::Subtree,
|
||||||
|
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||||
|
let err = match &*tt.token_trees {
|
||||||
|
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => {
|
||||||
|
let text = it.text.as_str();
|
||||||
|
if text.starts_with('"') && text.ends_with('"') {
|
||||||
|
// FIXME: does not handle raw strings
|
||||||
|
mbe::ExpandError::Other(format!(
|
||||||
|
"`compile_error!` called: {}",
|
||||||
|
&text[1..text.len() - 1]
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
ExpandResult { value: Some((quote! {}, FragmentKind::Items)), err: Some(err) }
|
||||||
|
}
|
||||||
|
|
||||||
fn concat_expand(
|
fn concat_expand(
|
||||||
_db: &dyn AstDatabase,
|
_db: &dyn AstDatabase,
|
||||||
_arg_id: EagerMacroId,
|
_arg_id: EagerMacroId,
|
||||||
@ -417,17 +422,25 @@ fn env_expand(
|
|||||||
Err(e) => return ExpandResult::only_err(e),
|
Err(e) => return ExpandResult::only_err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME:
|
let mut err = None;
|
||||||
// If the environment variable is not defined int rustc, then a compilation error will be emitted.
|
let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| {
|
||||||
// We might do the same if we fully support all other stuffs.
|
// The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
|
||||||
// But for now on, we should return some dummy string for better type infer purpose.
|
// unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
|
||||||
// However, we cannot use an empty string here, because for
|
if key == "OUT_DIR" {
|
||||||
// `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
|
err = Some(mbe::ExpandError::Other(
|
||||||
// `include!("foo.rs"), which might go to infinite loop
|
r#"`OUT_DIR` not set, enable "load out dirs from check" to fix"#.into(),
|
||||||
let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string());
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the variable is unset, still return a dummy string to help type inference along.
|
||||||
|
// We cannot use an empty string here, because for
|
||||||
|
// `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
|
||||||
|
// `include!("foo.rs"), which might go to infinite loop
|
||||||
|
"__RA_UNIMPLEMENTED__".to_string()
|
||||||
|
});
|
||||||
let expanded = quote! { #s };
|
let expanded = quote! { #s };
|
||||||
|
|
||||||
ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
|
ExpandResult { value: Some((expanded, FragmentKind::Expr)), err }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn option_env_expand(
|
fn option_env_expand(
|
||||||
@ -638,7 +651,8 @@ mod tests {
|
|||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(expanded, r#"loop{"error!"}"#);
|
// This expands to nothing (since it's in item position), but emits an error.
|
||||||
|
assert_eq!(expanded, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -207,6 +207,7 @@ fn macro_expand_with_arg(
|
|||||||
} else {
|
} else {
|
||||||
return ExpandResult {
|
return ExpandResult {
|
||||||
value: Some(db.lookup_intern_eager_expansion(id).subtree),
|
value: Some(db.lookup_intern_eager_expansion(id).subtree),
|
||||||
|
// FIXME: There could be errors here!
|
||||||
err: None,
|
err: None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -26,19 +26,89 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
|
use mbe::ExpandResult;
|
||||||
use parser::FragmentKind;
|
use parser::FragmentKind;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use syntax::{algo::SyntaxRewriter, SyntaxNode};
|
use syntax::{algo::SyntaxRewriter, SyntaxNode};
|
||||||
|
|
||||||
|
pub struct ErrorEmitted {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ErrorSink {
|
||||||
|
fn emit(&mut self, err: mbe::ExpandError);
|
||||||
|
|
||||||
|
fn option<T>(
|
||||||
|
&mut self,
|
||||||
|
opt: Option<T>,
|
||||||
|
error: impl FnOnce() -> mbe::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() -> mbe::ExpandError,
|
||||||
|
) -> Result<T, ErrorEmitted> {
|
||||||
|
self.option(opt(), error)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result<T>(&mut self, res: Result<T, mbe::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(mbe::ExpandError) {
|
||||||
|
fn emit(&mut self, err: mbe::ExpandError) {
|
||||||
|
self(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn err(msg: impl Into<String>) -> mbe::ExpandError {
|
||||||
|
mbe::ExpandError::Other(msg.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expand_eager_macro(
|
pub fn expand_eager_macro(
|
||||||
db: &dyn AstDatabase,
|
db: &dyn AstDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
macro_call: InFile<ast::MacroCall>,
|
macro_call: InFile<ast::MacroCall>,
|
||||||
def: MacroDefId,
|
def: MacroDefId,
|
||||||
resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
|
resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
|
||||||
) -> Option<EagerMacroId> {
|
mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
|
||||||
let args = macro_call.value.token_tree()?;
|
) -> Result<EagerMacroId, ErrorEmitted> {
|
||||||
let parsed_args = mbe::ast_to_token_tree(&args)?.0;
|
let parsed_args = diagnostic_sink.option_with(
|
||||||
|
|| Some(mbe::ast_to_token_tree(¯o_call.value.token_tree()?)?.0),
|
||||||
|
|| err("malformed macro invocation"),
|
||||||
|
)?;
|
||||||
|
|
||||||
// Note:
|
// Note:
|
||||||
// When `lazy_expand` is called, its *parent* file must be already exists.
|
// When `lazy_expand` is called, its *parent* file must be already exists.
|
||||||
@ -55,17 +125,22 @@ pub fn expand_eager_macro(
|
|||||||
});
|
});
|
||||||
let arg_file_id: MacroCallId = arg_id.into();
|
let arg_file_id: MacroCallId = arg_id.into();
|
||||||
|
|
||||||
let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
|
let parsed_args =
|
||||||
|
diagnostic_sink.result(mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr))?.0;
|
||||||
let result = eager_macro_recur(
|
let result = eager_macro_recur(
|
||||||
db,
|
db,
|
||||||
InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
|
InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
|
||||||
krate,
|
krate,
|
||||||
resolver,
|
resolver,
|
||||||
|
diagnostic_sink,
|
||||||
)?;
|
)?;
|
||||||
let subtree = to_subtree(&result)?;
|
let subtree =
|
||||||
|
diagnostic_sink.option(to_subtree(&result), || err("failed to parse macro result"))?;
|
||||||
|
|
||||||
if let MacroDefKind::BuiltInEager(eager) = def.kind {
|
if let MacroDefKind::BuiltInEager(eager) = def.kind {
|
||||||
let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?;
|
let res = eager.expand(db, arg_id, &subtree);
|
||||||
|
|
||||||
|
let (subtree, fragment) = diagnostic_sink.expand_result_option(res)?;
|
||||||
let eager = EagerCallLoc {
|
let eager = EagerCallLoc {
|
||||||
def,
|
def,
|
||||||
fragment,
|
fragment,
|
||||||
@ -74,9 +149,9 @@ pub fn expand_eager_macro(
|
|||||||
file_id: macro_call.file_id,
|
file_id: macro_call.file_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(db.intern_eager_expansion(eager))
|
Ok(db.intern_eager_expansion(eager))
|
||||||
} else {
|
} else {
|
||||||
None
|
panic!("called `expand_eager_macro` on non-eager macro def {:?}", def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,13 +166,16 @@ fn lazy_expand(
|
|||||||
def: &MacroDefId,
|
def: &MacroDefId,
|
||||||
macro_call: InFile<ast::MacroCall>,
|
macro_call: InFile<ast::MacroCall>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
) -> Option<InFile<SyntaxNode>> {
|
) -> ExpandResult<Option<InFile<SyntaxNode>>> {
|
||||||
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
||||||
|
|
||||||
let id: MacroCallId =
|
let id: MacroCallId =
|
||||||
def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
|
def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
|
||||||
|
|
||||||
db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
|
let err = db.macro_expand_error(id);
|
||||||
|
let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
|
||||||
|
|
||||||
|
ExpandResult { value, err }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eager_macro_recur(
|
fn eager_macro_recur(
|
||||||
@ -105,7 +183,8 @@ fn eager_macro_recur(
|
|||||||
curr: InFile<SyntaxNode>,
|
curr: InFile<SyntaxNode>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
|
macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
|
||||||
) -> Option<SyntaxNode> {
|
mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
|
||||||
|
) -> Result<SyntaxNode, ErrorEmitted> {
|
||||||
let original = curr.value.clone();
|
let original = curr.value.clone();
|
||||||
|
|
||||||
let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
|
let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
|
||||||
@ -113,7 +192,8 @@ fn eager_macro_recur(
|
|||||||
|
|
||||||
// Collect replacement
|
// Collect replacement
|
||||||
for child in children {
|
for child in children {
|
||||||
let def: MacroDefId = macro_resolver(child.path()?)?;
|
let def = diagnostic_sink
|
||||||
|
.option_with(|| macro_resolver(child.path()?), || err("failed to resolve macro"))?;
|
||||||
let insert = match def.kind {
|
let insert = match def.kind {
|
||||||
MacroDefKind::BuiltInEager(_) => {
|
MacroDefKind::BuiltInEager(_) => {
|
||||||
let id: MacroCallId = expand_eager_macro(
|
let id: MacroCallId = expand_eager_macro(
|
||||||
@ -122,17 +202,21 @@ fn eager_macro_recur(
|
|||||||
curr.with_value(child.clone()),
|
curr.with_value(child.clone()),
|
||||||
def,
|
def,
|
||||||
macro_resolver,
|
macro_resolver,
|
||||||
|
diagnostic_sink,
|
||||||
)?
|
)?
|
||||||
.into();
|
.into();
|
||||||
db.parse_or_expand(id.as_file())?
|
db.parse_or_expand(id.as_file())
|
||||||
|
.expect("successful macro expansion should be parseable")
|
||||||
}
|
}
|
||||||
MacroDefKind::Declarative
|
MacroDefKind::Declarative
|
||||||
| MacroDefKind::BuiltIn(_)
|
| MacroDefKind::BuiltIn(_)
|
||||||
| MacroDefKind::BuiltInDerive(_)
|
| MacroDefKind::BuiltInDerive(_)
|
||||||
| MacroDefKind::ProcMacro(_) => {
|
| MacroDefKind::ProcMacro(_) => {
|
||||||
let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
|
let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
|
||||||
|
let val = diagnostic_sink.expand_result_option(res)?;
|
||||||
|
|
||||||
// replace macro inside
|
// replace macro inside
|
||||||
eager_macro_recur(db, expanded, krate, macro_resolver)?
|
eager_macro_recur(db, val, krate, macro_resolver, diagnostic_sink)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -140,5 +224,5 @@ fn eager_macro_recur(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let res = rewriter.rewrite(&original);
|
let res = rewriter.rewrite(&original);
|
||||||
Some(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user