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