Skip to content

Commit

Permalink
Emit suggestions when equality constraints are wronlgy used
Browse files Browse the repository at this point in the history
  • Loading branch information
gurry committed Mar 22, 2024
1 parent 0ad927c commit 560d463
Show file tree
Hide file tree
Showing 14 changed files with 382 additions and 25 deletions.
88 changes: 81 additions & 7 deletions compiler/rustc_hir_analysis/src/astconv/errors.rs
Expand Up @@ -15,11 +15,13 @@ use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::traits::FulfillmentError;
use rustc_middle::query::Key;
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{
self, suggest_constraining_type_param, GenericParamCount, Ty, TyCtxt, TypeVisitableExt,
};
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_span::{BytePos, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::traits::object_safety_violations_for_assoc_item;

impl<'tcx> dyn AstConv<'tcx> + '_ {
Expand Down Expand Up @@ -1029,12 +1031,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
/// Emits an error regarding forbidden type binding associations
pub fn prohibit_assoc_ty_binding(
tcx: TyCtxt<'_>,
span: Span,
segment: Option<(&hir::PathSegment<'_>, Span)>,
binding: &hir::TypeBinding<'_>,
segment: Option<(DefId, &hir::PathSegment<'_>, Span)>,
) {
tcx.dcx().emit_err(AssocTypeBindingNotAllowed {
span,
fn_trait_expansion: if let Some((segment, span)) = segment
let mut err = tcx.dcx().create_err(AssocTypeBindingNotAllowed {
span: binding.span,
fn_trait_expansion: if let Some((_, segment, span)) = segment
&& segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar
{
Some(ParenthesizedFnTraitExpansion {
Expand All @@ -1045,6 +1047,78 @@ pub fn prohibit_assoc_ty_binding(
None
},
});

// Emit a suggestion if possible
if let Some((def_id, segment, _)) = segment
&& segment.args().parenthesized == hir::GenericArgsParentheses::No
&& let hir::TypeBindingKind::Equality { term: binding_arg_ty } = binding.kind
{
// Suggests removal of the offending equality constraint
let suggest_removal = |e: &mut Diag<'_>| {
let mut suggestion_span = binding.span.with_hi(binding.span.hi() + BytePos(1)); // Include the comma or the angle bracket at the end
if segment.args().bindings.len() == 1 {
// If it is the only binding specified
// include the starting angle bracket as well
suggestion_span = suggestion_span.with_lo(suggestion_span.lo() - BytePos(1))
}
if let Ok(suggestion) = tcx.sess.source_map().span_to_snippet(suggestion_span) {
e.span_suggestion_verbose(
suggestion_span,
"try removing this type binding",
suggestion,
Applicability::MaybeIncorrect,
);
}
};

// Suggests replacing the quality constraint with
// normal type argument
let suggest_direct_use = |e: &mut Diag<'_>, sp: Span, is_ty: bool| {
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sp) {
let ty_or_const = if is_ty { "generic" } else { "const" };
e.span_suggestion_verbose(
binding.span,
format!("to use `{snippet}` as a {ty_or_const} argument specify it directly"),
snippet,
Applicability::MaybeIncorrect,
);
}
};

// Get a sense of what generic args the type expects
let generics = tcx.generics_of(def_id);
let GenericParamCount { mut types, consts, .. } = generics.own_counts();
if generics.has_self {
types -= 1 // Ignore the `Self` type
}

// Now emit suggestion
if types == 0 && consts == 0 {
err.note(format!("`{0}` is not a generic type", segment.ident));
suggest_removal(&mut err);
} else {
match binding_arg_ty {
hir::Term::Ty(ty) => {
if types > 0 {
suggest_direct_use(&mut err, ty.span, true);
} else {
suggest_removal(&mut err);
}
}
hir::Term::Const(c) if consts > 0 => {
if consts > 0 {
let span = tcx.hir().span(c.hir_id);
suggest_direct_use(&mut err, span, false);
} else {
suggest_removal(&mut err);
}
}
_ => {}
}
}
}

err.emit();
}

pub(crate) fn fn_trait_to_string(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/astconv/generics.rs
Expand Up @@ -456,7 +456,7 @@ pub(crate) fn check_generic_arg_count(
if gen_pos != GenericArgPosition::Type
&& let Some(b) = gen_args.bindings.first()
{
prohibit_assoc_ty_binding(tcx, b.span, None);
prohibit_assoc_ty_binding(tcx, b, None);
}

let explicit_late_bound =
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Expand Up @@ -288,7 +288,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
ty::BoundConstness::NotConst,
);
if let Some(b) = item_segment.args().bindings.first() {
prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span)));
prohibit_assoc_ty_binding(self.tcx(), b, Some((def_id, item_segment, span)));
}

args
Expand Down Expand Up @@ -591,7 +591,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
);

if let Some(b) = item_segment.args().bindings.first() {
prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span)));
prohibit_assoc_ty_binding(self.tcx(), b, Some((item_def_id, item_segment, span)));
}

