Skip to content

Commit 12ddf77

Browse files
committed
When suggesting writing a fully qualified path probe for appropriate types
Fix #46585.
1 parent b22c152 commit 12ddf77

18 files changed

+295
-55
lines changed

compiler/rustc_hir_analysis/src/astconv/mod.rs

+160-17
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
2424
use rustc_hir::def_id::{DefId, LocalDefId};
2525
use rustc_hir::intravisit::{walk_generics, Visitor as _};
2626
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
27+
use rustc_infer::infer::TyCtxtInferExt;
2728
use rustc_middle::middle::stability::AllowUnstable;
2829
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
2930
use rustc_middle::ty::GenericParamDefKind;
@@ -1633,8 +1634,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16331634
fn report_ambiguous_associated_type(
16341635
&self,
16351636
span: Span,
1636-
type_str: &str,
1637-
trait_str: &str,
1637+
types: &[String],
1638+
traits: &[String],
16381639
name: Symbol,
16391640
) -> ErrorGuaranteed {
16401641
let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
@@ -1645,19 +1646,92 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16451646
.keys()
16461647
.any(|full_span| full_span.contains(span))
16471648
{
1648-
err.span_suggestion(
1649+
err.span_suggestion_verbose(
16491650
span.shrink_to_lo(),
16501651
"you are looking for the module in `std`, not the primitive type",
16511652
"std::",
16521653
Applicability::MachineApplicable,
16531654
);
16541655
} else {
1655-
err.span_suggestion(
1656-
span,
1657-
"use fully-qualified syntax",
1658-
format!("<{} as {}>::{}", type_str, trait_str, name),
1659-
Applicability::HasPlaceholders,
1660-
);
1656+
match (types, traits) {
1657+
([], []) => {
1658+
err.span_suggestion_verbose(
1659+
span,
1660+
&format!(
1661+
"if there were a type named `Type` that implements a trait named \
1662+
`Trait` with associated type `{name}`, you could use the \
1663+
fully-qualified path",
1664+
),
1665+
format!("<Type as Trait>::{name}"),
1666+
Applicability::HasPlaceholders,
1667+
);
1668+
}
1669+
([], [trait_str]) => {
1670+
err.span_suggestion_verbose(
1671+
span,
1672+
&format!(
1673+
"if there were a type named `Example` that implemented `{trait_str}`, \
1674+
you could use the fully-qualified path",
1675+
),
1676+
format!("<Example as {trait_str}>::{name}"),
1677+
Applicability::HasPlaceholders,
1678+
);
1679+
}
1680+
([], traits) => {
1681+
err.span_suggestions(
1682+
span,
1683+
&format!(
1684+
"if there were a type named `Example` that implemented one of the \
1685+
traits with associated type `{name}`, you could use the \
1686+
fully-qualified path",
1687+
),
1688+
traits
1689+
.iter()
1690+
.map(|trait_str| format!("<Example as {trait_str}>::{name}"))
1691+
.collect::<Vec<_>>(),
1692+
Applicability::HasPlaceholders,
1693+
);
1694+
}
1695+
([type_str], []) => {
1696+
err.span_suggestion_verbose(
1697+
span,
1698+
&format!(
1699+
"if there were a trait named `Example` with associated type `{name}` \
1700+
implemented for `{type_str}`, you could use the fully-qualified path",
1701+
),
1702+
format!("<{type_str} as Example>::{name}"),
1703+
Applicability::HasPlaceholders,
1704+
);
1705+
}
1706+
(types, []) => {
1707+
err.span_suggestions(
1708+
span,
1709+
&format!(
1710+
"if there were a trait named `Example` with associated type `{name}` \
1711+
implemented for one of the types, you could use the fully-qualified \
1712+
path",
1713+
),
1714+
types
1715+
.into_iter()
1716+
.map(|type_str| format!("<{type_str} as Example>::{name}")),
1717+
Applicability::HasPlaceholders,
1718+
);
1719+
}
1720+
(types, traits) => {
1721+
let mut suggestions = vec![];
1722+
for type_str in types {
1723+
for trait_str in traits {
1724+
suggestions.push(format!("<{type_str} as {trait_str}>::{name}"));
1725+
}
1726+
}
1727+
err.span_suggestions(
1728+
span,
1729+
"use the fully-qualified path",
1730+
suggestions,
1731+
Applicability::MachineApplicable,
1732+
);
1733+
}
1734+
}
16611735
}
16621736
err.emit()
16631737
}
@@ -2040,12 +2114,67 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
20402114
err.emit()
20412115
} else if let Err(reported) = qself_ty.error_reported() {
20422116
reported
2117+
} else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
2118+
// `<impl Trait as OtherTrait>::Assoc` makes no sense.
2119+
struct_span_err!(
2120+
tcx.sess,
2121+
tcx.def_span(alias_ty.def_id),
2122+
E0667,
2123+
"`impl Trait` is not allowed in path parameters"
2124+
)
2125+
.emit() // Already reported in an earlier stage.
20432126
} else {
2127+
// Find all the `impl`s that `qself_ty` has for any trait that has the
2128+
// associated type, so that we suggest the right one.
2129+
let infcx = tcx.infer_ctxt().build();
2130+
// We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
2131+
// to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
2132+
let param_env = ty::ParamEnv::new(
2133+
ty::List::empty(),
2134+
traits::Reveal::All,
2135+
hir::Constness::NotConst,
2136+
);
2137+
let traits: Vec<_> = self
2138+
.tcx()
2139+
.all_traits()
2140+
.filter(|trait_def_id| {
2141+
// Consider only traits with the associated type
2142+
tcx.associated_items(*trait_def_id)
2143+
.in_definition_order()
2144+
.find(|i| {
2145+
i.kind.namespace() == Namespace::TypeNS
2146+
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
2147+
&& matches!(i.kind, ty::AssocKind::Type)
2148+
})
2149+
.is_some()
2150+
// Consider only accessible traits
2151+
&& tcx.visibility(*trait_def_id)
2152+
.is_accessible_from(self.item_def_id(), tcx)
2153+
&& tcx.all_impls(*trait_def_id)
2154+
.filter(|impl_def_id| {
2155+
let trait_ref = tcx.impl_trait_ref(impl_def_id);
2156+
trait_ref.map_or(false, |impl_| {
2157+
infcx
2158+
.can_eq(
2159+
param_env,
2160+
tcx.erase_regions(impl_.self_ty()),
2161+
tcx.erase_regions(qself_ty),
2162+
)
2163+
.is_ok()
2164+
})
2165+
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
2166+
})
2167+
.next()
2168+
.is_some()
2169+
})
2170+
.map(|trait_def_id| tcx.def_path_str(trait_def_id))
2171+
.collect();
2172+
20442173
// Don't print `TyErr` to the user.
20452174
self.report_ambiguous_associated_type(
20462175
span,
2047-
&qself_ty.to_string(),
2048-
"Trait",
2176+
&[qself_ty.to_string()],
2177+
&traits,
20492178
assoc_ident.name,
20502179
)
20512180
};
@@ -2163,16 +2292,30 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
21632292
let is_part_of_self_trait_constraints = def_id == trait_def_id;
21642293
let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id);
21652294

