From: Esteban Küber Date: Wed, 8 Apr 2020 18:10:16 +0000 (-0700) Subject: On incorrect equality constraint likely to be assoc type, suggest appropriate syntax X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=d8d02f8f1806b564603982d8cf25795db744e0ff;p=rust.git On incorrect equality constraint likely to be assoc type, suggest appropriate syntax When encountering `where ::Bar = B`, it is possible that `Bar` is an associated type. If so, suggest `where A: Foo`. CC #20041. --- diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs index 395fd746085..5f47c9eb2c2 100644 --- a/src/librustc_ast_passes/ast_validation.rs +++ b/src/librustc_ast_passes/ast_validation.rs @@ -23,6 +23,7 @@ use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use std::mem; +use std::ops::DerefMut; const MORE_EXTERN: &str = "for more information, visit https://doc.rust-lang.org/std/keyword.extern.html"; @@ -1113,17 +1114,7 @@ fn visit_generics(&mut self, generics: &'a Generics) { for predicate in &generics.where_clause.predicates { if let WherePredicate::EqPredicate(ref predicate) = *predicate { - self.err_handler() - .struct_span_err( - predicate.span, - "equality constraints are not yet supported in `where` clauses", - ) - .span_label(predicate.span, "not supported") - .note( - "see issue #20041 \ - for more information", - ) - .emit(); + deny_equality_constraints(self, predicate, generics); } } @@ -1300,6 +1291,87 @@ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { } } +fn deny_equality_constraints( + this: &mut AstValidator<'_>, + predicate: &WhereEqPredicate, + generics: &Generics, +) { + let mut err = this.err_handler().struct_span_err( + predicate.span, + "equality constraints are not yet supported in `where` clauses", + ); + err.span_label(predicate.span, "not supported"); + + // Given `::Bar = RhsTy`, suggest `A: Foo`. + if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind { + if let TyKind::Path(None, path) = &qself.ty.kind { + match &path.segments[..] { + [PathSegment { ident, args: None, .. }] => { + for param in &generics.params { + if param.ident == *ident { + let param = ident; + match &full_path.segments[qself.position..] { + [PathSegment { ident, .. }] => { + // Make a new `Path` from `foo::Bar` to `Foo`. + let mut assoc_path = full_path.clone(); + // Remove `Bar` from `Foo::Bar`. + assoc_path.segments.pop(); + let len = assoc_path.segments.len() - 1; + // Build ``. + let arg = AngleBracketedArg::Constraint(AssocTyConstraint { + id: rustc_ast::node_id::DUMMY_NODE_ID, + ident: *ident, + kind: AssocTyConstraintKind::Equality { + ty: predicate.rhs_ty.clone(), + }, + span: ident.span, + }); + // Add `` to `Foo`. + match &mut assoc_path.segments[len].args { + Some(args) => match args.deref_mut() { + GenericArgs::Parenthesized(_) => continue, + GenericArgs::AngleBracketed(args) => { + args.args.push(arg); + } + }, + empty_args => { + *empty_args = AngleBracketedArgs { + span: ident.span, + args: vec![arg], + } + .into(); + } + } + err.span_suggestion_verbose( + predicate.span, + &format!( + "if `{}` is an associated type you're trying to set, \ + use the associated type binding syntax", + ident + ), + format!( + "{}: {}", + param, + pprust::path_to_string(&assoc_path) + ), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }; + } + } + } + _ => {} + } + } + } + err.note( + "see issue #20041 for more information", + ); + err.emit(); +} + pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> bool { let mut validator = AstValidator { session, diff --git a/src/test/ui/generic-associated-types/missing-bounds.fixed b/src/test/ui/generic-associated-types/missing-bounds.fixed index c2e619d1a7e..364d2388741 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.fixed +++ b/src/test/ui/generic-associated-types/missing-bounds.fixed @@ -32,4 +32,15 @@ impl> Add for D { } } +struct E(B); + +impl Add for E where B: Add, B: std::ops::Add { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.rs b/src/test/ui/generic-associated-types/missing-bounds.rs index 1852ef62fe6..ffafff5e9f5 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.rs +++ b/src/test/ui/generic-associated-types/missing-bounds.rs @@ -32,4 +32,15 @@ fn add(self, rhs: Self) -> Self { } } +struct E(B); + +impl Add for E where ::Output = B { + //~^ ERROR equality constraints are not yet supported in `where` clauses + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) //~ ERROR mismatched types + } +} + fn main() {} diff --git a/src/test/ui/generic-associated-types/missing-bounds.stderr b/src/test/ui/generic-associated-types/missing-bounds.stderr index 630ceac093e..50536fdaca9 100644 --- a/src/test/ui/generic-associated-types/missing-bounds.stderr +++ b/src/test/ui/generic-associated-types/missing-bounds.stderr @@ -1,3 +1,15 @@ +error: equality constraints are not yet supported in `where` clauses + --> $DIR/missing-bounds.rs:37:33 + | +LL | impl Add for E where ::Output = B { + | ^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `Output` is an associated type you're trying to set, use the associated type binding syntax + | +LL | impl Add for E where B: Add { + | ^^^^^^^^^^^^^^^^^^ + error[E0308]: mismatched types --> $DIR/missing-bounds.rs:11:11 | @@ -43,7 +55,23 @@ help: consider restricting type parameter `B` LL | impl> Add for D { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 3 previous errors +error[E0308]: mismatched types + --> $DIR/missing-bounds.rs:42:14 + | +LL | impl Add for E where ::Output = B { + | - this type parameter +... +LL | Self(self.0 + rhs.0) + | ^^^^^^^^^^^^^^ expected type parameter `B`, found associated type + | + = note: expected type parameter `B` + found associated type `::Output` +help: consider further restricting type parameter `B` + | +LL | impl Add for E where ::Output = B, B: std::ops::Add { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors Some errors have detailed explanations: E0308, E0369. For more information about an error, try `rustc --explain E0308`.