args
Expand Down Expand Up @@ -727,7 +727,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
constness,
);
if let Some(b) = trait_segment.args().bindings.first() {
prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span)));
prohibit_assoc_ty_binding(self.tcx(), b, Some((trait_def_id, trait_segment, span)));
}
ty::TraitRef::new(self.tcx(), trait_def_id, generic_args)
}
Expand Down Expand Up @@ -1676,7 +1676,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
for segment in segments {
// Only emit the first error to avoid overloading the user with error messages.
if let Some(b) = segment.args().bindings.first() {
prohibit_assoc_ty_binding(self.tcx(), b.span, None);
prohibit_assoc_ty_binding(self.tcx(), b, None);
return true;
}
}
Expand Down
8 changes: 7 additions & 1 deletion tests/ui/associated-type-bounds/issue-102335-ty.rs
@@ -1,5 +1,11 @@
trait T {
type A: S<C<i32 = u32> = ()>;
type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
//~^ ERROR associated type bindings are not allowed here
//~| ERROR associated type bindings are not allowed here
}

trait T2 {
type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
//~^ ERROR associated type bindings are not allowed here
//~| ERROR associated type bindings are not allowed here
}
Expand Down
38 changes: 35 additions & 3 deletions tests/ui/associated-type-bounds/issue-102335-ty.stderr
@@ -1,17 +1,49 @@
error[E0229]: associated type bindings are not allowed here
--> $DIR/issue-102335-ty.rs:2:17
|
LL | type A: S<C<i32 = u32> = ()>;
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
| ^^^^^^^^^ associated type not allowed here
|
help: to use `u32` as a generic argument specify it directly
|
LL | type A: S<C<u32> = ()>; // Just one erroneous equality constraint
| ~~~

error[E0229]: associated type bindings are not allowed here
--> $DIR/issue-102335-ty.rs:2:17
|
LL | type A: S<C<i32 = u32> = ()>;
LL | type A: S<C<i32 = u32> = ()>; // Just one erroneous equality constraint
| ^^^^^^^^^ associated type not allowed here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: to use `u32` as a generic argument specify it directly
|
LL | type A: S<C<u32> = ()>; // Just one erroneous equality constraint
| ~~~

error[E0229]: associated type bindings are not allowed here
--> $DIR/issue-102335-ty.rs:8:17
|
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
| ^^^^^^^^^ associated type not allowed here
|
help: to use `u32` as a generic argument specify it directly
|
LL | type A: S<C<u32, X = i32> = ()>; // More than one erroneous equality constraints
| ~~~

error[E0229]: associated type bindings are not allowed here
--> $DIR/issue-102335-ty.rs:8:17
|
LL | type A: S<C<i32 = u32, X = i32> = ()>; // More than one erroneous equality constraints
| ^^^^^^^^^ associated type not allowed here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: to use `u32` as a generic argument specify it directly
|
LL | type A: S<C<u32, X = i32> = ()>; // More than one erroneous equality constraints
| ~~~

error: aborting due to 2 previous errors
error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0229`.
54 changes: 52 additions & 2 deletions tests/ui/associated-types/associated-types-eq-2.rs
@@ -1,19 +1,69 @@
// Test equality constraints on associated types. Check we get an error when an
// equality constraint is used in a qualified path.
// equality constraint is used outside of type parameter declarations

pub trait Foo {
type A;
fn boo(&self) -> <Self as Foo>::A;
}

struct Bar;
struct Qux;

impl Foo for isize {
type A = usize;
fn boo(&self) -> usize { 42 }
}

fn baz<I: Foo>(x: &<I as Foo<A=Bar>>::A) {}
fn baz<I: Foo>(_x: &<I as Foo<A=Bar>>::A) {}
//~^ ERROR associated type bindings are not allowed here


trait Tr1<T1> {
}

impl Tr1<T1 = String> for Bar {
//~^ ERROR associated type bindings are not allowed here
//~| ERROR trait takes 1 generic argument but 0 generic arguments were supplied
}


trait Tr2<T1, T2, T3> {
}

// E0229 is emitted only for the first erroneous equality
// constraint (T2) not for any subequent ones (e.g. T3)
impl Tr2<i32, T2 = Qux, T3 = usize> for Bar {
//~^ ERROR associated type bindings are not allowed here
//~| ERROR trait takes 3 generic arguments but 1 generic argument was supplied
}

struct GenericStruct<T> { _t: T }

impl Tr2<i32, Qux, T3 = GenericStruct<i32>> for Bar {
//~^ ERROR associated type bindings are not allowed here
//~| ERROR trait takes 3 generic arguments but 2 generic arguments were supplied
}


// Covers the case when the type has a const param
trait Tr3<const N: i32, T2, T3> {
}

impl Tr3<N = 42, T2 = Qux, T3 = usize> for Bar {
//~^ ERROR associated type bindings are not allowed here
//~| ERROR associated const equality is incomplete
//~| ERROR trait takes 3 generic arguments but 0 generic arguments were supplied
}


// Covers the case when the type
// in question has no generic params
trait Tr4 {
}

impl Tr4<T = Qux, T2 = usize> for Bar {
//~^ ERROR associated type bindings are not allowed here
}


pub fn main() {}

0 comments on commit 560d463

Please sign in to comment.