1use rucc_session::Std;
67use rucc_target::TargetInfo;
68
69use crate::remarks::Remarks;
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum Encoding {
74 Plain,
76 Wide,
78 Utf8,
80 Utf16,
82 Utf32,
84}
85
86impl Encoding {
87 #[must_use]
89 pub fn element_width(self, target: &TargetInfo) -> u32 {
90 match self {
91 Encoding::Plain | Encoding::Utf8 => 8,
92 Encoding::Wide => target.wchar_width,
93 Encoding::Utf16 => 16,
94 Encoding::Utf32 => 32,
95 }
96 }
97
98 #[must_use]
100 pub fn is_signed(self, target: &TargetInfo) -> bool {
101 match self {
102 Encoding::Plain => target.char_is_signed,
103 Encoding::Wide => target.wchar_is_signed,
104 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => false,
105 }
106 }
107
108 fn read(bytes: &[u8]) -> (Encoding, usize) {
110 match bytes {
111 [b'u', b'8', ..] => (Encoding::Utf8, 2),
112 [b'u', ..] => (Encoding::Utf16, 1),
113 [b'U', ..] => (Encoding::Utf32, 1),
114 [b'L', ..] => (Encoding::Wide, 1),
115 _ => (Encoding::Plain, 0),
116 }
117 }
118
119 fn since(self, character: bool) -> Std {
122 match self {
123 Encoding::Plain | Encoding::Wide => Std::C89,
124 Encoding::Utf8 if character => Std::C23,
125 Encoding::Utf8 | Encoding::Utf16 | Encoding::Utf32 => Std::C11,
126 }
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct CharConstant {
133 pub value: i64,
136 pub encoding: Encoding,
138 pub remarks: Remarks,
140}
141
142#[derive(Debug, Clone, PartialEq, Eq)]
144pub struct StringLiteral {
145 pub elements: Vec<u32>,
149 pub encoding: Encoding,
151 pub remarks: Remarks,
153}
154
155impl StringLiteral {
156 #[must_use]
159 pub fn bytes(&self, target: &TargetInfo) -> Vec<u8> {
160 let width = self.encoding.element_width(target) / 8;
161 let mut bytes = Vec::with_capacity((self.elements.len() + 1) * width as usize);
162 for element in self.elements.iter().copied().chain([0]) {
163 let taken = &element.to_le_bytes()[..width as usize];
164 if target.little_endian {
165 bytes.extend_from_slice(taken);
166 } else {
167 bytes.extend(taken.iter().rev());
168 }
169 }
170 bytes
171 }
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
176pub enum LiteralError {
177 NotALiteral,
180 Empty,
182 TooLong,
185 NoHexDigits,
187 IncompleteUcn,
189 InvalidUcn,
192 NamedUcn,
194 InvalidUtf8,
197 PrefixNotInDialect,
199}
200
201impl LiteralError {
202 #[must_use]
204 pub const fn message(self) -> &'static str {
205 match self {
206 LiteralError::NotALiteral => "not a character constant or a string literal",
207 LiteralError::Empty => "empty character constant",
208 LiteralError::TooLong => "character constant too long for its type",
209 LiteralError::NoHexDigits => "\\x used with no following hex digits",
210 LiteralError::IncompleteUcn => "incomplete universal character name",
211 LiteralError::InvalidUcn => "not a valid universal character",
212 LiteralError::NamedUcn => "named universal character escapes are not supported yet",
213 LiteralError::InvalidUtf8 => "failure to convert the source to the execution charset",
214 LiteralError::PrefixNotInDialect => {
215 "this encoding prefix is not available in this dialect"
216 }
217 }
218 }
219}
220
221pub fn character(text: &str, std: Std, target: &TargetInfo) -> Result<CharConstant, LiteralError> {
228 let (encoding, body) = open(text, b'\'', std, true)?;
229 let width = encoding.element_width(target);
230 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
231
232 let mut value: u64 = 0;
236 let mut count = 0u32;
237 while let Some(piece) = reader.next(width)? {
238 for element in piece.elements(width) {
239 value = (value << width) | u64::from(element);
240 count += 1;
241 }
242 }
243 let mut remarks = reader.remarks;
244
245 let type_width = if encoding == Encoding::Plain { 32 } else { width };
248 let capacity = type_width / width;
249 match count {
250 0 => return Err(LiteralError::Empty),
251 1 => {}
252 _ if encoding == Encoding::Utf8 => return Err(LiteralError::TooLong),
253 _ if count > capacity => remarks = remarks.with(Remarks::TOO_LONG),
254 _ => remarks = remarks.with(Remarks::MULTICHARACTER),
255 }
256
257 let (bits, signed) = if count == 1 {
261 (width, encoding.is_signed(target))
262 } else {
263 (type_width, encoding == Encoding::Plain || encoding.is_signed(target))
264 };
265 Ok(CharConstant { value: narrow(value, bits, signed), encoding, remarks })
266}
267
268pub fn string(text: &str, std: Std, target: &TargetInfo) -> Result<StringLiteral, LiteralError> {
275 let (encoding, body) = open(text, b'"', std, false)?;
276 let width = encoding.element_width(target);
277 let mut reader = Reader { bytes: body, index: 0, std, remarks: Remarks::NONE };
278 let mut elements = Vec::new();
279 while let Some(piece) = reader.next(width)? {
280 elements.extend(piece.elements(width));
281 }
282 Ok(StringLiteral { elements, encoding, remarks: reader.remarks })
283}
284
285fn open(
287 text: &str,
288 quote: u8,
289 std: Std,
290 character: bool,
291) -> Result<(Encoding, &[u8]), LiteralError> {
292 let bytes = text.as_bytes();
293 let (encoding, prefix) = Encoding::read(bytes);
294 if std < encoding.since(character) {
295 return Err(LiteralError::PrefixNotInDialect);
296 }
297 let rest = &bytes[prefix..];
298 match rest {
299 [first, .., last] if *first == quote && *last == quote => {
300 Ok((encoding, &rest[1..rest.len() - 1]))
301 }
302 _ => Err(LiteralError::NotALiteral),
303 }
304}
305
306fn narrow(value: u64, bits: u32, signed: bool) -> i64 {
308 let masked = if bits >= 64 { value } else { value & ((1u64 << bits) - 1) };
309 if signed && bits < 64 && masked >> (bits - 1) & 1 == 1 {
310 (masked | !((1u64 << bits) - 1)) as i64
312 } else {
313 masked as i64
314 }
315}
316
317#[derive(Debug, Clone, Copy, PartialEq, Eq)]
319enum Piece {
320 Char(u32),
323 Value(u32),
326}
327
328impl Piece {
329 fn elements(self, width: u32) -> Vec<u32> {
331 let code = match self {
332 Piece::Value(value) => return vec![value],
333 Piece::Char(code) => code,
334 };
335 match width {
336 8 => {
337 let mut buffer = [0u8; 4];
338 let text = char::from_u32(code)
339 .map(|character| character.encode_utf8(&mut buffer).len())
340 .unwrap_or(0);
341 buffer[..text].iter().map(|&byte| u32::from(byte)).collect()
342 }
343 16 if code > 0xffff => {
346 let value = code - 0x1_0000;
347 vec![0xd800 + (value >> 10), 0xdc00 + (value & 0x3ff)]
348 }
349 _ => vec![code],
350 }
351 }
352}
353
354struct Reader<'a> {
356 bytes: &'a [u8],
358 index: usize,
360 std: Std,
362 remarks: Remarks,
364}
365
366impl Reader<'_> {
367 fn next(&mut self, width: u32) -> Result<Option<Piece>, LiteralError> {
369 let Some(&byte) = self.bytes.get(self.index) else {
370 return Ok(None);
371 };
372 self.index += 1;
373 if byte == b'\\' {
374 return self.escape(width).map(Some);
375 }
376 if byte < 0x80 {
377 return Ok(Some(Piece::Char(u32::from(byte))));
378 }
379 if width == 8 {
383 return Ok(Some(Piece::Value(u32::from(byte))));
384 }
385 let length = utf8_length(byte).ok_or(LiteralError::InvalidUtf8)?;
388 let end = self.index - 1 + length;
389 let text = self
390 .bytes
391 .get(self.index - 1..end)
392 .and_then(|slice| std::str::from_utf8(slice).ok())
393 .ok_or(LiteralError::InvalidUtf8)?;
394 let character = text.chars().next().ok_or(LiteralError::InvalidUtf8)?;
395 self.index = end;
396 Ok(Some(Piece::Char(character as u32)))
397 }
398
399 fn escape(&mut self, width: u32) -> Result<Piece, LiteralError> {
401 let Some(&byte) = self.bytes.get(self.index) else {
402 return Err(LiteralError::NotALiteral);
404 };
405 self.index += 1;
406 let simple = match byte {
407 b'n' => Some(0x0a),
408 b't' => Some(0x09),
409 b'r' => Some(0x0d),
410 b'a' => Some(0x07),
411 b'b' => Some(0x08),
412 b'f' => Some(0x0c),
413 b'v' => Some(0x0b),
414 b'\\' | b'\'' | b'"' | b'?' => Some(u32::from(byte)),
415 _ => None,
416 };
417 if let Some(value) = simple {
418 return Ok(Piece::Value(value));
419 }
420 match byte {
421 b'e' | b'E' => {
423 self.remarks = self.remarks.with(Remarks::NON_ISO_ESCAPE);
424 Ok(Piece::Value(0x1b))
425 }
426 b'0'..=b'7' => Ok(Piece::Value(self.octal(byte, width))),
427 b'x' => self.hex(width).map(Piece::Value),
428 b'u' | b'U' => self.ucn(byte).map(Piece::Char),
429 b'N' => Err(LiteralError::NamedUcn),
430 _ => {
433 self.remarks = self.remarks.with(Remarks::UNKNOWN_ESCAPE);
434 Ok(Piece::Value(u32::from(byte)))
435 }
436 }
437 }
438
439 fn octal(&mut self, first: u8, width: u32) -> u32 {
442 let mut value = u32::from(first - b'0');
443 for _ in 0..2 {
444 match self.bytes.get(self.index) {
445 Some(&byte @ b'0'..=b'7') => {
446 value = value * 8 + u32::from(byte - b'0');
447 self.index += 1;
448 }
449 _ => break,
450 }
451 }
452 self.fit(value, width)
453 }
454
455 fn hex(&mut self, width: u32) -> Result<u32, LiteralError> {
457 let mut value: u64 = 0;
458 let mut digits = 0;
459 while let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) {
460 value = value.saturating_mul(16).saturating_add(u64::from(digit));
463 digits += 1;
464 self.index += 1;
465 }
466 if digits == 0 {
467 return Err(LiteralError::NoHexDigits);
468 }
469 Ok(self.fit(u32::try_from(value).unwrap_or(u32::MAX), width))
470 }
471
472 fn ucn(&mut self, marker: u8) -> Result<u32, LiteralError> {
474 if self.bytes.get(self.index) == Some(&b'{') {
475 return Err(LiteralError::NamedUcn);
476 }
477 let digits = if marker == b'u' { 4 } else { 8 };
478 let mut value: u32 = 0;
479 for _ in 0..digits {
480 let Some(digit) = self.bytes.get(self.index).and_then(|&byte| hex_digit(byte)) else {
481 return Err(LiteralError::IncompleteUcn);
482 };
483 value = value * 16 + digit;
484 self.index += 1;
485 }
486 let allowed_low = matches!(value, 0x24 | 0x40 | 0x60);
490 if (value < 0xa0 && !allowed_low) || (0xd800..=0xdfff).contains(&value) || value > 0x10ffff
491 {
492 return Err(LiteralError::InvalidUcn);
493 }
494 if self.std < Std::C99 {
495 self.remarks = self.remarks.with(Remarks::UCN);
496 }
497 Ok(value)
498 }
499
500 fn fit(&mut self, value: u32, width: u32) -> u32 {
503 if width >= 32 {
504 return value;
505 }
506 let mask = (1u32 << width) - 1;
507 if value & !mask != 0 {
508 self.remarks = self.remarks.with(Remarks::ESCAPE_OUT_OF_RANGE);
509 }
510 value & mask
511 }
512}
513
514fn hex_digit(byte: u8) -> Option<u32> {
516 char::from(byte).to_digit(16)
517}
518
519fn utf8_length(byte: u8) -> Option<usize> {
522 match byte {
523 0x00..=0x7f => Some(1),
524 0xc2..=0xdf => Some(2),
525 0xe0..=0xef => Some(3),
526 0xf0..=0xf4 => Some(4),
527 _ => None,
528 }
529}
530
531#[cfg(test)]
532mod tests {
533 use rucc_target::Triple;
534
535 use super::*;
536
537 fn linux() -> TargetInfo {
538 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
539 }
540
541 fn windows() -> TargetInfo {
542 TargetInfo::new("x86_64-pc-windows-msvc".parse::<Triple>().expect("a known triple"))
543 }
544
545 fn arm() -> TargetInfo {
546 TargetInfo::new("aarch64-unknown-linux-gnu".parse::<Triple>().expect("a known triple"))
547 }
548
549 fn ch(text: &str) -> i64 {
551 character(text, Std::C23, &linux()).expect("a character constant").value
552 }
553
554 fn ch_remarks(text: &str) -> Remarks {
556 character(text, Std::C23, &linux()).expect("a character constant").remarks
557 }
558
559 fn ch_error(text: &str) -> LiteralError {
561 character(text, Std::C23, &linux()).expect_err("not a character constant")
562 }
563
564 fn str_elements(text: &str) -> Vec<u32> {
566 string(text, Std::C23, &linux()).expect("a string literal").elements
567 }
568
569 fn str_bytes(text: &str) -> Vec<u8> {
571 string(text, Std::C23, &linux()).expect("a string literal").bytes(&linux())
572 }
573
574 #[test]
575 fn the_ordinary_cases_are_the_characters_they_look_like() {
576 assert_eq!(ch("'a'"), 0x61);
577 assert_eq!(ch(r"'\n'"), 0x0a);
578 assert_eq!(ch(r"'\0'"), 0);
579 assert_eq!(ch(r"'\\'"), 0x5c);
580 assert_eq!(ch(r"'\''"), 0x27);
581 assert_eq!(ch(r#"'\"'"#), 0x22);
582 assert_eq!(ch(r"'\?'"), 0x3f);
583 assert_eq!(str_elements(r#""hi""#), vec![0x68, 0x69]);
584 }
585
586 #[test]
590 fn a_high_character_takes_the_sign_of_plain_char() {
591 assert_eq!(ch(r"'\xff'"), -1);
592 assert_eq!(ch(r"'\377'"), -1);
593 assert_eq!(character(r"'\xff'", Std::C23, &arm()).expect("a constant").value, 255);
594 assert_eq!(ch(r"u8'\xff'"), 255);
596 }
597
598 #[test]
601 fn an_escape_too_big_for_its_element_is_truncated_and_says_so() {
602 let out = character(r"'\x1ff'", Std::C23, &linux()).expect("a constant");
603 assert_eq!(out.value, -1);
604 assert!(out.remarks.has(Remarks::ESCAPE_OUT_OF_RANGE));
605 let out = character(r"'\400'", Std::C23, &linux()).expect("a constant");
606 assert_eq!(out.value, 0);
607 assert!(out.remarks.has(Remarks::ESCAPE_OUT_OF_RANGE));
608 assert!(!ch_remarks(r"L'\x1ff'").has(Remarks::ESCAPE_OUT_OF_RANGE));
610 assert_eq!(ch(r"L'\x1ff'"), 0x1ff);
611 }
612
613 #[test]
617 fn more_than_one_character_shifts_them_together() {
618 assert_eq!(ch("'ab'"), 0x6162);
619 assert_eq!(ch("'abc'"), 0x616263);
620 assert_eq!(ch("'abcd'"), 0x61626364);
621 assert_eq!(ch("'abcde'"), 0x62636465);
622 assert_eq!(ch(r"'\xff\xfe'"), 0xfffe);
623 assert_eq!(ch(r"'\xff\xff\xff\xff'"), -1);
624 assert_eq!(ch(r"'\x80\x00'"), 0x8000);
625
626 assert!(ch_remarks("'ab'").has(Remarks::MULTICHARACTER));
627 assert!(ch_remarks("'abcd'").has(Remarks::MULTICHARACTER));
628 assert!(ch_remarks("'abcde'").has(Remarks::TOO_LONG));
629 assert!(!ch_remarks("'abcde'").has(Remarks::MULTICHARACTER));
630 assert!(!ch_remarks("'a'").has(Remarks::MULTICHARACTER));
631 }
632
633 #[test]
636 fn a_prefixed_constant_holds_one_character_and_keeps_the_last() {
637 for text in [r"L'ab'", r"u'ab'", r"U'ab'"] {
638 let out = character(text, Std::C23, &linux()).expect("a constant");
639 assert_eq!(out.value, 0x62, "{text}");
640 assert!(out.remarks.has(Remarks::TOO_LONG), "{text}");
641 }
642 assert_eq!(ch_error("u8'ab'"), LiteralError::TooLong);
643 assert_eq!(ch_error("u8'é'"), LiteralError::TooLong);
644 }
645
646 #[test]
647 fn the_empty_constant_has_no_value_to_have() {
648 assert_eq!(ch_error("''"), LiteralError::Empty);
649 assert_eq!(ch_error("L''"), LiteralError::Empty);
650 assert_eq!(str_elements(r#""""#), Vec::<u32>::new());
652 assert_eq!(str_bytes(r#""""#), vec![0]);
653 }
654
655 #[test]
658 fn a_source_character_is_encoded_and_an_escape_is_not() {
659 assert_eq!(ch("'é'"), 0xc3a9);
660 assert_eq!(ch("L'é'"), 0xe9);
661 assert_eq!(ch("u'€'"), 0x20ac);
662 assert_eq!(ch(r"U'\U0001F600'"), 0x1f600);
663 assert_eq!(ch(r"'\U0001F600'"), i64::from(0xf09f_9880u32 as i32));
666 assert!(ch_remarks(r"'\U0001F600'").has(Remarks::MULTICHARACTER));
667 }
668
669 #[test]
672 fn the_escapes_outside_the_standard_still_have_values() {
673 assert_eq!(ch(r"'\e'"), 0x1b);
674 assert!(ch_remarks(r"'\e'").has(Remarks::NON_ISO_ESCAPE));
675 assert_eq!(ch(r"'\q'"), 0x71);
676 assert!(ch_remarks(r"'\q'").has(Remarks::UNKNOWN_ESCAPE));
677 assert_eq!(ch_error(r"'\x'"), LiteralError::NoHexDigits);
678 assert_eq!(ch_error(r"'\N{LATIN SMALL LETTER A}'"), LiteralError::NamedUcn);
679 }
680
681 #[test]
685 fn a_universal_character_name_may_not_name_just_anything() {
686 assert_eq!(ch("'\\u0024'"), 0x24);
687 assert_eq!(ch("'\\u00e9'"), 0xc3a9);
688 assert_eq!(ch_error("'\\u0041'"), LiteralError::InvalidUcn);
689 assert_eq!(ch_error(r"'\ud800'"), LiteralError::InvalidUcn);
690 assert_eq!(ch_error(r"'\u00'"), LiteralError::IncompleteUcn);
691 assert_eq!(ch_error(r"'\U00110000'"), LiteralError::InvalidUcn);
693 }
694
695 #[test]
696 fn a_universal_character_name_before_c99_is_worth_a_remark() {
697 let out = character("'\\u00e9'", Std::C89, &linux()).expect("a constant");
698 assert!(out.remarks.has(Remarks::UCN));
699 let out = character("'\\u00e9'", Std::C99, &linux()).expect("a constant");
700 assert!(!out.remarks.has(Remarks::UCN));
701 }
702
703 #[test]
706 fn an_octal_escape_ends_and_a_hex_escape_does_not() {
707 assert_eq!(str_elements(r#""\1234""#), vec![0x53, 0x34]);
708 assert_eq!(str_elements(r#""\x41z""#), vec![0x41, 0x7a]);
709 assert_eq!(str_elements(r#""\x41""#), vec![0x41]);
710 }
711
712 #[test]
715 fn a_string_is_as_many_bytes_as_its_encoding_makes_it() {
716 assert_eq!(str_bytes(r#""abc""#).len(), 4);
717 assert_eq!(str_bytes(r#"L"abc""#).len(), 16);
718 assert_eq!(str_bytes(r#"u"abc""#).len(), 8);
719 assert_eq!(str_bytes(r#"U"abc""#).len(), 16);
720 assert_eq!(str_bytes(r#"u8"abc""#).len(), 4);
721 assert_eq!(str_bytes(r#""a\0b""#), vec![0x61, 0x00, 0x62, 0x00]);
723 assert_eq!(str_bytes(r#""é""#), vec![0xc3, 0xa9, 0x00]);
724 }
725
726 #[test]
729 fn utf16_splits_the_characters_that_do_not_fit_into_a_surrogate_pair() {
730 assert_eq!(
731 str_elements(r#"u8"é€😀""#),
732 vec![0xc3, 0xa9, 0xe2, 0x82, 0xac, 0xf0, 0x9f, 0x98, 0x80]
733 );
734 assert_eq!(str_elements(r#"u"€😀""#), vec![0x20ac, 0xd83d, 0xde00]);
735 assert_eq!(str_elements(r#"U"€😀""#), vec![0x20ac, 0x1f600]);
736 }
737
738 #[test]
741 fn a_wide_literal_is_whatever_the_target_makes_wchar_t() {
742 let text = r#"L"a😀""#;
743 let here = string(text, Std::C23, &linux()).expect("a string");
744 assert_eq!(here.elements, vec![0x61, 0x1f600]);
745 assert_eq!(here.bytes(&linux()).len(), 12);
746 let there = string(text, Std::C23, &windows()).expect("a string");
747 assert_eq!(there.elements, vec![0x61, 0xd83d, 0xde00]);
748 assert_eq!(there.bytes(&windows()).len(), 8);
749 assert_eq!(character(r"L'\xffffffff'", Std::C23, &linux()).expect("a constant").value, -1);
752 assert_eq!(
753 character(r"L'\xffffffff'", Std::C23, &arm()).expect("a constant").value,
754 0xffff_ffff
755 );
756 }
757
758 #[test]
762 fn the_bytes_come_out_in_the_targets_order() {
763 let mut big = linux();
764 big.little_endian = false;
765 let literal = string(r#"u"ab""#, Std::C23, &big).expect("a string");
766 assert_eq!(literal.bytes(&big), vec![0x00, 0x61, 0x00, 0x62, 0x00, 0x00]);
767 assert_eq!(literal.bytes(&linux()), vec![0x61, 0x00, 0x62, 0x00, 0x00, 0x00]);
768 }
769
770 #[test]
773 fn a_prefix_is_only_available_in_the_dialect_that_has_it() {
774 assert!(character("L'a'", Std::C89, &linux()).is_ok());
775 assert_eq!(
776 character("u'a'", Std::C99, &linux()).expect_err("not in C99"),
777 LiteralError::PrefixNotInDialect
778 );
779 assert!(character("u'a'", Std::C11, &linux()).is_ok());
780 assert!(string(r#"u8"a""#, Std::C11, &linux()).is_ok());
781 assert_eq!(
782 character("u8'a'", Std::C11, &linux()).expect_err("not in C11"),
783 LiteralError::PrefixNotInDialect
784 );
785 assert!(character("u8'a'", Std::C23, &linux()).is_ok());
786 }
787
788 #[test]
791 fn an_element_is_as_wide_as_the_encoding_and_the_target_agree() {
792 let target = linux();
793 assert_eq!(Encoding::Plain.element_width(&target), 8);
794 assert_eq!(Encoding::Utf8.element_width(&target), 8);
795 assert_eq!(Encoding::Utf16.element_width(&target), 16);
796 assert_eq!(Encoding::Utf32.element_width(&target), 32);
797 assert_eq!(Encoding::Wide.element_width(&target), 32);
798 assert_eq!(Encoding::Wide.element_width(&windows()), 16);
799
800 assert!(Encoding::Plain.is_signed(&target));
801 assert!(!Encoding::Plain.is_signed(&arm()));
802 assert!(Encoding::Wide.is_signed(&target));
803 assert!(!Encoding::Wide.is_signed(&arm()));
804 assert!(!Encoding::Utf8.is_signed(&target));
805 assert!(!Encoding::Utf16.is_signed(&target));
806 assert!(!Encoding::Utf32.is_signed(&target));
807 }
808
809 #[test]
810 fn a_spelling_that_is_not_a_literal_is_refused_rather_than_guessed_at() {
811 assert_eq!(ch_error("a"), LiteralError::NotALiteral);
812 assert_eq!(ch_error("'a"), LiteralError::NotALiteral);
813 assert_eq!(
814 string("'a'", Std::C23, &linux()).expect_err("not a string"),
815 LiteralError::NotALiteral
816 );
817 assert_eq!(ch_error("'"), LiteralError::NotALiteral);
818 }
819
820 #[test]
821 fn every_error_has_something_to_print() {
822 for error in [
823 LiteralError::NotALiteral,
824 LiteralError::Empty,
825 LiteralError::TooLong,
826 LiteralError::NoHexDigits,
827 LiteralError::IncompleteUcn,
828 LiteralError::InvalidUcn,
829 LiteralError::NamedUcn,
830 LiteralError::InvalidUtf8,
831 LiteralError::PrefixNotInDialect,
832 ] {
833 assert!(!error.message().is_empty());
834 }
835 }
836}