Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 9e82ab5

Browse files
committed
Auto merge of rust-lang#16055 - Veykril:field-fallback-method, r=Veykril
Fallback to method resolution on unresolved field access with matching method name Allows typing out a method name without having to add calling parentheses to do IDE things on it. The inverse of this we already have.
2 parents 9c3de09 + 35fbc02 commit 9e82ab5

File tree

5 files changed

+140
-72
lines changed

5 files changed

+140
-72
lines changed

crates/hir-ty/src/infer/expr.rs

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ impl InferenceContext<'_> {
579579
}
580580
ty
581581
}
582-
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name),
582+
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
583583
Expr::Await { expr } => {
584584
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
585585
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
@@ -1456,7 +1456,13 @@ impl InferenceContext<'_> {
14561456
})
14571457
}
14581458

1459-
fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty {
1459+
fn infer_field_access(
1460+
&mut self,
1461+
tgt_expr: ExprId,
1462+
receiver: ExprId,
1463+
name: &Name,
1464+
expected: &Expectation,
1465+
) -> Ty {
14601466
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
14611467

14621468
if name.is_missing() {
@@ -1482,28 +1488,42 @@ impl InferenceContext<'_> {
14821488
ty
14831489
}
14841490
None => {
1485-
// no field found,
1486-
let method_with_same_name_exists = {
1487-
self.get_traits_in_scope();
1488-
1489-
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
1490-
method_resolution::lookup_method(
1491-
self.db,
1492-
&canonicalized_receiver.value,
1493-
self.table.trait_env.clone(),
1494-
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
1495-
VisibleFromModule::Filter(self.resolver.module()),
1496-
name,
1497-
)
1498-
.is_some()
1499-
};
1491+
// no field found, lets attempt to resolve it like a function so that IDE things
1492+
// work out while people are typing
1493+
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
1494+
let resolved = method_resolution::lookup_method(
1495+
self.db,
1496+
&canonicalized_receiver.value,
1497+
self.table.trait_env.clone(),
1498+
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
1499+
VisibleFromModule::Filter(self.resolver.module()),
1500+
name,
1501+
);
15001502
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
15011503
expr: tgt_expr,
1502-
receiver: receiver_ty,
1504+
receiver: receiver_ty.clone(),
15031505
name: name.clone(),
1504-
method_with_same_name_exists,
1506+
method_with_same_name_exists: resolved.is_some(),
15051507
});
1506-
self.err_ty()
1508+
match resolved {
1509+
Some((adjust, func, _)) => {
1510+
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
1511+
let generics = generics(self.db.upcast(), func.into());
1512+
let substs = self.substs_for_method_call(generics, None);
1513+
self.write_expr_adj(receiver, adjustments);
1514+
self.write_method_resolution(tgt_expr, func, substs.clone());
1515+
1516+
self.check_method_call(
1517+
tgt_expr,
1518+
&[],
1519+
self.db.value_ty(func.into()),
1520+
substs,
1521+
ty,
1522+
expected,
1523+
)
1524+
}
1525+
None => self.err_ty(),
1526+
}
15071527
}
15081528
}
15091529
}
@@ -1517,7 +1537,7 @@ impl InferenceContext<'_> {
15171537
generic_args: Option<&GenericArgs>,
15181538
expected: &Expectation,
15191539
) -> Ty {
1520-
let receiver_ty = self.infer_expr(receiver, &Expectation::none());
1540+
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none());
15211541
let canonicalized_receiver = self.canonicalize(receiver_ty.clone());
15221542

