Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/infer/expr.rs95
-rw-r--r--crates/hir/src/semantics.rs32
-rw-r--r--crates/hir/src/source_analyzer.rs47
-rw-r--r--crates/ide-db/src/defs.rs11
-rw-r--r--crates/ide/src/hover/tests.rs27
5 files changed, 140 insertions, 72 deletions
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 24026202b7..a5e77a12d8 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -579,7 +579,7 @@ impl InferenceContext<'_> {
}
ty
}
- Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name),
+ Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
Expr::Await { expr } => {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
@@ -1456,7 +1456,13 @@ impl InferenceContext<'_> {
})
}
- fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
+ fn infer_field_access(
+ &mut self,
+ tgt_expr: ExprId,
+ receiver: ExprId,
+ name: &Name,
+ expected: &Expectation,
+ ) -> Ty {
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
if name.is_missing() {
@@ -1482,28 +1488,42 @@ impl InferenceContext<'_> {
ty
}
None => {
- // no field found,
- let method_with_same_name_exists = {
- self.get_traits_in_scope();
-
- let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
- method_resolution::lookup_method(
- self.db,
- &canonicalized_receiver.value,
- self.table.trait_env.clone(),
- self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
- VisibleFromModule::Filter(self.resolver.module()),
- name,
- )
- .is_some()
- };
+ // no field found, lets attempt to resolve it like a function so that IDE things
+ // work out while people are typing
+ let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
+ let resolved = method_resolution::lookup_method(
+ self.db,
+ &canonicalized_receiver.value,
+ self.table.trait_env.clone(),
+ self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
+ VisibleFromModule::Filter(self.resolver.module()),
+ name,
+ );
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
expr: tgt_expr,
- receiver: receiver_ty,
+ receiver: receiver_ty.clone(),
name: name.clone(),
- method_with_same_name_exists,
+ method_with_same_name_exists: resolved.is_some(),
});
- self.err_ty()
+ match resolved {
+ Some((adjust, func, _)) => {
+ let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
+ let generics = generics(self.db.upcast(), func.into());
+ let substs = self.substs_for_method_call(generics, None);
+ self.write_expr_adj(receiver, adjustments);
+ self.write_method_resolution(tgt_expr, func, substs.clone());
+
+ self.check_method_call(
+ tgt_expr,
+ &[],
+ self.db.value_ty(func.into()),
+ substs,
+ ty,
+ expected,
+ )
+ }
+ None => self.err_ty(),
+ }
}
}
}
@@ -1517,7 +1537,7 @@ impl InferenceContext<'_> {
generic_args: Option<&GenericArgs>,
expected: &Expectation,
) -> Ty {
- let receiver_ty = self.infer_expr(receiver, &Expectation::none());
+ let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
let resolved = method_resolution::lookup_method(
@@ -1568,23 +1588,32 @@ impl InferenceContext<'_> {
)
}
};
+ self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
+ }
+
+ fn check_method_call(
+ &mut self,
+ tgt_expr: ExprId,
+ args: &[ExprId],
+ method_ty: Binders<Ty>,
+ substs: Substitution,
+ receiver_ty: Ty,
+ expected: &Expectation,
+ ) -> Ty {
let method_ty = method_ty.substitute(Interner, &substs);
self.register_obligations_for_call(&method_ty);
- let (formal_receiver_ty, param_tys, ret_ty, is_varargs) =
+ let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) =
match method_ty.callable_sig(self.db) {
- Some(sig) => {
+ Some(sig) => (
if !sig.params().is_empty() {
- (
- sig.params()[0].clone(),
- sig.params()[1..].to_vec(),
- sig.ret().clone(),
- sig.is_varargs,
- )
+ (sig.params()[0].clone(), sig.params()[1..].to_vec())
} else {
- (self.err_ty(), Vec::new(), sig.ret().clone(), sig.is_varargs)
- }
- }
- None => (self.err_ty(), Vec::new(), self.err_ty(), true),
+ (self.err_ty(), Vec::new())
+ },
+ sig.ret().clone(),
+ sig.is_varargs,
+ ),
+ None => ((self.err_ty(), Vec::new()), self.err_ty(), true),
};
self.unify(&formal_receiver_ty, &receiver_ty);
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index cb52a71d96..92fa76c96f 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -17,7 +17,7 @@ use hir_def::{
nameres::MacroSubNs,
resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability,
- AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
+ AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
};
use hir_expand::{
db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, InMacroFile, MacroCallId,
@@ -198,20 +198,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
}
- pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
- self.imp.resolve_method_call(call).map(Function::from)
- }
-
- /// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
- pub fn resolve_method_call_field_fallback(
- &self,
- call: &ast::MethodCallExpr,
- ) -> Option<Either<Function, Field>> {
- self.imp
- .resolve_method_call_fallback(call)
- .map(|it| it.map_left(Function::from).map_right(Field::from))
- }
-
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
}
@@ -1048,14 +1034,15 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
}
- fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
+ pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
self.analyze(call.syntax())?.resolve_method_call(self.db, call)
}
- fn resolve_method_call_fallback(
+ /// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
+ pub fn resolve_method_call_fallback(
&self,
call: &ast::MethodCallExpr,
- ) -> Option<Either<FunctionId, FieldId>> {
+ ) -> Option<Either<Function, Field>> {
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
}
@@ -1087,6 +1074,13 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(field.syntax())?.resolve_field(self.db, field)
}
+ pub fn resolve_field_fallback(
+ &self,
+ field: &ast::FieldExpr,
+ ) -> Option<Either<Field, Function>> {
+ self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
+ }
+
pub fn resolve_record_field(
&self,
field: &ast::RecordExprField,
@@ -1298,7 +1292,7 @@ impl<'db> SemanticsImpl<'db> {
return None;
}
- let func = self.resolve_method_call(method_call_expr).map(Function::from)?;
+ let func = self.resolve_method_call(method_call_expr)?;
let res = match func.self_param(self.db)?.access(self.db) {
Access::Shared | Access::Exclusive => true,
Access::Owned => false,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 7a31e6df1f..73db6f8f0b 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -280,25 +280,49 @@ impl SourceAnalyzer {
&self,
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
- ) -> Option<FunctionId> {
+ ) -> Option<Function> {
let expr_id = self.expr_id(db, &call.clone().into())?;
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
- Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
+ Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
}
pub(crate) fn resolve_method_call_fallback(
&self,
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
- ) -> Option<Either<FunctionId, FieldId>> {
+ ) -> Option<Either<Function, Field>> {
let expr_id = self.expr_id(db, &call.clone().into())?;
let inference_result = self.infer.as_ref()?;
match inference_result.method_resolution(expr_id) {
- Some((f_in_trait, substs)) => {
- Some(Either::Left(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs)))
- }
- None => inference_result.field_resolution(expr_id).map(Either::Right),
+ Some((f_in_trait, substs)) => Some(Either::Left(
+ self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(),
+ )),
+ None => inference_result.field_resolution(expr_id).map(Into::into).map(Either::Right),
+ }
+ }
+
+ pub(crate) fn resolve_field(
+ &self,
+ db: &dyn HirDatabase,
+ field: &ast::FieldExpr,
+ ) -> Option<Field> {
+ let expr_id = self.expr_id(db, &field.clone().into())?;
+ self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
+ }
+
+ pub(crate) fn resolve_field_fallback(
+ &self,
+ db: &dyn HirDatabase,
+ field: &ast::FieldExpr,
+ ) -> Option<Either<Field, Function>> {
+ let expr_id = self.expr_id(db, &field.clone().into())?;
+ let inference_result = self.infer.as_ref()?;
+ match inference_result.field_resolution(expr_id) {
+ Some(field) => Some(Either::Left(field.into())),
+ None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
+ Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into())
+ }),
}
}
@@ -417,15 +441,6 @@ impl SourceAnalyzer {
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
}
- pub(crate) fn resolve_field(
- &self,
- db: &dyn HirDatabase,
- field: &ast::FieldExpr,
- ) -> Option<Field> {
- let expr_id = self.expr_id(db, &field.clone().into())?;
- self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
- }
-
pub(crate) fn resolve_record_field(
&self,
db: &dyn HirDatabase,
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index ef72fc3861..ded5d4e3db 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -492,7 +492,7 @@ impl NameRefClass {
match_ast! {
match parent {
ast::MethodCallExpr(method_call) => {
- sema.resolve_method_call_field_fallback(&method_call)
+ sema.resolve_method_call_fallback(&method_call)
.map(|it| {
it.map_left(Definition::Function)
.map_right(Definition::Field)
@@ -500,9 +500,12 @@ impl NameRefClass {
})
},
ast::FieldExpr(field_expr) => {
- sema.resolve_field(&field_expr)
- .map(Definition::Field)
- .map(NameRefClass::Definition)
+ sema.resolve_field_fallback(&field_expr)
+ .map(|it| {
+ it.map_left(Definition::Field)
+ .map_right(Definition::Function)
+ .either(NameRefClass::Definition, NameRefClass::Definition)
+ })
},
ast::RecordPatField(record_pat_field) => {
sema.resolve_record_pat_field(&record_pat_field)
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 8c9d58671e..d5ec336fc7 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -6698,3 +6698,30 @@ foo!(r"{$0aaaaa}");
"#]],
);
}
+
+#[test]
+fn method_call_without_parens() {
+ check(
+ r#"
+struct S;
+impl S {
+ fn foo<T>(&self, t: T) {}
+}
+
+fn main() {
+ S.foo$0;
+}
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ test::S
+ ```
+
+ ```rust
+ fn foo<T>(&self, t: T)
+ ```
+ "#]],
+ );
+}