about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-01-27 22:37:17 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-01-27 22:38:28 +0300
commitf57ea7cb3daf186bcd181f9add9e56cc45ff8380 (patch)
tree2bfbc11bacea93adb416094b11a8e79743f9533d /src
parentd79f7cde061f18d354d914393640f9c1aacf45a8 (diff)
downloadrust-f57ea7cb3daf186bcd181f9add9e56cc45ff8380.tar.gz
rust-f57ea7cb3daf186bcd181f9add9e56cc45ff8380.zip
Make `+` in `impl/dyn Trait` non-associative
Diffstat (limited to 'src')
-rw-r--r--src/libsyntax/parse/parser.rs22
-rw-r--r--src/test/ui/impl-trait/impl-trait-plus-priority.rs (renamed from src/test/parse-fail/impl-trait-plus-priority.rs)26
-rw-r--r--src/test/ui/impl-trait/impl-trait-plus-priority.stderr68
3 files changed, 106 insertions, 10 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 0430be101b0..0b57d781412 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1528,6 +1528,7 @@ impl<'a> Parser<'a> {
         maybe_whole!(self, NtTy, |x| x);
 
         let lo = self.span;
+        let mut impl_dyn_multi = false;
         let node = if self.eat(&token::OpenDelim(token::Paren)) {
             // `(TYPE)` is a parenthesized type.
             // `(TYPE,)` is a tuple with a single field of type TYPE.
@@ -1614,12 +1615,17 @@ impl<'a> Parser<'a> {
                 self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
             }
         } else if self.eat_keyword(keywords::Impl) {
-            TyKind::ImplTrait(self.parse_ty_param_bounds_common(allow_plus)?)
+            // Always parse bounds greedily for better error recovery.
+            let bounds = self.parse_ty_param_bounds()?;
+            impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
+            TyKind::ImplTrait(bounds)
         } else if self.check_keyword(keywords::Dyn) &&
                   self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_ident(t)) {
             self.bump(); // `dyn`
-            TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?,
-                                TraitObjectSyntax::Dyn)
+            // Always parse bounds greedily for better error recovery.
+            let bounds = self.parse_ty_param_bounds()?;
+            impl_dyn_multi = bounds.len() > 1 || self.prev_token_kind == PrevTokenKind::Plus;
+            TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
         } else if self.check(&token::Question) ||
                   self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) {
             // Bound list (trait object type)
@@ -1655,6 +1661,7 @@ impl<'a> Parser<'a> {
         let ty = Ty { node, span, id: ast::DUMMY_NODE_ID };
 
         // Try to recover from use of `+` with incorrect priority.
+        self.maybe_report_ambiguous_plus(allow_plus, impl_dyn_multi, &ty);
         self.maybe_recover_from_bad_type_plus(allow_plus, &ty)?;
         let ty = self.maybe_recover_from_bad_qpath(ty, allow_qpath_recovery)?;
 
@@ -1672,6 +1679,15 @@ impl<'a> Parser<'a> {
         Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
     }
 
+    fn maybe_report_ambiguous_plus(&mut self, allow_plus: bool, impl_dyn_multi: bool, ty: &Ty) {
+        if !allow_plus && impl_dyn_multi {
+            let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
+            self.struct_span_err(ty.span, "ambiguous `+` in a type")
+                .span_suggestion(ty.span, "use parentheses to disambiguate", sum_with_parens)
+                .emit();
+        }
+    }
+
     fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
         // Do not add `+` to expected tokens.
         if !allow_plus || self.token != token::BinOp(token::Plus) {
diff --git a/src/test/parse-fail/impl-trait-plus-priority.rs b/src/test/ui/impl-trait/impl-trait-plus-priority.rs
index 6d12a68cf3d..f451123ca27 100644
--- a/src/test/parse-fail/impl-trait-plus-priority.rs
+++ b/src/test/ui/impl-trait/impl-trait-plus-priority.rs
@@ -10,11 +10,15 @@
 
 // compile-flags: -Z parse-only -Z continue-parse-after-error
 
+fn f() -> impl A + {} // OK
 fn f() -> impl A + B {} // OK
 fn f() -> dyn A + B {} // OK
 fn f() -> A + B {} // OK
 
 impl S {
+    fn f(self) -> impl A + { // OK
+        let _ = |a, b| -> impl A + {}; // OK
+    }
     fn f(self) -> impl A + B { // OK
         let _ = |a, b| -> impl A + B {}; // OK
     }
@@ -26,21 +30,29 @@ impl S {
     }
 }
 
+type A = fn() -> impl A +;
+//~^ ERROR ambiguous `+` in a type
 type A = fn() -> impl A + B;
-//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> impl A`
+//~^ ERROR ambiguous `+` in a type
 type A = fn() -> dyn A + B;
-//~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> dyn A`
+//~^ ERROR ambiguous `+` in a type
 type A = fn() -> A + B;
 //~^ ERROR expected a path on the left-hand side of `+`, not `fn() -> A`
 
-type A = Fn() -> impl A + B; // OK, interpreted as `(Fn() -> impl A) + B`
-type A = Fn() -> dyn A + B; // OK, interpreted as `(Fn() -> dyn A) + B`
-type A = Fn() -> A + B; // OK, interpreted as `(Fn() -> A) + B`
+type A = Fn() -> impl A +;
+//~^ ERROR ambiguous `+` in a type
+type A = Fn() -> impl A + B;
+//~^ ERROR ambiguous `+` in a type
+type A = Fn() -> dyn A + B;
+//~^ ERROR ambiguous `+` in a type
+type A = Fn() -> A + B; // OK, interpreted as `(Fn() -> A) + B` for compatibility
 
+type A = &impl A +;
+//~^ ERROR ambiguous `+` in a type
 type A = &impl A + B;
-//~^ ERROR expected a path on the left-hand side of `+`, not `&impl A`
+//~^ ERROR ambiguous `+` in a type
 type A = &dyn A + B;
-//~^ ERROR expected a path on the left-hand side of `+`, not `&dyn A`
+//~^ ERROR ambiguous `+` in a type
 type A = &A + B;
 //~^ ERROR expected a path on the left-hand side of `+`, not `&A`
 
diff --git a/src/test/ui/impl-trait/impl-trait-plus-priority.stderr b/src/test/ui/impl-trait/impl-trait-plus-priority.stderr
new file mode 100644
index 00000000000..885c3941971
--- /dev/null
+++ b/src/test/ui/impl-trait/impl-trait-plus-priority.stderr
@@ -0,0 +1,68 @@
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:33:18
+   |
+33 | type A = fn() -> impl A +;
+   |                  ^^^^^^^^ help: use parentheses to disambiguate: `(impl A)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:35:18
+   |
+35 | type A = fn() -> impl A + B;
+   |                  ^^^^^^^^^^ help: use parentheses to disambiguate: `(impl A + B)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:37:18
+   |
+37 | type A = fn() -> dyn A + B;
+   |                  ^^^^^^^^^ help: use parentheses to disambiguate: `(dyn A + B)`
+
+error[E0178]: expected a path on the left-hand side of `+`, not `fn() -> A`
+  --> $DIR/impl-trait-plus-priority.rs:39:10
+   |
+39 | type A = fn() -> A + B;
+   |          ^^^^^^^^^^^^^ perhaps you forgot parentheses?
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:42:18
+   |
+42 | type A = Fn() -> impl A +;
+   |                  ^^^^^^^^ help: use parentheses to disambiguate: `(impl A)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:44:18
+   |
+44 | type A = Fn() -> impl A + B;
+   |                  ^^^^^^^^^^ help: use parentheses to disambiguate: `(impl A + B)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:46:18
+   |
+46 | type A = Fn() -> dyn A + B;
+   |                  ^^^^^^^^^ help: use parentheses to disambiguate: `(dyn A + B)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:50:11
+   |
+50 | type A = &impl A +;
+   |           ^^^^^^^^ help: use parentheses to disambiguate: `(impl A)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:52:11
+   |
+52 | type A = &impl A + B;
+   |           ^^^^^^^^^^ help: use parentheses to disambiguate: `(impl A + B)`
+
+error: ambiguous `+` in a type
+  --> $DIR/impl-trait-plus-priority.rs:54:11
+   |
+54 | type A = &dyn A + B;
+   |           ^^^^^^^^^ help: use parentheses to disambiguate: `(dyn A + B)`
+
+error[E0178]: expected a path on the left-hand side of `+`, not `&A`
+  --> $DIR/impl-trait-plus-priority.rs:56:10
+   |
+56 | type A = &A + B;
+   |          ^^^^^^ help: try adding parentheses: `&(A + B)`
+
+error: aborting due to 11 previous errors
+