15231543
let resolved = method_resolution::lookup_method(
@@ -1568,23 +1588,32 @@ impl InferenceContext<'_> {
15681588
)
15691589
}
15701590
};
1591+
self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
1592+
}
1593+
1594+
fn check_method_call(
1595+
&mut self,
1596+
tgt_expr: ExprId,
1597+
args: &[ExprId],
1598+
method_ty: Binders<Ty>,
1599+
substs: Substitution,
1600+
receiver_ty: Ty,
1601+
expected: &Expectation,
1602+
) -> Ty {
15711603
let method_ty = method_ty.substitute(Interner, &substs);
15721604
self.register_obligations_for_call(&method_ty);
1573-
let (formal_receiver_ty, param_tys, ret_ty, is_varargs) =
1605+
let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) =
15741606
match method_ty.callable_sig(self.db) {
1575-
Some(sig) => {
1607+
Some(sig) => (
15761608
if !sig.params().is_empty() {
1577-
(
1578-
sig.params()[0].clone(),
1579-
sig.params()[1..].to_vec(),
1580-
sig.ret().clone(),
1581-
sig.is_varargs,
1582-
)
1609+
(sig.params()[0].clone(), sig.params()[1..].to_vec())
15831610
} else {
1584-
(self.err_ty(), Vec::new(), sig.ret().clone(), sig.is_varargs)
1585-
}
1586-
}
1587-
None => (self.err_ty(), Vec::new(), self.err_ty(), true),
1611+
(self.err_ty(), Vec::new())
1612+
},
1613+
sig.ret().clone(),
1614+
sig.is_varargs,
1615+
),
1616+
None => ((self.err_ty(), Vec::new()), self.err_ty(), true),
15881617
};
15891618
self.unify(&formal_receiver_ty, &receiver_ty);
15901619

crates/hir/src/semantics.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use hir_def::{
1717
nameres::MacroSubNs,
1818
resolver::{self, HasResolver, Resolver, TypeNs},
1919
type_ref::Mutability,
20-
AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
20+
AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
2121
};
2222
use hir_expand::{
2323
db::ExpandDatabase, files::InRealFile, name::AsName, ExpansionInfo, InMacroFile, MacroCallId,
@@ -198,20 +198,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
198198
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
199199
}
200200

201-
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
202-
self.imp.resolve_method_call(call).map(Function::from)
203-
}
204-
205-
/// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
206-
pub fn resolve_method_call_field_fallback(
207-
&self,
208-
call: &ast::MethodCallExpr,
209-
) -> Option<Either<Function, Field>> {
210-
self.imp
211-
.resolve_method_call_fallback(call)
212-
.map(|it| it.map_left(Function::from).map_right(Field::from))
213-
}
214-
215201
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
216202
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
217203
}
@@ -1048,14 +1034,15 @@ impl<'db> SemanticsImpl<'db> {
10481034
self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
10491035
}
10501036

1051-
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
1037+
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
10521038
self.analyze(call.syntax())?.resolve_method_call(self.db, call)
10531039
}
10541040

1055-
fn resolve_method_call_fallback(
1041+
/// Attempts to resolve this call expression as a method call falling back to resolving it as a field.
1042+
pub fn resolve_method_call_fallback(
10561043
&self,
10571044
call: &ast::MethodCallExpr,
1058-
) -> Option<Either<FunctionId, FieldId>> {
1045+
) -> Option<Either<Function, Field>> {
10591046
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
10601047
}
10611048

@@ -1087,6 +1074,13 @@ impl<'db> SemanticsImpl<'db> {
10871074
self.analyze(field.syntax())?.resolve_field(self.db, field)
10881075
}
10891076

1077+
pub fn resolve_field_fallback(
1078+
&self,
1079+
field: &ast::FieldExpr,
1080+
) -> Option<Either<Field, Function>> {
1081+
self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
1082+
}
1083+
10901084
pub fn resolve_record_field(
10911085
&self,
10921086
field: &ast::RecordExprField,
@@ -1298,7 +1292,7 @@ impl<'db> SemanticsImpl<'db> {
12981292
return None;
12991293
}
13001294

1301-
let func = self.resolve_method_call(method_call_expr).map(Function::from)?;
1295+
let func = self.resolve_method_call(method_call_expr)?;
13021296
let res = match func.self_param(self.db)?.access(self.db) {
13031297
Access::Shared | Access::Exclusive => true,
13041298
Access::Owned => false,

crates/hir/src/source_analyzer.rs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -280,25 +280,49 @@ impl SourceAnalyzer {
280280
&self,
281281
db: &dyn HirDatabase,
282282
call: &ast::MethodCallExpr,
283-
) -> Option<FunctionId> {
283+
) -> Option<Function> {
284284
let expr_id = self.expr_id(db, &call.clone().into())?;
285285
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
286286

287-
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs))
287+
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into())
288288
}
289289

