mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-08-04 18:58:41 +00:00
Merge pull request #20012 from lnicola/bump-literal-escaper
Some checks are pending
metrics / build_metrics (push) Waiting to run
metrics / other_metrics (diesel-1.4.8) (push) Blocked by required conditions
metrics / other_metrics (hyper-0.14.18) (push) Blocked by required conditions
metrics / other_metrics (ripgrep-13.0.0) (push) Blocked by required conditions
metrics / other_metrics (self) (push) Blocked by required conditions
metrics / other_metrics (webrender-2022) (push) Blocked by required conditions
metrics / generate_final_metrics (push) Blocked by required conditions
rustdoc / rustdoc (push) Waiting to run
Some checks are pending
metrics / build_metrics (push) Waiting to run
metrics / other_metrics (diesel-1.4.8) (push) Blocked by required conditions
metrics / other_metrics (hyper-0.14.18) (push) Blocked by required conditions
metrics / other_metrics (ripgrep-13.0.0) (push) Blocked by required conditions
metrics / other_metrics (self) (push) Blocked by required conditions
metrics / other_metrics (webrender-2022) (push) Blocked by required conditions
metrics / generate_final_metrics (push) Blocked by required conditions
rustdoc / rustdoc (push) Waiting to run
Update to literal-escaper 0.0.4
This commit is contained in:
commit
70cbf8332a
8 changed files with 139 additions and 169 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -1458,7 +1458,7 @@ dependencies = [
|
|||
"edition",
|
||||
"expect-test",
|
||||
"ra-ap-rustc_lexer",
|
||||
"rustc-literal-escaper 0.0.3",
|
||||
"rustc-literal-escaper 0.0.4",
|
||||
"stdx",
|
||||
"tracing",
|
||||
]
|
||||
|
@ -1927,9 +1927,9 @@ checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
|
|||
|
||||
[[package]]
|
||||
name = "rustc-literal-escaper"
|
||||
version = "0.0.3"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78744cd17f5d01c75b709e49807d1363e02a940ccee2e9e72435843fdb0d076e"
|
||||
checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-stable-hash"
|
||||
|
@ -2207,7 +2207,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"rowan",
|
||||
"rustc-hash 2.1.1",
|
||||
"rustc-literal-escaper 0.0.3",
|
||||
"rustc-literal-escaper 0.0.4",
|
||||
"rustc_apfloat",
|
||||
"smol_str",
|
||||
"stdx",
|
||||
|
|
|
@ -143,7 +143,7 @@ serde = { version = "1.0.219" }
|
|||
serde_derive = { version = "1.0.219" }
|
||||
serde_json = "1.0.140"
|
||||
rustc-hash = "2.1.1"
|
||||
rustc-literal-escaper = "0.0.3"
|
||||
rustc-literal-escaper = "0.0.4"
|
||||
smallvec = { version = "1.15.1", features = [
|
||||
"const_new",
|
||||
"union",
|
||||
|
|
|
@ -433,20 +433,19 @@ fn unescape(s: &str) -> Option<Cow<'_, str>> {
|
|||
let mut buf = String::new();
|
||||
let mut prev_end = 0;
|
||||
let mut has_error = false;
|
||||
unescape::unescape_unicode(s, unescape::Mode::Str, &mut |char_range, unescaped_char| match (
|
||||
unescaped_char,
|
||||
buf.capacity() == 0,
|
||||
) {
|
||||
(Ok(c), false) => buf.push(c),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
unescape::unescape_str(s, |char_range, unescaped_char| {
|
||||
match (unescaped_char, buf.capacity() == 0) {
|
||||
(Ok(c), false) => buf.push(c),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
}
|
||||
(Ok(c), true) => {
|
||||
buf.reserve_exact(s.len());
|
||||
buf.push_str(&s[..prev_end]);
|
||||
buf.push(c);
|
||||
}
|
||||
(Err(_), _) => has_error = true,
|
||||
}
|
||||
(Ok(c), true) => {
|
||||
buf.reserve_exact(s.len());
|
||||
buf.push_str(&s[..prev_end]);
|
||||
buf.push(c);
|
||||
}
|
||||
(Err(_), _) => has_error = true,
|
||||
});
|
||||
|
||||
match (has_error, buf.capacity() == 0) {
|
||||
|
|
|
@ -12,7 +12,7 @@ use span::{Edition, FileId, Span};
|
|||
use stdx::format_to;
|
||||
use syntax::{
|
||||
format_smolstr,
|
||||
unescape::{Mode, unescape_byte, unescape_char, unescape_unicode},
|
||||
unescape::{unescape_byte, unescape_char, unescape_str},
|
||||
};
|
||||
use syntax_bridge::syntax_node_to_token_tree;
|
||||
|
||||
|
@ -430,7 +430,7 @@ fn compile_error_expand(
|
|||
kind: tt::LitKind::Str | tt::LitKind::StrRaw(_),
|
||||
suffix: _,
|
||||
})),
|
||||
] => ExpandError::other(span, Box::from(unescape_str(text).as_str())),
|
||||
] => ExpandError::other(span, Box::from(unescape_symbol(text).as_str())),
|
||||
_ => ExpandError::other(span, "`compile_error!` argument must be a string"),
|
||||
};
|
||||
|
||||
|
@ -481,7 +481,7 @@ fn concat_expand(
|
|||
format_to!(text, "{}", it.symbol.as_str())
|
||||
}
|
||||
tt::LitKind::Str => {
|
||||
text.push_str(unescape_str(&it.symbol).as_str());
|
||||
text.push_str(unescape_symbol(&it.symbol).as_str());
|
||||
record_span(it.span);
|
||||
}
|
||||
tt::LitKind::StrRaw(_) => {
|
||||
|
@ -691,7 +691,7 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> {
|
|||
span,
|
||||
kind: tt::LitKind::Str,
|
||||
suffix: _,
|
||||
})) => Ok((unescape_str(text), *span)),
|
||||
})) => Ok((unescape_symbol(text), *span)),
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
|
@ -712,7 +712,7 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> {
|
|||
span,
|
||||
kind: tt::LitKind::Str,
|
||||
suffix: _,
|
||||
})) => Some((unescape_str(text), *span)),
|
||||
})) => Some((unescape_symbol(text), *span)),
|
||||
TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
|
||||
symbol: text,
|
||||
span,
|
||||
|
@ -897,11 +897,11 @@ fn quote_expand(
|
|||
)
|
||||
}
|
||||
|
||||
fn unescape_str(s: &Symbol) -> Symbol {
|
||||
fn unescape_symbol(s: &Symbol) -> Symbol {
|
||||
if s.as_str().contains('\\') {
|
||||
let s = s.as_str();
|
||||
let mut buf = String::with_capacity(s.len());
|
||||
unescape_unicode(s, Mode::Str, &mut |_, c| {
|
||||
unescape_str(s, |_, c| {
|
||||
if let Ok(c) = c {
|
||||
buf.push(c)
|
||||
}
|
||||
|
|
|
@ -70,11 +70,10 @@ pub(crate) fn goto_type_definition(
|
|||
}
|
||||
|
||||
let range = token.text_range();
|
||||
sema.descend_into_macros_no_opaque(token,false)
|
||||
sema.descend_into_macros_no_opaque(token, false)
|
||||
.into_iter()
|
||||
.filter_map(|token| {
|
||||
sema
|
||||
.token_ancestors_with_macros(token.value)
|
||||
sema.token_ancestors_with_macros(token.value)
|
||||
// When `token` is within a macro call, we can't determine its type. Don't continue
|
||||
// this traversal because otherwise we'll end up returning the type of *that* macro
|
||||
// call, which is not what we want in general.
|
||||
|
@ -103,7 +102,6 @@ pub(crate) fn goto_type_definition(
|
|||
_ => return None,
|
||||
}
|
||||
};
|
||||
|
||||
Some(ty)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
use std::ops;
|
||||
|
||||
use rustc_literal_escaper::{
|
||||
EscapeError, Mode, unescape_byte, unescape_char, unescape_mixed, unescape_unicode,
|
||||
EscapeError, Mode, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char,
|
||||
unescape_str,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -151,14 +152,14 @@ impl<'a> Converter<'a> {
|
|||
self.res
|
||||
}
|
||||
|
||||
fn push(&mut self, kind: SyntaxKind, len: usize, err: Option<&str>) {
|
||||
fn push(&mut self, kind: SyntaxKind, len: usize, errors: Vec<String>) {
|
||||
self.res.push(kind, self.offset);
|
||||
self.offset += len;
|
||||
|
||||
if let Some(err) = err {
|
||||
let token = self.res.len() as u32;
|
||||
let msg = err.to_owned();
|
||||
self.res.error.push(LexError { msg, token });
|
||||
for msg in errors {
|
||||
if !msg.is_empty() {
|
||||
self.res.error.push(LexError { msg, token: self.res.len() as u32 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,14 +168,16 @@ impl<'a> Converter<'a> {
|
|||
// We drop some useful information here (see patterns with double dots `..`)
|
||||
// Storing that info in `SyntaxKind` is not possible due to its layout requirements of
|
||||
// being `u16` that come from `rowan::SyntaxKind`.
|
||||
let mut err = "";
|
||||
let mut errors: Vec<String> = vec![];
|
||||
|
||||
let syntax_kind = {
|
||||
match kind {
|
||||
rustc_lexer::TokenKind::LineComment { doc_style: _ } => COMMENT,
|
||||
rustc_lexer::TokenKind::BlockComment { doc_style: _, terminated } => {
|
||||
if !terminated {
|
||||
err = "Missing trailing `*/` symbols to terminate the block comment";
|
||||
errors.push(
|
||||
"Missing trailing `*/` symbols to terminate the block comment".into(),
|
||||
);
|
||||
}
|
||||
COMMENT
|
||||
}
|
||||
|
@ -184,9 +187,9 @@ impl<'a> Converter<'a> {
|
|||
invalid_infostring,
|
||||
} => {
|
||||
if *has_invalid_preceding_whitespace {
|
||||
err = "invalid preceding whitespace for frontmatter opening"
|
||||
errors.push("invalid preceding whitespace for frontmatter opening".into());
|
||||
} else if *invalid_infostring {
|
||||
err = "invalid infostring for frontmatter"
|
||||
errors.push("invalid infostring for frontmatter".into());
|
||||
}
|
||||
FRONTMATTER
|
||||
}
|
||||
|
@ -198,7 +201,7 @@ impl<'a> Converter<'a> {
|
|||
SyntaxKind::from_keyword(token_text, self.edition).unwrap_or(IDENT)
|
||||
}
|
||||
rustc_lexer::TokenKind::InvalidIdent => {
|
||||
err = "Ident contains invalid characters";
|
||||
errors.push("Ident contains invalid characters".into());
|
||||
IDENT
|
||||
}
|
||||
|
||||
|
@ -206,7 +209,7 @@ impl<'a> Converter<'a> {
|
|||
|
||||
rustc_lexer::TokenKind::GuardedStrPrefix if self.edition.at_least_2024() => {
|
||||
// FIXME: rustc does something better for recovery.
|
||||
err = "Invalid string literal (reserved syntax)";
|
||||
errors.push("Invalid string literal (reserved syntax)".into());
|
||||
ERROR
|
||||
}
|
||||
rustc_lexer::TokenKind::GuardedStrPrefix => {
|
||||
|
@ -222,12 +225,12 @@ impl<'a> Converter<'a> {
|
|||
|
||||
rustc_lexer::TokenKind::Lifetime { starts_with_number } => {
|
||||
if *starts_with_number {
|
||||
err = "Lifetime name cannot start with a number";
|
||||
errors.push("Lifetime name cannot start with a number".into());
|
||||
}
|
||||
LIFETIME_IDENT
|
||||
}
|
||||
rustc_lexer::TokenKind::UnknownPrefixLifetime => {
|
||||
err = "Unknown lifetime prefix";
|
||||
errors.push("Unknown lifetime prefix".into());
|
||||
LIFETIME_IDENT
|
||||
}
|
||||
rustc_lexer::TokenKind::RawLifetime => LIFETIME_IDENT,
|
||||
|
@ -262,119 +265,128 @@ impl<'a> Converter<'a> {
|
|||
rustc_lexer::TokenKind::Unknown => ERROR,
|
||||
rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT,
|
||||
rustc_lexer::TokenKind::UnknownPrefix => {
|
||||
err = "unknown literal prefix";
|
||||
errors.push("unknown literal prefix".into());
|
||||
IDENT
|
||||
}
|
||||
rustc_lexer::TokenKind::Eof => EOF,
|
||||
}
|
||||
};
|
||||
|
||||
let err = if err.is_empty() { None } else { Some(err) };
|
||||
self.push(syntax_kind, token_text.len(), err);
|
||||
self.push(syntax_kind, token_text.len(), errors);
|
||||
}
|
||||
|
||||
fn extend_literal(&mut self, len: usize, kind: &rustc_lexer::LiteralKind) {
|
||||
let mut err = "";
|
||||
let invalid_raw_msg = String::from("Invalid raw string literal");
|
||||
|
||||
let mut errors = vec![];
|
||||
let mut no_end_quote = |c: char, kind: &str| {
|
||||
errors.push(format!("Missing trailing `{c}` symbol to terminate the {kind} literal"));
|
||||
};
|
||||
|
||||
let syntax_kind = match *kind {
|
||||
rustc_lexer::LiteralKind::Int { empty_int, base: _ } => {
|
||||
if empty_int {
|
||||
err = "Missing digits after the integer base prefix";
|
||||
errors.push("Missing digits after the integer base prefix".into());
|
||||
}
|
||||
INT_NUMBER
|
||||
}
|
||||
rustc_lexer::LiteralKind::Float { empty_exponent, base: _ } => {
|
||||
if empty_exponent {
|
||||
err = "Missing digits after the exponent symbol";
|
||||
errors.push("Missing digits after the exponent symbol".into());
|
||||
}
|
||||
FLOAT_NUMBER
|
||||
}
|
||||
rustc_lexer::LiteralKind::Char { terminated } => {
|
||||
if !terminated {
|
||||
err = "Missing trailing `'` symbol to terminate the character literal";
|
||||
no_end_quote('\'', "character");
|
||||
} else {
|
||||
let text = &self.res.text[self.offset + 1..][..len - 1];
|
||||
let i = text.rfind('\'').unwrap();
|
||||
let text = &text[..i];
|
||||
let text = &text[..text.rfind('\'').unwrap()];
|
||||
if let Err(e) = unescape_char(text) {
|
||||
err = error_to_diagnostic_message(e, Mode::Char);
|
||||
errors.push(err_to_msg(e, Mode::Char));
|
||||
}
|
||||
}
|
||||
CHAR
|
||||
}
|
||||
rustc_lexer::LiteralKind::Byte { terminated } => {
|
||||
if !terminated {
|
||||
err = "Missing trailing `'` symbol to terminate the byte literal";
|
||||
no_end_quote('\'', "byte");
|
||||
} else {
|
||||
let text = &self.res.text[self.offset + 2..][..len - 2];
|
||||
let i = text.rfind('\'').unwrap();
|
||||
let text = &text[..i];
|
||||
let text = &text[..text.rfind('\'').unwrap()];
|
||||
if let Err(e) = unescape_byte(text) {
|
||||
err = error_to_diagnostic_message(e, Mode::Byte);
|
||||
errors.push(err_to_msg(e, Mode::Byte));
|
||||
}
|
||||
}
|
||||
|
||||
BYTE
|
||||
}
|
||||
rustc_lexer::LiteralKind::Str { terminated } => {
|
||||
if !terminated {
|
||||
err = "Missing trailing `\"` symbol to terminate the string literal";
|
||||
no_end_quote('"', "string");
|
||||
} else {
|
||||
let text = &self.res.text[self.offset + 1..][..len - 1];
|
||||
let i = text.rfind('"').unwrap();
|
||||
let text = &text[..i];
|
||||
err = unescape_string_error_message(text, Mode::Str);
|
||||
let text = &text[..text.rfind('"').unwrap()];
|
||||
unescape_str(text, |_, res| {
|
||||
if let Err(e) = res {
|
||||
errors.push(err_to_msg(e, Mode::Str));
|
||||
}
|
||||
});
|
||||
}
|
||||
STRING
|
||||
}
|
||||
rustc_lexer::LiteralKind::ByteStr { terminated } => {
|
||||
if !terminated {
|
||||
err = "Missing trailing `\"` symbol to terminate the byte string literal";
|
||||
no_end_quote('"', "byte string");
|
||||
} else {
|
||||
let text = &self.res.text[self.offset + 2..][..len - 2];
|
||||
let i = text.rfind('"').unwrap();
|
||||
let text = &text[..i];
|
||||
err = unescape_string_error_message(text, Mode::ByteStr);
|
||||
let text = &text[..text.rfind('"').unwrap()];
|
||||
unescape_byte_str(text, |_, res| {
|
||||
if let Err(e) = res {
|
||||
errors.push(err_to_msg(e, Mode::ByteStr));
|
||||
}
|
||||
});
|
||||
}
|
||||
BYTE_STRING
|
||||
}
|
||||
rustc_lexer::LiteralKind::CStr { terminated } => {
|
||||
if !terminated {
|
||||
err = "Missing trailing `\"` symbol to terminate the string literal";
|
||||
no_end_quote('"', "C string")
|
||||
} else {
|
||||
let text = &self.res.text[self.offset + 2..][..len - 2];
|
||||
let i = text.rfind('"').unwrap();
|
||||
let text = &text[..i];
|
||||
err = unescape_string_error_message(text, Mode::CStr);
|
||||
let text = &text[..text.rfind('"').unwrap()];
|
||||
unescape_c_str(text, |_, res| {
|
||||
if let Err(e) = res {
|
||||
errors.push(err_to_msg(e, Mode::CStr));
|
||||
}
|
||||
});
|
||||
}
|
||||
C_STRING
|
||||
}
|
||||
rustc_lexer::LiteralKind::RawStr { n_hashes } => {
|
||||
if n_hashes.is_none() {
|
||||
err = "Invalid raw string literal";
|
||||
errors.push(invalid_raw_msg);
|
||||
}
|
||||
STRING
|
||||
}
|
||||
rustc_lexer::LiteralKind::RawByteStr { n_hashes } => {
|
||||
if n_hashes.is_none() {
|
||||
err = "Invalid raw string literal";
|
||||
errors.push(invalid_raw_msg);
|
||||
}
|
||||
BYTE_STRING
|
||||
}
|
||||
rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
|
||||
if n_hashes.is_none() {
|
||||
err = "Invalid raw string literal";
|
||||
errors.push(invalid_raw_msg);
|
||||
}
|
||||
C_STRING
|
||||
}
|
||||
};
|
||||
|
||||
let err = if err.is_empty() { None } else { Some(err) };
|
||||
self.push(syntax_kind, len, err);
|
||||
self.push(syntax_kind, len, errors);
|
||||
}
|
||||
}
|
||||
|
||||
fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str {
|
||||
fn err_to_msg(error: EscapeError, mode: Mode) -> String {
|
||||
match error {
|
||||
EscapeError::ZeroChars => "empty character literal",
|
||||
EscapeError::MoreThanOneChar => "character literal may only contain one codepoint",
|
||||
|
@ -410,28 +422,5 @@ fn error_to_diagnostic_message(error: EscapeError, mode: Mode) -> &'static str {
|
|||
EscapeError::UnskippedWhitespaceWarning => "",
|
||||
EscapeError::MultipleSkippedLinesWarning => "",
|
||||
}
|
||||
}
|
||||
|
||||
fn unescape_string_error_message(text: &str, mode: Mode) -> &'static str {
|
||||
let mut error_message = "";
|
||||
match mode {
|
||||
Mode::CStr => {
|
||||
unescape_mixed(text, mode, &mut |_, res| {
|
||||
if let Err(e) = res {
|
||||
error_message = error_to_diagnostic_message(e, mode);
|
||||
}
|
||||
});
|
||||
}
|
||||
Mode::ByteStr | Mode::Str => {
|
||||
unescape_unicode(text, mode, &mut |_, res| {
|
||||
if let Err(e) = res {
|
||||
error_message = error_to_diagnostic_message(e, mode);
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
// Other Modes are not supported yet or do not apply
|
||||
}
|
||||
}
|
||||
error_message
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
//! There are many AstNodes, but only a few tokens, so we hand-write them here.
|
||||
|
||||
use std::ops::Range;
|
||||
use std::{borrow::Cow, num::ParseIntError};
|
||||
|
||||
use rustc_literal_escaper::{
|
||||
EscapeError, MixedUnit, Mode, unescape_byte, unescape_char, unescape_mixed, unescape_unicode,
|
||||
EscapeError, MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char,
|
||||
unescape_str,
|
||||
};
|
||||
use stdx::always;
|
||||
|
||||
|
@ -150,7 +152,7 @@ impl QuoteOffsets {
|
|||
|
||||
pub trait IsString: AstToken {
|
||||
const RAW_PREFIX: &'static str;
|
||||
const MODE: Mode;
|
||||
fn unescape(s: &str, callback: impl FnMut(Range<usize>, Result<char, EscapeError>));
|
||||
fn is_raw(&self) -> bool {
|
||||
self.text().starts_with(Self::RAW_PREFIX)
|
||||
}
|
||||
|
@ -185,7 +187,7 @@ pub trait IsString: AstToken {
|
|||
let text = &self.text()[text_range_no_quotes - start];
|
||||
let offset = text_range_no_quotes.start() - start;
|
||||
|
||||
unescape_unicode(text, Self::MODE, &mut |range, unescaped_char| {
|
||||
Self::unescape(text, &mut |range: Range<usize>, unescaped_char| {
|
||||
if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) {
|
||||
cb(TextRange::new(s, e) + offset, unescaped_char);
|
||||
}
|
||||
|
@ -203,7 +205,9 @@ pub trait IsString: AstToken {
|
|||
|
||||
impl IsString for ast::String {
|
||||
const RAW_PREFIX: &'static str = "r";
|
||||
const MODE: Mode = Mode::Str;
|
||||
fn unescape(s: &str, cb: impl FnMut(Range<usize>, Result<char, EscapeError>)) {
|
||||
unescape_str(s, cb)
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::String {
|
||||
|
@ -218,20 +222,19 @@ impl ast::String {
|
|||
let mut buf = String::new();
|
||||
let mut prev_end = 0;
|
||||
let mut has_error = None;
|
||||
unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match (
|
||||
unescaped_char,
|
||||
buf.capacity() == 0,
|
||||
) {
|
||||
(Ok(c), false) => buf.push(c),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
unescape_str(text, |char_range, unescaped_char| {
|
||||
match (unescaped_char, buf.capacity() == 0) {
|
||||
(Ok(c), false) => buf.push(c),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
}
|
||||
(Ok(c), true) => {
|
||||
buf.reserve_exact(text.len());
|
||||
buf.push_str(&text[..prev_end]);
|
||||
buf.push(c);
|
||||
}
|
||||
(Err(e), _) => has_error = Some(e),
|
||||
}
|
||||
(Ok(c), true) => {
|
||||
buf.reserve_exact(text.len());
|
||||
buf.push_str(&text[..prev_end]);
|
||||
buf.push(c);
|
||||
}
|
||||
(Err(e), _) => has_error = Some(e),
|
||||
});
|
||||
|
||||
match (has_error, buf.capacity() == 0) {
|
||||
|
@ -244,7 +247,9 @@ impl ast::String {
|
|||
|
||||
impl IsString for ast::ByteString {
|
||||
const RAW_PREFIX: &'static str = "br";
|
||||
const MODE: Mode = Mode::ByteStr;
|
||||
fn unescape(s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) {
|
||||
unescape_byte_str(s, |range, res| callback(range, res.map(char::from)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ast::ByteString {
|
||||
|
@ -259,20 +264,19 @@ impl ast::ByteString {
|
|||
let mut buf: Vec<u8> = Vec::new();
|
||||
let mut prev_end = 0;
|
||||
let mut has_error = None;
|
||||
unescape_unicode(text, Self::MODE, &mut |char_range, unescaped_char| match (
|
||||
unescaped_char,
|
||||
buf.capacity() == 0,
|
||||
) {
|
||||
(Ok(c), false) => buf.push(c as u8),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
unescape_byte_str(text, |char_range, unescaped_byte| {
|
||||
match (unescaped_byte, buf.capacity() == 0) {
|
||||
(Ok(b), false) => buf.push(b),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
}
|
||||
(Ok(b), true) => {
|
||||
buf.reserve_exact(text.len());
|
||||
buf.extend_from_slice(&text.as_bytes()[..prev_end]);
|
||||
buf.push(b);
|
||||
}
|
||||
(Err(e), _) => has_error = Some(e),
|
||||
}
|
||||
(Ok(c), true) => {
|
||||
buf.reserve_exact(text.len());
|
||||
buf.extend_from_slice(&text.as_bytes()[..prev_end]);
|
||||
buf.push(c as u8);
|
||||
}
|
||||
(Err(e), _) => has_error = Some(e),
|
||||
});
|
||||
|
||||
match (has_error, buf.capacity() == 0) {
|
||||
|
@ -285,25 +289,10 @@ impl ast::ByteString {
|
|||
|
||||
impl IsString for ast::CString {
|
||||
const RAW_PREFIX: &'static str = "cr";
|
||||
const MODE: Mode = Mode::CStr;
|
||||
|
||||
fn escaped_char_ranges(&self, cb: &mut dyn FnMut(TextRange, Result<char, EscapeError>)) {
|
||||
let text_range_no_quotes = match self.text_range_between_quotes() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let start = self.syntax().text_range().start();
|
||||
let text = &self.text()[text_range_no_quotes - start];
|
||||
let offset = text_range_no_quotes.start() - start;
|
||||
|
||||
unescape_mixed(text, Self::MODE, &mut |range, unescaped_char| {
|
||||
let text_range =
|
||||
TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap());
|
||||
// XXX: This method should only be used for highlighting ranges. The unescaped
|
||||
// char/byte is not used. For simplicity, we return an arbitrary placeholder char.
|
||||
cb(text_range + offset, unescaped_char.map(|_| ' '));
|
||||
});
|
||||
// NOTE: This method should only be used for highlighting ranges. The unescaped
|
||||
// char/byte is not used. For simplicity, we return an arbitrary placeholder char.
|
||||
fn unescape(s: &str, mut callback: impl FnMut(Range<usize>, Result<char, EscapeError>)) {
|
||||
unescape_c_str(s, |range, _res| callback(range, Ok('_')))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,10 +312,7 @@ impl ast::CString {
|
|||
MixedUnit::Char(c) => buf.extend(c.encode_utf8(&mut [0; 4]).as_bytes()),
|
||||
MixedUnit::HighByte(b) => buf.push(b),
|
||||
};
|
||||
unescape_mixed(text, Self::MODE, &mut |char_range, unescaped| match (
|
||||
unescaped,
|
||||
buf.capacity() == 0,
|
||||
) {
|
||||
unescape_c_str(text, |char_range, unescaped| match (unescaped, buf.capacity() == 0) {
|
||||
(Ok(u), false) => extend_unit(&mut buf, u),
|
||||
(Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
|
||||
prev_end = char_range.end
|
||||
|
|
|
@ -6,7 +6,9 @@ mod block;
|
|||
|
||||
use itertools::Itertools;
|
||||
use rowan::Direction;
|
||||
use rustc_literal_escaper::{self, EscapeError, Mode, unescape_mixed, unescape_unicode};
|
||||
use rustc_literal_escaper::{
|
||||
EscapeError, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
AstNode, SyntaxError,
|
||||
|
@ -47,7 +49,7 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec<SyntaxError>) {
|
|||
}
|
||||
|
||||
fn rustc_unescape_error_to_string(err: EscapeError) -> (&'static str, bool) {
|
||||
use rustc_literal_escaper::EscapeError as EE;
|
||||
use EscapeError as EE;
|
||||
|
||||
#[rustfmt::skip]
|
||||
let err_message = match err {
|
||||
|
@ -142,7 +144,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
|
|||
ast::LiteralKind::String(s) => {
|
||||
if !s.is_raw() {
|
||||
if let Some(without_quotes) = unquote(text, 1, '"') {
|
||||
unescape_unicode(without_quotes, Mode::Str, &mut |range, char| {
|
||||
unescape_str(without_quotes, |range, char| {
|
||||
if let Err(err) = char {
|
||||
push_err(1, range.start, err);
|
||||
}
|
||||
|
@ -153,7 +155,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
|
|||
ast::LiteralKind::ByteString(s) => {
|
||||
if !s.is_raw() {
|
||||
if let Some(without_quotes) = unquote(text, 2, '"') {
|
||||
unescape_unicode(without_quotes, Mode::ByteStr, &mut |range, char| {
|
||||
unescape_byte_str(without_quotes, |range, char| {
|
||||
if let Err(err) = char {
|
||||
push_err(1, range.start, err);
|
||||
}
|
||||
|
@ -164,7 +166,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
|
|||
ast::LiteralKind::CString(s) => {
|
||||
if !s.is_raw() {
|
||||
if let Some(without_quotes) = unquote(text, 2, '"') {
|
||||
unescape_mixed(without_quotes, Mode::CStr, &mut |range, char| {
|
||||
unescape_c_str(without_quotes, |range, char| {
|
||||
if let Err(err) = char {
|
||||
push_err(1, range.start, err);
|
||||
}
|
||||
|
@ -174,20 +176,16 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
|
|||
}
|
||||
ast::LiteralKind::Char(_) => {
|
||||
if let Some(without_quotes) = unquote(text, 1, '\'') {
|
||||
unescape_unicode(without_quotes, Mode::Char, &mut |range, char| {
|
||||
if let Err(err) = char {
|
||||
push_err(1, range.start, err);
|
||||
}
|
||||
});
|
||||
if let Err(err) = unescape_char(without_quotes) {
|
||||
push_err(1, 0, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::Byte(_) => {
|
||||
if let Some(without_quotes) = unquote(text, 2, '\'') {
|
||||
unescape_unicode(without_quotes, Mode::Byte, &mut |range, char| {
|
||||
if let Err(err) = char {
|
||||
push_err(2, range.start, err);
|
||||
}
|
||||
});
|
||||
if let Err(err) = unescape_byte(without_quotes) {
|
||||
push_err(2, 0, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::LiteralKind::IntNumber(_)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue