Skip to content

Commit 82eb4a0

Browse files
authored
Rollup merge of #112486 - jieyouxu:issue-112472, r=oli-obk
Fix suggestion for E0404 not dealing with multiple generics Fixes #112472.
2 parents 9bc95a4 + 32ae881 commit 82eb4a0

File tree

7 files changed

+182
-36
lines changed

7 files changed

+182
-36
lines changed

compiler/rustc_ast_pretty/src/pprust/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
3232
State::new().bounds_to_string(bounds)
3333
}
3434

35+
pub fn where_bound_predicate_to_string(where_bound_predicate: &ast::WhereBoundPredicate) -> String {
36+
State::new().where_bound_predicate_to_string(where_bound_predicate)
37+
}
38+
3539
pub fn pat_to_string(pat: &ast::Pat) -> String {
3640
State::new().pat_to_string(pat)
3741
}

compiler/rustc_ast_pretty/src/pprust/state.rs

+7
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
824824
Self::to_string(|s| s.print_type_bounds(bounds))
825825
}
826826

827+
fn where_bound_predicate_to_string(
828+
&self,
829+
where_bound_predicate: &ast::WhereBoundPredicate,
830+
) -> String {
831+
Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
832+
}
833+
827834
fn pat_to_string(&self, pat: &ast::Pat) -> String {
828835
Self::to_string(|s| s.print_pat(pat))
829836
}

compiler/rustc_ast_pretty/src/pprust/state/item.rs

+15-13
Original file line numberDiff line numberDiff line change
@@ -623,19 +623,8 @@ impl<'a> State<'a> {
623623

624624
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
625625
match predicate {
626-
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
627-
bound_generic_params,
628-
bounded_ty,
629-
bounds,
630-
..
631-
}) => {
632-
self.print_formal_generic_params(bound_generic_params);
633-
self.print_type(bounded_ty);
634-
self.word(":");
635-
if !bounds.is_empty() {
636-
self.nbsp();
637-
self.print_type_bounds(bounds);
638-
}
626+
ast::WherePredicate::BoundPredicate(where_bound_predicate) => {
627+
self.print_where_bound_predicate(where_bound_predicate);
639628
}
640629
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
641630
lifetime,
@@ -658,6 +647,19 @@ impl<'a> State<'a> {
658647
}
659648
}
660649

