Skip to content

Commit c6e3a47

Browse files
authored
Rollup merge of #106585 - estebank:issue-46585, r=compiler-errors
When suggesting writing a fully qualified path probe for appropriate types Address the more common part of #46585.
2 parents f9dde54 + c6f322b commit c6e3a47

21 files changed

+324
-55
lines changed

compiler/rustc_hir_analysis/src/astconv/mod.rs

+157-17
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
2727
use rustc_hir::def_id::{DefId, LocalDefId};
2828
use rustc_hir::intravisit::{walk_generics, Visitor as _};
2929
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
30+
use rustc_infer::infer::TyCtxtInferExt;
3031
use rustc_middle::middle::stability::AllowUnstable;
3132
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
3233
use rustc_middle::ty::GenericParamDefKind;
@@ -1643,8 +1644,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16431644
fn report_ambiguous_associated_type(
16441645
&self,
16451646
span: Span,
1646-
type_str: &str,
1647-
trait_str: &str,
1647+
types: &[String],
1648+
traits: &[String],
16481649
name: Symbol,
16491650
) -> ErrorGuaranteed {
16501651
let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
@@ -1655,19 +1656,92 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
16551656
.keys()
16561657
.any(|full_span| full_span.contains(span))
16571658
{
1658-
err.span_suggestion(
1659+
err.span_suggestion_verbose(
16591660
span.shrink_to_lo(),
16601661
"you are looking for the module in `std`, not the primitive type",
16611662
"std::",
16621663
Applicability::MachineApplicable,
16631664
);
16641665
} else {
1665-
err.span_suggestion(
1666-
span,
1667-
"use fully-qualified syntax",
1668-
format!("<{} as {}>::{}", type_str, trait_str, name),
1669-
Applicability::HasPlaceholders,
1670-
);
1666+
match (types, traits) {
1667+
([], []) => {
1668+
err.span_suggestion_verbose(
1669+
span,
1670+
&format!(
1671+
"if there were a type named `Type` that implements a trait named \
1672+
`Trait` with associated type `{name}`, you could use the \
1673+
fully-qualified path",
1674+
),
1675+
format!("<Type as Trait>::{name}"),
1676+
Applicability::HasPlaceholders,
1677+
);
1678+
}
1679+
([], [trait_str]) => {
1680+
err.span_suggestion_verbose(
1681+
span,
1682+
&format!(
1683+
"if there were a type named `Example` that implemented `{trait_str}`, \
1684+
you could use the fully-qualified path",
1685+
),
1686+
format!("<Example as {trait_str}>::{name}"),
1687+
Applicability::HasPlaceholders,
1688+
);
1689+
}
1690+
([], traits) => {
1691+
err.span_suggestions(
1692+
span,
1693+
&format!(
1694+
"if there were a type named `Example` that implemented one of the \
1695+
traits with associated type `{name}`, you could use the \
1696+
fully-qualified path",
1697+
),
1698+
traits
1699+
.iter()
1700+
.map(|trait_str| format!("<Example as {trait_str}>::{name}"))
1701+
.collect::<Vec<_>>(),
1702+
Applicability::HasPlaceholders,
1703+
);
1704+
}
1705+
([type_str], []) => {
1706+
err.span_suggestion_verbose(
1707+
span,
1708+
&format!(
1709+
"if there were a trait named `Example` with associated type `{name}` \
1710+
implemented for `{type_str}`, you could use the fully-qualified path",
1711+
),
1712+
format!("<{type_str} as Example>::{name}"),
1713+
Applicability::HasPlaceholders,
1714+
);
1715+
}
1716+
(types, []) => {
1717+
err.span_suggestions(
1718+
span,
1719+
&format!(
1720+
"if there were a trait named `Example` with associated type `{name}` \
1721+
implemented for one of the types, you could use the fully-qualified \
1722+
path",
1723+
),
1724+
types
1725+
.into_iter()
1726+
.map(|type_str| format!("<{type_str} as Example>::{name}")),
1727+
Applicability::HasPlaceholders,
1728+
);
1729+
}
1730+
(types, traits) => {
1731+
let mut suggestions = vec![];
1732+
for type_str in types {
1733+
for trait_str in traits {
1734+
suggestions.push(format!("<{type_str} as {trait_str}>::{name}"));
1735+
}
1736+
}
1737+
err.span_suggestions(
1738+
span,
1739+
"use the fully-qualified path",
1740+
suggestions,
1741+
Applicability::MachineApplicable,
1742+
);
1743+
}
1744+
}
16711745
}
16721746
err.emit()
16731747
}
@@ -2050,12 +2124,64 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
20502124
err.emit()
20512125
} else if let Err(reported) = qself_ty.error_reported() {
20522126
reported
2127+
} else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
2128+
// `<impl Trait as OtherTrait>::Assoc` makes no sense.
2129+
struct_span_err!(
2130+
tcx.sess,
2131+
tcx.def_span(alias_ty.def_id),
2132+
E0667,
2133+
"`impl Trait` is not allowed in path parameters"
2134+
)
2135+
.emit() // Already reported in an earlier stage.
20532136
} else {
2137+
// Find all the `impl`s that `qself_ty` has for any trait that has the
2138+
// associated type, so that we suggest the right one.
2139+
let infcx = tcx.infer_ctxt().build();
2140+
// We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
2141+
// to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
2142+
let param_env = ty::ParamEnv::empty();
2143+
let traits: Vec<_> = self
2144+
.tcx()
2145+
.all_traits()
2146+
.filter(|trait_def_id| {
2147+
// Consider only traits with the associated type
2148+
tcx.associated_items(*trait_def_id)
2149+
.in_definition_order()
2150+
.any(|i| {
2151+
i.kind.namespace() == Namespace::TypeNS
2152+
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
2153+
&& matches!(i.kind, ty::AssocKind::Type)
2154+
})
2155+
// Consider only accessible traits
2156+
&& tcx.visibility(*trait_def_id)
2157+
.is_accessible_from(self.item_def_id(), tcx)
2158+
&& tcx.all_impls(*trait_def_id)
2159+
.any(|impl_def_id| {
2160+
let trait_ref = tcx.bound_impl_trait_ref(impl_def_id);
2161+
trait_ref.map_or(false, |trait_ref| {
2162+
let impl_ = trait_ref.subst(
2163+
tcx,
2164+
infcx.fresh_substs_for_item(span, impl_def_id),
2165+
);
2166+
infcx
2167+
.can_eq(
2168+
param_env,
2169+
tcx.erase_regions(impl_.self_ty()),
2170+
tcx.erase_regions(qself_ty),
2171+
)
2172+
.is_ok()
2173+
})
2174+
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
2175+
})
2176+
})
2177+
.map(|trait_def_id| tcx.def_path_str(trait_def_id))
2178+
.collect();
2179+
20542180
// Don't print `TyErr` to the user.
20552181
self.report_ambiguous_associated_type(
20562182
span,
2057-
&qself_ty.to_string(),
2058-
"Trait",
2183+
&[qself_ty.to_string()],
2184+
&traits,
20592185
assoc_ident.name,
20602186
)
20612187
};
@@ -2173,16 +2299,30 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
21732299
let is_part_of_self_trait_constraints = def_id == trait_def_id;
21742300
let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id);
21752301

