Fix suggestion for E0404 not dealing with multiple generics

This commit is contained in:
许杰友 Jieyou Xu (Joe) 2023-06-10 17:06:26 +08:00
parent 314c39d2ea
commit 32ae8810fc
No known key found for this signature in database
GPG key ID: C5FD5D32014FDB47
7 changed files with 182 additions and 36 deletions

View file

@ -32,6 +32,10 @@ pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
State::new().bounds_to_string(bounds)
}
pub fn where_bound_predicate_to_string(where_bound_predicate: &ast::WhereBoundPredicate) -> String {
State::new().where_bound_predicate_to_string(where_bound_predicate)
}
pub fn pat_to_string(pat: &ast::Pat) -> String {
State::new().pat_to_string(pat)
}

View file

@ -824,6 +824,13 @@ fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
Self::to_string(|s| s.print_type_bounds(bounds))
}
fn where_bound_predicate_to_string(
&self,
where_bound_predicate: &ast::WhereBoundPredicate,
) -> String {
Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
}
fn pat_to_string(&self, pat: &ast::Pat) -> String {
Self::to_string(|s| s.print_pat(pat))
}

View file

@ -623,19 +623,8 @@ pub(crate) fn print_where_clause_parts(
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
match predicate {
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
bound_generic_params,
bounded_ty,
bounds,
..
}) => {
self.print_formal_generic_params(bound_generic_params);
self.print_type(bounded_ty);
self.word(":");
if !bounds.is_empty() {
self.nbsp();
self.print_type_bounds(bounds);
}
ast::WherePredicate::BoundPredicate(where_bound_predicate) => {
self.print_where_bound_predicate(where_bound_predicate);
}
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
lifetime,
@ -658,6 +647,19 @@ pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
}
}
pub fn print_where_bound_predicate(
&mut self,
where_bound_predicate: &ast::WhereBoundPredicate,
) {
self.print_formal_generic_params(&where_bound_predicate.bound_generic_params);
self.print_type(&where_bound_predicate.bounded_ty);
self.word(":");
if !where_bound_predicate.bounds.is_empty() {
self.nbsp();
self.print_type_bounds(&where_bound_predicate.bounds);
}
}
fn print_use_tree(&mut self, tree: &ast::UseTree) {
match &tree.kind {
ast::UseTreeKind::Simple(rename) => {

View file

@ -10,7 +10,7 @@
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
};
use rustc_ast_pretty::pprust::path_segment_to_string;
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@ -1050,7 +1050,7 @@ fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnost
};
// Confirm that the target is an associated type.
let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
// use this to verify that ident is a type param.
let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
return false;
@ -1079,7 +1079,7 @@ fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnost
return false;
}
if let (
[ast::PathSegment { ident: constrain_ident, args: None, .. }],
[ast::PathSegment { args: None, .. }],
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
) = (&type_param_path.segments[..], &bounds[..])
{
@ -1087,29 +1087,11 @@ fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnost
&poly_trait_ref.trait_ref.path.segments[..]
{
if ident.span == span {
let Some(new_where_bound_predicate) = mk_where_bound_predicate(path, poly_trait_ref, ty) else { return false; };
err.span_suggestion_verbose(
*where_span,
format!("constrain the associated type to `{}`", ident),
format!(
"{}: {}<{} = {}>",
self.r
.tcx
.sess
.source_map()
.span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`.
.unwrap_or_else(|_| constrain_ident.to_string()),
path.segments[..position]
.iter()
.map(|segment| path_segment_to_string(segment))
.collect::<Vec<_>>()
.join("::"),
path.segments[position..]
.iter()
.map(|segment| path_segment_to_string(segment))
.collect::<Vec<_>>()
.join("::"),
ident,
),
where_bound_predicate_to_string(&new_where_bound_predicate),
Applicability::MaybeIncorrect,
);
}
@ -2605,6 +2587,70 @@ fn add_missing_lifetime_specifiers_label(
}
}
fn mk_where_bound_predicate(
path: &Path,
poly_trait_ref: &ast::PolyTraitRef,
ty: &ast::Ty,
) -> Option<ast::WhereBoundPredicate> {
use rustc_span::DUMMY_SP;
let modified_segments = {
let mut segments = path.segments.clone();
let [preceding @ .., second_last, last] = segments.as_mut_slice() else { return None; };
let mut segments = ThinVec::from(preceding);
let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint {
id: DUMMY_NODE_ID,
ident: last.ident,
gen_args: None,
kind: ast::AssocConstraintKind::Equality {
term: ast::Term::Ty(ast::ptr::P(ast::Ty {
kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()),
id: DUMMY_NODE_ID,
span: DUMMY_SP,
tokens: None,
})),
},
span: DUMMY_SP,
});
match second_last.args.as_deref_mut() {
Some(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { args, .. })) => {
args.push(added_constraint);
}
Some(_) => return None,
None => {
second_last.args =
Some(ast::ptr::P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs {
args: ThinVec::from([added_constraint]),
span: DUMMY_SP,
})));
}
}
segments.push(second_last.clone());
segments
};
let new_where_bound_predicate = ast::WhereBoundPredicate {
span: DUMMY_SP,
bound_generic_params: ThinVec::new(),
bounded_ty: ast::ptr::P(ty.clone()),
bounds: vec![ast::GenericBound::Trait(
ast::PolyTraitRef {
bound_generic_params: ThinVec::new(),
trait_ref: ast::TraitRef {
path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
ref_id: DUMMY_NODE_ID,
},
span: DUMMY_SP,
},
ast::TraitBoundModifier::None,
)],
};
Some(new_where_bound_predicate)
}
/// Report lifetime/lifetime shadowing as an error.
pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
let mut err = struct_span_err!(

View file

@ -0,0 +1,31 @@
// run-rustfix
use std::fmt::Debug;
use std::marker::PhantomData;
use std::convert::{self, TryFrom};
#[allow(unused)]
struct Codec<EncodeLine, DecodeLine> {
phantom_decode: PhantomData<DecodeLine>,
phantom_encode: PhantomData<EncodeLine>,
}
pub enum ParseError {}
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
DecodeLine: Debug + convert::TryFrom<String>,
DecodeLine: convert::TryFrom<String, Error = ParseError>,
//~^ ERROR expected trait, found enum `ParseError`
//~| HELP constrain the associated type to `ParseError`
{
}
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
DecodeLine: Debug + TryFrom<String>,
DecodeLine: TryFrom<String, Error = ParseError>,
//~^ ERROR expected trait, found enum `ParseError`
//~| HELP constrain the associated type to `ParseError`
{
}
fn main() {}

View file

@ -0,0 +1,31 @@
// run-rustfix
use std::fmt::Debug;
use std::marker::PhantomData;
use std::convert::{self, TryFrom};
#[allow(unused)]
struct Codec<EncodeLine, DecodeLine> {
phantom_decode: PhantomData<DecodeLine>,
phantom_encode: PhantomData<EncodeLine>,
}
pub enum ParseError {}
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
DecodeLine: Debug + convert::TryFrom<String>,
<DecodeLine as convert::TryFrom<String>>::Error: ParseError,
//~^ ERROR expected trait, found enum `ParseError`
//~| HELP constrain the associated type to `ParseError`
{
}
impl<EncodeLine, DecodeLine> Codec<EncodeLine, DecodeLine> where
DecodeLine: Debug + TryFrom<String>,
<DecodeLine as TryFrom<String>>::Error: ParseError,
//~^ ERROR expected trait, found enum `ParseError`
//~| HELP constrain the associated type to `ParseError`
{
}
fn main() {}

View file

@ -0,0 +1,25 @@
error[E0404]: expected trait, found enum `ParseError`
--> $DIR/issue-112472-multi-generics-suggestion.rs:17:54
|
LL | <DecodeLine as convert::TryFrom<String>>::Error: ParseError,
| ^^^^^^^^^^ not a trait
|
help: constrain the associated type to `ParseError`
|
LL | DecodeLine: convert::TryFrom<String, Error = ParseError>,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0404]: expected trait, found enum `ParseError`
--> $DIR/issue-112472-multi-generics-suggestion.rs:25:45
|
LL | <DecodeLine as TryFrom<String>>::Error: ParseError,
| ^^^^^^^^^^ not a trait
|
help: constrain the associated type to `ParseError`
|
LL | DecodeLine: TryFrom<String, Error = ParseError>,
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0404`.