use solar_interface::{BytePos, Span, diagnostics::DiagCtxt};
use std::ops::Range;
#[derive(Debug, PartialEq, Eq)]
pub enum EscapeError {
LoneSlash,
InvalidEscape,
BareCarriageReturn,
CannotSkipMultipleLines,
HexEscapeTooShort,
InvalidHexEscape,
UnicodeEscapeTooShort,
InvalidUnicodeEscape,
StrNewline,
StrNonAsciiChar,
HexNotHexDigit,
HexBadUnderscore,
HexOddDigits,
HexPrefix,
}
impl EscapeError {
fn msg(&self) -> &'static str {
match self {
Self::LoneSlash => "invalid trailing slash in literal",
Self::InvalidEscape => "unknown character escape",
Self::BareCarriageReturn => "bare CR not allowed in string, use `\\r` instead",
Self::CannotSkipMultipleLines => "cannot skip multiple lines with `\\`",
Self::HexEscapeTooShort => "hex escape must be followed by 2 hex digits",
Self::InvalidHexEscape => "invalid character in hex escape",
Self::UnicodeEscapeTooShort => "unicode escape must be followed by 4 hex digits",
Self::InvalidUnicodeEscape => "invalid character in unicode escape",
Self::StrNewline => "unescaped newline",
Self::StrNonAsciiChar => {
"unicode characters are not allowed in string literals; use a `unicode\"...\"` literal instead"
}
Self::HexNotHexDigit => "invalid hex digit",
Self::HexBadUnderscore => "invalid underscore in hex literal",
Self::HexOddDigits => "odd number of hex nibbles",
Self::HexPrefix => "hex prefix is not allowed",
}
}
}
pub(crate) fn emit_unescape_error(
dcx: &DiagCtxt,
lit: &str,
err_span: Span,
range: Range<usize>,
error: EscapeError,
) {
let last_char = || {
let c = lit[range.clone()].chars().next_back().unwrap();
let span = err_span.with_lo(err_span.hi() - BytePos(c.len_utf8() as u32));
(c, span)
};
let mut diag = dcx.err(error.msg()).span(err_span);
if matches!(
error,
EscapeError::InvalidEscape
| EscapeError::InvalidHexEscape
| EscapeError::InvalidUnicodeEscape
) {
diag = diag.span(last_char().1);
}
diag.emit();
}