Fix suggestion for E0404 not dealing with multiple generics
This commit is contained in:
parent
314c39d2ea
commit
32ae8810fc
@ -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)
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -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!(
|
||||
|
@ -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() {}
|
31
tests/ui/resolve/issue-112472-multi-generics-suggestion.rs
Normal file
31
tests/ui/resolve/issue-112472-multi-generics-suggestion.rs
Normal 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() {}
|
@ -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`.
|
Loading…
Reference in New Issue
Block a user