diff --git a/.github/ISSUE_TEMPLATE/ice.md b/.github/ISSUE_TEMPLATE/ice.md index e669e4912f8c9..03bc4bab45137 100644 --- a/.github/ISSUE_TEMPLATE/ice.md +++ b/.github/ISSUE_TEMPLATE/ice.md @@ -14,7 +14,7 @@ http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ ### Code -``` +```Rust ``` diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 7c89d38caa4e6..1e5fe125c550d 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1849,6 +1849,21 @@ impl<'a, 'b> Pattern<'a> for &'b String { fn is_prefix_of(self, haystack: &'a str) -> bool { self[..].is_prefix_of(haystack) } + + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + self[..].strip_prefix_of(haystack) + } + + #[inline] + fn is_suffix_of(self, haystack: &'a str) -> bool { + self[..].is_suffix_of(haystack) + } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + self[..].strip_suffix_of(haystack) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 013ca182c13cd..dc7637cfdb934 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -9,7 +9,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use self::pattern::Pattern; -use self::pattern::{DoubleEndedSearcher, ReverseSearcher, SearchStep, Searcher}; +use self::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher}; use crate::char; use crate::fmt::{self, Write}; @@ -2642,7 +2642,7 @@ impl str { /// # Panics /// /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// beyond the last code point of the string slice. + /// past the end of the last code point of the string slice. /// /// # Examples /// @@ -2683,7 +2683,7 @@ impl str { /// # Panics /// /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// beyond the last code point of the string slice. + /// past the end of the last code point of the string slice. /// /// # Examples /// @@ -3986,26 +3986,15 @@ impl str { /// ``` /// #![feature(str_strip)] /// - /// assert_eq!("foobar".strip_prefix("foo"), Some("bar")); - /// assert_eq!("foobar".strip_prefix("bar"), None); + /// assert_eq!("foo:bar".strip_prefix("foo:"), Some("bar")); + /// assert_eq!("foo:bar".strip_prefix("bar"), None); /// assert_eq!("foofoo".strip_prefix("foo"), Some("foo")); /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] #[unstable(feature = "str_strip", reason = "newly added", issue = "67302")] pub fn strip_prefix<'a, P: Pattern<'a>>(&'a self, prefix: P) -> Option<&'a str> { - let mut matcher = prefix.into_searcher(self); - if let SearchStep::Match(start, len) = matcher.next() { - debug_assert_eq!( - start, 0, - "The first search step from Searcher \ - must include the first character" - ); - // SAFETY: `Searcher` is known to return valid indices. - unsafe { Some(self.get_unchecked(len..)) } - } else { - None - } + prefix.strip_prefix_of(self) } /// Returns a string slice with the suffix removed. @@ -4020,8 +4009,8 @@ impl str { /// /// ``` /// #![feature(str_strip)] - /// assert_eq!("barfoo".strip_suffix("foo"), Some("bar")); - /// assert_eq!("barfoo".strip_suffix("bar"), None); + /// assert_eq!("bar:foo".strip_suffix(":foo"), Some("bar")); + /// assert_eq!("bar:foo".strip_suffix("bar"), None); /// assert_eq!("foofoo".strip_suffix("foo"), Some("foo")); /// ``` #[must_use = "this returns the remaining substring as a new slice, \ @@ -4032,19 +4021,7 @@ impl str { P: Pattern<'a>,

