Skip to content

Commit dbeee34

Browse files
committed
Auto merge of rust-lang#13867 - Veykril:meth-res-fallback, r=Veykril
Fallback to invisible associated functions and constants if no visible resolutions are found Still lacking tests, will add those later Fixes rust-lang/rust-analyzer#13126
2 parents f1c4150 + 1d782a9 commit dbeee34

File tree

4 files changed

+115
-54
lines changed

4 files changed

+115
-54
lines changed

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,16 @@ impl<'a> InferenceContext<'a> {
234234
let canonical_ty = self.canonicalize(ty.clone());
235235
let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
236236

237-
method_resolution::iterate_method_candidates(
237+
let mut not_visible = None;
238+
let res = method_resolution::iterate_method_candidates(
238239
&canonical_ty.value,
239240
self.db,
240241
self.table.trait_env.clone(),
241242
&traits_in_scope,
242243
VisibleFromModule::Filter(self.resolver.module()),
243244
Some(name),
244245
method_resolution::LookupMode::Path,
245-
move |_ty, item| {
246+
|_ty, item, visible| {
246247
let (def, container) = match item {
247248
AssocItemId::FunctionId(f) => {
248249
(ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
@@ -277,10 +278,21 @@ impl<'a> InferenceContext<'a> {
277278
}
278279
};
279280

280-
self.write_assoc_resolution(id, item, substs.clone());
281-
Some((def, Some(substs)))
281+
if visible {
282+
Some((def, item, Some(substs)))
283+
} else {
284+
if not_visible.is_none() {
285+
not_visible = Some((def, item, Some(substs)));
286+
}
287+
None
288+
}
282289
},
283-
)
290+
);
291+
let res = res.or(not_visible);
292+
if let Some((_, item, Some(ref substs))) = res {
293+
self.write_assoc_resolution(id, item, substs.clone());
294+
}
295+
res.map(|(def, _, substs)| (def, substs))
284296
}
285297

286298
fn resolve_enum_variant_on_ty(

crates/hir-ty/src/method_resolution.rs

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -495,19 +495,25 @@ pub(crate) fn lookup_method(
495495
visible_from_module: VisibleFromModule,
496496
name: &Name,
497497
) -> Option<(ReceiverAdjustments, FunctionId)> {
498-
iterate_method_candidates(
498+
let mut not_visible = None;
499+
let res = iterate_method_candidates(
499500
ty,
500501
db,
501502
env,
502503
traits_in_scope,
503504
visible_from_module,
504505
Some(name),
505506
LookupMode::MethodCall,
506-
|adjustments, f| match f {
507-
AssocItemId::FunctionId(f) => Some((adjustments, f)),
507+
|adjustments, f, visible| match f {
508+
AssocItemId::FunctionId(f) if visible => Some((adjustments, f)),
509+
AssocItemId::FunctionId(f) if not_visible.is_none() => {
510+
not_visible = Some((adjustments, f));
511+
None
512+
}
508513
_ => None,
509514
},
510-
)
515+
);
516+
res.or(not_visible)
511517
}
512518

513519
/// Whether we're looking up a dotted method call (like `v.len()`) or a path
@@ -619,7 +625,7 @@ pub(crate) fn iterate_method_candidates<T>(
619625
visible_from_module: VisibleFromModule,
620626
name: Option<&Name>,
621627
mode: LookupMode,
622-
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option<T>,
628+
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
623629
) -> Option<T> {
624630
let mut slot = None;
625631
iterate_method_candidates_dyn(
@@ -630,9 +636,9 @@ pub(crate) fn iterate_method_candidates<T>(
630636
visible_from_module,
631637
name,
632638
mode,
633-
&mut |adj, item| {
639+
&mut |adj, item, visible| {
634640
assert!(slot.is_none());
635-
if let Some(it) = callback(adj, item) {
641+
if let Some(it) = callback(adj, item, visible) {
636642
slot = Some(it);
637643
return ControlFlow::Break(());
638644
}
@@ -771,7 +777,7 @@ pub fn iterate_path_candidates(
771777
name,
772778
LookupMode::Path,
773779
// the adjustments are not relevant for path lookup
774-
&mut |_, id| callback(id),
780+
&mut |_, id, _| callback(id),
775781
)
776782
}
777783

@@ -783,7 +789,7 @@ pub fn iterate_method_candidates_dyn(
783789
visible_from_module: VisibleFromModule,
784790
name: Option<&Name>,
785791
mode: LookupMode,
786-
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
792+
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
787793
) -> ControlFlow<()> {
788794
match mode {
789795
LookupMode::MethodCall => {
@@ -847,7 +853,7 @@ fn iterate_method_candidates_with_autoref(
847853
traits_in_scope: &FxHashSet<TraitId>,
848854
visible_from_module: VisibleFromModule,
849855
name: Option<&Name>,
850-
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
856+
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
851857
) -> ControlFlow<()> {
852858
if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) {
853859
// don't try to resolve methods on unknown types
@@ -908,7 +914,7 @@ fn iterate_method_candidates_by_receiver(
908914
traits_in_scope: &FxHashSet<TraitId>,
909915
visible_from_module: VisibleFromModule,
910916
name: Option<&Name>,
911-
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
917+
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
912918
) -> ControlFlow<()> {
913919
let mut table = InferenceTable::new(db, env);
914920
let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
@@ -954,7 +960,7 @@ fn iterate_method_candidates_for_self_ty(
954960
traits_in_scope: &FxHashSet<TraitId>,
955961
visible_from_module: VisibleFromModule,
956962
name: Option<&Name>,
957-
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
963+
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
958964
) -> ControlFlow<()> {
959965
let mut table = InferenceTable::new(db, env);
960966
let self_ty = table.instantiate_canonical(self_ty.clone());
@@ -985,7 +991,7 @@ fn iterate_trait_method_candidates(
985991
name: Option<&Name>,
986992
receiver_ty: Option<&Ty>,
987993
receiver_adjustments: Option<ReceiverAdjustments>,
988-
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
994+
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
989995
) -> ControlFlow<()> {
990996
let db = table.db;
991997
let env = table.trait_env.clone();
@@ -1016,17 +1022,19 @@ fn iterate_trait_method_candidates(
10161022
for &(_, item) in data.items.iter() {
10171023
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
10181024
// since only inherent methods should be included into visibility checking.
1019-
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
1020-
continue;
1021-
}
1025+
let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
1026+
IsValidCandidate::Yes => true,
1027+
IsValidCandidate::NotVisible => false,
1028+
IsValidCandidate::No => continue,
1029+
};
10221030
if !known_implemented {
10231031
let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
10241032
if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
10251033
continue 'traits;
10261034
}
10271035
}
10281036
known_implemented = true;
1029-
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
1037+
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
10301038
}
10311039
}
10321040
ControlFlow::Continue(())
@@ -1039,7 +1047,7 @@ fn iterate_inherent_methods(
10391047
receiver_ty: Option<&Ty>,
10401048
receiver_adjustments: Option<ReceiverAdjustments>,
10411049
visible_from_module: VisibleFromModule,
1042-
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
1050+
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
10431051
) -> ControlFlow<()> {
10441052
let db = table.db;
10451053
let env = table.trait_env.clone();
@@ -1128,17 +1136,21 @@ fn iterate_inherent_methods(
11281136
name: Option<&Name>,
11291137
receiver_ty: Option<&Ty>,
11301138
receiver_adjustments: Option<ReceiverAdjustments>,
1131-
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
1139+
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
11321140
traits: impl Iterator<Item = TraitId>,
11331141
) -> ControlFlow<()> {
11341142
let db = table.db;
11351143
for t in traits {
11361144
let data = db.trait_data(t);
11371145
for &(_, item) in data.items.iter() {
11381146
// We don't pass `visible_from_module` as all trait items should be visible.
1139-
if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
1140-
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
1141-
}
1147+
let visible =
1148+
match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
1149+
IsValidCandidate::Yes => true,
1150+
IsValidCandidate::NotVisible => false,
1151+
IsValidCandidate::No => continue,
1152+
};
1153+
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
11421154
}
11431155
}
11441156
ControlFlow::Continue(())
@@ -1152,17 +1164,25 @@ fn iterate_inherent_methods(
11521164
receiver_ty: Option<&Ty>,
11531165
receiver_adjustments: Option<ReceiverAdjustments>,
11541166
visible_from_module: Option<ModuleId>,
1155-
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
1167+
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
11561168
) -> ControlFlow<()> {
11571169
let db = table.db;
11581170
let impls_for_self_ty = impls.for_self_ty(self_ty);
11591171
for &impl_def in impls_for_self_ty {
11601172
for &item in &db.impl_data(impl_def).items {
1161-
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module)
1162-
{
1163-
continue;
1164-
}
1165-
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
1173+
let visible = match is_valid_candidate(
1174+
table,
1175+
name,
1176+
receiver_ty,
1177+
item,
1178+
self_ty,
1179+
visible_from_module,
1180+
) {
1181+
IsValidCandidate::Yes => true,
1182+
IsValidCandidate::NotVisible => false,
1183+
IsValidCandidate::No => continue,
1184+
};
1185+
callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?;
11661186
}
11671187
}
11681188
ControlFlow::Continue(())
@@ -1191,7 +1211,7 @@ pub fn resolve_indexing_op(
11911211
macro_rules! check_that {
11921212
($cond:expr) => {
11931213
if !$cond {
1194-
return false;
1214+
return IsValidCandidate::No;
11951215
}
11961216
};
11971217
}
@@ -1203,7 +1223,7 @@ fn is_valid_candidate(
12031223
item: AssocItemId,
12041224
self_ty: &Ty,
12051225
visible_from_module: Option<ModuleId>,
1206-
) -> bool {
1226+
) -> IsValidCandidate {
12071227
let db = table.db;
12081228
match item {
12091229
AssocItemId::FunctionId(m) => {
@@ -1214,13 +1234,13 @@ fn is_valid_candidate(
12141234
check_that!(receiver_ty.is_none());
12151235

12161236
check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
1217-
check_that!(visible_from_module.map_or(true, |from_module| {
1218-
let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module);
1219-
if !v {
1237+
1238+
if let Some(from_module) = visible_from_module {
1239+
if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
12201240
cov_mark::hit!(const_candidate_not_visible);
1241+
return IsValidCandidate::NotVisible;
12211242
}
1222-
v
1223-
}));
1243+
}
12241244
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
12251245
let self_ty_matches = table.run_in_snapshot(|table| {
12261246
let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
@@ -1230,35 +1250,39 @@ fn is_valid_candidate(
12301250
});
12311251
if !self_ty_matches {
12321252
cov_mark::hit!(const_candidate_self_type_mismatch);
1233-
return false;
1253+
return IsValidCandidate::No;
12341254
}
12351255
}
1236-
true
1256+
IsValidCandidate::Yes
12371257
}
1238-
_ => false,
1258+
_ => IsValidCandidate::No,
12391259
}
12401260
}
12411261

1262+
enum IsValidCandidate {
1263+
Yes,
1264+
No,
1265+
NotVisible,
1266+
}
1267+
12421268
fn is_valid_fn_candidate(
12431269
table: &mut InferenceTable<'_>,
12441270
fn_id: FunctionId,
12451271
name: Option<&Name>,
12461272
receiver_ty: Option<&Ty>,
12471273
self_ty: &Ty,
12481274
visible_from_module: Option<ModuleId>,
1249-
) -> bool {
1275+
) -> IsValidCandidate {
12501276
let db = table.db;
12511277
let data = db.function_data(fn_id);
12521278

12531279
check_that!(name.map_or(true, |n| n == &data.name));
1254-
check_that!(visible_from_module.map_or(true, |from_module| {
1255-
let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module);
1256-
if !v {
1280+
if let Some(from_module) = visible_from_module {
1281+
if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) {
12571282
cov_mark::hit!(autoderef_candidate_not_visible);
1283+
return IsValidCandidate::NotVisible;
12581284
}
1259-
v
1260-
}));
1261-
1285+
}
12621286
table.run_in_snapshot(|table| {
12631287
let container = fn_id.lookup(db.upcast()).container;
12641288
let (impl_subst, expect_self_ty) = match container {
@@ -1297,7 +1321,7 @@ fn is_valid_fn_candidate(
12971321
// We need to consider the bounds on the impl to distinguish functions of the same name
12981322
// for a type.
12991323
let predicates = db.generic_predicates(impl_id.into());
1300-
predicates
1324+
let valid = predicates
13011325
.iter()
13021326
.map(|predicate| {
13031327
let (p, b) = predicate
@@ -1312,12 +1336,16 @@ fn is_valid_fn_candidate(
13121336
// It's ok to get ambiguity here, as we may not have enough information to prove
13131337
// obligations. We'll check if the user is calling the selected method properly
13141338
// later anyway.
1315-
.all(|p| table.try_obligation(p.cast(Interner)).is_some())
1339+
.all(|p| table.try_obligation(p.cast(Interner)).is_some());
1340+
match valid {
1341+
true => IsValidCandidate::Yes,
1342+
false => IsValidCandidate::No,
1343+
}
13161344
} else {
13171345
// For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in
13181346
// `iterate_trait_method_candidates()`.
13191347
// For others, this function shouldn't be called.
1320-
true
1348+
IsValidCandidate::Yes
13211349
}
13221350
})
13231351
}

crates/hir-ty/src/tests/method_resolution.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1896,3 +1896,24 @@ impl dyn Error + Send {
18961896
"#,
18971897
);
18981898
}
1899+
1900+
#[test]
1901+
fn fallback_private_methods() {
1902+
check(
1903+
r#"
1904+
mod module {
1905+
pub struct Struct;
1906+
1907+
impl Struct {
1908+
fn func(&self) {}
1909+
}
1910+
}
1911+
1912+
fn foo() {
1913+
let s = module::Struct;
1914+
s.func();
1915+
//^^^^^^^^ type: ()
1916+
}
1917+
"#,
1918+
);
1919+
}

crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3274,7 +3274,7 @@ impl Type {
32743274
with_local_impls.and_then(|b| b.id.containing_block()).into(),
32753275
name,
32763276
method_resolution::LookupMode::MethodCall,
3277-
&mut |_adj, id| callback(id),
3277+
&mut |_adj, id, _| callback(id),
32783278
);
32793279
}
32803280

0 commit comments

Comments
 (0)