Skip to content

Commit 0acea1c

Browse files
authored
feat: infer associate functions (#575)
1 parent 6537d40 commit 0acea1c

File tree

5 files changed

+134
-14
lines changed

5 files changed

+134
-14
lines changed

crates/mun_hir/src/method_resolution.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ impl InherentImpls {
161161
self.map.values().flatten().copied()
162162
}
163163

164-
// Returns all implementations defined for the specified type.
164+
/// Returns all implementations defined for the specified type.
165165
pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] {
166166
match self_ty.interned() {
167167
TyKind::Struct(s) => self.map.get(&s.id).map_or(&[], AsRef::as_ref),

crates/mun_hir/src/ty/infer.rs

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{ops::Index, sync::Arc};
22

33
use la_arena::ArenaMap;
4+
use mun_hir_input::ModuleId;
45
use rustc_hash::FxHashSet;
56

67
use crate::{
@@ -24,8 +25,10 @@ mod unify;
2425

2526
use crate::{
2627
expr::{LiteralFloat, LiteralFloatKind, LiteralInt, LiteralIntKind},
28+
has_module::HasModule,
2729
ids::DefWithBodyId,
28-
resolve::{resolver_for_expr, HasResolver},
30+
method_resolution::lookup_method,
31+
resolve::{resolver_for_expr, HasResolver, ResolveValueResult},
2932
ty::{
3033
primitives::{FloatTy, IntTy},
3134
TyKind,
@@ -184,6 +187,13 @@ impl<'a> InferenceResultBuilder<'a> {
184187
}
185188
}
186189

190+
/// Returns the module in which the body is defined.
191+
pub fn module(&self) -> ModuleId {
192+
match self.body.owner() {
193+
DefWithBodyId::FunctionId(func) => func.module(self.db.upcast()),
194+
}
195+
}
196+
187197
/// Associate the given `ExprId` with the specified `Ty`.
188198
fn set_expr_type(&mut self, expr: ExprId, ty: Ty) {
189199
self.type_of_expr.insert(expr, ty);
@@ -718,25 +728,73 @@ impl<'a> InferenceResultBuilder<'a> {
718728
}
719729
}
720730

731+
fn resolve_assoc_item(
732+
&mut self,
733+
def: TypeNs,
734+
path: &Path,
735+
remaining_index: usize,
736+
id: ExprId,
737+
) -> Option<ValueNs> {
738+
// We can only resolve the last element of the path.
739+
let name = if remaining_index == path.segments.len() - 1 {
740+
&path.segments[remaining_index]
741+
} else {
742+
return None;
743+
};
744+
745+
// Infer the type of the definitions
746+
let type_for_def_fn = |def| self.db.type_for_def(def, Namespace::Types);
747+
let root_ty = match def {
748+
TypeNs::SelfType(id) => self.db.type_for_impl_self(id),
749+
TypeNs::StructId(id) => type_for_def_fn(TypableDef::Struct(id.into())),
750+
TypeNs::TypeAliasId(id) => type_for_def_fn(TypableDef::TypeAlias(id.into())),
751+
TypeNs::PrimitiveType(id) => type_for_def_fn(TypableDef::PrimitiveType(id)),
752+
};
753+
754+
// Resolve the value.
755+
let function_id = match lookup_method(self.db, &root_ty, self.module(), name) {
756+
Ok(value) => value,
757+
Err(Some(value)) => {
758+
self.diagnostics
759+
.push(InferenceDiagnostic::PathIsPrivate { id });
760+
value
761+
}
762+
_ => return None,
763+
};
764+
765+
Some(ValueNs::FunctionId(function_id))
766+
}
767+
768+
fn resolve_value_path_inner(
769+
&mut self,
770+
resolver: &Resolver,
771+
path: &Path,
772+
id: ExprId,
773+
) -> Option<ValueNs> {
774+
let value_or_partial = resolver.resolve_path_as_value(self.db.upcast(), path)?;
775+
match value_or_partial {
776+
ResolveValueResult::ValueNs(it, vis) => {
777+
if !vis.is_visible_from(self.db, self.module()) {
778+
self.diagnostics
779+
.push(diagnostics::InferenceDiagnostic::PathIsPrivate { id });
780+
}
781+
782+
Some(it)
783+
}
784+
ResolveValueResult::Partial(def, remaining_index) => {
785+
self.resolve_assoc_item(def, path, remaining_index, id)
786+
}
787+
}
788+
}
789+
721790
fn infer_path_expr(
722791
&mut self,
723792
resolver: &Resolver,
724793
path: &Path,
725794
id: ExprId,
726795
check_params: &CheckParams,
727796
) -> Option<Ty> {
728-
if let Some((value, vis)) = resolver.resolve_path_as_value_fully(self.db.upcast(), path) {
729-
// Check visibility of this item
730-
if !vis.is_visible_from(
731-
self.db,
732-
self.resolver
733-
.module()
734-
.expect("resolver must have a module to be able to resolve modules"),
735-
) {
736-
self.diagnostics
737-
.push(diagnostics::InferenceDiagnostic::PathIsPrivate { id });
738-
}
739-
797+
if let Some(value) = self.resolve_value_path_inner(resolver, path, id) {
740798
// Match based on what type of value we found
741799
match value {
742800
ValueNs::ImplSelf(i) => {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
source: crates/mun_hir/src/ty/tests.rs
3+
expression: "infer(r#\"\n //- /foo.mun\n pub struct Foo {\n a: i32\n }\n\n impl Foo {\n fn new(){}\n }\n\n //- /mod.mun\n fn main() {\n foo::Foo::new();\n }\n \"#)"
4+
---
5+
16..29: access of private type
6+
10..34 '{ ...w(); }': ()
7+
16..29 'foo::Foo::new': function new() -> ()
8+
16..31 'foo::Foo::new()': ()
9+
54..56 '{}': ()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
source: crates/mun_hir/src/ty/tests.rs
3+
expression: "infer(r#\"\n struct Foo {\n a: i32\n }\n\n impl Foo {\n fn new() -> Self {\n Self { a: 3 }\n }\n }\n\n fn main() {\n let a = Foo::new();\n }\n \"#)"
4+
---
5+
102..129 '{ ...w(); }': ()
6+
112..113 'a': Foo
7+
116..124 'Foo::new': function new() -> Foo
8+
116..126 'Foo::new()': Foo
9+
59..88 '{ ... }': Foo
10+
69..82 'Self { a: 3 }': Foo
11+
79..80 '3': i32

crates/mun_hir/src/ty/tests.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,48 @@ fn infer_self_field() {
696696
));
697697
}
698698

699+
#[test]
700+
fn infer_assoc_function() {
701+
insta::assert_snapshot!(infer(
702+
r#"
703+
struct Foo {
704+
a: i32
705+
}
706+
707+
impl Foo {
708+
fn new() -> Self {
709+
Self { a: 3 }
710+
}
711+
}
712+
713+
fn main() {
714+
let a = Foo::new();
715+
}
716+
"#
717+
));
718+
}
719+
720+
#[test]
721+
fn infer_access_hidden_assoc_function() {
722+
insta::assert_snapshot!(infer(
723+
r#"
724+
//- /foo.mun
725+
pub struct Foo {
726+
a: i32
727+
}
728+
729+
impl Foo {
730+
fn new(){}
731+
}
732+
733+
//- /mod.mun
734+
fn main() {
735+
foo::Foo::new();
736+
}
737+
"#
738+
));
739+
}
740+
699741
#[test]
700742
fn infer_basics() {
701743
insta::assert_snapshot!(infer(

0 commit comments

Comments
 (0)