From 1465cc0c4feb52958d3281f066a663e0a52ed67e Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Mon, 2 Mar 2020 14:05:15 +0800 Subject: Implement concat macro --- crates/ra_hir_expand/src/eager.rs | 93 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 crates/ra_hir_expand/src/eager.rs (limited to 'crates/ra_hir_expand/src/eager.rs') diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs new file mode 100644 index 000000000..8caf8497e --- /dev/null +++ b/crates/ra_hir_expand/src/eager.rs @@ -0,0 +1,93 @@ +//! Eager expansion related utils + +use crate::{ + ast::{self, AstNode}, + db::AstDatabase, + EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, +}; + +use ra_parser::FragmentKind; +use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode}; +use std::{collections::HashMap, sync::Arc}; + +fn to_subtree(node: &SyntaxNode) -> Option { + let mut subtree = mbe::syntax_node_to_token_tree(node)?.0; + subtree.delimiter = None; + Some(subtree) +} + +fn lazy_expand( + db: &impl AstDatabase, + def: &MacroDefId, + macro_call: InFile, +) -> Option> { + let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value); + + let id: MacroCallId = + def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); + + db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) +} + +fn eager_macro_recur( + db: &impl AstDatabase, + curr: InFile, + macro_resolver: &dyn Fn(ast::Path) -> Option, +) -> Option { + let mut original = curr.value.clone(); + + let children = curr.value.descendants().filter_map(ast::MacroCall::cast); + let mut replaces: HashMap = HashMap::default(); + + // Collect replacement + for child in children { + let def: MacroDefId = macro_resolver(child.path()?)?; + let insert = match def.kind { + MacroDefKind::BuiltInEager(_) => { + let id: MacroCallId = + expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? + .into(); + db.parse_or_expand(id.as_file())? + } + MacroDefKind::Declarative + | MacroDefKind::BuiltIn(_) + | MacroDefKind::BuiltInDerive(_) => { + let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; + // replace macro inside + eager_macro_recur(db, expanded, macro_resolver)? + } + }; + + replaces.insert(child.syntax().clone().into(), insert.into()); + } + + if !replaces.is_empty() { + original = replace_descendants(&original, |n| replaces.get(n).cloned()); + } + + Some(original) +} + +pub fn expand_eager_macro( + db: &impl AstDatabase, + macro_call: InFile, + def: MacroDefId, + resolver: &dyn Fn(ast::Path) -> Option, +) -> Option { + let args = macro_call.value.token_tree()?; + let parsed_args = mbe::ast_to_token_tree(&args)?.0; + let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; + let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?; + + let subtree = to_subtree(&result)?; + + if let MacroDefKind::BuiltInEager(eager) = def.kind { + let (subtree, fragment) = eager.expand(&subtree).ok()?; + let eager = + EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; + + Some(db.intern_eager_expansion(eager)) + } else { + None + } +} -- cgit v1.2.3 From 02b865019b7ca07ef929d1cfe560070d12ab3f96 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Tue, 3 Mar 2020 00:10:20 +0800 Subject: Add doc about eager expansion name resolution --- crates/ra_hir_expand/src/eager.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'crates/ra_hir_expand/src/eager.rs') diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs index 8caf8497e..b2b54ba2b 100644 --- a/crates/ra_hir_expand/src/eager.rs +++ b/crates/ra_hir_expand/src/eager.rs @@ -1,4 +1,23 @@ //! Eager expansion related utils +//! +//! Here is a dump of a discussion from Vadim Petrochenkov about Eager Expansion and +//! Its name resolution : +//! +//! > Eagerly expanded macros (and also macros eagerly expanded by eagerly expanded macros, +//! > which actually happens in practice too!) are resolved at the location of the "root" macro +//! > that performs the eager expansion on its arguments. +//! > If some name cannot be resolved at the eager expansion time it's considered unresolved, +//! > even if becomes available later (e.g. from a glob import or other macro). +//! +//! > Eagerly expanded macros don't add anything to the module structure of the crate and +//! > don't build any speculative module structures, i.e. they are expanded in a "flat" +//! > way even if tokens in them look like modules. +//! +//! > In other words, it kinda works for simple cases for which it was originally intended, +//! > and we need to live with it because it's available on stable and widely relied upon. +//! +//! +//! See the full discussion : https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros use crate::{ ast::{self, AstNode}, -- cgit v1.2.3 From 1e9a3233f753e1e4de6a56e84e46d1811526ddd9 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Wed, 4 Mar 2020 01:13:20 +0800 Subject: Move pub function first --- crates/ra_hir_expand/src/eager.rs | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'crates/ra_hir_expand/src/eager.rs') diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs index b2b54ba2b..7fcdfab5a 100644 --- a/crates/ra_hir_expand/src/eager.rs +++ b/crates/ra_hir_expand/src/eager.rs @@ -29,6 +29,30 @@ use ra_parser::FragmentKind; use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode}; use std::{collections::HashMap, sync::Arc}; +pub fn expand_eager_macro( + db: &impl AstDatabase, + macro_call: InFile, + def: MacroDefId, + resolver: &dyn Fn(ast::Path) -> Option, +) -> Option { + let args = macro_call.value.token_tree()?; + let parsed_args = mbe::ast_to_token_tree(&args)?.0; + let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; + let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?; + + let subtree = to_subtree(&result)?; + + if let MacroDefKind::BuiltInEager(eager) = def.kind { + let (subtree, fragment) = eager.expand(&subtree).ok()?; + let eager = + EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; + + Some(db.intern_eager_expansion(eager)) + } else { + None + } +} + fn to_subtree(node: &SyntaxNode) -> Option { let mut subtree = mbe::syntax_node_to_token_tree(node)?.0; subtree.delimiter = None; @@ -86,27 +110,3 @@ fn eager_macro_recur( Some(original) } - -pub fn expand_eager_macro( - db: &impl AstDatabase, - macro_call: InFile, - def: MacroDefId, - resolver: &dyn Fn(ast::Path) -> Option, -) -> Option { - let args = macro_call.value.token_tree()?; - let parsed_args = mbe::ast_to_token_tree(&args)?.0; - let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, FragmentKind::Expr).ok()?.0; - let result = eager_macro_recur(db, macro_call.with_value(parsed_args.syntax_node()), resolver)?; - - let subtree = to_subtree(&result)?; - - if let MacroDefKind::BuiltInEager(eager) = def.kind { - let (subtree, fragment) = eager.expand(&subtree).ok()?; - let eager = - EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; - - Some(db.intern_eager_expansion(eager)) - } else { - None - } -} -- cgit v1.2.3