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 @@ pub fn name(self, db: &dyn HirDatabase) -> Name {
db.function_data(self.id).name.clone()
}
pub fn has_self_param(self, db: &dyn HirDatabase) -> bool {
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 {
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
if !db.function_data(self.id).has_self_param {
return None;
}
func_data.params.first().and_then(|param| {
if let TypeRef::Reference(_, mutability) = param {
Some(*mutability)
} else {
None
}
})
Some(SelfParam { func: self.id })
}
pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> {
@ -698,6 +686,41 @@ pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
}
}
// 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 {
fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
let function_data = db.function_data(self.id);

View File

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

View File

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

View File

@ -136,7 +136,7 @@ fn add_function_impl(
.lookup_by(fn_name)
.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
} else {
CompletionItemKind::Function

View File

@ -191,14 +191,12 @@ pub(crate) fn add_function(
func: hir::Function,
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 ast_node = func.source(ctx.db).value;
let mut builder =
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
} else {
CompletionItemKind::Function

View File

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

View File

@ -545,7 +545,7 @@ fn attempt_match_ufcs_to_method_call(
// 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
// 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()) {
let deref_count = self.check_expr_type(pattern_type, expr)?;
let pattern_receiver = pattern_args.next();

View File

@ -165,7 +165,7 @@ fn path_contains_placeholder(&self, path: &ast::Path) -> bool {
fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool {
match resolution {
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
// calls. e.g. `Foo::bar($s)` should match `x.bar()`.
true