about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-05-17 20:02:13 +0000
committerbors <bors@rust-lang.org>2019-05-17 20:02:13 +0000
commit73a3a90d25ddb3be3d0ad2238dbb55ae79e459dc (patch)
tree010c00a2f10b722cee1ffd7349450594941fc825
parent823a75d9ba34860b9a6712c7a9d35e86e0a88436 (diff)
parentf48f37b052bd2683ff89037001d794f5ffac9e5b (diff)
downloadrust-73a3a90d25ddb3be3d0ad2238dbb55ae79e459dc.tar.gz
rust-73a3a90d25ddb3be3d0ad2238dbb55ae79e459dc.zip
Auto merge of #60920 - Manishearth:rollup-p4xp4gk, r=Manishearth
Rollup of 4 pull requests

Successful merges:

 - #60791 (Update books)
 - #60891 (Allow claiming issues with triagebot)
 - #60901 (Handle more string addition cases with appropriate suggestions)
 - #60902 (Prevent Error::type_id overrides)

Failed merges:

r? @ghost
-rwxr-xr-xsrc/ci/docker/x86_64-gnu-tools/checktools.sh4
m---------src/doc/book0
m---------src/doc/edition-guide0
m---------src/doc/embedded-book0
m---------src/doc/nomicon0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-guide0
-rw-r--r--src/librustc_typeck/check/op.rs83
-rw-r--r--src/libstd/error.rs12
-rw-r--r--src/libsyntax/parse/parser.rs3
-rw-r--r--src/test/ui/issues/issue-47377.stderr2
-rw-r--r--src/test/ui/issues/issue-47380.stderr2
-rw-r--r--src/test/ui/span/issue-39018.rs20
-rw-r--r--src/test/ui/span/issue-39018.stderr150
-rw-r--r--src/test/ui/str/str-concat-on-double-ref.stderr7
-rw-r--r--triagebot.toml2
17 files changed, 246 insertions, 39 deletions
diff --git a/src/ci/docker/x86_64-gnu-tools/checktools.sh b/src/ci/docker/x86_64-gnu-tools/checktools.sh
index af0198705a2..a0fe307cffc 100755
--- a/src/ci/docker/x86_64-gnu-tools/checktools.sh
+++ b/src/ci/docker/x86_64-gnu-tools/checktools.sh
@@ -74,7 +74,9 @@ status_check() {
     check_dispatch $1 beta nomicon src/doc/nomicon
     check_dispatch $1 beta reference src/doc/reference
     check_dispatch $1 beta rust-by-example src/doc/rust-by-example
-    check_dispatch $1 beta edition-guide src/doc/edition-guide
+    # Temporarily disabled until
+    # https://github.com/rust-lang/rust/issues/60459 is fixed.
+    # check_dispatch $1 beta edition-guide src/doc/edition-guide
     check_dispatch $1 beta rls src/tools/rls
     check_dispatch $1 beta rustfmt src/tools/rustfmt
     check_dispatch $1 beta clippy-driver src/tools/clippy
diff --git a/src/doc/book b/src/doc/book
-Subproject db919bc6bb9071566e9c4f05053672133eaac33
+Subproject 29fe982990e43b9367be0ff47abc82fb2123fd0
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
-Subproject c413d42a207bd082f801ec0137c31b71e4bfed4
+Subproject 581c6cccfaf995394ea9dcac362dc8e731c1855
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject de3d55f521e657863df45260ebbca1b10527f66
+Subproject 9858872bd1b7dbba5ec27dc30d34eba00acd7ef
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject fb29b147be4d9a1f8e24aba753a7e1de537abf6
+Subproject c656171b749b7307f21371dd0d3278efee5573b
diff --git a/src/doc/reference b/src/doc/reference
-Subproject 2a2de9ce095979978ad7b582daecf94e4070b91
+Subproject 862b669c395822bb0938781d74f860e5762ad4f
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 1ff0f8e018838a710ebc0cc1a7bf74ebe73ad9f
+Subproject 811c697b232c611ed754d279ed20643a0c4096f
diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide
-Subproject 99e1b1d53656be08654df399fc200584aebb50e
+Subproject 3cb727b62b953d59b4360d39aa68b6dc8f15765
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index d1ca0578093..cd207478f8f 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
@@ -502,9 +502,13 @@ 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,
         lhs_expr: &'gcx hir::Expr,
         rhs_expr: &'gcx hir::Expr,
         lhs_ty: Ty<'tcx>,
@@ -514,45 +518,78 @@ 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
+
+        let is_std_string = |ty| &format!("{:?}", ty) == "std::string::String";
+
         match (&lhs_ty.sty, &rhs_ty.sty) {
-            (&Ref(_, l_ty, _), &Ref(_, r_ty, _))
-            if l_ty.sty == Str && r_ty.sty == Str => {
-                if !is_assign {
-                    err.span_label(op.span,
-                                   "`+` can't be used to concatenate two `&str` strings");
+            (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&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,
+                        "`+` cannot 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" => {
-                err.span_label(expr.span,
-                    "`+` can't be used to concatenate a `&str` with a `String`");
+            (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String`
+                if (l_ty.sty == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
+            {
+                err.span_label(
+                    op.span,
+                    "`+` cannot 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),
                     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/libstd/error.rs b/src/libstd/error.rs
index 7cb830e751a..62282006a40 100644
--- a/src/libstd/error.rs
+++ b/src/libstd/error.rs
@@ -201,11 +201,19 @@ pub trait Error: Debug + Display {
     #[unstable(feature = "error_type_id",
                reason = "this is memory unsafe to override in user code",
                issue = "60784")]
-    fn type_id(&self) -> TypeId where Self: 'static {
+    fn type_id(&self, _: private::Internal) -> TypeId where Self: 'static {
         TypeId::of::<Self>()
     }
 }
 
+mod private {
+    // This is a hack to prevent `type_id` from being overridden by `Error`
+    // implementations, since that can enable unsound downcasting.
+    #[unstable(feature = "error_type_id", issue = "60784")]
+    #[derive(Debug)]
+    pub struct Internal;
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
     /// Converts a type of [`Error`] into a box of dyn [`Error`].
@@ -575,7 +583,7 @@ impl dyn Error + 'static {
         let t = TypeId::of::<T>();
 
         // Get TypeId of the type in the trait object
-        let boxed = self.type_id();
+        let boxed = self.type_id(private::Internal);
 
         // Compare both TypeIds on equality
         t == boxed
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index b1c3e46adc0..3d55267b8e8 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -3497,8 +3497,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/issues/issue-47377.stderr b/src/test/ui/issues/issue-47377.stderr
index 88466131e31..7d11a8c8021 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 d69101eab4c..89a154c5109 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.rs b/src/test/ui/span/issue-39018.rs
index 6dbc8d39976..a3b1d1d8179 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 a5b91f090d2..d8fbf841b61 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
    |
@@ -25,16 +25,152 @@ 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
+   |             |        |
+   |             |        `+` cannot 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();
    |             ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^
 
-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
+   |             |  |
+   |             |  `+` 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
+   |
+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
+   |             |  |
+   |             |  `+` 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
+   |
+LL |     let _ = a + &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
+   |             | |
+   |             | `+` 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
+   |
+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
+   |             | |
+   |             | `+` 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
+   |
+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
+   |             | |
+   |             | `+` 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
+   |
+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
+   |             | |
+   |             | `+` 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
+   |
+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
+   |             | |
+   |             | `+` 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
+   |
+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
+   |             | |
+   |             | `+` 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
+   |
+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 61ebcfdefc3..3e53cdc4d98 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
-   |             |
+   |             | |
+   |             | `+` 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
    |
-   = 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
 
diff --git a/triagebot.toml b/triagebot.toml
index 6f60481600c..0e703a3ab34 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -4,3 +4,5 @@ allow-unauthenticated = [
     # I-* without I-nominated
     "I-compilemem", "I-compiletime", "I-crash", "I-hang", "I-ICE", "I-slow",
 ]
+
+[assign]