290290
pub(crate) fn resolve_method_call_fallback(
291291
&self,
292292
db: &dyn HirDatabase,
293293
call: &ast::MethodCallExpr,
294-
) -> Option<Either<FunctionId, FieldId>> {
294+
) -> Option<Either<Function, Field>> {
295295
let expr_id = self.expr_id(db, &call.clone().into())?;
296296
let inference_result = self.infer.as_ref()?;
297297
match inference_result.method_resolution(expr_id) {
298-
Some((f_in_trait, substs)) => {
299-
Some(Either::Left(self.resolve_impl_method_or_trait_def(db, f_in_trait, substs)))
300-
}
301-
None => inference_result.field_resolution(expr_id).map(Either::Right),
298+
Some((f_in_trait, substs)) => Some(Either::Left(
299+
self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(),
300+
)),
301+
None => inference_result.field_resolution(expr_id).map(Into::into).map(Either::Right),
302+
}
303+
}
304+
305+
pub(crate) fn resolve_field(
306+
&self,
307+
db: &dyn HirDatabase,
308+
field: &ast::FieldExpr,
309+
) -> Option<Field> {
310+
let expr_id = self.expr_id(db, &field.clone().into())?;
311+
self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
312+
}
313+
314+
pub(crate) fn resolve_field_fallback(
315+
&self,
316+
db: &dyn HirDatabase,
317+
field: &ast::FieldExpr,
318+
) -> Option<Either<Field, Function>> {
319+
let expr_id = self.expr_id(db, &field.clone().into())?;
320+
let inference_result = self.infer.as_ref()?;
321+
match inference_result.field_resolution(expr_id) {
322+
Some(field) => Some(Either::Left(field.into())),
323+
None => inference_result.method_resolution(expr_id).map(|(f, substs)| {
324+
Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into())
325+
}),
302326
}
303327
}
304328

@@ -417,15 +441,6 @@ impl SourceAnalyzer {
417441
Some(self.resolve_impl_method_or_trait_def(db, op_fn, substs))
418442
}
419443

420-
pub(crate) fn resolve_field(
421-
&self,
422-
db: &dyn HirDatabase,
423-
field: &ast::FieldExpr,
424-
) -> Option<Field> {
425-
let expr_id = self.expr_id(db, &field.clone().into())?;
426-
self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
427-
}
428-
429444
pub(crate) fn resolve_record_field(
430445
&self,
431446
db: &dyn HirDatabase,

crates/ide-db/src/defs.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -492,17 +492,20 @@ impl NameRefClass {
492492
match_ast! {
493493
match parent {
494494
ast::MethodCallExpr(method_call) => {
495-
sema.resolve_method_call_field_fallback(&method_call)
495+
sema.resolve_method_call_fallback(&method_call)
496496
.map(|it| {
497497
it.map_left(Definition::Function)
498498
.map_right(Definition::Field)
499499
.either(NameRefClass::Definition, NameRefClass::Definition)
500500
})
501501
},
502502
ast::FieldExpr(field_expr) => {
503-
sema.resolve_field(&field_expr)
504-
.map(Definition::Field)
505-
.map(NameRefClass::Definition)
503+
sema.resolve_field_fallback(&field_expr)
504+
.map(|it| {
505+
it.map_left(Definition::Field)
506+
.map_right(Definition::Function)
507+
.either(NameRefClass::Definition, NameRefClass::Definition)
508+
})
506509
},
507510
ast::RecordPatField(record_pat_field) => {
508511
sema.resolve_record_pat_field(&record_pat_field)

crates/ide/src/hover/tests.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6698,3 +6698,30 @@ foo!(r"{$0aaaaa}");
66986698
"#]],
66996699
);
67006700
}
6701+
6702+
#[test]
6703+
fn method_call_without_parens() {
6704+
check(
6705+
r#"
6706+
struct S;
6707+
impl S {
6708+
fn foo<T>(&self, t: T) {}
6709+
}
6710+
6711+
fn main() {
6712+
S.foo$0;
6713+
}
6714+
"#,
6715+
expect![[r#"
6716+
*foo*
6717+
6718+
```rust
6719+
test::S
6720+
```
6721+
6722+
```rust
6723+
fn foo<T>(&self, t: T)
6724+
```
6725+
"#]],
6726+
);
6727+
}

0 commit comments

Comments
 (0)