diff --git a/.vscode/launch.json b/.vscode/launch.json index 1515ea41102..9aafc8bd335 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,9 +14,7 @@ "--disable-extensions" ], "env": { - "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server", - "RUST_LOG" :"ra_hir=debug", - "RA_INTERNAL_MODE":"1" + "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/debug/ra_lsp_server" }, "outFiles": ["${workspaceFolder}/editors/code/out/**/*.js"], "preLaunchTask": "Build All" diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 7d52574612f..db74d28e8e4 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -5,14 +5,14 @@ use rustc_hash::FxHashMap; use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; use ra_syntax::{ - SyntaxNodePtr, AstPtr, AstNode, + SyntaxNodePtr, AstPtr, AstNode,TreeArc, ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind,ArrayExprKind, TypeAscriptionOwner} }; use crate::{ Path, Name, HirDatabase, Resolver,DefWithBody, Either, name::AsName, - ids::{MacroCallLoc,HirFileId}, + ids::{MacroCallId}, type_ref::{Mutability, TypeRef}, }; use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; @@ -488,23 +488,45 @@ pub(crate) struct ExprCollector { params: Vec, body_expr: Option, resolver: Resolver, + // FIXEME: Its a quick hack,see issue #1196 + is_in_macro: bool, } impl<'a, DB> ExprCollector<&'a DB> where DB: HirDatabase, { + fn new(owner: DefWithBody, resolver: Resolver, db: &'a DB) -> Self { + ExprCollector { + owner, + resolver, + db, + exprs: Arena::default(), + pats: Arena::default(), + source_map: BodySourceMap::default(), + params: Vec::new(), + body_expr: None, + is_in_macro: false, + } + } fn alloc_expr(&mut self, expr: Expr, syntax_ptr: SyntaxNodePtr) -> ExprId { let id = self.exprs.alloc(expr); - self.source_map.expr_map.insert(syntax_ptr, id); - self.source_map.expr_map_back.insert(id, syntax_ptr); + if !self.is_in_macro { + self.source_map.expr_map.insert(syntax_ptr, id); + self.source_map.expr_map_back.insert(id, syntax_ptr); + } + id } fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { let id = self.pats.alloc(pat); - self.source_map.pat_map.insert(ptr, id); - self.source_map.pat_map_back.insert(id, ptr); + + if !self.is_in_macro { + self.source_map.pat_map.insert(ptr, id); + self.source_map.pat_map_back.insert(id, ptr); + } + id } @@ -790,40 +812,19 @@ where ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::MacroCall(e) => { - // very hacky.TODO change to use the macro resolution - let name = e - .path() - .and_then(Path::from_ast) - .and_then(|path| path.expand_macro_expr()) - .unwrap_or_else(Name::missing); + // very hacky.FIXME change to use the macro resolution + let path = e.path().and_then(Path::from_ast); - if let Some(macro_id) = self.resolver.resolve_macro_call(&name) { - if let Some((module, _)) = self.resolver.module() { - // we do this to get the ast_id for the macro call - // if we used the ast_id from the macro_id variable - // it gives us the ast_id of the defenition site - let module = module.mk_module(module.root()); - let hir_file_id = module.definition_source(self.db).0; - let ast_id = - self.db.ast_id_map(hir_file_id).ast_id(e).with_file_id(hir_file_id); - - let call_loc = MacroCallLoc { def: *macro_id, ast_id }; - let call_id = call_loc.id(self.db); - let file_id: HirFileId = call_id.into(); - - log::debug!( - "expanded macro ast {}", - self.db.hir_parse(file_id).syntax().debug_dump() - ); - - self.db - .hir_parse(file_id) - .syntax() - .descendants() - .find_map(ast::Expr::cast) - .map(|expr| self.collect_expr(expr)) - .unwrap_or(self.alloc_expr(Expr::Missing, syntax_ptr)) + if let Some(call_id) = self.resolver.resolve_macro_call(self.db, path, e) { + if let Some(expr) = expand_macro_to_expr(self.db, call_id, e.token_tree()) { + log::debug!("macro expansion {}", expr.syntax().debug_dump()); + let old = std::mem::replace(&mut self.is_in_macro, true); + let id = self.collect_expr(&expr); + self.is_in_macro = old; + id } else { + // FIXME: Instead of just dropping the error from expansion + // report it self.alloc_expr(Expr::Missing, syntax_ptr) } } else { @@ -987,20 +988,25 @@ where } } +fn expand_macro_to_expr( + db: &impl HirDatabase, + macro_call: MacroCallId, + args: Option<&ast::TokenTree>, +) -> Option> { + let rules = db.macro_def(macro_call.loc(db).def)?; + + let args = mbe::ast_to_token_tree(args?)?.0; + + let expanded = rules.expand(&args).ok()?; + + mbe::token_tree_to_expr(&expanded).ok() +} + pub(crate) fn body_with_source_map_query( db: &impl HirDatabase, def: DefWithBody, ) -> (Arc, Arc) { - let mut collector = ExprCollector { - db, - owner: def, - resolver: def.resolver(db), - exprs: Arena::default(), - pats: Arena::default(), - source_map: BodySourceMap::default(), - params: Vec::new(), - body_expr: None, - }; + let mut collector = ExprCollector::new(def, def.resolver(db), db); match def { DefWithBody::Const(ref c) => collector.collect_const_body(&c.source(db).1), diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index a07624a1981..c7849c995b0 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -101,7 +101,7 @@ fn parse_macro( return Err(format!("Total tokens count exceed limit : count = {}", count)); } - Some(mbe::token_tree_to_ast_item_list(&tt)) + Ok(mbe::token_tree_to_ast_item_list(&tt)) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 762a6160472..b34c9b8e697 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -524,7 +524,7 @@ where { let macro_call_id = MacroCallLoc { def: *macro_id, ast_id }.id(self.def_collector.db); - self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_id); + self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, *macro_id); return; } @@ -616,6 +616,7 @@ mod tests { modules, public_macros: FxHashMap::default(), poison_macros: FxHashSet::default(), + local_macros: FxHashMap::default(), diagnostics: Vec::new(), } }; diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 1def032f952..d1f97c1047b 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -1,12 +1,15 @@ //! Name resolution. use std::sync::Arc; +use ra_syntax::ast; + use rustc_hash::FxHashMap; use crate::{ ModuleDef, code_model_api::Crate, - MacroDefId, + MacroCallId, + MacroCallLoc, db::HirDatabase, name::{Name, KnownName}, nameres::{PerNs, CrateDefMap, CrateModuleId}, @@ -131,8 +134,29 @@ impl Resolver { resolution } - pub fn resolve_macro_call(&self, name: &Name) -> Option<&MacroDefId> { - self.module().and_then(|(module, _)| module.find_macro(name)) + pub fn resolve_macro_call( + &self, + db: &impl HirDatabase, + path: Option, + call: &ast::MacroCall, + ) -> Option { + let name = path.and_then(|path| path.expand_macro_expr()).unwrap_or_else(Name::missing); + let macro_def_id = self.module().and_then(|(module, _)| module.find_macro(&name)); + if let Some(def_id) = macro_def_id { + self.module().and_then(|(module, _)| { + // we do this to get the ast_id for the macro call + // if we used the ast_id from the def_id variable + // it gives us the ast_id of the defenition site + let module = module.mk_module(module.root()); + let hir_file_id = module.definition_source(db).0; + let ast_id = db.ast_id_map(hir_file_id).ast_id(call).with_file_id(hir_file_id); + let call_loc = MacroCallLoc { def: *def_id, ast_id }.id(db); + + Some(call_loc) + }) + } else { + None + } } /// Returns the resolved path segments @@ -197,7 +221,7 @@ impl Resolver { .flatten() } - pub(crate) fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { + fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> { self.scopes.iter().rev().find_map(|scope| match scope { Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index a4c99528d01..c76a5012f07 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2417,6 +2417,30 @@ fn test() -> u64 { ); } +#[test] +fn infer_macros_expanded() { + assert_snapshot_matches!( + infer(r#" +struct Foo(Vec); + +macro_rules! foo { + ($($item:expr),*) => { + { + Foo(vec![$($item,)*]) + } + }; +} + +fn main() { + let x = foo!(1,2); +} +"#), + @r###" +[156; 182) '{ ...,2); }': () +[166; 167) 'x': Foo"### + ); +} + #[ignore] #[test] fn method_resolution_trait_before_autoref() { @@ -2510,6 +2534,7 @@ fn type_at(content: &str) -> String { fn infer(content: &str) -> String { let (db, _, file_id) = MockDatabase::with_single_file(content); let source_file = db.parse(file_id); + let mut acc = String::new(); acc.push_str("\n"); @@ -2532,6 +2557,7 @@ fn infer(content: &str) -> String { }; types.push((syntax_ptr, ty)); } + // sort ranges for consistency types.sort_by_key(|(ptr, _)| (ptr.range().start(), ptr.range().end())); for (syntax_ptr, ty) in &types {