2342: Use proper MacroFileKind in `SourceAnalyzer` r=matklad a=edwin0cheng

* Add `MacroFileKind::Statements`
* Add `to_macro_file_kind` in `source_binding.rs` to set a proper `MacroFileKind` when expanding a macro. 
* Add a test for trying expanding `match_ast` which is not correct before this PR.
* Fix some spacing issues in `insert_whitespaces`

Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
bors[bot] 2019-11-21 19:31:19 +00:00 committed by GitHub
commit c9273828b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 93 additions and 13 deletions

View File

@ -131,6 +131,7 @@ pub struct ReferenceDescriptor {
}
pub struct Expansion {
macro_file_kind: MacroFileKind,
macro_call_id: MacroCallId,
}
@ -145,7 +146,7 @@ impl Expansion {
}
pub fn file_id(&self) -> HirFileId {
self.macro_call_id.as_file(MacroFileKind::Items)
self.macro_call_id.as_file(self.macro_file_kind)
}
}
@ -439,7 +440,10 @@ impl SourceAnalyzer {
db.ast_id_map(macro_call.file_id).ast_id(macro_call.value),
);
let macro_call_loc = MacroCallLoc { def, ast_id };
Some(Expansion { macro_call_id: db.intern_macro(macro_call_loc) })
Some(Expansion {
macro_call_id: db.intern_macro(macro_call_loc),
macro_file_kind: to_macro_file_kind(macro_call.value),
})
}
#[cfg(test)]
@ -538,3 +542,35 @@ fn adjust(
})
.map(|(_ptr, scope)| *scope)
}
/// Given a `ast::MacroCall`, return what `MacroKindFile` it belongs to.
/// FIXME: Not completed
fn to_macro_file_kind(macro_call: &ast::MacroCall) -> MacroFileKind {
let syn = macro_call.syntax();
let parent = match syn.parent() {
Some(it) => it,
None => {
// FIXME:
// If it is root, which means the parent HirFile
// MacroKindFile must be non-items
// return expr now.
return MacroFileKind::Expr;
}
};
match parent.kind() {
MACRO_ITEMS | SOURCE_FILE => MacroFileKind::Items,
LET_STMT => {
// FIXME: Handle Pattern
MacroFileKind::Expr
}
EXPR_STMT => MacroFileKind::Statements,
BLOCK => MacroFileKind::Statements,
ARG_LIST => MacroFileKind::Expr,
TRY_EXPR => MacroFileKind::Expr,
_ => {
// Unknown , Just guess it is `Items`
MacroFileKind::Items
}
}
}

View File

@ -151,6 +151,7 @@ pub(crate) fn parse_macro(
let fragment_kind = match macro_file.macro_file_kind {
MacroFileKind::Items => FragmentKind::Items,
MacroFileKind::Expr => FragmentKind::Expr,
MacroFileKind::Statements => FragmentKind::Statements,
};
let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
Some((parse, Arc::new(rev_token_map)))

View File

@ -109,6 +109,7 @@ pub struct MacroFile {
pub enum MacroFileKind {
Items,
Expr,
Statements,
}
/// `MacroCallId` identifies a particular macro invocation, like

View File

@ -84,24 +84,19 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
};
res += &match token.kind() {
k @ _
if (k.is_keyword() || k.is_literal() || k == IDENT)
&& is_next(|it| !it.is_punct(), true) =>
{
k @ _ if is_text(k) && is_next(|it| !it.is_punct(), true) => {
token.text().to_string() + " "
}
L_CURLY if is_next(|it| it != R_CURLY, true) => {
indent += 1;
format!(" {{\n{}", " ".repeat(indent))
let leading_space = if is_last(|it| is_text(it), false) { " " } else { "" };
format!("{}{{\n{}", leading_space, " ".repeat(indent))
}
R_CURLY if is_last(|it| it != L_CURLY, true) => {
indent = indent.checked_sub(1).unwrap_or(0);
format!("\n}}{}", " ".repeat(indent))
}
R_CURLY => {
indent = indent.checked_sub(1).unwrap_or(0);
format!("}}\n{}", " ".repeat(indent))
format!("\n{}}}", " ".repeat(indent))
}
R_CURLY => format!("}}\n{}", " ".repeat(indent)),
T![;] => format!(";\n{}", " ".repeat(indent)),
T![->] => " -> ".to_string(),
T![=] => " = ".to_string(),
@ -112,7 +107,11 @@ fn insert_whitespaces(syn: SyntaxNode) -> String {
last = Some(token.kind());
}
res
return res;
fn is_text(k: SyntaxKind) -> bool {
k.is_keyword() || k.is_literal() || k == IDENT
}
}
#[cfg(test)]
@ -173,6 +172,49 @@ fn some_thing() -> u32 {
let a = 0;
a+10
}
"###);
}
#[test]
fn macro_expand_match_ast() {
let res = check_expand_macro(
r#"
//- /lib.rs
macro_rules! match_ast {
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
(match ($node:expr) {
$( ast::$ast:ident($it:ident) => $res:block, )*
_ => $catch_all:expr $(,)?
}) => {{
$( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
{ $catch_all }
}};
}
fn main() {
mat<|>ch_ast! {
match container {
ast::TraitDef(it) => {},
ast::ImplBlock(it) => {},
_ => { continue },
}
}
}
"#,
);
assert_eq!(res.name, "match_ast");
assert_snapshot!(res.expansion, @r###"
{
if let Some(it) = ast::TraitDef::cast(container.clone()){}
else if let Some(it) = ast::ImplBlock::cast(container.clone()){}
else {
{
continue
}
}
}
"###);
}
}