1use rucc_base::{Interner, Symbol};
36use rucc_diag::{Diagnostic, Span};
37use rucc_session::Std;
38use rucc_target::TargetInfo;
39
40use crate::keyword::{Keyword, Keywords};
41use crate::literal::{CharConstant, LiteralError, StringLiteral};
42use crate::number::{FloatConstant, IntConstant, IntError};
43use crate::remarks::Remarks;
44use crate::token::{PpToken, PpTokenKind, Punct, TokenFlags};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum TokenKind {
52 Keyword(Keyword),
54 Ident,
56 Int,
58 Float,
60 Char,
62 Str,
65 Punct(Punct),
67 Eof,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub struct Token {
77 pub kind: TokenKind,
79 pub flags: TokenFlags,
82 pub value: u32,
85 pub span: Span,
87}
88
89impl Token {
90 #[inline]
92 #[must_use]
93 pub const fn is_eof(self) -> bool {
94 matches!(self.kind, TokenKind::Eof)
95 }
96
97 #[inline]
99 #[must_use]
100 pub const fn keyword(self) -> Option<Keyword> {
101 match self.kind {
102 TokenKind::Keyword(word) => Some(word),
103 _ => None,
104 }
105 }
106
107 #[inline]
109 #[must_use]
110 pub const fn punct(self) -> Option<Punct> {
111 match self.kind {
112 TokenKind::Punct(punct) => Some(punct),
113 _ => None,
114 }
115 }
116
117 #[inline]
119 #[must_use]
120 pub const fn ident(self) -> Option<Symbol> {
121 match self.kind {
122 TokenKind::Ident => Some(Symbol::from_raw(self.value)),
123 _ => None,
124 }
125 }
126}
127
128#[derive(Debug, Default)]
130pub struct Tokens {
131 pub tokens: Vec<Token>,
133 pub ints: Vec<IntConstant>,
135 pub floats: Vec<FloatConstant>,
137 pub chars: Vec<CharConstant>,
139 pub strings: Vec<StringLiteral>,
141 pub pragmas: Vec<Pragma>,
143}
144
145#[derive(Debug, Clone)]
155pub struct Pragma {
156 pub before: u32,
158 pub tokens: Vec<Token>,
160 pub span: Span,
162}
163
164impl Tokens {
165 #[must_use]
167 pub fn int(&self, token: Token) -> Option<&IntConstant> {
168 match token.kind {
169 TokenKind::Int => self.ints.get(token.value as usize),
170 _ => None,
171 }
172 }
173
174 #[must_use]
176 pub fn float(&self, token: Token) -> Option<&FloatConstant> {
177 match token.kind {
178 TokenKind::Float => self.floats.get(token.value as usize),
179 _ => None,
180 }
181 }
182
183 #[must_use]
185 pub fn character(&self, token: Token) -> Option<&CharConstant> {
186 match token.kind {
187 TokenKind::Char => self.chars.get(token.value as usize),
188 _ => None,
189 }
190 }
191
192 #[must_use]
194 pub fn string(&self, token: Token) -> Option<&StringLiteral> {
195 match token.kind {
196 TokenKind::Str => self.strings.get(token.value as usize),
197 _ => None,
198 }
199 }
200}
201
202#[derive(Debug, Clone, Copy)]
204pub struct Convert<'a> {
205 pub keywords: &'a Keywords,
207 pub interner: &'a Interner,
209 pub target: &'a TargetInfo,
211 pub std: Std,
213 pub gnu: bool,
217 pub pedantic: bool,
220}
221
222#[must_use]
228pub fn convert(pp: &[PpToken], cx: &Convert<'_>) -> (Tokens, Vec<Diagnostic>) {
229 let mut out = Tokens { tokens: Vec::with_capacity(pp.len()), ..Tokens::default() };
230 let mut diagnostics = Vec::new();
231 let mut index = 0;
232 while index < pp.len() {
233 index = one(pp, index, cx, &mut out, &mut diagnostics);
234 }
235 if out.tokens.last().is_none_or(|last| !last.is_eof()) {
236 let end =
239 out.tokens.last().map_or(Span::new(0, 0), |last| Span::new(last.span.hi, last.span.hi));
240 out.tokens.push(Token {
241 kind: TokenKind::Eof,
242 flags: TokenFlags::EMPTY,
243 value: 0,
244 span: end,
245 });
246 }
247 (out, diagnostics)
248}
249
250fn one(
256 pp: &[PpToken],
257 index: usize,
258 cx: &Convert<'_>,
259 out: &mut Tokens,
260 diagnostics: &mut Vec<Diagnostic>,
261) -> usize {
262 let token = pp[index];
263 let mut index = index + 1;
264 match token.kind {
265 PpTokenKind::Ident => out.tokens.push(identifier(token, cx)),
266 PpTokenKind::Number => {
267 out.tokens.push(number(token, cx, &mut out.ints, &mut out.floats, diagnostics));
268 }
269 PpTokenKind::CharConst => {
270 out.tokens.push(char_const(token, cx, &mut out.chars, diagnostics));
271 }
272 PpTokenKind::StringLit => {
273 let start = index - 1;
277 while pp.get(index).is_some_and(|next| next.kind == PpTokenKind::StringLit) {
278 index += 1;
279 }
280 let run = &pp[start..index];
281 out.tokens.push(string_lit(run, cx, &mut out.strings, diagnostics));
282 }
283 PpTokenKind::Punct(Punct::Hash)
287 if token.flags.has(TokenFlags::START_OF_LINE)
288 && pp.get(index).is_some_and(|next| is_pragma(*next, cx)) =>
289 {
290 index += 1;
291 let before = u32::try_from(out.tokens.len()).unwrap_or(u32::MAX);
292 let mut line = Tokens::default();
293 while pp.get(index).is_some_and(|next| {
294 !matches!(next.kind, PpTokenKind::Eof) && !next.flags.has(TokenFlags::START_OF_LINE)
295 }) {
296 index = one(pp, index, cx, out, diagnostics);
297 line.tokens.push(out.tokens.pop().expect("one token out"));
298 }
299 out.pragmas.push(Pragma { before, tokens: line.tokens, span: token.span });
300 }
301 PpTokenKind::Punct(punct) => out.tokens.push(Token {
302 kind: TokenKind::Punct(punct),
303 flags: token.flags,
304 value: 0,
305 span: token.span,
306 }),
307 PpTokenKind::Eof => out.tokens.push(Token {
308 kind: TokenKind::Eof,
309 flags: token.flags,
310 value: 0,
311 span: token.span,
312 }),
313 PpTokenKind::Other | PpTokenKind::HeaderName => {
318 let text = spelling(token, cx);
319 diagnostics.push(Diagnostic::error(format!("stray '{text}' in program"), token.span));
320 }
321 }
322 index
323}
324
325fn is_pragma(token: PpToken, cx: &Convert<'_>) -> bool {
328 token.kind == PpTokenKind::Ident && spelling(token, cx) == "pragma"
329}
330
331fn spelling<'a>(token: PpToken, cx: &Convert<'a>) -> &'a str {
333 token.value.map_or("", |symbol| cx.interner.resolve(symbol))
334}
335
336fn spelling_bytes<'a>(token: PpToken, cx: &Convert<'a>) -> &'a [u8] {
342 token.value.map_or(&[][..], |symbol| cx.interner.resolve_bytes(symbol))
343}
344
345fn identifier(token: PpToken, cx: &Convert<'_>) -> Token {
347 let symbol = token.value.expect("an identifier carries its spelling");
348 let kind = match cx.keywords.get(symbol) {
349 Some(word) => TokenKind::Keyword(word),
350 None => TokenKind::Ident,
351 };
352 Token { kind, flags: token.flags, value: symbol.raw(), span: token.span }
353}
354
355fn number(
357 token: PpToken,
358 cx: &Convert<'_>,
359 ints: &mut Vec<IntConstant>,
360 floats: &mut Vec<FloatConstant>,
361 diagnostics: &mut Vec<Diagnostic>,
362) -> Token {
363 let text = spelling(token, cx);
364 match crate::number::integer(text, cx.std, cx.target) {
367 Ok(value) => {
368 report(value.remarks, None, token.span, cx, diagnostics);
369 ints.push(value);
370 let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
371 Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
372 }
373 Err(IntError::Floating) => match crate::number::floating(text, cx.std, cx.target) {
374 Ok(value) => {
375 report(value.remarks, Some(value.ty.name()), token.span, cx, diagnostics);
376 floats.push(value);
377 let index =
378 u32::try_from(floats.len() - 1).expect("that many constants in one file");
379 Token { kind: TokenKind::Float, flags: token.flags, value: index, span: token.span }
380 }
381 Err(error) => {
382 diagnostics.push(Diagnostic::error(error.message(), token.span));
383 floats.push(zero_float(cx));
386 let index =
387 u32::try_from(floats.len() - 1).expect("that many constants in one file");
388 Token { kind: TokenKind::Float, flags: token.flags, value: index, span: token.span }
389 }
390 },
391 Err(error) => {
392 diagnostics.push(Diagnostic::error(error.message(), token.span));
393 ints.push(IntConstant {
394 value: 0,
395 ty: crate::number::IntConstantType::Standard(rucc_types::IntKind::Int),
396 imaginary: false,
397 remarks: Remarks::NONE,
398 });
399 let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
400 Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
401 }
402 }
403}
404
405fn zero_float(cx: &Convert<'_>) -> FloatConstant {
407 let ty = crate::number::FloatConstantType::Double;
408 FloatConstant {
409 value: rucc_base::float::Float::zero(ty.format(cx.target), false),
410 ty,
411 imaginary: false,
412 remarks: Remarks::NONE,
413 }
414}
415
416fn char_const(
418 token: PpToken,
419 cx: &Convert<'_>,
420 chars: &mut Vec<CharConstant>,
421 diagnostics: &mut Vec<Diagnostic>,
422) -> Token {
423 let text = spelling_bytes(token, cx);
424 let value = match crate::literal::character(text, cx.std, cx.gnu, cx.target) {
425 Ok(value) => {
426 report(value.remarks, None, token.span, cx, diagnostics);
427 value
428 }
429 Err(error) => {
430 diagnostics.push(Diagnostic::error(error.message(), token.span));
431 CharConstant {
432 value: 0,
433 encoding: crate::literal::Encoding::Plain,
434 remarks: Remarks::NONE,
435 }
436 }
437 };
438 chars.push(value);
439 let index = u32::try_from(chars.len() - 1).expect("that many constants in one file");
440 Token { kind: TokenKind::Char, flags: token.flags, value: index, span: token.span }
441}
442
443fn string_lit(
445 run: &[PpToken],
446 cx: &Convert<'_>,
447 strings: &mut Vec<StringLiteral>,
448 diagnostics: &mut Vec<Diagnostic>,
449) -> Token {
450 let first = run[0];
451 let span = first.span.to(run[run.len() - 1].span);
452 let texts: Vec<&[u8]> = run.iter().map(|token| spelling_bytes(*token, cx)).collect();
453 let value = match crate::literal::strings(&texts, cx.std, cx.gnu, cx.target) {
454 Ok(value) => {
455 report(value.remarks, None, span, cx, diagnostics);
456 value
457 }
458 Err(error) => {
459 diagnostics.push(Diagnostic::error(error.message(), span));
460 let encoding = if error == LiteralError::MixedEncodings {
463 crate::literal::Encoding::Plain
464 } else {
465 crate::literal::Encoding::read_prefix(texts[0])
466 };
467 StringLiteral { elements: Vec::new(), encoding, remarks: Remarks::NONE }
468 }
469 };
470 strings.push(value);
471 let index = u32::try_from(strings.len() - 1).expect("that many literals in one file");
472 Token { kind: TokenKind::Str, flags: first.flags, value: index, span }
473}
474
475fn report(
481 remarks: Remarks,
482 type_name: Option<&str>,
483 span: Span,
484 cx: &Convert<'_>,
485 diagnostics: &mut Vec<Diagnostic>,
486) {
487 if remarks.is_none() {
488 return;
489 }
490
491 let always: [(Remarks, &str); 6] = [
494 (Remarks::MULTICHARACTER, "multi-character character constant"),
495 (Remarks::TOO_LONG, "character constant too long for its type"),
496 (Remarks::UNKNOWN_ESCAPE, "unknown escape sequence"),
497 (Remarks::HEX_ESCAPE_OUT_OF_RANGE, "hex escape sequence out of range"),
498 (Remarks::OCTAL_ESCAPE_OUT_OF_RANGE, "octal escape sequence out of range"),
499 (Remarks::UNSIGNED, "integer constant is so large that it is unsigned"),
500 ];
501 for (remark, message) in always {
502 if remarks.has(remark) {
503 diagnostics.push(Diagnostic::warning(message, span));
504 }
505 }
506 if remarks.has(Remarks::OUT_OF_RANGE) {
507 let ty = type_name.unwrap_or("double");
508 diagnostics
509 .push(Diagnostic::warning(format!("floating constant exceeds range of '{ty}'"), span));
510 }
511 if remarks.has(Remarks::TRUNCATED) {
512 diagnostics.push(Diagnostic::warning("floating constant truncated to zero", span));
513 }
514
515 if !cx.pedantic {
516 return;
517 }
518 let pedantic: [(Remarks, &str); 9] = [
521 (Remarks::NON_ISO_ESCAPE, "non-ISO-standard escape sequence"),
522 (Remarks::DOUBLE_SUFFIX, "suffix for double constant is a GCC extension"),
523 (Remarks::IMAGINARY, "imaginary constants are a GCC extension"),
524 (Remarks::BINARY, "binary constants are a C23 feature or GCC extension"),
525 (Remarks::EXTENDED_SUFFIX, "non-standard suffix on floating constant"),
526 (Remarks::HEX_FLOAT, "use of C99 hexadecimal floating constant"),
527 (Remarks::LONG_LONG, "use of C99 long long integer constant"),
528 (Remarks::SEPARATORS, "digit separators are a C23 feature"),
529 (Remarks::BIT_INT, "'_BitInt' constants are a C23 feature"),
530 ];
531 for (remark, message) in pedantic {
532 if remarks.has(remark) {
533 diagnostics.push(Diagnostic::warning(message, span));
534 }
535 }
536 if remarks.has(Remarks::UCN) {
537 diagnostics.push(Diagnostic::warning(
538 "universal character names are only valid in C++ and C99",
539 span,
540 ));
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use rucc_target::Triple;
547
548 use super::*;
549 use crate::lexer::{Options, tokenize};
550
551 struct Fixture {
554 interner: Interner,
555 keywords: Keywords,
556 target: TargetInfo,
557 std: Std,
558 gnu: bool,
559 pedantic: bool,
560 }
561
562 impl Fixture {
563 fn new(std: Std) -> Fixture {
564 let mut interner = Interner::new();
565 let keywords = Keywords::new(&mut interner, std, true);
566 let target =
567 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
568 Fixture { interner, keywords, target, std, gnu: false, pedantic: false }
569 }
570
571 fn run(&mut self, src: &str) -> (Tokens, Vec<String>) {
573 let (pp, lex_diagnostics) =
574 tokenize(src.as_bytes(), 0, Options::new(), &mut self.interner);
575 assert!(lex_diagnostics.is_empty(), "the scanner disliked the source: {src}");
576 let cx = Convert {
577 keywords: &self.keywords,
578 interner: &self.interner,
579 target: &self.target,
580 std: self.std,
581 gnu: self.gnu,
582 pedantic: self.pedantic,
583 };
584 let (tokens, diagnostics) = convert(&pp, &cx);
585 (tokens, diagnostics.iter().map(|d| d.message.clone()).collect())
586 }
587 }
588
589 fn kinds(src: &str) -> Vec<TokenKind> {
590 Fixture::new(Std::C23).run(src).0.tokens.iter().map(|t| t.kind).collect()
591 }
592
593 #[test]
594 fn a_token_is_sixteen_bytes() {
595 assert_eq!(size_of::<Token>(), 16);
598 }
599
600 #[test]
601 fn a_declaration_converts_into_keywords_an_identifier_and_a_constant() {
602 assert_eq!(
603 kinds("int x = 1;"),
604 vec![
605 TokenKind::Keyword(Keyword::Int),
606 TokenKind::Ident,
607 TokenKind::Punct(Punct::Eq),
608 TokenKind::Int,
609 TokenKind::Punct(Punct::Semi),
610 TokenKind::Eof,
611 ]
612 );
613 }
614
615 #[test]
617 fn the_dialect_decides_which_identifiers_are_keywords() {
618 let mut c89 = Fixture::new(Std::C89);
619 let (tokens, _) = c89.run("restrict");
620 assert_eq!(tokens.tokens[0].kind, TokenKind::Ident);
621 let mut c99 = Fixture::new(Std::C99);
622 let (tokens, _) = c99.run("restrict");
623 assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::Restrict));
624 }
625
626 #[test]
627 fn a_number_becomes_whichever_kind_of_constant_it_is() {
628 let mut fixture = Fixture::new(Std::C23);
629 let (tokens, diagnostics) = fixture.run("1 2.5 0x1p3 1u");
630 assert!(diagnostics.is_empty());
631 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
632 assert_eq!(
633 kinds,
634 vec![
635 TokenKind::Int,
636 TokenKind::Float,
637 TokenKind::Float,
638 TokenKind::Int,
639 TokenKind::Eof
640 ]
641 );
642 assert_eq!(tokens.int(tokens.tokens[0]).expect("an integer").value, 1);
643 assert!(tokens.float(tokens.tokens[1]).is_some());
644 assert_eq!(tokens.tokens[2].value, 1);
647 assert_eq!(tokens.tokens[3].value, 1);
648 assert_eq!(tokens.int(tokens.tokens[3]).expect("an integer").value, 1);
649 assert!(tokens.float(tokens.tokens[0]).is_none());
651 assert!(tokens.string(tokens.tokens[0]).is_none());
652 }
653
654 #[test]
657 fn adjacent_string_literals_become_one_token() {
658 let mut fixture = Fixture::new(Std::C23);
659 let (tokens, diagnostics) = fixture.run(r#"char *s = "a" "b" L"c";"#);
660 assert!(diagnostics.is_empty(), "{diagnostics:?}");
661 let literal = tokens
662 .tokens
663 .iter()
664 .find(|t| t.kind == TokenKind::Str)
665 .copied()
666 .expect("a string literal");
667 let value = tokens.string(literal).expect("the literal");
668 assert_eq!(value.elements, vec![0x61, 0x62, 0x63]);
669 assert_eq!(value.encoding, crate::literal::Encoding::Wide);
670 assert_eq!(tokens.tokens.iter().filter(|t| t.kind == TokenKind::Str).count(), 1);
671 assert_eq!(literal.span.lo, 10);
673 assert_eq!(literal.span.hi, 22);
674 }
675
676 #[test]
677 fn a_character_constant_carries_its_value_and_its_warning() {
678 let mut fixture = Fixture::new(Std::C23);
679 let (tokens, diagnostics) = fixture.run("'ab'");
680 assert_eq!(diagnostics, vec!["multi-character character constant".to_owned()]);
681 assert_eq!(tokens.character(tokens.tokens[0]).expect("a constant").value, 0x6162);
682 }
683
684 #[test]
687 fn the_warnings_that_need_no_flag_are_given_without_one() {
688 let mut fixture = Fixture::new(Std::C17);
689 let (_, diagnostics) = fixture.run(r"'abcde' '\q' '\x1ff' '\400' 1e400 1e-400");
690 assert_eq!(
691 diagnostics,
692 vec![
693 "character constant too long for its type".to_owned(),
694 "unknown escape sequence".to_owned(),
695 "hex escape sequence out of range".to_owned(),
696 "octal escape sequence out of range".to_owned(),
697 "floating constant exceeds range of 'double'".to_owned(),
698 "floating constant truncated to zero".to_owned(),
699 ]
700 );
701 }
702
703 #[test]
705 fn the_warnings_that_need_pedantic_wait_for_it() {
706 let mut quiet = Fixture::new(Std::C17);
707 let (_, diagnostics) = quiet.run(r"1.0d 1.0i 0b1010 '\e'");
708 assert!(diagnostics.is_empty(), "{diagnostics:?}");
709
710 let mut loud = Fixture::new(Std::C17);
711 loud.pedantic = true;
712 let (_, diagnostics) = loud.run(r"1.0d 1.0i 0b1010 '\e'");
713 assert_eq!(
714 diagnostics,
715 vec![
716 "suffix for double constant is a GCC extension".to_owned(),
717 "imaginary constants are a GCC extension".to_owned(),
718 "binary constants are a C23 feature or GCC extension".to_owned(),
719 "non-ISO-standard escape sequence".to_owned(),
720 ]
721 );
722 }
723
724 #[test]
725 fn the_overflow_warning_names_the_type_the_constant_actually_has() {
726 let mut fixture = Fixture::new(Std::C23);
727 let (_, diagnostics) = fixture.run("1e400f");
728 assert_eq!(diagnostics, vec!["floating constant exceeds range of 'float'".to_owned()]);
729 }
730
731 #[test]
734 fn a_constant_that_will_not_convert_still_leaves_a_token_behind() {
735 let mut fixture = Fixture::new(Std::C23);
736 let (tokens, diagnostics) = fixture.run("int x = 1.2.3;");
737 assert_eq!(diagnostics.len(), 1);
738 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
739 assert_eq!(
740 kinds,
741 vec![
742 TokenKind::Keyword(Keyword::Int),
743 TokenKind::Ident,
744 TokenKind::Punct(Punct::Eq),
745 TokenKind::Float,
746 TokenKind::Punct(Punct::Semi),
747 TokenKind::Eof,
748 ]
749 );
750
751 let mut fixture = Fixture::new(Std::C23);
752 let (tokens, diagnostics) = fixture.run("int x = 42ux;");
753 assert_eq!(diagnostics, vec!["invalid suffix on integer constant".to_owned()]);
754 assert_eq!(tokens.int(tokens.tokens[3]).expect("a stand in").value, 0);
755 }
756
757 #[test]
758 fn a_run_of_literals_with_two_prefixes_is_refused_the_way_gcc_refuses_it() {
759 let mut fixture = Fixture::new(Std::C23);
760 let (tokens, diagnostics) = fixture.run(r#"u"a" L"b""#);
761 assert_eq!(
762 diagnostics,
763 vec!["unsupported non-standard concatenation of string literals".to_owned()]
764 );
765 assert!(tokens.string(tokens.tokens[0]).expect("a stand in").elements.is_empty());
766 }
767
768 #[test]
771 fn a_stray_byte_is_an_error_here_and_nowhere_earlier() {
772 let mut fixture = Fixture::new(Std::C23);
773 let (tokens, diagnostics) = fixture.run("a ` b");
774 assert_eq!(diagnostics, vec!["stray '`' in program".to_owned()]);
775 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
776 assert_eq!(kinds, vec![TokenKind::Ident, TokenKind::Ident, TokenKind::Eof]);
777 }
778
779 #[test]
780 fn the_stream_always_ends_in_end_of_file() {
781 let mut fixture = Fixture::new(Std::C23);
782 let (tokens, _) = fixture.run("");
783 assert_eq!(tokens.tokens.len(), 1);
784 assert!(tokens.tokens[0].is_eof());
785 let (tokens, _) = convert(
787 &[],
788 &Convert {
789 keywords: &fixture.keywords,
790 interner: &fixture.interner,
791 target: &fixture.target,
792 std: fixture.std,
793 gnu: false,
794 pedantic: false,
795 },
796 );
797 assert_eq!(tokens.tokens.len(), 1);
798 assert!(tokens.tokens[0].is_eof());
799 }
800
801 #[test]
802 fn a_token_says_what_it_is_without_the_caller_matching_on_the_kind() {
803 let mut fixture = Fixture::new(Std::C23);
804 let (tokens, _) = fixture.run("int x;");
805 assert_eq!(tokens.tokens[0].keyword(), Some(Keyword::Int));
806 assert_eq!(tokens.tokens[0].ident(), None);
807 assert!(tokens.tokens[1].ident().is_some());
808 assert_eq!(tokens.tokens[2].punct(), Some(Punct::Semi));
809 assert_eq!(tokens.tokens[2].keyword(), None);
810 }
811
812 #[test]
815 fn a_pragma_line_leaves_the_stream_and_is_kept_beside_it() {
816 let mut fixture = Fixture::new(Std::C23);
817 let (tokens, diagnostics) = fixture.run("int a;\n#pragma pack(4)\nint b;");
818 assert!(diagnostics.is_empty(), "{diagnostics:?}");
819 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
820 assert_eq!(
821 kinds,
822 vec![
823 TokenKind::Keyword(Keyword::Int),
824 TokenKind::Ident,
825 TokenKind::Punct(Punct::Semi),
826 TokenKind::Keyword(Keyword::Int),
827 TokenKind::Ident,
828 TokenKind::Punct(Punct::Semi),
829 TokenKind::Eof,
830 ]
831 );
832 assert_eq!(tokens.pragmas.len(), 1);
833 let pragma = &tokens.pragmas[0];
834 assert_eq!(pragma.before, 3);
837 let kinds: Vec<_> = pragma.tokens.iter().map(|t| t.kind).collect();
838 assert_eq!(
839 kinds,
840 vec![
841 TokenKind::Ident,
842 TokenKind::Punct(Punct::LParen),
843 TokenKind::Int,
844 TokenKind::Punct(Punct::RParen),
845 ]
846 );
847 }
848
849 #[test]
852 fn a_pragma_at_either_end_of_the_file_is_still_a_line() {
853 let mut fixture = Fixture::new(Std::C23);
854 let (tokens, diagnostics) = fixture.run("#pragma once\nint a;\n#pragma GCC poison x");
855 assert!(diagnostics.is_empty(), "{diagnostics:?}");
856 assert_eq!(tokens.tokens.len(), 4);
857 assert_eq!(tokens.pragmas.len(), 2);
858 assert_eq!(tokens.pragmas[0].before, 0);
859 assert_eq!(tokens.pragmas[0].tokens.len(), 1);
860 assert_eq!(tokens.pragmas[1].before, 3);
861 assert_eq!(tokens.pragmas[1].tokens.len(), 3);
862 }
863
864 #[test]
867 fn a_hash_that_is_not_a_pragma_is_left_where_it_is() {
868 let mut fixture = Fixture::new(Std::C23);
869 let (tokens, _) = fixture.run("#define x\nint pragma;\n# pragma");
870 assert_eq!(tokens.tokens[0].kind, TokenKind::Punct(Punct::Hash));
871 assert_eq!(tokens.pragmas.len(), 1);
874 }
875
876 #[test]
879 fn the_two_extra_spellings_of_the_wide_integer_are_keywords() {
880 let kinds = kinds("__int128_t a; __uint128_t b;");
881 assert_eq!(kinds[0], TokenKind::Keyword(Keyword::Int128T));
882 assert_eq!(kinds[3], TokenKind::Keyword(Keyword::UInt128T));
883 let mut c89 = Fixture::new(Std::C89);
885 let (tokens, _) = c89.run("__uint128_t");
886 assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::UInt128T));
887 }
888
889 #[test]
892 fn the_flags_come_through_from_the_preprocessing_token() {
893 let mut fixture = Fixture::new(Std::C23);
894 let (tokens, _) = fixture.run("a\n b");
895 assert!(tokens.tokens[0].flags.has(TokenFlags::START_OF_LINE));
896 assert!(tokens.tokens[1].flags.has(TokenFlags::START_OF_LINE));
897 assert!(tokens.tokens[1].flags.has(TokenFlags::LEADING_SPACE));
898 }
899}