mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
internal: Split unresolve proc-macro error out of mbe
This commit is contained in:
parent
b663b733d9
commit
1505b6a9b4
13 changed files with 147 additions and 104 deletions
|
@ -97,9 +97,9 @@ impl Expander {
|
||||||
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
|
) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
|
||||||
if self.recursion_limit(db).check(self.recursion_limit + 1).is_err() {
|
if self.recursion_limit(db).check(self.recursion_limit + 1).is_err() {
|
||||||
cov_mark::hit!(your_stack_belongs_to_me);
|
cov_mark::hit!(your_stack_belongs_to_me);
|
||||||
return Ok(ExpandResult::str_err(
|
return Ok(ExpandResult::only_err(ExpandError::Other(
|
||||||
"reached recursion limit during macro expansion".into(),
|
"reached recursion limit during macro expansion".into(),
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
||||||
|
@ -151,7 +151,7 @@ impl Expander {
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExpandResult::only_err(err.unwrap_or_else(|| {
|
return ExpandResult::only_err(err.unwrap_or_else(|| {
|
||||||
mbe::ExpandError::Other("failed to parse macro invocation".into())
|
ExpandError::Other("failed to parse macro invocation".into())
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,8 +63,8 @@ use hir_expand::{
|
||||||
ast_id_map::FileAstId,
|
ast_id_map::FileAstId,
|
||||||
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
|
eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
|
||||||
hygiene::Hygiene,
|
hygiene::Hygiene,
|
||||||
AstId, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
|
AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
|
||||||
UnresolvedMacro,
|
MacroDefKind, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
use item_tree::ExternBlock;
|
use item_tree::ExternBlock;
|
||||||
use la_arena::Idx;
|
use la_arena::Idx;
|
||||||
|
@ -662,7 +662,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(mbe::ExpandError),
|
error_sink: &mut dyn FnMut(ExpandError),
|
||||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
|
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +672,7 @@ 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(mbe::ExpandError),
|
mut error_sink: &mut dyn FnMut(ExpandError),
|
||||||
) -> Result<Result<MacroCallId, ErrorEmitted>, 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));
|
||||||
|
@ -681,7 +681,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
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 path = match error_sink
|
||||||
.option(path, || mbe::ExpandError::Other("malformed macro invocation".into()))
|
.option(path, || ExpandError::Other("malformed macro invocation".into()))
|
||||||
{
|
{
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
@ -719,7 +719,7 @@ fn macro_call_as_call_id(
|
||||||
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),
|
error_sink: &mut dyn FnMut(ExpandError),
|
||||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||||
let def: MacroDefId =
|
let def: MacroDefId =
|
||||||
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Builtin attributes.
|
//! Builtin attributes.
|
||||||
|
|
||||||
use mbe::ExpandResult;
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{db::AstDatabase, name, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
|
use crate::{
|
||||||
|
db::AstDatabase, name, AstId, CrateId, ExpandResult, MacroCallId, MacroDefId, MacroDefKind,
|
||||||
|
};
|
||||||
|
|
||||||
macro_rules! register_builtin {
|
macro_rules! register_builtin {
|
||||||
( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
|
( $(($name:ident, $variant:ident) => $expand:ident),* ) => {
|
||||||
|
|
|
@ -2,13 +2,16 @@
|
||||||
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use mbe::ExpandResult;
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
|
ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
|
||||||
match_ast,
|
match_ast,
|
||||||
};
|
};
|
||||||
|
use tt::TokenId;
|
||||||
|
|
||||||
use crate::{db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroDefId, MacroDefKind};
|
use crate::{
|
||||||
|
db::AstDatabase, name, quote, AstId, CrateId, ExpandError, ExpandResult, MacroCallId,
|
||||||
|
MacroDefId, MacroDefKind,
|
||||||
|
};
|
||||||
|
|
||||||
macro_rules! register_builtin {
|
macro_rules! register_builtin {
|
||||||
( $($trait:ident => $expand:ident),* ) => {
|
( $($trait:ident => $expand:ident),* ) => {
|
||||||
|
@ -71,15 +74,15 @@ struct BasicAdtInfo {
|
||||||
type_params: usize,
|
type_params: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
|
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
|
||||||
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems); // FragmentKind::Items doesn't parse attrs?
|
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
|
||||||
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
|
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
|
||||||
debug!("derive node didn't parse");
|
debug!("derive node didn't parse");
|
||||||
mbe::ExpandError::UnexpectedToken
|
ExpandError::Other("invalid item definition".into())
|
||||||
})?;
|
})?;
|
||||||
let item = macro_items.items().next().ok_or_else(|| {
|
let item = macro_items.items().next().ok_or_else(|| {
|
||||||
debug!("no module item parsed");
|
debug!("no module item parsed");
|
||||||
mbe::ExpandError::NoMatchingRule
|
ExpandError::Other("no item found".into())
|
||||||
})?;
|
})?;
|
||||||
let node = item.syntax();
|
let node = item.syntax();
|
||||||
let (name, params) = match_ast! {
|
let (name, params) = match_ast! {
|
||||||
|
@ -89,18 +92,17 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, mbe::ExpandError> {
|
||||||
ast::Union(it) => (it.name(), it.generic_param_list()),
|
ast::Union(it) => (it.name(), it.generic_param_list()),
|
||||||
_ => {
|
_ => {
|
||||||
debug!("unexpected node is {:?}", node);
|
debug!("unexpected node is {:?}", node);
|
||||||
return Err(mbe::ExpandError::ConversionError)
|
return Err(ExpandError::Other("expected struct, enum or union".into()))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let name = name.ok_or_else(|| {
|
let name = name.ok_or_else(|| {
|
||||||
debug!("parsed item has no name");
|
debug!("parsed item has no name");
|
||||||
mbe::ExpandError::NoMatchingRule
|
ExpandError::Other("missing name".into())
|
||||||
})?;
|
|
||||||
let name_token_id = token_map.token_by_range(name.syntax().text_range()).ok_or_else(|| {
|
|
||||||
debug!("name token not found");
|
|
||||||
mbe::ExpandError::ConversionError
|
|
||||||
})?;
|
})?;
|
||||||
|
let name_token_id = token_map
|
||||||
|
.token_by_range(name.syntax().text_range())
|
||||||
|
.unwrap_or_else(|| TokenId::unspecified());
|
||||||
let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
|
let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
|
||||||
let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
|
let type_params = params.map_or(0, |type_param_list| type_param_list.type_params().count());
|
||||||
Ok(BasicAdtInfo { name: name_token, type_params })
|
Ok(BasicAdtInfo { name: name_token, type_params })
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
//! Builtin macro
|
//! Builtin macro
|
||||||
use crate::{
|
|
||||||
db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroCallLoc, MacroDefId,
|
|
||||||
MacroDefKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
use base_db::{AnchoredPath, Edition, FileId};
|
use base_db::{AnchoredPath, Edition, FileId};
|
||||||
use cfg::CfgExpr;
|
use cfg::CfgExpr;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
|
use mbe::{parse_exprs_with_sep, parse_to_token_tree};
|
||||||
use syntax::ast::{self, AstToken};
|
use syntax::ast::{self, AstToken};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::AstDatabase, name, quote, AstId, CrateId, ExpandError, ExpandResult, MacroCallId,
|
||||||
|
MacroCallLoc, MacroDefId, MacroDefKind,
|
||||||
|
};
|
||||||
|
|
||||||
macro_rules! register_builtin {
|
macro_rules! register_builtin {
|
||||||
( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
|
( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -257,7 +258,7 @@ fn format_args_expand(
|
||||||
let mut args = parse_exprs_with_sep(tt, ',');
|
let mut args = parse_exprs_with_sep(tt, ',');
|
||||||
|
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
|
return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule.into());
|
||||||
}
|
}
|
||||||
for arg in &mut args {
|
for arg in &mut args {
|
||||||
// Remove `key =`.
|
// Remove `key =`.
|
||||||
|
@ -368,12 +369,12 @@ fn compile_error_expand(
|
||||||
let text = it.text.as_str();
|
let text = it.text.as_str();
|
||||||
if text.starts_with('"') && text.ends_with('"') {
|
if text.starts_with('"') && text.ends_with('"') {
|
||||||
// FIXME: does not handle raw strings
|
// FIXME: does not handle raw strings
|
||||||
mbe::ExpandError::Other(text[1..text.len() - 1].into())
|
ExpandError::Other(text[1..text.len() - 1].into())
|
||||||
} else {
|
} else {
|
||||||
mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into())
|
ExpandError::Other("`compile_error!` argument must be a string".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
|
_ => ExpandError::Other("`compile_error!` argument must be a string".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) }
|
ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) }
|
||||||
|
@ -414,7 +415,7 @@ fn concat_expand(
|
||||||
}
|
}
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||||
_ => {
|
_ => {
|
||||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken);
|
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,7 +436,7 @@ fn concat_idents_expand(
|
||||||
}
|
}
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||||
_ => {
|
_ => {
|
||||||
err.get_or_insert(mbe::ExpandError::UnexpectedToken);
|
err.get_or_insert(mbe::ExpandError::UnexpectedToken.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,28 +449,28 @@ fn relative_file(
|
||||||
call_id: MacroCallId,
|
call_id: MacroCallId,
|
||||||
path_str: &str,
|
path_str: &str,
|
||||||
allow_recursion: bool,
|
allow_recursion: bool,
|
||||||
) -> Result<FileId, mbe::ExpandError> {
|
) -> Result<FileId, ExpandError> {
|
||||||
let call_site = call_id.as_file().original_file(db);
|
let call_site = call_id.as_file().original_file(db);
|
||||||
let path = AnchoredPath { anchor: call_site, path: path_str };
|
let path = AnchoredPath { anchor: call_site, path: path_str };
|
||||||
let res = db.resolve_path(path).ok_or_else(|| {
|
let res = db
|
||||||
mbe::ExpandError::Other(format!("failed to load file `{path_str}`").into())
|
.resolve_path(path)
|
||||||
})?;
|
.ok_or_else(|| ExpandError::Other(format!("failed to load file `{path_str}`").into()))?;
|
||||||
// Prevent include itself
|
// Prevent include itself
|
||||||
if res == call_site && !allow_recursion {
|
if res == call_site && !allow_recursion {
|
||||||
Err(mbe::ExpandError::Other(format!("recursive inclusion of `{path_str}`").into()))
|
Err(ExpandError::Other(format!("recursive inclusion of `{path_str}`").into()))
|
||||||
} else {
|
} else {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_string(tt: &tt::Subtree) -> Result<String, mbe::ExpandError> {
|
fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
|
||||||
tt.token_trees
|
tt.token_trees
|
||||||
.get(0)
|
.get(0)
|
||||||
.and_then(|tt| match tt {
|
.and_then(|tt| match tt {
|
||||||
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
|
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(it),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.ok_or(mbe::ExpandError::ConversionError)
|
.ok_or(mbe::ExpandError::ConversionError.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn include_expand(
|
fn include_expand(
|
||||||
|
@ -561,7 +562,7 @@ fn env_expand(
|
||||||
// The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
|
// The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
|
||||||
// unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
|
// unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
|
||||||
if key == "OUT_DIR" {
|
if key == "OUT_DIR" {
|
||||||
err = Some(mbe::ExpandError::Other(
|
err = Some(ExpandError::Other(
|
||||||
r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(),
|
r#"`OUT_DIR` not set, enable "run build scripts" to fix"#.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
||||||
use base_db::{salsa, SourceDatabase};
|
use base_db::{salsa, SourceDatabase};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use limit::Limit;
|
use limit::Limit;
|
||||||
use mbe::{syntax_node_to_token_tree, ExpandError, ExpandResult};
|
use mbe::syntax_node_to_token_tree;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::diff,
|
algo::diff,
|
||||||
|
@ -15,8 +15,9 @@ use syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast_id_map::AstIdMap, fixup, hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander,
|
ast_id_map::AstIdMap, fixup, hygiene::HygieneFrame, BuiltinAttrExpander, BuiltinDeriveExpander,
|
||||||
BuiltinFnLikeExpander, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
|
BuiltinFnLikeExpander, ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr,
|
||||||
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
|
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile,
|
||||||
|
ProcMacroExpander,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Total limit on the number of tokens produced by any macro invocation.
|
/// Total limit on the number of tokens produced by any macro invocation.
|
||||||
|
@ -47,10 +48,10 @@ impl TokenExpander {
|
||||||
db: &dyn AstDatabase,
|
db: &dyn AstDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
tt: &tt::Subtree,
|
tt: &tt::Subtree,
|
||||||
) -> mbe::ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
match self {
|
match self {
|
||||||
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt),
|
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
|
||||||
TokenExpander::Builtin(it) => it.expand(db, id, tt),
|
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
|
||||||
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
|
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
|
||||||
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
|
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
|
||||||
TokenExpander::ProcMacro(_) => {
|
TokenExpander::ProcMacro(_) => {
|
||||||
|
@ -432,7 +433,11 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
|
||||||
|
|
||||||
let macro_arg = match db.macro_arg(id) {
|
let macro_arg = match db.macro_arg(id) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return ExpandResult::str_err("Failed to lower macro args to token tree".into()),
|
None => {
|
||||||
|
return ExpandResult::only_err(ExpandError::Other(
|
||||||
|
"Failed to lower macro args to token tree".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let expander = match db.macro_def(loc.def) {
|
let expander = match db.macro_def(loc.def) {
|
||||||
|
@ -440,16 +445,23 @@ fn macro_expand(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<Option<Ar
|
||||||
// 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).
|
||||||
Err(err) => return ExpandResult::str_err(format!("invalid macro definition: {}", err)),
|
Err(err) => {
|
||||||
|
return ExpandResult::only_err(ExpandError::Other(
|
||||||
|
format!("invalid macro definition: {}", err).into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let ExpandResult { value: mut tt, err } = expander.expand(db, id, ¯o_arg.0);
|
let ExpandResult { value: mut tt, err } = expander.expand(db, id, ¯o_arg.0);
|
||||||
// Set a hard limit for the expanded tt
|
// Set a hard limit for the expanded tt
|
||||||
let count = tt.count();
|
let count = tt.count();
|
||||||
if TOKEN_LIMIT.check(count).is_err() {
|
if TOKEN_LIMIT.check(count).is_err() {
|
||||||
return ExpandResult::str_err(format!(
|
return ExpandResult::only_err(ExpandError::Other(
|
||||||
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
format!(
|
||||||
count,
|
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
|
||||||
TOKEN_LIMIT.inner(),
|
count,
|
||||||
|
TOKEN_LIMIT.inner(),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +478,9 @@ fn expand_proc_macro(db: &dyn AstDatabase, id: MacroCallId) -> ExpandResult<tt::
|
||||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
|
||||||
let macro_arg = match db.macro_arg(id) {
|
let macro_arg = match db.macro_arg(id) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return ExpandResult::str_err("No arguments for proc-macro".to_string()),
|
None => {
|
||||||
|
return ExpandResult::only_err(ExpandError::Other("No arguments for proc-macro".into()))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let expander = match loc.def.kind {
|
let expander = match loc.def.kind {
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::CrateId;
|
use base_db::CrateId;
|
||||||
use mbe::ExpandResult;
|
|
||||||
use syntax::{ted, SyntaxNode};
|
use syntax::{ted, SyntaxNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -29,8 +28,8 @@ use crate::{
|
||||||
db::AstDatabase,
|
db::AstDatabase,
|
||||||
hygiene::Hygiene,
|
hygiene::Hygiene,
|
||||||
mod_path::ModPath,
|
mod_path::ModPath,
|
||||||
EagerCallInfo, ExpandTo, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
|
EagerCallInfo, ExpandError, ExpandResult, ExpandTo, InFile, MacroCallId, MacroCallKind,
|
||||||
MacroDefKind, UnresolvedMacro,
|
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -39,12 +38,12 @@ pub struct ErrorEmitted {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ErrorSink {
|
pub trait ErrorSink {
|
||||||
fn emit(&mut self, err: mbe::ExpandError);
|
fn emit(&mut self, err: ExpandError);
|
||||||
|
|
||||||
fn option<T>(
|
fn option<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
opt: Option<T>,
|
opt: Option<T>,
|
||||||
error: impl FnOnce() -> mbe::ExpandError,
|
error: impl FnOnce() -> ExpandError,
|
||||||
) -> Result<T, ErrorEmitted> {
|
) -> Result<T, ErrorEmitted> {
|
||||||
match opt {
|
match opt {
|
||||||
Some(it) => Ok(it),
|
Some(it) => Ok(it),
|
||||||
|
@ -58,12 +57,12 @@ pub trait ErrorSink {
|
||||||
fn option_with<T>(
|
fn option_with<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
opt: impl FnOnce() -> Option<T>,
|
opt: impl FnOnce() -> Option<T>,
|
||||||
error: impl FnOnce() -> mbe::ExpandError,
|
error: impl FnOnce() -> ExpandError,
|
||||||
) -> Result<T, ErrorEmitted> {
|
) -> Result<T, ErrorEmitted> {
|
||||||
self.option(opt(), error)
|
self.option(opt(), error)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn result<T>(&mut self, res: Result<T, mbe::ExpandError>) -> Result<T, ErrorEmitted> {
|
fn result<T>(&mut self, res: Result<T, ExpandError>) -> Result<T, ErrorEmitted> {
|
||||||
match res {
|
match res {
|
||||||
Ok(it) => Ok(it),
|
Ok(it) => Ok(it),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -90,8 +89,8 @@ pub trait ErrorSink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorSink for &'_ mut dyn FnMut(mbe::ExpandError) {
|
impl ErrorSink for &'_ mut dyn FnMut(ExpandError) {
|
||||||
fn emit(&mut self, err: mbe::ExpandError) {
|
fn emit(&mut self, err: ExpandError) {
|
||||||
self(err);
|
self(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,7 +101,7 @@ pub fn expand_eager_macro(
|
||||||
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(mbe::ExpandError),
|
diagnostic_sink: &mut dyn FnMut(ExpandError),
|
||||||
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
|
||||||
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
|
||||||
|
@ -147,7 +146,7 @@ pub fn expand_eager_macro(
|
||||||
if let MacroDefKind::BuiltInEager(eager, _) = def.kind {
|
if let MacroDefKind::BuiltInEager(eager, _) = def.kind {
|
||||||
let res = eager.expand(db, arg_id, &subtree);
|
let res = eager.expand(db, arg_id, &subtree);
|
||||||
if let Some(err) = res.err {
|
if let Some(err) = res.err {
|
||||||
diagnostic_sink(err);
|
diagnostic_sink(err.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let loc = MacroCallLoc {
|
let loc = MacroCallLoc {
|
||||||
|
@ -199,7 +198,7 @@ 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(mbe::ExpandError),
|
mut diagnostic_sink: &mut dyn FnMut(ExpandError),
|
||||||
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
|
) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
|
||||||
let original = curr.value.clone_for_update();
|
let original = curr.value.clone_for_update();
|
||||||
|
|
||||||
|
@ -211,7 +210,7 @@ fn eager_macro_recur(
|
||||||
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_else(|| UnresolvedMacro { path })?,
|
Some(path) => macro_resolver(path.clone()).ok_or_else(|| UnresolvedMacro { path })?,
|
||||||
None => {
|
None => {
|
||||||
diagnostic_sink(mbe::ExpandError::Other("malformed macro invocation".into()));
|
diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,9 +17,9 @@ pub mod eager;
|
||||||
pub mod mod_path;
|
pub mod mod_path;
|
||||||
mod fixup;
|
mod fixup;
|
||||||
|
|
||||||
pub use mbe::{ExpandError, ExpandResult, Origin};
|
pub use mbe::{Origin, ValueResult};
|
||||||
|
|
||||||
use std::{hash::Hash, iter, sync::Arc};
|
use std::{fmt, hash::Hash, iter, sync::Arc};
|
||||||
|
|
||||||
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange, ProcMacroKind};
|
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange, ProcMacroKind};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
|
@ -39,6 +39,31 @@ use crate::{
|
||||||
proc_macro::ProcMacroExpander,
|
proc_macro::ProcMacroExpander,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum ExpandError {
|
||||||
|
UnresolvedProcMacro,
|
||||||
|
Mbe(mbe::ExpandError),
|
||||||
|
Other(Box<str>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<mbe::ExpandError> for ExpandError {
|
||||||
|
fn from(mbe: mbe::ExpandError) -> Self {
|
||||||
|
Self::Mbe(mbe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ExpandError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc-macro"),
|
||||||
|
ExpandError::Mbe(it) => it.fmt(f),
|
||||||
|
ExpandError::Other(it) => f.write_str(it),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Input to the analyzer is a set of files, where each file is identified by
|
/// Input to the analyzer is a set of files, where each file is identified by
|
||||||
/// `FileId` and contains source code. However, another source of source code in
|
/// `FileId` and contains source code. However, another source of source code in
|
||||||
/// Rust are macros: each macro can be thought of as producing a "temporary
|
/// Rust are macros: each macro can be thought of as producing a "temporary
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
//! Proc Macro Expander stub
|
//! Proc Macro Expander stub
|
||||||
|
|
||||||
use base_db::{CrateId, ProcMacroExpansionError, ProcMacroId, ProcMacroKind};
|
use base_db::{CrateId, ProcMacroExpansionError, ProcMacroId, ProcMacroKind};
|
||||||
use mbe::ExpandResult;
|
|
||||||
|
|
||||||
use crate::db::AstDatabase;
|
use crate::{db::AstDatabase, ExpandError, ExpandResult};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
pub struct ProcMacroExpander {
|
pub struct ProcMacroExpander {
|
||||||
|
@ -37,7 +36,11 @@ impl ProcMacroExpander {
|
||||||
let krate_graph = db.crate_graph();
|
let krate_graph = db.crate_graph();
|
||||||
let proc_macro = match krate_graph[self.krate].proc_macro.get(id.0 as usize) {
|
let proc_macro = match krate_graph[self.krate].proc_macro.get(id.0 as usize) {
|
||||||
Some(proc_macro) => proc_macro,
|
Some(proc_macro) => proc_macro,
|
||||||
None => return ExpandResult::str_err("No proc-macro found.".to_string()),
|
None => {
|
||||||
|
return ExpandResult::only_err(ExpandError::Other(
|
||||||
|
"No proc-macro found.".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Proc macros have access to the environment variables of the invoking crate.
|
// Proc macros have access to the environment variables of the invoking crate.
|
||||||
|
@ -51,17 +54,17 @@ impl ProcMacroExpander {
|
||||||
{
|
{
|
||||||
ExpandResult {
|
ExpandResult {
|
||||||
value: tt.clone(),
|
value: tt.clone(),
|
||||||
err: Some(mbe::ExpandError::Other(text.into())),
|
err: Some(ExpandError::Other(text.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ProcMacroExpansionError::System(text)
|
ProcMacroExpansionError::System(text)
|
||||||
| ProcMacroExpansionError::Panic(text) => {
|
| ProcMacroExpansionError::Panic(text) => {
|
||||||
ExpandResult::only_err(mbe::ExpandError::Other(text.into()))
|
ExpandResult::only_err(ExpandError::Other(text.into()))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => ExpandResult::only_err(mbe::ExpandError::UnresolvedProcMacro),
|
None => ExpandResult::only_err(ExpandError::UnresolvedProcMacro),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -599,7 +599,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
|
||||||
src = it;
|
src = it;
|
||||||
res.unmatched_tts += src.len();
|
res.unmatched_tts += src.len();
|
||||||
}
|
}
|
||||||
res.add_err(ExpandError::binding_error("leftover tokens"));
|
res.add_err(ExpandError::LeftoverTokens);
|
||||||
|
|
||||||
if let Some(error_reover_item) = error_recover_item {
|
if let Some(error_reover_item) = error_recover_item {
|
||||||
res.bindings = bindings_builder.build(&error_reover_item);
|
res.bindings = bindings_builder.build(&error_reover_item);
|
||||||
|
@ -658,7 +658,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
|
||||||
fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter) -> Result<(), ExpandError> {
|
fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter) -> Result<(), ExpandError> {
|
||||||
let rhs = src
|
let rhs = src
|
||||||
.expect_leaf()
|
.expect_leaf()
|
||||||
.map_err(|()| ExpandError::BindingError(format!("expected leaf: `{lhs}`").into()))?;
|
.map_err(|()| ExpandError::binding_error(format!("expected leaf: `{lhs}`")))?;
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(
|
(
|
||||||
tt::Leaf::Punct(tt::Punct { char: lhs, .. }),
|
tt::Leaf::Punct(tt::Punct { char: lhs, .. }),
|
||||||
|
|
|
@ -17,7 +17,7 @@ impl Bindings {
|
||||||
|
|
||||||
fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
|
fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
|
||||||
macro_rules! binding_err {
|
macro_rules! binding_err {
|
||||||
($($arg:tt)*) => { ExpandError::BindingError(format!($($arg)*).into()) };
|
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut b: &Binding =
|
let mut b: &Binding =
|
||||||
|
@ -178,7 +178,7 @@ fn expand_repeat(
|
||||||
);
|
);
|
||||||
return ExpandResult {
|
return ExpandResult {
|
||||||
value: Fragment::Tokens(Subtree::default().into()),
|
value: Fragment::Tokens(Subtree::default().into()),
|
||||||
err: Some(ExpandError::Other("Expand exceed limit".into())),
|
err: Some(ExpandError::LimitExceeded),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,18 +67,17 @@ impl fmt::Display for ParseError {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum ExpandError {
|
pub enum ExpandError {
|
||||||
|
BindingError(Box<Box<str>>),
|
||||||
|
LeftoverTokens,
|
||||||
|
ConversionError,
|
||||||
|
LimitExceeded,
|
||||||
NoMatchingRule,
|
NoMatchingRule,
|
||||||
UnexpectedToken,
|
UnexpectedToken,
|
||||||
BindingError(Box<str>),
|
|
||||||
ConversionError,
|
|
||||||
// FIXME: no way mbe should know about proc macros.
|
|
||||||
UnresolvedProcMacro,
|
|
||||||
Other(Box<str>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpandError {
|
impl ExpandError {
|
||||||
fn binding_error(e: &str) -> ExpandError {
|
fn binding_error(e: impl Into<Box<str>>) -> ExpandError {
|
||||||
ExpandError::BindingError(e.into())
|
ExpandError::BindingError(Box::new(e.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +88,8 @@ impl fmt::Display for ExpandError {
|
||||||
ExpandError::UnexpectedToken => f.write_str("unexpected token in input"),
|
ExpandError::UnexpectedToken => f.write_str("unexpected token in input"),
|
||||||
ExpandError::BindingError(e) => f.write_str(e),
|
ExpandError::BindingError(e) => f.write_str(e),
|
||||||
ExpandError::ConversionError => f.write_str("could not convert tokens"),
|
ExpandError::ConversionError => f.write_str("could not convert tokens"),
|
||||||
ExpandError::UnresolvedProcMacro => f.write_str("unresolved proc macro"),
|
ExpandError::LimitExceeded => f.write_str("Expand exceed limit"),
|
||||||
ExpandError::Other(e) => f.write_str(e),
|
ExpandError::LeftoverTokens => f.write_str("leftover tokens"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,42 +310,41 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct ExpandResult<T> {
|
pub struct ValueResult<T, E> {
|
||||||
pub value: T,
|
pub value: T,
|
||||||
pub err: Option<ExpandError>,
|
pub err: Option<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ExpandResult<T> {
|
impl<T, E> ValueResult<T, E> {
|
||||||
pub fn ok(value: T) -> Self {
|
pub fn ok(value: T) -> Self {
|
||||||
Self { value, err: None }
|
Self { value, err: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn only_err(err: ExpandError) -> Self
|
pub fn only_err(err: E) -> Self
|
||||||
where
|
where
|
||||||
T: Default,
|
T: Default,
|
||||||
{
|
{
|
||||||
Self { value: Default::default(), err: Some(err) }
|
Self { value: Default::default(), err: Some(err) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn str_err(err: String) -> Self
|
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ValueResult<U, E> {
|
||||||
where
|
ValueResult { value: f(self.value), err: self.err }
|
||||||
T: Default,
|
|
||||||
{
|
|
||||||
Self::only_err(ExpandError::Other(err.into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ExpandResult<U> {
|
pub fn map_err<E2>(self, f: impl FnOnce(E) -> E2) -> ValueResult<T, E2> {
|
||||||
ExpandResult { value: f(self.value), err: self.err }
|
ValueResult { value: self.value, err: self.err.map(f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn result(self) -> Result<T, ExpandError> {
|
pub fn result(self) -> Result<T, E> {
|
||||||
self.err.map_or(Ok(self.value), Err)
|
self.err.map_or(Ok(self.value), Err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> {
|
impl<T: Default, E> From<Result<T, E>> for ValueResult<T, E> {
|
||||||
fn from(result: Result<T, ExpandError>) -> Self {
|
fn from(result: Result<T, E>) -> Self {
|
||||||
result.map_or_else(Self::only_err, Self::ok)
|
result.map_or_else(Self::only_err, Self::ok)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ impl<'a> TtIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let err = if error || !cursor.is_root() {
|
let err = if error || !cursor.is_root() {
|
||||||
Some(ExpandError::BindingError(format!("expected {entry_point:?}").into()))
|
Some(ExpandError::binding_error(format!("expected {entry_point:?}")))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue