Skip to content

Commit

Permalink
Rollup merge of #106585 - estebank:issue-46585, r=compiler-errors
Browse files Browse the repository at this point in the history
When suggesting writing a fully qualified path probe for appropriate types

Address the more common part of #46585.
  • Loading branch information
matthiaskrgr authored Jan 13, 2023
2 parents f9dde54 + c6f322b commit c6e3a47
Show file tree
Hide file tree
Showing 21 changed files with 324 additions and 55 deletions.
174 changes: 157 additions & 17 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{walk_generics, Visitor as _};
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
Expand Down Expand Up @@ -1643,8 +1644,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
fn report_ambiguous_associated_type(
&self,
span: Span,
type_str: &str,
trait_str: &str,
types: &[String],
traits: &[String],
name: Symbol,
) -> ErrorGuaranteed {
let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
Expand All @@ -1655,19 +1656,92 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.keys()
.any(|full_span| full_span.contains(span))
{
err.span_suggestion(
err.span_suggestion_verbose(
span.shrink_to_lo(),
"you are looking for the module in `std`, not the primitive type",
"std::",
Applicability::MachineApplicable,
);
} else {
err.span_suggestion(
span,
"use fully-qualified syntax",
format!("<{} as {}>::{}", type_str, trait_str, name),
Applicability::HasPlaceholders,
);
match (types, traits) {
([], []) => {
err.span_suggestion_verbose(
span,
&format!(
"if there were a type named `Type` that implements a trait named \
`Trait` with associated type `{name}`, you could use the \
fully-qualified path",
),
format!("<Type as Trait>::{name}"),
Applicability::HasPlaceholders,
);
}
([], [trait_str]) => {
err.span_suggestion_verbose(
span,
&format!(
"if there were a type named `Example` that implemented `{trait_str}`, \
you could use the fully-qualified path",
),
format!("<Example as {trait_str}>::{name}"),
Applicability::HasPlaceholders,
);
}
([], traits) => {
err.span_suggestions(
span,
&format!(
"if there were a type named `Example` that implemented one of the \
traits with associated type `{name}`, you could use the \
fully-qualified path",
),
traits
.iter()
.map(|trait_str| format!("<Example as {trait_str}>::{name}"))
.collect::<Vec<_>>(),
Applicability::HasPlaceholders,
);
}
([type_str], []) => {
err.span_suggestion_verbose(
span,
&format!(
"if there were a trait named `Example` with associated type `{name}` \
implemented for `{type_str}`, you could use the fully-qualified path",
),
format!("<{type_str} as Example>::{name}"),
Applicability::HasPlaceholders,
);
}
(types, []) => {
err.span_suggestions(
span,
&format!(
"if there were a trait named `Example` with associated type `{name}` \
implemented for one of the types, you could use the fully-qualified \
path",
),
types
.into_iter()
.map(|type_str| format!("<{type_str} as Example>::{name}")),
Applicability::HasPlaceholders,
);
}
(types, traits) => {
let mut suggestions = vec![];
for type_str in types {
for trait_str in traits {
suggestions.push(format!("<{type_str} as {trait_str}>::{name}"));
}
}
err.span_suggestions(
span,
"use the fully-qualified path",
suggestions,
Applicability::MachineApplicable,
);
}
}
}
err.emit()
}
Expand Down Expand Up @@ -2050,12 +2124,64 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
err.emit()
} else if let Err(reported) = qself_ty.error_reported() {
reported
} else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
// `<impl Trait as OtherTrait>::Assoc` makes no sense.
struct_span_err!(
tcx.sess,
tcx.def_span(alias_ty.def_id),
E0667,
"`impl Trait` is not allowed in path parameters"
)
.emit() // Already reported in an earlier stage.
} else {
// Find all the `impl`s that `qself_ty` has for any trait that has the
// associated type, so that we suggest the right one.
let infcx = tcx.infer_ctxt().build();
// We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
// to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
let param_env = ty::ParamEnv::empty();
let traits: Vec<_> = self
.tcx()
.all_traits()
.filter(|trait_def_id| {
// Consider only traits with the associated type
tcx.associated_items(*trait_def_id)
.in_definition_order()
.any(|i| {
i.kind.namespace() == Namespace::TypeNS
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
&& matches!(i.kind, ty::AssocKind::Type)
})
// Consider only accessible traits
&& tcx.visibility(*trait_def_id)
.is_accessible_from(self.item_def_id(), tcx)
&& tcx.all_impls(*trait_def_id)
.any(|impl_def_id| {
let trait_ref = tcx.bound_impl_trait_ref(impl_def_id);
trait_ref.map_or(false, |trait_ref| {
let impl_ = trait_ref.subst(
tcx,
infcx.fresh_substs_for_item(span, impl_def_id),
);
infcx
.can_eq(
param_env,
tcx.erase_regions(impl_.self_ty()),
tcx.erase_regions(qself_ty),
)
.is_ok()
})
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
})
})
.map(|trait_def_id| tcx.def_path_str(trait_def_id))
.collect();

// Don't print `TyErr` to the user.
self.report_ambiguous_associated_type(
span,
&qself_ty.to_string(),
"Trait",
&[qself_ty.to_string()],
&traits,
assoc_ident.name,
)
};
Expand Down Expand Up @@ -2173,16 +2299,30 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let is_part_of_self_trait_constraints = def_id == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id);

let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
"Self"
let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
vec!["Self".to_string()]
} else {
"Type"
// Find all the types that have an `impl` for the trait.
tcx.all_impls(trait_def_id)
.filter(|impl_def_id| {
// Consider only accessible traits
tcx.visibility(*impl_def_id).is_accessible_from(self.item_def_id(), tcx)
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
})
.filter_map(|impl_def_id| tcx.impl_trait_ref(impl_def_id))
.map(|impl_| impl_.self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
.collect()
};

// FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
// references the trait. Relevant for the first case in
// `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
let reported = self.report_ambiguous_associated_type(
span,
type_name,
&path_str,
&type_names,
&[path_str],
item_segment.ident.name,
);
return tcx.ty_error_with_guaranteed(reported)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// run-rustfix
trait Trait<A> {}

trait Assoc {
type Ty;
}

impl<A> Assoc for dyn Trait<A> {
type Ty = i32;
}

fn main() {
let _x: <dyn Trait<i32> as Assoc>::Ty; //~ ERROR ambiguous associated type
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// run-rustfix
trait Trait<A> {}

trait Assoc {
type Ty;
}

impl<A> Assoc for dyn Trait<A> {
type Ty = i32;
}

fn main() {
let _x: <dyn Trait<i32>>::Ty; //~ ERROR ambiguous associated type
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
error[E0223]: ambiguous associated type
--> $DIR/ambiguous-associated-type-with-generics.rs:13:13
|
LL | let _x: <dyn Trait<i32>>::Ty;
| ^^^^^^^^^^^^^^^^^^^^ help: use the fully-qualified path: `<dyn Trait<i32> as Assoc>::Ty`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0223`.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ error[E0223]: ambiguous associated type
--> $DIR/associated-item-duplicate-names-3.rs:18:12
|
LL | let x: Baz::Bar = 5;
| ^^^^^^^^ help: use fully-qualified syntax: `<Baz as Trait>::Bar`
| ^^^^^^^^ help: use the fully-qualified path: `<Baz as Foo>::Bar`

error: aborting due to 2 previous errors

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

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

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

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

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

error: aborting due to 5 previous errors

Expand Down
Loading

0 comments on commit c6e3a47

Please sign in to comment.