From c3a4c4429de83450654795534e64e878a774a088 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 18 Feb 2020 18:35:10 +0100 Subject: Refactor primary IDE API This introduces the new type -- Semantics. Semantics maps SyntaxNodes to various semantic info, such as type, name resolution or macro expansions. To do so, Semantics maintains a HashMap which maps every node it saw to the file from which the node originated. This is enough to get all the necessary hir bits just from syntax. --- crates/ra_hir/src/lib.rs | 5 +- crates/ra_hir/src/semantics.rs | 335 +++++++++++++++++++++++++++++++++++ crates/ra_hir/src/source_analyzer.rs | 194 ++++++++------------ crates/ra_hir/src/source_binder.rs | 161 ++++++++--------- 4 files changed, 480 insertions(+), 215 deletions(-) create mode 100644 crates/ra_hir/src/semantics.rs (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7a9745ebe..004a2185f 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -26,6 +26,7 @@ macro_rules! impl_froms { } } +mod semantics; pub mod db; pub mod source_analyzer; pub mod source_binder; @@ -45,8 +46,8 @@ pub use crate::{ StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, }, has_source::HasSource, - source_analyzer::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, - source_binder::SourceBinder, + semantics::{original_range, Semantics, SemanticsScope}, + source_analyzer::{PathResolution, ScopeEntryWithSyntax}, }; pub use hir_def::{ diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs new file mode 100644 index 000000000..22a7e7588 --- /dev/null +++ b/crates/ra_hir/src/semantics.rs @@ -0,0 +1,335 @@ +//! See `Semantics`. + +use std::{cell::RefCell, fmt, iter::successors}; + +use hir_def::{ + resolver::{self, HasResolver, Resolver}, + TraitId, +}; +use ra_db::{FileId, FileRange}; +use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextUnit}; +use rustc_hash::{FxHashMap, FxHashSet}; + +use crate::{ + db::HirDatabase, + source_analyzer::{resolve_hir_path, ReferenceDescriptor, SourceAnalyzer}, + source_binder::{ChildContainer, SourceBinder, ToDef}, + Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution, + ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, +}; +use ra_prof::profile; + +/// Primary API to get semantic information, like types, from syntax trees. +pub struct Semantics<'db, DB> { + pub db: &'db DB, + pub(crate) sb: RefCell, + cache: RefCell>, +} + +impl fmt::Debug for Semantics<'_, DB> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Semantics {{ ... }}") + } +} + +impl<'db, DB: HirDatabase> Semantics<'db, DB> { + pub fn new(db: &DB) -> Semantics { + let sb = RefCell::new(SourceBinder::new()); + Semantics { db, sb, cache: RefCell::default() } + } + + pub fn parse(&self, file_id: FileId) -> ast::SourceFile { + let tree = self.db.parse(file_id).tree(); + self.cache(tree.syntax().clone(), file_id.into()); + tree + } + + pub fn expand(&self, macro_call: &ast::MacroCall) -> Option { + let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); + let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); + let file_id = sa.expand(self.db, macro_call)?; + let node = self.db.parse_or_expand(file_id)?; + self.cache(node.clone(), file_id); + Some(node) + } + + pub fn descend_into_macros(&self, token: SyntaxToken) -> SyntaxToken { + let parent = token.parent(); + let parent = self.find_file(parent); + let sa = self.analyze2(parent.as_ref(), None); + + let token = successors(Some(parent.with_value(token)), |token| { + let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; + let tt = macro_call.token_tree()?; + if !token.value.text_range().is_subrange(&tt.syntax().text_range()) { + return None; + } + let file_id = sa.expand(self.db, token.with_value(¯o_call))?; + let token = file_id.expansion_info(self.db)?.map_token_down(token.as_ref())?; + + self.cache(find_root(&token.value.parent()), token.file_id); + + Some(token) + }) + .last() + .unwrap(); + + token.value + } + + pub fn original_range(&self, node: &SyntaxNode) -> FileRange { + let node = self.find_file(node.clone()); + original_range(self.db, node.as_ref()) + } + + pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator + '_ { + let node = self.find_file(node); + node.ancestors_with_macros(self.db).map(|it| it.value) + } + + pub fn type_of_expr(&self, expr: &ast::Expr) -> Option { + self.analyze(expr.syntax()).type_of(self.db, &expr) + } + + pub fn type_of_pat(&self, pat: &ast::Pat) -> Option { + self.analyze(pat.syntax()).type_of_pat(self.db, &pat) + } + + pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { + self.analyze(call.syntax()).resolve_method_call(call) + } + + pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option { + self.analyze(field.syntax()).resolve_field(field) + } + + pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option { + self.analyze(field.syntax()).resolve_record_field(field) + } + + pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option { + self.analyze(record_lit.syntax()).resolve_record_literal(record_lit) + } + + pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option { + self.analyze(record_pat.syntax()).resolve_record_pattern(record_pat) + } + + pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option { + let sa = self.analyze(macro_call.syntax()); + let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call); + sa.resolve_macro_call(self.db, macro_call) + } + + pub fn resolve_path(&self, path: &ast::Path) -> Option { + self.analyze(path.syntax()).resolve_path(self.db, path) + } + + // FIXME: use this instead? + // pub fn resolve_name_ref(&self, name_ref: &ast::NameRef) -> Option; + + pub fn to_def(&self, src: &T) -> Option { + let src = self.find_file(src.syntax().clone()).with_value(src.clone()); + let mut sb = self.sb.borrow_mut(); + T::to_def(self.db, &mut sb, src) + } + + pub fn to_module_def(&self, file: FileId) -> Option { + let mut sb = self.sb.borrow_mut(); + sb.to_module_def(self.db, file) + } + + pub fn scope(&self, node: &SyntaxNode) -> SemanticsScope<'db, DB> { + let node = self.find_file(node.clone()); + let resolver = self.analyze2(node.as_ref(), None).resolver; + SemanticsScope { db: self.db, resolver } + } + + pub fn scope_at_offset(&self, node: &SyntaxNode, offset: TextUnit) -> SemanticsScope<'db, DB> { + let node = self.find_file(node.clone()); + let resolver = self.analyze2(node.as_ref(), Some(offset)).resolver; + SemanticsScope { db: self.db, resolver } + } + + pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db, DB> { + let resolver = def.id.resolver(self.db); + SemanticsScope { db: self.db, resolver } + } + + // FIXME: we only use this in `inline_local_variable` assist, ideally, we + // should switch to general reference search infra there. + pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { + self.analyze(pat.syntax()).find_all_refs(pat) + } + + fn analyze(&self, node: &SyntaxNode) -> SourceAnalyzer { + let src = self.find_file(node.clone()); + self.analyze2(src.as_ref(), None) + } + + fn analyze2(&self, src: InFile<&SyntaxNode>, offset: Option) -> SourceAnalyzer { + let _p = profile("Semantics::analyze2"); + + let container = match self.sb.borrow_mut().find_container(self.db, src) { + Some(it) => it, + None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), + }; + + let resolver = match container { + ChildContainer::DefWithBodyId(def) => { + return SourceAnalyzer::new_for_body(self.db, def, src, offset) + } + ChildContainer::TraitId(it) => it.resolver(self.db), + ChildContainer::ImplId(it) => it.resolver(self.db), + ChildContainer::ModuleId(it) => it.resolver(self.db), + ChildContainer::EnumId(it) => it.resolver(self.db), + ChildContainer::VariantId(it) => it.resolver(self.db), + ChildContainer::GenericDefId(it) => it.resolver(self.db), + }; + SourceAnalyzer::new_for_resolver(resolver, src) + } + + fn cache(&self, root_node: SyntaxNode, file_id: HirFileId) { + assert!(root_node.parent().is_none()); + let mut cache = self.cache.borrow_mut(); + let prev = cache.insert(root_node, file_id); + assert!(prev == None || prev == Some(file_id)) + } + + pub fn assert_contains_node(&self, node: &SyntaxNode) { + self.find_file(node.clone()); + } + + fn lookup(&self, root_node: &SyntaxNode) -> Option { + let cache = self.cache.borrow(); + cache.get(root_node).copied() + } + + fn find_file(&self, node: SyntaxNode) -> InFile { + let root_node = find_root(&node); + let file_id = self.lookup(&root_node).unwrap_or_else(|| { + panic!( + "\n\nFailed to lookup {:?} in this Semantics.\n\ + Make sure to use only query nodes, derived from this instance of Semantics.\n\ + root node: {:?}\n\ + known nodes: {}\n\n", + node, + root_node, + self.cache + .borrow() + .keys() + .map(|it| format!("{:?}", it)) + .collect::>() + .join(", ") + ) + }); + InFile::new(file_id, node) + } +} + +fn find_root(node: &SyntaxNode) -> SyntaxNode { + node.ancestors().last().unwrap() +} + +pub struct SemanticsScope<'a, DB> { + pub db: &'a DB, + resolver: Resolver, +} + +impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { + pub fn module(&self) -> Option { + Some(Module { id: self.resolver.module()? }) + } + + /// Note: `FxHashSet` should be treated as an opaque type, passed into `Type + // FIXME: rename to visible_traits to not repeat scope? + pub fn traits_in_scope(&self) -> FxHashSet { + let resolver = &self.resolver; + resolver.traits_in_scope(self.db) + } + + pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) { + let resolver = &self.resolver; + + resolver.process_all_names(self.db, &mut |name, def| { + let def = match def { + resolver::ScopeDef::PerNs(it) => it.into(), + resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), + resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), + resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), + resolver::ScopeDef::Local(pat_id) => { + let parent = resolver.body_owner().unwrap().into(); + ScopeDef::Local(Local { parent, pat_id }) + } + }; + f(name, def) + }) + } + + pub fn resolve_hir_path(&self, path: &Path) -> Option { + resolve_hir_path(self.db, &self.resolver, path) + } +} + +// FIXME: Change `HasSource` trait to work with `Semantics` and remove this? +pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { + if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { + return range; + } + + if let Some(expansion) = node.file_id.expansion_info(db) { + if let Some(call_node) = expansion.call_node() { + return FileRange { + file_id: call_node.file_id.original_file(db), + range: call_node.value.text_range(), + }; + } + } + + FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } +} + +fn original_range_and_origin( + db: &impl HirDatabase, + node: InFile<&SyntaxNode>, +) -> Option<(FileRange, Origin)> { + let expansion = node.file_id.expansion_info(db)?; + + // the input node has only one token ? + let single = node.value.first_token()? == node.value.last_token()?; + + // FIXME: We should handle recurside macro expansions + let (range, origin) = node.value.descendants().find_map(|it| { + let first = it.first_token()?; + let last = it.last_token()?; + + if !single && first == last { + return None; + } + + // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens + let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; + let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; + + if first.file_id != last.file_id || first_origin != last_origin { + return None; + } + + // FIXME: Add union method in TextRange + Some(( + first.with_value(union_range(first.value.text_range(), last.value.text_range())), + first_origin, + )) + })?; + + return Some(( + FileRange { file_id: range.file_id.original_file(db), range: range.value }, + origin, + )); + + fn union_range(a: TextRange, b: TextRange) -> TextRange { + let start = a.start().min(b.start()); + let end = a.end().max(b.end()); + TextRange::from_to(start, end) + } +} diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index efa3f8a79..bff1ecd14 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -14,29 +14,27 @@ use hir_def::{ BodySourceMap, }, expr::{ExprId, PatId}, - resolver::{self, resolver_for_scope, Resolver, TypeNs, ValueNs}, - AsMacroCall, DefWithBodyId, TraitId, + resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, + AsMacroCall, DefWithBodyId, }; -use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile, MacroCallId}; +use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile}; use hir_ty::{InEnvironment, InferenceResult, TraitEnvironment}; use ra_syntax::{ ast::{self, AstNode}, - AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextUnit, + AstPtr, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit, }; -use rustc_hash::FxHashSet; use crate::{ - db::HirDatabase, Adt, Const, DefWithBody, EnumVariant, Function, Local, MacroDef, Name, Path, - ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, + db::HirDatabase, Adt, Const, EnumVariant, Function, Local, MacroDef, Name, Path, Static, + Struct, Trait, Type, TypeAlias, TypeParam, }; /// `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 { +pub(crate) struct SourceAnalyzer { file_id: HirFileId, - resolver: Resolver, - body_owner: Option, + pub(crate) resolver: Resolver, body_source_map: Option>, infer: Option>, scopes: Option>, @@ -77,35 +75,7 @@ pub struct ReferenceDescriptor { pub name: String, } -#[derive(Debug)] -pub struct Expansion { - macro_call_id: MacroCallId, -} - -impl Expansion { - pub fn map_token_down( - &self, - db: &impl HirDatabase, - token: InFile<&SyntaxToken>, - ) -> Option> { - let exp_info = self.file_id().expansion_info(db)?; - exp_info.map_token_down(token) - } - - pub fn file_id(&self) -> HirFileId { - self.macro_call_id.as_file() - } -} - impl SourceAnalyzer { - pub fn new( - db: &impl HirDatabase, - node: InFile<&SyntaxNode>, - offset: Option, - ) -> SourceAnalyzer { - crate::source_binder::SourceBinder::new(db).analyze(node, offset) - } - pub(crate) fn new_for_body( db: &impl HirDatabase, def: DefWithBodyId, @@ -121,7 +91,6 @@ impl SourceAnalyzer { let resolver = resolver_for_scope(db, def, scope); SourceAnalyzer { resolver, - body_owner: Some(def.into()), body_source_map: Some(source_map), infer: Some(db.infer(def)), scopes: Some(scopes), @@ -135,7 +104,6 @@ impl SourceAnalyzer { ) -> SourceAnalyzer { SourceAnalyzer { resolver, - body_owner: None, body_source_map: None, infer: None, scopes: None, @@ -143,10 +111,6 @@ impl SourceAnalyzer { } } - pub fn module(&self) -> Option { - Some(crate::code_model::Module { id: self.resolver.module()? }) - } - fn expr_id(&self, expr: &ast::Expr) -> Option { let src = InFile { file_id: self.file_id, value: expr }; self.body_source_map.as_ref()?.node_expr(src) @@ -180,7 +144,7 @@ impl SourceAnalyzer { TraitEnvironment::lower(db, &self.resolver) } - pub fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option { + pub(crate) fn type_of(&self, db: &impl HirDatabase, expr: &ast::Expr) -> Option { let expr_id = if let Some(expr) = self.expand_expr(db, InFile::new(self.file_id, expr)) { self.body_source_map.as_ref()?.node_expr(expr.as_ref())? } else { @@ -192,24 +156,27 @@ impl SourceAnalyzer { Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) } - pub fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option { + pub(crate) fn type_of_pat(&self, db: &impl HirDatabase, pat: &ast::Pat) -> Option { let pat_id = self.pat_id(pat)?; let ty = self.infer.as_ref()?[pat_id].clone(); let environment = self.trait_env(db); Some(Type { krate: self.resolver.krate()?, ty: InEnvironment { value: ty, environment } }) } - pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { + pub(crate) fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { let expr_id = self.expr_id(&call.clone().into())?; self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) } - pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option { + pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option { let expr_id = self.expr_id(&field.clone().into())?; self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) } - pub fn resolve_record_field(&self, field: &ast::RecordField) -> Option { + pub(crate) fn resolve_record_field( + &self, + field: &ast::RecordField, + ) -> Option { let expr_id = match field.expr() { Some(it) => self.expr_id(&it)?, None => { @@ -220,17 +187,23 @@ impl SourceAnalyzer { self.infer.as_ref()?.record_field_resolution(expr_id).map(|it| it.into()) } - pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option { + pub(crate) fn resolve_record_literal( + &self, + record_lit: &ast::RecordLit, + ) -> Option { let expr_id = self.expr_id(&record_lit.clone().into())?; self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) } - pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option { + pub(crate) fn resolve_record_pattern( + &self, + record_pat: &ast::RecordPat, + ) -> Option { let pat_id = self.pat_id(&record_pat.clone().into())?; self.infer.as_ref()?.variant_resolution_for_pat(pat_id).map(|it| it.into()) } - pub fn resolve_macro_call( + pub(crate) fn resolve_macro_call( &self, db: &impl HirDatabase, macro_call: InFile<&ast::MacroCall>, @@ -240,52 +213,11 @@ impl SourceAnalyzer { self.resolver.resolve_path_as_macro(db, path.mod_path()).map(|it| it.into()) } - pub fn resolve_hir_path( + pub(crate) fn resolve_path( &self, db: &impl HirDatabase, - path: &crate::Path, + path: &ast::Path, ) -> Option { - let types = - self.resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { - TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), - TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), - TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => { - PathResolution::Def(Adt::from(it).into()) - } - TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), - TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), - TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), - TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - }); - let values = - self.resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { - let res = match val { - ValueNs::LocalBinding(pat_id) => { - let var = Local { parent: self.body_owner?, pat_id }; - PathResolution::Local(var) - } - ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), - ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), - ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), - ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), - ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), - }; - Some(res) - }); - - let items = self - .resolver - .resolve_module_path_in_items(db, path.mod_path()) - .take_types() - .map(|it| PathResolution::Def(it.into())); - types.or(values).or(items).or_else(|| { - self.resolver - .resolve_path_as_macro(db, path.mod_path()) - .map(|def| PathResolution::Macro(def.into())) - }) - } - - 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.expr_id(&path_expr.into())?; if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { @@ -300,7 +232,7 @@ impl SourceAnalyzer { } // This must be a normal source file rather than macro file. let hir_path = crate::Path::from_ast(path.clone())?; - self.resolve_hir_path(db, &hir_path) + resolve_hir_path(db, &self.resolver, &hir_path) } fn resolve_local_name(&self, name_ref: &ast::NameRef) -> Option { @@ -315,25 +247,9 @@ impl SourceAnalyzer { }) } - pub fn process_all_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, ScopeDef)) { - self.resolver.process_all_names(db, &mut |name, def| { - let def = match def { - resolver::ScopeDef::PerNs(it) => it.into(), - resolver::ScopeDef::ImplSelfType(it) => ScopeDef::ImplSelfType(it.into()), - resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()), - resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(TypeParam { id }), - resolver::ScopeDef::Local(pat_id) => { - let parent = self.resolver.body_owner().unwrap().into(); - ScopeDef::Local(Local { parent, pat_id }) - } - }; - f(name, def) - }) - } - // FIXME: we only use this in `inline_local_variable` assist, ideally, we // should switch to general reference search infra there. - 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::Left(AstPtr::new(&ast::Pat::from(pat.clone()))); fn_def @@ -351,19 +267,14 @@ impl SourceAnalyzer { .collect() } - /// Note: `FxHashSet` should be treated as an opaque type, passed into `Type - pub fn traits_in_scope(&self, db: &impl HirDatabase) -> FxHashSet { - self.resolver.traits_in_scope(db) - } - - pub fn expand( + pub(crate) fn expand( &self, db: &impl HirDatabase, macro_call: InFile<&ast::MacroCall>, - ) -> Option { + ) -> Option { let macro_call_id = macro_call.as_call_id(db, |path| self.resolver.resolve_path_as_macro(db, &path))?; - Some(Expansion { macro_call_id }) + Some(macro_call_id.as_file()) } } @@ -409,6 +320,47 @@ fn scope_for_offset( }) } +pub(crate) fn resolve_hir_path( + db: &impl HirDatabase, + resolver: &Resolver, + path: &crate::Path, +) -> Option { + let types = resolver.resolve_path_in_type_ns_fully(db, path.mod_path()).map(|ty| match ty { + TypeNs::SelfType(it) => PathResolution::SelfType(it.into()), + TypeNs::GenericParam(id) => PathResolution::TypeParam(TypeParam { id }), + TypeNs::AdtSelfType(it) | TypeNs::AdtId(it) => PathResolution::Def(Adt::from(it).into()), + TypeNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), + TypeNs::BuiltinType(it) => PathResolution::Def(it.into()), + TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), + }); + let body_owner = resolver.body_owner(); + let values = resolver.resolve_path_in_value_ns_fully(db, path.mod_path()).and_then(|val| { + let res = match val { + ValueNs::LocalBinding(pat_id) => { + let var = Local { parent: body_owner?.into(), pat_id }; + PathResolution::Local(var) + } + ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()), + ValueNs::ConstId(it) => PathResolution::Def(Const::from(it).into()), + ValueNs::StaticId(it) => PathResolution::Def(Static::from(it).into()), + ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), + ValueNs::EnumVariantId(it) => PathResolution::Def(EnumVariant::from(it).into()), + }; + Some(res) + }); + + let items = resolver + .resolve_module_path_in_items(db, path.mod_path()) + .take_types() + .map(|it| PathResolution::Def(it.into())); + types.or(values).or(items).or_else(|| { + resolver + .resolve_path_as_macro(db, path.mod_path()) + .map(|def| PathResolution::Macro(def.into())) + }) +} + // XXX: during completion, cursor might be outside of any particular // expression. Try to figure out the correct scope... fn adjust( diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index f3150f578..0b8a641f9 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -5,112 +5,85 @@ use hir_def::{ child_by_source::ChildBySource, dyn_map::DynMap, keys::{self, Key}, - resolver::{HasResolver, Resolver}, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, ModuleId, StaticId, StructFieldId, StructId, TraitId, TypeAliasId, UnionId, VariantId, }; use hir_expand::{name::AsName, AstId, InFile, MacroDefId, MacroDefKind}; +use ra_db::FileId; use ra_prof::profile; use ra_syntax::{ ast::{self, NameOwner}, - match_ast, AstNode, SyntaxNode, TextUnit, + match_ast, AstNode, SyntaxNode, }; use rustc_hash::FxHashMap; -use crate::{db::HirDatabase, Local, Module, SourceAnalyzer, TypeParam}; -use ra_db::FileId; +use crate::{db::HirDatabase, Local, Module, TypeParam}; -pub struct SourceBinder<'a, DB> { - pub db: &'a DB, +pub struct SourceBinder { child_by_source_cache: FxHashMap, } -impl SourceBinder<'_, DB> { - pub fn new(db: &DB) -> SourceBinder { - SourceBinder { db, child_by_source_cache: FxHashMap::default() } - } - - pub fn analyze( - &mut self, - src: InFile<&SyntaxNode>, - offset: Option, - ) -> SourceAnalyzer { - let _p = profile("SourceBinder::analyzer"); - let container = match self.find_container(src) { - Some(it) => it, - None => return SourceAnalyzer::new_for_resolver(Resolver::default(), src), - }; - - let resolver = match container { - ChildContainer::DefWithBodyId(def) => { - return SourceAnalyzer::new_for_body(self.db, def, src, offset) - } - ChildContainer::TraitId(it) => it.resolver(self.db), - ChildContainer::ImplId(it) => it.resolver(self.db), - ChildContainer::ModuleId(it) => it.resolver(self.db), - ChildContainer::EnumId(it) => it.resolver(self.db), - ChildContainer::VariantId(it) => it.resolver(self.db), - ChildContainer::GenericDefId(it) => it.resolver(self.db), - }; - SourceAnalyzer::new_for_resolver(resolver, src) +impl SourceBinder { + pub(crate) fn new() -> SourceBinder { + SourceBinder { child_by_source_cache: FxHashMap::default() } } - pub fn to_def(&mut self, src: InFile) -> Option { - T::to_def(self, src) - } - - pub fn to_module_def(&mut self, file: FileId) -> Option { + pub(crate) fn to_module_def(&mut self, db: &impl HirDatabase, file: FileId) -> Option { let _p = profile("SourceBinder::to_module_def"); - let (krate, local_id) = self.db.relevant_crates(file).iter().find_map(|&crate_id| { - let crate_def_map = self.db.crate_def_map(crate_id); + let (krate, local_id) = db.relevant_crates(file).iter().find_map(|&crate_id| { + let crate_def_map = db.crate_def_map(crate_id); let local_id = crate_def_map.modules_for_file(file).next()?; Some((crate_id, local_id)) })?; Some(Module { id: ModuleId { krate, local_id } }) } - fn to_id(&mut self, src: InFile) -> Option { - T::to_id(self, src) + fn to_id(&mut self, db: &impl HirDatabase, src: InFile) -> Option { + T::to_id(db, self, src) } - fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option { - for container in src.cloned().ancestors_with_macros(self.db).skip(1) { + pub(crate) fn find_container( + &mut self, + db: &impl HirDatabase, + src: InFile<&SyntaxNode>, + ) -> Option { + for container in src.cloned().ancestors_with_macros(db).skip(1) { let res: ChildContainer = match_ast! { match (container.value) { ast::TraitDef(it) => { - let def: TraitId = self.to_id(container.with_value(it))?; + let def: TraitId = self.to_id(db, container.with_value(it))?; def.into() }, ast::ImplBlock(it) => { - let def: ImplId = self.to_id(container.with_value(it))?; + let def: ImplId = self.to_id(db, container.with_value(it))?; def.into() }, ast::FnDef(it) => { - let def: FunctionId = self.to_id(container.with_value(it))?; + let def: FunctionId = self.to_id(db, container.with_value(it))?; DefWithBodyId::from(def).into() }, ast::StaticDef(it) => { - let def: StaticId = self.to_id(container.with_value(it))?; + let def: StaticId = self.to_id(db, container.with_value(it))?; DefWithBodyId::from(def).into() }, ast::ConstDef(it) => { - let def: ConstId = self.to_id(container.with_value(it))?; + let def: ConstId = self.to_id(db, container.with_value(it))?; DefWithBodyId::from(def).into() }, ast::EnumDef(it) => { - let def: EnumId = self.to_id(container.with_value(it))?; + let def: EnumId = self.to_id(db, container.with_value(it))?; def.into() }, ast::StructDef(it) => { - let def: StructId = self.to_id(container.with_value(it))?; + let def: StructId = self.to_id(db, container.with_value(it))?; VariantId::from(def).into() }, ast::UnionDef(it) => { - let def: UnionId = self.to_id(container.with_value(it))?; + let def: UnionId = self.to_id(db, container.with_value(it))?; VariantId::from(def).into() }, ast::Module(it) => { - let def: ModuleId = self.to_id(container.with_value(it))?; + let def: ModuleId = self.to_id(db, container.with_value(it))?; def.into() }, _ => { continue }, @@ -119,12 +92,11 @@ impl SourceBinder<'_, DB> { return Some(res); } - let c = self.to_module_def(src.file_id.original_file(self.db))?; + let c = self.to_module_def(db, src.file_id.original_file(db))?; Some(c.id.into()) } - fn child_by_source(&mut self, container: ChildContainer) -> &DynMap { - let db = self.db; + fn child_by_source(&mut self, db: &impl HirDatabase, container: ChildContainer) -> &DynMap { self.child_by_source_cache.entry(container).or_insert_with(|| match container { ChildContainer::DefWithBodyId(it) => it.child_by_source(db), ChildContainer::ModuleId(it) => it.child_by_source(db), @@ -137,16 +109,20 @@ impl SourceBinder<'_, DB> { } } -pub trait ToId: Sized { +pub(crate) trait ToId: Sized { type ID: Sized + Copy + 'static; - fn to_id(sb: &mut SourceBinder<'_, DB>, src: InFile) - -> Option; + fn to_id( + db: &DB, + sb: &mut SourceBinder, + src: InFile, + ) -> Option; } pub trait ToDef: Sized + AstNode + 'static { type Def; fn to_def( - sb: &mut SourceBinder<'_, DB>, + db: &DB, + sb: &mut SourceBinder, src: InFile, ) -> Option; } @@ -155,9 +131,9 @@ macro_rules! to_def_impls { ($(($def:path, $ast:path)),* ,) => {$( impl ToDef for $ast { type Def = $def; - fn to_def(sb: &mut SourceBinder<'_, DB>, src: InFile) + fn to_def(db: &DB, sb: &mut SourceBinder, src: InFile) -> Option - { sb.to_id(src).map(Into::into) } + { sb.to_id(db, src).map(Into::into) } } )*} } @@ -179,7 +155,7 @@ to_def_impls![ ]; #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -enum ChildContainer { +pub(crate) enum ChildContainer { DefWithBodyId(DefWithBodyId), ModuleId(ModuleId), TraitId(TraitId), @@ -201,7 +177,7 @@ impl_froms! { GenericDefId } -pub trait ToIdByKey: Sized + AstNode + 'static { +pub(crate) trait ToIdByKey: Sized + AstNode + 'static { type ID: Sized + Copy + 'static; const KEY: Key; } @@ -209,11 +185,11 @@ pub trait ToIdByKey: Sized + AstNode + 'static { impl ToId for T { type ID = ::ID; fn to_id( - sb: &mut SourceBinder<'_, DB>, + db: &DB, + sb: &mut SourceBinder, src: InFile, ) -> Option { - let container = sb.find_container(src.as_ref().map(|it| it.syntax()))?; - let db = sb.db; + let container = sb.find_container(db, src.as_ref().map(|it| it.syntax()))?; let dyn_map = &*sb.child_by_source_cache.entry(container).or_insert_with(|| match container { ChildContainer::DefWithBodyId(it) => it.child_by_source(db), @@ -255,15 +231,15 @@ to_id_key_impls![ impl ToId for ast::MacroCall { type ID = MacroDefId; fn to_id( - sb: &mut SourceBinder<'_, DB>, + db: &DB, + sb: &mut SourceBinder, src: InFile, ) -> Option { let kind = MacroDefKind::Declarative; - let krate = sb.to_module_def(src.file_id.original_file(sb.db))?.id.krate; + let krate = sb.to_module_def(db, src.file_id.original_file(db))?.id.krate; - let ast_id = - Some(AstId::new(src.file_id, sb.db.ast_id_map(src.file_id).ast_id(&src.value))); + let ast_id = Some(AstId::new(src.file_id, db.ast_id_map(src.file_id).ast_id(&src.value))); Some(MacroDefId { krate: Some(krate), ast_id, kind }) } @@ -272,20 +248,20 @@ impl ToId for ast::MacroCall { impl ToDef for ast::BindPat { type Def = Local; - fn to_def(sb: &mut SourceBinder<'_, DB>, src: InFile) -> Option { + fn to_def(db: &DB, sb: &mut SourceBinder, src: InFile) -> Option { let file_id = src.file_id; let parent: DefWithBodyId = src.value.syntax().ancestors().find_map(|it| { let res = match_ast! { match it { - ast::ConstDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::StaticDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, + ast::ConstDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::StaticDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, _ => return None, } }; Some(res) })?; - let (_body, source_map) = sb.db.body_with_source_map(parent); + let (_body, source_map) = db.body_with_source_map(parent); let src = src.map(ast::Pat::from); let pat_id = source_map.node_pat(src.as_ref())?; Some(Local { parent: parent.into(), pat_id }) @@ -296,26 +272,26 @@ impl ToDef for ast::TypeParam { type Def = TypeParam; fn to_def( - sb: &mut SourceBinder<'_, DB>, + db: &DB, + sb: &mut SourceBinder, src: InFile, ) -> Option { - let mut sb = SourceBinder::new(sb.db); let file_id = src.file_id; let parent: GenericDefId = src.value.syntax().ancestors().find_map(|it| { let res = match_ast! { match it { - ast::FnDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::StructDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::EnumDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::TraitDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::TypeAliasDef(value) => { sb.to_id(InFile { value, file_id})?.into() }, - ast::ImplBlock(value) => { sb.to_id(InFile { value, file_id})?.into() }, + ast::FnDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::StructDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::EnumDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::TraitDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::TypeAliasDef(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, + ast::ImplBlock(value) => { sb.to_id(db, InFile { value, file_id})?.into() }, _ => return None, } }; Some(res) })?; - let &id = sb.child_by_source(parent.into())[keys::TYPE_PARAM].get(&src)?; + let &id = sb.child_by_source(db, parent.into())[keys::TYPE_PARAM].get(&src)?; Some(TypeParam { id }) } } @@ -324,7 +300,8 @@ impl ToId for ast::Module { type ID = ModuleId; fn to_id( - sb: &mut SourceBinder<'_, DB>, + db: &DB, + sb: &mut SourceBinder, src: InFile, ) -> Option { { @@ -333,7 +310,7 @@ impl ToId for ast::Module { .as_ref() .map(|it| it.syntax()) .cloned() - .ancestors_with_macros(sb.db) + .ancestors_with_macros(db) .skip(1) .find_map(|it| { let m = ast::Module::cast(it.value.clone())?; @@ -341,15 +318,15 @@ impl ToId for ast::Module { }); let parent_module = match parent_declaration { - Some(parent_declaration) => sb.to_id(parent_declaration)?, + Some(parent_declaration) => sb.to_id(db, parent_declaration)?, None => { - let file_id = src.file_id.original_file(sb.db); - sb.to_module_def(file_id)?.id + let file_id = src.file_id.original_file(db); + sb.to_module_def(db, file_id)?.id } }; let child_name = src.value.name()?.as_name(); - let def_map = sb.db.crate_def_map(parent_module.krate); + let def_map = db.crate_def_map(parent_module.krate); let child_id = *def_map[parent_module.local_id].children.get(&child_name)?; Some(ModuleId { krate: parent_module.krate, local_id: child_id }) } -- cgit v1.2.3