From 05fd1c7bbb5f8f2e6d176cf048d39e9c3bf48c03 Mon Sep 17 00:00:00 2001 From: Ruster-a11y Date: Sun, 17 Nov 2019 18:26:49 +0530 Subject: [PATCH 1/6] impl Add and AddAssign for String --- src/liballoc/string.rs | 139 +++++++++++++++++++++++++++++ src/libsyntax/parse/parser/expr.rs | 4 +- src/libsyntax_pos/symbol.rs | 14 +++ 3 files changed, 156 insertions(+), 1 deletion(-) diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index f7dff4c21f7c4..582e42096bce4 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1982,6 +1982,123 @@ impl Add<&str> for String { } } +#[allow(missing_docs)] +#[stable(feature = "add_string_and_dbl_ref_str", since = "1.41.0")] +impl Add<&&str> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &&str) -> String { + self.push_str(other); + self + } +} + +/// Implements the `+` operator for concatenating two `String`s. +/// +/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if +/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on +/// every operation, which would lead to `O(n^2)` running time when building an `n`-byte string by +/// repeated concatenation. +/// +/// The string on the right-hand side is only borrowed; its contents are copied into the returned +/// `String`. +/// +/// # Examples +/// +/// Concatenating two `String`s takes the first by value and borrows the second: +/// +/// ``` +/// let a = String::from("hello"); +/// let b = String::from(" world"); +/// let c = a + &b; +/// // `a` is moved and can no longer be used here. +/// ``` +/// +/// If you want to keep using the first `String`, you can clone it and append to the clone instead: +/// +/// ``` +/// let a = String::from("hello"); +/// let b = String::from(" world"); +/// let c = a.clone() + &b; +/// // `a` is still valid here. +/// ``` +/// +/// Concatenating `&str` slices can be done by converting the first to a `String`: +/// +/// ``` +/// let a = "hello"; +/// let b = " world"; +/// let c = a.to_string() + b; +/// ``` +#[stable(feature = "add_string_and_ref_string", since = "1.41.0")] +impl Add<&String> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &String) -> String { + self.push_str(other); + self + } +} + +#[allow(missing_docs)] +#[stable(feature = "add_string_and_dbl_ref_string", since = "1.41.0")] +impl Add<&&String> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &&String) -> String { + self.push_str(other); + self + } +} + +/// Implements the `+` operator for concatenating a string and a char together. +/// +/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if +/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on +/// every operation, which would lead to `O(n^2)` running time when building an `n`-byte string by +/// repeated concatenation. +/// +/// # Examples +/// +/// Concatenating a `String` with a `char` takes the `String` by value and copies the `char`: +/// +/// ``` +/// let a = String::from("hello world! "); +/// let b = '👋'; +/// let c = a + b; +/// // `a` is moved and can no longer be used here. +/// ``` +/// +/// If you want to keep using the initial `String`, you can clone it and append to the clone instead: +/// +/// ``` +/// let a = String::from("hello world! "); +/// let b = '👋'; +/// let c = a.clone() + b; +/// // `a` is still valid here. +/// ``` +/// +/// Concatenating `char` to a `&str` slice can be done by converting the `&str` to a `String`: +/// +/// ``` +/// let a = "hello world! "; +/// let b = '👋'; +/// let c = a.to_string() + b; +/// ``` +#[stable(feature = "add_string_and_char", since = "1.41.0")] +impl Add for String { + type Output = String; + + #[inline] + fn add(mut self, other: char) -> String { + self.push(other); + self + } +} + /// Implements the `+=` operator for appending to a `String`. /// /// This has the same behavior as the [`push_str`][String::push_str] method. @@ -1993,6 +2110,28 @@ impl AddAssign<&str> for String { } } +/// Implements the `+=` operator for appending to a `String`. +/// +/// This has the same behavior as the [`push_str`][String::push_str] method. +#[stable(feature = "stringaddassign_string", since = "1.41.0")] +impl AddAssign<&String> for String { + #[inline] + fn add_assign(&mut self, other: &String) { + self.push_str(other); + } +} + +/// Implements the `+=` operator for appending a `char` to a `String`. +/// +/// This has the same behavior as the [`push`][String::push] method. +#[stable(feature = "stringaddassign_char", since = "1.41.0")] +impl AddAssign for String { + #[inline] + fn add_assign(&mut self, other: char) { + self.push(other); + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index> for String { type Output = str; diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 800074035ce8b..1761462f4adb6 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -1084,7 +1084,9 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind { if self.token.span.hi() == next_token.span.lo() { - let s = String::from("0.") + &symbol.as_str(); + // FIXME: Should just be `&symbol.as_str()` but can't as of now due to Deref coercion + // Issue: https://github.com/rust-lang/rust/issues/51916 + let s = String::from("0.") + &symbol.as_str().get_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); return Some(Token::new(kind, self.token.span.to(next_token.span))); } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index d87b17eee3109..e7c7c40cc8cc7 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -1104,6 +1104,15 @@ pub struct SymbolStr { string: &'static str, } +/// FIXME: This is not needed once we are able to fix the Deref coercion issue. +/// Issue: https://github.com/rust-lang/rust/issues/51916 +impl SymbolStr { + #[inline] + pub fn get_str(&self) -> &str { + self.string + } +} + // This impl allows a `SymbolStr` to be directly equated with a `String` or // `&str`. impl> std::cmp::PartialEq for SymbolStr { @@ -1120,6 +1129,11 @@ impl !Sync for SymbolStr {} /// - `&*ss` is a `&str`; /// - `&ss as &str` is a `&str`, which means that `&ss` can be passed to a /// function expecting a `&str`. +/// +/// FIXME: This has no meaning anymore since the addition of implementations +/// of `Add for String`, `Add<&&str> for String`, and `Add<&&String> for String`. +/// Due to the outstanding Deref coercion issue this Deref implementation gets ignored. +/// Issue: https://github.com/rust-lang/rust/issues/51916 impl std::ops::Deref for SymbolStr { type Target = str; #[inline] From 0aeac9682f9e4efa706d268a8612605e3c206bd4 Mon Sep 17 00:00:00 2001 From: Ruster-a11y Date: Sun, 17 Nov 2019 18:53:13 +0530 Subject: [PATCH 2/6] Added tests for Add & AddAssign for String. Made previous code tidy friendly. --- src/liballoc/string.rs | 3 ++- src/liballoc/tests/string.rs | 18 +++++++++++++----- src/libsyntax/parse/parser/expr.rs | 3 ++- src/libsyntax_pos/symbol.rs | 5 ++--- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 582e42096bce4..7d765fecbec96 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2072,7 +2072,8 @@ impl Add<&&String> for String { /// // `a` is moved and can no longer be used here. /// ``` /// -/// If you want to keep using the initial `String`, you can clone it and append to the clone instead: +/// If you want to keep using the initial `String`, you can clone it and append to the +/// clone instead: /// /// ``` /// let a = String::from("hello world! "); diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 55edf56345b59..8676224fc14d4 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -195,8 +195,15 @@ fn test_add_assign() { assert_eq!(s.as_str(), ""); s += "abc"; assert_eq!(s.as_str(), "abc"); - s += "ประเทศไทย中华Việt Nam"; - assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam"); + s += "ประเทศไทย中华Việt Nam "; + assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam "); + + let s2 = "OPS ".to_string(); + s += &s2; + assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam OPS "); + + s += '👋'; + assert_eq!(s.as_str(), "abcประเทศไทย中华Việt Nam 👋"); } #[test] @@ -304,9 +311,10 @@ fn test_str_clear() { fn test_str_add() { let a = String::from("12345"); let b = a + "2"; - let b = b + "2"; - assert_eq!(b.len(), 7); - assert_eq!(b, "1234522"); + let b = b + "2 "; + let b = b + '👋'; + assert_eq!(b.len(), 12); + assert_eq!(b, "1234522 👋"); } #[test] diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 1761462f4adb6..ab363b4bf3de5 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -1084,7 +1084,8 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind { if self.token.span.hi() == next_token.span.lo() { - // FIXME: Should just be `&symbol.as_str()` but can't as of now due to Deref coercion + // FIXME: Should just be `&symbol.as_str()` but can't as of now due to + // Deref coercion. // Issue: https://github.com/rust-lang/rust/issues/51916 let s = String::from("0.") + &symbol.as_str().get_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index e7c7c40cc8cc7..5a0d61150213b 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -1129,9 +1129,8 @@ impl !Sync for SymbolStr {} /// - `&*ss` is a `&str`; /// - `&ss as &str` is a `&str`, which means that `&ss` can be passed to a /// function expecting a `&str`. -/// -/// FIXME: This has no meaning anymore since the addition of implementations -/// of `Add for String`, `Add<&&str> for String`, and `Add<&&String> for String`. +/// +/// FIXME: This has no meaning anymore since the addition of `impl Add for String`. /// Due to the outstanding Deref coercion issue this Deref implementation gets ignored. /// Issue: https://github.com/rust-lang/rust/issues/51916 impl std::ops::Deref for SymbolStr { From ec2b1c6cafab119a551966c70b442ff6022c5810 Mon Sep 17 00:00:00 2001 From: Ruster-a11y Date: Sun, 17 Nov 2019 21:16:59 +0530 Subject: [PATCH 3/6] Revised comments and fixed a deref issue in libsyntax\parse\parser\expr.rs --- src/liballoc/string.rs | 51 ++++++------------------------ src/libsyntax/parse/parser/expr.rs | 5 +-- src/libsyntax_pos/symbol.rs | 13 -------- 3 files changed, 10 insertions(+), 59 deletions(-) diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 7d765fecbec96..f806e5ca31f46 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1982,8 +1982,9 @@ impl Add<&str> for String { } } +// This had to be added to avoid breakage after adding `impl Add for String` #[allow(missing_docs)] -#[stable(feature = "add_string_and_dbl_ref_str", since = "1.41.0")] +#[stable(feature = "extra_add_string_and_dbl_ref_str", since = "1.41.0")] impl Add<&&str> for String { type Output = String; @@ -1994,44 +1995,9 @@ impl Add<&&str> for String { } } -/// Implements the `+` operator for concatenating two `String`s. -/// -/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if -/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on -/// every operation, which would lead to `O(n^2)` running time when building an `n`-byte string by -/// repeated concatenation. -/// -/// The string on the right-hand side is only borrowed; its contents are copied into the returned -/// `String`. -/// -/// # Examples -/// -/// Concatenating two `String`s takes the first by value and borrows the second: -/// -/// ``` -/// let a = String::from("hello"); -/// let b = String::from(" world"); -/// let c = a + &b; -/// // `a` is moved and can no longer be used here. -/// ``` -/// -/// If you want to keep using the first `String`, you can clone it and append to the clone instead: -/// -/// ``` -/// let a = String::from("hello"); -/// let b = String::from(" world"); -/// let c = a.clone() + &b; -/// // `a` is still valid here. -/// ``` -/// -/// Concatenating `&str` slices can be done by converting the first to a `String`: -/// -/// ``` -/// let a = "hello"; -/// let b = " world"; -/// let c = a.to_string() + b; -/// ``` -#[stable(feature = "add_string_and_ref_string", since = "1.41.0")] +// This had to be added to avoid breakage after adding `impl Add for String` +#[allow(missing_docs)] +#[stable(feature = "extra_add_string_and_ref_string", since = "1.41.0")] impl Add<&String> for String { type Output = String; @@ -2042,8 +2008,9 @@ impl Add<&String> for String { } } +// This had to be added to avoid breakage after adding `impl Add for String` #[allow(missing_docs)] -#[stable(feature = "add_string_and_dbl_ref_string", since = "1.41.0")] +#[stable(feature = "extra_add_string_and_dbl_ref_string", since = "1.41.0")] impl Add<&&String> for String { type Output = String; @@ -2114,7 +2081,7 @@ impl AddAssign<&str> for String { /// Implements the `+=` operator for appending to a `String`. /// /// This has the same behavior as the [`push_str`][String::push_str] method. -#[stable(feature = "stringaddassign_string", since = "1.41.0")] +#[stable(feature = "string_add_assign_string", since = "1.41.0")] impl AddAssign<&String> for String { #[inline] fn add_assign(&mut self, other: &String) { @@ -2125,7 +2092,7 @@ impl AddAssign<&String> for String { /// Implements the `+=` operator for appending a `char` to a `String`. /// /// This has the same behavior as the [`push`][String::push] method. -#[stable(feature = "stringaddassign_char", since = "1.41.0")] +#[stable(feature = "string_add_assign_char", since = "1.41.0")] impl AddAssign for String { #[inline] fn add_assign(&mut self, other: char) { diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index ab363b4bf3de5..296dd894cddc7 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -1084,10 +1084,7 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind { if self.token.span.hi() == next_token.span.lo() { - // FIXME: Should just be `&symbol.as_str()` but can't as of now due to - // Deref coercion. - // Issue: https://github.com/rust-lang/rust/issues/51916 - let s = String::from("0.") + &symbol.as_str().get_str(); + let s = String::from("0.") + &*symbol.as_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); return Some(Token::new(kind, self.token.span.to(next_token.span))); } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 5a0d61150213b..d87b17eee3109 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -1104,15 +1104,6 @@ pub struct SymbolStr { string: &'static str, } -/// FIXME: This is not needed once we are able to fix the Deref coercion issue. -/// Issue: https://github.com/rust-lang/rust/issues/51916 -impl SymbolStr { - #[inline] - pub fn get_str(&self) -> &str { - self.string - } -} - // This impl allows a `SymbolStr` to be directly equated with a `String` or // `&str`. impl> std::cmp::PartialEq for SymbolStr { @@ -1129,10 +1120,6 @@ impl !Sync for SymbolStr {} /// - `&*ss` is a `&str`; /// - `&ss as &str` is a `&str`, which means that `&ss` can be passed to a /// function expecting a `&str`. -/// -/// FIXME: This has no meaning anymore since the addition of `impl Add for String`. -/// Due to the outstanding Deref coercion issue this Deref implementation gets ignored. -/// Issue: https://github.com/rust-lang/rust/issues/51916 impl std::ops::Deref for SymbolStr { type Target = str; #[inline] From c5e19cab8b5a14537d40f19c43f56f289b3c89d5 Mon Sep 17 00:00:00 2001 From: Ruster-a11y Date: Mon, 18 Nov 2019 04:23:56 +0530 Subject: [PATCH 4/6] impl Add and AddAssign for String + rebase with master --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- src/doc/rustc-guide | 2 +- src/liballoc/string.rs | 107 ++++++++++++++++++++++++++++++ src/librustc_parse/parser/expr.rs | 2 +- src/llvm-project | 2 +- src/tools/cargo | 2 +- src/tools/clippy | 2 +- src/tools/miri | 2 +- 12 files changed, 118 insertions(+), 11 deletions(-) diff --git a/src/doc/book b/src/doc/book index e79dd62aa6339..28fa3d15b0bc6 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit e79dd62aa63396714278d484d91d48826737f47f +Subproject commit 28fa3d15b0bc67ea5e79eeff2198e4277fc61baf diff --git a/src/doc/edition-guide b/src/doc/edition-guide index f553fb26c60c4..e58bc4ca104e8 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit f553fb26c60c4623ea88a1cfe731eafe0643ce34 +Subproject commit e58bc4ca104e890ac56af846877c874c432a64b5 diff --git a/src/doc/nomicon b/src/doc/nomicon index 58e36e0e08dec..5004ad30d69f9 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 58e36e0e08dec5a379ac568827c058e25990d6cd +Subproject commit 5004ad30d69f93553ceef74439fea2159d1f769e diff --git a/src/doc/reference b/src/doc/reference index 45558c464fb45..4b21b646669e0 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 45558c464fb458affbcdcb34323946da45c8a117 +Subproject commit 4b21b646669e0af49fae7cae301898dc4bfaa1f0 diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index dcee312c66267..f3197ddf2abab 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit dcee312c66267eb5a2f6f1561354003950e29105 +Subproject commit f3197ddf2abab9abdbc029def8164f4a748b0d91 diff --git a/src/doc/rustc-guide b/src/doc/rustc-guide index 934380b7cfcea..941968db2fd9c 160000 --- a/src/doc/rustc-guide +++ b/src/doc/rustc-guide @@ -1 +1 @@ -Subproject commit 934380b7cfceaaa4e1b9bb0de4a372f32725520b +Subproject commit 941968db2fd9c85788a4f971c8e425d46b4cb734 diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index f7dff4c21f7c4..f806e5ca31f46 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1982,6 +1982,91 @@ impl Add<&str> for String { } } +// This had to be added to avoid breakage after adding `impl Add for String` +#[allow(missing_docs)] +#[stable(feature = "extra_add_string_and_dbl_ref_str", since = "1.41.0")] +impl Add<&&str> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &&str) -> String { + self.push_str(other); + self + } +} + +// This had to be added to avoid breakage after adding `impl Add for String` +#[allow(missing_docs)] +#[stable(feature = "extra_add_string_and_ref_string", since = "1.41.0")] +impl Add<&String> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &String) -> String { + self.push_str(other); + self + } +} + +// This had to be added to avoid breakage after adding `impl Add for String` +#[allow(missing_docs)] +#[stable(feature = "extra_add_string_and_dbl_ref_string", since = "1.41.0")] +impl Add<&&String> for String { + type Output = String; + + #[inline] + fn add(mut self, other: &&String) -> String { + self.push_str(other); + self + } +} + +/// Implements the `+` operator for concatenating a string and a char together. +/// +/// This consumes the `String` on the left-hand side and re-uses its buffer (growing it if +/// necessary). This is done to avoid allocating a new `String` and copying the entire contents on +/// every operation, which would lead to `O(n^2)` running time when building an `n`-byte string by +/// repeated concatenation. +/// +/// # Examples +/// +/// Concatenating a `String` with a `char` takes the `String` by value and copies the `char`: +/// +/// ``` +/// let a = String::from("hello world! "); +/// let b = '👋'; +/// let c = a + b; +/// // `a` is moved and can no longer be used here. +/// ``` +/// +/// If you want to keep using the initial `String`, you can clone it and append to the +/// clone instead: +/// +/// ``` +/// let a = String::from("hello world! "); +/// let b = '👋'; +/// let c = a.clone() + b; +/// // `a` is still valid here. +/// ``` +/// +/// Concatenating `char` to a `&str` slice can be done by converting the `&str` to a `String`: +/// +/// ``` +/// let a = "hello world! "; +/// let b = '👋'; +/// let c = a.to_string() + b; +/// ``` +#[stable(feature = "add_string_and_char", since = "1.41.0")] +impl Add for String { + type Output = String; + + #[inline] + fn add(mut self, other: char) -> String { + self.push(other); + self + } +} + /// Implements the `+=` operator for appending to a `String`. /// /// This has the same behavior as the [`push_str`][String::push_str] method. @@ -1993,6 +2078,28 @@ impl AddAssign<&str> for String { } } +/// Implements the `+=` operator for appending to a `String`. +/// +/// This has the same behavior as the [`push_str`][String::push_str] method. +#[stable(feature = "string_add_assign_string", since = "1.41.0")] +impl AddAssign<&String> for String { + #[inline] + fn add_assign(&mut self, other: &String) { + self.push_str(other); + } +} + +/// Implements the `+=` operator for appending a `char` to a `String`. +/// +/// This has the same behavior as the [`push`][String::push] method. +#[stable(feature = "string_add_assign_char", since = "1.41.0")] +impl AddAssign for String { + #[inline] + fn add_assign(&mut self, other: char) { + self.push(other); + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index> for String { type Output = str; diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index a56a7bf1802c7..1f0f3f2020117 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1110,7 +1110,7 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = next_token.kind { if self.token.span.hi() == next_token.span.lo() { - let s = String::from("0.") + &symbol.as_str(); + let s = String::from("0.") + &*symbol.as_str(); let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); return Some(Token::new(kind, self.token.span.to(next_token.span))); } diff --git a/src/llvm-project b/src/llvm-project index cf9304d6d0c6a..14a3b123074e0 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit cf9304d6d0c6a66c27a1664dd55d8bcc8be0bf09 +Subproject commit 14a3b123074e066d64a99886941473058e52197d diff --git a/src/tools/cargo b/src/tools/cargo index 8280633db680d..5da4b4d479638 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 8280633db680dec5bfe1de25156d1a1d53e6d190 +Subproject commit 5da4b4d47963868d9878480197581ccbbdaece74 diff --git a/src/tools/clippy b/src/tools/clippy index b4f1769734b62..c8e3cfbdd9978 160000 --- a/src/tools/clippy +++ b/src/tools/clippy @@ -1 +1 @@ -Subproject commit b4f1769734b6204fc6bece8556b7b80a7683271e +Subproject commit c8e3cfbdd997839c771ca32c7ac860fe95149a04 diff --git a/src/tools/miri b/src/tools/miri index 67a63f89d8c0e..d4e4fe71e6a95 160000 --- a/src/tools/miri +++ b/src/tools/miri @@ -1 +1 @@ -Subproject commit 67a63f89d8c0e5b22fb52cc33274283819f41792 +Subproject commit d4e4fe71e6a9568f5d081d99f1c621c5a4ddd7db From e4f4cf93d92db5750b35c8d58fab72a3d5cd4d5d Mon Sep 17 00:00:00 2001 From: Ruster-a11y <57512108+Ruster-a11y@users.noreply.github.com> Date: Mon, 18 Nov 2019 04:49:48 +0530 Subject: [PATCH 5/6] File moved This file was moved to a different directory so this isn't needed anymore. --- src/libsyntax/parse/parser/expr.rs | 1964 ---------------------------- 1 file changed, 1964 deletions(-) delete mode 100644 src/libsyntax/parse/parser/expr.rs diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs deleted file mode 100644 index 296dd894cddc7..0000000000000 --- a/src/libsyntax/parse/parser/expr.rs +++ /dev/null @@ -1,1964 +0,0 @@ -use super::{Parser, Restrictions, PrevTokenKind, TokenType, PathStyle, BlockMode}; -use super::{SemiColonMode, SeqSep, TokenExpectType}; -use super::pat::{GateOr, PARAM_EXPECTED}; -use super::diagnostics::Error; - -use crate::ast::{ - self, DUMMY_NODE_ID, Attribute, AttrStyle, Ident, CaptureBy, BlockCheckMode, - Expr, ExprKind, RangeLimits, Label, Movability, IsAsync, Arm, Ty, TyKind, - FunctionRetTy, Param, FnDecl, BinOpKind, BinOp, UnOp, Mac, AnonConst, Field, Lit, -}; -use crate::maybe_recover_from_interpolated_ty_qpath; -use crate::token::{self, Token, TokenKind}; -use crate::print::pprust; -use crate::ptr::P; -use crate::source_map::{self, Span}; -use crate::util::classify; -use crate::util::literal::LitError; -use crate::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par}; - -use errors::{PResult, Applicability}; -use syntax_pos::symbol::{kw, sym}; -use syntax_pos::Symbol; -use std::mem; -use rustc_data_structures::thin_vec::ThinVec; - -/// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression -/// dropped into the token stream, which happens while parsing the result of -/// macro expansion). Placement of these is not as complex as I feared it would -/// be. The important thing is to make sure that lookahead doesn't balk at -/// `token::Interpolated` tokens. -macro_rules! maybe_whole_expr { - ($p:expr) => { - if let token::Interpolated(nt) = &$p.token.kind { - match &**nt { - token::NtExpr(e) | token::NtLiteral(e) => { - let e = e.clone(); - $p.bump(); - return Ok(e); - } - token::NtPath(path) => { - let path = path.clone(); - $p.bump(); - return Ok($p.mk_expr( - $p.token.span, ExprKind::Path(None, path), ThinVec::new() - )); - } - token::NtBlock(block) => { - let block = block.clone(); - $p.bump(); - return Ok($p.mk_expr( - $p.token.span, ExprKind::Block(block, None), ThinVec::new() - )); - } - // N.B., `NtIdent(ident)` is normalized to `Ident` in `fn bump`. - _ => {}, - }; - } - } -} - -#[derive(Debug)] -pub(super) enum LhsExpr { - NotYetParsed, - AttributesParsed(ThinVec), - AlreadyParsed(P), -} - -impl From>> for LhsExpr { - /// Converts `Some(attrs)` into `LhsExpr::AttributesParsed(attrs)` - /// and `None` into `LhsExpr::NotYetParsed`. - /// - /// This conversion does not allocate. - fn from(o: Option>) -> Self { - if let Some(attrs) = o { - LhsExpr::AttributesParsed(attrs) - } else { - LhsExpr::NotYetParsed - } - } -} - -impl From> for LhsExpr { - /// Converts the `expr: P` into `LhsExpr::AlreadyParsed(expr)`. - /// - /// This conversion does not allocate. - fn from(expr: P) -> Self { - LhsExpr::AlreadyParsed(expr) - } -} - -impl<'a> Parser<'a> { - /// Parses an expression. - #[inline] - pub fn parse_expr(&mut self) -> PResult<'a, P> { - self.parse_expr_res(Restrictions::empty(), None) - } - - fn parse_paren_expr_seq(&mut self) -> PResult<'a, Vec>> { - self.parse_paren_comma_seq(|p| { - match p.parse_expr() { - Ok(expr) => Ok(expr), - Err(mut err) => match p.token.kind { - token::Ident(name, false) - if name == kw::Underscore && p.look_ahead(1, |t| { - t == &token::Comma - }) => { - // Special-case handling of `foo(_, _, _)` - err.emit(); - let sp = p.token.span; - p.bump(); - Ok(p.mk_expr(sp, ExprKind::Err, ThinVec::new())) - } - _ => Err(err), - }, - } - }).map(|(r, _)| r) - } - - /// Parses an expression, subject to the given restrictions. - #[inline] - pub(super) fn parse_expr_res( - &mut self, - r: Restrictions, - already_parsed_attrs: Option> - ) -> PResult<'a, P> { - self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs)) - } - - /// Parses an associative expression. - /// - /// This parses an expression accounting for associativity and precedence of the operators in - /// the expression. - #[inline] - fn parse_assoc_expr( - &mut self, - already_parsed_attrs: Option>, - ) -> PResult<'a, P> { - self.parse_assoc_expr_with(0, already_parsed_attrs.into()) - } - - /// Parses an associative expression with operators of at least `min_prec` precedence. - pub(super) fn parse_assoc_expr_with( - &mut self, - min_prec: usize, - lhs: LhsExpr, - ) -> PResult<'a, P> { - let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs { - expr - } else { - let attrs = match lhs { - LhsExpr::AttributesParsed(attrs) => Some(attrs), - _ => None, - }; - if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind) { - return self.parse_prefix_range_expr(attrs); - } else { - self.parse_prefix_expr(attrs)? - } - }; - let last_type_ascription_set = self.last_type_ascription.is_some(); - - match (self.expr_is_complete(&lhs), AssocOp::from_token(&self.token)) { - (true, None) => { - self.last_type_ascription = None; - // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071 - return Ok(lhs); - } - (false, _) => {} // continue parsing the expression - // An exhaustive check is done in the following block, but these are checked first - // because they *are* ambiguous but also reasonable looking incorrect syntax, so we - // want to keep their span info to improve diagnostics in these cases in a later stage. - (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` - (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` - (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) - (true, Some(AssocOp::Add)) // `{ 42 } + 42 - // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: - // `if x { a } else { b } && if y { c } else { d }` - if !self.look_ahead(1, |t| t.is_reserved_ident()) => { - self.last_type_ascription = None; - // These cases are ambiguous and can't be identified in the parser alone - let sp = self.sess.source_map().start_point(self.token.span); - self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); - return Ok(lhs); - } - (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => { - self.last_type_ascription = None; - return Ok(lhs); - } - (true, Some(_)) => { - // We've found an expression that would be parsed as a statement, but the next - // token implies this should be parsed as an expression. - // For example: `if let Some(x) = x { x } else { 0 } / 2` - let mut err = self.struct_span_err(self.token.span, &format!( - "expected expression, found `{}`", - pprust::token_to_string(&self.token), - )); - err.span_label(self.token.span, "expected expression"); - self.sess.expr_parentheses_needed( - &mut err, - lhs.span, - Some(pprust::expr_to_string(&lhs), - )); - err.emit(); - } - } - self.expected_tokens.push(TokenType::Operator); - while let Some(op) = AssocOp::from_token(&self.token) { - - // Adjust the span for interpolated LHS to point to the `$lhs` token and not to what - // it refers to. Interpolated identifiers are unwrapped early and never show up here - // as `PrevTokenKind::Interpolated` so if LHS is a single identifier we always process - // it as "interpolated", it doesn't change the answer for non-interpolated idents. - let lhs_span = match (self.prev_token_kind, &lhs.kind) { - (PrevTokenKind::Interpolated, _) => self.prev_span, - (PrevTokenKind::Ident, &ExprKind::Path(None, ref path)) - if path.segments.len() == 1 => self.prev_span, - _ => lhs.span, - }; - - let cur_op_span = self.token.span; - let restrictions = if op.is_assign_like() { - self.restrictions & Restrictions::NO_STRUCT_LITERAL - } else { - self.restrictions - }; - let prec = op.precedence(); - if prec < min_prec { - break; - } - // Check for deprecated `...` syntax - if self.token == token::DotDotDot && op == AssocOp::DotDotEq { - self.err_dotdotdot_syntax(self.token.span); - } - - if self.token == token::LArrow { - self.err_larrow_operator(self.token.span); - } - - self.bump(); - if op.is_comparison() { - if let Some(expr) = self.check_no_chained_comparison(&lhs, &op)? { - return Ok(expr); - } - } - // Special cases: - if op == AssocOp::As { - lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?; - continue - } else if op == AssocOp::Colon { - let maybe_path = self.could_ascription_be_path(&lhs.kind); - self.last_type_ascription = Some((self.prev_span, maybe_path)); - - lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?; - self.sess.gated_spans.gate(sym::type_ascription, lhs.span); - continue - } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { - // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to - // generalise it to the Fixity::None code. - // - // We have 2 alternatives here: `x..y`/`x..=y` and `x..`/`x..=` The other - // two variants are handled with `parse_prefix_range_expr` call above. - let rhs = if self.is_at_start_of_range_notation_rhs() { - Some(self.parse_assoc_expr_with(prec + 1, LhsExpr::NotYetParsed)?) - } else { - None - }; - let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs { - x.span - } else { - cur_op_span - }); - let limits = if op == AssocOp::DotDot { - RangeLimits::HalfOpen - } else { - RangeLimits::Closed - }; - - let r = self.mk_range(Some(lhs), rhs, limits)?; - lhs = self.mk_expr(lhs_span.to(rhs_span), r, ThinVec::new()); - break - } - - let fixity = op.fixity(); - let prec_adjustment = match fixity { - Fixity::Right => 0, - Fixity::Left => 1, - // We currently have no non-associative operators that are not handled above by - // the special cases. The code is here only for future convenience. - Fixity::None => 1, - }; - let rhs = self.with_res( - restrictions - Restrictions::STMT_EXPR, - |this| this.parse_assoc_expr_with(prec + prec_adjustment, LhsExpr::NotYetParsed) - )?; - - // Make sure that the span of the parent node is larger than the span of lhs and rhs, - // including the attributes. - let lhs_span = lhs - .attrs - .iter() - .filter(|a| a.style == AttrStyle::Outer) - .next() - .map_or(lhs_span, |a| a.span); - let span = lhs_span.to(rhs.span); - lhs = match op { - AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | - AssocOp::Modulus | AssocOp::LAnd | AssocOp::LOr | AssocOp::BitXor | - AssocOp::BitAnd | AssocOp::BitOr | AssocOp::ShiftLeft | AssocOp::ShiftRight | - AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual | - AssocOp::Greater | AssocOp::GreaterEqual => { - let ast_op = op.to_ast_binop().unwrap(); - 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::AssignOp(k) => { - let aop = match k { - token::Plus => BinOpKind::Add, - token::Minus => BinOpKind::Sub, - token::Star => BinOpKind::Mul, - token::Slash => BinOpKind::Div, - token::Percent => BinOpKind::Rem, - token::Caret => BinOpKind::BitXor, - token::And => BinOpKind::BitAnd, - token::Or => BinOpKind::BitOr, - token::Shl => BinOpKind::Shl, - token::Shr => BinOpKind::Shr, - }; - let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); - self.mk_expr(span, aopexpr, ThinVec::new()) - } - AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => { - self.bug("AssocOp should have been handled by special case") - } - }; - - if let Fixity::None = fixity { break } - } - if last_type_ascription_set { - self.last_type_ascription = None; - } - Ok(lhs) - } - - /// Checks if this expression is a successfully parsed statement. - fn expr_is_complete(&self, e: &Expr) -> bool { - self.restrictions.contains(Restrictions::STMT_EXPR) && - !classify::expr_requires_semi_to_be_stmt(e) - } - - fn is_at_start_of_range_notation_rhs(&self) -> bool { - if self.token.can_begin_expr() { - // Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`. - if self.token == token::OpenDelim(token::Brace) { - return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); - } - true - } else { - false - } - } - - /// Parses prefix-forms of range notation: `..expr`, `..`, `..=expr`. - fn parse_prefix_range_expr( - &mut self, - already_parsed_attrs: Option> - ) -> PResult<'a, P> { - // Check for deprecated `...` syntax. - if self.token == token::DotDotDot { - self.err_dotdotdot_syntax(self.token.span); - } - - debug_assert!([token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind), - "parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq", - self.token); - let tok = self.token.clone(); - let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; - let lo = self.token.span; - let mut hi = self.token.span; - self.bump(); - let opt_end = if self.is_at_start_of_range_notation_rhs() { - // RHS must be parsed with more associativity than the dots. - let next_prec = AssocOp::from_token(&tok).unwrap().precedence() + 1; - Some(self.parse_assoc_expr_with(next_prec, LhsExpr::NotYetParsed) - .map(|x| { - hi = x.span; - x - })?) - } else { - None - }; - let limits = if tok == token::DotDot { - RangeLimits::HalfOpen - } else { - RangeLimits::Closed - }; - - let r = self.mk_range(None, opt_end, limits)?; - Ok(self.mk_expr(lo.to(hi), r, attrs)) - } - - /// Parses a prefix-unary-operator expr. - fn parse_prefix_expr( - &mut self, - already_parsed_attrs: Option> - ) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; - let lo = self.token.span; - // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() - let (hi, ex) = match self.token.kind { - token::Not => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Not, e)) - } - // Suggest `!` for bitwise negation when encountering a `~` - token::Tilde => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - let span_of_tilde = lo; - self.struct_span_err(span_of_tilde, "`~` cannot be used as a unary operator") - .span_suggestion_short( - span_of_tilde, - "use `!` to perform bitwise not", - "!".to_owned(), - Applicability::MachineApplicable - ) - .emit(); - (lo.to(span), self.mk_unary(UnOp::Not, e)) - } - token::BinOp(token::Minus) => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Neg, e)) - } - token::BinOp(token::Star) => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Deref, e)) - } - token::BinOp(token::And) | token::AndAnd => { - self.expect_and()?; - let m = self.parse_mutability(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), ExprKind::AddrOf(m, e)) - } - token::Ident(..) if self.token.is_keyword(kw::Box) => { - self.bump(); - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - let span = lo.to(span); - self.sess.gated_spans.gate(sym::box_syntax, span); - (span, ExprKind::Box(e)) - } - token::Ident(..) if self.token.is_ident_named(sym::not) => { - // `not` is just an ordinary identifier in Rust-the-language, - // but as `rustc`-the-compiler, we can issue clever diagnostics - // for confused users who really want to say `!` - let token_cannot_continue_expr = |t: &Token| match t.kind { - // These tokens can start an expression after `!`, but - // can't continue an expression after an ident - token::Ident(name, is_raw) => token::ident_can_begin_expr(name, t.span, is_raw), - token::Literal(..) | token::Pound => true, - _ => t.is_whole_expr(), - }; - let cannot_continue_expr = self.look_ahead(1, token_cannot_continue_expr); - if cannot_continue_expr { - self.bump(); - // Emit the error ... - self.struct_span_err( - self.token.span, - &format!("unexpected {} after identifier",self.this_token_descr()) - ) - .span_suggestion_short( - // Span the `not` plus trailing whitespace to avoid - // trailing whitespace after the `!` in our suggestion - self.sess.source_map() - .span_until_non_whitespace(lo.to(self.token.span)), - "use `!` to perform logical negation", - "!".to_owned(), - Applicability::MachineApplicable - ) - .emit(); - // —and recover! (just as if we were in the block - // for the `token::Not` arm) - let e = self.parse_prefix_expr(None); - let (span, e) = self.interpolated_or_expr_span(e)?; - (lo.to(span), self.mk_unary(UnOp::Not, e)) - } else { - return self.parse_dot_or_call_expr(Some(attrs)); - } - } - _ => { return self.parse_dot_or_call_expr(Some(attrs)); } - }; - return Ok(self.mk_expr(lo.to(hi), ex, attrs)); - } - - /// Returns the span of expr, if it was not interpolated or the span of the interpolated token. - fn interpolated_or_expr_span( - &self, - expr: PResult<'a, P>, - ) -> PResult<'a, (Span, P)> { - expr.map(|e| { - if self.prev_token_kind == PrevTokenKind::Interpolated { - (self.prev_span, e) - } else { - (e.span, e) - } - }) - } - - fn parse_assoc_op_cast(&mut self, lhs: P, lhs_span: Span, - expr_kind: fn(P, P) -> ExprKind) - -> PResult<'a, P> { - let mk_expr = |this: &mut Self, rhs: P| { - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), ThinVec::new()) - }; - - // Save the state of the parser before parsing type normally, in case there is a - // LessThan comparison after this cast. - let parser_snapshot_before_type = self.clone(); - match self.parse_ty_no_plus() { - Ok(rhs) => { - Ok(mk_expr(self, rhs)) - } - Err(mut type_err) => { - // Rewind to before attempting to parse the type with generics, to recover - // from situations like `x as usize < y` in which we first tried to parse - // `usize < y` as a type with generic arguments. - let parser_snapshot_after_type = self.clone(); - mem::replace(self, parser_snapshot_before_type); - - match self.parse_path(PathStyle::Expr) { - Ok(path) => { - let (op_noun, op_verb) = match self.token.kind { - token::Lt => ("comparison", "comparing"), - token::BinOp(token::Shl) => ("shift", "shifting"), - _ => { - // We can end up here even without `<` being the next token, for - // example because `parse_ty_no_plus` returns `Err` on keywords, - // but `parse_path` returns `Ok` on them due to error recovery. - // Return original error and parser state. - mem::replace(self, parser_snapshot_after_type); - return Err(type_err); - } - }; - - // Successfully parsed the type path leaving a `<` yet to parse. - type_err.cancel(); - - // Report non-fatal diagnostics, keep `x as usize` as an expression - // in AST and continue parsing. - let msg = format!( - "`<` is interpreted as a start of generic arguments for `{}`, not a {}", - pprust::path_to_string(&path), - op_noun, - ); - let span_after_type = parser_snapshot_after_type.token.span; - let expr = mk_expr(self, P(Ty { - span: path.span, - kind: TyKind::Path(None, path), - id: DUMMY_NODE_ID, - })); - - let expr_str = self.span_to_snippet(expr.span) - .unwrap_or_else(|_| pprust::expr_to_string(&expr)); - - self.struct_span_err(self.token.span, &msg) - .span_label( - self.look_ahead(1, |t| t.span).to(span_after_type), - "interpreted as generic arguments" - ) - .span_label(self.token.span, format!("not interpreted as {}", op_noun)) - .span_suggestion( - expr.span, - &format!("try {} the cast value", op_verb), - format!("({})", expr_str), - Applicability::MachineApplicable, - ) - .emit(); - - Ok(expr) - } - Err(mut path_err) => { - // Couldn't parse as a path, return original error and parser state. - path_err.cancel(); - mem::replace(self, parser_snapshot_after_type); - Err(type_err) - } - } - } - } - } - - /// Parses `a.b` or `a(13)` or `a[4]` or just `a`. - fn parse_dot_or_call_expr( - &mut self, - already_parsed_attrs: Option>, - ) -> PResult<'a, P> { - let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; - - let b = self.parse_bottom_expr(); - let (span, b) = self.interpolated_or_expr_span(b)?; - self.parse_dot_or_call_expr_with(b, span, attrs) - } - - pub(super) fn parse_dot_or_call_expr_with( - &mut self, - e0: P, - lo: Span, - mut attrs: ThinVec, - ) -> PResult<'a, P> { - // Stitch the list of outer attributes onto the return value. - // A little bit ugly, but the best way given the current code - // structure - self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| - expr.map(|mut expr| { - attrs.extend::>(expr.attrs.into()); - expr.attrs = attrs; - match expr.kind { - ExprKind::If(..) if !expr.attrs.is_empty() => { - // Just point to the first attribute in there... - let span = expr.attrs[0].span; - self.span_err(span, "attributes are not yet allowed on `if` expressions"); - } - _ => {} - } - expr - }) - ) - } - - fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: Span) -> PResult<'a, P> { - let mut e = e0; - let mut hi; - loop { - // expr? - while self.eat(&token::Question) { - let hi = self.prev_span; - e = self.mk_expr(lo.to(hi), ExprKind::Try(e), ThinVec::new()); - } - - // expr.f - if self.eat(&token::Dot) { - match self.token.kind { - token::Ident(..) => { - e = self.parse_dot_suffix(e, lo)?; - } - token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) => { - let span = self.token.span; - self.bump(); - let field = ExprKind::Field(e, Ident::new(symbol, span)); - e = self.mk_expr(lo.to(span), field, ThinVec::new()); - - self.expect_no_suffix(span, "a tuple index", suffix); - } - token::Literal(token::Lit { kind: token::Float, symbol, .. }) => { - self.bump(); - let fstr = symbol.as_str(); - let msg = format!("unexpected token: `{}`", symbol); - let mut err = self.diagnostic().struct_span_err(self.prev_span, &msg); - err.span_label(self.prev_span, "unexpected token"); - if fstr.chars().all(|x| "0123456789.".contains(x)) { - let float = match fstr.parse::().ok() { - Some(f) => f, - None => continue, - }; - let sugg = pprust::to_string(|s| { - s.popen(); - s.print_expr(&e); - s.s.word( "."); - s.print_usize(float.trunc() as usize); - s.pclose(); - s.s.word("."); - s.s.word(fstr.splitn(2, ".").last().unwrap().to_string()) - }); - err.span_suggestion( - lo.to(self.prev_span), - "try parenthesizing the first index", - sugg, - Applicability::MachineApplicable - ); - } - return Err(err); - - } - _ => { - // FIXME Could factor this out into non_fatal_unexpected or something. - let actual = self.this_token_to_string(); - self.span_err(self.token.span, &format!("unexpected token: `{}`", actual)); - } - } - continue; - } - if self.expr_is_complete(&e) { break; } - match self.token.kind { - // expr(...) - token::OpenDelim(token::Paren) => { - let seq = self.parse_paren_expr_seq().map(|es| { - let nd = self.mk_call(e, es); - let hi = self.prev_span; - self.mk_expr(lo.to(hi), nd, ThinVec::new()) - }); - e = self.recover_seq_parse_error(token::Paren, lo, seq); - } - - // expr[...] - // Could be either an index expression or a slicing expression. - token::OpenDelim(token::Bracket) => { - self.bump(); - let ix = self.parse_expr()?; - hi = self.token.span; - self.expect(&token::CloseDelim(token::Bracket))?; - let index = self.mk_index(e, ix); - e = self.mk_expr(lo.to(hi), index, ThinVec::new()) - } - _ => return Ok(e) - } - } - return Ok(e); - } - - /// Assuming we have just parsed `.`, continue parsing into an expression. - fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { - if self.token.span.rust_2018() && self.eat_keyword(kw::Await) { - return self.mk_await_expr(self_arg, lo); - } - - let segment = self.parse_path_segment(PathStyle::Expr)?; - self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); - - Ok(match self.token.kind { - token::OpenDelim(token::Paren) => { - // Method call `expr.f()` - let mut args = self.parse_paren_expr_seq()?; - args.insert(0, self_arg); - - let span = lo.to(self.prev_span); - self.mk_expr(span, ExprKind::MethodCall(segment, args), ThinVec::new()) - } - _ => { - // Field access `expr.f` - if let Some(args) = segment.args { - self.span_err(args.span(), - "field expressions may not have generic arguments"); - } - - let span = lo.to(self.prev_span); - self.mk_expr(span, ExprKind::Field(self_arg, segment.ident), ThinVec::new()) - } - }) - } - - /// At the bottom (top?) of the precedence hierarchy, - /// Parses things like parenthesized exprs, macros, `return`, etc. - /// - /// N.B., this does not parse outer attributes, and is private because it only works - /// correctly if called from `parse_dot_or_call_expr()`. - fn parse_bottom_expr(&mut self) -> PResult<'a, P> { - maybe_recover_from_interpolated_ty_qpath!(self, true); - maybe_whole_expr!(self); - - // Outer attributes are already parsed and will be - // added to the return value after the fact. - // - // Therefore, prevent sub-parser from parsing - // attributes by giving them a empty "already-parsed" list. - let mut attrs = ThinVec::new(); - - let lo = self.token.span; - let mut hi = self.token.span; - - let ex: ExprKind; - - macro_rules! parse_lit { - () => { - match self.parse_lit() { - Ok(literal) => { - hi = self.prev_span; - ex = ExprKind::Lit(literal); - } - Err(mut err) => { - err.cancel(); - return Err(self.expected_expression_found()); - } - } - } - } - - // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. - match self.token.kind { - // This match arm is a special-case of the `_` match arm below and - // could be removed without changing functionality, but it's faster - // to have it here, especially for programs with large constants. - token::Literal(_) => { - parse_lit!() - } - token::OpenDelim(token::Paren) => { - self.bump(); - - attrs.extend(self.parse_inner_attributes()?); - - // `(e)` is parenthesized `e`. - // `(e,)` is a tuple with only one field, `e`. - let mut es = vec![]; - let mut trailing_comma = false; - let mut recovered = false; - while self.token != token::CloseDelim(token::Paren) { - es.push(match self.parse_expr() { - Ok(es) => es, - Err(mut err) => { - // Recover from parse error in tuple list. - match self.token.kind { - token::Ident(name, false) - if name == kw::Underscore && self.look_ahead(1, |t| { - t == &token::Comma - }) => { - // Special-case handling of `Foo<(_, _, _)>` - err.emit(); - let sp = self.token.span; - self.bump(); - self.mk_expr(sp, ExprKind::Err, ThinVec::new()) - } - _ => return Ok( - self.recover_seq_parse_error(token::Paren, lo, Err(err)), - ), - } - } - }); - recovered = self.expect_one_of( - &[], - &[token::Comma, token::CloseDelim(token::Paren)], - )?; - if self.eat(&token::Comma) { - trailing_comma = true; - } else { - trailing_comma = false; - break; - } - } - if !recovered { - self.bump(); - } - - hi = self.prev_span; - ex = if es.len() == 1 && !trailing_comma { - ExprKind::Paren(es.into_iter().nth(0).unwrap()) - } else { - ExprKind::Tup(es) - }; - } - token::OpenDelim(token::Brace) => { - return self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs); - } - token::BinOp(token::Or) | token::OrOr => { - return self.parse_closure_expr(attrs); - } - token::OpenDelim(token::Bracket) => { - self.bump(); - - attrs.extend(self.parse_inner_attributes()?); - - if self.eat(&token::CloseDelim(token::Bracket)) { - // Empty vector - ex = ExprKind::Array(Vec::new()); - } else { - // Non-empty vector - let first_expr = self.parse_expr()?; - if self.eat(&token::Semi) { - // Repeating array syntax: `[ 0; 512 ]` - let count = AnonConst { - id: DUMMY_NODE_ID, - value: self.parse_expr()?, - }; - self.expect(&token::CloseDelim(token::Bracket))?; - ex = ExprKind::Repeat(first_expr, count); - } else if self.eat(&token::Comma) { - // Vector with two or more elements - let remaining_exprs = self.parse_seq_to_end( - &token::CloseDelim(token::Bracket), - SeqSep::trailing_allowed(token::Comma), - |p| Ok(p.parse_expr()?) - )?; - let mut exprs = vec![first_expr]; - exprs.extend(remaining_exprs); - ex = ExprKind::Array(exprs); - } else { - // Vector with one element - self.expect(&token::CloseDelim(token::Bracket))?; - ex = ExprKind::Array(vec![first_expr]); - } - } - hi = self.prev_span; - } - _ => { - if self.eat_lt() { - let (qself, path) = self.parse_qpath(PathStyle::Expr)?; - hi = path.span; - return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs)); - } - if self.token.is_path_start() { - let path = self.parse_path(PathStyle::Expr)?; - - // `!`, as an operator, is prefix, so we know this isn't that. - if self.eat(&token::Not) { - // MACRO INVOCATION expression - let (delim, tts) = self.expect_delimited_token_tree()?; - hi = self.prev_span; - ex = ExprKind::Mac(Mac { - path, - tts, - delim, - span: lo.to(hi), - prior_type_ascription: self.last_type_ascription, - }); - } else if self.check(&token::OpenDelim(token::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) { - return expr; - } else { - hi = path.span; - ex = ExprKind::Path(None, path); - } - } else { - hi = path.span; - ex = ExprKind::Path(None, path); - } - - let expr = self.mk_expr(lo.to(hi), ex, attrs); - return self.maybe_recover_from_bad_qpath(expr, true); - } - if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { - return self.parse_closure_expr(attrs); - } - if self.eat_keyword(kw::If) { - return self.parse_if_expr(attrs); - } - if self.eat_keyword(kw::For) { - let lo = self.prev_span; - return self.parse_for_expr(None, lo, attrs); - } - if self.eat_keyword(kw::While) { - let lo = self.prev_span; - return self.parse_while_expr(None, lo, attrs); - } - if let Some(label) = self.eat_label() { - let lo = label.ident.span; - self.expect(&token::Colon)?; - if self.eat_keyword(kw::While) { - return self.parse_while_expr(Some(label), lo, attrs) - } - if self.eat_keyword(kw::For) { - return self.parse_for_expr(Some(label), lo, attrs) - } - if self.eat_keyword(kw::Loop) { - return self.parse_loop_expr(Some(label), lo, attrs) - } - if self.token == token::OpenDelim(token::Brace) { - return self.parse_block_expr(Some(label), - lo, - BlockCheckMode::Default, - attrs); - } - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - let mut err = self.fatal(msg); - err.span_label(self.token.span, msg); - return Err(err); - } - if self.eat_keyword(kw::Loop) { - let lo = self.prev_span; - return self.parse_loop_expr(None, lo, attrs); - } - if self.eat_keyword(kw::Continue) { - let label = self.eat_label(); - let ex = ExprKind::Continue(label); - let hi = self.prev_span; - return Ok(self.mk_expr(lo.to(hi), ex, attrs)); - } - if self.eat_keyword(kw::Match) { - let match_sp = self.prev_span; - return self.parse_match_expr(attrs).map_err(|mut err| { - err.span_label(match_sp, "while parsing this match expression"); - err - }); - } - if self.eat_keyword(kw::Unsafe) { - return self.parse_block_expr( - None, - lo, - BlockCheckMode::Unsafe(ast::UserProvided), - attrs); - } - if self.is_do_catch_block() { - let mut db = self.fatal("found removed `do catch` syntax"); - db.help("following RFC #2388, the new non-placeholder syntax is `try`"); - return Err(db); - } - if self.is_try_block() { - let lo = self.token.span; - assert!(self.eat_keyword(kw::Try)); - return self.parse_try_block(lo, attrs); - } - - // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly. - let is_span_rust_2018 = self.token.span.rust_2018(); - if is_span_rust_2018 && self.check_keyword(kw::Async) { - return if self.is_async_block() { // Check for `async {` and `async move {`. - self.parse_async_block(attrs) - } else { - self.parse_closure_expr(attrs) - }; - } - if self.eat_keyword(kw::Return) { - if self.token.can_begin_expr() { - let e = self.parse_expr()?; - hi = e.span; - ex = ExprKind::Ret(Some(e)); - } else { - ex = ExprKind::Ret(None); - } - } else if self.eat_keyword(kw::Break) { - let label = self.eat_label(); - let e = if self.token.can_begin_expr() - && !(self.token == token::OpenDelim(token::Brace) - && self.restrictions.contains( - Restrictions::NO_STRUCT_LITERAL)) { - Some(self.parse_expr()?) - } else { - None - }; - ex = ExprKind::Break(label, e); - hi = self.prev_span; - } else if self.eat_keyword(kw::Yield) { - if self.token.can_begin_expr() { - let e = self.parse_expr()?; - hi = e.span; - ex = ExprKind::Yield(Some(e)); - } else { - ex = ExprKind::Yield(None); - } - - let span = lo.to(hi); - self.sess.gated_spans.gate(sym::generators, span); - } else if self.eat_keyword(kw::Let) { - return self.parse_let_expr(attrs); - } else if is_span_rust_2018 && self.eat_keyword(kw::Await) { - let (await_hi, e_kind) = self.parse_incorrect_await_syntax(lo, self.prev_span)?; - hi = await_hi; - ex = e_kind; - } else { - if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { - // Don't complain about bare semicolons after unclosed braces - // recovery in order to keep the error count down. Fixing the - // delimiters will possibly also fix the bare semicolon found in - // expression context. For example, silence the following error: - // - // error: expected expression, found `;` - // --> file.rs:2:13 - // | - // 2 | foo(bar(; - // | ^ expected expression - self.bump(); - return Ok(self.mk_expr(self.token.span, ExprKind::Err, ThinVec::new())); - } - parse_lit!() - } - } - } - - let expr = self.mk_expr(lo.to(hi), ex, attrs); - self.maybe_recover_from_bad_qpath(expr, true) - } - - /// Matches `lit = true | false | token_lit`. - pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> { - let mut recovered = None; - if self.token == token::Dot { - // Attempt to recover `.4` as `0.4`. - recovered = self.look_ahead(1, |next_token| { - if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) - = next_token.kind { - if self.token.span.hi() == next_token.span.lo() { - let s = String::from("0.") + &*symbol.as_str(); - let kind = TokenKind::lit(token::Float, Symbol::intern(&s), suffix); - return Some(Token::new(kind, self.token.span.to(next_token.span))); - } - } - None - }); - if let Some(token) = &recovered { - self.bump(); - self.struct_span_err(token.span, "float literals must have an integer part") - .span_suggestion( - token.span, - "must have an integer part", - pprust::token_to_string(token), - Applicability::MachineApplicable, - ) - .emit(); - } - } - - let token = recovered.as_ref().unwrap_or(&self.token); - match Lit::from_token(token) { - Ok(lit) => { - self.bump(); - Ok(lit) - } - Err(LitError::NotLiteral) => { - let msg = format!("unexpected token: {}", self.this_token_descr()); - Err(self.span_fatal(token.span, &msg)) - } - Err(err) => { - let span = token.span; - let lit = match token.kind { - token::Literal(lit) => lit, - _ => unreachable!(), - }; - self.bump(); - self.error_literal_from_token(err, lit, span); - // Pack possible quotes and prefixes from the original literal into - // the error literal's symbol so they can be pretty-printed faithfully. - let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None); - let symbol = Symbol::intern(&suffixless_lit.to_string()); - let lit = token::Lit::new(token::Err, symbol, lit.suffix); - Lit::from_lit_token(lit, span).map_err(|_| unreachable!()) - } - } - } - - fn error_literal_from_token(&self, err: LitError, lit: token::Lit, span: Span) { - // Checks if `s` looks like i32 or u1234 etc. - fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool { - s.len() > 1 - && s.starts_with(first_chars) - && s[1..].chars().all(|c| c.is_ascii_digit()) - } - - let token::Lit { kind, suffix, .. } = lit; - match err { - // `NotLiteral` is not an error by itself, so we don't report - // it and give the parser opportunity to try something else. - LitError::NotLiteral => {} - // `LexerError` *is* an error, but it was already reported - // by lexer, so here we don't report it the second time. - LitError::LexerError => {} - LitError::InvalidSuffix => { - self.expect_no_suffix( - span, - &format!("{} {} literal", kind.article(), kind.descr()), - suffix, - ); - } - LitError::InvalidIntSuffix => { - let suf = suffix.expect("suffix error with no suffix").as_str(); - if looks_like_width_suffix(&['i', 'u'], &suf) { - // If it looks like a width, try to be helpful. - let msg = format!("invalid width `{}` for integer literal", &suf[1..]); - self.struct_span_err(span, &msg) - .help("valid widths are 8, 16, 32, 64 and 128") - .emit(); - } else { - let msg = format!("invalid suffix `{}` for integer literal", suf); - self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{}`", suf)) - .help("the suffix must be one of the integral types (`u32`, `isize`, etc)") - .emit(); - } - } - LitError::InvalidFloatSuffix => { - let suf = suffix.expect("suffix error with no suffix").as_str(); - if looks_like_width_suffix(&['f'], &suf) { - // If it looks like a width, try to be helpful. - let msg = format!("invalid width `{}` for float literal", &suf[1..]); - self.struct_span_err(span, &msg) - .help("valid widths are 32 and 64") - .emit(); - } else { - let msg = format!("invalid suffix `{}` for float literal", suf); - self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{}`", suf)) - .help("valid suffixes are `f32` and `f64`") - .emit(); - } - } - LitError::NonDecimalFloat(base) => { - let descr = match base { - 16 => "hexadecimal", - 8 => "octal", - 2 => "binary", - _ => unreachable!(), - }; - self.struct_span_err(span, &format!("{} float literal is not supported", descr)) - .span_label(span, "not supported") - .emit(); - } - LitError::IntTooLarge => { - self.struct_span_err(span, "integer literal is too large") - .emit(); - } - } - } - - pub(super) fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option) { - if let Some(suf) = suffix { - let mut err = if kind == "a tuple index" - && [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suf) - { - // #59553: warn instead of reject out of hand to allow the fix to percolate - // through the ecosystem when people fix their macros - let mut err = self.sess.span_diagnostic.struct_span_warn( - sp, - &format!("suffixes on {} are invalid", kind), - ); - err.note(&format!( - "`{}` is *temporarily* accepted on tuple index fields as it was \ - incorrectly accepted on stable for a few releases", - suf, - )); - err.help( - "on proc macros, you'll want to use `syn::Index::from` or \ - `proc_macro::Literal::*_unsuffixed` for code that will desugar \ - to tuple field access", - ); - err.note( - "for more context, see https://github.com/rust-lang/rust/issues/60210", - ); - err - } else { - self.struct_span_err(sp, &format!("suffixes on {} are invalid", kind)) - }; - err.span_label(sp, format!("invalid suffix `{}`", suf)); - err.emit(); - } - } - - /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). - pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P> { - maybe_whole_expr!(self); - - let minus_lo = self.token.span; - let minus_present = self.eat(&token::BinOp(token::Minus)); - let lo = self.token.span; - let literal = self.parse_lit()?; - let hi = self.prev_span; - let expr = self.mk_expr(lo.to(hi), ExprKind::Lit(literal), ThinVec::new()); - - if minus_present { - let minus_hi = self.prev_span; - let unary = self.mk_unary(UnOp::Neg, expr); - Ok(self.mk_expr(minus_lo.to(minus_hi), unary, ThinVec::new())) - } else { - Ok(expr) - } - } - - /// Parses a block or unsafe block. - pub(super) fn parse_block_expr( - &mut self, - opt_label: Option