);
}
+ #[test]
+ fn add_explicit_type_works_for_macro_call() {
+ check_assist(
+ add_explicit_type,
+ "macro_rules! v { () => {0u64} } fn f() { let a<|> = v!(); }",
+ "macro_rules! v { () => {0u64} } fn f() { let a<|>: u64 = v!(); }",
+ );
+ }
+
+ #[test]
+ fn add_explicit_type_works_for_macro_call_recursive() {
+ check_assist(
+ add_explicit_type,
+ "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|> = v!(); }",
+ "macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a<|>: u64 = v!(); }",
+ );
+ }
+
#[test]
fn add_explicit_type_not_applicable_if_ty_not_inferred() {
check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }");
nameres::ModuleSource,
path::path,
resolver::{self, resolver_for_scope, HasResolver, Resolver, TypeNs, ValueNs},
- AssocItemId, DefWithBodyId,
+ AssocItemId, DefWithBodyId, Expander,
};
use hir_expand::{
hygiene::Hygiene, name::AsName, AstId, HirFileId, InFile, MacroCallId, MacroCallKind,
}
pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option<Type> {
- let expr_id = self.expr_id(expr)?;
+ let expr_id = if let Some(macro_call) = ast::MacroCall::cast(expr.syntax().clone()) {
+ let mut expander = Expander::new(db, self.file_id, self.body_owner?.module(db).id);
+ let expr = expand_macro_call_to_expr(db, &mut expander, macro_call)?;
+ self.body_source_map.as_ref()?.node_expr(expr.as_ref())?
+ } else {
+ self.expr_id(expr)?
+ };
+
let ty = self.infer.as_ref()?[expr_id].clone();
let environment = TraitEnvironment::lower(db, &self.resolver);
Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } })
})
}
+fn expand_macro_call_to_expr(
+ db: &impl HirDatabase,
+ expander: &mut Expander,
+ macro_call: ast::MacroCall,
+) -> Option<InFile<ast::Expr>> {
+ let (mark, expr): (_, ast::Expr) = expander.enter_expand(db, macro_call)?;
+ let expr = if let Some(child) = ast::MacroCall::cast(expr.syntax().clone()) {
+ expand_macro_call_to_expr(db, expander, child)
+ } else {
+ Some(expander.to_source(expr))
+ };
+ expander.exit(db, mark);
+ expr
+}
+
// XXX: during completion, cursor might be outside of any particular
// expression. Try to figure out the correct scope...
fn adjust(
DefWithBodyId, HasModule, Lookup, ModuleId,
};
-pub(crate) struct Expander {
+pub struct Expander {
crate_def_map: Arc<CrateDefMap>,
current_file_id: HirFileId,
hygiene: Hygiene,
}
impl Expander {
- pub(crate) fn new(
- db: &impl DefDatabase,
- current_file_id: HirFileId,
- module: ModuleId,
- ) -> Expander {
+ pub fn new(db: &impl DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
let crate_def_map = db.crate_def_map(module.krate);
let hygiene = Hygiene::new(db, current_file_id);
let ast_id_map = db.ast_id_map(current_file_id);
Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module }
}
- pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
+ pub fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
&mut self,
db: &DB,
macro_call: ast::MacroCall,
None
}
- pub(crate) fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) {
+ pub fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) {
self.hygiene = Hygiene::new(db, mark.file_id);
self.current_file_id = mark.file_id;
self.ast_id_map = mem::take(&mut mark.ast_id_map);
mark.bomb.defuse();
}
- pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
+ pub fn to_source<T>(&self, value: T) -> InFile<T> {
InFile { file_id: self.current_file_id, value }
}
}
}
-pub(crate) struct Mark {
+pub struct Mark {
file_id: HirFileId,
ast_id_map: Arc<AstIdMap>,
bomb: DropBomb,
use ra_db::{impl_intern_key, salsa, CrateId};
use ra_syntax::{ast, AstNode};
-use crate::body::Expander;
+pub use crate::body::Expander;
use crate::builtin_type::BuiltinType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]