diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 87d81f4a4a6..9e6170440e5 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -429,6 +429,45 @@ impl Docs for EnumVariant { } } +/// The defs which have a body. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum DefWithBody { + Function(Function), + Const(Const), + Static(Static), +} + +impl_froms!(DefWithBody: Function, Const, Static); + +impl DefWithBody { + pub fn infer(&self, db: &impl HirDatabase) -> Arc { + db.infer(*self) + } + + pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + db.body_with_source_map(*self).1 + } + + pub fn body(&self, db: &impl HirDatabase) -> Arc { + db.body_hir(*self) + } + + /// Builds a resolver for code inside this item. + pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + match *self { + DefWithBody::Const(ref c) => c.resolver(db), + DefWithBody::Function(ref f) => f.resolver(db), + DefWithBody::Static(ref s) => s.resolver(db), + } + } + + pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSourceMap { + let scopes = db.expr_scopes(*self); + let source_map = db.body_with_source_map(*self).1; + ScopesWithSourceMap { scopes, source_map } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Function { pub(crate) id: FunctionId, @@ -479,11 +518,11 @@ impl Function { } pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { - db.body_with_source_map(*self).1 + db.body_with_source_map((*self).into()).1 } pub fn body(&self, db: &impl HirDatabase) -> Arc { - db.body_hir(*self) + db.body_hir((*self).into()) } pub fn ty(&self, db: &impl HirDatabase) -> Ty { @@ -491,8 +530,8 @@ impl Function { } pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSourceMap { - let scopes = db.expr_scopes(*self); - let source_map = db.body_with_source_map(*self).1; + let scopes = db.expr_scopes((*self).into()); + let source_map = db.body_with_source_map((*self).into()).1; ScopesWithSourceMap { scopes, source_map } } @@ -501,7 +540,7 @@ impl Function { } pub fn infer(&self, db: &impl HirDatabase) -> Arc { - db.infer(*self) + db.infer((*self).into()) } pub fn generic_params(&self, db: &impl DefDatabase) -> Arc { @@ -557,6 +596,14 @@ impl Const { db.const_signature(*self) } + pub fn infer(&self, db: &impl HirDatabase) -> Arc { + db.infer((*self).into()) + } + + pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + db.body_with_source_map((*self).into()).1 + } + /// The containing impl block, if this is a method. pub fn impl_block(&self, db: &impl DefDatabase) -> Option { let module_impls = db.impls_in_module(self.module(db)); @@ -621,6 +668,14 @@ impl Static { // take the outer scope... self.module(db).resolver(db) } + + pub fn infer(&self, db: &impl HirDatabase) -> Arc { + db.infer((*self).into()) + } + + pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + db.body_with_source_map((*self).into()).1 + } } impl Docs for Static { diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 1470058480a..be8a8c98bb8 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -8,6 +8,7 @@ use crate::{ Function, FnSignature, ExprScopes, TypeAlias, Struct, Enum, StructField, Const, ConstSignature, Static, + DefWithBody, nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap}, ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig}, adt::{StructData, EnumData}, @@ -83,10 +84,10 @@ pub trait DefDatabase: SourceDatabase + AsRef { #[salsa::query_group(HirDatabaseStorage)] pub trait HirDatabase: DefDatabase { #[salsa::invoke(ExprScopes::expr_scopes_query)] - fn expr_scopes(&self, func: Function) -> Arc; + fn expr_scopes(&self, def: DefWithBody) -> Arc; #[salsa::invoke(crate::ty::infer)] - fn infer(&self, func: Function) -> Arc; + fn infer(&self, def: DefWithBody) -> Arc; #[salsa::invoke(crate::ty::type_for_def)] fn type_for_def(&self, def: TypableDef, ns: Namespace) -> Ty; @@ -100,11 +101,11 @@ pub trait HirDatabase: DefDatabase { #[salsa::invoke(crate::expr::body_with_source_map_query)] fn body_with_source_map( &self, - func: Function, + def: DefWithBody, ) -> (Arc, Arc); #[salsa::invoke(crate::expr::body_hir_query)] - fn body_hir(&self, func: Function) -> Arc; + fn body_hir(&self, def: DefWithBody) -> Arc; #[salsa::invoke(crate::ty::method_resolution::CrateImplBlocks::impls_in_crate_query)] fn impls_in_crate(&self, krate: Crate) -> Arc; diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 45012827f45..b2a237ecea2 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -10,7 +10,7 @@ use ra_syntax::{ }; use crate::{ - Path, Name, HirDatabase, Function, Resolver, + Path, Name, HirDatabase, Resolver,DefWithBody, name::AsName, type_ref::{Mutability, TypeRef}, }; @@ -27,9 +27,8 @@ impl_arena_id!(ExprId); /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { - // FIXME: this should be more general, consts & statics also have bodies - /// The Function of the item this body belongs to - owner: Function, + /// The def of the item this body belongs to + owner: DefWithBody, exprs: Arena, pats: Arena, /// The patterns for the function's parameters. While the parameter types are @@ -66,7 +65,7 @@ impl Body { self.body_expr } - pub fn owner(&self) -> Function { + pub fn owner(&self) -> DefWithBody { self.owner } @@ -463,8 +462,8 @@ impl Pat { // Queries -struct ExprCollector { - owner: Function, +pub(crate) struct ExprCollector { + owner: DefWithBody, exprs: Arena, pats: Arena, source_map: BodySourceMap, @@ -473,7 +472,7 @@ struct ExprCollector { } impl ExprCollector { - fn new(owner: Function) -> Self { + fn new(owner: DefWithBody) -> Self { ExprCollector { owner, exprs: Arena::default(), @@ -866,6 +865,16 @@ impl ExprCollector { } } + fn collect_const_body(&mut self, node: &ast::ConstDef) { + let body = self.collect_expr_opt(node.body()); + self.body_expr = Some(body); + } + + fn collect_static_body(&mut self, node: &ast::StaticDef) { + let body = self.collect_expr_opt(node.body()); + self.body_expr = Some(body); + } + fn collect_fn_body(&mut self, node: &ast::FnDef) { if let Some(param_list) = node.param_list() { if let Some(self_param) = param_list.self_param() { @@ -910,24 +919,20 @@ impl ExprCollector { pub(crate) fn body_with_source_map_query( db: &impl HirDatabase, - func: Function, + def: DefWithBody, ) -> (Arc, Arc) { - let mut collector = ExprCollector::new(func); + let mut collector = ExprCollector::new(def); - // FIXME: consts, etc. - collector.collect_fn_body(&func.source(db).1); + match def { + DefWithBody::Const(ref c) => collector.collect_const_body(&c.source(db).1), + DefWithBody::Function(ref f) => collector.collect_fn_body(&f.source(db).1), + DefWithBody::Static(ref s) => collector.collect_static_body(&s.source(db).1), + } let (body, source_map) = collector.finish(); (Arc::new(body), Arc::new(source_map)) } -pub(crate) fn body_hir_query(db: &impl HirDatabase, func: Function) -> Arc { - db.body_with_source_map(func).0 -} - -#[cfg(test)] -fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> (Body, BodySourceMap) { - let mut collector = ExprCollector::new(function); - collector.collect_fn_body(node); - collector.finish() +pub(crate) fn body_hir_query(db: &impl HirDatabase, def: DefWithBody) -> Arc { + db.body_with_source_map(def).0 } diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index ed005c9f700..48283907b35 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -10,7 +10,7 @@ use ra_syntax::{ use ra_arena::{Arena, RawId, impl_arena_id}; use crate::{ - Name, AsName, Function, + Name, AsName,DefWithBody, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySourceMap}, HirDatabase, }; @@ -40,8 +40,8 @@ pub struct ScopeData { impl ExprScopes { // FIXME: This should take something more general than Function - pub(crate) fn expr_scopes_query(db: &impl HirDatabase, function: Function) -> Arc { - let body = db.body_hir(function); + pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc { + let body = db.body_hir(def); let res = ExprScopes::new(body); Arc::new(res) } @@ -297,8 +297,9 @@ mod tests { use ra_syntax::{SourceFile, algo::find_node_at_offset}; use test_utils::{extract_offset, assert_eq_text}; use ra_arena::ArenaId; + use crate::Function; - use crate::expr; + use crate::expr::{ExprCollector}; use super::*; @@ -316,7 +317,7 @@ mod tests { let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); let irrelevant_function = Function { id: crate::ids::FunctionId::from_raw(0.into()) }; - let (body, source_map) = expr::collect_fn_body_syntax(irrelevant_function, fn_def); + let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def); let scopes = ExprScopes::new(Arc::new(body)); let scopes = ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) }; @@ -405,6 +406,12 @@ mod tests { ); } + fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> (Body, BodySourceMap) { + let mut collector = ExprCollector::new(DefWithBody::Function(function)); + collector.collect_fn_body(node); + collector.finish() + } + fn do_check_local_name(code: &str, expected_offset: u32) { let (off, code) = extract_offset(code); let file = SourceFile::parse(&code); @@ -415,7 +422,7 @@ mod tests { let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); let irrelevant_function = Function { id: crate::ids::FunctionId::from_raw(0.into()) }; - let (body, source_map) = expr::collect_fn_body_syntax(irrelevant_function, fn_def); + let (body, source_map) = collect_fn_body_syntax(irrelevant_function, fn_def); let scopes = ExprScopes::new(Arc::new(body)); let scopes = ScopesWithSourceMap { scopes: Arc::new(scopes), source_map: Arc::new(source_map) }; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 643bee6cdc6..c19450f3903 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -67,6 +67,7 @@ pub use self::{ pub use self::code_model_api::{ Crate, CrateDependency, + DefWithBody, Module, ModuleDef, ModuleSource, Struct, Enum, EnumVariant, Function, FnSignature, diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 54dc273998a..182ed4c9142 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -13,7 +13,7 @@ use ra_syntax::{ }; use crate::{ - HirDatabase, Function, Struct, Enum, + HirDatabase, Function, Struct, Enum,Const,Static, AsName, Module, HirFileId, Crate, Trait, Resolver, ids::LocationCtx, expr, AstId @@ -87,6 +87,27 @@ fn module_from_source( ) } +pub fn const_from_source( + db: &impl HirDatabase, + file_id: FileId, + const_def: &ast::ConstDef, +) -> Option { + let module = module_from_child_node(db, file_id, const_def.syntax())?; + let res = const_from_module(db, module, const_def); + Some(res) +} + +pub fn const_from_module( + db: &impl HirDatabase, + module: Module, + const_def: &ast::ConstDef, +) -> Const { + let (file_id, _) = module.definition_source(db); + let file_id = file_id.into(); + let ctx = LocationCtx::new(db, module, file_id); + Const { id: ctx.to_def(const_def) } +} + pub fn function_from_position(db: &impl HirDatabase, position: FilePosition) -> Option { let file = db.parse(position.file_id); let fn_def = find_node_at_offset::(file.syntax(), position.offset)?; @@ -134,6 +155,27 @@ pub fn struct_from_module( Struct { id: ctx.to_def(struct_def) } } +pub fn static_from_source( + db: &impl HirDatabase, + file_id: FileId, + static_def: &ast::StaticDef, +) -> Option { + let module = module_from_child_node(db, file_id, static_def.syntax())?; + let res = static_from_module(db, module, static_def); + Some(res) +} + +pub fn static_from_module( + db: &impl HirDatabase, + module: Module, + static_def: &ast::StaticDef, +) -> Static { + let (file_id, _) = module.definition_source(db); + let file_id = file_id.into(); + let ctx = LocationCtx::new(db, module, file_id); + Static { id: ctx.to_def(static_def) } +} + pub fn enum_from_module(db: &impl HirDatabase, module: Module, enum_def: &ast::EnumDef) -> Enum { let (file_id, _) = module.definition_source(db); let file_id = file_id.into(); diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 5731153216a..887153484d7 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -27,8 +27,9 @@ use test_utils::tested_by; use crate::{ Function, StructField, Path, Name, - FnSignature, AdtDef, + FnSignature, AdtDef,ConstSignature, HirDatabase, + DefWithBody, ImplItem, type_ref::{TypeRef, Mutability}, expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self}, @@ -43,14 +44,17 @@ use crate::{ use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor}; /// The entry point of type inference. -pub fn infer(db: &impl HirDatabase, func: Function) -> Arc { +pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc { db.check_canceled(); - let body = func.body(db); - let resolver = func.resolver(db); + let body = def.body(db); + let resolver = def.resolver(db); let mut ctx = InferenceContext::new(db, body, resolver); - let signature = func.signature(db); - ctx.collect_fn_signature(&signature); + match def { + DefWithBody::Const(ref c) => ctx.collect_const_signature(&c.signature(db)), + DefWithBody::Function(ref f) => ctx.collect_fn_signature(&f.signature(db)), + DefWithBody::Static(ref s) => ctx.collect_const_signature(&s.signature(db)), + } ctx.infer_body(); @@ -1142,6 +1146,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ty } + fn collect_const_signature(&mut self, signature: &ConstSignature) { + self.return_ty = self.make_ty(signature.type_ref()); + } + fn collect_fn_signature(&mut self, signature: &FnSignature) { let body = Arc::clone(&self.body); // avoid borrow checker problem for (type_ref, pat) in signature.params().iter().zip(body.params()) { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 943c5499bdc..0b7c841dfd2 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -11,6 +11,8 @@ use crate::{ source_binder, mock::MockDatabase, ty::display::HirDisplay, + ty::InferenceResult, + expr::BodySourceMap }; // These tests compare the inference results for all expressions in a file @@ -1267,6 +1269,9 @@ fn test() { } "#), @r###" +[52; 53) '1': u32 +[103; 104) '2': u32 +[211; 212) '5': u32 [227; 305) '{ ...:ID; }': () [237; 238) 'x': u32 [241; 252) 'Struct::FOO': u32 @@ -1855,6 +1860,9 @@ fn test() { } "#), @r###" +[49; 50) '0': u32 +[80; 83) '101': u32 +[126; 128) '99': u32 [95; 213) '{ ...NST; }': () [138; 139) 'x': {unknown} [142; 153) 'LOCAL_CONST': {unknown} @@ -1881,6 +1889,10 @@ fn test() { } "#), @r###" +[29; 32) '101': u32 +[70; 73) '101': u32 +[118; 120) '99': u32 +[161; 163) '99': u32 [85; 280) '{ ...MUT; }': () [173; 174) 'x': {unknown} [177; 189) 'LOCAL_STATIC': {unknown} @@ -2212,6 +2224,24 @@ fn test>() { ); } +#[test] +fn infer_const_body() { + assert_snapshot_matches!( + infer(r#" +const A: u32 = 1 + 1; +static B: u64 = { let x = 1; x }; +"#), + @r###" +[16; 17) '1': u32 +[16; 21) '1 + 1': u32 +[20; 21) '1': u32 +[39; 55) '{ let ...1; x }': u64 +[45; 46) 'x': u64 +[49; 50) '1': u64 +[52; 53) 'x': u64"### + ); +} + fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let func = source_binder::function_from_position(db, pos).unwrap(); let body_source_map = func.body_source_map(db); @@ -2228,11 +2258,11 @@ fn infer(content: &str) -> String { let source_file = db.parse(file_id); let mut acc = String::new(); acc.push_str("\n"); - for fn_def in source_file.syntax().descendants().filter_map(ast::FnDef::cast) { - let func = source_binder::function_from_source(&db, file_id, fn_def).unwrap(); - let inference_result = func.infer(&db); - let body_source_map = func.body_source_map(&db); + + let mut infer_def = |inference_result: Arc, + body_source_map: Arc| { let mut types = Vec::new(); + for (pat, ty) in inference_result.type_of_pat.iter() { let syntax_ptr = match body_source_map.pat_syntax(pat) { Some(sp) => sp, @@ -2240,6 +2270,7 @@ fn infer(content: &str) -> String { }; types.push((syntax_ptr, ty)); } + for (expr, ty) in inference_result.type_of_expr.iter() { let syntax_ptr = match body_source_map.expr_syntax(expr) { Some(sp) => sp, @@ -2258,7 +2289,29 @@ fn infer(content: &str) -> String { }; write!(acc, "{} '{}': {}\n", range, ellipsize(text, 15), ty.display(&db)).unwrap(); } + }; + + for const_def in source_file.syntax().descendants().filter_map(ast::ConstDef::cast) { + let konst = source_binder::const_from_source(&db, file_id, const_def).unwrap(); + let inference_result = konst.infer(&db); + let body_source_map = konst.body_source_map(&db); + infer_def(inference_result, body_source_map) } + + for static_def in source_file.syntax().descendants().filter_map(ast::StaticDef::cast) { + let static_ = source_binder::static_from_source(&db, file_id, static_def).unwrap(); + let inference_result = static_.infer(&db); + let body_source_map = static_.body_source_map(&db); + infer_def(inference_result, body_source_map) + } + + for fn_def in source_file.syntax().descendants().filter_map(ast::FnDef::cast) { + let func = source_binder::function_from_source(&db, file_id, fn_def).unwrap(); + let inference_result = func.infer(&db); + let body_source_map = func.body_source_map(&db); + infer_def(inference_result, body_source_map) + } + acc.truncate(acc.trim_end().len()); acc } diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c2e89de1501..0376c91c88c 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs @@ -513,7 +513,11 @@ impl ast::TypeParamsOwner for ConstDef {} impl ast::AttrsOwner for ConstDef {} impl ast::DocCommentsOwner for ConstDef {} impl ast::TypeAscriptionOwner for ConstDef {} -impl ConstDef {} +impl ConstDef { + pub fn body(&self) -> Option<&Expr> { + super::child_opt(self) + } +} // ContinueExpr #[derive(Debug, PartialEq, Eq, Hash)] @@ -3364,7 +3368,11 @@ impl ast::TypeParamsOwner for StaticDef {} impl ast::AttrsOwner for StaticDef {} impl ast::DocCommentsOwner for StaticDef {} impl ast::TypeAscriptionOwner for StaticDef {} -impl StaticDef {} +impl StaticDef { + pub fn body(&self) -> Option<&Expr> { + super::child_opt(self) + } +} // Stmt #[derive(Debug, PartialEq, Eq, Hash)] diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index dc0de5808a9..0a35e25d5aa 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -315,6 +315,7 @@ Grammar( "DocCommentsOwner", "TypeAscriptionOwner", ], + options: [ ["body","Expr"]], ), "StaticDef": ( traits: [ @@ -325,6 +326,7 @@ Grammar( "DocCommentsOwner", "TypeAscriptionOwner", ], + options: [ ["body","Expr"]], ), "TypeAliasDef": ( traits: [