mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-04 15:45:25 +00:00
Merge #693
693: Name resolution refactoring r=matklad a=flodiebold This is still very WIP, but it's becoming quite big and I want to make sure this isn't going in a completely bad direction 😅. I'm not really happy with how the path resolution looks, and I'm not sure `PerNs<Resolution>` is the best return type -- there are 'this cannot happen in the (types/values) namespace' cases everywhere. I also want to unify the `resolver` and `nameres` namespaces once I'm done switching everything to `Resolver`. Also, `Resolver` only has a lifetime because it needs to have a reference to the `ItemMap` during import resolution 😕 The differences in the completion snapshots are almost completely just ordering (except it completes `Self` as well now), so I changed it to sort the completions before snapshotting. Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
4447019f4b
26 changed files with 910 additions and 425 deletions
|
@ -5,18 +5,19 @@ use ra_db::{CrateId, FileId};
|
||||||
use ra_syntax::{ast::self, TreeArc, SyntaxNode};
|
use ra_syntax::{ast::self, TreeArc, SyntaxNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Name, Path, PerNs, ScopesWithSyntaxMapping, Ty, HirFileId,
|
Name, ScopesWithSyntaxMapping, Ty, HirFileId,
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
nameres::{ModuleScope, lower::ImportId},
|
nameres::{ModuleScope, lower::ImportId},
|
||||||
HirDatabase, PersistentHirDatabase,
|
HirDatabase, PersistentHirDatabase,
|
||||||
expr::BodySyntaxMapping,
|
expr::{Body, BodySyntaxMapping},
|
||||||
ty::{InferenceResult},
|
ty::InferenceResult,
|
||||||
adt::{EnumVariantId, StructFieldId, VariantDef},
|
adt::{EnumVariantId, StructFieldId, VariantDef},
|
||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
docs::{Documentation, Docs, docs_from_ast},
|
docs::{Documentation, Docs, docs_from_ast},
|
||||||
module_tree::ModuleId,
|
module_tree::ModuleId,
|
||||||
ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
|
ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
|
||||||
impl_block::ImplId,
|
impl_block::ImplId,
|
||||||
|
resolve::Resolver,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// hir::Crate describes a single crate. It's the main interface with which
|
/// hir::Crate describes a single crate. It's the main interface with which
|
||||||
|
@ -174,13 +175,14 @@ impl Module {
|
||||||
db.item_map(self.krate)[self.module_id].clone()
|
db.item_map(self.krate)[self.module_id].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_path(&self, db: &impl PersistentHirDatabase, path: &Path) -> PerNs<ModuleDef> {
|
|
||||||
db.item_map(self.krate).resolve_path(db, *self, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
|
pub fn problems(&self, db: &impl HirDatabase) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
|
||||||
self.problems_impl(db)
|
self.problems_impl(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
let item_map = db.item_map(self.krate);
|
||||||
|
Resolver::default().push_module_scope(item_map, *self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Module {
|
impl Docs for Module {
|
||||||
|
@ -282,6 +284,21 @@ impl Struct {
|
||||||
pub fn ty(&self, db: &impl HirDatabase) -> Ty {
|
pub fn ty(&self, db: &impl HirDatabase) -> Ty {
|
||||||
db.type_for_def((*self).into())
|
db.type_for_def((*self).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move to a more general type
|
||||||
|
/// Builds a resolver for type references inside this struct.
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
// take the outer scope...
|
||||||
|
let r = self.module(db).resolver(db);
|
||||||
|
// ...and add generic params, if present
|
||||||
|
let p = self.generic_params(db);
|
||||||
|
let r = if !p.params.is_empty() {
|
||||||
|
r.push_generic_params_scope(p)
|
||||||
|
} else {
|
||||||
|
r
|
||||||
|
};
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Struct {
|
impl Docs for Struct {
|
||||||
|
@ -331,6 +348,21 @@ impl Enum {
|
||||||
pub fn ty(&self, db: &impl HirDatabase) -> Ty {
|
pub fn ty(&self, db: &impl HirDatabase) -> Ty {
|
||||||
db.type_for_def((*self).into())
|
db.type_for_def((*self).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO move to a more general type
|
||||||
|
/// Builds a resolver for type references inside this struct.
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
// take the outer scope...
|
||||||
|
let r = self.module(db).resolver(db);
|
||||||
|
// ...and add generic params, if present
|
||||||
|
let p = self.generic_params(db);
|
||||||
|
let r = if !p.params.is_empty() {
|
||||||
|
r.push_generic_params_scope(p)
|
||||||
|
} else {
|
||||||
|
r
|
||||||
|
};
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Enum {
|
impl Docs for Enum {
|
||||||
|
@ -449,6 +481,10 @@ impl Function {
|
||||||
db.body_syntax_mapping(*self)
|
db.body_syntax_mapping(*self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn body(&self, db: &impl HirDatabase) -> Arc<Body> {
|
||||||
|
db.body_hir(*self)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSyntaxMapping {
|
pub fn scopes(&self, db: &impl HirDatabase) -> ScopesWithSyntaxMapping {
|
||||||
let scopes = db.expr_scopes(*self);
|
let scopes = db.expr_scopes(*self);
|
||||||
let syntax_mapping = db.body_syntax_mapping(*self);
|
let syntax_mapping = db.body_syntax_mapping(*self);
|
||||||
|
@ -469,6 +505,24 @@ impl Function {
|
||||||
pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> {
|
pub fn generic_params(&self, db: &impl PersistentHirDatabase) -> Arc<GenericParams> {
|
||||||
db.generic_params((*self).into())
|
db.generic_params((*self).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO 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 {
|
||||||
|
// take the outer scope...
|
||||||
|
let r = self
|
||||||
|
.impl_block(db)
|
||||||
|
.map(|ib| ib.resolver(db))
|
||||||
|
.unwrap_or_else(|| self.module(db).resolver(db));
|
||||||
|
// ...and add generic params, if present
|
||||||
|
let p = self.generic_params(db);
|
||||||
|
let r = if !p.params.is_empty() {
|
||||||
|
r.push_generic_params_scope(p)
|
||||||
|
} else {
|
||||||
|
r
|
||||||
|
};
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Function {
|
impl Docs for Function {
|
||||||
|
|
|
@ -5,14 +5,12 @@ use ra_syntax::ast::{self, NameOwner};
|
||||||
use crate::{
|
use crate::{
|
||||||
HirDatabase, Name, AsName, Function, FnSignature,
|
HirDatabase, Name, AsName, Function, FnSignature,
|
||||||
type_ref::{TypeRef, Mutability},
|
type_ref::{TypeRef, Mutability},
|
||||||
expr::Body, PersistentHirDatabase,
|
PersistentHirDatabase,
|
||||||
impl_block::ImplBlock,
|
impl_block::ImplBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub(crate) fn body(&self, db: &impl HirDatabase) -> Arc<Body> {
|
// TODO impl_block should probably also be part of the code model API?
|
||||||
db.body_hir(*self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The containing impl block, if this is a method.
|
/// The containing impl block, if this is a method.
|
||||||
pub(crate) fn impl_block(&self, db: &impl HirDatabase) -> Option<ImplBlock> {
|
pub(crate) fn impl_block(&self, db: &impl HirDatabase) -> Option<ImplBlock> {
|
||||||
|
|
|
@ -10,15 +10,15 @@ use ra_syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Path, Name, Function,
|
Path, Name, HirDatabase, Function, Resolver,
|
||||||
name::AsName, HirDatabase,
|
name::AsName,
|
||||||
type_ref::{Mutability, TypeRef},
|
type_ref::{Mutability, TypeRef},
|
||||||
};
|
};
|
||||||
use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy};
|
use crate::ty::primitive::{UintTy, UncertainIntTy, UncertainFloatTy};
|
||||||
|
|
||||||
pub use self::scope::{ExprScopes, ScopesWithSyntaxMapping, ScopeEntryWithSyntax};
|
pub use self::scope::{ExprScopes, ScopesWithSyntaxMapping, ScopeEntryWithSyntax};
|
||||||
|
|
||||||
mod scope;
|
pub(crate) mod scope;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct ExprId(RawId);
|
pub struct ExprId(RawId);
|
||||||
|
@ -27,6 +27,9 @@ impl_arena_id!(ExprId);
|
||||||
/// The body of an item (function, const etc.).
|
/// The body of an item (function, const etc.).
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Body {
|
pub struct Body {
|
||||||
|
// TODO: this should be more general, consts & statics also have bodies
|
||||||
|
/// The Function of the item this body belongs to
|
||||||
|
owner: Function,
|
||||||
exprs: Arena<ExprId, Expr>,
|
exprs: Arena<ExprId, Expr>,
|
||||||
pats: Arena<PatId, Pat>,
|
pats: Arena<PatId, Pat>,
|
||||||
/// The patterns for the function's parameters. While the parameter types are
|
/// The patterns for the function's parameters. While the parameter types are
|
||||||
|
@ -62,6 +65,34 @@ impl Body {
|
||||||
pub fn body_expr(&self) -> ExprId {
|
pub fn body_expr(&self) -> ExprId {
|
||||||
self.body_expr
|
self.body_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn owner(&self) -> Function {
|
||||||
|
self.owner
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> {
|
||||||
|
db.body_syntax_mapping(self.owner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// needs arbitrary_self_types to be a method... or maybe move to the def?
|
||||||
|
pub fn resolver_for_expr(body: Arc<Body>, 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(
|
||||||
|
body: Arc<Body>,
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
scope_id: Option<scope::ScopeId>,
|
||||||
|
) -> 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::<Vec<_>>();
|
||||||
|
for scope in scope_chain.into_iter().rev() {
|
||||||
|
r = r.push_expr_scope(Arc::clone(&scopes), scope);
|
||||||
|
}
|
||||||
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<ExprId> for Body {
|
impl Index<ExprId> for Body {
|
||||||
|
@ -448,23 +479,29 @@ pub(crate) fn body_hir(db: &impl HirDatabase, func: Function) -> Arc<Body> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExprCollector {
|
struct ExprCollector {
|
||||||
|
owner: Function,
|
||||||
exprs: Arena<ExprId, Expr>,
|
exprs: Arena<ExprId, Expr>,
|
||||||
pats: Arena<PatId, Pat>,
|
pats: Arena<PatId, Pat>,
|
||||||
expr_syntax_mapping: FxHashMap<SyntaxNodePtr, ExprId>,
|
expr_syntax_mapping: FxHashMap<SyntaxNodePtr, ExprId>,
|
||||||
expr_syntax_mapping_back: ArenaMap<ExprId, SyntaxNodePtr>,
|
expr_syntax_mapping_back: ArenaMap<ExprId, SyntaxNodePtr>,
|
||||||
pat_syntax_mapping: FxHashMap<SyntaxNodePtr, PatId>,
|
pat_syntax_mapping: FxHashMap<SyntaxNodePtr, PatId>,
|
||||||
pat_syntax_mapping_back: ArenaMap<PatId, SyntaxNodePtr>,
|
pat_syntax_mapping_back: ArenaMap<PatId, SyntaxNodePtr>,
|
||||||
|
params: Vec<PatId>,
|
||||||
|
body_expr: Option<ExprId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExprCollector {
|
impl ExprCollector {
|
||||||
fn new() -> Self {
|
fn new(owner: Function) -> Self {
|
||||||
ExprCollector {
|
ExprCollector {
|
||||||
|
owner,
|
||||||
exprs: Arena::default(),
|
exprs: Arena::default(),
|
||||||
pats: Arena::default(),
|
pats: Arena::default(),
|
||||||
expr_syntax_mapping: FxHashMap::default(),
|
expr_syntax_mapping: FxHashMap::default(),
|
||||||
expr_syntax_mapping_back: ArenaMap::default(),
|
expr_syntax_mapping_back: ArenaMap::default(),
|
||||||
pat_syntax_mapping: FxHashMap::default(),
|
pat_syntax_mapping: FxHashMap::default(),
|
||||||
pat_syntax_mapping_back: ArenaMap::default(),
|
pat_syntax_mapping_back: ArenaMap::default(),
|
||||||
|
params: Vec::new(),
|
||||||
|
body_expr: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,10 +939,7 @@ impl ExprCollector {
|
||||||
});
|
});
|
||||||
fields.extend(iter);
|
fields.extend(iter);
|
||||||
|
|
||||||
Pat::Struct {
|
Pat::Struct { path, args: fields }
|
||||||
path: path,
|
|
||||||
args: fields,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
|
@ -923,12 +957,48 @@ impl ExprCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_body_syntax_mapping(self, params: Vec<PatId>, body_expr: ExprId) -> BodySyntaxMapping {
|
fn collect_fn_body(&mut self, node: &ast::FnDef) {
|
||||||
|
if let Some(param_list) = node.param_list() {
|
||||||
|
if let Some(self_param) = param_list.self_param() {
|
||||||
|
let self_param = SyntaxNodePtr::new(
|
||||||
|
self_param
|
||||||
|
.self_kw()
|
||||||
|
.expect("self param without self keyword")
|
||||||
|
.syntax(),
|
||||||
|
);
|
||||||
|
let param_pat = self.alloc_pat(
|
||||||
|
Pat::Bind {
|
||||||
|
name: Name::self_param(),
|
||||||
|
mode: BindingAnnotation::Unannotated,
|
||||||
|
subpat: None,
|
||||||
|
},
|
||||||
|
self_param,
|
||||||
|
);
|
||||||
|
self.params.push(param_pat);
|
||||||
|
}
|
||||||
|
|
||||||
|
for param in param_list.params() {
|
||||||
|
let pat = if let Some(pat) = param.pat() {
|
||||||
|
pat
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let param_pat = self.collect_pat(pat);
|
||||||
|
self.params.push(param_pat);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = self.collect_block_opt(node.body());
|
||||||
|
self.body_expr = Some(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_body_syntax_mapping(self) -> BodySyntaxMapping {
|
||||||
let body = Body {
|
let body = Body {
|
||||||
|
owner: self.owner,
|
||||||
exprs: self.exprs,
|
exprs: self.exprs,
|
||||||
pats: self.pats,
|
pats: self.pats,
|
||||||
params,
|
params: self.params,
|
||||||
body_expr,
|
body_expr: self.body_expr.expect("A body should have been collected"),
|
||||||
};
|
};
|
||||||
BodySyntaxMapping {
|
BodySyntaxMapping {
|
||||||
body: Arc::new(body),
|
body: Arc::new(body),
|
||||||
|
@ -940,49 +1010,18 @@ impl ExprCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_fn_body_syntax(node: &ast::FnDef) -> BodySyntaxMapping {
|
|
||||||
let mut collector = ExprCollector::new();
|
|
||||||
|
|
||||||
let params = if let Some(param_list) = node.param_list() {
|
|
||||||
let mut params = Vec::new();
|
|
||||||
|
|
||||||
if let Some(self_param) = param_list.self_param() {
|
|
||||||
let self_param = SyntaxNodePtr::new(
|
|
||||||
self_param
|
|
||||||
.self_kw()
|
|
||||||
.expect("self param without self keyword")
|
|
||||||
.syntax(),
|
|
||||||
);
|
|
||||||
let param = collector.alloc_pat(
|
|
||||||
Pat::Bind {
|
|
||||||
name: Name::self_param(),
|
|
||||||
mode: BindingAnnotation::Unannotated,
|
|
||||||
subpat: None,
|
|
||||||
},
|
|
||||||
self_param,
|
|
||||||
);
|
|
||||||
params.push(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
for param in param_list.params() {
|
|
||||||
let pat = if let Some(pat) = param.pat() {
|
|
||||||
pat
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
params.push(collector.collect_pat(pat));
|
|
||||||
}
|
|
||||||
params
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
let body = collector.collect_block_opt(node.body());
|
|
||||||
collector.into_body_syntax_mapping(params, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn body_syntax_mapping(db: &impl HirDatabase, func: Function) -> Arc<BodySyntaxMapping> {
|
pub(crate) fn body_syntax_mapping(db: &impl HirDatabase, func: Function) -> Arc<BodySyntaxMapping> {
|
||||||
let (_, fn_def) = func.source(db);
|
let mut collector = ExprCollector::new(func);
|
||||||
let body_syntax_mapping = collect_fn_body_syntax(&fn_def);
|
|
||||||
Arc::new(body_syntax_mapping)
|
// TODO: consts, etc.
|
||||||
|
collector.collect_fn_body(&func.source(db).1);
|
||||||
|
|
||||||
|
Arc::new(collector.into_body_syntax_mapping())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn collect_fn_body_syntax(function: Function, node: &ast::FnDef) -> BodySyntaxMapping {
|
||||||
|
let mut collector = ExprCollector::new(function);
|
||||||
|
collector.collect_fn_body(node);
|
||||||
|
collector.into_body_syntax_mapping()
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,28 +58,19 @@ impl ExprScopes {
|
||||||
scopes
|
scopes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn body(&self) -> Arc<Body> {
|
||||||
|
self.body.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
|
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
|
||||||
&self.scopes[scope].entries
|
&self.scopes[scope].entries
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a {
|
pub fn scope_chain_for<'a>(
|
||||||
generate(self.scope_for(expr), move |&scope| {
|
|
||||||
self.scopes[scope].parent
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve_local_name<'a>(
|
|
||||||
&'a self,
|
&'a self,
|
||||||
context_expr: ExprId,
|
scope: Option<ScopeId>,
|
||||||
name: Name,
|
) -> impl Iterator<Item = ScopeId> + 'a {
|
||||||
) -> Option<&'a ScopeEntry> {
|
generate(scope, move |&scope| self.scopes[scope].parent)
|
||||||
let mut shadowed = FxHashSet::default();
|
|
||||||
let ret = self
|
|
||||||
.scope_chain_for(context_expr)
|
|
||||||
.flat_map(|scope| self.entries(scope).iter())
|
|
||||||
.filter(|entry| shadowed.insert(entry.name()))
|
|
||||||
.find(|entry| entry.name() == &name);
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_scope(&mut self) -> ScopeId {
|
fn root_scope(&mut self) -> ScopeId {
|
||||||
|
@ -122,7 +113,7 @@ impl ExprScopes {
|
||||||
self.scope_for.insert(node, scope);
|
self.scope_for.insert(node, scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
|
pub fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
|
||||||
self.scope_for.get(&expr).map(|&scope| scope)
|
self.scope_for.get(&expr).map(|&scope| scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,18 +141,14 @@ impl ScopeEntryWithSyntax {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopesWithSyntaxMapping {
|
impl ScopesWithSyntaxMapping {
|
||||||
pub fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator<Item = ScopeId> + 'a {
|
fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator<Item = ScopeId> + 'a {
|
||||||
generate(self.scope_for(node), move |&scope| {
|
generate(self.scope_for(node), move |&scope| {
|
||||||
self.scopes.scopes[scope].parent
|
self.scopes.scopes[scope].parent
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scope_chain_for_offset<'a>(
|
pub fn scope_for_offset<'a>(&'a self, offset: TextUnit) -> Option<ScopeId> {
|
||||||
&'a self,
|
self.scopes
|
||||||
offset: TextUnit,
|
|
||||||
) -> impl Iterator<Item = ScopeId> + 'a {
|
|
||||||
let scope = self
|
|
||||||
.scopes
|
|
||||||
.scope_for
|
.scope_for
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
|
.filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
|
||||||
|
@ -172,13 +159,12 @@ impl ScopesWithSyntaxMapping {
|
||||||
ptr.range().len(),
|
ptr.range().len(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.map(|(ptr, scope)| self.adjust(ptr, *scope, offset));
|
.map(|(ptr, scope)| self.adjust(ptr, *scope, offset))
|
||||||
|
|
||||||
generate(scope, move |&scope| self.scopes.scopes[scope].parent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: during completion, cursor might be outside of any particular
|
// XXX: during completion, cursor might be outside of any particular
|
||||||
// expression. Try to figure out the correct scope...
|
// expression. Try to figure out the correct scope...
|
||||||
|
// TODO: move this to source binder?
|
||||||
fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
|
fn adjust(&self, ptr: SyntaxNodePtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
|
||||||
let r = ptr.range();
|
let r = ptr.range();
|
||||||
let child_scopes = self
|
let child_scopes = self
|
||||||
|
@ -238,7 +224,7 @@ impl ScopesWithSyntaxMapping {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
|
pub fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
|
||||||
node.ancestors()
|
node.ancestors()
|
||||||
.map(SyntaxNodePtr::new)
|
.map(SyntaxNodePtr::new)
|
||||||
.filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
|
.filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
|
||||||
|
@ -336,6 +322,7 @@ pub struct ReferenceDescriptor {
|
||||||
mod tests {
|
mod tests {
|
||||||
use ra_syntax::{SourceFile, algo::find_node_at_offset};
|
use ra_syntax::{SourceFile, algo::find_node_at_offset};
|
||||||
use test_utils::{extract_offset, assert_eq_text};
|
use test_utils::{extract_offset, assert_eq_text};
|
||||||
|
use ra_arena::ArenaId;
|
||||||
|
|
||||||
use crate::expr;
|
use crate::expr;
|
||||||
|
|
||||||
|
@ -354,7 +341,10 @@ mod tests {
|
||||||
let file = SourceFile::parse(&code);
|
let file = SourceFile::parse(&code);
|
||||||
let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
|
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 fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
|
||||||
let body_hir = expr::collect_fn_body_syntax(fn_def);
|
let irrelevant_function = Function {
|
||||||
|
id: crate::ids::FunctionId::from_raw(0.into()),
|
||||||
|
};
|
||||||
|
let body_hir = expr::collect_fn_body_syntax(irrelevant_function, fn_def);
|
||||||
let scopes = ExprScopes::new(Arc::clone(body_hir.body()));
|
let scopes = ExprScopes::new(Arc::clone(body_hir.body()));
|
||||||
let scopes = ScopesWithSyntaxMapping {
|
let scopes = ScopesWithSyntaxMapping {
|
||||||
scopes: Arc::new(scopes),
|
scopes: Arc::new(scopes),
|
||||||
|
@ -454,7 +444,10 @@ mod tests {
|
||||||
let fn_def: &ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
|
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 name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
|
||||||
|
|
||||||
let body_hir = expr::collect_fn_body_syntax(fn_def);
|
let irrelevant_function = Function {
|
||||||
|
id: crate::ids::FunctionId::from_raw(0.into()),
|
||||||
|
};
|
||||||
|
let body_hir = expr::collect_fn_body_syntax(irrelevant_function, fn_def);
|
||||||
let scopes = ExprScopes::new(Arc::clone(body_hir.body()));
|
let scopes = ExprScopes::new(Arc::clone(body_hir.body()));
|
||||||
let scopes = ScopesWithSyntaxMapping {
|
let scopes = ScopesWithSyntaxMapping {
|
||||||
scopes: Arc::new(scopes),
|
scopes: Arc::new(scopes),
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::{db::PersistentHirDatabase, Name, AsName, Function, Struct, Enum, Tra
|
||||||
/// Data about a generic parameter (to a function, struct, impl, ...).
|
/// Data about a generic parameter (to a function, struct, impl, ...).
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct GenericParam {
|
pub struct GenericParam {
|
||||||
|
// TODO: give generic params proper IDs
|
||||||
pub(crate) idx: u32,
|
pub(crate) idx: u32,
|
||||||
pub(crate) name: Name,
|
pub(crate) name: Name,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,13 @@ use ra_syntax::{
|
||||||
ast::{self, AstNode}};
|
ast::{self, AstNode}};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Const, Type,
|
Const, Type, Function, HirFileId,
|
||||||
Function, HirFileId,
|
HirDatabase, PersistentHirDatabase,
|
||||||
PersistentHirDatabase,
|
ModuleDef, Trait, Resolution,
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
ids::LocationCtx,
|
ids::LocationCtx,
|
||||||
|
resolve::Resolver,
|
||||||
|
ty::Ty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::code_model_api::{Module, ModuleSource};
|
use crate::code_model_api::{Module, ModuleSource};
|
||||||
|
@ -69,7 +71,11 @@ impl ImplBlock {
|
||||||
&self.module_impl_blocks.impls[self.impl_id]
|
&self.module_impl_blocks.impls[self.impl_id]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_trait(&self) -> Option<&TypeRef> {
|
pub fn module(&self) -> Module {
|
||||||
|
self.module_impl_blocks.module.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_trait_ref(&self) -> Option<&TypeRef> {
|
||||||
self.impl_data().target_trait()
|
self.impl_data().target_trait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +83,32 @@ impl ImplBlock {
|
||||||
self.impl_data().target_type()
|
self.impl_data().target_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn target_ty(&self, db: &impl HirDatabase) -> Ty {
|
||||||
|
Ty::from_hir(db, &self.resolver(db), self.target_type())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_trait(&self, db: &impl HirDatabase) -> Option<Trait> {
|
||||||
|
if let Some(TypeRef::Path(path)) = self.target_trait_ref() {
|
||||||
|
let resolver = self.resolver(db);
|
||||||
|
if let Some(Resolution::Def(ModuleDef::Trait(tr))) =
|
||||||
|
resolver.resolve_path(db, path).take_types()
|
||||||
|
{
|
||||||
|
return Some(tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn items(&self) -> &[ImplItem] {
|
pub fn items(&self) -> &[ImplItem] {
|
||||||
self.impl_data().items()
|
self.impl_data().items()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
let r = self.module().resolver(db);
|
||||||
|
// TODO: add generics
|
||||||
|
let r = r.push_impl_block_scope(self.clone());
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -162,25 +191,24 @@ impl_arena_id!(ImplId);
|
||||||
/// we don't need to do the second step again.
|
/// we don't need to do the second step again.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct ModuleImplBlocks {
|
pub struct ModuleImplBlocks {
|
||||||
|
module: Module,
|
||||||
pub(crate) impls: Arena<ImplId, ImplData>,
|
pub(crate) impls: Arena<ImplId, ImplData>,
|
||||||
impls_by_def: FxHashMap<ImplItem, ImplId>,
|
impls_by_def: FxHashMap<ImplItem, ImplId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleImplBlocks {
|
impl ModuleImplBlocks {
|
||||||
fn new() -> Self {
|
|
||||||
ModuleImplBlocks {
|
|
||||||
impls: Arena::default(),
|
|
||||||
impls_by_def: FxHashMap::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect(
|
fn collect(
|
||||||
&mut self,
|
|
||||||
db: &impl PersistentHirDatabase,
|
db: &impl PersistentHirDatabase,
|
||||||
module: Module,
|
module: Module,
|
||||||
source_map: &mut ImplSourceMap,
|
source_map: &mut ImplSourceMap,
|
||||||
) {
|
) -> Self {
|
||||||
let (file_id, module_source) = module.definition_source(db);
|
let mut m = ModuleImplBlocks {
|
||||||
|
module,
|
||||||
|
impls: Arena::default(),
|
||||||
|
impls_by_def: FxHashMap::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (file_id, module_source) = m.module.definition_source(db);
|
||||||
let file_id: HirFileId = file_id.into();
|
let file_id: HirFileId = file_id.into();
|
||||||
let node = match &module_source {
|
let node = match &module_source {
|
||||||
ModuleSource::SourceFile(node) => node.syntax(),
|
ModuleSource::SourceFile(node) => node.syntax(),
|
||||||
|
@ -191,14 +219,16 @@ impl ModuleImplBlocks {
|
||||||
};
|
};
|
||||||
|
|
||||||
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
|
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
|
||||||
let impl_block = ImplData::from_ast(db, file_id, module, impl_block_ast);
|
let impl_block = ImplData::from_ast(db, file_id, m.module, impl_block_ast);
|
||||||
let id = self.impls.alloc(impl_block);
|
let id = m.impls.alloc(impl_block);
|
||||||
for &impl_item in &self.impls[id].items {
|
for &impl_item in &m.impls[id].items {
|
||||||
self.impls_by_def.insert(impl_item, id);
|
m.impls_by_def.insert(impl_item, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
source_map.insert(id, impl_block_ast);
|
source_map.insert(id, impl_block_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,8 +238,7 @@ pub(crate) fn impls_in_module_with_source_map_query(
|
||||||
) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) {
|
) -> (Arc<ModuleImplBlocks>, Arc<ImplSourceMap>) {
|
||||||
let mut source_map = ImplSourceMap::default();
|
let mut source_map = ImplSourceMap::default();
|
||||||
|
|
||||||
let mut result = ModuleImplBlocks::new();
|
let result = ModuleImplBlocks::collect(db, module, &mut source_map);
|
||||||
result.collect(db, module, &mut source_map);
|
|
||||||
|
|
||||||
(Arc::new(result), Arc::new(source_map))
|
(Arc::new(result), Arc::new(source_map))
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ mod impl_block;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod generics;
|
mod generics;
|
||||||
mod docs;
|
mod docs;
|
||||||
|
mod resolve;
|
||||||
|
|
||||||
mod code_model_api;
|
mod code_model_api;
|
||||||
mod code_model_impl;
|
mod code_model_impl;
|
||||||
|
@ -54,12 +55,13 @@ pub use self::{
|
||||||
name::Name,
|
name::Name,
|
||||||
ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner},
|
ids::{HirFileId, MacroCallId, MacroCallLoc, HirInterner},
|
||||||
macros::{MacroDef, MacroInput, MacroExpansion},
|
macros::{MacroDef, MacroInput, MacroExpansion},
|
||||||
nameres::{ItemMap, PerNs, Namespace, Resolution},
|
nameres::{ItemMap, PerNs, Namespace},
|
||||||
ty::Ty,
|
ty::Ty,
|
||||||
impl_block::{ImplBlock, ImplItem},
|
impl_block::{ImplBlock, ImplItem},
|
||||||
docs::{Docs, Documentation},
|
docs::{Docs, Documentation},
|
||||||
adt::AdtDef,
|
adt::AdtDef,
|
||||||
expr::{ExprScopes, ScopesWithSyntaxMapping},
|
expr::{ExprScopes, ScopesWithSyntaxMapping},
|
||||||
|
resolve::{Resolver, Resolution},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::code_model_api::{
|
pub use self::code_model_api::{
|
||||||
|
|
|
@ -24,13 +24,13 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Module, ModuleDef,
|
Module, ModuleDef,
|
||||||
Path, PathKind, Crate,
|
Path, PathKind, PersistentHirDatabase,
|
||||||
Name, PersistentHirDatabase,
|
Crate, Name,
|
||||||
module_tree::{ModuleId, ModuleTree},
|
module_tree::{ModuleId, ModuleTree},
|
||||||
nameres::lower::{ImportId, LoweredModule, ImportData},
|
nameres::lower::{ImportId, LoweredModule, ImportData},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `ItemMap` is the result of name resolution. It contains, for each
|
/// `ItemMap` is the result of module name resolution. It contains, for each
|
||||||
/// module, the set of visible items.
|
/// module, the set of visible items.
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Default, Debug, PartialEq, Eq)]
|
||||||
pub struct ItemMap {
|
pub struct ItemMap {
|
||||||
|
@ -46,7 +46,7 @@ impl std::ops::Index<ModuleId> for ItemMap {
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||||
pub struct ModuleScope {
|
pub struct ModuleScope {
|
||||||
items: FxHashMap<Name, Resolution>,
|
pub(crate) items: FxHashMap<Name, Resolution>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleScope {
|
impl ModuleScope {
|
||||||
|
@ -80,6 +80,15 @@ pub struct PerNs<T> {
|
||||||
pub values: Option<T>,
|
pub values: Option<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Default for PerNs<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
PerNs {
|
||||||
|
types: None,
|
||||||
|
values: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> PerNs<T> {
|
impl<T> PerNs<T> {
|
||||||
pub fn none() -> PerNs<T> {
|
pub fn none() -> PerNs<T> {
|
||||||
PerNs {
|
PerNs {
|
||||||
|
@ -113,6 +122,10 @@ impl<T> PerNs<T> {
|
||||||
self.types.is_none() && self.values.is_none()
|
self.types.is_none() && self.values.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_both(&self) -> bool {
|
||||||
|
self.types.is_some() && self.values.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn take(self, namespace: Namespace) -> Option<T> {
|
pub fn take(self, namespace: Namespace) -> Option<T> {
|
||||||
match namespace {
|
match namespace {
|
||||||
Namespace::Types => self.types,
|
Namespace::Types => self.types,
|
||||||
|
@ -139,6 +152,13 @@ impl<T> PerNs<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn combine(self, other: PerNs<T>) -> PerNs<T> {
|
||||||
|
PerNs {
|
||||||
|
types: self.types.or(other.types),
|
||||||
|
values: self.values.or(other.values),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
|
pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
|
||||||
PerNs {
|
PerNs {
|
||||||
types: self.types.and_then(&f),
|
types: self.types.and_then(&f),
|
||||||
|
@ -402,10 +422,11 @@ impl ItemMap {
|
||||||
if module.krate != original_module.krate {
|
if module.krate != original_module.krate {
|
||||||
let path = Path {
|
let path = Path {
|
||||||
segments: path.segments[i..].iter().cloned().collect(),
|
segments: path.segments[i..].iter().cloned().collect(),
|
||||||
kind: PathKind::Crate,
|
kind: PathKind::Self_,
|
||||||
};
|
};
|
||||||
log::debug!("resolving {:?} in other crate", path);
|
log::debug!("resolving {:?} in other crate", path);
|
||||||
let def = module.resolve_path(db, &path);
|
let item_map = db.item_map(module.krate);
|
||||||
|
let def = item_map.resolve_path(db, *module, &path);
|
||||||
return (def, ReachedFixedPoint::Yes);
|
return (def, ReachedFixedPoint::Yes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,12 @@ use relative_path::RelativePath;
|
||||||
use test_utils::{assert_eq_text, covers};
|
use test_utils::{assert_eq_text, covers};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ItemMap, Resolution,
|
ItemMap,
|
||||||
PersistentHirDatabase,
|
PersistentHirDatabase,
|
||||||
mock::MockDatabase,
|
mock::MockDatabase,
|
||||||
module_tree::ModuleId,
|
module_tree::ModuleId,
|
||||||
};
|
};
|
||||||
|
use super::Resolution;
|
||||||
|
|
||||||
fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
|
fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
|
||||||
let (db, pos) = MockDatabase::with_position(fixture);
|
let (db, pos) = MockDatabase::with_position(fixture);
|
||||||
|
|
226
crates/ra_hir/src/resolve.rs
Normal file
226
crates/ra_hir/src/resolve.rs
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
//! Name resolution.
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ModuleDef, Module,
|
||||||
|
db::HirDatabase,
|
||||||
|
name::{Name, KnownName},
|
||||||
|
nameres::{PerNs, ItemMap},
|
||||||
|
generics::GenericParams,
|
||||||
|
expr::{scope::{ExprScopes, ScopeId}, PatId, Body},
|
||||||
|
impl_block::ImplBlock,
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct Resolver {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO how to store these best
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct ModuleItemMap {
|
||||||
|
item_map: Arc<ItemMap>,
|
||||||
|
module: Module,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct ExprScope {
|
||||||
|
expr_scopes: Arc<ExprScopes>,
|
||||||
|
scope_id: ScopeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) enum Scope {
|
||||||
|
/// All the items and imported names of a module
|
||||||
|
ModuleScope(ModuleItemMap),
|
||||||
|
/// Brings the generic parameters of an item into scope
|
||||||
|
GenericParams(Arc<GenericParams>),
|
||||||
|
/// Brings `Self` into scope
|
||||||
|
ImplBlockScope(ImplBlock),
|
||||||
|
/// Local bindings
|
||||||
|
ExprScope(ExprScope),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Resolution {
|
||||||
|
/// An item
|
||||||
|
Def(ModuleDef),
|
||||||
|
/// A local binding (only value namespace)
|
||||||
|
LocalBinding(PatId),
|
||||||
|
/// A generic parameter
|
||||||
|
GenericParam(u32),
|
||||||
|
SelfType(ImplBlock),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver {
|
||||||
|
pub fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
|
||||||
|
let mut resolution = PerNs::none();
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
resolution = resolution.combine(scope.resolve_name(name));
|
||||||
|
if resolution.is_both() {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolution
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
|
||||||
|
if let Some(name) = path.as_ident() {
|
||||||
|
self.resolve_name(name)
|
||||||
|
} else if path.is_self() {
|
||||||
|
self.resolve_name(&Name::self_param())
|
||||||
|
} else {
|
||||||
|
let (item_map, module) = match self.module() {
|
||||||
|
Some(m) => m,
|
||||||
|
_ => return PerNs::none(),
|
||||||
|
};
|
||||||
|
let module_res = item_map.resolve_path(db, module, path);
|
||||||
|
module_res.map(|def| Resolution::Def(def))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_names(&self) -> FxHashMap<Name, PerNs<Resolution>> {
|
||||||
|
let mut names = FxHashMap::default();
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
scope.collect_names(&mut |name, res| {
|
||||||
|
let current: &mut PerNs<Resolution> = names.entry(name).or_default();
|
||||||
|
if current.types.is_none() {
|
||||||
|
current.types = res.types;
|
||||||
|
}
|
||||||
|
if current.values.is_none() {
|
||||||
|
current.values = res.values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
names
|
||||||
|
}
|
||||||
|
|
||||||
|
fn module(&self) -> Option<(&ItemMap, Module)> {
|
||||||
|
self.scopes.iter().rev().find_map(|scope| match scope {
|
||||||
|
Scope::ModuleScope(m) => Some((&*m.item_map, m.module.clone())),
|
||||||
|
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The body from which any `LocalBinding` resolutions in this resolver come.
|
||||||
|
pub fn body(&self) -> Option<Arc<Body>> {
|
||||||
|
self.scopes.iter().rev().find_map(|scope| match scope {
|
||||||
|
Scope::ExprScope(expr_scope) => Some(expr_scope.expr_scopes.body()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver {
|
||||||
|
pub(crate) fn push_scope(mut self, scope: Scope) -> Resolver {
|
||||||
|
self.scopes.push(scope);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_generic_params_scope(self, params: Arc<GenericParams>) -> Resolver {
|
||||||
|
self.push_scope(Scope::GenericParams(params))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_impl_block_scope(self, impl_block: ImplBlock) -> Resolver {
|
||||||
|
self.push_scope(Scope::ImplBlockScope(impl_block))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_module_scope(self, item_map: Arc<ItemMap>, module: Module) -> Resolver {
|
||||||
|
self.push_scope(Scope::ModuleScope(ModuleItemMap { item_map, module }))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push_expr_scope(
|
||||||
|
self,
|
||||||
|
expr_scopes: Arc<ExprScopes>,
|
||||||
|
scope_id: ScopeId,
|
||||||
|
) -> Resolver {
|
||||||
|
self.push_scope(Scope::ExprScope(ExprScope {
|
||||||
|
expr_scopes,
|
||||||
|
scope_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scope {
|
||||||
|
fn resolve_name(&self, name: &Name) -> PerNs<Resolution> {
|
||||||
|
match self {
|
||||||
|
Scope::ModuleScope(m) => {
|
||||||
|
if let Some(KnownName::SelfParam) = name.as_known_name() {
|
||||||
|
PerNs::types(Resolution::Def(m.module.into()))
|
||||||
|
} else {
|
||||||
|
match m.item_map[m.module.module_id].get(name) {
|
||||||
|
Some(res) => res.def.map(Resolution::Def),
|
||||||
|
None => PerNs::none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::GenericParams(gp) => match gp.find_by_name(name) {
|
||||||
|
Some(gp) => PerNs::types(Resolution::GenericParam(gp.idx)),
|
||||||
|
None => PerNs::none(),
|
||||||
|
},
|
||||||
|
Scope::ImplBlockScope(i) => {
|
||||||
|
if name.as_known_name() == Some(KnownName::SelfType) {
|
||||||
|
PerNs::types(Resolution::SelfType(i.clone()))
|
||||||
|
} else {
|
||||||
|
PerNs::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::ExprScope(e) => {
|
||||||
|
let entry = e
|
||||||
|
.expr_scopes
|
||||||
|
.entries(e.scope_id)
|
||||||
|
.iter()
|
||||||
|
.find(|entry| entry.name() == name);
|
||||||
|
match entry {
|
||||||
|
Some(e) => PerNs::values(Resolution::LocalBinding(e.pat())),
|
||||||
|
None => PerNs::none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_names(&self, f: &mut FnMut(Name, PerNs<Resolution>)) {
|
||||||
|
match self {
|
||||||
|
Scope::ModuleScope(m) => {
|
||||||
|
// TODO: should we provide `self` here?
|
||||||
|
// f(
|
||||||
|
// Name::self_param(),
|
||||||
|
// PerNs::types(Resolution::Def {
|
||||||
|
// def: m.module.into(),
|
||||||
|
// }),
|
||||||
|
// );
|
||||||
|
m.item_map[m.module.module_id]
|
||||||
|
.entries()
|
||||||
|
.for_each(|(name, res)| {
|
||||||
|
f(name.clone(), res.def.map(Resolution::Def));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Scope::GenericParams(gp) => {
|
||||||
|
for param in &gp.params {
|
||||||
|
f(
|
||||||
|
param.name.clone(),
|
||||||
|
PerNs::types(Resolution::GenericParam(param.idx)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scope::ImplBlockScope(i) => {
|
||||||
|
f(
|
||||||
|
Name::self_type(),
|
||||||
|
PerNs::types(Resolution::SelfType(i.clone())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Scope::ExprScope(e) => {
|
||||||
|
e.expr_scopes.entries(e.scope_id).iter().for_each(|e| {
|
||||||
|
f(
|
||||||
|
e.name().clone(),
|
||||||
|
PerNs::values(Resolution::LocalBinding(e.pat())),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,13 +9,14 @@ use ra_db::{FileId, FilePosition};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
SmolStr, TextRange, SyntaxNode,
|
SmolStr, TextRange, SyntaxNode,
|
||||||
ast::{self, AstNode, NameOwner},
|
ast::{self, AstNode, NameOwner},
|
||||||
algo::find_node_at_offset,
|
algo::{find_node_at_offset, find_leaf_at_offset},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
HirDatabase, Function, ModuleDef, Struct, Enum,
|
HirDatabase, Function, ModuleDef, Struct, Enum,
|
||||||
AsName, Module, HirFileId, Crate, Trait,
|
AsName, Module, HirFileId, Crate, Trait, Resolver,
|
||||||
ids::{LocationCtx, SourceFileItemId},
|
ids::{LocationCtx, SourceFileItemId},
|
||||||
|
expr
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Locates the module by `FileId`. Picks topmost module in the file.
|
/// Locates the module by `FileId`. Picks topmost module in the file.
|
||||||
|
@ -201,3 +202,67 @@ pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, Te
|
||||||
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver {
|
||||||
|
let file_id = position.file_id;
|
||||||
|
let file = db.parse(file_id);
|
||||||
|
find_leaf_at_offset(file.syntax(), position.offset)
|
||||||
|
.find_map(|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) {
|
||||||
|
let scopes = func.scopes(db);
|
||||||
|
let scope = scopes.scope_for_offset(position.offset);
|
||||||
|
Some(expr::resolver_for_scope(func.body(db), db, scope))
|
||||||
|
} else {
|
||||||
|
// TODO const/static/array length
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if let Some(module) = ast::Module::cast(node) {
|
||||||
|
Some(module_from_declaration(db, file_id, module)?.resolver(db))
|
||||||
|
} else if let Some(_) = ast::SourceFile::cast(node) {
|
||||||
|
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
|
||||||
|
} else if let Some(s) = ast::StructDef::cast(node) {
|
||||||
|
let module = module_from_child_node(db, file_id, s.syntax())?;
|
||||||
|
Some(struct_from_module(db, module, s).resolver(db))
|
||||||
|
} 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 {
|
||||||
|
// TODO add missing cases
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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() {
|
||||||
|
if let Some(func) = function_from_child_node(db, file_id, node) {
|
||||||
|
let scopes = func.scopes(db);
|
||||||
|
let scope = scopes.scope_for(&node);
|
||||||
|
Some(expr::resolver_for_scope(func.body(db), db, scope))
|
||||||
|
} else {
|
||||||
|
// TODO const/static/array length
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if let Some(module) = ast::Module::cast(node) {
|
||||||
|
Some(module_from_declaration(db, file_id, module)?.resolver(db))
|
||||||
|
} else if let Some(_) = ast::SourceFile::cast(node) {
|
||||||
|
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
|
||||||
|
} else if let Some(s) = ast::StructDef::cast(node) {
|
||||||
|
let module = module_from_child_node(db, file_id, s.syntax())?;
|
||||||
|
Some(struct_from_module(db, module, s).resolver(db))
|
||||||
|
} 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 {
|
||||||
|
// TODO add missing cases
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
|
@ -33,15 +33,16 @@ use rustc_hash::FxHashMap;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock,
|
Function, Struct, StructField, Enum, EnumVariant, Path, Name,
|
||||||
FnSignature, ExprScopes, ModuleDef, AdtDef,
|
FnSignature, ModuleDef, AdtDef,
|
||||||
HirDatabase,
|
HirDatabase,
|
||||||
type_ref::{TypeRef, Mutability},
|
type_ref::{TypeRef, Mutability},
|
||||||
name::KnownName,
|
name::KnownName,
|
||||||
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat},
|
expr::{Body, Expr, BindingAnnotation, Literal, ExprId, Pat, PatId, UnaryOp, BinaryOp, Statement, FieldPat, self},
|
||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
path::GenericArg,
|
path::GenericArg,
|
||||||
adt::VariantDef,
|
adt::VariantDef,
|
||||||
|
resolve::{Resolver, Resolution},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The ID of a type variable.
|
/// The ID of a type variable.
|
||||||
|
@ -300,47 +301,38 @@ pub struct FnSig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ty {
|
impl Ty {
|
||||||
pub(crate) fn from_hir(
|
pub(crate) fn from_hir(db: &impl HirDatabase, resolver: &Resolver, type_ref: &TypeRef) -> Self {
|
||||||
db: &impl HirDatabase,
|
|
||||||
// TODO: the next three parameters basically describe the scope for name
|
|
||||||
// resolution; this should be refactored into something like a general
|
|
||||||
// resolver architecture
|
|
||||||
module: &Module,
|
|
||||||
impl_block: Option<&ImplBlock>,
|
|
||||||
generics: &GenericParams,
|
|
||||||
type_ref: &TypeRef,
|
|
||||||
) -> Self {
|
|
||||||
match type_ref {
|
match type_ref {
|
||||||
TypeRef::Never => Ty::Never,
|
TypeRef::Never => Ty::Never,
|
||||||
TypeRef::Tuple(inner) => {
|
TypeRef::Tuple(inner) => {
|
||||||
let inner_tys = inner
|
let inner_tys = inner
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, module, impl_block, generics, tr))
|
.map(|tr| Ty::from_hir(db, resolver, tr))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
Ty::Tuple(inner_tys.into())
|
Ty::Tuple(inner_tys.into())
|
||||||
}
|
}
|
||||||
TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, generics, path),
|
TypeRef::Path(path) => Ty::from_hir_path(db, resolver, path),
|
||||||
TypeRef::RawPtr(inner, mutability) => {
|
TypeRef::RawPtr(inner, mutability) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner);
|
let inner_ty = Ty::from_hir(db, resolver, inner);
|
||||||
Ty::RawPtr(Arc::new(inner_ty), *mutability)
|
Ty::RawPtr(Arc::new(inner_ty), *mutability)
|
||||||
}
|
}
|
||||||
TypeRef::Array(inner) => {
|
TypeRef::Array(inner) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner);
|
let inner_ty = Ty::from_hir(db, resolver, inner);
|
||||||
Ty::Array(Arc::new(inner_ty))
|
Ty::Array(Arc::new(inner_ty))
|
||||||
}
|
}
|
||||||
TypeRef::Slice(inner) => {
|
TypeRef::Slice(inner) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner);
|
let inner_ty = Ty::from_hir(db, resolver, inner);
|
||||||
Ty::Slice(Arc::new(inner_ty))
|
Ty::Slice(Arc::new(inner_ty))
|
||||||
}
|
}
|
||||||
TypeRef::Reference(inner, mutability) => {
|
TypeRef::Reference(inner, mutability) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, impl_block, generics, inner);
|
let inner_ty = Ty::from_hir(db, resolver, inner);
|
||||||
Ty::Ref(Arc::new(inner_ty), *mutability)
|
Ty::Ref(Arc::new(inner_ty), *mutability)
|
||||||
}
|
}
|
||||||
TypeRef::Placeholder => Ty::Unknown,
|
TypeRef::Placeholder => Ty::Unknown,
|
||||||
TypeRef::Fn(params) => {
|
TypeRef::Fn(params) => {
|
||||||
let mut inner_tys = params
|
let mut inner_tys = params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, module, impl_block, generics, tr))
|
.map(|tr| Ty::from_hir(db, resolver, tr))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let return_ty = inner_tys
|
let return_ty = inner_tys
|
||||||
.pop()
|
.pop()
|
||||||
|
@ -355,40 +347,13 @@ impl Ty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_hir_opt(
|
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
|
||||||
db: &impl HirDatabase,
|
|
||||||
module: &Module,
|
|
||||||
impl_block: Option<&ImplBlock>,
|
|
||||||
generics: &GenericParams,
|
|
||||||
type_ref: Option<&TypeRef>,
|
|
||||||
) -> Self {
|
|
||||||
type_ref.map_or(Ty::Unknown, |t| {
|
|
||||||
Ty::from_hir(db, module, impl_block, generics, t)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_hir_path(
|
|
||||||
db: &impl HirDatabase,
|
|
||||||
module: &Module,
|
|
||||||
impl_block: Option<&ImplBlock>,
|
|
||||||
generics: &GenericParams,
|
|
||||||
path: &Path,
|
|
||||||
) -> Self {
|
|
||||||
if let Some(name) = path.as_ident() {
|
if let Some(name) = path.as_ident() {
|
||||||
|
// TODO handle primitive type names in resolver as well?
|
||||||
if let Some(int_ty) = primitive::UncertainIntTy::from_name(name) {
|
if let Some(int_ty) = primitive::UncertainIntTy::from_name(name) {
|
||||||
return Ty::Int(int_ty);
|
return Ty::Int(int_ty);
|
||||||
} else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) {
|
} else if let Some(float_ty) = primitive::UncertainFloatTy::from_name(name) {
|
||||||
return Ty::Float(float_ty);
|
return Ty::Float(float_ty);
|
||||||
} else if name.as_known_name() == Some(KnownName::SelfType) {
|
|
||||||
// TODO pass the impl block's generics?
|
|
||||||
let generics = &GenericParams::default();
|
|
||||||
return Ty::from_hir_opt(
|
|
||||||
db,
|
|
||||||
module,
|
|
||||||
None,
|
|
||||||
generics,
|
|
||||||
impl_block.map(|i| i.target_type()),
|
|
||||||
);
|
|
||||||
} else if let Some(known) = name.as_known_name() {
|
} else if let Some(known) = name.as_known_name() {
|
||||||
match known {
|
match known {
|
||||||
KnownName::Bool => return Ty::Bool,
|
KnownName::Bool => return Ty::Bool,
|
||||||
|
@ -396,25 +361,40 @@ impl Ty {
|
||||||
KnownName::Str => return Ty::Str,
|
KnownName::Str => return Ty::Str,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
} else if let Some(generic_param) = generics.find_by_name(&name) {
|
|
||||||
return Ty::Param {
|
|
||||||
idx: generic_param.idx,
|
|
||||||
name: generic_param.name.clone(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve in module (in type namespace)
|
// Resolve the path (in type namespace)
|
||||||
let typable: TypableDef = match module
|
let resolution = resolver.resolve_path(db, path).take_types();
|
||||||
.resolve_path(db, path)
|
|
||||||
.take_types()
|
let def = match resolution {
|
||||||
.and_then(|it| it.into())
|
Some(Resolution::Def(def)) => def,
|
||||||
{
|
Some(Resolution::LocalBinding(..)) => {
|
||||||
|
// this should never happen
|
||||||
|
panic!("path resolved to local binding in type ns");
|
||||||
|
}
|
||||||
|
Some(Resolution::GenericParam(idx)) => {
|
||||||
|
return Ty::Param {
|
||||||
|
idx,
|
||||||
|
// TODO: maybe return name in resolution?
|
||||||
|
name: path
|
||||||
|
.as_ident()
|
||||||
|
.expect("generic param should be single-segment path")
|
||||||
|
.clone(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(Resolution::SelfType(impl_block)) => {
|
||||||
|
return impl_block.target_ty(db);
|
||||||
|
}
|
||||||
|
None => return Ty::Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
let typable: TypableDef = match def.into() {
|
||||||
None => return Ty::Unknown,
|
None => return Ty::Unknown,
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
};
|
};
|
||||||
let ty = db.type_for_def(typable);
|
let ty = db.type_for_def(typable);
|
||||||
let substs = Ty::substs_from_path(db, module, impl_block, generics, path, typable);
|
let substs = Ty::substs_from_path(db, resolver, path, typable);
|
||||||
ty.apply_substs(substs)
|
ty.apply_substs(substs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,10 +402,7 @@ impl Ty {
|
||||||
/// `create_substs_for_ast_path` and `def_to_ty` in rustc.
|
/// `create_substs_for_ast_path` and `def_to_ty` in rustc.
|
||||||
fn substs_from_path(
|
fn substs_from_path(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
// the scope of the segment...
|
resolver: &Resolver,
|
||||||
module: &Module,
|
|
||||||
impl_block: Option<&ImplBlock>,
|
|
||||||
outer_generics: &GenericParams,
|
|
||||||
path: &Path,
|
path: &Path,
|
||||||
resolved: TypableDef,
|
resolved: TypableDef,
|
||||||
) -> Substs {
|
) -> Substs {
|
||||||
|
@ -462,7 +439,7 @@ impl Ty {
|
||||||
for arg in generic_args.args.iter().take(param_count) {
|
for arg in generic_args.args.iter().take(param_count) {
|
||||||
match arg {
|
match arg {
|
||||||
GenericArg::Type(type_ref) => {
|
GenericArg::Type(type_ref) => {
|
||||||
let ty = Ty::from_hir(db, module, impl_block, outer_generics, type_ref);
|
let ty = Ty::from_hir(db, resolver, type_ref);
|
||||||
substs.push(ty);
|
substs.push(ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,24 +643,17 @@ impl fmt::Display for Ty {
|
||||||
/// function body.
|
/// function body.
|
||||||
fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty {
|
fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty {
|
||||||
let signature = def.signature(db);
|
let signature = def.signature(db);
|
||||||
let module = def.module(db);
|
let resolver = def.resolver(db);
|
||||||
let impl_block = def.impl_block(db);
|
|
||||||
let generics = def.generic_params(db);
|
let generics = def.generic_params(db);
|
||||||
|
let name = def.name(db);
|
||||||
let input = signature
|
let input = signature
|
||||||
.params()
|
.params()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, &module, impl_block.as_ref(), &generics, tr))
|
.map(|tr| Ty::from_hir(db, &resolver, tr))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let output = Ty::from_hir(
|
let output = Ty::from_hir(db, &resolver, signature.ret_type());
|
||||||
db,
|
|
||||||
&module,
|
|
||||||
impl_block.as_ref(),
|
|
||||||
&generics,
|
|
||||||
signature.ret_type(),
|
|
||||||
);
|
|
||||||
let sig = Arc::new(FnSig { input, output });
|
let sig = Arc::new(FnSig { input, output });
|
||||||
let substs = make_substs(&generics);
|
let substs = make_substs(&generics);
|
||||||
let name = def.name(db);
|
|
||||||
Ty::FnDef {
|
Ty::FnDef {
|
||||||
def,
|
def,
|
||||||
sig,
|
sig,
|
||||||
|
@ -764,13 +734,13 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def: TypableDef) -> Ty {
|
||||||
|
|
||||||
pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
|
pub(super) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty {
|
||||||
let parent_def = field.parent_def(db);
|
let parent_def = field.parent_def(db);
|
||||||
let (generics, module) = match parent_def {
|
let resolver = match parent_def {
|
||||||
VariantDef::Struct(it) => (it.generic_params(db), it.module(db)),
|
VariantDef::Struct(it) => it.resolver(db),
|
||||||
VariantDef::EnumVariant(it) => (it.parent_enum(db).generic_params(db), it.module(db)),
|
VariantDef::EnumVariant(it) => it.parent_enum(db).resolver(db),
|
||||||
};
|
};
|
||||||
let var_data = parent_def.variant_data(db);
|
let var_data = parent_def.variant_data(db);
|
||||||
let type_ref = &var_data.fields().unwrap()[field.id].type_ref;
|
let type_ref = &var_data.fields().unwrap()[field.id].type_ref;
|
||||||
Ty::from_hir(db, &module, None, &generics, type_ref)
|
Ty::from_hir(db, &resolver, type_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of type inference: A mapping from expressions and patterns to types.
|
/// The result of type inference: A mapping from expressions and patterns to types.
|
||||||
|
@ -814,9 +784,7 @@ impl Index<PatId> for InferenceResult {
|
||||||
struct InferenceContext<'a, D: HirDatabase> {
|
struct InferenceContext<'a, D: HirDatabase> {
|
||||||
db: &'a D,
|
db: &'a D,
|
||||||
body: Arc<Body>,
|
body: Arc<Body>,
|
||||||
scopes: Arc<ExprScopes>,
|
resolver: Resolver,
|
||||||
module: Module,
|
|
||||||
impl_block: Option<ImplBlock>,
|
|
||||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||||
method_resolutions: FxHashMap<ExprId, Function>,
|
method_resolutions: FxHashMap<ExprId, Function>,
|
||||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||||
|
@ -905,13 +873,7 @@ fn binary_op_rhs_expectation(op: BinaryOp, lhs_ty: Ty) -> Ty {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
fn new(
|
fn new(db: &'a D, body: Arc<Body>, resolver: Resolver) -> Self {
|
||||||
db: &'a D,
|
|
||||||
body: Arc<Body>,
|
|
||||||
scopes: Arc<ExprScopes>,
|
|
||||||
module: Module,
|
|
||||||
impl_block: Option<ImplBlock>,
|
|
||||||
) -> Self {
|
|
||||||
InferenceContext {
|
InferenceContext {
|
||||||
method_resolutions: FxHashMap::default(),
|
method_resolutions: FxHashMap::default(),
|
||||||
field_resolutions: FxHashMap::default(),
|
field_resolutions: FxHashMap::default(),
|
||||||
|
@ -921,9 +883,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
return_ty: Ty::Unknown, // set in collect_fn_signature
|
return_ty: Ty::Unknown, // set in collect_fn_signature
|
||||||
db,
|
db,
|
||||||
body,
|
body,
|
||||||
scopes,
|
resolver,
|
||||||
module,
|
|
||||||
impl_block,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -940,8 +900,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
*ty = resolved;
|
*ty = resolved;
|
||||||
}
|
}
|
||||||
InferenceResult {
|
InferenceResult {
|
||||||
method_resolutions: mem::replace(&mut self.method_resolutions, Default::default()),
|
method_resolutions: self.method_resolutions,
|
||||||
field_resolutions: mem::replace(&mut self.field_resolutions, Default::default()),
|
field_resolutions: self.field_resolutions,
|
||||||
type_of_expr: expr_types,
|
type_of_expr: expr_types,
|
||||||
type_of_pat: pat_types,
|
type_of_pat: pat_types,
|
||||||
}
|
}
|
||||||
|
@ -964,13 +924,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
|
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
|
||||||
// TODO provide generics of function
|
|
||||||
let generics = GenericParams::default();
|
|
||||||
let ty = Ty::from_hir(
|
let ty = Ty::from_hir(
|
||||||
self.db,
|
self.db,
|
||||||
&self.module,
|
// TODO use right resolver for block
|
||||||
self.impl_block.as_ref(),
|
&self.resolver,
|
||||||
&generics,
|
|
||||||
type_ref,
|
type_ref,
|
||||||
);
|
);
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
|
@ -1147,65 +1104,62 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_path_expr(&mut self, expr: ExprId, path: &Path) -> Option<Ty> {
|
fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> {
|
||||||
if path.is_ident() || path.is_self() {
|
let resolved = resolver.resolve_path(self.db, &path).take_values()?;
|
||||||
// resolve locally
|
match resolved {
|
||||||
let name = path.as_ident().cloned().unwrap_or_else(Name::self_param);
|
Resolution::Def(def) => {
|
||||||
if let Some(scope_entry) = self.scopes.resolve_local_name(expr, name) {
|
let typable: Option<TypableDef> = def.into();
|
||||||
let ty = self.type_of_pat.get(scope_entry.pat())?;
|
|
||||||
let ty = self.resolve_ty_as_possible(&mut vec![], ty.clone());
|
|
||||||
return Some(ty);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// resolve in module
|
|
||||||
let typable: Option<TypableDef> = self
|
|
||||||
.module
|
|
||||||
.resolve_path(self.db, &path)
|
|
||||||
.take_values()?
|
|
||||||
.into();
|
|
||||||
let typable = typable?;
|
let typable = typable?;
|
||||||
let ty = self.db.type_for_def(typable);
|
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||||
let generics = GenericParams::default();
|
let ty = self.db.type_for_def(typable).apply_substs(substs);
|
||||||
let substs = Ty::substs_from_path(
|
|
||||||
self.db,
|
|
||||||
&self.module,
|
|
||||||
self.impl_block.as_ref(),
|
|
||||||
&generics,
|
|
||||||
path,
|
|
||||||
typable,
|
|
||||||
);
|
|
||||||
let ty = ty.apply_substs(substs);
|
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
|
|
||||||
Some(ty)
|
Some(ty)
|
||||||
}
|
}
|
||||||
|
Resolution::LocalBinding(pat) => {
|
||||||
|
let ty = self.type_of_pat.get(pat)?;
|
||||||
|
let ty = self.resolve_ty_as_possible(&mut vec![], ty.clone());
|
||||||
|
Some(ty)
|
||||||
|
}
|
||||||
|
Resolution::GenericParam(..) => {
|
||||||
|
// generic params can't refer to values... yet
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Resolution::SelfType(_) => {
|
||||||
|
log::error!("path expr {:?} resolved to Self type in values ns", path);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantDef>) {
|
fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantDef>) {
|
||||||
let path = match path {
|
let path = match path {
|
||||||
Some(path) => path,
|
Some(path) => path,
|
||||||
None => return (Ty::Unknown, None),
|
None => return (Ty::Unknown, None),
|
||||||
};
|
};
|
||||||
let typable: Option<TypableDef> = self
|
let resolver = &self.resolver;
|
||||||
.module
|
let typable: Option<TypableDef> = match resolver.resolve_path(self.db, &path).take_types() {
|
||||||
.resolve_path(self.db, &path)
|
Some(Resolution::Def(def)) => def.into(),
|
||||||
.take_types()
|
Some(Resolution::LocalBinding(..)) => {
|
||||||
.and_then(|it| it.into());
|
// this cannot happen
|
||||||
|
log::error!("path resolved to local binding in type ns");
|
||||||
|
return (Ty::Unknown, None);
|
||||||
|
}
|
||||||
|
Some(Resolution::GenericParam(..)) => {
|
||||||
|
// generic params can't be used in struct literals
|
||||||
|
return (Ty::Unknown, None);
|
||||||
|
}
|
||||||
|
Some(Resolution::SelfType(..)) => {
|
||||||
|
// TODO this is allowed in an impl for a struct, handle this
|
||||||
|
return (Ty::Unknown, None);
|
||||||
|
}
|
||||||
|
None => return (Ty::Unknown, None),
|
||||||
|
};
|
||||||
let def = match typable {
|
let def = match typable {
|
||||||
None => return (Ty::Unknown, None),
|
None => return (Ty::Unknown, None),
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
};
|
};
|
||||||
// TODO remove the duplication between here and `Ty::from_path`?
|
// TODO remove the duplication between here and `Ty::from_path`?
|
||||||
// TODO provide generics of function
|
let substs = Ty::substs_from_path(self.db, resolver, path, def);
|
||||||
let generics = GenericParams::default();
|
|
||||||
let substs = Ty::substs_from_path(
|
|
||||||
self.db,
|
|
||||||
&self.module,
|
|
||||||
self.impl_block.as_ref(),
|
|
||||||
&generics,
|
|
||||||
path,
|
|
||||||
def,
|
|
||||||
);
|
|
||||||
match def {
|
match def {
|
||||||
TypableDef::Struct(s) => {
|
TypableDef::Struct(s) => {
|
||||||
let ty = type_for_struct(self.db, s);
|
let ty = type_for_struct(self.db, s);
|
||||||
|
@ -1303,12 +1257,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
path: ref p,
|
path: ref p,
|
||||||
args: ref fields,
|
args: ref fields,
|
||||||
} => self.infer_struct_pat(p.as_ref(), fields, expected),
|
} => self.infer_struct_pat(p.as_ref(), fields, expected),
|
||||||
Pat::Path(path) => self
|
Pat::Path(path) => {
|
||||||
.module
|
// TODO use correct resolver for the surrounding expression
|
||||||
.resolve_path(self.db, &path)
|
let resolver = self.resolver.clone();
|
||||||
.take_values()
|
self.infer_path_expr(&resolver, &path)
|
||||||
.and_then(|module_def| module_def.into())
|
.unwrap_or(Ty::Unknown)
|
||||||
.map_or(Ty::Unknown, |resolved| self.db.type_for_def(resolved)),
|
}
|
||||||
Pat::Bind {
|
Pat::Bind {
|
||||||
mode,
|
mode,
|
||||||
name: _name,
|
name: _name,
|
||||||
|
@ -1496,7 +1450,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
|
|
||||||
expected.ty
|
expected.ty
|
||||||
}
|
}
|
||||||
Expr::Path(p) => self.infer_path_expr(tgt_expr, p).unwrap_or(Ty::Unknown),
|
Expr::Path(p) => {
|
||||||
|
// TODO this could be more efficient...
|
||||||
|
let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr);
|
||||||
|
self.infer_path_expr(&resolver, p).unwrap_or(Ty::Unknown)
|
||||||
|
}
|
||||||
Expr::Continue => Ty::Never,
|
Expr::Continue => Ty::Never,
|
||||||
Expr::Break { expr } => {
|
Expr::Break { expr } => {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
|
@ -1730,10 +1688,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
pub fn infer(db: &impl HirDatabase, func: Function) -> Arc<InferenceResult> {
|
pub fn infer(db: &impl HirDatabase, func: Function) -> Arc<InferenceResult> {
|
||||||
db.check_canceled();
|
db.check_canceled();
|
||||||
let body = func.body(db);
|
let body = func.body(db);
|
||||||
let scopes = db.expr_scopes(func);
|
let resolver = func.resolver(db);
|
||||||
let module = func.module(db);
|
let mut ctx = InferenceContext::new(db, body, resolver);
|
||||||
let impl_block = func.impl_block(db);
|
|
||||||
let mut ctx = InferenceContext::new(db, body, scopes, module, impl_block);
|
|
||||||
|
|
||||||
let signature = func.signature(db);
|
let signature = func.signature(db);
|
||||||
ctx.collect_fn_signature(&signature);
|
ctx.collect_fn_signature(&signature);
|
||||||
|
|
|
@ -7,12 +7,10 @@ use std::sync::Arc;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
HirDatabase, module_tree::ModuleId, Module, ModuleDef, Crate, Name, Function, Trait,
|
HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function, Trait,
|
||||||
ids::TraitId,
|
ids::TraitId,
|
||||||
impl_block::{ImplId, ImplBlock, ImplItem},
|
impl_block::{ImplId, ImplBlock, ImplItem},
|
||||||
generics::GenericParams,
|
|
||||||
ty::{AdtDef, Ty},
|
ty::{AdtDef, Ty},
|
||||||
type_ref::TypeRef,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This is used as a key for indexing impls.
|
/// This is used as a key for indexing impls.
|
||||||
|
@ -85,17 +83,10 @@ impl CrateImplBlocks {
|
||||||
fn collect_recursive(&mut self, db: &impl HirDatabase, module: &Module) {
|
fn collect_recursive(&mut self, db: &impl HirDatabase, module: &Module) {
|
||||||
let module_impl_blocks = db.impls_in_module(module.clone());
|
let module_impl_blocks = db.impls_in_module(module.clone());
|
||||||
|
|
||||||
for (impl_id, impl_data) in module_impl_blocks.impls.iter() {
|
for (impl_id, _) in module_impl_blocks.impls.iter() {
|
||||||
let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id);
|
let impl_block = ImplBlock::from_id(Arc::clone(&module_impl_blocks), impl_id);
|
||||||
// TODO provide generics of impl
|
|
||||||
let generics = GenericParams::default();
|
let target_ty = impl_block.target_ty(db);
|
||||||
let target_ty = Ty::from_hir(
|
|
||||||
db,
|
|
||||||
&module,
|
|
||||||
Some(&impl_block),
|
|
||||||
&generics,
|
|
||||||
impl_data.target_type(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
|
if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
|
||||||
self.impls
|
self.impls
|
||||||
|
@ -104,16 +95,13 @@ impl CrateImplBlocks {
|
||||||
.push((module.module_id, impl_id));
|
.push((module.module_id, impl_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(TypeRef::Path(path)) = impl_data.target_trait() {
|
if let Some(tr) = impl_block.target_trait(db) {
|
||||||
let perns = module.resolve_path(db, path);
|
|
||||||
if let Some(ModuleDef::Trait(tr)) = perns.take_types() {
|
|
||||||
self.impls_by_trait
|
self.impls_by_trait
|
||||||
.entry(tr.id)
|
.entry(tr.id)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push((module.module_id, impl_id));
|
.push((module.module_id, impl_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for child in module.children(db) {
|
for child in module.children(db) {
|
||||||
self.collect_recursive(db, &child);
|
self.collect_recursive(db, &child);
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
---
|
---
|
||||||
created: "2019-01-26T18:16:16.530712344+00:00"
|
created: "2019-01-27T14:52:29.934503829+00:00"
|
||||||
creator: insta@0.5.2
|
creator: insta@0.5.2
|
||||||
expression: "&result"
|
expression: "&result"
|
||||||
source: crates/ra_hir/src/ty/tests.rs
|
source: crates/ra_hir/src/ty/tests.rs
|
||||||
---
|
---
|
||||||
[10; 11) 't': [unknown]
|
[10; 11) 't': T
|
||||||
[21; 26) '{ t }': [unknown]
|
[21; 26) '{ t }': T
|
||||||
[23; 24) 't': [unknown]
|
[23; 24) 't': T
|
||||||
[38; 98) '{ ...(1); }': ()
|
[38; 98) '{ ...(1); }': ()
|
||||||
[44; 46) 'id': fn id<u32>(T) -> T
|
[44; 46) 'id': fn id<u32>(T) -> T
|
||||||
[44; 52) 'id(1u32)': u32
|
[44; 52) 'id(1u32)': u32
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
---
|
---
|
||||||
created: "2019-01-26T17:46:03.866825843+00:00"
|
created: "2019-01-27T14:52:29.938713255+00:00"
|
||||||
creator: insta@0.5.2
|
creator: insta@0.5.2
|
||||||
expression: "&result"
|
expression: "&result"
|
||||||
source: crates/ra_hir/src/ty/tests.rs
|
source: crates/ra_hir/src/ty/tests.rs
|
||||||
|
@ -8,9 +8,9 @@ source: crates/ra_hir/src/ty/tests.rs
|
||||||
[65; 87) '{ ... }': [unknown]
|
[65; 87) '{ ... }': [unknown]
|
||||||
[75; 79) 'self': A<[unknown]>
|
[75; 79) 'self': A<[unknown]>
|
||||||
[75; 81) 'self.x': [unknown]
|
[75; 81) 'self.x': [unknown]
|
||||||
[99; 100) 't': [unknown]
|
[99; 100) 't': T
|
||||||
[110; 115) '{ t }': [unknown]
|
[110; 115) '{ t }': T
|
||||||
[112; 113) 't': [unknown]
|
[112; 113) 't': T
|
||||||
[135; 261) '{ ....x() }': i128
|
[135; 261) '{ ....x() }': i128
|
||||||
[146; 147) 'x': i32
|
[146; 147) 'x': i32
|
||||||
[150; 151) '1': i32
|
[150; 151) '1': i32
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
---
|
---
|
||||||
created: "2019-01-27T16:54:18.368427685+00:00"
|
created: "2019-01-27T20:38:32.153717698+00:00"
|
||||||
creator: insta@0.5.2
|
creator: insta@0.5.2
|
||||||
expression: "&result"
|
expression: "&result"
|
||||||
source: crates/ra_hir/src/ty/tests.rs
|
source: crates/ra_hir/src/ty/tests.rs
|
||||||
---
|
---
|
||||||
[10; 11) 'x': [unknown]
|
[10; 11) 'x': T
|
||||||
[21; 30) '{ x }': [unknown]
|
[21; 30) '{ x }': T
|
||||||
[27; 28) 'x': [unknown]
|
[27; 28) 'x': T
|
||||||
[44; 45) 'x': &[unknown]
|
[44; 45) 'x': &T
|
||||||
[56; 65) '{ x }': &[unknown]
|
[56; 65) '{ x }': &T
|
||||||
[62; 63) 'x': &[unknown]
|
[62; 63) 'x': &T
|
||||||
[77; 157) '{ ...(1); }': ()
|
[77; 157) '{ ...(1); }': ()
|
||||||
[87; 88) 'y': u32
|
[87; 88) 'y': u32
|
||||||
[91; 96) '10u32': u32
|
[91; 96) '10u32': u32
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
use join_to_string::join;
|
use join_to_string::join;
|
||||||
|
|
||||||
|
use hir::{Docs, Resolution};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
|
completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
|
||||||
};
|
};
|
||||||
|
|
||||||
use hir::Docs;
|
|
||||||
|
|
||||||
pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let (path, module) = match (&ctx.path_prefix, &ctx.module) {
|
let path = match &ctx.path_prefix {
|
||||||
(Some(path), Some(module)) => (path.clone(), module),
|
Some(path) => path.clone(),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let def_id = match module.resolve_path(ctx.db, &path).take_types() {
|
let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() {
|
||||||
Some(it) => it,
|
Some(Resolution::Def(def)) => def,
|
||||||
None => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
match def_id {
|
match def {
|
||||||
hir::ModuleDef::Module(module) => {
|
hir::ModuleDef::Module(module) => {
|
||||||
let module_scope = module.scope(ctx.db);
|
let module_scope = module.scope(ctx.db);
|
||||||
for (name, res) in module_scope.entries() {
|
for (name, res) in module_scope.entries() {
|
||||||
|
@ -24,7 +24,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
)
|
)
|
||||||
.from_resolution(ctx, res)
|
.from_resolution(ctx, &res.def.map(hir::Resolution::Def))
|
||||||
.add_to(acc);
|
.add_to(acc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,17 @@ mod tests {
|
||||||
check_completion(code, expected_completions, CompletionKind::Reference);
|
check_completion(code, expected_completions, CompletionKind::Reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore] // should not complete foo, which currently doesn't work
|
||||||
|
fn dont_complete_current_use() {
|
||||||
|
check_reference_completion(
|
||||||
|
"dont_complete_current_use",
|
||||||
|
r"
|
||||||
|
use self::foo<|>;
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_mod_with_docs() {
|
fn completes_mod_with_docs() {
|
||||||
check_reference_completion(
|
check_reference_completion(
|
||||||
|
|
|
@ -1,61 +1,18 @@
|
||||||
use rustc_hash::FxHashSet;
|
use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext};
|
||||||
use ra_syntax::ast::AstNode;
|
|
||||||
use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext};
|
|
||||||
|
|
||||||
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !ctx.is_trivial_path {
|
if !ctx.is_trivial_path {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let module = match &ctx.module {
|
let names = ctx.resolver.all_names();
|
||||||
Some(it) => it,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
if let Some(function) = &ctx.function {
|
|
||||||
let scopes = function.scopes(ctx.db);
|
|
||||||
complete_fn(acc, &scopes, ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
let module_scope = module.scope(ctx.db);
|
names.into_iter().for_each(|(name, res)| {
|
||||||
module_scope
|
|
||||||
.entries()
|
|
||||||
.filter(|(_name, res)| {
|
|
||||||
// For cases like `use self::foo<|>` don't suggest foo itself.
|
|
||||||
match res.import {
|
|
||||||
None => true,
|
|
||||||
Some(import) => {
|
|
||||||
let source = module.import_source(ctx.db, import);
|
|
||||||
!source.syntax().range().is_subrange(&ctx.leaf.range())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.for_each(|(name, res)| {
|
|
||||||
CompletionItem::new(
|
CompletionItem::new(
|
||||||
CompletionKind::Reference,
|
CompletionKind::Reference,
|
||||||
ctx.source_range(),
|
ctx.source_range(),
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
)
|
)
|
||||||
.from_resolution(ctx, res)
|
.from_resolution(ctx, &res)
|
||||||
.add_to(acc)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complete_fn(
|
|
||||||
acc: &mut Completions,
|
|
||||||
scopes: &hir::ScopesWithSyntaxMapping,
|
|
||||||
ctx: &CompletionContext,
|
|
||||||
) {
|
|
||||||
let mut shadowed = FxHashSet::default();
|
|
||||||
scopes
|
|
||||||
.scope_chain_for_offset(ctx.offset)
|
|
||||||
.flat_map(|scope| scopes.scopes.entries(scope).iter())
|
|
||||||
.filter(|entry| shadowed.insert(entry.name()))
|
|
||||||
.for_each(|entry| {
|
|
||||||
CompletionItem::new(
|
|
||||||
CompletionKind::Reference,
|
|
||||||
ctx.source_range(),
|
|
||||||
entry.name().to_string(),
|
|
||||||
)
|
|
||||||
.kind(CompletionItemKind::Binding)
|
|
||||||
.add_to(acc)
|
.add_to(acc)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -115,6 +72,30 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_generic_params() {
|
||||||
|
check_reference_completion(
|
||||||
|
"generic_params",
|
||||||
|
r"
|
||||||
|
fn quux<T>() {
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_generic_params_in_struct() {
|
||||||
|
check_reference_completion(
|
||||||
|
"generic_params_in_struct",
|
||||||
|
r"
|
||||||
|
struct X<T> {
|
||||||
|
x: <|>
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_module_items() {
|
fn completes_module_items() {
|
||||||
check_reference_completion(
|
check_reference_completion(
|
||||||
|
@ -174,5 +155,4 @@ mod tests {
|
||||||
fn completes_self_in_methods() {
|
fn completes_self_in_methods() {
|
||||||
check_reference_completion("self_in_methods", r"impl S { fn foo(&self) { <|> } }")
|
check_reference_completion("self_in_methods", r"impl S { fn foo(&self) { <|> } }")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
||||||
algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset},
|
algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset},
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
};
|
};
|
||||||
use hir::source_binder;
|
use hir::{source_binder, Resolver};
|
||||||
|
|
||||||
use crate::{db, FilePosition};
|
use crate::{db, FilePosition};
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ pub(crate) struct CompletionContext<'a> {
|
||||||
pub(super) db: &'a db::RootDatabase,
|
pub(super) db: &'a db::RootDatabase,
|
||||||
pub(super) offset: TextUnit,
|
pub(super) offset: TextUnit,
|
||||||
pub(super) leaf: &'a SyntaxNode,
|
pub(super) leaf: &'a SyntaxNode,
|
||||||
|
pub(super) resolver: Resolver,
|
||||||
pub(super) module: Option<hir::Module>,
|
pub(super) module: Option<hir::Module>,
|
||||||
pub(super) function: Option<hir::Function>,
|
pub(super) function: Option<hir::Function>,
|
||||||
pub(super) function_syntax: Option<&'a ast::FnDef>,
|
pub(super) function_syntax: Option<&'a ast::FnDef>,
|
||||||
|
@ -42,12 +43,14 @@ impl<'a> CompletionContext<'a> {
|
||||||
original_file: &'a SourceFile,
|
original_file: &'a SourceFile,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<CompletionContext<'a>> {
|
) -> Option<CompletionContext<'a>> {
|
||||||
|
let resolver = source_binder::resolver_for_position(db, position);
|
||||||
let module = source_binder::module_from_position(db, position);
|
let module = source_binder::module_from_position(db, position);
|
||||||
let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?;
|
let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?;
|
||||||
let mut ctx = CompletionContext {
|
let mut ctx = CompletionContext {
|
||||||
db,
|
db,
|
||||||
leaf,
|
leaf,
|
||||||
offset: position.offset,
|
offset: position.offset,
|
||||||
|
resolver,
|
||||||
module,
|
module,
|
||||||
function: None,
|
function: None,
|
||||||
function_syntax: None,
|
function_syntax: None,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use hir::{Docs, Documentation};
|
use hir::{Docs, Documentation, PerNs, Resolution};
|
||||||
use ra_syntax::TextRange;
|
use ra_syntax::{
|
||||||
|
TextRange,
|
||||||
|
};
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
|
@ -48,6 +50,7 @@ pub enum CompletionItemKind {
|
||||||
Trait,
|
Trait,
|
||||||
TypeAlias,
|
TypeAlias,
|
||||||
Method,
|
Method,
|
||||||
|
TypeParam,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
@ -207,23 +210,34 @@ impl Builder {
|
||||||
pub(super) fn from_resolution(
|
pub(super) fn from_resolution(
|
||||||
mut self,
|
mut self,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
resolution: &hir::Resolution,
|
resolution: &PerNs<Resolution>,
|
||||||
) -> Builder {
|
) -> Builder {
|
||||||
let def = resolution.def.take_types().or(resolution.def.take_values());
|
use hir::ModuleDef::*;
|
||||||
|
|
||||||
|
let def = resolution
|
||||||
|
.as_ref()
|
||||||
|
.take_types()
|
||||||
|
.or(resolution.as_ref().take_values());
|
||||||
let def = match def {
|
let def = match def {
|
||||||
None => return self,
|
None => return self,
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
};
|
};
|
||||||
let (kind, docs) = match def {
|
let (kind, docs) = match def {
|
||||||
hir::ModuleDef::Module(it) => (CompletionItemKind::Module, it.docs(ctx.db)),
|
Resolution::Def(Module(it)) => (CompletionItemKind::Module, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::Function(func) => return self.from_function(ctx, func),
|
Resolution::Def(Function(func)) => return self.from_function(ctx, *func),
|
||||||
hir::ModuleDef::Struct(it) => (CompletionItemKind::Struct, it.docs(ctx.db)),
|
Resolution::Def(Struct(it)) => (CompletionItemKind::Struct, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::Enum(it) => (CompletionItemKind::Enum, it.docs(ctx.db)),
|
Resolution::Def(Enum(it)) => (CompletionItemKind::Enum, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::EnumVariant(it) => (CompletionItemKind::EnumVariant, it.docs(ctx.db)),
|
Resolution::Def(EnumVariant(it)) => (CompletionItemKind::EnumVariant, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::Const(it) => (CompletionItemKind::Const, it.docs(ctx.db)),
|
Resolution::Def(Const(it)) => (CompletionItemKind::Const, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::Static(it) => (CompletionItemKind::Static, it.docs(ctx.db)),
|
Resolution::Def(Static(it)) => (CompletionItemKind::Static, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::Trait(it) => (CompletionItemKind::Trait, it.docs(ctx.db)),
|
Resolution::Def(Trait(it)) => (CompletionItemKind::Trait, it.docs(ctx.db)),
|
||||||
hir::ModuleDef::Type(it) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)),
|
Resolution::Def(Type(it)) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)),
|
||||||
|
Resolution::GenericParam(..) => (CompletionItemKind::TypeParam, None),
|
||||||
|
Resolution::LocalBinding(..) => (CompletionItemKind::Binding, None),
|
||||||
|
Resolution::SelfType(..) => (
|
||||||
|
CompletionItemKind::TypeParam, // (does this need its own kind?)
|
||||||
|
None,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
self.kind = Some(kind);
|
self.kind = Some(kind);
|
||||||
self.documentation = docs;
|
self.documentation = docs;
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
created: "2019-02-01T22:20:40.580128393+00:00"
|
||||||
|
creator: insta@0.5.3
|
||||||
|
expression: kind_completions
|
||||||
|
source: crates/ra_ide_api/src/completion/completion_item.rs
|
||||||
|
---
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
completion_kind: Reference,
|
||||||
|
label: "T",
|
||||||
|
kind: Some(
|
||||||
|
TypeParam
|
||||||
|
),
|
||||||
|
detail: None,
|
||||||
|
documentation: None,
|
||||||
|
lookup: None,
|
||||||
|
insert_text: None,
|
||||||
|
insert_text_format: PlainText,
|
||||||
|
source_range: [44; 44),
|
||||||
|
text_edit: None
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
completion_kind: Reference,
|
||||||
|
label: "quux",
|
||||||
|
kind: Some(
|
||||||
|
Function
|
||||||
|
),
|
||||||
|
detail: Some(
|
||||||
|
"fn quux<T>()"
|
||||||
|
),
|
||||||
|
documentation: None,
|
||||||
|
lookup: None,
|
||||||
|
insert_text: Some(
|
||||||
|
"quux()$0"
|
||||||
|
),
|
||||||
|
insert_text_format: Snippet,
|
||||||
|
source_range: [44; 44),
|
||||||
|
text_edit: None
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
created: "2019-02-01T22:23:21.508620224+00:00"
|
||||||
|
creator: insta@0.5.3
|
||||||
|
expression: kind_completions
|
||||||
|
source: crates/ra_ide_api/src/completion/completion_item.rs
|
||||||
|
---
|
||||||
|
[
|
||||||
|
CompletionItem {
|
||||||
|
completion_kind: Reference,
|
||||||
|
label: "T",
|
||||||
|
kind: Some(
|
||||||
|
TypeParam
|
||||||
|
),
|
||||||
|
detail: None,
|
||||||
|
documentation: None,
|
||||||
|
lookup: None,
|
||||||
|
insert_text: None,
|
||||||
|
insert_text_format: PlainText,
|
||||||
|
source_range: [46; 46),
|
||||||
|
text_edit: None
|
||||||
|
},
|
||||||
|
CompletionItem {
|
||||||
|
completion_kind: Reference,
|
||||||
|
label: "X",
|
||||||
|
kind: Some(
|
||||||
|
Struct
|
||||||
|
),
|
||||||
|
detail: None,
|
||||||
|
documentation: None,
|
||||||
|
lookup: None,
|
||||||
|
insert_text: None,
|
||||||
|
insert_text_format: PlainText,
|
||||||
|
source_range: [46; 46),
|
||||||
|
text_edit: None
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,10 +1,24 @@
|
||||||
---
|
---
|
||||||
created: "2019-01-23T05:27:32.422259+00:00"
|
created: "2019-01-27T20:17:10.051725945+00:00"
|
||||||
creator: insta@0.4.0
|
creator: insta@0.5.2
|
||||||
expression: kind_completions
|
expression: kind_completions
|
||||||
source: crates/ra_ide_api/src/completion/completion_item.rs
|
source: crates/ra_ide_api/src/completion/completion_item.rs
|
||||||
---
|
---
|
||||||
[
|
[
|
||||||
|
CompletionItem {
|
||||||
|
completion_kind: Reference,
|
||||||
|
label: "Self",
|
||||||
|
kind: Some(
|
||||||
|
TypeParam
|
||||||
|
),
|
||||||
|
detail: None,
|
||||||
|
documentation: None,
|
||||||
|
lookup: None,
|
||||||
|
insert_text: None,
|
||||||
|
insert_text_format: PlainText,
|
||||||
|
source_range: [25; 25),
|
||||||
|
text_edit: None
|
||||||
|
},
|
||||||
CompletionItem {
|
CompletionItem {
|
||||||
completion_kind: Reference,
|
completion_kind: Reference,
|
||||||
label: "self",
|
label: "self",
|
||||||
|
|
|
@ -4,6 +4,7 @@ use ra_syntax::{
|
||||||
algo::find_node_at_offset,
|
algo::find_node_at_offset,
|
||||||
};
|
};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
use hir::Resolution;
|
||||||
|
|
||||||
use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
|
use crate::{FilePosition, NavigationTarget, db::RootDatabase, RangeInfo};
|
||||||
|
|
||||||
|
@ -48,14 +49,7 @@ pub(crate) fn reference_definition(
|
||||||
if let Some(function) =
|
if let Some(function) =
|
||||||
hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
|
hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
|
||||||
{
|
{
|
||||||
let scope = function.scopes(db);
|
// Check if it is a method
|
||||||
// First try to resolve the symbol locally
|
|
||||||
if let Some(entry) = scope.resolve_local_name(name_ref) {
|
|
||||||
let nav = NavigationTarget::from_scope_entry(file_id, &entry);
|
|
||||||
return Exact(nav);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Next check if it is a method
|
|
||||||
if let Some(method_call) = name_ref
|
if let Some(method_call) = name_ref
|
||||||
.syntax()
|
.syntax()
|
||||||
.parent()
|
.parent()
|
||||||
|
@ -86,19 +80,37 @@ pub(crate) fn reference_definition(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Then try module name resolution
|
// Try name resolution
|
||||||
if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())
|
let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax());
|
||||||
{
|
|
||||||
if let Some(path) = name_ref
|
if let Some(path) = name_ref
|
||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(ast::Path::cast)
|
.find_map(ast::Path::cast)
|
||||||
.and_then(hir::Path::from_ast)
|
.and_then(hir::Path::from_ast)
|
||||||
{
|
{
|
||||||
let resolved = module.resolve_path(db, &path);
|
let resolved = resolver.resolve_path(db, &path);
|
||||||
if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
|
match resolved.clone().take_types().or(resolved.take_values()) {
|
||||||
return Exact(NavigationTarget::from_def(db, def_id));
|
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 syntax_mapping = body.syntax_mapping(db);
|
||||||
|
let ptr = syntax_mapping
|
||||||
|
.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 nav = NavigationTarget::from_scope_entry(file_id, name, ptr);
|
||||||
|
return Exact(nav);
|
||||||
}
|
}
|
||||||
|
Some(Resolution::GenericParam(..)) => {
|
||||||
|
// TODO go to the generic param def
|
||||||
|
}
|
||||||
|
Some(Resolution::SelfType(_impl_block)) => {
|
||||||
|
// TODO go to the implemented type
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If that fails try the index based approach.
|
// If that fails try the index based approach.
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use ra_db::FileId;
|
use ra_db::FileId;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
SyntaxNode, AstNode, SmolStr, TextRange, ast,
|
SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast,
|
||||||
SyntaxKind::{self, NAME},
|
SyntaxKind::{self, NAME},
|
||||||
};
|
};
|
||||||
use hir::{ModuleSource, FieldSource};
|
use hir::{ModuleSource, FieldSource, Name};
|
||||||
|
|
||||||
use crate::{FileSymbol, db::RootDatabase};
|
use crate::{FileSymbol, db::RootDatabase};
|
||||||
|
|
||||||
|
@ -58,12 +58,13 @@ impl NavigationTarget {
|
||||||
|
|
||||||
pub(crate) fn from_scope_entry(
|
pub(crate) fn from_scope_entry(
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
entry: &hir::ScopeEntryWithSyntax,
|
name: Name,
|
||||||
|
ptr: SyntaxNodePtr,
|
||||||
) -> NavigationTarget {
|
) -> NavigationTarget {
|
||||||
NavigationTarget {
|
NavigationTarget {
|
||||||
file_id,
|
file_id,
|
||||||
name: entry.name().to_string().into(),
|
name: name.to_string().into(),
|
||||||
full_range: entry.ptr().range(),
|
full_range: ptr.range(),
|
||||||
focus_range: None,
|
focus_range: None,
|
||||||
kind: NAME,
|
kind: NAME,
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ impl Conv for CompletionItemKind {
|
||||||
CompletionItemKind::Const => Constant,
|
CompletionItemKind::Const => Constant,
|
||||||
CompletionItemKind::Static => Value,
|
CompletionItemKind::Static => Value,
|
||||||
CompletionItemKind::Method => Method,
|
CompletionItemKind::Method => Method,
|
||||||
|
CompletionItemKind::TypeParam => TypeParameter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue