Skip to content

Commit 5e23cc4

Browse files
committed
Given <T as Trait>::A: Ty suggest T: Trait<A = Ty>
Fix #75829
1 parent e89ce46 commit 5e23cc4

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

compiler/rustc_resolve/src/late.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,9 @@ struct DiagnosticMetadata<'ast> {
386386
in_if_condition: Option<&'ast Expr>,
387387

388388
current_trait_object: Option<&'ast [ast::GenericBound]>,
389+
390+
/// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
391+
current_where_predicate: Option<&'ast WherePredicate>,
389392
}
390393

391394
struct LateResolutionVisitor<'a, 'b, 'ast> {
@@ -667,6 +670,14 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
667670
}
668671
self.diagnostic_metadata.currently_processing_generics = prev;
669672
}
673+
674+
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
675+
debug!("visit_where_predicate {:?}", p);
676+
let previous_value =
677+
replace(&mut self.diagnostic_metadata.current_where_predicate, Some(p));
678+
visit::walk_where_predicate(self, p);
679+
self.diagnostic_metadata.current_where_predicate = previous_value;
680+
}
670681
}
671682

672683
impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use crate::{PathResult, PathSource, Segment};
88
use rustc_ast::util::lev_distance::find_best_match_for_name;
99
use rustc_ast::visit::FnKind;
1010
use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind};
11+
use rustc_ast_pretty::pprust::path_segment_to_string;
1112
use rustc_data_structures::fx::FxHashSet;
1213
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
1314
use rustc_hir as hir;
@@ -497,6 +498,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
497498
}
498499
}
499500
}
501+
502+
fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err);
503+
500504
if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) {
501505
fallback = true;
502506
match self.diagnostic_metadata.current_let_binding {
@@ -521,6 +525,120 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
521525
(err, candidates)
522526
}
523527

528+
/// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
529+
fn restrict_assoc_type_in_where_clause(
530+
&mut self,
531+
span: Span,
532+
err: &mut DiagnosticBuilder<'_>,
533+
) -> bool {
534+
// Detect that we are actually in a `where` predicate.
535+
let (bounded_ty, bounds, where_span) =
536+
if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
537+
bounded_ty,
538+
bound_generic_params,
539+
bounds,
540+
span,
541+
})) = self.diagnostic_metadata.current_where_predicate
542+
{
543+
if !bound_generic_params.is_empty() {
544+
return false;
545+
}
546+
(bounded_ty, bounds, span)
547+
} else {
548+
return false;
549+
};
550+
551+
// Confirm that the target is an associated type.
552+
let (ty, position, path) = if let ast::TyKind::Path(
553+
Some(ast::QSelf { ty, position, .. }),
554+
path,
555+
) = &bounded_ty.kind
556+
{
557+
// use this to verify that ident is a type param.
558+
let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
559+
bounded_ty.id,
560+
None,
561+
&Segment::from_path(path),
562+
Namespace::TypeNS,
563+
span,
564+
true,
565+
CrateLint::No,
566+
) {
567+
partial_res
568+
} else {
569+
return false;
570+
};
571+
if !(matches!(
572+
partial_res.base_res(),
573+
hir::def::Res::Def(hir::def::DefKind::AssocTy, _)
574+
) && partial_res.unresolved_segments() == 0)
575+
{
576+
return false;
577+
}
578+
(ty, position, path)
579+
} else {
580+
return false;
581+
};
582+
583+
if let ast::TyKind::Path(None, type_param_path) = &ty.kind {
584+
// Confirm that the `SelfTy` is a type parameter.
585+
let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
586+
bounded_ty.id,
587+
None,
588+
&Segment::from_path(type_param_path),
589+
Namespace::TypeNS,
590+
span,
591+
true,
592+
CrateLint::No,
593+
) {
594+
partial_res
595+
} else {
596+
return false;
597+
};
598+
if !(matches!(
599+
partial_res.base_res(),
600+
hir::def::Res::Def(hir::def::DefKind::TyParam, _)
601+
) && partial_res.unresolved_segments() == 0)
602+
{
603+
return false;
604+
}
605+
if let (
606+
[ast::PathSegment { ident, args: None, .. }],
607+
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
608+
) = (&type_param_path.segments[..], &bounds[..])
609+
{
610+
if let [ast::PathSegment { ident: bound_ident, args: None, .. }] =
611+
&poly_trait_ref.trait_ref.path.segments[..]
612+
{
613+
if bound_ident.span == span {
614+
err.span_suggestion_verbose(
615+
*where_span,
616+
&format!("constrain the associated type to `{}`", bound_ident),
617+
format!(
618+
"{}: {}<{} = {}>",
619+
ident,
620+
path.segments[..*position]
621+
.iter()
622+
.map(|segment| path_segment_to_string(segment))
623+
.collect::<Vec<_>>()
624+
.join("::"),
625+
path.segments[*position..]
626+
.iter()
627+
.map(|segment| path_segment_to_string(segment))
628+
.collect::<Vec<_>>()
629+
.join("::"),
630+
bound_ident,
631+
),
632+
Applicability::MaybeIncorrect,
633+
);
634+
}
635+
return true;
636+
}
637+
}
638+
}
639+
false
640+
}
641+
524642
/// Check if the source is call expression and the first argument is `self`. If true,
525643
/// return the span of whole call and the span for all arguments expect the first one (`self`).
526644
fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
trait Bar {
2+
type Baz;
3+
}
4+
5+
struct Foo<T> where T: Bar, <T as Bar>::Baz: String { //~ ERROR expected trait, found struct
6+
t: T,
7+
}
8+
9+
fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0404]: expected trait, found struct `String`
2+
--> $DIR/assoc_type_bound_with_struct.rs:5:46
3+
|
4+
LL | struct Foo<T> where T: Bar, <T as Bar>::Baz: String {
5+
| ^^^^^^ not a trait
6+
|
7+
::: $SRC_DIR/alloc/src/string.rs:LL:COL
8+
|
9+
LL | pub trait ToString {
10+
| ------------------ similarly named trait `ToString` defined here
11+
|
12+
help: constrain the associated type to `String`
13+
|
14+
LL | struct Foo<T> where T: Bar, T: Bar<Baz = String> {
15+
| ^^^^^^^^^^^^^^^^^^^^
16+
help: a trait with a similar name exists
17+
|
18+
LL | struct Foo<T> where T: Bar, <T as Bar>::Baz: ToString {
19+
| ^^^^^^^^
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0404`.

0 commit comments

Comments
 (0)