From 234adf84bd834146c76b98dd4328e05a4bf7900b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Thu, 16 May 2019 19:29:02 -0700
Subject: [PATCH 1/4] Handle more string addition cases with appropriate
 suggestions

---
 src/librustc_typeck/check/op.rs               |  57 +++++--
 src/test/ui/span/issue-39018.rs               |  20 +++
 src/test/ui/span/issue-39018.stderr           | 143 +++++++++++++++++-
 .../ui/str/str-concat-on-double-ref.stderr    |   7 +-
 4 files changed, 209 insertions(+), 18 deletions(-)

diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index d1ca05780930a..1913850c94c7e 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -502,6 +502,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         false
     }
 
+    /// Provide actionable suggestions when trying to add two strings with incorrect types,
+    /// like `&str + &str`, `String + String` and `&str + &String`.
+    ///
+    /// If this function returns `true` it means a note was printed, so we don't need
+    /// to print the normal "implementation of `std::ops::Add` might be missing" note
     fn check_str_addition(
         &self,
         expr: &'gcx hir::Expr,
@@ -514,33 +519,57 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         op: hir::BinOp,
     ) -> bool {
         let source_map = self.tcx.sess.source_map();
+        let remove_borrow_msg = "String concatenation appends the string on the right to the \
+                                 string on the left and may require reallocation. This \
+                                 requires ownership of the string on the left";
+
         let msg = "`to_owned()` can be used to create an owned `String` \
                    from a string reference. String concatenation \
                    appends the string on the right to the string \
                    on the left and may require reallocation. This \
                    requires ownership of the string on the left";
-        // If this function returns true it means a note was printed, so we don't need
-        // to print the normal "implementation of `std::ops::Add` might be missing" note
+        debug!("check_str_addition: {:?} + {:?}", lhs_ty, rhs_ty);
         match (&lhs_ty.sty, &rhs_ty.sty) {
-            (&Ref(_, l_ty, _), &Ref(_, r_ty, _))
-            if l_ty.sty == Str && r_ty.sty == Str => {
+            (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
+            if (l_ty.sty == Str || &format!("{:?}", l_ty) == "std::string::String") && (
+                    r_ty.sty == Str ||
+                    &format!("{:?}", r_ty) == "std::string::String" ||
+                    &format!("{:?}", rhs_ty) == "&&str"
+                ) =>
+            {
                 if !is_assign {
-                    err.span_label(op.span,
-                                   "`+` can't be used to concatenate two `&str` strings");
+                    err.span_label(
+                        op.span,
+                        "`+` can't be used to concatenate two `&str` strings",
+                    );
                     match source_map.span_to_snippet(lhs_expr.span) {
-                        Ok(lstring) => err.span_suggestion(
-                            lhs_expr.span,
-                            msg,
-                            format!("{}.to_owned()", lstring),
-                            Applicability::MachineApplicable,
-                        ),
+                        Ok(lstring) => {
+                            err.span_suggestion(
+                                lhs_expr.span,
+                                if lstring.starts_with("&") {
+                                    remove_borrow_msg
+                                } else {
+                                    msg
+                                },
+                                if lstring.starts_with("&") {
+                                    // let a = String::new();
+                                    // let _ = &a + "bar";
+                                    format!("{}", &lstring[1..])
+                                } else {
+                                    format!("{}.to_owned()", lstring)
+                                },
+                                Applicability::MachineApplicable,
+                            )
+                        }
                         _ => err.help(msg),
                     };
                 }
                 true
             }
-            (&Ref(_, l_ty, _), &Adt(..))
-            if l_ty.sty == Str && &format!("{:?}", rhs_ty) == "std::string::String" => {
+            (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
+            if (l_ty.sty == Str || &format!("{:?}", l_ty) == "std::string::String") &&
+                &format!("{:?}", rhs_ty) == "std::string::String" =>
+            {
                 err.span_label(expr.span,
                     "`+` can't be used to concatenate a `&str` with a `String`");
                 match (
diff --git a/src/test/ui/span/issue-39018.rs b/src/test/ui/span/issue-39018.rs
index 6dbc8d39976ad..a3b1d1d81799f 100644
--- a/src/test/ui/span/issue-39018.rs
+++ b/src/test/ui/span/issue-39018.rs
@@ -16,3 +16,23 @@ enum World {
     Hello,
     Goodbye,
 }
+
+fn foo() {
+    let a = String::new();
+    let b = String::new();
+    let c = "";
+    let d = "";
+    let e = &a;
+    let _ = &a + &b; //~ ERROR binary operation
+    let _ = &a + b; //~ ERROR binary operation
+    let _ = a + &b; // ok
+    let _ = a + b; //~ ERROR mismatched types
+    let _ = e + b; //~ ERROR binary operation
+    let _ = e + &b; //~ ERROR binary operation
+    let _ = e + d; //~ ERROR binary operation
+    let _ = e + &d; //~ ERROR binary operation
+    let _ = &c + &d; //~ ERROR binary operation
+    let _ = &c + d; //~ ERROR binary operation
+    let _ = c + &d; //~ ERROR binary operation
+    let _ = c + d; //~ ERROR binary operation
+}
diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr
index a5b91f090d2c0..2f48abcaf3339 100644
--- a/src/test/ui/span/issue-39018.stderr
+++ b/src/test/ui/span/issue-39018.stderr
@@ -35,6 +35,145 @@ help: `to_owned()` can be used to create an owned `String` from a string referen
 LL |     let x = "Hello ".to_owned() + &"World!".to_owned();
    |             ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error[E0369]: binary operation `+` cannot be applied to type `&std::string::String`
+  --> $DIR/issue-39018.rs:26:16
+   |
+LL |     let _ = &a + &b;
+   |             -- ^ -- &std::string::String
+   |             |  |
+   |             |  `+` can't be used to concatenate two `&str` strings
+   |             &std::string::String
+help: String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = a + &b;
+   |             ^
+
+error[E0369]: binary operation `+` cannot be applied to type `&std::string::String`
+  --> $DIR/issue-39018.rs:27:16
+   |
+LL |     let _ = &a + b;
+   |             ---^--
+   |             |    |
+   |             |    std::string::String
+   |             &std::string::String
+   |             `+` can't be used to concatenate a `&str` with a `String`
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = &a.to_owned() + &b;
+   |             ^^^^^^^^^^^^^   ^^
+
+error[E0308]: mismatched types
+  --> $DIR/issue-39018.rs:29:17
+   |
+LL |     let _ = a + b;
+   |                 ^
+   |                 |
+   |                 expected &str, found struct `std::string::String`
+   |                 help: consider borrowing here: `&b`
+   |
+   = note: expected type `&str`
+              found type `std::string::String`
+
+error[E0369]: binary operation `+` cannot be applied to type `&std::string::String`
+  --> $DIR/issue-39018.rs:30:15
+   |
+LL |     let _ = e + b;
+   |             --^--
+   |             |   |
+   |             |   std::string::String
+   |             &std::string::String
+   |             `+` can't be used to concatenate a `&str` with a `String`
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = e.to_owned() + &b;
+   |             ^^^^^^^^^^^^   ^^
+
+error[E0369]: binary operation `+` cannot be applied to type `&std::string::String`
+  --> $DIR/issue-39018.rs:31:15
+   |
+LL |     let _ = e + &b;
+   |             - ^ -- &std::string::String
+   |             | |
+   |             | `+` can't be used to concatenate two `&str` strings
+   |             &std::string::String
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = e.to_owned() + &b;
+   |             ^^^^^^^^^^^^
+
+error[E0369]: binary operation `+` cannot be applied to type `&std::string::String`
+  --> $DIR/issue-39018.rs:32:15
+   |
+LL |     let _ = e + d;
+   |             - ^ - &str
+   |             | |
+   |             | `+` can't be used to concatenate two `&str` strings
+   |             &std::string::String
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = e.to_owned() + d;
+   |             ^^^^^^^^^^^^
+
+error[E0369]: binary operation `+` cannot be applied to type `&std::string::String`
+  --> $DIR/issue-39018.rs:33:15
+   |
+LL |     let _ = e + &d;
+   |             - ^ -- &&str
+   |             | |
+   |             | `+` can't be used to concatenate two `&str` strings
+   |             &std::string::String
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = e.to_owned() + &d;
+   |             ^^^^^^^^^^^^
+
+error[E0369]: binary operation `+` cannot be applied to type `&&str`
+  --> $DIR/issue-39018.rs:34:16
+   |
+LL |     let _ = &c + &d;
+   |             -- ^ -- &&str
+   |             |
+   |             &&str
+   |
+   = note: an implementation of `std::ops::Add` might be missing for `&&str`
+
+error[E0369]: binary operation `+` cannot be applied to type `&&str`
+  --> $DIR/issue-39018.rs:35:16
+   |
+LL |     let _ = &c + d;
+   |             -- ^ - &str
+   |             |
+   |             &&str
+   |
+   = note: an implementation of `std::ops::Add` might be missing for `&&str`
+
+error[E0369]: binary operation `+` cannot be applied to type `&str`
+  --> $DIR/issue-39018.rs:36:15
+   |
+LL |     let _ = c + &d;
+   |             - ^ -- &&str
+   |             | |
+   |             | `+` can't be used to concatenate two `&str` strings
+   |             &str
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = c.to_owned() + &d;
+   |             ^^^^^^^^^^^^
+
+error[E0369]: binary operation `+` cannot be applied to type `&str`
+  --> $DIR/issue-39018.rs:37:15
+   |
+LL |     let _ = c + d;
+   |             - ^ - &str
+   |             | |
+   |             | `+` can't be used to concatenate two `&str` strings
+   |             &str
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
+   |
+LL |     let _ = c.to_owned() + d;
+   |             ^^^^^^^^^^^^
+
+error: aborting due to 14 previous errors
 
-For more information about this error, try `rustc --explain E0369`.
+Some errors have detailed explanations: E0308, E0369.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/src/test/ui/str/str-concat-on-double-ref.stderr b/src/test/ui/str/str-concat-on-double-ref.stderr
index 61ebcfdefc316..17d9475b9c07d 100644
--- a/src/test/ui/str/str-concat-on-double-ref.stderr
+++ b/src/test/ui/str/str-concat-on-double-ref.stderr
@@ -3,10 +3,13 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
    |
 LL |     let c = a + b;
    |             - ^ - &str
-   |             |
+   |             | |
+   |             | `+` can't be used to concatenate two `&str` strings
    |             &std::string::String
+help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
-   = note: an implementation of `std::ops::Add` might be missing for `&std::string::String`
+LL |     let c = a.to_owned() + b;
+   |             ^^^^^^^^^^^^
 
 error: aborting due to previous error
 

From 2cb91816f24a096a0760a49b6b4095309098e7f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Thu, 16 May 2019 19:56:11 -0700
Subject: [PATCH 2/4] Fix binop span

---
 src/librustc_typeck/check/op.rs     | 17 +++++++++--------
 src/libsyntax/parse/parser.rs       |  3 +--
 src/test/ui/span/issue-39018.stderr | 21 +++++++++------------
 3 files changed, 19 insertions(+), 22 deletions(-)

diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 1913850c94c7e..619900b6ad903 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -305,8 +305,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             };
                             if let Some(missing_trait) = missing_trait {
                                 if op.node == hir::BinOpKind::Add &&
-                                    self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty,
-                                                            rhs_ty, &mut err, true, op) {
+                                    self.check_str_addition(
+                                        lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, true, op) {
                                     // This has nothing here because it means we did string
                                     // concatenation (e.g., "Hello " += "World!"). This means
                                     // we don't want the note in the else clause to be emitted
@@ -400,8 +400,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             };
                             if let Some(missing_trait) = missing_trait {
                                 if op.node == hir::BinOpKind::Add &&
-                                    self.check_str_addition(expr, lhs_expr, rhs_expr, lhs_ty,
-                                                            rhs_ty, &mut err, false, op) {
+                                    self.check_str_addition(
+                                        lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, false, op) {
                                     // This has nothing here because it means we did string
                                     // concatenation (e.g., "Hello " + "World!"). This means
                                     // we don't want the note in the else clause to be emitted
@@ -509,7 +509,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
     /// to print the normal "implementation of `std::ops::Add` might be missing" note
     fn check_str_addition(
         &self,
-        expr: &'gcx hir::Expr,
         lhs_expr: &'gcx hir::Expr,
         rhs_expr: &'gcx hir::Expr,
         lhs_ty: Ty<'tcx>,
@@ -537,7 +536,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     &format!("{:?}", rhs_ty) == "&&str"
                 ) =>
             {
-                if !is_assign {
+                if !is_assign { // Do not supply this message if `&str += &str`
                     err.span_label(
                         op.span,
                         "`+` can't be used to concatenate two `&str` strings",
@@ -570,8 +569,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             if (l_ty.sty == Str || &format!("{:?}", l_ty) == "std::string::String") &&
                 &format!("{:?}", rhs_ty) == "std::string::String" =>
             {
-                err.span_label(expr.span,
-                    "`+` can't be used to concatenate a `&str` with a `String`");
+                err.span_label(
+                    op.span,
+                    "`+` can't be used to concatenate a `&str` with a `String`",
+                );
                 match (
                     source_map.span_to_snippet(lhs_expr.span),
                     source_map.span_to_snippet(rhs_expr.span),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 99e8db9d8e6d2..a80fe4b2fd8e5 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3540,8 +3540,7 @@ impl<'a> Parser<'a> {
                     let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
                     self.mk_expr(span, binary, ThinVec::new())
                 }
-                AssocOp::Assign =>
-                    self.mk_expr(span, ExprKind::Assign(lhs, rhs), ThinVec::new()),
+                AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), ThinVec::new()),
                 AssocOp::ObsoleteInPlace =>
                     self.mk_expr(span, ExprKind::ObsoleteInPlace(lhs, rhs), ThinVec::new()),
                 AssocOp::AssignOp(k) => {
diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr
index 2f48abcaf3339..b174e95c8a1d4 100644
--- a/src/test/ui/span/issue-39018.stderr
+++ b/src/test/ui/span/issue-39018.stderr
@@ -25,11 +25,10 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
   --> $DIR/issue-39018.rs:11:22
    |
 LL |     let x = "Hello " + "World!".to_owned();
-   |             ---------^--------------------
-   |             |          |
-   |             |          std::string::String
+   |             -------- ^ ------------------- std::string::String
+   |             |        |
+   |             |        `+` can't be used to concatenate a `&str` with a `String`
    |             &str
-   |             `+` can't be used to concatenate a `&str` with a `String`
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
 LL |     let x = "Hello ".to_owned() + &"World!".to_owned();
@@ -52,11 +51,10 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
   --> $DIR/issue-39018.rs:27:16
    |
 LL |     let _ = &a + b;
-   |             ---^--
-   |             |    |
-   |             |    std::string::String
+   |             -- ^ - std::string::String
+   |             |  |
+   |             |  `+` can't be used to concatenate a `&str` with a `String`
    |             &std::string::String
-   |             `+` can't be used to concatenate a `&str` with a `String`
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
 LL |     let _ = &a.to_owned() + &b;
@@ -78,11 +76,10 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
   --> $DIR/issue-39018.rs:30:15
    |
 LL |     let _ = e + b;
-   |             --^--
-   |             |   |
-   |             |   std::string::String
+   |             - ^ - std::string::String
+   |             | |
+   |             | `+` can't be used to concatenate a `&str` with a `String`
    |             &std::string::String
-   |             `+` can't be used to concatenate a `&str` with a `String`
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
 LL |     let _ = e.to_owned() + &b;

From ee0bf5e6aa7a964bb5c776adba0faad087a6a425 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Thu, 16 May 2019 20:05:00 -0700
Subject: [PATCH 3/4] review comments

---
 src/librustc_typeck/check/op.rs               | 20 +++++++++----------
 src/test/ui/issues/issue-47377.stderr         |  2 +-
 src/test/ui/issues/issue-47380.stderr         |  2 +-
 src/test/ui/span/issue-39018.stderr           | 20 +++++++++----------
 .../ui/str/str-concat-on-double-ref.stderr    |  2 +-
 5 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 619900b6ad903..26a5bdfef7d66 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -527,19 +527,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                    appends the string on the right to the string \
                    on the left and may require reallocation. This \
                    requires ownership of the string on the left";
-        debug!("check_str_addition: {:?} + {:?}", lhs_ty, rhs_ty);
+
+        let is_std_string = |ty| &format!("{:?}", ty) == "std::string::String";
+
         match (&lhs_ty.sty, &rhs_ty.sty) {
             (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str
-            if (l_ty.sty == Str || &format!("{:?}", l_ty) == "std::string::String") && (
-                    r_ty.sty == Str ||
-                    &format!("{:?}", r_ty) == "std::string::String" ||
-                    &format!("{:?}", rhs_ty) == "&&str"
-                ) =>
+                if (l_ty.sty == Str || is_std_string(l_ty)) && (
+                        r_ty.sty == Str || is_std_string(r_ty) ||
+                        &format!("{:?}", rhs_ty) == "&&str"
+                    ) =>
             {
                 if !is_assign { // Do not supply this message if `&str += &str`
                     err.span_label(
                         op.span,
-                        "`+` can't be used to concatenate two `&str` strings",
+                        "`+` cannot be used to concatenate two `&str` strings",
                     );
                     match source_map.span_to_snippet(lhs_expr.span) {
                         Ok(lstring) => {
@@ -566,12 +567,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 true
             }
             (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
-            if (l_ty.sty == Str || &format!("{:?}", l_ty) == "std::string::String") &&
-                &format!("{:?}", rhs_ty) == "std::string::String" =>
+                if (l_ty.sty == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
             {
                 err.span_label(
                     op.span,
-                    "`+` can't be used to concatenate a `&str` with a `String`",
+                    "`+` cannot be used to concatenate a `&str` with a `String`",
                 );
                 match (
                     source_map.span_to_snippet(lhs_expr.span),
diff --git a/src/test/ui/issues/issue-47377.stderr b/src/test/ui/issues/issue-47377.stderr
index 88466131e3144..7d11a8c802128 100644
--- a/src/test/ui/issues/issue-47377.stderr
+++ b/src/test/ui/issues/issue-47377.stderr
@@ -4,7 +4,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
 LL |      let _a = b + ", World!";
    |               - ^ ---------- &str
    |               | |
-   |               | `+` can't be used to concatenate two `&str` strings
+   |               | `+` cannot be used to concatenate two `&str` strings
    |               &str
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
diff --git a/src/test/ui/issues/issue-47380.stderr b/src/test/ui/issues/issue-47380.stderr
index d69101eab4c46..89a154c5109d8 100644
--- a/src/test/ui/issues/issue-47380.stderr
+++ b/src/test/ui/issues/issue-47380.stderr
@@ -4,7 +4,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
 LL |     println!("🦀🦀🦀🦀🦀"); let _a = b + ", World!";
    |                                      - ^ ---------- &str
    |                                      | |
-   |                                      | `+` can't be used to concatenate two `&str` strings
+   |                                      | `+` cannot be used to concatenate two `&str` strings
    |                                      &str
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr
index b174e95c8a1d4..cdb9c1168d8e7 100644
--- a/src/test/ui/span/issue-39018.stderr
+++ b/src/test/ui/span/issue-39018.stderr
@@ -4,7 +4,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
 LL |     let x = "Hello " + "World!";
    |             -------- ^ -------- &str
    |             |        |
-   |             |        `+` can't be used to concatenate two `&str` strings
+   |             |        `+` cannot be used to concatenate two `&str` strings
    |             &str
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -27,7 +27,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
 LL |     let x = "Hello " + "World!".to_owned();
    |             -------- ^ ------------------- std::string::String
    |             |        |
-   |             |        `+` can't be used to concatenate a `&str` with a `String`
+   |             |        `+` cannot be used to concatenate a `&str` with a `String`
    |             &str
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -40,7 +40,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let _ = &a + &b;
    |             -- ^ -- &std::string::String
    |             |  |
-   |             |  `+` can't be used to concatenate two `&str` strings
+   |             |  `+` cannot be used to concatenate two `&str` strings
    |             &std::string::String
 help: String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -53,7 +53,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let _ = &a + b;
    |             -- ^ - std::string::String
    |             |  |
-   |             |  `+` can't be used to concatenate a `&str` with a `String`
+   |             |  `+` cannot be used to concatenate a `&str` with a `String`
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -78,7 +78,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let _ = e + b;
    |             - ^ - std::string::String
    |             | |
-   |             | `+` can't be used to concatenate a `&str` with a `String`
+   |             | `+` cannot be used to concatenate a `&str` with a `String`
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -91,7 +91,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let _ = e + &b;
    |             - ^ -- &std::string::String
    |             | |
-   |             | `+` can't be used to concatenate two `&str` strings
+   |             | `+` cannot be used to concatenate two `&str` strings
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -104,7 +104,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let _ = e + d;
    |             - ^ - &str
    |             | |
-   |             | `+` can't be used to concatenate two `&str` strings
+   |             | `+` cannot be used to concatenate two `&str` strings
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -117,7 +117,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let _ = e + &d;
    |             - ^ -- &&str
    |             | |
-   |             | `+` can't be used to concatenate two `&str` strings
+   |             | `+` cannot be used to concatenate two `&str` strings
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -150,7 +150,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
 LL |     let _ = c + &d;
    |             - ^ -- &&str
    |             | |
-   |             | `+` can't be used to concatenate two `&str` strings
+   |             | `+` cannot be used to concatenate two `&str` strings
    |             &str
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
@@ -163,7 +163,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&str`
 LL |     let _ = c + d;
    |             - ^ - &str
    |             | |
-   |             | `+` can't be used to concatenate two `&str` strings
+   |             | `+` cannot be used to concatenate two `&str` strings
    |             &str
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
diff --git a/src/test/ui/str/str-concat-on-double-ref.stderr b/src/test/ui/str/str-concat-on-double-ref.stderr
index 17d9475b9c07d..3e53cdc4d98ca 100644
--- a/src/test/ui/str/str-concat-on-double-ref.stderr
+++ b/src/test/ui/str/str-concat-on-double-ref.stderr
@@ -4,7 +4,7 @@ error[E0369]: binary operation `+` cannot be applied to type `&std::string::Stri
 LL |     let c = a + b;
    |             - ^ - &str
    |             | |
-   |             | `+` can't be used to concatenate two `&str` strings
+   |             | `+` cannot be used to concatenate two `&str` strings
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |

From 8895fb945d0ea0ce124923aadc16adb68c74f3e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Fri, 17 May 2019 10:45:54 -0700
Subject: [PATCH 4/4] Account for &String + String

---
 src/librustc_typeck/check/op.rs     | 9 ++++++++-
 src/test/ui/span/issue-39018.stderr | 4 ++--
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 26a5bdfef7d66..cd207478f8f6f 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -579,10 +579,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     is_assign,
                 ) {
                     (Ok(l), Ok(r), false) => {
+                        let to_string = if l.starts_with("&") {
+                            // let a = String::new(); let b = String::new();
+                            // let _ = &a + b;
+                            format!("{}", &l[1..])
+                        } else {
+                            format!("{}.to_owned()", l)
+                        };
                         err.multipart_suggestion(
                             msg,
                             vec![
-                                (lhs_expr.span, format!("{}.to_owned()", l)),
+                                (lhs_expr.span, to_string),
                                 (rhs_expr.span, format!("&{}", r)),
                             ],
                             Applicability::MachineApplicable,
diff --git a/src/test/ui/span/issue-39018.stderr b/src/test/ui/span/issue-39018.stderr
index cdb9c1168d8e7..d8fbf841b6157 100644
--- a/src/test/ui/span/issue-39018.stderr
+++ b/src/test/ui/span/issue-39018.stderr
@@ -57,8 +57,8 @@ LL |     let _ = &a + b;
    |             &std::string::String
 help: `to_owned()` can be used to create an owned `String` from a string reference. String concatenation appends the string on the right to the string on the left and may require reallocation. This requires ownership of the string on the left
    |
-LL |     let _ = &a.to_owned() + &b;
-   |             ^^^^^^^^^^^^^   ^^
+LL |     let _ = a + &b;
+   |             ^   ^^
 
 error[E0308]: mismatched types
   --> $DIR/issue-39018.rs:29:17