650+
pub fn print_where_bound_predicate(
651+
&mut self,
652+
where_bound_predicate: &ast::WhereBoundPredicate,
653+
) {
654+
self.print_formal_generic_params(&where_bound_predicate.bound_generic_params);
655+
self.print_type(&where_bound_predicate.bounded_ty);
656+
self.word(":");
657+
if !where_bound_predicate.bounds.is_empty() {
658+
self.nbsp();
659+
self.print_type_bounds(&where_bound_predicate.bounds);
660+
}
661+
}
662+
661663
fn print_use_tree(&mut self, tree: &ast::UseTree) {
662664
match &tree.kind {
663665
ast::UseTreeKind::Simple(rename) => {

compiler/rustc_resolve/src/late/diagnostics.rs

+69-23
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_ast::{
1010
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
1111
MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
1212
};
13-
use rustc_ast_pretty::pprust::path_segment_to_string;
13+
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
1414
use rustc_data_structures::fx::FxHashSet;
1515
use rustc_errors::{
1616
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -1050,7 +1050,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
10501050
};
10511051

10521052
// Confirm that the target is an associated type.
1053-
let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
1053+
let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
10541054
// use this to verify that ident is a type param.
10551055
let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
10561056
return false;
@@ -1079,37 +1079,19 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
10791079
return false;
10801080
}
10811081
if let (
1082-
[ast::PathSegment { ident: constrain_ident, args: None, .. }],
1082+
[ast::PathSegment { args: None, .. }],
10831083
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
10841084
) = (&type_param_path.segments[..], &bounds[..])
10851085
{
10861086
if let [ast::PathSegment { ident, args: None, .. }] =
10871087
&poly_trait_ref.trait_ref.path.segments[..]
10881088
{
10891089
if ident.span == span {
1090+
let Some(new_where_bound_predicate) = mk_where_bound_predicate(path, poly_trait_ref, ty) else { return false; };
10901091
err.span_suggestion_verbose(
10911092
*where_span,
10921093
format!("constrain the associated type to `{}`", ident),
1093-
format!(
1094-
"{}: {}<{} = {}>",
1095-
self.r
1096-
.tcx
1097-
.sess
1098-
.source_map()
1099-
.span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`.
1100-
.unwrap_or_else(|_| constrain_ident.to_string()),
1101-
path.segments[..position]
1102-
.iter()
1103-
.map(|segment| path_segment_to_string(segment))
1104-
.collect::<Vec<_>>()
1105-
.join("::"),
1106-
path.segments[position..]
1107-
.iter()
1108-
.map(|segment| path_segment_to_string(segment))
1109-
.collect::<Vec<_>>()
1110-
.join("::"),
1111-
ident,
1112-
),
1094+
where_bound_predicate_to_string(&new_where_bound_predicate),
11131095
Applicability::MaybeIncorrect,
11141096
);
11151097
}
@@ -2605,6 +2587,70 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
26052587
}
26062588
}
26072589

2590+
fn mk_where_bound_predicate(
2591+
path: &Path,
2592+
poly_trait_ref: &ast::PolyTraitRef,
2593+
ty: &ast::Ty,
2594+
) -> Option<ast::WhereBoundPredicate> {
2595+
use rustc_span::DUMMY_SP;
2596+
let modified_segments = {
2597+
let mut segments = path.segments.clone();
2598+
let [preceding @ .., second_last, last] = segments.as_mut_slice() else { return None; };
2599+
let mut segments = ThinVec::from(preceding);
2600+
2601+
let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint {
2602+
id: DUMMY_NODE_ID,
2603+
ident: last.ident,
2604+
gen_args: None,
2605+
kind: ast::AssocConstraintKind::Equality {
2606+
term: ast::Term::Ty(ast::ptr::P(ast::Ty {
2607+
kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()),
2608+
id: DUMMY_NODE_ID,
2609+
span: DUMMY_SP,
2610+
tokens: None,
2611+
})),
2612+
},
2613+
span: DUMMY_SP,
2614+
});
2615+
2616+
match second_last.args.as_deref_mut() {
2617+
Some(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { args, .. })) => {
2618+
args.push(added_constraint);
2619+
}
2620+
Some(_) => return None,
2621+
None => {
2622+
second_last.args =
2623+
Some(ast::ptr::P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs {
2624+
args: ThinVec::from([added_constraint]),
2625+
span: DUMMY_SP,
2626+
})));
2627+
}
2628+
}
2629+
2630+
segments.push(second_last.clone());
2631+
segments
2632+
};
2633+
2634+
let new_where_bound_predicate = ast::WhereBoundPredicate {
2635+
span: DUMMY_SP,
2636+
bound_generic_params: ThinVec::new(),
2637+
bounded_ty: ast::ptr::P(ty.clone()),
2638+
bounds: vec![ast::GenericBound::Trait(
2639+
ast::PolyTraitRef {
2640+
bound_generic_params: ThinVec::new(),
2641+
trait_ref: ast::TraitRef {
2642+
path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
2643+
ref_id: DUMMY_NODE_ID,
2644+
},
2645+
span: DUMMY_SP,
2646+
},
2647+
ast::TraitBoundModifier::None,
2648+
)],
2649+
};
2650+
2651+
Some(new_where_bound_predicate)
2652+
}
2653+
26082654
/// Report lifetime/lifetime shadowing as an error.
26092655
pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
26102656
let mut err = struct_span_err!(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
3+
use std::fmt::Debug;
4+
use std::marker::PhantomData;
5+
use std::convert::{self, TryFrom};
6+
7+
#[allow(unused)]
8+
struct Codec<EncodeLine, DecodeLine> {
9+
phantom_decode: PhantomData<DecodeLine>,
10+
phantom_encode: PhantomData<EncodeLine>,
11+
}
12+
13+
pub enum ParseError {}
14+
15+
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
16+
DecodeLine: Debug + convert::TryFrom<String>,
17+
DecodeLine: convert::TryFrom<String, Error = ParseError>,
18+
//~^ ERROR expected trait, found enum `ParseError`
19+
//~| HELP constrain the associated type to `ParseError`
20+
{
21+
}
22+
23+
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
24+
DecodeLine: Debug + TryFrom<String>,
25+
DecodeLine: TryFrom<String, Error = ParseError>,
26+
//~^ ERROR expected trait, found enum `ParseError`
27+
//~| HELP constrain the associated type to `ParseError`
28+
{
29+
}
30+
31+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// run-rustfix
2+
3+
use std::fmt::Debug;
4+
use std::marker::PhantomData;
5+
use std::convert::{self, TryFrom};
6+
7+
#[allow(unused)]
8+
struct Codec<EncodeLine, DecodeLine> {
9+
phantom_decode: PhantomData<DecodeLine>,
10+
phantom_encode: PhantomData<EncodeLine>,
11+
}
12+
13+
pub enum ParseError {}
14+
15+
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
16+
DecodeLine: Debug + convert::TryFrom<String>,
17+
<DecodeLine as convert::TryFrom<String>>::Error: ParseError,
18+
//~^ ERROR expected trait, found enum `ParseError`
19+
//~| HELP constrain the associated type to `ParseError`
20+
{
21+
}
22+
23+
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
24+
DecodeLine: Debug + TryFrom<String>,
25+
<DecodeLine as TryFrom<String>>::Error: ParseError,
26+
//~^ ERROR expected trait, found enum `ParseError`
27+
//~| HELP constrain the associated type to `ParseError`
28+
{
29+
}
30+
31+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0404]: expected trait, found enum `ParseError`
2+
--> $DIR/issue-112472-multi-generics-suggestion.rs:17:54
3+
|
4+
LL | <DecodeLine as convert::TryFrom<String>>::Error: ParseError,
5+
| ^^^^^^^^^^ not a trait
6+
|
7+
help: constrain the associated type to `ParseError`
8+
|
9+
LL | DecodeLine: convert::TryFrom<String, Error = ParseError>,
10+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11+
12+
error[E0404]: expected trait, found enum `ParseError`
13+
--> $DIR/issue-112472-multi-generics-suggestion.rs:25:45
14+
|
15+
LL | <DecodeLine as TryFrom<String>>::Error: ParseError,
16+
| ^^^^^^^^^^ not a trait
17+
|
18+
help: constrain the associated type to `ParseError`
19+
|
20+
LL | DecodeLine: TryFrom<String, Error = ParseError>,
21+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0404`.

0 commit comments

Comments
 (0)