Fix suggestion for E0404 not dealing with multiple generics
This commit is contained in:
parent
314c39d2ea
commit
32ae8810fc
7 changed files with 182 additions and 36 deletions
|
@ -32,6 +32,10 @@ pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
|
||||||
State::new().bounds_to_string(bounds)
|
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 {
|
pub fn pat_to_string(pat: &ast::Pat) -> String {
|
||||||
State::new().pat_to_string(pat)
|
State::new().pat_to_string(pat)
|
||||||
}
|
}
|
||||||
|
|
|
@ -824,6 +824,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
|
||||||
Self::to_string(|s| s.print_type_bounds(bounds))
|
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 {
|
fn pat_to_string(&self, pat: &ast::Pat) -> String {
|
||||||
Self::to_string(|s| s.print_pat(pat))
|
Self::to_string(|s| s.print_pat(pat))
|
||||||
}
|
}
|
||||||
|
|
|
@ -623,19 +623,8 @@ impl<'a> State<'a> {
|
||||||
|
|
||||||
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
|
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
|
||||||
match predicate {
|
match predicate {
|
||||||
ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
|
ast::WherePredicate::BoundPredicate(where_bound_predicate) => {
|
||||||
bound_generic_params,
|
self.print_where_bound_predicate(where_bound_predicate);
|
||||||
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::RegionPredicate(ast::WhereRegionPredicate {
|
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
|
||||||
lifetime,
|
lifetime,
|
||||||
|
@ -658,6 +647,19 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
fn print_use_tree(&mut self, tree: &ast::UseTree) {
|
||||||
match &tree.kind {
|
match &tree.kind {
|
||||||
ast::UseTreeKind::Simple(rename) => {
|
ast::UseTreeKind::Simple(rename) => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rustc_ast::{
|
||||||
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
||||||
MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
|
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_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::{
|
use rustc_errors::{
|
||||||
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||||
|
@ -1050,7 +1050,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Confirm that the target is an associated type.
|
// 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.
|
// use this to verify that ident is a type param.
|
||||||
let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
|
let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1079,7 +1079,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if let (
|
if let (
|
||||||
[ast::PathSegment { ident: constrain_ident, args: None, .. }],
|
[ast::PathSegment { args: None, .. }],
|
||||||
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
|
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
|
||||||
) = (&type_param_path.segments[..], &bounds[..])
|
) = (&type_param_path.segments[..], &bounds[..])
|
||||||
{
|
{
|
||||||
|
@ -1087,29 +1087,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||||
&poly_trait_ref.trait_ref.path.segments[..]
|
&poly_trait_ref.trait_ref.path.segments[..]
|
||||||
{
|
{
|
||||||
if ident.span == span {
|
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(
|
err.span_suggestion_verbose(
|
||||||
*where_span,
|
*where_span,
|
||||||
format!("constrain the associated type to `{}`", ident),
|
format!("constrain the associated type to `{}`", ident),
|
||||||
format!(
|
where_bound_predicate_to_string(&new_where_bound_predicate),
|
||||||
"{}: {}<{} = {}>",
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2605,6 +2587,70 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
/// Report lifetime/lifetime shadowing as an error.
|
||||||
pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
|
pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
|
||||||
let mut err = struct_span_err!(
|
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…
Add table
Add a link
Reference in a new issue