Add SelfParam to code_model

This commit is contained in:
Aleksey Kladov 2020-08-19 15:16:24 +02:00
parent a3b0a3aeb8
commit b9b4693ce3
9 changed files with 62 additions and 43 deletions

View File

@ -666,23 +666,11 @@ impl Function {
db.function_data(self.id).name.clone() db.function_data(self.id).name.clone()
} }
pub fn has_self_param(self, db: &dyn HirDatabase) -> bool { pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
db.function_data(self.id).has_self_param if !db.function_data(self.id).has_self_param {
}
pub fn mutability_of_self_param(self, db: &dyn HirDatabase) -> Option<Mutability> {
let func_data = db.function_data(self.id);
if !func_data.has_self_param {
return None; return None;
} }
Some(SelfParam { func: self.id })
func_data.params.first().and_then(|param| {
if let TypeRef::Reference(_, mutability) = param {
Some(*mutability)
} else {
None
}
})
} }
pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> { pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> {
@ -698,6 +686,41 @@ impl Function {
} }
} }
// Note: logically, this belongs to `hir_ty`, but we are not using it there yet.
pub enum Access {
Shared,
Exclusive,
Owned,
}
impl From<Mutability> for Access {
fn from(mutability: Mutability) -> Access {
match mutability {
Mutability::Shared => Access::Shared,
Mutability::Mut => Access::Exclusive,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SelfParam {
func: FunctionId,
}
impl SelfParam {
pub fn access(self, db: &dyn HirDatabase) -> Access {
let func_data = db.function_data(self.func);
func_data
.params
.first()
.map(|param| match *param {
TypeRef::Reference(_, mutability) => mutability.into(),
_ => Access::Owned,
})
.unwrap_or(Access::Owned)
}
}
impl HasVisibility for Function { impl HasVisibility for Function {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility { fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
let function_data = db.function_data(self.id); let function_data = db.function_data(self.id);

View File

@ -32,10 +32,10 @@ mod has_source;
pub use crate::{ pub use crate::{
code_model::{ code_model::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind, Const, Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind,
Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, Const, Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource,
GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Function, GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef,
Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
}, },
has_source::HasSource, has_source::HasSource,
semantics::{original_range, PathResolution, SelfKind, Semantics, SemanticsScope}, semantics::{original_range, PathResolution, SelfKind, Semantics, SemanticsScope},

View File

@ -21,13 +21,13 @@ use syntax::{
}; };
use crate::{ use crate::{
code_model::Access,
db::HirDatabase, db::HirDatabase,
diagnostics::Diagnostic, diagnostics::Diagnostic,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, SourceAnalyzer}, source_analyzer::{resolve_hir_path, SourceAnalyzer},
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef, Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
VariantDef,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -627,9 +627,11 @@ impl<'db> SemanticsImpl<'db> {
} }
let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; let func = self.resolve_method_call(&method_call_expr).map(Function::from)?;
let is_unsafe = func.has_self_param(self.db) let res = match func.self_param(self.db)?.access(self.db) {
&& matches!(func.params(self.db).first(), Some(TypeRef::Reference(..))); Access::Shared | Access::Exclusive => true,
Some(is_unsafe) Access::Owned => false,
};
Some(res)
}) })
.unwrap_or(false) .unwrap_or(false)
} }

View File

@ -48,7 +48,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
let mut seen_methods = FxHashSet::default(); let mut seen_methods = FxHashSet::default();
let traits_in_scope = ctx.scope.traits_in_scope(); let traits_in_scope = ctx.scope.traits_in_scope();
receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
if func.has_self_param(ctx.db) if func.self_param(ctx.db).is_some()
&& ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
&& seen_methods.insert(func.name(ctx.db)) && seen_methods.insert(func.name(ctx.db))
{ {

View File

@ -136,7 +136,7 @@ fn add_function_impl(
.lookup_by(fn_name) .lookup_by(fn_name)
.set_documentation(func.docs(ctx.db)); .set_documentation(func.docs(ctx.db));
let completion_kind = if func.has_self_param(ctx.db) { let completion_kind = if func.self_param(ctx.db).is_some() {
CompletionItemKind::Method CompletionItemKind::Method
} else { } else {
CompletionItemKind::Function CompletionItemKind::Function

View File

@ -191,14 +191,12 @@ impl Completions {
func: hir::Function, func: hir::Function,
local_name: Option<String>, local_name: Option<String>,
) { ) {
let has_self_param = func.has_self_param(ctx.db);
let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
let ast_node = func.source(ctx.db).value; let ast_node = func.source(ctx.db).value;
let mut builder = let mut builder =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone()) CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone())
.kind(if has_self_param { .kind(if func.self_param(ctx.db).is_some() {
CompletionItemKind::Method CompletionItemKind::Method
} else { } else {
CompletionItemKind::Function CompletionItemKind::Function

View File

@ -4,7 +4,7 @@ mod injection;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use hir::{Mutability, Name, SelfKind, Semantics, VariantDef}; use hir::{Name, SelfKind, Semantics, VariantDef};
use ide_db::{ use ide_db::{
defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
@ -761,17 +761,13 @@ fn highlight_name(
h |= HighlightModifier::Unsafe; h |= HighlightModifier::Unsafe;
} }
return if func.has_self_param(db) { match func.self_param(db) {
match func.mutability_of_self_param(db) { None => h,
Some(mutability) => match mutability { Some(self_param) => match self_param.access(db) {
Mutability::Mut => h | HighlightModifier::Mutable, hir::Access::Exclusive => h | HighlightModifier::Mutable,
Mutability::Shared => h, hir::Access::Shared | hir::Access::Owned => h,
}, },
None => h, }
}
} else {
h
};
}); });
} }
hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,

View File

@ -545,7 +545,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
// If the function we're calling takes a self parameter, then we store additional // If the function we're calling takes a self parameter, then we store additional
// information on the placeholder match about autoderef and autoref. This allows us to use // information on the placeholder match about autoderef and autoref. This allows us to use
// the placeholder in a context where autoderef and autoref don't apply. // the placeholder in a context where autoderef and autoref don't apply.
if code_resolved_function.has_self_param(self.sema.db) { if code_resolved_function.self_param(self.sema.db).is_some() {
if let (Some(pattern_type), Some(expr)) = (&pattern_ufcs.qualifier_type, &code.expr()) { if let (Some(pattern_type), Some(expr)) = (&pattern_ufcs.qualifier_type, &code.expr()) {
let deref_count = self.check_expr_type(pattern_type, expr)?; let deref_count = self.check_expr_type(pattern_type, expr)?;
let pattern_receiver = pattern_args.next(); let pattern_receiver = pattern_args.next();

View File

@ -165,7 +165,7 @@ impl Resolver<'_, '_> {
fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool { fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool {
match resolution { match resolution {
hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => { hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => {
if function.has_self_param(self.resolution_scope.scope.db) { if function.self_param(self.resolution_scope.scope.db).is_some() {
// If we don't use this path resolution, then we won't be able to match method // If we don't use this path resolution, then we won't be able to match method
// calls. e.g. `Foo::bar($s)` should match `x.bar()`. // calls. e.g. `Foo::bar($s)` should match `x.bar()`.
true true