2176-
let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
2177-
"Self"
2302+
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
2303+
vec!["Self".to_string()]
21782304
} else {
2179-
"Type"
2305+
// Find all the types that have an `impl` for the trait.
2306+
tcx.all_impls(trait_def_id)
2307+
.filter(|impl_def_id| {
2308+
// Consider only accessible traits
2309+
tcx.visibility(*impl_def_id).is_accessible_from(self.item_def_id(), tcx)
2310+
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
2311+
})
2312+
.filter_map(|impl_def_id| tcx.impl_trait_ref(impl_def_id))
2313+
.map(|impl_| impl_.self_ty())
2314+
// We don't care about blanket impls.
2315+
.filter(|self_ty| !self_ty.has_non_region_param())
2316+
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
2317+
.collect()
21802318
};
2181-
2319+
// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
2320+
// references the trait. Relevant for the first case in
2321+
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
21822322
let reported = self.report_ambiguous_associated_type(
21832323
span,
2184-
type_name,
2185-
&path_str,
2324+
&type_names,
2325+
&[path_str],
21862326
item_segment.ident.name,
21872327
);
21882328
return tcx.ty_error_with_guaranteed(reported)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
trait Trait<A> {}
3+
4+
trait Assoc {
5+
type Ty;
6+
}
7+
8+
impl<A> Assoc for dyn Trait<A> {
9+
type Ty = i32;
10+
}
11+
12+
fn main() {
13+
let _x: <dyn Trait<i32> as Assoc>::Ty; //~ ERROR ambiguous associated type
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
trait Trait<A> {}
3+
4+
trait Assoc {
5+
type Ty;
6+
}
7+
8+
impl<A> Assoc for dyn Trait<A> {
9+
type Ty = i32;
10+
}
11+
12+
fn main() {
13+
let _x: <dyn Trait<i32>>::Ty; //~ ERROR ambiguous associated type
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0223]: ambiguous associated type
2+
--> $DIR/ambiguous-associated-type-with-generics.rs:13:13
3+
|
4+
LL | let _x: <dyn Trait<i32>>::Ty;
5+
| ^^^^^^^^^^^^^^^^^^^^ help: use the fully-qualified path: `<dyn Trait<i32> as Assoc>::Ty`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0223`.

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)