From 10d66d63d716a10ba7a5a8d1b69c9066249caf69 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 10 Apr 2019 11:15:55 +0300 Subject: [PATCH 01/33] introduce SourceAnalyzer --- crates/ra_assists/src/add_explicit_type.rs | 8 +- .../src/add_missing_impl_members.rs | 15 +- crates/ra_assists/src/fill_match_arms.rs | 10 +- crates/ra_assists/src/fill_struct_fields.rs | 11 +- crates/ra_hir/src/code_model_api.rs | 10 +- crates/ra_hir/src/expr.rs | 16 +- crates/ra_hir/src/expr/scope.rs | 2 +- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/source_binder.rs | 100 +++++++++++- .../ra_ide_api/src/completion/complete_dot.rs | 11 +- .../src/completion/complete_struct_literal.rs | 11 +- .../src/completion/completion_context.rs | 3 + .../src/display/navigation_target.rs | 25 ++- crates/ra_ide_api/src/goto_definition.rs | 150 ++++++------------ crates/ra_ide_api/src/hover.rs | 18 +-- 15 files changed, 205 insertions(+), 186 deletions(-) diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs index 1dc59bb8712..c50db4e2102 100644 --- a/crates/ra_assists/src/add_explicit_type.rs +++ b/crates/ra_assists/src/add_explicit_type.rs @@ -1,7 +1,6 @@ use hir::{ HirDisplay, Ty, db::HirDatabase, - source_binder::function_from_child_node, }; use ra_syntax::{ SyntaxKind, @@ -30,11 +29,8 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx) -> Option< } // Infer type let db = ctx.db; - let func = function_from_child_node(db, ctx.frange.file_id, pat.syntax())?; - let inference_res = func.infer(db); - let source_map = func.body_source_map(db); - let expr_id = source_map.node_expr(expr.into())?; - let ty = inference_res[expr_id].clone(); + let analyzer = hir::SourceAnalyser::new(db, ctx.frange.file_id, stmt.syntax()); + let ty = analyzer.type_of(db, expr)?; // Assist not applicable if the type is unknown if is_unknown(&ty) { return None; diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 19a2d05bcbd..0b2127e1180 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs @@ -2,7 +2,6 @@ use std::fmt::Write; use crate::{Assist, AssistId, AssistCtx}; -use hir::Resolver; use hir::db::HirDatabase; use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; @@ -46,9 +45,9 @@ fn add_missing_impl_members_inner( let trait_def = { let file_id = ctx.frange.file_id; let position = FilePosition { file_id, offset: impl_node.syntax().range().start() }; - let resolver = hir::source_binder::resolver_for_position(ctx.db, position); + let analyser = hir::SourceAnalyser::new(ctx.db, position.file_id, impl_node.syntax()); - resolve_target_trait_def(ctx.db, &resolver, impl_node)? + resolve_target_trait_def(ctx.db, &analyser, impl_node)? }; let missing_fns: Vec<_> = { @@ -122,14 +121,14 @@ fn add_missing_impl_members_inner( /// implemented) to a `ast::TraitDef`. fn resolve_target_trait_def( db: &impl HirDatabase, - resolver: &Resolver, + binder: &hir::SourceAnalyser, impl_block: &ast::ImplBlock, ) -> Option> { - let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?; - let hir_path = ast_path.path().and_then(hir::Path::from_ast)?; + let ast_path = + impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?.path()?; - match resolver.resolve_path(db, &hir_path).take_types() { - Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1), + match binder.resolve_path(db, &ast_path) { + Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1), _ => None, } } diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs index da67ab667b8..050b1c73f31 100644 --- a/crates/ra_assists/src/fill_match_arms.rs +++ b/crates/ra_assists/src/fill_match_arms.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use hir::{ - AdtDef, FieldSource, source_binder, + AdtDef, FieldSource, db::HirDatabase, }; use ra_syntax::ast::{self, AstNode}; @@ -20,12 +20,8 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx) -> Option Some(e), _ => None, diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs index c7790dc7253..e23cccbb8f0 100644 --- a/crates/ra_assists/src/fill_struct_fields.rs +++ b/crates/ra_assists/src/fill_struct_fields.rs @@ -1,6 +1,6 @@ use std::fmt::Write; -use hir::{AdtDef, db::HirDatabase, source_binder::function_from_child_node}; +use hir::{AdtDef, db::HirDatabase}; use ra_syntax::ast::{self, AstNode}; @@ -51,15 +51,12 @@ where } fn evaluate_struct_def_fields(&mut self) -> Option<()> { - let function = function_from_child_node( + let analyzer = hir::SourceAnalyser::new( self.ctx.db, self.ctx.frange.file_id, self.struct_lit.syntax(), - )?; - let infer_result = function.infer(self.ctx.db); - let source_map = function.body_source_map(self.ctx.db); - let node_expr = source_map.node_expr(self.struct_lit.into())?; - let struct_lit_ty = infer_result[node_expr].clone(); + ); + let struct_lit_ty = analyzer.type_of(self.ctx.db, self.struct_lit.into())?; let struct_def = match struct_lit_ty.as_adt() { Some((AdtDef::Struct(s), _)) => s, _ => return None, diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index f13a6b37ac6..660edf006dc 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -450,10 +450,6 @@ impl DefWithBody { 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) } @@ -523,7 +519,7 @@ impl Function { self.signature(db).name.clone() } - pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc { db.body_with_source_map((*self).into()).1 } @@ -606,7 +602,7 @@ impl Const { db.infer((*self).into()) } - pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc { db.body_with_source_map((*self).into()).1 } @@ -679,7 +675,7 @@ impl Static { db.infer((*self).into()) } - pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc { db.body_with_source_map((*self).into()).1 } } diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index a8e115e47eb..3806a360595 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -117,31 +117,27 @@ impl Index for Body { } impl BodySourceMap { - pub fn expr_syntax(&self, expr: ExprId) -> Option { + pub(crate) fn expr_syntax(&self, expr: ExprId) -> Option { self.expr_map_back.get(expr).cloned() } - pub fn syntax_expr(&self, ptr: SyntaxNodePtr) -> Option { + pub(crate) fn syntax_expr(&self, ptr: SyntaxNodePtr) -> Option { self.expr_map.get(&ptr).cloned() } - pub fn node_expr(&self, node: &ast::Expr) -> Option { + pub(crate) fn node_expr(&self, node: &ast::Expr) -> Option { self.expr_map.get(&SyntaxNodePtr::new(node.syntax())).cloned() } - pub fn pat_syntax(&self, pat: PatId) -> Option { + pub(crate) fn pat_syntax(&self, pat: PatId) -> Option { self.pat_map_back.get(pat).cloned() } - pub fn syntax_pat(&self, ptr: PatPtr) -> Option { - self.pat_map.get(&ptr).cloned() - } - - pub fn node_pat(&self, node: &ast::Pat) -> Option { + pub(crate) fn node_pat(&self, node: &ast::Pat) -> Option { self.pat_map.get(&Either::A(AstPtr::new(node))).cloned() } - pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr { + pub(crate) fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr { self.field_map[&(expr, field)].clone() } } diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 725b6c00eed..404c979eb1f 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -109,7 +109,7 @@ impl ExprScopes { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ScopesWithSourceMap { - pub source_map: Arc, + pub(crate) source_map: Arc, pub scopes: Arc, } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 0881939a20c..59b402c5783 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -66,6 +66,7 @@ pub use self::{ adt::AdtDef, expr::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}, resolve::{Resolver, Resolution}, + source_binder::{SourceAnalyser, PathResolution}, }; pub use self::code_model_api::{ diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 182ed4c9142..79e304383a0 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -5,15 +5,17 @@ /// /// So, this modules should not be used during hir construction, it exists /// purely for "IDE needs". +use std::sync::Arc; + use ra_db::{FileId, FilePosition}; use ra_syntax::{ - SyntaxNode, + SyntaxNode, AstPtr, ast::{self, AstNode, NameOwner}, algo::{find_node_at_offset, find_token_at_offset}, }; use crate::{ - HirDatabase, Function, Struct, Enum,Const,Static, + HirDatabase, Function, Struct, Enum, Const, Static, Either, AsName, Module, HirFileId, Crate, Trait, Resolver, ids::LocationCtx, expr, AstId @@ -258,3 +260,97 @@ fn try_get_resolver_for_node( None } } + +// Name is bad, don't use inside HIR +#[derive(Debug)] +pub struct SourceAnalyser { + resolver: Resolver, + body_source_map: Option>, + infer: Option>, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PathResolution { + /// An item + Def(crate::ModuleDef), + /// A local binding (only value namespace) + LocalBinding(crate::expr::PatId), + /// A generic parameter + GenericParam(u32), + SelfType(crate::ImplBlock), + AssocItem(crate::ImplItem), +} + +impl SourceAnalyser { + pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyser { + let resolver = resolver_for_node(db, file_id, node); + let function = function_from_child_node(db, file_id, node); + if let Some(function) = function { + SourceAnalyser { + resolver, + body_source_map: Some(function.body_source_map(db)), + infer: Some(function.infer(db)), + } + } else { + SourceAnalyser { resolver, body_source_map: None, infer: None } + } + } + + pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option { + let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?; + Some(self.infer.as_ref()?[expr_id].clone()) + } + + pub fn type_of_pat(&self, _db: &impl HirDatabase, pat: &ast::Pat) -> Option { + let pat_id = self.body_source_map.as_ref()?.node_pat(pat)?; + Some(self.infer.as_ref()?[pat_id].clone()) + } + + pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { + let expr_id = self.body_source_map.as_ref()?.node_expr(call.into())?; + self.infer.as_ref()?.method_resolution(expr_id) + } + + pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option { + let expr_id = self.body_source_map.as_ref()?.node_expr(field.into())?; + self.infer.as_ref()?.field_resolution(expr_id) + } + + pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option { + if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { + let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?; + if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { + return Some(PathResolution::AssocItem(assoc)); + } + } + if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) { + let pat_id = self.body_source_map.as_ref()?.node_pat(path_pat.into())?; + if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) { + return Some(PathResolution::AssocItem(assoc)); + } + } + let hir_path = crate::Path::from_ast(path)?; + let res = self.resolver.resolve_path(db, &hir_path); + let res = res.clone().take_types().or_else(|| res.take_values())?; + Some(res.into()) + } + + pub fn pat_syntax( + &self, + _db: &impl HirDatabase, + pat: crate::expr::PatId, + ) -> Option, AstPtr>> { + self.body_source_map.as_ref()?.pat_syntax(pat) + } +} + +impl From for PathResolution { + fn from(res: crate::Resolution) -> PathResolution { + match res { + crate::Resolution::Def(it) => PathResolution::Def(it), + crate::Resolution::LocalBinding(it) => PathResolution::LocalBinding(it), + crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it), + crate::Resolution::SelfType(it) => PathResolution::SelfType(it), + } + } +} diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index c093d5cfb6f..3580573647c 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs @@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions}; /// Complete dot accesses, i.e. fields or methods (currently only fields). pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { - let (function, receiver) = match (&ctx.function, ctx.dot_receiver) { - (Some(function), Some(receiver)) => (function, receiver), - _ => return, - }; - let infer_result = function.infer(ctx.db); - let source_map = function.body_source_map(ctx.db); - let expr = match source_map.node_expr(receiver) { - Some(expr) => expr, + let receiver_ty = match ctx.dot_receiver.and_then(|it| ctx.analyzer.type_of(ctx.db, it)) { + Some(it) => it, None => return, }; - let receiver_ty = infer_result[expr].clone(); if !ctx.is_call { complete_fields(acc, ctx, receiver_ty.clone()); } diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_struct_literal.rs index f58bcd03eb8..48fbf67f74f 100644 --- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs +++ b/crates/ra_ide_api/src/completion/complete_struct_literal.rs @@ -4,17 +4,10 @@ use crate::completion::{CompletionContext, Completions}; /// Complete fields in fields literals. pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { - let (function, struct_lit) = match (&ctx.function, ctx.struct_lit_syntax) { - (Some(function), Some(struct_lit)) => (function, struct_lit), - _ => return, - }; - let infer_result = function.infer(ctx.db); - let source_map = function.body_source_map(ctx.db); - let expr = match source_map.node_expr(struct_lit.into()) { - Some(expr) => expr, + let ty = match ctx.struct_lit_syntax.and_then(|it| ctx.analyzer.type_of(ctx.db, it.into())) { + Some(it) => it, None => return, }; - let ty = infer_result[expr].clone(); let (adt, substs) = match ty.as_adt() { Some(res) => res, _ => return, diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index 65dffa4706c..ce21fca9b0d 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -14,6 +14,7 @@ use crate::{db, FilePosition}; #[derive(Debug)] pub(crate) struct CompletionContext<'a> { pub(super) db: &'a db::RootDatabase, + pub(super) analyzer: hir::SourceAnalyser, pub(super) offset: TextUnit, pub(super) token: SyntaxToken<'a>, pub(super) resolver: Resolver, @@ -50,8 +51,10 @@ impl<'a> CompletionContext<'a> { let resolver = source_binder::resolver_for_position(db, position); let module = source_binder::module_from_position(db, position); let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; + let analyzer = hir::SourceAnalyser::new(db, position.file_id, token.parent()); let mut ctx = CompletionContext { db, + analyzer, token, offset: position.offset, resolver, diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index 3c518faf50f..84645287d74 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs @@ -1,11 +1,11 @@ use ra_db::{FileId, SourceDatabase}; use ra_syntax::{ - SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, TreeArc, + SyntaxNode, AstNode, SmolStr, TextRange, TreeArc, AstPtr, SyntaxKind::{self, NAME}, ast::{self, NameOwner, VisibilityOwner, TypeAscriptionOwner}, algo::visit::{visitor, Visitor}, }; -use hir::{ModuleSource, FieldSource, Name, ImplItem}; +use hir::{ModuleSource, FieldSource, ImplItem, Either}; use crate::{FileSymbol, db::RootDatabase}; @@ -74,15 +74,25 @@ impl NavigationTarget { } } - pub(crate) fn from_scope_entry( + pub(crate) fn from_pat( + db: &RootDatabase, file_id: FileId, - name: Name, - ptr: SyntaxNodePtr, + pat: Either, AstPtr>, ) -> NavigationTarget { + let file = db.parse(file_id); + let (name, full_range) = match pat { + Either::A(pat) => match pat.to_node(&file).kind() { + ast::PatKind::BindPat(pat) => { + return NavigationTarget::from_bind_pat(file_id, &pat) + } + _ => ("_".into(), pat.syntax_node_ptr().range()), + }, + Either::B(slf) => ("self".into(), slf.syntax_node_ptr().range()), + }; NavigationTarget { file_id, - name: name.to_string().into(), - full_range: ptr.range(), + name, + full_range, focus_range: None, kind: NAME, container_name: None, @@ -229,6 +239,7 @@ impl NavigationTarget { /// Allows `NavigationTarget` to be created from a `NameOwner` pub(crate) fn from_named(file_id: FileId, node: &impl ast::NameOwner) -> NavigationTarget { + //FIXME: use `_` instead of empty string let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); let focus_range = node.name().map(|it| it.syntax().range()); NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax()) diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 60c1f50856a..7f93f50c441 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -5,7 +5,6 @@ use ra_syntax::{ SyntaxNode, }; use test_utils::tested_by; -use hir::Resolution; use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo}; @@ -48,127 +47,72 @@ pub(crate) fn reference_definition( ) -> ReferenceResult { use self::ReferenceResult::*; - let function = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax()); + let analyzer = hir::SourceAnalyser::new(db, file_id, name_ref.syntax()); - if let Some(function) = function { - // Check if it is a method - if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { - tested_by!(goto_definition_works_for_methods); - let infer_result = function.infer(db); - let source_map = function.body_source_map(db); - let expr = ast::Expr::cast(method_call.syntax()).unwrap(); - if let Some(func) = - source_map.node_expr(expr).and_then(|it| infer_result.method_resolution(it)) - { - return Exact(NavigationTarget::from_function(db, func)); - }; - } - // It could also be a field access - if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { - tested_by!(goto_definition_works_for_fields); - let infer_result = function.infer(db); - let source_map = function.body_source_map(db); - let expr = ast::Expr::cast(field_expr.syntax()).unwrap(); - if let Some(field) = - source_map.node_expr(expr).and_then(|it| infer_result.field_resolution(it)) - { - return Exact(NavigationTarget::from_field(db, field)); - }; + // Special cases: + + // Check if it is a method + if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { + tested_by!(goto_definition_works_for_methods); + if let Some(func) = analyzer.resolve_method_call(method_call) { + return Exact(NavigationTarget::from_function(db, func)); } + } + // It could also be a field access + if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { + tested_by!(goto_definition_works_for_fields); + if let Some(field) = analyzer.resolve_field(field_expr) { + return Exact(NavigationTarget::from_field(db, field)); + }; + } - // It could also be a named field - if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { - tested_by!(goto_definition_works_for_named_fields); + // It could also be a named field + if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { + tested_by!(goto_definition_works_for_named_fields); - let infer_result = function.infer(db); - let source_map = function.body_source_map(db); + let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); - let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); + if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) { + if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { + let hir_path = hir::Path::from_name_ref(name_ref); + let hir_name = hir_path.as_ident().unwrap(); - if let Some(expr) = struct_lit.and_then(|lit| source_map.node_expr(lit.into())) { - let ty = infer_result[expr].clone(); - if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { - let hir_path = hir::Path::from_name_ref(name_ref); - let hir_name = hir_path.as_ident().unwrap(); - - if let Some(field) = s.field(db, hir_name) { - return Exact(NavigationTarget::from_field(db, field)); - } + if let Some(field) = s.field(db, hir_name) { + return Exact(NavigationTarget::from_field(db, field)); } } } } - // Try name resolution - let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax()); - if let Some(path) = - name_ref.syntax().ancestors().find_map(ast::Path::cast).and_then(hir::Path::from_ast) - { - let resolved = resolver.resolve_path(db, &path); - match resolved.clone().take_types().or_else(|| resolved.take_values()) { - Some(Resolution::Def(def)) => return Exact(NavigationTarget::from_def(db, def)), - Some(Resolution::LocalBinding(pat)) => { - let body = resolver.body().expect("no body for local binding"); - let source_map = body.owner().body_source_map(db); - let ptr = source_map.pat_syntax(pat).expect("pattern not found in syntax mapping"); - let name = - path.as_ident().cloned().expect("local binding from a multi-segment path"); - let ptr = ptr.either(|it| it.into(), |it| it.into()); - let nav = NavigationTarget::from_scope_entry(file_id, name, ptr); - return Exact(nav); - } - Some(Resolution::GenericParam(..)) => { - // FIXME: go to the generic param def - } - Some(Resolution::SelfType(impl_block)) => { - let ty = impl_block.target_ty(db); - - if let Some((def_id, _)) = ty.as_adt() { - return Exact(NavigationTarget::from_adt_def(db, def_id)); + // General case, a path or a local: + if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { + if let Some(resolved) = analyzer.resolve_path(db, path) { + match resolved { + hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)), + hir::PathResolution::LocalBinding(pat) => { + if let Some(pat) = analyzer.pat_syntax(db, pat) { + let nav = NavigationTarget::from_pat(db, file_id, pat); + return Exact(nav); + } } - } - None => { - // If we failed to resolve then check associated items - if let Some(function) = function { - // Resolve associated item for path expressions - if let Some(path_expr) = - name_ref.syntax().ancestors().find_map(ast::PathExpr::cast) - { - let infer_result = function.infer(db); - let source_map = function.body_source_map(db); + hir::PathResolution::GenericParam(..) => { + // FIXME: go to the generic param def + } + hir::PathResolution::SelfType(impl_block) => { + let ty = impl_block.target_ty(db); - if let Some(expr) = ast::Expr::cast(path_expr.syntax()) { - if let Some(res) = source_map - .node_expr(expr) - .and_then(|it| infer_result.assoc_resolutions_for_expr(it.into())) - { - return Exact(NavigationTarget::from_impl_item(db, res)); - } - } - } - - // Resolve associated item for path patterns - if let Some(path_pat) = - name_ref.syntax().ancestors().find_map(ast::PathPat::cast) - { - let infer_result = function.infer(db); - let source_map = function.body_source_map(db); - - let pat: &ast::Pat = path_pat.into(); - - if let Some(res) = source_map - .node_pat(pat) - .and_then(|it| infer_result.assoc_resolutions_for_pat(it.into())) - { - return Exact(NavigationTarget::from_impl_item(db, res)); - } + if let Some((def_id, _)) = ty.as_adt() { + return Exact(NavigationTarget::from_adt_def(db, def_id)); } } + hir::PathResolution::AssocItem(assoc) => { + return Exact(NavigationTarget::from_impl_item(db, assoc)) + } } } } - // If that fails try the index based approach. + // Fallback index based approach: let navs = crate::symbol_index::index_resolve(db, name_ref) .into_iter() .map(NavigationTarget::from_symbol) diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 3a8c93b99fc..ec167a19651 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -132,17 +132,15 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option { .ancestors() .take_while(|it| it.range() == leaf_node.range()) .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?; - let parent_fn = node.ancestors().find_map(ast::FnDef::cast)?; - let function = hir::source_binder::function_from_source(db, frange.file_id, parent_fn)?; - let infer = function.infer(db); - let source_map = function.body_source_map(db); - if let Some(expr) = ast::Expr::cast(node).and_then(|e| source_map.node_expr(e)) { - Some(infer[expr].display(db).to_string()) - } else if let Some(pat) = ast::Pat::cast(node).and_then(|p| source_map.node_pat(p)) { - Some(infer[pat].display(db).to_string()) + let analyzer = hir::SourceAnalyser::new(db, frange.file_id, node); + let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) { + ty + } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) { + ty } else { - None - } + return None; + }; + Some(ty.display(db).to_string()) } #[cfg(test)] From 505acc973b3b865195d7d0aeb47c419c35f6bbbc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 15:34:13 +0300 Subject: [PATCH 02/33] Make call info to use real name resolution --- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/ty.rs | 16 ++++++-- crates/ra_ide_api/src/call_info.rs | 37 ++++++++++--------- .../src/completion/completion_context.rs | 6 --- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 59b402c5783..8702c6222a7 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -60,7 +60,7 @@ pub use self::{ source_id::{AstIdMap, ErasedFileAstId}, ids::{HirFileId, MacroDefId, MacroCallId, MacroCallLoc}, nameres::{PerNs, Namespace, ImportId}, - ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay}, + ty::{Ty, ApplicationTy, TypeCtor, Substs, display::HirDisplay, CallableDef}, impl_block::{ImplBlock, ImplItem}, docs::{Docs, Documentation}, adt::AdtDef, diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 20e55d92d09..ecf13fbc300 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -15,11 +15,12 @@ use std::sync::Arc; use std::{fmt, mem}; use crate::{Name, AdtDef, type_ref::Mutability, db::HirDatabase, Trait}; - -pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field, callable_item_sig}; -pub(crate) use infer::{infer, InferenceResult, InferTy}; use display::{HirDisplay, HirFormatter}; +pub(crate) use lower::{TypableDef, type_for_def, type_for_field, callable_item_sig}; +pub(crate) use infer::{infer, InferenceResult, InferTy}; +pub use lower::CallableDef; + /// A type constructor or type name: this might be something like the primitive /// type `bool`, a struct like `Vec`, or things like function pointers or /// tuples. @@ -288,6 +289,15 @@ impl Ty { } } + pub fn as_callable(&self) -> Option<(CallableDef, &Substs)> { + match self { + Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(callable_def), parameters }) => { + Some((*callable_def, parameters)) + } + _ => None, + } + } + fn builtin_deref(&self) -> Option { match self { Ty::Apply(a_ty) => match a_ty.ctor { diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index dbb3853d0fc..d0687677720 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs @@ -2,7 +2,6 @@ use test_utils::tested_by; use ra_db::SourceDatabase; use ra_syntax::{ AstNode, SyntaxNode, TextUnit, - SyntaxKind::FN_DEF, ast::{self, ArgListOwner}, algo::find_node_at_offset, }; @@ -18,19 +17,26 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { + //FIXME: apply subst + let (callable_def, _subst) = + analyser.type_of(db, expr.expr()?.into())?.as_callable()?; + match callable_def { + hir::CallableDef::Function(it) => it, + //FIXME: handle other callables + _ => return None, + } + } + FnCallNode::MethodCallExpr(expr) => analyser.resolve_method_call(expr)?, + }; let mut call_info = CallInfo::new(db, function); // If we have a calling expression let's find which argument we are on let num_params = call_info.parameters().len(); - let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); + let has_self = function.signature(db).has_self_param(); if num_params == 1 { if !has_self { @@ -142,7 +148,7 @@ mod tests { } #[test] - fn test_fn_signature_two_args_first() { + fn test_fn_signature_two_args_firstx() { let info = call_info( r#"fn foo(x: u32, y: u32) -> u32 {x + y} fn bar() { foo(<|>3, ); }"#, @@ -382,11 +388,9 @@ assert_eq!(6, my_crate::add_one(5)); fn test_fn_signature_with_docs_from_actix() { let info = call_info( r#" -pub trait WriteHandler -where - Self: Actor, - Self::Context: ActorContext, -{ +struct WriteHandler; + +impl WriteHandler { /// Method is called when writer emits error. /// /// If this method returns `ErrorAction::Continue` writer processing @@ -403,8 +407,7 @@ where } } -pub fn foo() { - WriteHandler r; +pub fn foo(mut r: WriteHandler<()>) { r.finished(<|>); } diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index ce21fca9b0d..ddcf46b4e39 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -19,7 +19,6 @@ pub(crate) struct CompletionContext<'a> { pub(super) token: SyntaxToken<'a>, pub(super) resolver: Resolver, pub(super) module: Option, - pub(super) function: Option, pub(super) function_syntax: Option<&'a ast::FnDef>, pub(super) use_item_syntax: Option<&'a ast::UseItem>, pub(super) struct_lit_syntax: Option<&'a ast::StructLit>, @@ -59,7 +58,6 @@ impl<'a> CompletionContext<'a> { offset: position.offset, resolver, module, - function: None, function_syntax: None, use_item_syntax: None, struct_lit_syntax: None, @@ -150,10 +148,6 @@ impl<'a> CompletionContext<'a> { .ancestors() .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) .find_map(ast::FnDef::cast); - if let (Some(module), Some(fn_def)) = (self.module, self.function_syntax) { - let function = source_binder::function_from_module(self.db, module, fn_def); - self.function = Some(function); - } let parent = match name_ref.syntax().parent() { Some(it) => it, From 6c2ba945ed331f0ce95eddd5b2183aa6fdf0f94b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 15:35:38 +0300 Subject: [PATCH 03/33] reduce visibility --- crates/ra_ide_api/src/call_info.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index d0687677720..1f1d05409bb 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs @@ -80,7 +80,7 @@ enum FnCallNode<'a> { } impl<'a> FnCallNode<'a> { - pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option> { + fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option> { if let Some(expr) = find_node_at_offset::(syntax, offset) { return Some(FnCallNode::CallExpr(expr)); } @@ -90,7 +90,7 @@ impl<'a> FnCallNode<'a> { None } - pub fn name_ref(&self) -> Option<&'a ast::NameRef> { + fn name_ref(&self) -> Option<&'a ast::NameRef> { match *self { FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() { ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, @@ -103,7 +103,7 @@ impl<'a> FnCallNode<'a> { } } - pub fn arg_list(&self) -> Option<&'a ast::ArgList> { + fn arg_list(&self) -> Option<&'a ast::ArgList> { match *self { FnCallNode::CallExpr(expr) => expr.arg_list(), FnCallNode::MethodCallExpr(expr) => expr.arg_list(), From b6809b6695f9c4cec82ff98d73f7ff24f96cbecf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 15:51:02 +0300 Subject: [PATCH 04/33] rename --- crates/ra_assists/src/add_explicit_type.rs | 2 +- crates/ra_assists/src/add_missing_impl_members.rs | 6 +++--- crates/ra_assists/src/fill_match_arms.rs | 2 +- crates/ra_assists/src/fill_struct_fields.rs | 2 +- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/source_binder.rs | 12 ++++++------ crates/ra_ide_api/src/call_info.rs | 2 +- .../ra_ide_api/src/completion/completion_context.rs | 4 ++-- crates/ra_ide_api/src/goto_definition.rs | 2 +- crates/ra_ide_api/src/hover.rs | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs index c50db4e2102..c3674ffdcc9 100644 --- a/crates/ra_assists/src/add_explicit_type.rs +++ b/crates/ra_assists/src/add_explicit_type.rs @@ -29,7 +29,7 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx) -> Option< } // Infer type let db = ctx.db; - let analyzer = hir::SourceAnalyser::new(db, ctx.frange.file_id, stmt.syntax()); + let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax()); let ty = analyzer.type_of(db, expr)?; // Assist not applicable if the type is unknown if is_unknown(&ty) { diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 0b2127e1180..04b3f3c7622 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs @@ -45,7 +45,7 @@ fn add_missing_impl_members_inner( let trait_def = { let file_id = ctx.frange.file_id; let position = FilePosition { file_id, offset: impl_node.syntax().range().start() }; - let analyser = hir::SourceAnalyser::new(ctx.db, position.file_id, impl_node.syntax()); + let analyser = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax()); resolve_target_trait_def(ctx.db, &analyser, impl_node)? }; @@ -121,13 +121,13 @@ fn add_missing_impl_members_inner( /// implemented) to a `ast::TraitDef`. fn resolve_target_trait_def( db: &impl HirDatabase, - binder: &hir::SourceAnalyser, + analyzer: &hir::SourceAnalyzer, impl_block: &ast::ImplBlock, ) -> Option> { let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?.path()?; - match binder.resolve_path(db, &ast_path) { + match analyzer.resolve_path(db, &ast_path) { Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1), _ => None, } diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs index 050b1c73f31..8110b2676be 100644 --- a/crates/ra_assists/src/fill_match_arms.rs +++ b/crates/ra_assists/src/fill_match_arms.rs @@ -20,7 +20,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx) -> Option Some(e), diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs index e23cccbb8f0..81f762e8d86 100644 --- a/crates/ra_assists/src/fill_struct_fields.rs +++ b/crates/ra_assists/src/fill_struct_fields.rs @@ -51,7 +51,7 @@ where } fn evaluate_struct_def_fields(&mut self) -> Option<()> { - let analyzer = hir::SourceAnalyser::new( + let analyzer = hir::SourceAnalyzer::new( self.ctx.db, self.ctx.frange.file_id, self.struct_lit.syntax(), diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 8702c6222a7..3ca810a8bce 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -66,7 +66,7 @@ pub use self::{ adt::AdtDef, expr::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}, resolve::{Resolver, Resolution}, - source_binder::{SourceAnalyser, PathResolution}, + source_binder::{SourceAnalyzer, PathResolution}, }; pub use self::code_model_api::{ diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 79e304383a0..309e33ca93a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -261,9 +261,9 @@ fn try_get_resolver_for_node( } } -// Name is bad, don't use inside HIR +/// `SourceAnalyzer` #[derive(Debug)] -pub struct SourceAnalyser { +pub struct SourceAnalyzer { resolver: Resolver, body_source_map: Option>, infer: Option>, @@ -281,18 +281,18 @@ pub enum PathResolution { AssocItem(crate::ImplItem), } -impl SourceAnalyser { - pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyser { +impl SourceAnalyzer { + pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyzer { let resolver = resolver_for_node(db, file_id, node); let function = function_from_child_node(db, file_id, node); if let Some(function) = function { - SourceAnalyser { + SourceAnalyzer { resolver, body_source_map: Some(function.body_source_map(db)), infer: Some(function.infer(db)), } } else { - SourceAnalyser { resolver, body_source_map: None, infer: None } + SourceAnalyzer { resolver, body_source_map: None, infer: None } } } diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index 1f1d05409bb..a6676cad5ce 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs @@ -17,7 +17,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { //FIXME: apply subst diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index ddcf46b4e39..98cdccef7fd 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -14,7 +14,7 @@ use crate::{db, FilePosition}; #[derive(Debug)] pub(crate) struct CompletionContext<'a> { pub(super) db: &'a db::RootDatabase, - pub(super) analyzer: hir::SourceAnalyser, + pub(super) analyzer: hir::SourceAnalyzer, pub(super) offset: TextUnit, pub(super) token: SyntaxToken<'a>, pub(super) resolver: Resolver, @@ -50,7 +50,7 @@ impl<'a> CompletionContext<'a> { let resolver = source_binder::resolver_for_position(db, position); let module = source_binder::module_from_position(db, position); let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; - let analyzer = hir::SourceAnalyser::new(db, position.file_id, token.parent()); + let analyzer = hir::SourceAnalyzer::new(db, position.file_id, token.parent()); let mut ctx = CompletionContext { db, analyzer, diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 7f93f50c441..1f1a8d1261b 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -47,7 +47,7 @@ pub(crate) fn reference_definition( ) -> ReferenceResult { use self::ReferenceResult::*; - let analyzer = hir::SourceAnalyser::new(db, file_id, name_ref.syntax()); + let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax()); // Special cases: diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index ec167a19651..0cba5a6651c 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -132,7 +132,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option { .ancestors() .take_while(|it| it.range() == leaf_node.range()) .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?; - let analyzer = hir::SourceAnalyser::new(db, frange.file_id, node); + let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, node); let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) { ty } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) { From 07cc047b4ffe3049dfe95fc5cd59383336976e2d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 15:58:00 +0300 Subject: [PATCH 05/33] minimize the API --- crates/ra_hir/src/source_binder.rs | 28 ++++++++---------------- crates/ra_ide_api/src/goto_definition.rs | 6 ++--- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 309e33ca93a..ec9af035f72 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -261,7 +261,8 @@ fn try_get_resolver_for_node( } } -/// `SourceAnalyzer` +/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of +/// original source files. It should not be used inside the HIR itself. #[derive(Debug)] pub struct SourceAnalyzer { resolver: Resolver, @@ -274,7 +275,7 @@ pub enum PathResolution { /// An item Def(crate::ModuleDef), /// A local binding (only value namespace) - LocalBinding(crate::expr::PatId), + LocalBinding(Either, AstPtr>), /// A generic parameter GenericParam(u32), SelfType(crate::ImplBlock), @@ -332,25 +333,14 @@ impl SourceAnalyzer { let hir_path = crate::Path::from_ast(path)?; let res = self.resolver.resolve_path(db, &hir_path); let res = res.clone().take_types().or_else(|| res.take_values())?; - Some(res.into()) - } - - pub fn pat_syntax( - &self, - _db: &impl HirDatabase, - pat: crate::expr::PatId, - ) -> Option, AstPtr>> { - self.body_source_map.as_ref()?.pat_syntax(pat) - } -} - -impl From for PathResolution { - fn from(res: crate::Resolution) -> PathResolution { - match res { + let res = match res { crate::Resolution::Def(it) => PathResolution::Def(it), - crate::Resolution::LocalBinding(it) => PathResolution::LocalBinding(it), + crate::Resolution::LocalBinding(it) => { + PathResolution::LocalBinding(self.body_source_map.as_ref()?.pat_syntax(it)?) + } crate::Resolution::GenericParam(it) => PathResolution::GenericParam(it), crate::Resolution::SelfType(it) => PathResolution::SelfType(it), - } + }; + Some(res) } } diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 1f1a8d1261b..abcc682e70f 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -90,10 +90,8 @@ pub(crate) fn reference_definition( match resolved { hir::PathResolution::Def(def) => return Exact(NavigationTarget::from_def(db, def)), hir::PathResolution::LocalBinding(pat) => { - if let Some(pat) = analyzer.pat_syntax(db, pat) { - let nav = NavigationTarget::from_pat(db, file_id, pat); - return Exact(nav); - } + let nav = NavigationTarget::from_pat(db, file_id, pat); + return Exact(nav); } hir::PathResolution::GenericParam(..) => { // FIXME: go to the generic param def From 5471c1ef4b2fda2fbaa63f7d8404abf04a3e9da4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 16:22:10 +0300 Subject: [PATCH 06/33] generalize SourceAnalyzer to handle all defs with bodies --- crates/ra_hir/src/code_model_api.rs | 8 +++++ crates/ra_hir/src/source_binder.rs | 30 ++++++++++++------- .../ra_ide_api/src/completion/complete_dot.rs | 24 +++++++++++++++ 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 660edf006dc..40bfd5faf03 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -454,6 +454,14 @@ impl DefWithBody { db.body_hir(*self) } + pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { + match *self { + DefWithBody::Const(ref c) => c.body_source_map(db), + DefWithBody::Function(ref f) => f.body_source_map(db), + DefWithBody::Static(ref s) => s.body_source_map(db), + } + } + /// Builds a resolver for code inside this item. pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { match *self { diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index ec9af035f72..dc9d614c0e2 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -15,7 +15,7 @@ use ra_syntax::{ }; use crate::{ - HirDatabase, Function, Struct, Enum, Const, Static, Either, + HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, AsName, Module, HirFileId, Crate, Trait, Resolver, ids::LocationCtx, expr, AstId @@ -219,7 +219,7 @@ pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> R .unwrap_or_default() } -pub fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver { +fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver { node.ancestors() .find_map(|node| { if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { @@ -284,16 +284,24 @@ pub enum PathResolution { impl SourceAnalyzer { pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyzer { - let resolver = resolver_for_node(db, file_id, node); - let function = function_from_child_node(db, file_id, node); - if let Some(function) = function { - SourceAnalyzer { - resolver, - body_source_map: Some(function.body_source_map(db)), - infer: Some(function.infer(db)), + let def_with_body = node.ancestors().find_map(|node| { + if let Some(src) = ast::FnDef::cast(node) { + return function_from_source(db, file_id, src).map(DefWithBody::from); } - } else { - SourceAnalyzer { resolver, body_source_map: None, infer: None } + if let Some(src) = ast::StaticDef::cast(node) { + return static_from_source(db, file_id, src).map(DefWithBody::from); + } + if let Some(src) = ast::ConstDef::cast(node) { + return const_from_source(db, file_id, src).map(DefWithBody::from); + } + None + }); + SourceAnalyzer { + resolver: def_with_body + .map(|it| it.resolver(db)) + .unwrap_or_else(|| resolver_for_node(db, file_id, node)), + body_source_map: def_with_body.map(|it| it.body_source_map(db)), + infer: def_with_body.map(|it| it.infer(db)), } } diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index 3580573647c..4a111aba5a9 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs @@ -305,6 +305,30 @@ mod tests { kind: Method, detail: "pub fn blah(&self)" } +]"### + ); + } + + #[test] + fn test_completion_works_in_consts() { + assert_debug_snapshot_matches!( + do_ref_completion( + r" + struct A { the_field: u32 } + const X: u32 = { + A { the_field: 92 }.<|> + }; + ", + ), + @r###"[ + CompletionItem { + label: "the_field", + source_range: [106; 106), + delete: [106; 106), + insert: "the_field", + kind: Field, + detail: "u32" + } ]"### ); } From 3c9f2d0e372cff6490dcd30411cb6cc1f691fde7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 16:39:56 +0300 Subject: [PATCH 07/33] simplify --- crates/ra_hir/src/code_model_api.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 40bfd5faf03..3c1f7cd65b3 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -455,11 +455,7 @@ impl DefWithBody { } pub fn body_source_map(&self, db: &impl HirDatabase) -> Arc { - match *self { - DefWithBody::Const(ref c) => c.body_source_map(db), - DefWithBody::Function(ref f) => f.body_source_map(db), - DefWithBody::Static(ref s) => s.body_source_map(db), - } + db.body_with_source_map(*self).1 } /// Builds a resolver for code inside this item. @@ -610,6 +606,7 @@ impl Const { db.infer((*self).into()) } + #[cfg(test)] pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc { db.body_with_source_map((*self).into()).1 } @@ -683,6 +680,7 @@ impl Static { db.infer((*self).into()) } + #[cfg(test)] pub(crate) fn body_source_map(&self, db: &impl HirDatabase) -> Arc { db.body_with_source_map((*self).into()).1 } From ebb0c377f0ab99a0f5e6d0c776cb9b026b62b0e4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 16:49:35 +0300 Subject: [PATCH 08/33] remove resolver from CompletonContext --- crates/ra_hir/src/source_binder.rs | 29 ++++--------------- .../src/completion/complete_path.rs | 2 +- .../src/completion/complete_pattern.rs | 2 +- .../src/completion/complete_scope.rs | 2 +- .../src/completion/completion_context.rs | 5 +--- 5 files changed, 9 insertions(+), 31 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index dc9d614c0e2..560b483035f 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -11,7 +11,7 @@ use ra_db::{FileId, FilePosition}; use ra_syntax::{ SyntaxNode, AstPtr, ast::{self, AstNode, NameOwner}, - algo::{find_node_at_offset, find_token_at_offset}, + algo::find_node_at_offset, }; use crate::{ @@ -196,29 +196,6 @@ pub fn trait_from_module( Trait { id: ctx.to_def(trait_def) } } -pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver { - let file_id = position.file_id; - let file = db.parse(file_id); - find_token_at_offset(file.syntax(), position.offset) - .find_map(|token| { - token.parent().ancestors().find_map(|node| { - if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { - if let Some(func) = function_from_child_node(db, file_id, node) { - let scopes = func.scopes(db); - let scope = scopes.scope_for_offset(position.offset); - Some(expr::resolver_for_scope(func.body(db), db, scope)) - } else { - // FIXME const/static/array length - None - } - } else { - try_get_resolver_for_node(db, file_id, node) - } - }) - }) - .unwrap_or_default() -} - fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver { node.ancestors() .find_map(|node| { @@ -305,6 +282,10 @@ impl SourceAnalyzer { } } + pub fn resolver(&self) -> &Resolver { + &self.resolver + } + pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option { let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?; Some(self.infer.as_ref()?[expr_id].clone()) diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index 7e47fa6bdf7..e9bdf5af25f 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -9,7 +9,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { Some(path) => path.clone(), _ => return, }; - let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() { + let def = match ctx.analyzer.resolver().resolve_path(ctx.db, &path).take_types() { Some(Resolution::Def(def)) => def, _ => return, }; diff --git a/crates/ra_ide_api/src/completion/complete_pattern.rs b/crates/ra_ide_api/src/completion/complete_pattern.rs index 7abcd019b12..abbb8351886 100644 --- a/crates/ra_ide_api/src/completion/complete_pattern.rs +++ b/crates/ra_ide_api/src/completion/complete_pattern.rs @@ -7,7 +7,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { } // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports - let names = ctx.resolver.all_names(ctx.db); + let names = ctx.analyzer.resolver().all_names(ctx.db); for (name, res) in names.into_iter() { let r = res.as_ref(); let def = match r.take_types().or(r.take_values()) { diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 9d82f22708f..4c5d07ce5d3 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -4,7 +4,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { return; } - let names = ctx.resolver.all_names(ctx.db); + let names = ctx.analyzer.resolver().all_names(ctx.db); names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); } diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index 98cdccef7fd..86b30e78715 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -5,7 +5,7 @@ use ra_syntax::{ algo::{find_token_at_offset, find_covering_element, find_node_at_offset}, SyntaxKind::*, }; -use hir::{source_binder, Resolver}; +use hir::source_binder; use crate::{db, FilePosition}; @@ -17,7 +17,6 @@ pub(crate) struct CompletionContext<'a> { pub(super) analyzer: hir::SourceAnalyzer, pub(super) offset: TextUnit, pub(super) token: SyntaxToken<'a>, - pub(super) resolver: Resolver, pub(super) module: Option, pub(super) function_syntax: Option<&'a ast::FnDef>, pub(super) use_item_syntax: Option<&'a ast::UseItem>, @@ -47,7 +46,6 @@ impl<'a> CompletionContext<'a> { original_file: &'a SourceFile, position: FilePosition, ) -> Option> { - let resolver = source_binder::resolver_for_position(db, position); let module = source_binder::module_from_position(db, position); let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; let analyzer = hir::SourceAnalyzer::new(db, position.file_id, token.parent()); @@ -56,7 +54,6 @@ impl<'a> CompletionContext<'a> { analyzer, token, offset: position.offset, - resolver, module, function_syntax: None, use_item_syntax: None, From a347208bb283963cacb75439d1cf474d1f064f02 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 16:51:30 +0300 Subject: [PATCH 09/33] use correct resolver for expressions --- crates/ra_hir/src/source_binder.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 560b483035f..a764c54c037 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -274,9 +274,7 @@ impl SourceAnalyzer { None }); SourceAnalyzer { - resolver: def_with_body - .map(|it| it.resolver(db)) - .unwrap_or_else(|| resolver_for_node(db, file_id, node)), + resolver: resolver_for_node(db, file_id, node), body_source_map: def_with_body.map(|it| it.body_source_map(db)), infer: def_with_body.map(|it| it.infer(db)), } From cf2ba2791d57123248141a3066071180f4255b1c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 17:14:41 +0300 Subject: [PATCH 10/33] add todo --- crates/ra_hir/src/source_binder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a764c54c037..182bc36ffbe 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -274,6 +274,7 @@ impl SourceAnalyzer { None }); SourceAnalyzer { + //TODO: use scope_for_offset here to get correct scope for completion resolver: resolver_for_node(db, file_id, node), body_source_map: def_with_body.map(|it| it.body_source_map(db)), infer: def_with_body.map(|it| it.infer(db)), From 20013de2abb95bc024f55163b1a5044cfb52a873 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 11 Apr 2019 17:15:20 +0300 Subject: [PATCH 11/33] fix typo --- crates/ra_assists/src/add_missing_impl_members.rs | 4 ++-- crates/ra_ide_api/src/call_info.rs | 6 +++--- crates/ra_syntax/tests/test.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 04b3f3c7622..104a4d78a6e 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs @@ -45,9 +45,9 @@ fn add_missing_impl_members_inner( let trait_def = { let file_id = ctx.frange.file_id; let position = FilePosition { file_id, offset: impl_node.syntax().range().start() }; - let analyser = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax()); + let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax()); - resolve_target_trait_def(ctx.db, &analyser, impl_node)? + resolve_target_trait_def(ctx.db, &analyzer, impl_node)? }; let missing_fns: Vec<_> = { diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index a6676cad5ce..a8495ab0196 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs @@ -17,19 +17,19 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { //FIXME: apply subst let (callable_def, _subst) = - analyser.type_of(db, expr.expr()?.into())?.as_callable()?; + analyzer.type_of(db, expr.expr()?.into())?.as_callable()?; match callable_def { hir::CallableDef::Function(it) => it, //FIXME: handle other callables _ => return None, } } - FnCallNode::MethodCallExpr(expr) => analyser.resolve_method_call(expr)?, + FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(expr)?, }; let mut call_info = CallInfo::new(db, function); diff --git a/crates/ra_syntax/tests/test.rs b/crates/ra_syntax/tests/test.rs index 276756c857d..91799f8b55a 100644 --- a/crates/ra_syntax/tests/test.rs +++ b/crates/ra_syntax/tests/test.rs @@ -60,7 +60,7 @@ fn reparse_fuzz_tests() { } } -/// Test that Rust-analyzer can parse and validate the rust-analyser +/// Test that Rust-analyzer can parse and validate the rust-analyzer /// FIXME: Use this as a benchmark #[test] fn self_hosting_parsing() { From 0fd93bc14a2d0ce2edd682d26c18979c13f181c5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 00:44:47 +0300 Subject: [PATCH 12/33] use really correct resolver for expressions --- crates/ra_assists/src/add_explicit_type.rs | 2 +- .../src/add_missing_impl_members.rs | 2 +- crates/ra_assists/src/fill_match_arms.rs | 2 +- crates/ra_assists/src/fill_struct_fields.rs | 1 + crates/ra_hir/src/source_binder.rs | 24 ++++++++++++++----- crates/ra_ide_api/src/call_info.rs | 2 +- .../src/completion/completion_context.rs | 3 ++- crates/ra_ide_api/src/goto_definition.rs | 2 +- crates/ra_ide_api/src/hover.rs | 2 +- 9 files changed, 27 insertions(+), 13 deletions(-) diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs index c3674ffdcc9..cb0ac988597 100644 --- a/crates/ra_assists/src/add_explicit_type.rs +++ b/crates/ra_assists/src/add_explicit_type.rs @@ -29,7 +29,7 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx) -> Option< } // Infer type let db = ctx.db; - let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax()); + let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None); let ty = analyzer.type_of(db, expr)?; // Assist not applicable if the type is unknown if is_unknown(&ty) { diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs index 104a4d78a6e..100ebb7b6f6 100644 --- a/crates/ra_assists/src/add_missing_impl_members.rs +++ b/crates/ra_assists/src/add_missing_impl_members.rs @@ -45,7 +45,7 @@ fn add_missing_impl_members_inner( let trait_def = { let file_id = ctx.frange.file_id; let position = FilePosition { file_id, offset: impl_node.syntax().range().start() }; - let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax()); + let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None); resolve_target_trait_def(ctx.db, &analyzer, impl_node)? }; diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs index 8110b2676be..45e327cd4ae 100644 --- a/crates/ra_assists/src/fill_match_arms.rs +++ b/crates/ra_assists/src/fill_match_arms.rs @@ -20,7 +20,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx) -> Option Some(e), diff --git a/crates/ra_assists/src/fill_struct_fields.rs b/crates/ra_assists/src/fill_struct_fields.rs index 81f762e8d86..663b4f669d0 100644 --- a/crates/ra_assists/src/fill_struct_fields.rs +++ b/crates/ra_assists/src/fill_struct_fields.rs @@ -55,6 +55,7 @@ where self.ctx.db, self.ctx.frange.file_id, self.struct_lit.syntax(), + None, ); let struct_lit_ty = analyzer.type_of(self.ctx.db, self.struct_lit.into())?; let struct_def = match struct_lit_ty.as_adt() { diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 182bc36ffbe..846394212c9 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -9,7 +9,7 @@ use std::sync::Arc; use ra_db::{FileId, FilePosition}; use ra_syntax::{ - SyntaxNode, AstPtr, + SyntaxNode, AstPtr, TextUnit, ast::{self, AstNode, NameOwner}, algo::find_node_at_offset, }; @@ -196,13 +196,21 @@ pub fn trait_from_module( Trait { id: ctx.to_def(trait_def) } } -fn resolver_for_node(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> Resolver { +fn resolver_for_node( + db: &impl HirDatabase, + file_id: FileId, + node: &SyntaxNode, + offset: Option, +) -> Resolver { node.ancestors() .find_map(|node| { if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { if let Some(func) = function_from_child_node(db, file_id, node) { let scopes = func.scopes(db); - let scope = scopes.scope_for(&node); + let scope = match offset { + None => scopes.scope_for(&node), + Some(offset) => scopes.scope_for_offset(offset), + }; Some(expr::resolver_for_scope(func.body(db), db, scope)) } else { // FIXME const/static/array length @@ -260,7 +268,12 @@ pub enum PathResolution { } impl SourceAnalyzer { - pub fn new(db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode) -> SourceAnalyzer { + pub fn new( + db: &impl HirDatabase, + file_id: FileId, + node: &SyntaxNode, + offset: Option, + ) -> SourceAnalyzer { let def_with_body = node.ancestors().find_map(|node| { if let Some(src) = ast::FnDef::cast(node) { return function_from_source(db, file_id, src).map(DefWithBody::from); @@ -274,8 +287,7 @@ impl SourceAnalyzer { None }); SourceAnalyzer { - //TODO: use scope_for_offset here to get correct scope for completion - resolver: resolver_for_node(db, file_id, node), + resolver: resolver_for_node(db, file_id, node, offset), body_source_map: def_with_body.map(|it| it.body_source_map(db)), infer: def_with_body.map(|it| it.infer(db)), } diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index a8495ab0196..4413aec7378 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs @@ -17,7 +17,7 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { //FIXME: apply subst diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index 86b30e78715..359f2cffa54 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -48,7 +48,8 @@ impl<'a> CompletionContext<'a> { ) -> Option> { let module = source_binder::module_from_position(db, position); let token = find_token_at_offset(original_file.syntax(), position.offset).left_biased()?; - let analyzer = hir::SourceAnalyzer::new(db, position.file_id, token.parent()); + let analyzer = + hir::SourceAnalyzer::new(db, position.file_id, token.parent(), Some(position.offset)); let mut ctx = CompletionContext { db, analyzer, diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index abcc682e70f..517dffbca5f 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -47,7 +47,7 @@ pub(crate) fn reference_definition( ) -> ReferenceResult { use self::ReferenceResult::*; - let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax()); + let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); // Special cases: diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 0cba5a6651c..397b567867a 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -132,7 +132,7 @@ pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option { .ancestors() .take_while(|it| it.range() == leaf_node.range()) .find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?; - let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, node); + let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, node, None); let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) { ty } else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) { From 7c13e223345e4ff14cc884fc5b785c925f0e0b61 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 00:56:57 +0300 Subject: [PATCH 13/33] simplify tests --- crates/ra_hir/src/code_model_api.rs | 10 -------- crates/ra_hir/src/source_binder.rs | 10 ++++++++ crates/ra_hir/src/ty/tests.rs | 39 ++++++++++------------------- 3 files changed, 23 insertions(+), 36 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 3c1f7cd65b3..5179f719db9 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -606,11 +606,6 @@ impl Const { db.infer((*self).into()) } - #[cfg(test)] - pub(crate) 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)); @@ -679,11 +674,6 @@ impl Static { pub fn infer(&self, db: &impl HirDatabase) -> Arc { db.infer((*self).into()) } - - #[cfg(test)] - pub(crate) 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/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 846394212c9..c3cb17882be 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -343,4 +343,14 @@ impl SourceAnalyzer { }; Some(res) } + + #[cfg(test)] + pub(crate) fn body_source_map(&self) -> Arc { + self.body_source_map.clone().unwrap() + } + + #[cfg(test)] + pub(crate) fn inference_result(&self) -> Arc { + self.infer.clone().unwrap() + } } diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index ecc63f37629..87d6bcaec19 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -4,7 +4,7 @@ use std::fmt::Write; use insta::assert_snapshot_matches; use ra_db::{SourceDatabase, salsa::Database, FilePosition}; -use ra_syntax::{algo, ast::{self, AstNode}}; +use ra_syntax::{algo, ast::{self, AstNode}, SyntaxKind::*}; use test_utils::covers; use crate::{ @@ -12,7 +12,8 @@ use crate::{ mock::MockDatabase, ty::display::HirDisplay, ty::InferenceResult, - expr::BodySourceMap + expr::BodySourceMap, + SourceAnalyzer, }; // These tests compare the inference results for all expressions in a file @@ -1862,14 +1863,14 @@ 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} [163; 164) 'z': u32 [167; 179) 'GLOBAL_CONST': u32 [189; 191) 'id': u32 -[194; 210) 'Foo::A..._CONST': u32"### +[194; 210) 'Foo::A..._CONST': u32 +[126; 128) '99': u32"### ); } @@ -1891,8 +1892,6 @@ 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} @@ -1901,7 +1900,9 @@ fn test() { [229; 230) 'z': u32 [233; 246) 'GLOBAL_STATIC': u32 [256; 257) 'w': u32 -[260; 277) 'GLOBAL...IC_MUT': u32"### +[260; 277) 'GLOBAL...IC_MUT': u32 +[118; 120) '99': u32 +[161; 163) '99': u32"### ); } @@ -2350,25 +2351,11 @@ fn infer(content: &str) -> String { } }; - 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) + for node in source_file.syntax().descendants() { + if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF { + let analyzer = SourceAnalyzer::new(&db, file_id, node, None); + infer_def(analyzer.inference_result(), analyzer.body_source_map()); + } } acc.truncate(acc.trim_end().len()); From 81e91980dd87f62bdd9013f99101f6599f067e3b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 01:05:18 +0300 Subject: [PATCH 14/33] simplify --- crates/ra_hir/src/source_binder.rs | 76 +++++++++--------------------- 1 file changed, 22 insertions(+), 54 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index c3cb17882be..476cf117f17 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -89,27 +89,6 @@ 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)?; @@ -157,27 +136,6 @@ 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(); @@ -246,6 +204,27 @@ fn try_get_resolver_for_node( } } +pub fn def_with_body_from_child_node( + db: &impl HirDatabase, + file_id: FileId, + node: &SyntaxNode, +) -> Option { + let module = module_from_child_node(db, file_id, node)?; + let ctx = LocationCtx::new(db, module, file_id.into()); + node.ancestors().find_map(|node| { + if let Some(def) = ast::FnDef::cast(node) { + return Some(Function { id: ctx.to_def(def) }.into()); + } + if let Some(def) = ast::ConstDef::cast(node) { + return Some(Const { id: ctx.to_def(def) }.into()); + } + if let Some(def) = ast::StaticDef::cast(node) { + return Some(Static { id: ctx.to_def(def) }.into()); + } + None + }) +} + /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// original source files. It should not be used inside the HIR itself. #[derive(Debug)] @@ -274,18 +253,7 @@ impl SourceAnalyzer { node: &SyntaxNode, offset: Option, ) -> SourceAnalyzer { - let def_with_body = node.ancestors().find_map(|node| { - if let Some(src) = ast::FnDef::cast(node) { - return function_from_source(db, file_id, src).map(DefWithBody::from); - } - if let Some(src) = ast::StaticDef::cast(node) { - return static_from_source(db, file_id, src).map(DefWithBody::from); - } - if let Some(src) = ast::ConstDef::cast(node) { - return const_from_source(db, file_id, src).map(DefWithBody::from); - } - None - }); + let def_with_body = def_with_body_from_child_node(db, file_id, node); SourceAnalyzer { resolver: resolver_for_node(db, file_id, node, offset), body_source_map: def_with_body.map(|it| it.body_source_map(db)), From b0d8f9ff5d2a64f93189543385485c5b48fb8d76 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 01:07:24 +0300 Subject: [PATCH 15/33] make stuff private --- crates/ra_hir/src/source_binder.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 476cf117f17..8e1f290190c 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -95,7 +95,7 @@ pub fn function_from_position(db: &impl HirDatabase, position: FilePosition) -> function_from_source(db, position.file_id, fn_def) } -pub fn function_from_source( +fn function_from_source( db: &impl HirDatabase, file_id: FileId, fn_def: &ast::FnDef, @@ -105,11 +105,7 @@ pub fn function_from_source( Some(res) } -pub fn function_from_module( - db: &impl HirDatabase, - module: Module, - fn_def: &ast::FnDef, -) -> Function { +fn function_from_module(db: &impl HirDatabase, module: Module, fn_def: &ast::FnDef) -> Function { let (file_id, _) = module.definition_source(db); let file_id = file_id.into(); let ctx = LocationCtx::new(db, module, file_id); From 58fe5598e70eef6edf109865cb87b806b22536fb Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 01:32:43 +0300 Subject: [PATCH 16/33] simplify --- crates/ra_hir/src/source_binder.rs | 6 ------ crates/ra_hir/src/ty/tests.rs | 23 ++++++++++++----------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 8e1f290190c..0d2746ac0f9 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -89,12 +89,6 @@ fn module_from_source( ) } -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)?; - function_from_source(db, position.file_id, fn_def) -} - fn function_from_source( db: &impl HirDatabase, file_id: FileId, diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 87d6bcaec19..d7c2ca132db 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -8,7 +8,6 @@ use ra_syntax::{algo, ast::{self, AstNode}, SyntaxKind::*}; use test_utils::covers; use crate::{ - source_binder, mock::MockDatabase, ty::display::HirDisplay, ty::InferenceResult, @@ -2303,13 +2302,10 @@ fn test() -> 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); - let inference_result = func.infer(db); - let (_, syntax) = func.source(db); - let node = algo::find_node_at_offset::(syntax.syntax(), pos.offset).unwrap(); - let expr = body_source_map.node_expr(node).unwrap(); - let ty = &inference_result[expr]; + let file = db.parse(pos.file_id); + let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); + let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset)); + let ty = analyzer.type_of(db, expr).unwrap(); ty.display(db).to_string() } @@ -2390,10 +2386,12 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { } ", ); - let func = source_binder::function_from_position(&db, pos).unwrap(); { + let file = db.parse(pos.file_id); + let node = + algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent(); let events = db.log_executed(|| { - func.infer(&db); + SourceAnalyzer::new(&db, pos.file_id, node, None); }); assert!(format!("{:?}", events).contains("infer")) } @@ -2410,8 +2408,11 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { db.query_mut(ra_db::FileTextQuery).set(pos.file_id, Arc::new(new_text)); { + let file = db.parse(pos.file_id); + let node = + algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent(); let events = db.log_executed(|| { - func.infer(&db); + SourceAnalyzer::new(&db, pos.file_id, node, None); }); assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) } From 3aae223d938e5a36d997c45a0f86cfcabf83b570 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:31:03 +0300 Subject: [PATCH 17/33] hide some scopes --- .../ra_assists/src/inline_local_variable.rs | 11 +++------- crates/ra_hir/src/expr/scope.rs | 7 ++++-- crates/ra_hir/src/source_binder.rs | 11 ++++++++++ crates/ra_ide_api/src/references.rs | 22 ++++++++----------- 4 files changed, 28 insertions(+), 23 deletions(-) diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs index 950c2910b80..9493acec94d 100644 --- a/crates/ra_assists/src/inline_local_variable.rs +++ b/crates/ra_assists/src/inline_local_variable.rs @@ -1,7 +1,4 @@ -use hir::{ - db::HirDatabase, - source_binder::function_from_child_node, -}; +use hir::db::HirDatabase; use ra_syntax::{ ast::{self, AstNode, AstToken, PatKind, ExprKind}, TextRange, @@ -29,10 +26,8 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx) -> Opt } else { let_stmt.syntax().range() }; - - let function = function_from_child_node(ctx.db, ctx.frange.file_id, bind_pat.syntax())?; - let scope = function.scopes(ctx.db); - let refs = scope.find_all_refs(bind_pat); + let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None); + let refs = analyzer.find_all_refs(bind_pat)?; let mut wrap_in_parens = vec![true; refs.len()]; diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 404c979eb1f..a9be9fbdbdb 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -173,7 +173,10 @@ impl ScopesWithSourceMap { .unwrap_or(original_scope) } - pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { + pub(crate) fn resolve_local_name( + &self, + name_ref: &ast::NameRef, + ) -> Option { let mut shadowed = FxHashSet::default(); let name = name_ref.as_name(); let ret = self @@ -190,7 +193,7 @@ impl ScopesWithSourceMap { }) } - pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { + pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); let ptr = Either::A(AstPtr::new(pat.into())); fn_def diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 0d2746ac0f9..bdb30031105 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -17,6 +17,7 @@ use ra_syntax::{ use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, AsName, Module, HirFileId, Crate, Trait, Resolver, + expr::scope::{ReferenceDescriptor, ScopeEntryWithSyntax}, ids::LocationCtx, expr, AstId }; @@ -222,6 +223,7 @@ pub struct SourceAnalyzer { resolver: Resolver, body_source_map: Option>, infer: Option>, + scopes: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -248,6 +250,7 @@ impl SourceAnalyzer { resolver: resolver_for_node(db, file_id, node, offset), body_source_map: def_with_body.map(|it| it.body_source_map(db)), infer: def_with_body.map(|it| it.infer(db)), + scopes: def_with_body.map(|it| it.scopes(db)), } } @@ -302,6 +305,14 @@ impl SourceAnalyzer { Some(res) } + pub fn find_all_refs(&self, pat: &ast::BindPat) -> Option> { + self.scopes.as_ref().map(|it| it.find_all_refs(pat)) + } + + pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { + self.scopes.as_ref()?.resolve_local_name(name_ref) + } + #[cfg(test)] pub(crate) fn body_source_map(&self) -> Arc { self.body_source_map.clone().unwrap() diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 3e30e047c69..ee2c1d0f09e 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -61,12 +61,11 @@ pub(crate) fn find_all_refs( position: FilePosition, ) -> Option { let file = db.parse(position.file_id); - let (binding, descr) = find_binding(db, &file, position)?; + let (binding, analyzer) = find_binding(db, &file, position)?; let declaration = NavigationTarget::from_bind_pat(position.file_id, binding); - let references = descr - .scopes(db) - .find_all_refs(binding) + let references = analyzer + .find_all_refs(binding)? .into_iter() .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) .collect::>(); @@ -77,21 +76,18 @@ pub(crate) fn find_all_refs( db: &RootDatabase, source_file: &'a SourceFile, position: FilePosition, - ) -> Option<(&'a ast::BindPat, hir::Function)> { + ) -> Option<(&'a ast::BindPat, hir::SourceAnalyzer)> { let syntax = source_file.syntax(); if let Some(binding) = find_node_at_offset::(syntax, position.offset) { - let descr = - source_binder::function_from_child_node(db, position.file_id, binding.syntax())?; - return Some((binding, descr)); + let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); + return Some((binding, analyzer)); }; let name_ref = find_node_at_offset::(syntax, position.offset)?; - let descr = - source_binder::function_from_child_node(db, position.file_id, name_ref.syntax())?; - let scope = descr.scopes(db); - let resolved = scope.resolve_local_name(name_ref)?; + let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); + let resolved = analyzer.resolve_local_name(name_ref)?; if let Either::A(ptr) = resolved.ptr() { if let ast::PatKind::BindPat(binding) = ptr.to_node(source_file).kind() { - return Some((binding, descr)); + return Some((binding, analyzer)); } } None From 9ead801a9cc8a98e44cde96ed860da8db99576e6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:32:25 +0300 Subject: [PATCH 18/33] make private --- crates/ra_hir/src/source_binder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index bdb30031105..bb638bcb6ce 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -107,7 +107,7 @@ fn function_from_module(db: &impl HirDatabase, module: Module, fn_def: &ast::FnD Function { id: ctx.to_def(fn_def) } } -pub fn function_from_child_node( +fn function_from_child_node( db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode, From 65b00736535255c089cfef58c7b4eb13d4d8dbad Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:33:34 +0300 Subject: [PATCH 19/33] simplify --- crates/ra_hir/src/source_binder.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index bb638bcb6ce..880596615f3 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -96,15 +96,9 @@ fn function_from_source( fn_def: &ast::FnDef, ) -> Option { let module = module_from_child_node(db, file_id, fn_def.syntax())?; - let res = function_from_module(db, module, fn_def); - Some(res) -} - -fn function_from_module(db: &impl HirDatabase, module: Module, fn_def: &ast::FnDef) -> Function { - let (file_id, _) = module.definition_source(db); let file_id = file_id.into(); let ctx = LocationCtx::new(db, module, file_id); - Function { id: ctx.to_def(fn_def) } + Some(Function { id: ctx.to_def(fn_def) }) } fn function_from_child_node( From 17a0e228833de16ed7e8718ba177dceeec4abfb2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:36:28 +0300 Subject: [PATCH 20/33] simplify --- crates/ra_hir/src/source_binder.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 880596615f3..649c5b68478 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -101,15 +101,6 @@ fn function_from_source( Some(Function { id: ctx.to_def(fn_def) }) } -fn function_from_child_node( - db: &impl HirDatabase, - file_id: FileId, - node: &SyntaxNode, -) -> Option { - let fn_def = node.ancestors().find_map(ast::FnDef::cast)?; - function_from_source(db, file_id, fn_def) -} - pub fn struct_from_module( db: &impl HirDatabase, module: Module, @@ -148,7 +139,11 @@ fn resolver_for_node( node.ancestors() .find_map(|node| { if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { - if let Some(func) = function_from_child_node(db, file_id, node) { + if let Some(func) = node + .ancestors() + .find_map(ast::FnDef::cast) + .and_then(|it| function_from_source(db, file_id, it)) + { let scopes = func.scopes(db); let scope = match offset { None => scopes.scope_for(&node), From d88269bc2d36d163eef5173912c40d5b6eee3847 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:38:37 +0300 Subject: [PATCH 21/33] generalize --- crates/ra_hir/src/source_binder.rs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 649c5b68478..5bb6d64ffc9 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -139,21 +139,13 @@ fn resolver_for_node( node.ancestors() .find_map(|node| { if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { - if let Some(func) = node - .ancestors() - .find_map(ast::FnDef::cast) - .and_then(|it| function_from_source(db, file_id, it)) - { - let scopes = func.scopes(db); - let scope = match offset { - None => scopes.scope_for(&node), - Some(offset) => scopes.scope_for_offset(offset), - }; - Some(expr::resolver_for_scope(func.body(db), db, scope)) - } else { - // FIXME const/static/array length - None - } + let def = def_with_body_from_child_node(db, file_id, node)?; + let scopes = def.scopes(db); + let scope = match offset { + None => scopes.scope_for(&node), + Some(offset) => scopes.scope_for_offset(offset), + }; + Some(expr::resolver_for_scope(def.body(db), db, scope)) } else { try_get_resolver_for_node(db, file_id, node) } From d4043a8dbafe6de9517dee760967dd0218bf28b7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:45:52 +0300 Subject: [PATCH 22/33] only def-with-body remains --- crates/ra_hir/src/source_binder.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 5bb6d64ffc9..67bf2de895f 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -12,6 +12,7 @@ use ra_syntax::{ SyntaxNode, AstPtr, TextUnit, ast::{self, AstNode, NameOwner}, algo::find_node_at_offset, + SyntaxKind::*, }; use crate::{ @@ -90,17 +91,6 @@ fn module_from_source( ) } -fn function_from_source( - db: &impl HirDatabase, - file_id: FileId, - fn_def: &ast::FnDef, -) -> Option { - let module = module_from_child_node(db, file_id, fn_def.syntax())?; - let file_id = file_id.into(); - let ctx = LocationCtx::new(db, module, file_id); - Some(Function { id: ctx.to_def(fn_def) }) -} - pub fn struct_from_module( db: &impl HirDatabase, module: Module, @@ -168,8 +158,8 @@ fn try_get_resolver_for_node( } else if let Some(e) = ast::EnumDef::cast(node) { let module = module_from_child_node(db, file_id, e.syntax())?; Some(enum_from_module(db, module, e).resolver(db)) - } else if let Some(f) = ast::FnDef::cast(node) { - function_from_source(db, file_id, f).map(|f| f.resolver(db)) + } else if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF { + Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db)) } else { // FIXME add missing cases None From 1e8569dce9a7259208503a6819d0a93618ddcc45 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:46:39 +0300 Subject: [PATCH 23/33] make private --- crates/ra_hir/src/source_binder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 67bf2de895f..1c9e9320d59 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -166,7 +166,7 @@ fn try_get_resolver_for_node( } } -pub fn def_with_body_from_child_node( +fn def_with_body_from_child_node( db: &impl HirDatabase, file_id: FileId, node: &SyntaxNode, From cec67b2b652e1ee140635828ba2bca1fb95cd49a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:50:02 +0300 Subject: [PATCH 24/33] obsolete fixm --- crates/ra_ide_api/src/runnables.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ra_ide_api/src/runnables.rs b/crates/ra_ide_api/src/runnables.rs index 2395930f0d7..3969076a8b2 100644 --- a/crates/ra_ide_api/src/runnables.rs +++ b/crates/ra_ide_api/src/runnables.rs @@ -65,7 +65,6 @@ fn runnable_mod(db: &RootDatabase, file_id: FileId, module: &ast::Module) -> Opt let range = module.syntax().range(); let module = hir::source_binder::module_from_child_node(db, file_id, module.syntax())?; - // FIXME: thread cancellation instead of `.ok`ing let path = module.path_to_root(db).into_iter().rev().filter_map(|it| it.name(db)).join("::"); Some(Runnable { range, kind: RunnableKind::TestMod { path } }) } From 30481808fbfea109f324dfaf93daaaebacc75333 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 09:53:02 +0300 Subject: [PATCH 25/33] make stuff private --- crates/ra_hir/src/expr/scope.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index a9be9fbdbdb..dcec51a10ee 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -110,7 +110,7 @@ impl ExprScopes { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ScopesWithSourceMap { pub(crate) source_map: Arc, - pub scopes: Arc, + pub(crate) scopes: Arc, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -134,7 +134,7 @@ impl ScopesWithSourceMap { generate(self.scope_for(node), move |&scope| self.scopes.scopes[scope].parent) } - pub fn scope_for_offset(&self, offset: TextUnit) -> Option { + pub(crate) fn scope_for_offset(&self, offset: TextUnit) -> Option { self.scopes .scope_for .iter() @@ -211,7 +211,7 @@ impl ScopesWithSourceMap { .collect() } - pub fn scope_for(&self, node: &SyntaxNode) -> Option { + pub(crate) fn scope_for(&self, node: &SyntaxNode) -> Option { node.ancestors() .map(SyntaxNodePtr::new) .filter_map(|ptr| self.source_map.syntax_expr(ptr)) From f4a94e74bcd6c8f9275a57a775e64314af1878da Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 10:49:01 +0300 Subject: [PATCH 26/33] fold ScopeWithSyntax into SourceAnalyzer --- .../ra_assists/src/inline_local_variable.rs | 2 +- crates/ra_hir/src/code_model_api.rs | 14 +- crates/ra_hir/src/expr.rs | 4 +- crates/ra_hir/src/expr/scope.rs | 166 +++--------------- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/source_binder.rs | 162 +++++++++++++---- crates/ra_ide_api/src/references.rs | 2 +- 7 files changed, 159 insertions(+), 193 deletions(-) diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs index 9493acec94d..e0479ef1307 100644 --- a/crates/ra_assists/src/inline_local_variable.rs +++ b/crates/ra_assists/src/inline_local_variable.rs @@ -27,7 +27,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx) -> Opt let_stmt.syntax().range() }; let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None); - let refs = analyzer.find_all_refs(bind_pat)?; + let refs = analyzer.find_all_refs(bind_pat); let mut wrap_in_parens = vec![true; refs.len()]; diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 5179f719db9..882208ec1f0 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -4,7 +4,7 @@ use ra_db::{CrateId, SourceRootId, Edition}; use ra_syntax::{ast::self, TreeArc}; use crate::{ - Name, ScopesWithSourceMap, Ty, HirFileId, Either, + Name, Ty, HirFileId, Either, HirDatabase, DefDatabase, type_ref::TypeRef, nameres::{ModuleScope, Namespace, ImportId, CrateModuleId}, @@ -466,12 +466,6 @@ impl DefWithBody { 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)] @@ -535,12 +529,6 @@ impl Function { db.type_for_def((*self).into(), Namespace::Values) } - pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSourceMap { - let scopes = db.expr_scopes((*self).into()); - let source_map = db.body_with_source_map((*self).into()).1; - ScopesWithSourceMap { scopes, source_map } - } - pub fn signature(&self, db: &impl HirDatabase) -> Arc { db.fn_signature(*self) } diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 3806a360595..038a25a9717 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -16,7 +16,7 @@ use crate::{ }; use crate::{ path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; -pub use self::scope::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}; +pub use self::scope::{ExprScopes, ScopeEntryWithSyntax}; pub(crate) mod scope; @@ -93,7 +93,7 @@ pub fn resolver_for_scope( ) -> Resolver { let mut r = body.owner.resolver(db); let scopes = db.expr_scopes(body.owner); - let scope_chain = scopes.scope_chain_for(scope_id).collect::>(); + let scope_chain = scopes.scope_chain(scope_id).collect::>(); for scope in scope_chain.into_iter().rev() { r = r.push_expr_scope(Arc::clone(&scopes), scope); } diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index dcec51a10ee..476385a2fc7 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -1,17 +1,16 @@ use std::sync::Arc; -use rustc_hash::{FxHashMap, FxHashSet}; - +use rustc_hash::{FxHashMap}; use ra_syntax::{ - AstNode, SyntaxNode, TextUnit, TextRange, SyntaxNodePtr, AstPtr, + TextRange, AstPtr, algo::generate, ast, }; use ra_arena::{Arena, RawId, impl_arena_id}; use crate::{ - Name, AsName,DefWithBody, Either, - expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySourceMap}, + Name, DefWithBody, Either, + expr::{PatId, ExprId, Pat, Expr, Body, Statement}, HirDatabase, }; @@ -23,7 +22,7 @@ impl_arena_id!(ScopeId); pub struct ExprScopes { body: Arc, scopes: Arena, - scope_for: FxHashMap, + pub(crate) scope_for: FxHashMap, } #[derive(Debug, PartialEq, Eq)] @@ -66,10 +65,7 @@ impl ExprScopes { &self.scopes[scope].entries } - pub fn scope_chain_for<'a>( - &'a self, - scope: Option, - ) -> impl Iterator + 'a { + pub fn scope_chain<'a>(&'a self, scope: Option) -> impl Iterator + 'a { generate(scope, move |&scope| self.scopes[scope].parent) } @@ -107,16 +103,10 @@ impl ExprScopes { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ScopesWithSourceMap { - pub(crate) source_map: Arc, - pub(crate) scopes: Arc, -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct ScopeEntryWithSyntax { - name: Name, - ptr: Either, AstPtr>, + pub(crate) name: Name, + pub(crate) ptr: Either, AstPtr>, } impl ScopeEntryWithSyntax { @@ -129,96 +119,6 @@ impl ScopeEntryWithSyntax { } } -impl ScopesWithSourceMap { - fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator + 'a { - generate(self.scope_for(node), move |&scope| self.scopes.scopes[scope].parent) - } - - pub(crate) fn scope_for_offset(&self, offset: TextUnit) -> Option { - self.scopes - .scope_for - .iter() - .filter_map(|(id, scope)| Some((self.source_map.expr_syntax(*id)?, scope))) - // find containing scope - .min_by_key(|(ptr, _scope)| { - (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len()) - }) - .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)) - } - - // XXX: during completion, cursor might be outside of any particular - // expression. Try to figure out the correct scope... - // FIXME: move this to source binder? - fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId { - let r = ptr.range(); - let child_scopes = self - .scopes - .scope_for - .iter() - .filter_map(|(id, scope)| Some((self.source_map.expr_syntax(*id)?, scope))) - .map(|(ptr, scope)| (ptr.range(), scope)) - .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); - - child_scopes - .max_by(|(r1, _), (r2, _)| { - if r2.is_subrange(&r1) { - std::cmp::Ordering::Greater - } else if r1.is_subrange(&r2) { - std::cmp::Ordering::Less - } else { - r1.start().cmp(&r2.start()) - } - }) - .map(|(_ptr, scope)| *scope) - .unwrap_or(original_scope) - } - - pub(crate) fn resolve_local_name( - &self, - name_ref: &ast::NameRef, - ) -> Option { - let mut shadowed = FxHashSet::default(); - let name = name_ref.as_name(); - let ret = self - .scope_chain(name_ref.syntax()) - .flat_map(|scope| self.scopes.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .filter(|entry| entry.name() == &name) - .nth(0); - ret.and_then(|entry| { - Some(ScopeEntryWithSyntax { - name: entry.name().clone(), - ptr: self.source_map.pat_syntax(entry.pat())?, - }) - }) - } - - pub(crate) fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { - let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); - let ptr = Either::A(AstPtr::new(pat.into())); - fn_def - .syntax() - .descendants() - .filter_map(ast::NameRef::cast) - .filter(|name_ref| match self.resolve_local_name(*name_ref) { - None => false, - Some(entry) => entry.ptr() == ptr, - }) - .map(|name_ref| ReferenceDescriptor { - name: name_ref.syntax().text().to_string(), - range: name_ref.syntax().range(), - }) - .collect() - } - - pub(crate) fn scope_for(&self, node: &SyntaxNode) -> Option { - node.ancestors() - .map(SyntaxNodePtr::new) - .filter_map(|ptr| self.source_map.syntax_expr(ptr)) - .find_map(|it| self.scopes.scope_for(it)) - } -} - impl ScopeEntry { pub fn name(&self) -> &Name { &self.name @@ -297,12 +197,11 @@ pub struct ReferenceDescriptor { #[cfg(test)] mod tests { - use ra_db::salsa::InternKey; - use ra_syntax::{SourceFile, algo::find_node_at_offset}; + use ra_db::SourceDatabase; + use ra_syntax::{algo::find_node_at_offset, AstNode, SyntaxNodePtr}; use test_utils::{extract_offset, assert_eq_text}; - use crate::Function; - use crate::expr::{ExprCollector}; + use crate::{source_binder::SourceAnalyzer, mock::MockDatabase}; use super::*; @@ -316,18 +215,20 @@ mod tests { buf.push_str(&code[off..]); buf }; - let file = SourceFile::parse(&code); + + let (db, _source_root, file_id) = MockDatabase::with_single_file(&code); + let file = db.parse(file_id); 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_intern_id(0u32.into()) }; - 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) }; + let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None); + + let scopes = analyzer.scopes(); + let expr_id = + analyzer.body_source_map().syntax_expr(SyntaxNodePtr::new(marker.syntax())).unwrap(); + let scope = scopes.scope_for(expr_id); + let actual = scopes - .scope_chain(marker.syntax()) - .flat_map(|scope| scopes.scopes.entries(scope)) + .scope_chain(scope) + .flat_map(|scope| scopes.entries(scope)) .map(|it| it.name().to_string()) .collect::>() .join("\n"); @@ -410,28 +311,17 @@ 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); + + let (db, _source_root, file_id) = MockDatabase::with_single_file(&code); + let file = db.parse(file_id); let expected_name = find_node_at_offset::(file.syntax(), expected_offset.into()) .expect("failed to find a name at the target offset"); - - let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); + let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None); - let irrelevant_function = - Function { id: crate::ids::FunctionId::from_intern_id(0u32.into()) }; - 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) }; - let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); + let local_name_entry = analyzer.resolve_local_name(name_ref).unwrap(); let local_name = local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()); assert_eq!(local_name.range(), expected_name.syntax().range()); diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 3ca810a8bce..eb2aa0e6c14 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -64,7 +64,7 @@ pub use self::{ impl_block::{ImplBlock, ImplItem}, docs::{Docs, Documentation}, adt::AdtDef, - expr::{ExprScopes, ScopesWithSourceMap, ScopeEntryWithSyntax}, + expr::{ExprScopes, ScopeEntryWithSyntax}, resolve::{Resolver, Resolution}, source_binder::{SourceAnalyzer, PathResolution}, }; diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 1c9e9320d59..d87f8ff34a9 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -7,9 +7,10 @@ /// purely for "IDE needs". use std::sync::Arc; +use rustc_hash::FxHashSet; use ra_db::{FileId, FilePosition}; use ra_syntax::{ - SyntaxNode, AstPtr, TextUnit, + SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, ast::{self, AstNode, NameOwner}, algo::find_node_at_offset, SyntaxKind::*, @@ -18,7 +19,7 @@ use ra_syntax::{ use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, AsName, Module, HirFileId, Crate, Trait, Resolver, - expr::scope::{ReferenceDescriptor, ScopeEntryWithSyntax}, + expr::{BodySourceMap, scope::{ReferenceDescriptor, ScopeEntryWithSyntax, ScopeId, ExprScopes}}, ids::LocationCtx, expr, AstId }; @@ -120,29 +121,6 @@ pub fn trait_from_module( Trait { id: ctx.to_def(trait_def) } } -fn resolver_for_node( - db: &impl HirDatabase, - file_id: FileId, - node: &SyntaxNode, - offset: Option, -) -> Resolver { - node.ancestors() - .find_map(|node| { - if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { - let def = def_with_body_from_child_node(db, file_id, node)?; - let scopes = def.scopes(db); - let scope = match offset { - None => scopes.scope_for(&node), - Some(offset) => scopes.scope_for_offset(offset), - }; - Some(expr::resolver_for_scope(def.body(db), db, scope)) - } else { - try_get_resolver_for_node(db, file_id, node) - } - }) - .unwrap_or_default() -} - fn try_get_resolver_for_node( db: &impl HirDatabase, file_id: FileId, @@ -192,9 +170,9 @@ fn def_with_body_from_child_node( #[derive(Debug)] pub struct SourceAnalyzer { resolver: Resolver, - body_source_map: Option>, + body_source_map: Option>, infer: Option>, - scopes: Option, + scopes: Option>, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -217,11 +195,30 @@ impl SourceAnalyzer { offset: Option, ) -> SourceAnalyzer { let def_with_body = def_with_body_from_child_node(db, file_id, node); - SourceAnalyzer { - resolver: resolver_for_node(db, file_id, node, offset), - body_source_map: def_with_body.map(|it| it.body_source_map(db)), - infer: def_with_body.map(|it| it.infer(db)), - scopes: def_with_body.map(|it| it.scopes(db)), + if let Some(def) = def_with_body { + let source_map = def.body_source_map(db); + let scopes = db.expr_scopes(def); + let scope = match offset { + None => scope_for(&scopes, &source_map, &node), + Some(offset) => scope_for_offset(&scopes, &source_map, offset), + }; + let resolver = expr::resolver_for_scope(def.body(db), db, scope); + SourceAnalyzer { + resolver, + body_source_map: Some(source_map), + infer: Some(def.infer(db)), + scopes: Some(scopes), + } + } else { + SourceAnalyzer { + resolver: node + .ancestors() + .find_map(|node| try_get_resolver_for_node(db, file_id, node)) + .unwrap_or_default(), + body_source_map: None, + infer: None, + scopes: None, + } } } @@ -276,16 +273,46 @@ impl SourceAnalyzer { Some(res) } - pub fn find_all_refs(&self, pat: &ast::BindPat) -> Option> { - self.scopes.as_ref().map(|it| it.find_all_refs(pat)) + pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { + let mut shadowed = FxHashSet::default(); + let name = name_ref.as_name(); + let source_map = self.body_source_map.as_ref()?; + let scopes = self.scopes.as_ref()?; + let scope = scope_for(scopes, source_map, name_ref.syntax()); + let ret = scopes + .scope_chain(scope) + .flat_map(|scope| scopes.entries(scope).iter()) + .filter(|entry| shadowed.insert(entry.name())) + .filter(|entry| entry.name() == &name) + .nth(0); + ret.and_then(|entry| { + Some(ScopeEntryWithSyntax { + name: entry.name().clone(), + ptr: source_map.pat_syntax(entry.pat())?, + }) + }) } - pub fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { - self.scopes.as_ref()?.resolve_local_name(name_ref) + pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { + let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); + let ptr = Either::A(AstPtr::new(pat.into())); + fn_def + .syntax() + .descendants() + .filter_map(ast::NameRef::cast) + .filter(|name_ref| match self.resolve_local_name(*name_ref) { + None => false, + Some(entry) => entry.ptr() == ptr, + }) + .map(|name_ref| ReferenceDescriptor { + name: name_ref.syntax().text().to_string(), + range: name_ref.syntax().range(), + }) + .collect() } #[cfg(test)] - pub(crate) fn body_source_map(&self) -> Arc { + pub(crate) fn body_source_map(&self) -> Arc { self.body_source_map.clone().unwrap() } @@ -293,4 +320,65 @@ impl SourceAnalyzer { pub(crate) fn inference_result(&self) -> Arc { self.infer.clone().unwrap() } + + #[cfg(test)] + pub(crate) fn scopes(&self) -> Arc { + self.scopes.clone().unwrap() + } +} + +fn scope_for( + scopes: &ExprScopes, + source_map: &BodySourceMap, + node: &SyntaxNode, +) -> Option { + node.ancestors() + .map(SyntaxNodePtr::new) + .filter_map(|ptr| source_map.syntax_expr(ptr)) + .find_map(|it| scopes.scope_for(it)) +} + +fn scope_for_offset( + scopes: &ExprScopes, + source_map: &BodySourceMap, + offset: TextUnit, +) -> Option { + scopes + .scope_for + .iter() + .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope))) + // find containing scope + .min_by_key(|(ptr, _scope)| { + (!(ptr.range().start() <= offset && offset <= ptr.range().end()), ptr.range().len()) + }) + .map(|(ptr, scope)| adjust(scopes, source_map, ptr, offset).unwrap_or(*scope)) +} + +// XXX: during completion, cursor might be outside of any particular +// expression. Try to figure out the correct scope... +fn adjust( + scopes: &ExprScopes, + source_map: &BodySourceMap, + ptr: SyntaxNodePtr, + offset: TextUnit, +) -> Option { + let r = ptr.range(); + let child_scopes = scopes + .scope_for + .iter() + .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope))) + .map(|(ptr, scope)| (ptr.range(), scope)) + .filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r); + + child_scopes + .max_by(|(r1, _), (r2, _)| { + if r2.is_subrange(&r1) { + std::cmp::Ordering::Greater + } else if r1.is_subrange(&r2) { + std::cmp::Ordering::Less + } else { + r1.start().cmp(&r2.start()) + } + }) + .map(|(_ptr, scope)| *scope) } diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index ee2c1d0f09e..9f655d83ce0 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -65,7 +65,7 @@ pub(crate) fn find_all_refs( let declaration = NavigationTarget::from_bind_pat(position.file_id, binding); let references = analyzer - .find_all_refs(binding)? + .find_all_refs(binding) .into_iter() .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) .collect::>(); From 62d01dd4dfc8feb52c006b84f9d1a1a7142cc060 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:00:15 +0300 Subject: [PATCH 27/33] hide resolver --- crates/ra_hir/src/source_binder.rs | 20 +++++++++++++------ .../src/completion/complete_path.rs | 2 +- .../src/completion/complete_pattern.rs | 2 +- .../src/completion/complete_scope.rs | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index d87f8ff34a9..b5e2f86becf 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -7,7 +7,7 @@ /// purely for "IDE needs". use std::sync::Arc; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashSet, FxHashMap}; use ra_db::{FileId, FilePosition}; use ra_syntax::{ SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, @@ -17,7 +17,7 @@ use ra_syntax::{ }; use crate::{ - HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, + HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, AsName, Module, HirFileId, Crate, Trait, Resolver, expr::{BodySourceMap, scope::{ReferenceDescriptor, ScopeEntryWithSyntax, ScopeId, ExprScopes}}, ids::LocationCtx, @@ -222,10 +222,6 @@ impl SourceAnalyzer { } } - pub fn resolver(&self) -> &Resolver { - &self.resolver - } - pub fn type_of(&self, _db: &impl HirDatabase, expr: &ast::Expr) -> Option { let expr_id = self.body_source_map.as_ref()?.node_expr(expr)?; Some(self.infer.as_ref()?[expr_id].clone()) @@ -246,6 +242,18 @@ impl SourceAnalyzer { self.infer.as_ref()?.field_resolution(expr_id) } + pub fn resolve_hir_path( + &self, + db: &impl HirDatabase, + path: &crate::Path, + ) -> PerNs { + self.resolver.resolve_path(db, path) + } + + pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { + self.resolver.all_names(db) + } + pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option { if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?; diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index e9bdf5af25f..bc03a70957a 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -9,7 +9,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { Some(path) => path.clone(), _ => return, }; - let def = match ctx.analyzer.resolver().resolve_path(ctx.db, &path).take_types() { + let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path).take_types() { Some(Resolution::Def(def)) => def, _ => return, }; diff --git a/crates/ra_ide_api/src/completion/complete_pattern.rs b/crates/ra_ide_api/src/completion/complete_pattern.rs index abbb8351886..0ef24868796 100644 --- a/crates/ra_ide_api/src/completion/complete_pattern.rs +++ b/crates/ra_ide_api/src/completion/complete_pattern.rs @@ -7,7 +7,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { } // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports - let names = ctx.analyzer.resolver().all_names(ctx.db); + let names = ctx.analyzer.all_names(ctx.db); for (name, res) in names.into_iter() { let r = res.as_ref(); let def = match r.take_types().or(r.take_values()) { diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 4c5d07ce5d3..fd256fc3b34 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -4,7 +4,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { return; } - let names = ctx.analyzer.resolver().all_names(ctx.db); + let names = ctx.analyzer.all_names(ctx.db); names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); } From a2cc76ce63c3449d33d4a2261392e452df1d31b1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:02:23 +0300 Subject: [PATCH 28/33] make resolver private --- crates/ra_hir/src/code_model_api.rs | 16 ++++++++-------- crates/ra_hir/src/expr.rs | 8 ++++++-- crates/ra_hir/src/impl_block.rs | 2 +- crates/ra_hir/src/lib.rs | 3 ++- crates/ra_hir/src/resolve.rs | 18 +++++------------- crates/ra_hir/src/ty/method_resolution.rs | 2 +- 6 files changed, 23 insertions(+), 26 deletions(-) diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 882208ec1f0..5d8cf57b6ca 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -189,7 +189,7 @@ impl Module { } } - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { let def_map = db.crate_def_map(self.krate); Resolver::default().push_module_scope(def_map, self.module_id) } @@ -313,7 +313,7 @@ impl Struct { // FIXME move to a more general type /// Builds a resolver for type references inside this struct. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { // take the outer scope... let r = self.module(db).resolver(db); // ...and add generic params, if present @@ -373,7 +373,7 @@ impl Enum { // FIXME: move to a more general type /// Builds a resolver for type references inside this struct. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { // take the outer scope... let r = self.module(db).resolver(db); // ...and add generic params, if present @@ -459,7 +459,7 @@ impl DefWithBody { } /// Builds a resolver for code inside this item. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { match *self { DefWithBody::Const(ref c) => c.resolver(db), DefWithBody::Function(ref f) => f.resolver(db), @@ -549,7 +549,7 @@ impl Function { // FIXME: move to a more general type for 'body-having' items /// Builds a resolver for code inside this item. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { // take the outer scope... let r = self .impl_block(db) @@ -602,7 +602,7 @@ impl Const { // FIXME: move to a more general type for 'body-having' items /// Builds a resolver for code inside this item. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { // take the outer scope... let r = self .impl_block(db) @@ -654,7 +654,7 @@ impl Static { } /// Builds a resolver for code inside this item. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { // take the outer scope... self.module(db).resolver(db) } @@ -736,7 +736,7 @@ impl TypeAlias { } /// Builds a resolver for the type references in this type alias. - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { // take the outer scope... let r = self .impl_block(db) diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 038a25a9717..23987479210 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -81,12 +81,16 @@ impl Body { } // needs arbitrary_self_types to be a method... or maybe move to the def? -pub fn resolver_for_expr(body: Arc, db: &impl HirDatabase, expr_id: ExprId) -> Resolver { +pub(crate) fn resolver_for_expr( + body: Arc, + db: &impl HirDatabase, + expr_id: ExprId, +) -> Resolver { let scopes = db.expr_scopes(body.owner); resolver_for_scope(body, db, scopes.scope_for(expr_id)) } -pub fn resolver_for_scope( +pub(crate) fn resolver_for_scope( body: Arc, db: &impl HirDatabase, scope_id: Option, diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 40d368cd9f6..b306874cc10 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -105,7 +105,7 @@ impl ImplBlock { db.generic_params((*self).into()) } - pub fn resolver(&self, db: &impl HirDatabase) -> Resolver { + pub(crate) fn resolver(&self, db: &impl HirDatabase) -> Resolver { let r = self.module().resolver(db); // add generic params, if present let p = self.generic_params(db); diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index eb2aa0e6c14..5eb2f32bd96 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -51,6 +51,7 @@ use crate::{ db::{HirDatabase, DefDatabase}, name::{AsName, KnownName}, source_id::{FileAstId, AstId}, + resolve::Resolver, }; pub use self::{ @@ -65,7 +66,7 @@ pub use self::{ docs::{Docs, Documentation}, adt::AdtDef, expr::{ExprScopes, ScopeEntryWithSyntax}, - resolve::{Resolver, Resolution}, + resolve::Resolution, source_binder::{SourceAnalyzer, PathResolution}, }; diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 2609585b1f1..685f4b8b160 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -9,13 +9,13 @@ use crate::{ name::{Name, KnownName}, nameres::{PerNs, CrateDefMap, CrateModuleId}, generics::GenericParams, - expr::{scope::{ExprScopes, ScopeId}, PatId, Body}, + expr::{scope::{ExprScopes, ScopeId}, PatId}, impl_block::ImplBlock, path::Path, Trait }; #[derive(Debug, Clone, Default)] -pub struct Resolver { +pub(crate) struct Resolver { scopes: Vec, } @@ -117,7 +117,7 @@ pub enum Resolution { } impl Resolver { - pub fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs { + pub(crate) fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs { let mut resolution = PerNs::none(); for scope in self.scopes.iter().rev() { resolution = resolution.or(scope.resolve_name(db, name)); @@ -154,12 +154,12 @@ impl Resolver { /// Returns the fully resolved path if we were able to resolve it. /// otherwise returns `PerNs::none` - pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs { + pub(crate) fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs { // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise self.resolve_path_segments(db, path).into_fully_resolved() } - pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { + pub(crate) fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { let mut names = FxHashMap::default(); for scope in self.scopes.iter().rev() { scope.collect_names(db, &mut |name, res| { @@ -197,14 +197,6 @@ impl Resolver { _ => None, }) } - - /// The body from which any `LocalBinding` resolutions in this resolver come. - pub fn body(&self) -> Option> { - self.scopes.iter().rev().find_map(|scope| match scope { - Scope::ExprScope(expr_scope) => Some(expr_scope.expr_scopes.body()), - _ => None, - }) - } } impl Resolver { diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 3ac8dc46b0d..bb23246a6af 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -135,7 +135,7 @@ fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option { impl Ty { /// Look up the method with the given name, returning the actual autoderefed /// receiver type (but without autoref applied yet). - pub fn lookup_method( + pub(crate) fn lookup_method( self, db: &impl HirDatabase, name: &Name, From 4f8dc1b9f0fd544b0565dc95bb3f14478aa467bf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:06:53 +0300 Subject: [PATCH 29/33] make expr scope stuff private --- crates/ra_hir/src/expr/scope.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 476385a2fc7..4ac797eb76e 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use rustc_hash::{FxHashMap}; use ra_syntax::{ TextRange, AstPtr, - algo::generate, ast, }; use ra_arena::{Arena, RawId, impl_arena_id}; @@ -26,13 +25,13 @@ pub struct ExprScopes { } #[derive(Debug, PartialEq, Eq)] -pub struct ScopeEntry { +pub(crate) struct ScopeEntry { name: Name, pat: PatId, } #[derive(Debug, PartialEq, Eq)] -pub struct ScopeData { +pub(crate) struct ScopeData { parent: Option, entries: Vec, } @@ -57,16 +56,15 @@ impl ExprScopes { scopes } - pub fn body(&self) -> Arc { - self.body.clone() - } - - pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { + pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { &self.scopes[scope].entries } - pub fn scope_chain<'a>(&'a self, scope: Option) -> impl Iterator + 'a { - generate(scope, move |&scope| self.scopes[scope].parent) + pub(crate) fn scope_chain<'a>( + &'a self, + scope: Option, + ) -> impl Iterator + 'a { + std::iter::successors(scope, move |&scope| self.scopes[scope].parent) } fn root_scope(&mut self) -> ScopeId { @@ -98,7 +96,7 @@ impl ExprScopes { self.scope_for.insert(node, scope); } - pub fn scope_for(&self, expr: ExprId) -> Option { + pub(crate) fn scope_for(&self, expr: ExprId) -> Option { self.scope_for.get(&expr).map(|&scope| scope) } } From d387bdccba2b5bb681e5b6c84cc1b0efe5c1a79a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:21:32 +0300 Subject: [PATCH 30/33] drop obsolete fixme --- crates/ra_hir/src/expr/scope.rs | 1 - crates/ra_hir/src/source_binder.rs | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 4ac797eb76e..6a6de577231 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -37,7 +37,6 @@ pub(crate) struct ScopeData { } impl ExprScopes { - // FIXME: This should take something more general than Function pub(crate) fn expr_scopes_query(db: &impl HirDatabase, def: DefWithBody) -> Arc { let body = db.body_hir(def); let res = ExprScopes::new(body); diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index b5e2f86becf..a50287339f7 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -250,10 +250,6 @@ impl SourceAnalyzer { self.resolver.resolve_path(db, path) } - pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { - self.resolver.all_names(db) - } - pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option { if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?; @@ -301,6 +297,10 @@ impl SourceAnalyzer { }) } + pub fn all_names(&self, db: &impl HirDatabase) -> FxHashMap> { + self.resolver.all_names(db) + } + pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); let ptr = Either::A(AstPtr::new(pat.into())); From f9e825d95624129b66c34f25904f6ae46b9d5760 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:24:09 +0300 Subject: [PATCH 31/33] move ScopeEntryWithSyntax --- crates/ra_hir/src/expr.rs | 4 +-- crates/ra_hir/src/expr/scope.rs | 45 +++++++++--------------------- crates/ra_hir/src/lib.rs | 4 +-- crates/ra_hir/src/source_binder.rs | 18 +++++++++++- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 23987479210..817e660f913 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -14,9 +14,9 @@ use crate::{ name::AsName, type_ref::{Mutability, TypeRef}, }; -use crate::{ path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; +use crate::{path::GenericArgs, ty::primitive::{IntTy, UncertainIntTy, FloatTy, UncertainFloatTy}}; -pub use self::scope::{ExprScopes, ScopeEntryWithSyntax}; +pub use self::scope::ExprScopes; pub(crate) mod scope; diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 6a6de577231..090343d1200 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -1,14 +1,11 @@ use std::sync::Arc; -use rustc_hash::{FxHashMap}; -use ra_syntax::{ - TextRange, AstPtr, - ast, -}; +use rustc_hash::FxHashMap; +use ra_syntax::TextRange; use ra_arena::{Arena, RawId, impl_arena_id}; use crate::{ - Name, DefWithBody, Either, + Name, DefWithBody, expr::{PatId, ExprId, Pat, Expr, Body, Statement}, HirDatabase, }; @@ -30,6 +27,16 @@ pub(crate) struct ScopeEntry { pat: PatId, } +impl ScopeEntry { + pub(crate) fn name(&self) -> &Name { + &self.name + } + + pub(crate) fn pat(&self) -> PatId { + self.pat + } +} + #[derive(Debug, PartialEq, Eq)] pub(crate) struct ScopeData { parent: Option, @@ -100,32 +107,6 @@ impl ExprScopes { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ScopeEntryWithSyntax { - pub(crate) name: Name, - pub(crate) ptr: Either, AstPtr>, -} - -impl ScopeEntryWithSyntax { - pub fn name(&self) -> &Name { - &self.name - } - - pub fn ptr(&self) -> Either, AstPtr> { - self.ptr - } -} - -impl ScopeEntry { - pub fn name(&self) -> &Name { - &self.name - } - - pub fn pat(&self) -> PatId { - self.pat - } -} - fn compute_block_scopes( statements: &[Statement], tail: Option, diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 5eb2f32bd96..a9db23060d3 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -65,9 +65,9 @@ pub use self::{ impl_block::{ImplBlock, ImplItem}, docs::{Docs, Documentation}, adt::AdtDef, - expr::{ExprScopes, ScopeEntryWithSyntax}, + expr::ExprScopes, resolve::Resolution, - source_binder::{SourceAnalyzer, PathResolution}, + source_binder::{SourceAnalyzer, PathResolution, ScopeEntryWithSyntax}, }; pub use self::code_model_api::{ diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a50287339f7..34a00a3007c 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -19,7 +19,7 @@ use ra_syntax::{ use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, AsName, Module, HirFileId, Crate, Trait, Resolver, - expr::{BodySourceMap, scope::{ReferenceDescriptor, ScopeEntryWithSyntax, ScopeId, ExprScopes}}, + expr::{BodySourceMap, scope::{ReferenceDescriptor, ScopeId, ExprScopes}}, ids::LocationCtx, expr, AstId }; @@ -187,6 +187,22 @@ pub enum PathResolution { AssocItem(crate::ImplItem), } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ScopeEntryWithSyntax { + pub(crate) name: Name, + pub(crate) ptr: Either, AstPtr>, +} + +impl ScopeEntryWithSyntax { + pub fn name(&self) -> &Name { + &self.name + } + + pub fn ptr(&self) -> Either, AstPtr> { + self.ptr + } +} + impl SourceAnalyzer { pub fn new( db: &impl HirDatabase, From b260641e0caa3938151afe66fa3bf5691b8c3caa Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:29:47 +0300 Subject: [PATCH 32/33] slight encapsulation --- crates/ra_hir/src/expr/scope.rs | 18 +++++++++++------- crates/ra_hir/src/source_binder.rs | 4 ++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 090343d1200..7f53f23aa93 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -18,7 +18,7 @@ impl_arena_id!(ScopeId); pub struct ExprScopes { body: Arc, scopes: Arena, - pub(crate) scope_for: FxHashMap, + scope_by_expr: FxHashMap, } #[derive(Debug, PartialEq, Eq)] @@ -54,7 +54,7 @@ impl ExprScopes { let mut scopes = ExprScopes { body: body.clone(), scopes: Arena::default(), - scope_for: FxHashMap::default(), + scope_by_expr: FxHashMap::default(), }; let root = scopes.root_scope(); scopes.add_params_bindings(root, body.params()); @@ -73,6 +73,14 @@ impl ExprScopes { std::iter::successors(scope, move |&scope| self.scopes[scope].parent) } + pub(crate) fn scope_for(&self, expr: ExprId) -> Option { + self.scope_by_expr.get(&expr).map(|&scope| scope) + } + + pub(crate) fn scope_by_expr(&self) -> &FxHashMap { + &self.scope_by_expr + } + fn root_scope(&mut self) -> ScopeId { self.scopes.alloc(ScopeData { parent: None, entries: vec![] }) } @@ -99,11 +107,7 @@ impl ExprScopes { } fn set_scope(&mut self, node: ExprId, scope: ScopeId) { - self.scope_for.insert(node, scope); - } - - pub(crate) fn scope_for(&self, expr: ExprId) -> Option { - self.scope_for.get(&expr).map(|&scope| scope) + self.scope_by_expr.insert(node, scope); } } diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 34a00a3007c..8d53079c69a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -368,7 +368,7 @@ fn scope_for_offset( offset: TextUnit, ) -> Option { scopes - .scope_for + .scope_by_expr() .iter() .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope))) // find containing scope @@ -388,7 +388,7 @@ fn adjust( ) -> Option { let r = ptr.range(); let child_scopes = scopes - .scope_for + .scope_by_expr() .iter() .filter_map(|(id, scope)| Some((source_map.expr_syntax(*id)?, scope))) .map(|(ptr, scope)| (ptr.range(), scope)) From 2facb5e061971afbf6bd2fabe3966d5de9d46489 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Apr 2019 11:32:58 +0300 Subject: [PATCH 33/33] cleanups --- crates/ra_hir/src/expr/scope.rs | 11 +---------- crates/ra_hir/src/source_binder.rs | 14 +++++++++++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 7f53f23aa93..58f365128b9 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use rustc_hash::FxHashMap; -use ra_syntax::TextRange; use ra_arena::{Arena, RawId, impl_arena_id}; use crate::{ @@ -171,22 +170,14 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope }; } -#[derive(Debug)] -pub struct ReferenceDescriptor { - pub range: TextRange, - pub name: String, -} - #[cfg(test)] mod tests { use ra_db::SourceDatabase; - use ra_syntax::{algo::find_node_at_offset, AstNode, SyntaxNodePtr}; + use ra_syntax::{algo::find_node_at_offset, AstNode, SyntaxNodePtr, ast}; use test_utils::{extract_offset, assert_eq_text}; use crate::{source_binder::SourceAnalyzer, mock::MockDatabase}; - use super::*; - fn do_check(code: &str, expected: &[&str]) { let (off, code) = extract_offset(code); let code = { diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 8d53079c69a..bd035ced95d 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use rustc_hash::{FxHashSet, FxHashMap}; use ra_db::{FileId, FilePosition}; use ra_syntax::{ - SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, + SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, TextRange, ast::{self, AstNode, NameOwner}, algo::find_node_at_offset, SyntaxKind::*, @@ -19,7 +19,7 @@ use ra_syntax::{ use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, AsName, Module, HirFileId, Crate, Trait, Resolver, - expr::{BodySourceMap, scope::{ReferenceDescriptor, ScopeId, ExprScopes}}, + expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, ids::LocationCtx, expr, AstId }; @@ -203,6 +203,12 @@ impl ScopeEntryWithSyntax { } } +#[derive(Debug)] +pub struct ReferenceDescriptor { + pub range: TextRange, + pub name: String, +} + impl SourceAnalyzer { pub fn new( db: &impl HirDatabase, @@ -318,6 +324,8 @@ impl SourceAnalyzer { } pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { + // FIXME: at least, this should work with any DefWithBody, but ideally + // this should be hir-based altogether let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); let ptr = Either::A(AstPtr::new(pat.into())); fn_def @@ -329,7 +337,7 @@ impl SourceAnalyzer { Some(entry) => entry.ptr() == ptr, }) .map(|name_ref| ReferenceDescriptor { - name: name_ref.syntax().text().to_string(), + name: name_ref.text().to_string(), range: name_ref.syntax().range(), }) .collect()