2166-
let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
2167-
"Self"
2295+
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
2296+
vec!["Self".to_string()]
21682297
} else {
2169-
"Type"
2298+
// Find all the types that have an `impl` for the trait.
2299+
tcx.all_impls(trait_def_id)
2300+
.filter(|impl_def_id| {
2301+
// Consider only accessible traits
2302+
tcx.visibility(*impl_def_id).is_accessible_from(self.item_def_id(), tcx)
2303+
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
2304+
})
2305+
.filter_map(|impl_def_id| tcx.impl_trait_ref(impl_def_id))
2306+
.map(|impl_| impl_.self_ty())
2307+
// We don't care about blanket impls.
2308+
.filter(|self_ty| !self_ty.has_non_region_infer())
2309+
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
2310+
.collect()
21702311
};
2171-
2312+
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
2313+
// references the trait. Relevant for the first case in
2314+
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
21722315
let reported = self.report_ambiguous_associated_type(
21732316
span,
2174-
type_name,
2175-
&path_str,
2317+
&type_names,
2318+
&[path_str],
21762319
item_segment.ident.name,
21772320
);
21782321
return tcx.ty_error_with_guaranteed(reported)

tests/ui/associated-item/associated-item-duplicate-names-3.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ error[E0223]: ambiguous associated type
1313
--> $DIR/associated-item-duplicate-names-3.rs:18:12
1414
|
1515
LL | let x: Baz::Bar = 5;
16-
| ^^^^^^^^ help: use fully-qualified syntax: `<Baz as Trait>::Bar`
16+
| ^^^^^^^^ help: use the fully-qualified path: `<Baz as Foo>::Bar`
1717

1818
error: aborting due to 2 previous errors
1919

tests/ui/associated-types/associated-types-in-ambiguous-context.stderr

+20-5
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,46 @@ error[E0223]: ambiguous associated type
22
--> $DIR/associated-types-in-ambiguous-context.rs:6:36
33
|
44
LL | fn get<T:Get,U:Get>(x: T, y: U) -> Get::Value {}
5-
| ^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Get>::Value`
5+
| ^^^^^^^^^^
6+
|
7+
help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path
8+
|
9+
LL | fn get<T:Get,U:Get>(x: T, y: U) -> <Example as Get>::Value {}
10+
| ~~~~~~~~~~~~~~~~~~~~~~~
611

712
error[E0223]: ambiguous associated type
813
--> $DIR/associated-types-in-ambiguous-context.rs:20:17
914
|
1015
LL | trait Foo where Foo::Assoc: Bar {
11-
| ^^^^^^^^^^ help: use fully-qualified syntax: `<Self as Foo>::Assoc`
16+
| ^^^^^^^^^^ help: use the fully-qualified path: `<Self as Foo>::Assoc`
1217

1318
error[E0223]: ambiguous associated type
1419
--> $DIR/associated-types-in-ambiguous-context.rs:25:10
1520
|
1621
LL | type X = std::ops::Deref::Target;
17-
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Deref>::Target`
22+
| ^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
help: if there were a type named `Example` that implemented `Deref`, you could use the fully-qualified path
25+
|
26+
LL | type X = <Example as Deref>::Target;
27+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
1828

1929
error[E0223]: ambiguous associated type
2030
--> $DIR/associated-types-in-ambiguous-context.rs:11:23
2131
|
2232
LL | fn grab(&self) -> Grab::Value;
23-
| ^^^^^^^^^^^ help: use fully-qualified syntax: `<Self as Grab>::Value`
33+
| ^^^^^^^^^^^ help: use the fully-qualified path: `<Self as Grab>::Value`
2434

2535
error[E0223]: ambiguous associated type
2636
--> $DIR/associated-types-in-ambiguous-context.rs:14:22
2737
|
2838
LL | fn get(&self) -> Get::Value;
29-
| ^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Get>::Value`
39+
| ^^^^^^^^^^
40+
|
41+
help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path
42+
|
43+
LL | fn get(&self) -> <Example as Get>::Value;
44+
| ~~~~~~~~~~~~~~~~~~~~~~~
3045

3146
error: aborting due to 5 previous errors
3247

0 commit comments

Comments
 (0)