3494: Implement include macro r=matklad a=edwin0cheng

This PR implement builtin `include` macro.

* It does not support include as expression yet.
* It doesn't consider `env!("OUT_DIR")` yet.


Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
bors[bot] 2020-03-06 15:08:41 +00:00 committed by GitHub
commit aa82b5915d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 19 deletions

View file

@ -5,8 +5,9 @@ use crate::{
name, AstId, CrateId, MacroDefId, MacroDefKind, TextUnit, name, AstId, CrateId, MacroDefId, MacroDefKind, TextUnit,
}; };
use crate::{quote, LazyMacroId}; use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId};
use either::Either; use either::Either;
use ra_db::{FileId, RelativePath};
use ra_parser::FragmentKind; use ra_parser::FragmentKind;
macro_rules! register_builtin { macro_rules! register_builtin {
@ -38,12 +39,14 @@ macro_rules! register_builtin {
impl EagerExpander { impl EagerExpander {
pub fn expand( pub fn expand(
&self, &self,
db: &dyn AstDatabase,
arg_id: EagerMacroId,
tt: &tt::Subtree, tt: &tt::Subtree,
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { ) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
let expander = match *self { let expander = match *self {
$( EagerExpander::$e_kind => $e_expand, )* $( EagerExpander::$e_kind => $e_expand, )*
}; };
expander(tt) expander(db,arg_id,tt)
} }
} }
@ -80,7 +83,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, (compile_error, CompileError) => compile_error_expand,
(file, File) => file_expand, (file, File) => file_expand,
@ -94,8 +96,8 @@ register_builtin! {
(format_args_nl, FormatArgsNl) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_expand,
EAGER: EAGER:
// eagers (concat, Concat) => concat_expand,
(concat, Concat) => concat_expand (include, Include) => include_expand
} }
fn line_expand( fn line_expand(
@ -251,7 +253,11 @@ fn unquote_str(lit: &tt::Literal) -> Option<String> {
token.value() token.value()
} }
fn concat_expand(tt: &tt::Subtree) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> { fn concat_expand(
_db: &dyn AstDatabase,
_arg_id: EagerMacroId,
tt: &tt::Subtree,
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
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 {
@ -266,6 +272,40 @@ fn concat_expand(tt: &tt::Subtree) -> Result<(tt::Subtree, FragmentKind), mbe::E
Ok((quote!(#text), FragmentKind::Expr)) Ok((quote!(#text), FragmentKind::Expr))
} }
fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Option<FileId> {
let call_site = call_id.as_file().original_file(db);
let path = RelativePath::new(&path);
db.resolve_relative_path(call_site, &path)
}
fn include_expand(
db: &dyn AstDatabase,
arg_id: EagerMacroId,
tt: &tt::Subtree,
) -> Result<(tt::Subtree, FragmentKind), mbe::ExpandError> {
let path = tt
.token_trees
.get(0)
.and_then(|tt| match tt {
tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => unquote_str(&it),
_ => None,
})
.ok_or_else(|| mbe::ExpandError::ConversionError)?;
let file_id =
relative_file(db, arg_id.into(), &path).ok_or_else(|| mbe::ExpandError::ConversionError)?;
// FIXME:
// Handle include as expression
let node =
db.parse_or_expand(file_id.into()).ok_or_else(|| mbe::ExpandError::ConversionError)?;
let res =
mbe::syntax_node_to_token_tree(&node).ok_or_else(|| mbe::ExpandError::ConversionError)?.0;
Ok((res, FragmentKind::Items))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -42,24 +42,26 @@ pub fn expand_eager_macro(
// When `lazy_expand` is called, its *parent* file must be already exists. // When `lazy_expand` is called, its *parent* file must be already exists.
// Here we store an eager macro id for the argument expanded subtree here // Here we store an eager macro id for the argument expanded subtree here
// for that purpose. // for that purpose.
let arg_id: MacroCallId = db let arg_id = db.intern_eager_expansion({
.intern_eager_expansion({ EagerCallLoc {
EagerCallLoc { def,
def, fragment: FragmentKind::Expr,
fragment: FragmentKind::Expr, subtree: Arc::new(parsed_args.clone()),
subtree: Arc::new(parsed_args.clone()), file_id: macro_call.file_id,
file_id: macro_call.file_id, }
} });
}) let arg_file_id: MacroCallId = arg_id.into();
.into();
let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0;
let result = let result = eager_macro_recur(
eager_macro_recur(db, InFile::new(arg_id.as_file(), parsed_args.syntax_node()), resolver)?; db,
InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
resolver,
)?;
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(&subtree).ok()?; let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
let eager = let eager =
EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id };

View file

@ -174,6 +174,7 @@ pub mod known {
line, line,
stringify, stringify,
concat, concat,
include,
format_args, format_args,
format_args_nl, format_args_nl,
env, env,

View file

@ -438,6 +438,51 @@ fn main() {
); );
} }
#[test]
fn infer_builtin_macros_include() {
let (db, pos) = TestDB::with_position(
r#"
//- /main.rs
#[rustc_builtin_macro]
macro_rules! include {() => {}}
include!("foo.rs");
fn main() {
bar()<|>;
}
//- /foo.rs
fn bar() -> u32 {0}
"#,
);
assert_eq!("u32", type_at_pos(&db, pos));
}
#[test]
fn infer_builtin_macros_include_concat() {
let (db, pos) = TestDB::with_position(
r#"
//- /main.rs
#[rustc_builtin_macro]
macro_rules! include {() => {}}
#[rustc_builtin_macro]
macro_rules! concat {() => {}}
include!(concat!("f", "oo.rs"));
fn main() {
bar()<|>;
}
//- /foo.rs
fn bar() -> u32 {0}
"#,
);
assert_eq!("u32", type_at_pos(&db, pos));
}
#[test] #[test]
fn infer_builtin_macros_concat_with_lazy() { fn infer_builtin_macros_concat_with_lazy() {
assert_snapshot!( assert_snapshot!(