mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 04:23:25 +00:00
builtin_macro: move to mbe::ExpandResult
This commit is contained in:
parent
70eb170271
commit
92f52c5c9a
3 changed files with 79 additions and 50 deletions
|
@ -6,7 +6,7 @@ use crate::{
|
|||
|
||||
use base_db::FileId;
|
||||
use either::Either;
|
||||
use mbe::parse_to_token_tree;
|
||||
use mbe::{parse_to_token_tree, ExpandResult};
|
||||
use parser::FragmentKind;
|
||||
use syntax::ast::{self, AstToken};
|
||||
|
||||
|
@ -28,7 +28,7 @@ macro_rules! register_builtin {
|
|||
db: &dyn AstDatabase,
|
||||
id: LazyMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let expander = match *self {
|
||||
$( BuiltinFnLikeExpander::$kind => $expand, )*
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ macro_rules! register_builtin {
|
|||
db: &dyn AstDatabase,
|
||||
arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
let expander = match *self {
|
||||
$( EagerExpander::$e_kind => $e_expand, )*
|
||||
};
|
||||
|
@ -109,25 +109,28 @@ fn line_expand(
|
|||
_db: &dyn AstDatabase,
|
||||
_id: LazyMacroId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// dummy implementation for type-checking purposes
|
||||
let line_num = 0;
|
||||
let expanded = quote! {
|
||||
#line_num
|
||||
};
|
||||
|
||||
Ok(expanded)
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn stringify_expand(
|
||||
db: &dyn AstDatabase,
|
||||
id: LazyMacroId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
let loc = db.lookup_intern_macro(id);
|
||||
|
||||
let macro_content = {
|
||||
let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
|
||||
let arg = match loc.kind.arg(db) {
|
||||
Some(arg) => arg,
|
||||
None => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
|
||||
};
|
||||
let macro_args = arg;
|
||||
let text = macro_args.text();
|
||||
let without_parens = TextSize::of('(')..text.len() - TextSize::of(')');
|
||||
|
@ -138,28 +141,28 @@ fn stringify_expand(
|
|||
#macro_content
|
||||
};
|
||||
|
||||
Ok(expanded)
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn column_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: LazyMacroId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// dummy implementation for type-checking purposes
|
||||
let col_num = 0;
|
||||
let expanded = quote! {
|
||||
#col_num
|
||||
};
|
||||
|
||||
Ok(expanded)
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn assert_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: LazyMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// A hacky implementation for goto def and hover
|
||||
// We expand `assert!(cond, arg1, arg2)` to
|
||||
// ```
|
||||
|
@ -191,14 +194,14 @@ fn assert_expand(
|
|||
let expanded = quote! {
|
||||
{ { (##arg_tts); } }
|
||||
};
|
||||
Ok(expanded)
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn file_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: LazyMacroId,
|
||||
_tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// FIXME: RA purposefully lacks knowledge of absolute file names
|
||||
// so just return "".
|
||||
let file_name = "";
|
||||
|
@ -207,31 +210,33 @@ fn file_expand(
|
|||
#file_name
|
||||
};
|
||||
|
||||
Ok(expanded)
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn compile_error_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: LazyMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> 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 Ok(quote! { loop { #it }});
|
||||
return ExpandResult::ok(quote! { loop { #it }});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Err(mbe::ExpandError::BindingError("Must be a string".into()))
|
||||
ExpandResult::only_err(mbe::ExpandError::BindingError(
|
||||
"`compile_error!` argument be a string".into(),
|
||||
))
|
||||
}
|
||||
|
||||
fn format_args_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_id: LazyMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<tt::Subtree, mbe::ExpandError> {
|
||||
) -> ExpandResult<tt::Subtree> {
|
||||
// We expand `format_args!("", a1, a2)` to
|
||||
// ```
|
||||
// std::fmt::Arguments::new_v1(&[], &[
|
||||
|
@ -257,7 +262,7 @@ fn format_args_expand(
|
|||
args.push(current);
|
||||
}
|
||||
if args.is_empty() {
|
||||
return Err(mbe::ExpandError::NoMatchingRule);
|
||||
return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
|
||||
}
|
||||
let _format_string = args.remove(0);
|
||||
let arg_tts = args.into_iter().flat_map(|arg| {
|
||||
|
@ -266,7 +271,7 @@ fn format_args_expand(
|
|||
let expanded = quote! {
|
||||
std::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
||||
};
|
||||
Ok(expanded)
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn unquote_str(lit: &tt::Literal) -> Option<String> {
|
||||
|
@ -279,19 +284,24 @@ fn concat_expand(
|
|||
_db: &dyn AstDatabase,
|
||||
_arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
let mut text = String::new();
|
||||
for (i, t) in tt.token_trees.iter().enumerate() {
|
||||
match t {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) if i % 2 == 0 => {
|
||||
text += &unquote_str(&it).ok_or_else(|| mbe::ExpandError::ConversionError)?;
|
||||
text += &match unquote_str(&it) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
return ExpandResult::only_err(mbe::ExpandError::ConversionError);
|
||||
}
|
||||
};
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
|
||||
_ => return Err(mbe::ExpandError::UnexpectedToken),
|
||||
_ => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
|
||||
}
|
||||
}
|
||||
|
||||
Ok((quote!(#text), FragmentKind::Expr))
|
||||
ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr)))
|
||||
}
|
||||
|
||||
fn relative_file(
|
||||
|
@ -324,26 +334,35 @@ fn include_expand(
|
|||
db: &dyn AstDatabase,
|
||||
arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
let path = parse_string(tt)?;
|
||||
let file_id = relative_file(db, arg_id.into(), &path, false)
|
||||
.ok_or_else(|| mbe::ExpandError::ConversionError)?;
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
let res = (|| {
|
||||
let path = parse_string(tt)?;
|
||||
let file_id = relative_file(db, arg_id.into(), &path, false)
|
||||
.ok_or_else(|| mbe::ExpandError::ConversionError)?;
|
||||
|
||||
// FIXME:
|
||||
// Handle include as expression
|
||||
let res = parse_to_token_tree(&db.file_text(file_id))
|
||||
.ok_or_else(|| mbe::ExpandError::ConversionError)?
|
||||
.0;
|
||||
Ok(parse_to_token_tree(&db.file_text(file_id))
|
||||
.ok_or_else(|| mbe::ExpandError::ConversionError)?
|
||||
.0)
|
||||
})();
|
||||
|
||||
Ok((res, FragmentKind::Items))
|
||||
match res {
|
||||
Ok(res) => {
|
||||
// FIXME:
|
||||
// Handle include as expression
|
||||
ExpandResult::ok(Some((res, FragmentKind::Items)))
|
||||
}
|
||||
Err(e) => ExpandResult::only_err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn include_bytes_expand(
|
||||
_db: &dyn AstDatabase,
|
||||
_arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
let _path = parse_string(tt)?;
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
if let Err(e) = parse_string(tt) {
|
||||
return ExpandResult::only_err(e);
|
||||
}
|
||||
|
||||
// FIXME: actually read the file here if the user asked for macro expansion
|
||||
let res = tt::Subtree {
|
||||
|
@ -353,15 +372,18 @@ fn include_bytes_expand(
|
|||
id: tt::TokenId::unspecified(),
|
||||
}))],
|
||||
};
|
||||
Ok((res, FragmentKind::Expr))
|
||||
ExpandResult::ok(Some((res, FragmentKind::Expr)))
|
||||
}
|
||||
|
||||
fn include_str_expand(
|
||||
db: &dyn AstDatabase,
|
||||
arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
let path = parse_string(tt)?;
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
let path = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
};
|
||||
|
||||
// FIXME: we're not able to read excluded files (which is most of them because
|
||||
// it's unusual to `include_str!` a Rust file), but we can return an empty string.
|
||||
|
@ -370,14 +392,14 @@ fn include_str_expand(
|
|||
let file_id = match relative_file(db, arg_id.into(), &path, true) {
|
||||
Some(file_id) => file_id,
|
||||
None => {
|
||||
return Ok((quote!(""), FragmentKind::Expr));
|
||||
return ExpandResult::ok(Some((quote!(""), FragmentKind::Expr)));
|
||||
}
|
||||
};
|
||||
|
||||
let text = db.file_text(file_id);
|
||||
let text = &*text;
|
||||
|
||||
Ok((quote!(#text), FragmentKind::Expr))
|
||||
ExpandResult::ok(Some((quote!(#text), FragmentKind::Expr)))
|
||||
}
|
||||
|
||||
fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
|
||||
|
@ -389,8 +411,11 @@ fn env_expand(
|
|||
db: &dyn AstDatabase,
|
||||
arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
let key = parse_string(tt)?;
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
};
|
||||
|
||||
// FIXME:
|
||||
// If the environment variable is not defined int rustc, then a compilation error will be emitted.
|
||||
|
@ -402,21 +427,25 @@ fn env_expand(
|
|||
let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string());
|
||||
let expanded = quote! { #s };
|
||||
|
||||
Ok((expanded, FragmentKind::Expr))
|
||||
ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
|
||||
}
|
||||
|
||||
fn option_env_expand(
|
||||
db: &dyn AstDatabase,
|
||||
arg_id: EagerMacroId,
|
||||
tt: &tt::Subtree,
|
||||
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
|
||||
let key = parse_string(tt)?;
|
||||
) -> ExpandResult<Option<(tt::Subtree, FragmentKind)>> {
|
||||
let key = match parse_string(tt) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return ExpandResult::only_err(e),
|
||||
};
|
||||
|
||||
let expanded = match get_env_inner(db, arg_id, &key) {
|
||||
None => quote! { std::option::Option::None::<&str> },
|
||||
Some(s) => quote! { std::option::Some(#s) },
|
||||
};
|
||||
|
||||
Ok((expanded, FragmentKind::Expr))
|
||||
ExpandResult::ok(Some((expanded, FragmentKind::Expr)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -485,7 +514,7 @@ mod tests {
|
|||
}
|
||||
});
|
||||
|
||||
let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).unwrap();
|
||||
let (subtree, fragment) = expander.expand(&db, arg_id, &parsed_args).value.unwrap();
|
||||
let eager = EagerCallLoc {
|
||||
def,
|
||||
fragment,
|
||||
|
|
|
@ -30,8 +30,8 @@ impl TokenExpander {
|
|||
) -> mbe::ExpandResult<tt::Subtree> {
|
||||
match self {
|
||||
TokenExpander::MacroRules(it) => it.expand(tt),
|
||||
TokenExpander::Builtin(it) => it.expand(db, id, tt),
|
||||
// FIXME switch these to ExpandResult as well
|
||||
TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
|
||||
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
|
||||
TokenExpander::ProcMacro(_) => {
|
||||
// We store the result in salsa db to prevent non-determinisc behavior in
|
||||
|
|
|
@ -65,7 +65,7 @@ pub fn expand_eager_macro(
|
|||
let subtree = to_subtree(&result)?;
|
||||
|
||||
if let MacroDefKind::BuiltInEager(eager) = def.kind {
|
||||
let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
|
||||
let (subtree, fragment) = eager.expand(db, arg_id, &subtree).value?;
|
||||
let eager = EagerCallLoc {
|
||||
def,
|
||||
fragment,
|
||||
|
|
Loading…
Reference in a new issue