>::Searcher: ReverseSearcher<'a>, { - let mut matcher = suffix.into_searcher(self); - if let SearchStep::Match(start, end) = matcher.next_back() { - debug_assert_eq!( - end, - self.len(), - "The first search step from ReverseSearcher \ - must include the last character" - ); - // SAFETY: `Searcher` is known to return valid indices. - unsafe { Some(self.get_unchecked(..start)) } - } else { - None - } + suffix.strip_suffix_of(self) } /// Returns a string slice with all suffixes that match a pattern diff --git a/src/libcore/str/pattern.rs b/src/libcore/str/pattern.rs index ffa418cba6c99..30fd55f7b7f64 100644 --- a/src/libcore/str/pattern.rs +++ b/src/libcore/str/pattern.rs @@ -47,6 +47,22 @@ pub trait Pattern<'a>: Sized { matches!(self.into_searcher(haystack).next(), SearchStep::Match(0, _)) } + /// Removes the pattern from the front of haystack, if it matches. + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + if let SearchStep::Match(start, len) = self.into_searcher(haystack).next() { + debug_assert_eq!( + start, 0, + "The first search step from Searcher \ + must include the first character" + ); + // SAFETY: `Searcher` is known to return valid indices. + unsafe { Some(haystack.get_unchecked(len..)) } + } else { + None + } + } + /// Checks whether the pattern matches at the back of the haystack #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool @@ -55,6 +71,26 @@ pub trait Pattern<'a>: Sized { { matches!(self.into_searcher(haystack).next_back(), SearchStep::Match(_, j) if haystack.len() == j) } + + /// Removes the pattern from the back of haystack, if it matches. + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + where + Self::Searcher: ReverseSearcher<'a>, + { + if let SearchStep::Match(start, end) = self.into_searcher(haystack).next_back() { + debug_assert_eq!( + end, + haystack.len(), + "The first search step from ReverseSearcher \ + must include the last character" + ); + // SAFETY: `Searcher` is known to return valid indices. + unsafe { Some(haystack.get_unchecked(..start)) } + } else { + None + } + } } // Searcher @@ -448,6 +484,11 @@ impl<'a> Pattern<'a> for char { self.encode_utf8(&mut [0u8; 4]).is_prefix_of(haystack) } + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + self.encode_utf8(&mut [0u8; 4]).strip_prefix_of(haystack) + } + #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool where @@ -455,6 +496,14 @@ impl<'a> Pattern<'a> for char { { self.encode_utf8(&mut [0u8; 4]).is_suffix_of(haystack) } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + where + Self::Searcher: ReverseSearcher<'a>, + { + self.encode_utf8(&mut [0u8; 4]).strip_suffix_of(haystack) + } } ///////////////////////////////////////////////////////////////////////////// @@ -569,6 +618,11 @@ macro_rules! pattern_methods { ($pmap)(self).is_prefix_of(haystack) } + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + ($pmap)(self).strip_prefix_of(haystack) + } + #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool where @@ -576,6 +630,14 @@ macro_rules! pattern_methods { { ($pmap)(self).is_suffix_of(haystack) } + + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> + where + $t: ReverseSearcher<'a>, + { + ($pmap)(self).strip_suffix_of(haystack) + } }; } @@ -715,11 +777,34 @@ impl<'a, 'b> Pattern<'a> for &'b str { haystack.as_bytes().starts_with(self.as_bytes()) } + /// Removes the pattern from the front of haystack, if it matches. + #[inline] + fn strip_prefix_of(self, haystack: &'a str) -> Option<&'a str> { + if self.is_prefix_of(haystack) { + // SAFETY: prefix was just verified to exist. + unsafe { Some(haystack.get_unchecked(self.as_bytes().len()..)) } + } else { + None + } + } + /// Checks whether the pattern matches at the back of the haystack #[inline] fn is_suffix_of(self, haystack: &'a str) -> bool { haystack.as_bytes().ends_with(self.as_bytes()) } + + /// Removes the pattern from the back of haystack, if it matches. + #[inline] + fn strip_suffix_of(self, haystack: &'a str) -> Option<&'a str> { + if self.is_suffix_of(haystack) { + let i = haystack.len() - self.as_bytes().len(); + // SAFETY: suffix was just verified to exist. + unsafe { Some(haystack.get_unchecked(..i)) } + } else { + None + } + } } ///////////////////////////////////////////////////////////////////////////// diff --git a/src/librustc_error_codes/error_codes/E0730.md b/src/librustc_error_codes/error_codes/E0730.md index bf1f72be32589..c2a71ca5669a1 100644 --- a/src/librustc_error_codes/error_codes/E0730.md +++ b/src/librustc_error_codes/error_codes/E0730.md @@ -7,8 +7,8 @@ Example of erroneous code: fn is_123(x: [u32; N]) -> bool { match x { - [1, 2, 3] => true, // error: cannot pattern-match on an - // array without a fixed length + [1, 2, ..] => true, // error: cannot pattern-match on an + // array without a fixed length _ => false } } diff --git a/src/librustc_lexer/src/lib.rs b/src/librustc_lexer/src/lib.rs index d3ac58a49c8d5..fcb7475cc2e89 100644 --- a/src/librustc_lexer/src/lib.rs +++ b/src/librustc_lexer/src/lib.rs @@ -17,9 +17,13 @@ mod cursor; pub mod unescape; +#[cfg(test)] +mod tests; + use self::LiteralKind::*; use self::TokenKind::*; use crate::cursor::{Cursor, EOF_CHAR}; +use std::convert::TryInto; /// Parsed token. /// It doesn't contain information about data that has been parsed, @@ -132,9 +136,80 @@ pub enum LiteralKind { /// "b"abc"", "b"abc" ByteStr { terminated: bool }, /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a" - RawStr { n_hashes: usize, started: bool, terminated: bool }, + RawStr(UnvalidatedRawStr), /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" - RawByteStr { n_hashes: usize, started: bool, terminated: bool }, + RawByteStr(UnvalidatedRawStr), +} + +/// Represents something that looks like a raw string, but may have some +/// problems. Use `.validate()` to convert it into something +/// usable. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct UnvalidatedRawStr { + /// The prefix (`r###"`) is valid + valid_start: bool, + /// The number of leading `#` + n_start_hashes: usize, + /// The number of trailing `#`. `n_end_hashes` <= `n_start_hashes` + n_end_hashes: usize, + /// The offset starting at `r` or `br` where the user may have intended to end the string. + /// Currently, it is the longest sequence of pattern `"#+"`. + possible_terminator_offset: Option, +} + +/// Error produced validating a raw string. Represents cases like: +/// - `r##~"abcde"##`: `LexRawStrError::InvalidStarter` +/// - `r###"abcde"##`: `LexRawStrError::NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` +/// - Too many `#`s (>65536): `TooManyDelimiters` +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum LexRawStrError { + /// Non `#` characters exist between `r` and `"` eg. `r#~"..` + InvalidStarter, + /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they + /// may have intended to terminate it. + NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option }, + /// More than 65536 `#`s exist. + TooManyDelimiters, +} + +/// Raw String that contains a valid prefix (`#+"`) and postfix (`"#+`) where +/// there are a matching number of `#` characters in both. Note that this will +/// not consume extra trailing `#` characters: `r###"abcde"####` is lexed as a +/// `ValidatedRawString { n_hashes: 3 }` followed by a `#` token. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +pub struct ValidatedRawStr { + n_hashes: u16, +} + +impl ValidatedRawStr { + pub fn num_hashes(&self) -> u16 { + self.n_hashes + } +} + +impl UnvalidatedRawStr { + pub fn validate(self) -> Result { + if !self.valid_start { + return Err(LexRawStrError::InvalidStarter); + } + + // Only up to 65535 `#`s are allowed in raw strings + let n_start_safe: u16 = + self.n_start_hashes.try_into().map_err(|_| LexRawStrError::TooManyDelimiters)?; + + if self.n_start_hashes > self.n_end_hashes { + Err(LexRawStrError::NoTerminator { + expected: self.n_start_hashes, + found: self.n_end_hashes, + possible_terminator_offset: self.possible_terminator_offset, + }) + } else { + // Since the lexer should never produce a literal with n_end > n_start, if n_start <= n_end, + // they must be equal. + debug_assert_eq!(self.n_start_hashes, self.n_end_hashes); + Ok(ValidatedRawStr { n_hashes: n_start_safe }) + } + } } /// Base of numeric literal encoding according to its prefix. @@ -209,7 +284,7 @@ pub fn is_whitespace(c: char) -> bool { // Dedicated whitespace characters from Unicode | '\u{2028}' // LINE SEPARATOR | '\u{2029}' // PARAGRAPH SEPARATOR - => true, + => true, _ => false, } } @@ -258,12 +333,12 @@ impl Cursor<'_> { 'r' => match (self.first(), self.second()) { ('#', c1) if is_id_start(c1) => self.raw_ident(), ('#', _) | ('"', _) => { - let (n_hashes, started, terminated) = self.raw_double_quoted_string(); + let raw_str_i = self.raw_double_quoted_string(1); let suffix_start = self.len_consumed(); - if terminated { + if raw_str_i.n_end_hashes == raw_str_i.n_start_hashes { self.eat_literal_suffix(); } - let kind = RawStr { n_hashes, started, terminated }; + let kind = RawStr(raw_str_i); Literal { kind, suffix_start } } _ => self.ident(), @@ -293,12 +368,14 @@ impl Cursor<'_> { } ('r', '"') | ('r', '#') => { self.bump(); - let (n_hashes, started, terminated) = self.raw_double_quoted_string(); + let raw_str_i = self.raw_double_quoted_string(2); let suffix_start = self.len_consumed(); + let terminated = raw_str_i.n_start_hashes == raw_str_i.n_end_hashes; if terminated { self.eat_literal_suffix(); } - let kind = RawByteStr { n_hashes, started, terminated }; + + let kind = RawByteStr(raw_str_i); Literal { kind, suffix_start } } _ => self.ident(), @@ -594,29 +671,41 @@ impl Cursor<'_> { false } - /// Eats the double-quoted string and returns a tuple of - /// (amount of the '#' symbols, raw string started, raw string terminated) - fn raw_double_quoted_string(&mut self) -> (usize, bool, bool) { + /// Eats the double-quoted string and returns an `UnvalidatedRawStr`. + fn raw_double_quoted_string(&mut self, prefix_len: usize) -> UnvalidatedRawStr { debug_assert!(self.prev() == 'r'); - let mut started: bool = false; - let mut finished: bool = false; + let mut valid_start: bool = false; + let start_pos = self.len_consumed(); + let (mut possible_terminator_offset, mut max_hashes) = (None, 0); // Count opening '#' symbols. - let n_hashes = self.eat_while(|c| c == '#'); + let n_start_hashes = self.eat_while(|c| c == '#'); // Check that string is started. match self.bump() { - Some('"') => started = true, - _ => return (n_hashes, started, finished), + Some('"') => valid_start = true, + _ => { + return UnvalidatedRawStr { + valid_start, + n_start_hashes, + n_end_hashes: 0, + possible_terminator_offset, + }; + } } // Skip the string contents and on each '#' character met, check if this is // a raw string termination. - while !finished { + loop { self.eat_while(|c| c != '"'); if self.is_eof() { - return (n_hashes, started, finished); + return UnvalidatedRawStr { + valid_start, + n_start_hashes, + n_end_hashes: max_hashes, + possible_terminator_offset, + }; } // Eat closing double quote. @@ -624,7 +713,7 @@ impl Cursor<'_> { // Check that amount of closing '#' symbols // is equal to the amount of opening ones. - let mut hashes_left = n_hashes; + let mut hashes_left = n_start_hashes; let is_closing_hash = |c| { if c == '#' && hashes_left != 0 { hashes_left -= 1; @@ -633,10 +722,23 @@ impl Cursor<'_> { false } }; - finished = self.eat_while(is_closing_hash) == n_hashes; + let n_end_hashes = self.eat_while(is_closing_hash); + + if n_end_hashes == n_start_hashes { + return UnvalidatedRawStr { + valid_start, + n_start_hashes, + n_end_hashes, + possible_terminator_offset: None, + }; + } else if n_end_hashes > max_hashes { + // Keep track of possible terminators to give a hint about where there might be + // a missing terminator + possible_terminator_offset = + Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len); + max_hashes = n_end_hashes; + } } - - (n_hashes, started, finished) } fn eat_decimal_digits(&mut self) -> bool { diff --git a/src/librustc_lexer/src/tests.rs b/src/librustc_lexer/src/tests.rs new file mode 100644 index 0000000000000..ba5897c5d4218 --- /dev/null +++ b/src/librustc_lexer/src/tests.rs @@ -0,0 +1,119 @@ +#[cfg(test)] +mod tests { + use crate::*; + + fn check_raw_str( + s: &str, + expected: UnvalidatedRawStr, + validated: Result, + ) { + let mut cursor = Cursor::new(s); + let tok = cursor.raw_double_quoted_string(0); + assert_eq!(tok, expected); + assert_eq!(tok.validate(), validated); + } + + #[test] + fn test_naked_raw_str() { + check_raw_str( + r#""abc""#, + UnvalidatedRawStr { + n_start_hashes: 0, + n_end_hashes: 0, + valid_start: true, + possible_terminator_offset: None, + }, + Ok(ValidatedRawStr { n_hashes: 0 }), + ); + } + + #[test] + fn test_raw_no_start() { + check_raw_str( + r##""abc"#"##, + UnvalidatedRawStr { + n_start_hashes: 0, + n_end_hashes: 0, + valid_start: true, + possible_terminator_offset: None, + }, + Ok(ValidatedRawStr { n_hashes: 0 }), + ); + } + + #[test] + fn test_too_many_terminators() { + // this error is handled in the parser later + check_raw_str( + r###"#"abc"##"###, + UnvalidatedRawStr { + n_start_hashes: 1, + n_end_hashes: 1, + valid_start: true, + possible_terminator_offset: None, + }, + Ok(ValidatedRawStr { n_hashes: 1 }), + ); + } + + #[test] + fn test_unterminated() { + check_raw_str( + r#"#"abc"#, + UnvalidatedRawStr { + n_start_hashes: 1, + n_end_hashes: 0, + valid_start: true, + possible_terminator_offset: None, + }, + Err(LexRawStrError::NoTerminator { + expected: 1, + found: 0, + possible_terminator_offset: None, + }), + ); + check_raw_str( + r###"##"abc"#"###, + UnvalidatedRawStr { + n_start_hashes: 2, + n_end_hashes: 1, + valid_start: true, + possible_terminator_offset: Some(7), + }, + Err(LexRawStrError::NoTerminator { + expected: 2, + found: 1, + possible_terminator_offset: Some(7), + }), + ); + // We're looking for "# not just any # + check_raw_str( + r###"##"abc#"###, + UnvalidatedRawStr { + n_start_hashes: 2, + n_end_hashes: 0, + valid_start: true, + possible_terminator_offset: None, + }, + Err(LexRawStrError::NoTerminator { + expected: 2, + found: 0, + possible_terminator_offset: None, + }), + ) + } + + #[test] + fn test_invalid_start() { + check_raw_str( + r##"#~"abc"#"##, + UnvalidatedRawStr { + n_start_hashes: 1, + n_end_hashes: 0, + valid_start: false, + possible_terminator_offset: None, + }, + Err(LexRawStrError::InvalidStarter), + ); + } +} diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs index ac58cbb9e8dae..96321ef2145e5 100644 --- a/src/librustc_parse/lexer/mod.rs +++ b/src/librustc_parse/lexer/mod.rs @@ -1,20 +1,20 @@ use rustc_ast::token::{self, Token, TokenKind}; use rustc_ast::util::comments; use rustc_data_structures::sync::Lrc; -use rustc_errors::{error_code, DiagnosticBuilder, FatalError}; -use rustc_lexer::unescape; +use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError}; use rustc_lexer::Base; +use rustc_lexer::{unescape, LexRawStrError, UnvalidatedRawStr, ValidatedRawStr}; use rustc_session::parse::ParseSess; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Pos, Span}; use log::debug; use std::char; -use std::convert::TryInto; mod tokentrees; mod unescape_error_reporting; mod unicode_chars; + use unescape_error_reporting::{emit_unescape_error, push_escaped_char}; #[derive(Clone, Debug)] @@ -93,9 +93,6 @@ impl<'a> StringReader<'a> { } /// Returns the next token, including trivia like whitespace or comments. - /// - /// `Err(())` means that some errors were encountered, which can be - /// retrieved using `buffer_fatal_errors`. pub fn next_token(&mut self) -> Token { let start_src_index = self.src_index(self.pos); let text: &str = &self.src[start_src_index..self.end_src_index]; @@ -376,30 +373,22 @@ impl<'a> StringReader<'a> { let id = self.symbol_from_to(content_start, content_end); (token::ByteStr, id) } - rustc_lexer::LiteralKind::RawStr { n_hashes, started, terminated } => { - if !started { - self.report_non_started_raw_string(start); - } - if !terminated { - self.report_unterminated_raw_string(start, n_hashes) - } - let n_hashes: u16 = self.restrict_n_hashes(start, n_hashes); + rustc_lexer::LiteralKind::RawStr(unvalidated_raw_str) => { + let valid_raw_str = self.validate_and_report_errors(start, unvalidated_raw_str); + let n_hashes = valid_raw_str.num_hashes(); let n = u32::from(n_hashes); + let content_start = start + BytePos(2 + n); let content_end = suffix_start - BytePos(1 + n); self.validate_raw_str_escape(content_start, content_end); let id = self.symbol_from_to(content_start, content_end); (token::StrRaw(n_hashes), id) } - rustc_lexer::LiteralKind::RawByteStr { n_hashes, started, terminated } => { - if !started { - self.report_non_started_raw_string(start); - } - if !terminated { - self.report_unterminated_raw_string(start, n_hashes) - } - let n_hashes: u16 = self.restrict_n_hashes(start, n_hashes); + rustc_lexer::LiteralKind::RawByteStr(unvalidated_raw_str) => { + let validated_raw_str = self.validate_and_report_errors(start, unvalidated_raw_str); + let n_hashes = validated_raw_str.num_hashes(); let n = u32::from(n_hashes); + let content_start = start + BytePos(3 + n); let content_end = suffix_start - BytePos(1 + n); self.validate_raw_byte_str_escape(content_start, content_end); @@ -485,6 +474,26 @@ impl<'a> StringReader<'a> { } } + fn validate_and_report_errors( + &self, + start: BytePos, + unvalidated_raw_str: UnvalidatedRawStr, + ) -> ValidatedRawStr { + match unvalidated_raw_str.validate() { + Err(LexRawStrError::InvalidStarter) => self.report_non_started_raw_string(start), + Err(LexRawStrError::NoTerminator { expected, found, possible_terminator_offset }) => { + self.report_unterminated_raw_string( + start, + expected, + possible_terminator_offset, + found, + ) + } + Err(LexRawStrError::TooManyDelimiters) => self.report_too_many_hashes(start), + Ok(valid) => valid, + } + } + fn report_non_started_raw_string(&self, start: BytePos) -> ! { let bad_char = self.str_from(start).chars().last().unwrap(); self.struct_fatal_span_char( @@ -498,38 +507,51 @@ impl<'a> StringReader<'a> { FatalError.raise() } - fn report_unterminated_raw_string(&self, start: BytePos, n_hashes: usize) -> ! { + fn report_unterminated_raw_string( + &self, + start: BytePos, + n_hashes: usize, + possible_offset: Option, + found_terminators: usize, + ) -> ! { let mut err = self.sess.span_diagnostic.struct_span_fatal_with_code( self.mk_sp(start, start), "unterminated raw string", error_code!(E0748), ); + err.span_label(self.mk_sp(start, start), "unterminated raw string"); if n_hashes > 0 { err.note(&format!( "this raw string should be terminated with `\"{}`", - "#".repeat(n_hashes as usize) + "#".repeat(n_hashes) )); } + if let Some(possible_offset) = possible_offset { + let lo = start + BytePos(possible_offset as u32); + let hi = lo + BytePos(found_terminators as u32); + let span = self.mk_sp(lo, hi); + err.span_suggestion( + span, + "consider terminating the string here", + "#".repeat(n_hashes), + Applicability::MaybeIncorrect, + ); + } + err.emit(); FatalError.raise() } - fn restrict_n_hashes(&self, start: BytePos, n_hashes: usize) -> u16 { - match n_hashes.try_into() { - Ok(n_hashes) => n_hashes, - Err(_) => { - self.fatal_span_( - start, - self.pos, - "too many `#` symbols: raw strings may be \ - delimited by up to 65535 `#` symbols", - ) - .raise(); - } - } + fn report_too_many_hashes(&self, start: BytePos) -> ! { + self.fatal_span_( + start, + self.pos, + "too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols", + ) + .raise(); } fn validate_char_escape(&self, content_start: BytePos, content_end: BytePos) { diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 13fb85db84779..8e2a9513d6b82 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -4,6 +4,7 @@ #![feature(crate_visibility_modifier)] #![feature(bindings_after_at)] #![feature(try_blocks)] +#![feature(or_patterns)] use rustc_ast::ast; use rustc_ast::token::{self, Nonterminal}; diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs index c4546dedfcdd4..12b9b68268248 100644 --- a/src/librustc_parse/parser/diagnostics.rs +++ b/src/librustc_parse/parser/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::ast::{ }; use rustc_ast::ast::{AttrVec, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind}; use rustc_ast::ptr::P; -use rustc_ast::token::{self, TokenKind}; +use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -255,6 +255,10 @@ impl<'a> Parser<'a> { } } + if self.check_too_many_raw_str_terminators(&mut err) { + return Err(err); + } + let sm = self.sess.source_map(); if self.prev_token.span == DUMMY_SP { // Account for macro context where the previous span might not be @@ -282,6 +286,29 @@ impl<'a> Parser<'a> { Err(err) } + fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool { + match (&self.prev_token.kind, &self.token.kind) { + ( + TokenKind::Literal(Lit { + kind: LitKind::StrRaw(n_hashes) | LitKind::ByteStrRaw(n_hashes), + .. + }), + TokenKind::Pound, + ) => { + err.set_primary_message("too many `#` when terminating raw string"); + err.span_suggestion( + self.token.span, + "remove the extra `#`", + String::new(), + Applicability::MachineApplicable, + ); + err.note(&format!("the raw string started with {} `#`s", n_hashes)); + true + } + _ => false, + } + } + pub fn maybe_annotate_with_ascription( &mut self, err: &mut DiagnosticBuilder<'_>, @@ -491,7 +518,7 @@ impl<'a> Parser<'a> { .unwrap_or_else(|_| pprust::expr_to_string(&e)) }; err.span_suggestion_verbose( - inner_op.span.shrink_to_hi(), + inner_op.span.shrink_to_hi(), "split the comparison into two", format!(" && {}", expr_to_str(&r1)), Applicability::MaybeIncorrect, @@ -1086,7 +1113,7 @@ impl<'a> Parser<'a> { self.look_ahead(2, |t| t.is_ident()) || self.look_ahead(1, |t| t == &token::ModSep) && (self.look_ahead(2, |t| t.is_ident()) || // `foo:bar::baz` - self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::` + self.look_ahead(2, |t| t == &token::Lt)) // `foo:bar::` } pub(super) fn recover_seq_parse_error( diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 1b40d2bea06c3..2b90527a82537 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1536,20 +1536,18 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { let is_syntactic_ambiguity = !has_sub && bm == BindingMode::ByValue(Mutability::Not); match res { - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::ConstParam, _) - if is_syntactic_ambiguity => - { + Res::SelfCtor(_) // See #70549. + | Res::Def( + DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::ConstParam, + _, + ) if is_syntactic_ambiguity => { // Disambiguate in favor of a unit struct/variant or constant pattern. if let Some(binding) = binding { self.r.record_use(ident, ValueNS, binding, false); } Some(res) } - Res::Def(DefKind::Ctor(..), _) - | Res::Def(DefKind::Const, _) - | Res::Def(DefKind::Static, _) => { + Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static, _) => { // This is unambiguously a fresh binding, either syntactically // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves // to something unusable as a pattern (e.g., constructor function), @@ -1572,7 +1570,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { _ => span_bug!( ident.span, "unexpected resolution for an identifier in pattern: {:?}", - res + res, ), } } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 57454b62cdd0f..75c5dfb8d18b0 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -10,6 +10,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] #![feature(nll)] +#![feature(or_patterns)] #![recursion_limit = "256"] pub use rustc_hir::def::{Namespace, PerNS}; diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs index fc91142dd7da3..b3cace8298a92 100644 --- a/src/librustc_typeck/check/pat.rs +++ b/src/librustc_typeck/check/pat.rs @@ -1355,16 +1355,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let err = self.tcx.types.err; let expected = self.structurally_resolved_type(span, expected); - let (inner_ty, slice_ty, expected) = match expected.kind { + let (element_ty, slice_ty, inferred) = match expected.kind { // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. - ty::Array(inner_ty, len) => { + ty::Array(element_ty, len) => { let min = before.len() as u64 + after.len() as u64; - let slice_ty = self - .check_array_pat_len(span, slice, len, min) - .map_or(err, |len| self.tcx.mk_array(inner_ty, len)); - (inner_ty, slice_ty, expected) + let (slice_ty, expected) = + self.check_array_pat_len(span, element_ty, expected, slice, len, min); + (element_ty, slice_ty, expected) } - ty::Slice(inner_ty) => (inner_ty, expected, expected), + ty::Slice(element_ty) => (element_ty, expected, expected), // The expected type must be an array or slice, but was neither, so error. _ => { if !expected.references_error() { @@ -1376,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type check all the patterns before `slice`. for elt in before { - self.check_pat(&elt, inner_ty, def_bm, ti); + self.check_pat(&elt, element_ty, def_bm, ti); } // Type check the `slice`, if present, against its expected type. if let Some(slice) = slice { @@ -1384,22 +1383,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Type check the elements after `slice`, if present. for elt in after { - self.check_pat(&elt, inner_ty, def_bm, ti); + self.check_pat(&elt, element_ty, def_bm, ti); } - expected + inferred } /// Type check the length of an array pattern. /// - /// Return the length of the variable length pattern, - /// if it exists and there are no errors. + /// Returns both the type of the variable length pattern + /// (or `tcx.err` in case there is none), + /// and the potentially inferred array type. fn check_array_pat_len( &self, span: Span, + element_ty: Ty<'tcx>, + arr_ty: Ty<'tcx>, slice: Option<&'tcx Pat<'tcx>>, len: &ty::Const<'tcx>, min_len: u64, - ) -> Option { + ) -> (Ty<'tcx>, Ty<'tcx>) { if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { // Now we know the length... if slice.is_none() { @@ -1409,21 +1411,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if min_len != len { self.error_scrutinee_inconsistent_length(span, min_len, len); } - } else if let r @ Some(_) = len.checked_sub(min_len) { + } else if let Some(pat_len) = len.checked_sub(min_len) { // The variable-length pattern was there, // so it has an array type with the remaining elements left as its size... - return r; + return (self.tcx.mk_array(element_ty, pat_len), arr_ty); } else { // ...however, in this case, there were no remaining elements. // That is, the slice pattern requires more than the array type offers. self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); } + } else if slice.is_none() { + // We have a pattern with a fixed length, + // which we can use to infer the length of the array. + let updated_arr_ty = self.tcx.mk_array(element_ty, min_len); + self.demand_eqtype(span, updated_arr_ty, arr_ty); + return (self.tcx.types.err, updated_arr_ty); } else { - // No idea what the length is, which happens if we have e.g., - // `let [a, b] = arr` where `arr: [T; N]` where `const N: usize`. + // We have a variable-length pattern and don't know the array length. + // This happens if we have e.g., + // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. self.error_scrutinee_unfixed_length(span); } - None + (self.tcx.types.err, arr_ty) } fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { diff --git a/src/libstd/sys/wasi/alloc.rs b/src/libstd/sys/wasi/alloc.rs index e9760d050e105..bc61416278401 100644 --- a/src/libstd/sys/wasi/alloc.rs +++ b/src/libstd/sys/wasi/alloc.rs @@ -10,7 +10,7 @@ unsafe impl GlobalAlloc for System { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { libc::malloc(layout.size()) as *mut u8 } else { - libc::aligned_alloc(layout.size(), layout.align()) as *mut u8 + libc::aligned_alloc(layout.align(), layout.size()) as *mut u8 } } diff --git a/src/test/ui/array-slice-vec/infer_array_len.rs b/src/test/ui/array-slice-vec/infer_array_len.rs new file mode 100644 index 0000000000000..22fe7cb883888 --- /dev/null +++ b/src/test/ui/array-slice-vec/infer_array_len.rs @@ -0,0 +1,21 @@ +// see issue #70529 +struct A; + +impl From for [u8; 2] { + fn from(a: A) -> Self { + [0; 2] + } +} + +impl From for [u8; 3] { + fn from(a: A) -> Self { + [0; 3] + } +} + + +fn main() { + let a = A; + let [_, _] = a.into(); + //~^ ERROR type annotations needed +} diff --git a/src/test/ui/array-slice-vec/infer_array_len.stderr b/src/test/ui/array-slice-vec/infer_array_len.stderr new file mode 100644 index 0000000000000..6eed4ce4f0c01 --- /dev/null +++ b/src/test/ui/array-slice-vec/infer_array_len.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/infer_array_len.rs:19:9 + | +LL | let [_, _] = a.into(); + | ^^^^^^ consider giving this pattern a type + | + = note: type must be known at this point + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.rs b/src/test/ui/array-slice-vec/match_arr_unknown_len.rs new file mode 100644 index 0000000000000..7f3da75ddcbe8 --- /dev/null +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.rs @@ -0,0 +1,11 @@ +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn is_123(x: [u32; N]) -> bool { + match x { + [1, 2] => true, //~ ERROR mismatched types + _ => false + } +} + +fn main() {} diff --git a/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr new file mode 100644 index 0000000000000..9edb139028b72 --- /dev/null +++ b/src/test/ui/array-slice-vec/match_arr_unknown_len.stderr @@ -0,0 +1,20 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/match_arr_unknown_len.rs:1:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/match_arr_unknown_len.rs:6:9 + | +LL | [1, 2] => true, + | ^^^^^^ expected `2usize`, found `N` + | + = note: expected array `[u32; 2]` + found array `[u32; _]` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/const-generics/infer_arg_from_pat.rs b/src/test/ui/const-generics/infer_arg_from_pat.rs new file mode 100644 index 0000000000000..a4e3d3dee4a82 --- /dev/null +++ b/src/test/ui/const-generics/infer_arg_from_pat.rs @@ -0,0 +1,27 @@ +// run-pass +// +// see issue #70529 +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +struct A { + arr: [u8; N], +} + +impl A { + fn new() -> Self { + A { + arr: [0; N], + } + } + + fn value(&self) -> usize { + N + } +} + +fn main() { + let a = A::new(); + let [_, _] = a.arr; + assert_eq!(a.value(), 2); +} diff --git a/src/test/ui/const-generics/infer_arg_from_pat.stderr b/src/test/ui/const-generics/infer_arg_from_pat.stderr new file mode 100644 index 0000000000000..ad6bf3e235aeb --- /dev/null +++ b/src/test/ui/const-generics/infer_arg_from_pat.stderr @@ -0,0 +1,8 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/infer_arg_from_pat.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.rs b/src/test/ui/const-generics/infer_arr_len_from_pat.rs new file mode 100644 index 0000000000000..70633bbb141d4 --- /dev/null +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.rs @@ -0,0 +1,13 @@ +// check-pass +// +// see issue #70529 +#![feature(const_generics)] +//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash + +fn as_chunks() -> [u8; N] { + loop {} +} + +fn main() { + let [_, _] = as_chunks(); +} diff --git a/src/test/ui/const-generics/infer_arr_len_from_pat.stderr b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr new file mode 100644 index 0000000000000..6f5b601e14ca1 --- /dev/null +++ b/src/test/ui/const-generics/infer_arr_len_from_pat.stderr @@ -0,0 +1,8 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/infer_arr_len_from_pat.rs:4:12 + | +LL | #![feature(const_generics)] + | ^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + diff --git a/src/test/ui/error-codes/E0730.rs b/src/test/ui/error-codes/E0730.rs index e5048d6e6e320..66a6e1c817a37 100644 --- a/src/test/ui/error-codes/E0730.rs +++ b/src/test/ui/error-codes/E0730.rs @@ -3,7 +3,7 @@ fn is_123(x: [u32; N]) -> bool { match x { - [1, 2, 3] => true, //~ ERROR cannot pattern-match on an array without a fixed length + [1, 2, ..] => true, //~ ERROR cannot pattern-match on an array without a fixed length _ => false } } diff --git a/src/test/ui/error-codes/E0730.stderr b/src/test/ui/error-codes/E0730.stderr index 9309ee99064c9..fb53ae31c0b42 100644 --- a/src/test/ui/error-codes/E0730.stderr +++ b/src/test/ui/error-codes/E0730.stderr @@ -9,8 +9,8 @@ LL | #![feature(const_generics)] error[E0730]: cannot pattern-match on an array without a fixed length --> $DIR/E0730.rs:6:9 | -LL | [1, 2, 3] => true, - | ^^^^^^^^^ +LL | [1, 2, ..] => true, + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.rs b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.rs new file mode 100644 index 0000000000000..aeccd0d9f76ca --- /dev/null +++ b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.rs @@ -0,0 +1,18 @@ +struct S {} + +impl S { + fn foo(&mur Self) {} + //~^ ERROR expected identifier, found keyword `Self` + //~| ERROR expected one of `:`, `@` + //~| ERROR the `Self` constructor can only be used with + fn bar(&'static mur Self) {} + //~^ ERROR unexpected lifetime + //~| ERROR expected identifier, found keyword `Self` + //~| ERROR expected one of `:`, `@` + //~| ERROR the `Self` constructor can only be used with + + fn baz(&mur Self @ _) {} + //~^ ERROR expected one of `:`, `@` +} + +fn main() {} diff --git a/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.stderr b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.stderr new file mode 100644 index 0000000000000..421f145403623 --- /dev/null +++ b/src/test/ui/parser/issue-70549-resolve-after-recovered-self-ctor.stderr @@ -0,0 +1,56 @@ +error: expected identifier, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 + | +LL | fn foo(&mur Self) {} + | ^^^^ expected identifier, found keyword + +error: expected one of `:`, `@`, or `|`, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 + | +LL | fn foo(&mur Self) {} + | -----^^^^ + | | | + | | expected one of `:`, `@`, or `|` + | help: declare the type after the parameter binding: `: ` + +error: unexpected lifetime `'static` in pattern + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:13 + | +LL | fn bar(&'static mur Self) {} + | ^^^^^^^ help: remove the lifetime + +error: expected identifier, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:25 + | +LL | fn bar(&'static mur Self) {} + | ^^^^ expected identifier, found keyword + +error: expected one of `:`, `@`, or `|`, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:25 + | +LL | fn bar(&'static mur Self) {} + | -------------^^^^ + | | | + | | expected one of `:`, `@`, or `|` + | help: declare the type after the parameter binding: `: ` + +error: expected one of `:`, `@`, or `|`, found keyword `Self` + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:14:17 + | +LL | fn baz(&mur Self @ _) {} + | ^^^^ expected one of `:`, `@`, or `|` + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:4:17 + | +LL | fn foo(&mur Self) {} + | ^^^^ help: use curly brackets: `Self { /* fields */ }` + +error: the `Self` constructor can only be used with tuple or unit structs + --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:25 + | +LL | fn bar(&'static mur Self) {} + | ^^^^ help: use curly brackets: `Self { /* fields */ }` + +error: aborting due to 8 previous errors + diff --git a/src/test/ui/parser/raw-str-unbalanced.rs b/src/test/ui/parser/raw-str-unbalanced.rs deleted file mode 100644 index 5a1d1be11b633..0000000000000 --- a/src/test/ui/parser/raw-str-unbalanced.rs +++ /dev/null @@ -1,4 +0,0 @@ -static s: &'static str = - r#" - "## //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `#` -; diff --git a/src/test/ui/parser/raw-str-unbalanced.stderr b/src/test/ui/parser/raw-str-unbalanced.stderr deleted file mode 100644 index ddb75722bef9f..0000000000000 --- a/src/test/ui/parser/raw-str-unbalanced.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `.`, `;`, `?`, or an operator, found `#` - --> $DIR/raw-str-unbalanced.rs:3:9 - | -LL | "## - | ^ expected one of `.`, `;`, `?`, or an operator - -error: aborting due to previous error - diff --git a/src/test/ui/parser/raw-byte-string-eof.rs b/src/test/ui/parser/raw/raw-byte-string-eof.rs similarity index 100% rename from src/test/ui/parser/raw-byte-string-eof.rs rename to src/test/ui/parser/raw/raw-byte-string-eof.rs diff --git a/src/test/ui/parser/raw-byte-string-eof.stderr b/src/test/ui/parser/raw/raw-byte-string-eof.stderr similarity index 71% rename from src/test/ui/parser/raw-byte-string-eof.stderr rename to src/test/ui/parser/raw/raw-byte-string-eof.stderr index d5f22e2a1a814..a76668e8051b5 100644 --- a/src/test/ui/parser/raw-byte-string-eof.stderr +++ b/src/test/ui/parser/raw/raw-byte-string-eof.stderr @@ -2,7 +2,9 @@ error[E0748]: unterminated raw string --> $DIR/raw-byte-string-eof.rs:2:5 | LL | br##"a"#; - | ^ unterminated raw string + | ^ - help: consider terminating the string here: `##` + | | + | unterminated raw string | = note: this raw string should be terminated with `"##` diff --git a/src/test/ui/parser/raw-byte-string-literals.rs b/src/test/ui/parser/raw/raw-byte-string-literals.rs similarity index 100% rename from src/test/ui/parser/raw-byte-string-literals.rs rename to src/test/ui/parser/raw/raw-byte-string-literals.rs diff --git a/src/test/ui/parser/raw-byte-string-literals.stderr b/src/test/ui/parser/raw/raw-byte-string-literals.stderr similarity index 100% rename from src/test/ui/parser/raw-byte-string-literals.stderr rename to src/test/ui/parser/raw/raw-byte-string-literals.stderr diff --git a/src/test/ui/parser/raw-str-delim.rs b/src/test/ui/parser/raw/raw-str-delim.rs similarity index 100% rename from src/test/ui/parser/raw-str-delim.rs rename to src/test/ui/parser/raw/raw-str-delim.rs diff --git a/src/test/ui/parser/raw-str-delim.stderr b/src/test/ui/parser/raw/raw-str-delim.stderr similarity index 100% rename from src/test/ui/parser/raw-str-delim.stderr rename to src/test/ui/parser/raw/raw-str-delim.stderr diff --git a/src/test/ui/parser/raw/raw-str-in-macro-call.rs b/src/test/ui/parser/raw/raw-str-in-macro-call.rs new file mode 100644 index 0000000000000..462c2279f5c1c --- /dev/null +++ b/src/test/ui/parser/raw/raw-str-in-macro-call.rs @@ -0,0 +1,14 @@ +// check-pass + +macro_rules! m1 { + ($tt:tt #) => () +} + +macro_rules! m2 { + ($tt:tt) => () +} + +fn main() { + m1!(r#"abc"##); + m2!(r#"abc"#); +} diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.rs b/src/test/ui/parser/raw/raw-str-unbalanced.rs new file mode 100644 index 0000000000000..35f118f5ce6ee --- /dev/null +++ b/src/test/ui/parser/raw/raw-str-unbalanced.rs @@ -0,0 +1,4 @@ +static s: &'static str = + r#" + "## //~ too many `#` when terminating raw string +; diff --git a/src/test/ui/parser/raw/raw-str-unbalanced.stderr b/src/test/ui/parser/raw/raw-str-unbalanced.stderr new file mode 100644 index 0000000000000..bf8f3a7a5a4bd --- /dev/null +++ b/src/test/ui/parser/raw/raw-str-unbalanced.stderr @@ -0,0 +1,10 @@ +error: too many `#` when terminating raw string + --> $DIR/raw-str-unbalanced.rs:3:9 + | +LL | "## + | ^ help: remove the extra `#` + | + = note: the raw string started with 1 `#`s + +error: aborting due to previous error + diff --git a/src/test/ui/parser/raw-str-unterminated.rs b/src/test/ui/parser/raw/raw-str-unterminated.rs similarity index 100% rename from src/test/ui/parser/raw-str-unterminated.rs rename to src/test/ui/parser/raw/raw-str-unterminated.rs diff --git a/src/test/ui/parser/raw-str-unterminated.stderr b/src/test/ui/parser/raw/raw-str-unterminated.stderr similarity index 100% rename from src/test/ui/parser/raw-str-unterminated.stderr rename to src/test/ui/parser/raw/raw-str-unterminated.stderr diff --git a/src/test/ui/parser/raw/raw-string-2.rs b/src/test/ui/parser/raw/raw-string-2.rs new file mode 100644 index 0000000000000..067332d2819bd --- /dev/null +++ b/src/test/ui/parser/raw/raw-string-2.rs @@ -0,0 +1,4 @@ +fn main() { + let x = r###"here's a long string"# "# "##; + //~^ ERROR unterminated raw string +} diff --git a/src/test/ui/parser/raw/raw-string-2.stderr b/src/test/ui/parser/raw/raw-string-2.stderr new file mode 100644 index 0000000000000..8bbac9d7bd0bd --- /dev/null +++ b/src/test/ui/parser/raw/raw-string-2.stderr @@ -0,0 +1,11 @@ +error[E0748]: unterminated raw string + --> $DIR/raw-string-2.rs:2:13 + | +LL | let x = r###"here's a long string"# "# "##; + | ^ unterminated raw string -- help: consider terminating the string here: `###` + | + = note: this raw string should be terminated with `"###` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0748`. diff --git a/src/test/ui/parser/raw/raw_string.rs b/src/test/ui/parser/raw/raw-string.rs similarity index 100% rename from src/test/ui/parser/raw/raw_string.rs rename to src/test/ui/parser/raw/raw-string.rs diff --git a/src/test/ui/parser/raw/raw_string.stderr b/src/test/ui/parser/raw/raw-string.stderr similarity index 59% rename from src/test/ui/parser/raw/raw_string.stderr rename to src/test/ui/parser/raw/raw-string.stderr index 0f1d7e4651deb..b2b853a89e751 100644 --- a/src/test/ui/parser/raw/raw_string.stderr +++ b/src/test/ui/parser/raw/raw-string.stderr @@ -1,8 +1,10 @@ error[E0748]: unterminated raw string - --> $DIR/raw_string.rs:2:13 + --> $DIR/raw-string.rs:2:13 | LL | let x = r##"lol"#; - | ^ unterminated raw string + | ^ - help: consider terminating the string here: `##` + | | + | unterminated raw string | = note: this raw string should be terminated with `"##`