mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 07:22:42 +00:00
parent
e89ce461d3
commit
5e23cc4960
@ -386,6 +386,9 @@ struct DiagnosticMetadata<'ast> {
|
||||
in_if_condition: Option<&'ast Expr>,
|
||||
|
||||
current_trait_object: Option<&'ast [ast::GenericBound]>,
|
||||
|
||||
/// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
|
||||
current_where_predicate: Option<&'ast WherePredicate>,
|
||||
}
|
||||
|
||||
struct LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
@ -667,6 +670,14 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
|
||||
}
|
||||
self.diagnostic_metadata.currently_processing_generics = prev;
|
||||
}
|
||||
|
||||
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
|
||||
debug!("visit_where_predicate {:?}", p);
|
||||
let previous_value =
|
||||
replace(&mut self.diagnostic_metadata.current_where_predicate, Some(p));
|
||||
visit::walk_where_predicate(self, p);
|
||||
self.diagnostic_metadata.current_where_predicate = previous_value;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||
|
@ -8,6 +8,7 @@ use crate::{PathResult, PathSource, Segment};
|
||||
use rustc_ast::util::lev_distance::find_best_match_for_name;
|
||||
use rustc_ast::visit::FnKind;
|
||||
use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind};
|
||||
use rustc_ast_pretty::pprust::path_segment_to_string;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
@ -497,6 +498,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err);
|
||||
|
||||
if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) {
|
||||
fallback = true;
|
||||
match self.diagnostic_metadata.current_let_binding {
|
||||
@ -521,6 +525,120 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
(err, candidates)
|
||||
}
|
||||
|
||||
/// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
|
||||
fn restrict_assoc_type_in_where_clause(
|
||||
&mut self,
|
||||
span: Span,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
) -> bool {
|
||||
// Detect that we are actually in a `where` predicate.
|
||||
let (bounded_ty, bounds, where_span) =
|
||||
if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
|
||||
bounded_ty,
|
||||
bound_generic_params,
|
||||
bounds,
|
||||
span,
|
||||
})) = self.diagnostic_metadata.current_where_predicate
|
||||
{
|
||||
if !bound_generic_params.is_empty() {
|
||||
return false;
|
||||
}
|
||||
(bounded_ty, bounds, span)
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Confirm that the target is an associated type.
|
||||
let (ty, position, path) = if let ast::TyKind::Path(
|
||||
Some(ast::QSelf { ty, position, .. }),
|
||||
path,
|
||||
) = &bounded_ty.kind
|
||||
{
|
||||
// use this to verify that ident is a type param.
|
||||
let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
|
||||
bounded_ty.id,
|
||||
None,
|
||||
&Segment::from_path(path),
|
||||
Namespace::TypeNS,
|
||||
span,
|
||||
true,
|
||||
CrateLint::No,
|
||||
) {
|
||||
partial_res
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
if !(matches!(
|
||||
partial_res.base_res(),
|
||||
hir::def::Res::Def(hir::def::DefKind::AssocTy, _)
|
||||
) && partial_res.unresolved_segments() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
(ty, position, path)
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let ast::TyKind::Path(None, type_param_path) = &ty.kind {
|
||||
// Confirm that the `SelfTy` is a type parameter.
|
||||
let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
|
||||
bounded_ty.id,
|
||||
None,
|
||||
&Segment::from_path(type_param_path),
|
||||
Namespace::TypeNS,
|
||||
span,
|
||||
true,
|
||||
CrateLint::No,
|
||||
) {
|
||||
partial_res
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
if !(matches!(
|
||||
partial_res.base_res(),
|
||||
hir::def::Res::Def(hir::def::DefKind::TyParam, _)
|
||||
) && partial_res.unresolved_segments() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if let (
|
||||
[ast::PathSegment { ident, args: None, .. }],
|
||||
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
|
||||
) = (&type_param_path.segments[..], &bounds[..])
|
||||
{
|
||||
if let [ast::PathSegment { ident: bound_ident, args: None, .. }] =
|
||||
&poly_trait_ref.trait_ref.path.segments[..]
|
||||
{
|
||||
if bound_ident.span == span {
|
||||
err.span_suggestion_verbose(
|
||||
*where_span,
|
||||
&format!("constrain the associated type to `{}`", bound_ident),
|
||||
format!(
|
||||
"{}: {}<{} = {}>",
|
||||
ident,
|
||||
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("::"),
|
||||
bound_ident,
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if the source is call expression and the first argument is `self`. If true,
|
||||
/// return the span of whole call and the span for all arguments expect the first one (`self`).
|
||||
fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {
|
||||
|
9
src/test/ui/traits/assoc_type_bound_with_struct.rs
Normal file
9
src/test/ui/traits/assoc_type_bound_with_struct.rs
Normal file
@ -0,0 +1,9 @@
|
||||
trait Bar {
|
||||
type Baz;
|
||||
}
|
||||
|
||||
struct Foo<T> where T: Bar, <T as Bar>::Baz: String { //~ ERROR expected trait, found struct
|
||||
t: T,
|
||||
}
|
||||
|
||||
fn main() {}
|
23
src/test/ui/traits/assoc_type_bound_with_struct.stderr
Normal file
23
src/test/ui/traits/assoc_type_bound_with_struct.stderr
Normal file
@ -0,0 +1,23 @@
|
||||
error[E0404]: expected trait, found struct `String`
|
||||
--> $DIR/assoc_type_bound_with_struct.rs:5:46
|
||||
|
|
||||
LL | struct Foo<T> where T: Bar, <T as Bar>::Baz: String {
|
||||
| ^^^^^^ not a trait
|
||||
|
|
||||
::: $SRC_DIR/alloc/src/string.rs:LL:COL
|
||||
|
|
||||
LL | pub trait ToString {
|
||||
| ------------------ similarly named trait `ToString` defined here
|
||||
|
|
||||
help: constrain the associated type to `String`
|
||||
|
|
||||
LL | struct Foo<T> where T: Bar, T: Bar<Baz = String> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
help: a trait with a similar name exists
|
||||
|
|
||||
LL | struct Foo<T> where T: Bar, <T as Bar>::Baz: ToString {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0404`.
|
Loading…
Reference in New Issue
Block a user