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 pedantic: bool,
216}
217
218#[must_use]
224pub fn convert(pp: &[PpToken], cx: &Convert<'_>) -> (Tokens, Vec<Diagnostic>) {
225 let mut out = Tokens { tokens: Vec::with_capacity(pp.len()), ..Tokens::default() };
226 let mut diagnostics = Vec::new();
227 let mut index = 0;
228 while index < pp.len() {
229 index = one(pp, index, cx, &mut out, &mut diagnostics);
230 }
231 if out.tokens.last().is_none_or(|last| !last.is_eof()) {
232 let end =
235 out.tokens.last().map_or(Span::new(0, 0), |last| Span::new(last.span.hi, last.span.hi));
236 out.tokens.push(Token {
237 kind: TokenKind::Eof,
238 flags: TokenFlags::EMPTY,
239 value: 0,
240 span: end,
241 });
242 }
243 (out, diagnostics)
244}
245
246fn one(
252 pp: &[PpToken],
253 index: usize,
254 cx: &Convert<'_>,
255 out: &mut Tokens,
256 diagnostics: &mut Vec<Diagnostic>,
257) -> usize {
258 let token = pp[index];
259 let mut index = index + 1;
260 match token.kind {
261 PpTokenKind::Ident => out.tokens.push(identifier(token, cx)),
262 PpTokenKind::Number => {
263 out.tokens.push(number(token, cx, &mut out.ints, &mut out.floats, diagnostics));
264 }
265 PpTokenKind::CharConst => {
266 out.tokens.push(char_const(token, cx, &mut out.chars, diagnostics));
267 }
268 PpTokenKind::StringLit => {
269 let start = index - 1;
273 while pp.get(index).is_some_and(|next| next.kind == PpTokenKind::StringLit) {
274 index += 1;
275 }
276 let run = &pp[start..index];
277 out.tokens.push(string_lit(run, cx, &mut out.strings, diagnostics));
278 }
279 PpTokenKind::Punct(Punct::Hash)
283 if token.flags.has(TokenFlags::START_OF_LINE)
284 && pp.get(index).is_some_and(|next| is_pragma(*next, cx)) =>
285 {
286 index += 1;
287 let before = u32::try_from(out.tokens.len()).unwrap_or(u32::MAX);
288 let mut line = Tokens::default();
289 while pp.get(index).is_some_and(|next| {
290 !matches!(next.kind, PpTokenKind::Eof) && !next.flags.has(TokenFlags::START_OF_LINE)
291 }) {
292 index = one(pp, index, cx, out, diagnostics);
293 line.tokens.push(out.tokens.pop().expect("one token out"));
294 }
295 out.pragmas.push(Pragma { before, tokens: line.tokens, span: token.span });
296 }
297 PpTokenKind::Punct(punct) => out.tokens.push(Token {
298 kind: TokenKind::Punct(punct),
299 flags: token.flags,
300 value: 0,
301 span: token.span,
302 }),
303 PpTokenKind::Eof => out.tokens.push(Token {
304 kind: TokenKind::Eof,
305 flags: token.flags,
306 value: 0,
307 span: token.span,
308 }),
309 PpTokenKind::Other | PpTokenKind::HeaderName => {
314 let text = spelling(token, cx);
315 diagnostics.push(Diagnostic::error(format!("stray '{text}' in program"), token.span));
316 }
317 }
318 index
319}
320
321fn is_pragma(token: PpToken, cx: &Convert<'_>) -> bool {
324 token.kind == PpTokenKind::Ident && spelling(token, cx) == "pragma"
325}
326
327fn spelling<'a>(token: PpToken, cx: &Convert<'a>) -> &'a str {
329 token.value.map_or("", |symbol| cx.interner.resolve(symbol))
330}
331
332fn identifier(token: PpToken, cx: &Convert<'_>) -> Token {
334 let symbol = token.value.expect("an identifier carries its spelling");
335 let kind = match cx.keywords.get(symbol) {
336 Some(word) => TokenKind::Keyword(word),
337 None => TokenKind::Ident,
338 };
339 Token { kind, flags: token.flags, value: symbol.raw(), span: token.span }
340}
341
342fn number(
344 token: PpToken,
345 cx: &Convert<'_>,
346 ints: &mut Vec<IntConstant>,
347 floats: &mut Vec<FloatConstant>,
348 diagnostics: &mut Vec<Diagnostic>,
349) -> Token {
350 let text = spelling(token, cx);
351 match crate::number::integer(text, cx.std, cx.target) {
354 Ok(value) => {
355 report(value.remarks, None, token.span, cx, diagnostics);
356 ints.push(value);
357 let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
358 Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
359 }
360 Err(IntError::Floating) => match crate::number::floating(text, cx.std, cx.target) {
361 Ok(value) => {
362 report(value.remarks, Some(value.ty.name()), token.span, cx, diagnostics);
363 floats.push(value);
364 let index =
365 u32::try_from(floats.len() - 1).expect("that many constants in one file");
366 Token { kind: TokenKind::Float, flags: token.flags, value: index, span: token.span }
367 }
368 Err(error) => {
369 diagnostics.push(Diagnostic::error(error.message(), token.span));
370 floats.push(zero_float(cx));
373 let index =
374 u32::try_from(floats.len() - 1).expect("that many constants in one file");
375 Token { kind: TokenKind::Float, flags: token.flags, value: index, span: token.span }
376 }
377 },
378 Err(error) => {
379 diagnostics.push(Diagnostic::error(error.message(), token.span));
380 ints.push(IntConstant {
381 value: 0,
382 ty: crate::number::IntConstantType::Standard(rucc_types::IntKind::Int),
383 remarks: Remarks::NONE,
384 });
385 let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
386 Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
387 }
388 }
389}
390
391fn zero_float(cx: &Convert<'_>) -> FloatConstant {
393 let ty = crate::number::FloatConstantType::Double;
394 FloatConstant {
395 value: rucc_base::float::Float::zero(ty.format(cx.target), false),
396 ty,
397 imaginary: false,
398 remarks: Remarks::NONE,
399 }
400}
401
402fn char_const(
404 token: PpToken,
405 cx: &Convert<'_>,
406 chars: &mut Vec<CharConstant>,
407 diagnostics: &mut Vec<Diagnostic>,
408) -> Token {
409 let text = spelling(token, cx);
410 let value = match crate::literal::character(text, cx.std, cx.target) {
411 Ok(value) => {
412 report(value.remarks, None, token.span, cx, diagnostics);
413 value
414 }
415 Err(error) => {
416 diagnostics.push(Diagnostic::error(error.message(), token.span));
417 CharConstant {
418 value: 0,
419 encoding: crate::literal::Encoding::Plain,
420 remarks: Remarks::NONE,
421 }
422 }
423 };
424 chars.push(value);
425 let index = u32::try_from(chars.len() - 1).expect("that many constants in one file");
426 Token { kind: TokenKind::Char, flags: token.flags, value: index, span: token.span }
427}
428
429fn string_lit(
431 run: &[PpToken],
432 cx: &Convert<'_>,
433 strings: &mut Vec<StringLiteral>,
434 diagnostics: &mut Vec<Diagnostic>,
435) -> Token {
436 let first = run[0];
437 let span = first.span.to(run[run.len() - 1].span);
438 let texts: Vec<&str> = run.iter().map(|token| spelling(*token, cx)).collect();
439 let value = match crate::literal::strings(&texts, cx.std, cx.target) {
440 Ok(value) => {
441 report(value.remarks, None, span, cx, diagnostics);
442 value
443 }
444 Err(error) => {
445 diagnostics.push(Diagnostic::error(error.message(), span));
446 let encoding = if error == LiteralError::MixedEncodings {
449 crate::literal::Encoding::Plain
450 } else {
451 crate::literal::Encoding::read_prefix(texts[0])
452 };
453 StringLiteral { elements: Vec::new(), encoding, remarks: Remarks::NONE }
454 }
455 };
456 strings.push(value);
457 let index = u32::try_from(strings.len() - 1).expect("that many literals in one file");
458 Token { kind: TokenKind::Str, flags: first.flags, value: index, span }
459}
460
461fn report(
467 remarks: Remarks,
468 type_name: Option<&str>,
469 span: Span,
470 cx: &Convert<'_>,
471 diagnostics: &mut Vec<Diagnostic>,
472) {
473 if remarks.is_none() {
474 return;
475 }
476
477 let always: [(Remarks, &str); 6] = [
480 (Remarks::MULTICHARACTER, "multi-character character constant"),
481 (Remarks::TOO_LONG, "character constant too long for its type"),
482 (Remarks::UNKNOWN_ESCAPE, "unknown escape sequence"),
483 (Remarks::HEX_ESCAPE_OUT_OF_RANGE, "hex escape sequence out of range"),
484 (Remarks::OCTAL_ESCAPE_OUT_OF_RANGE, "octal escape sequence out of range"),
485 (Remarks::UNSIGNED, "integer constant is so large that it is unsigned"),
486 ];
487 for (remark, message) in always {
488 if remarks.has(remark) {
489 diagnostics.push(Diagnostic::warning(message, span));
490 }
491 }
492 if remarks.has(Remarks::OUT_OF_RANGE) {
493 let ty = type_name.unwrap_or("double");
494 diagnostics
495 .push(Diagnostic::warning(format!("floating constant exceeds range of '{ty}'"), span));
496 }
497 if remarks.has(Remarks::TRUNCATED) {
498 diagnostics.push(Diagnostic::warning("floating constant truncated to zero", span));
499 }
500
501 if !cx.pedantic {
502 return;
503 }
504 let pedantic: [(Remarks, &str); 9] = [
507 (Remarks::NON_ISO_ESCAPE, "non-ISO-standard escape sequence"),
508 (Remarks::DOUBLE_SUFFIX, "suffix for double constant is a GCC extension"),
509 (Remarks::IMAGINARY, "imaginary constants are a GCC extension"),
510 (Remarks::BINARY, "binary constants are a C23 feature or GCC extension"),
511 (Remarks::EXTENDED_SUFFIX, "non-standard suffix on floating constant"),
512 (Remarks::HEX_FLOAT, "use of C99 hexadecimal floating constant"),
513 (Remarks::LONG_LONG, "use of C99 long long integer constant"),
514 (Remarks::SEPARATORS, "digit separators are a C23 feature"),
515 (Remarks::BIT_INT, "'_BitInt' constants are a C23 feature"),
516 ];
517 for (remark, message) in pedantic {
518 if remarks.has(remark) {
519 diagnostics.push(Diagnostic::warning(message, span));
520 }
521 }
522 if remarks.has(Remarks::UCN) {
523 diagnostics.push(Diagnostic::warning(
524 "universal character names are only valid in C++ and C99",
525 span,
526 ));
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use rucc_target::Triple;
533
534 use super::*;
535 use crate::lexer::{Options, tokenize};
536
537 struct Fixture {
540 interner: Interner,
541 keywords: Keywords,
542 target: TargetInfo,
543 std: Std,
544 pedantic: bool,
545 }
546
547 impl Fixture {
548 fn new(std: Std) -> Fixture {
549 let mut interner = Interner::new();
550 let keywords = Keywords::new(&mut interner, std, true);
551 let target =
552 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
553 Fixture { interner, keywords, target, std, pedantic: false }
554 }
555
556 fn run(&mut self, src: &str) -> (Tokens, Vec<String>) {
558 let (pp, lex_diagnostics) =
559 tokenize(src.as_bytes(), 0, Options::new(), &mut self.interner);
560 assert!(lex_diagnostics.is_empty(), "the scanner disliked the source: {src}");
561 let cx = Convert {
562 keywords: &self.keywords,
563 interner: &self.interner,
564 target: &self.target,
565 std: self.std,
566 pedantic: self.pedantic,
567 };
568 let (tokens, diagnostics) = convert(&pp, &cx);
569 (tokens, diagnostics.iter().map(|d| d.message.clone()).collect())
570 }
571 }
572
573 fn kinds(src: &str) -> Vec<TokenKind> {
574 Fixture::new(Std::C23).run(src).0.tokens.iter().map(|t| t.kind).collect()
575 }
576
577 #[test]
578 fn a_token_is_sixteen_bytes() {
579 assert_eq!(size_of::<Token>(), 16);
582 }
583
584 #[test]
585 fn a_declaration_converts_into_keywords_an_identifier_and_a_constant() {
586 assert_eq!(
587 kinds("int x = 1;"),
588 vec![
589 TokenKind::Keyword(Keyword::Int),
590 TokenKind::Ident,
591 TokenKind::Punct(Punct::Eq),
592 TokenKind::Int,
593 TokenKind::Punct(Punct::Semi),
594 TokenKind::Eof,
595 ]
596 );
597 }
598
599 #[test]
601 fn the_dialect_decides_which_identifiers_are_keywords() {
602 let mut c89 = Fixture::new(Std::C89);
603 let (tokens, _) = c89.run("restrict");
604 assert_eq!(tokens.tokens[0].kind, TokenKind::Ident);
605 let mut c99 = Fixture::new(Std::C99);
606 let (tokens, _) = c99.run("restrict");
607 assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::Restrict));
608 }
609
610 #[test]
611 fn a_number_becomes_whichever_kind_of_constant_it_is() {
612 let mut fixture = Fixture::new(Std::C23);
613 let (tokens, diagnostics) = fixture.run("1 2.5 0x1p3 1u");
614 assert!(diagnostics.is_empty());
615 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
616 assert_eq!(
617 kinds,
618 vec![
619 TokenKind::Int,
620 TokenKind::Float,
621 TokenKind::Float,
622 TokenKind::Int,
623 TokenKind::Eof
624 ]
625 );
626 assert_eq!(tokens.int(tokens.tokens[0]).expect("an integer").value, 1);
627 assert!(tokens.float(tokens.tokens[1]).is_some());
628 assert_eq!(tokens.tokens[2].value, 1);
631 assert_eq!(tokens.tokens[3].value, 1);
632 assert_eq!(tokens.int(tokens.tokens[3]).expect("an integer").value, 1);
633 assert!(tokens.float(tokens.tokens[0]).is_none());
635 assert!(tokens.string(tokens.tokens[0]).is_none());
636 }
637
638 #[test]
641 fn adjacent_string_literals_become_one_token() {
642 let mut fixture = Fixture::new(Std::C23);
643 let (tokens, diagnostics) = fixture.run(r#"char *s = "a" "b" L"c";"#);
644 assert!(diagnostics.is_empty(), "{diagnostics:?}");
645 let literal = tokens
646 .tokens
647 .iter()
648 .find(|t| t.kind == TokenKind::Str)
649 .copied()
650 .expect("a string literal");
651 let value = tokens.string(literal).expect("the literal");
652 assert_eq!(value.elements, vec![0x61, 0x62, 0x63]);
653 assert_eq!(value.encoding, crate::literal::Encoding::Wide);
654 assert_eq!(tokens.tokens.iter().filter(|t| t.kind == TokenKind::Str).count(), 1);
655 assert_eq!(literal.span.lo, 10);
657 assert_eq!(literal.span.hi, 22);
658 }
659
660 #[test]
661 fn a_character_constant_carries_its_value_and_its_warning() {
662 let mut fixture = Fixture::new(Std::C23);
663 let (tokens, diagnostics) = fixture.run("'ab'");
664 assert_eq!(diagnostics, vec!["multi-character character constant".to_owned()]);
665 assert_eq!(tokens.character(tokens.tokens[0]).expect("a constant").value, 0x6162);
666 }
667
668 #[test]
671 fn the_warnings_that_need_no_flag_are_given_without_one() {
672 let mut fixture = Fixture::new(Std::C17);
673 let (_, diagnostics) = fixture.run(r"'abcde' '\q' '\x1ff' '\400' 1e400 1e-400");
674 assert_eq!(
675 diagnostics,
676 vec![
677 "character constant too long for its type".to_owned(),
678 "unknown escape sequence".to_owned(),
679 "hex escape sequence out of range".to_owned(),
680 "octal escape sequence out of range".to_owned(),
681 "floating constant exceeds range of 'double'".to_owned(),
682 "floating constant truncated to zero".to_owned(),
683 ]
684 );
685 }
686
687 #[test]
689 fn the_warnings_that_need_pedantic_wait_for_it() {
690 let mut quiet = Fixture::new(Std::C17);
691 let (_, diagnostics) = quiet.run(r"1.0d 1.0i 0b1010 '\e'");
692 assert!(diagnostics.is_empty(), "{diagnostics:?}");
693
694 let mut loud = Fixture::new(Std::C17);
695 loud.pedantic = true;
696 let (_, diagnostics) = loud.run(r"1.0d 1.0i 0b1010 '\e'");
697 assert_eq!(
698 diagnostics,
699 vec![
700 "suffix for double constant is a GCC extension".to_owned(),
701 "imaginary constants are a GCC extension".to_owned(),
702 "binary constants are a C23 feature or GCC extension".to_owned(),
703 "non-ISO-standard escape sequence".to_owned(),
704 ]
705 );
706 }
707
708 #[test]
709 fn the_overflow_warning_names_the_type_the_constant_actually_has() {
710 let mut fixture = Fixture::new(Std::C23);
711 let (_, diagnostics) = fixture.run("1e400f");
712 assert_eq!(diagnostics, vec!["floating constant exceeds range of 'float'".to_owned()]);
713 }
714
715 #[test]
718 fn a_constant_that_will_not_convert_still_leaves_a_token_behind() {
719 let mut fixture = Fixture::new(Std::C23);
720 let (tokens, diagnostics) = fixture.run("int x = 1.2.3;");
721 assert_eq!(diagnostics.len(), 1);
722 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
723 assert_eq!(
724 kinds,
725 vec![
726 TokenKind::Keyword(Keyword::Int),
727 TokenKind::Ident,
728 TokenKind::Punct(Punct::Eq),
729 TokenKind::Float,
730 TokenKind::Punct(Punct::Semi),
731 TokenKind::Eof,
732 ]
733 );
734
735 let mut fixture = Fixture::new(Std::C23);
736 let (tokens, diagnostics) = fixture.run("int x = 42ux;");
737 assert_eq!(diagnostics, vec!["invalid suffix on integer constant".to_owned()]);
738 assert_eq!(tokens.int(tokens.tokens[3]).expect("a stand in").value, 0);
739 }
740
741 #[test]
742 fn a_run_of_literals_with_two_prefixes_is_refused_the_way_gcc_refuses_it() {
743 let mut fixture = Fixture::new(Std::C23);
744 let (tokens, diagnostics) = fixture.run(r#"u"a" L"b""#);
745 assert_eq!(
746 diagnostics,
747 vec!["unsupported non-standard concatenation of string literals".to_owned()]
748 );
749 assert!(tokens.string(tokens.tokens[0]).expect("a stand in").elements.is_empty());
750 }
751
752 #[test]
755 fn a_stray_byte_is_an_error_here_and_nowhere_earlier() {
756 let mut fixture = Fixture::new(Std::C23);
757 let (tokens, diagnostics) = fixture.run("a ` b");
758 assert_eq!(diagnostics, vec!["stray '`' in program".to_owned()]);
759 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
760 assert_eq!(kinds, vec![TokenKind::Ident, TokenKind::Ident, TokenKind::Eof]);
761 }
762
763 #[test]
764 fn the_stream_always_ends_in_end_of_file() {
765 let mut fixture = Fixture::new(Std::C23);
766 let (tokens, _) = fixture.run("");
767 assert_eq!(tokens.tokens.len(), 1);
768 assert!(tokens.tokens[0].is_eof());
769 let (tokens, _) = convert(
771 &[],
772 &Convert {
773 keywords: &fixture.keywords,
774 interner: &fixture.interner,
775 target: &fixture.target,
776 std: fixture.std,
777 pedantic: false,
778 },
779 );
780 assert_eq!(tokens.tokens.len(), 1);
781 assert!(tokens.tokens[0].is_eof());
782 }
783
784 #[test]
785 fn a_token_says_what_it_is_without_the_caller_matching_on_the_kind() {
786 let mut fixture = Fixture::new(Std::C23);
787 let (tokens, _) = fixture.run("int x;");
788 assert_eq!(tokens.tokens[0].keyword(), Some(Keyword::Int));
789 assert_eq!(tokens.tokens[0].ident(), None);
790 assert!(tokens.tokens[1].ident().is_some());
791 assert_eq!(tokens.tokens[2].punct(), Some(Punct::Semi));
792 assert_eq!(tokens.tokens[2].keyword(), None);
793 }
794
795 #[test]
798 fn a_pragma_line_leaves_the_stream_and_is_kept_beside_it() {
799 let mut fixture = Fixture::new(Std::C23);
800 let (tokens, diagnostics) = fixture.run("int a;\n#pragma pack(4)\nint b;");
801 assert!(diagnostics.is_empty(), "{diagnostics:?}");
802 let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
803 assert_eq!(
804 kinds,
805 vec![
806 TokenKind::Keyword(Keyword::Int),
807 TokenKind::Ident,
808 TokenKind::Punct(Punct::Semi),
809 TokenKind::Keyword(Keyword::Int),
810 TokenKind::Ident,
811 TokenKind::Punct(Punct::Semi),
812 TokenKind::Eof,
813 ]
814 );
815 assert_eq!(tokens.pragmas.len(), 1);
816 let pragma = &tokens.pragmas[0];
817 assert_eq!(pragma.before, 3);
820 let kinds: Vec<_> = pragma.tokens.iter().map(|t| t.kind).collect();
821 assert_eq!(
822 kinds,
823 vec![
824 TokenKind::Ident,
825 TokenKind::Punct(Punct::LParen),
826 TokenKind::Int,
827 TokenKind::Punct(Punct::RParen),
828 ]
829 );
830 }
831
832 #[test]
835 fn a_pragma_at_either_end_of_the_file_is_still_a_line() {
836 let mut fixture = Fixture::new(Std::C23);
837 let (tokens, diagnostics) = fixture.run("#pragma once\nint a;\n#pragma GCC poison x");
838 assert!(diagnostics.is_empty(), "{diagnostics:?}");
839 assert_eq!(tokens.tokens.len(), 4);
840 assert_eq!(tokens.pragmas.len(), 2);
841 assert_eq!(tokens.pragmas[0].before, 0);
842 assert_eq!(tokens.pragmas[0].tokens.len(), 1);
843 assert_eq!(tokens.pragmas[1].before, 3);
844 assert_eq!(tokens.pragmas[1].tokens.len(), 3);
845 }
846
847 #[test]
850 fn a_hash_that_is_not_a_pragma_is_left_where_it_is() {
851 let mut fixture = Fixture::new(Std::C23);
852 let (tokens, _) = fixture.run("#define x\nint pragma;\n# pragma");
853 assert_eq!(tokens.tokens[0].kind, TokenKind::Punct(Punct::Hash));
854 assert_eq!(tokens.pragmas.len(), 1);
857 }
858
859 #[test]
862 fn the_two_extra_spellings_of_the_wide_integer_are_keywords() {
863 let kinds = kinds("__int128_t a; __uint128_t b;");
864 assert_eq!(kinds[0], TokenKind::Keyword(Keyword::Int128T));
865 assert_eq!(kinds[3], TokenKind::Keyword(Keyword::UInt128T));
866 let mut c89 = Fixture::new(Std::C89);
868 let (tokens, _) = c89.run("__uint128_t");
869 assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::UInt128T));
870 }
871
872 #[test]
875 fn the_flags_come_through_from_the_preprocessing_token() {
876 let mut fixture = Fixture::new(Std::C23);
877 let (tokens, _) = fixture.run("a\n b");
878 assert!(tokens.tokens[0].flags.has(TokenFlags::START_OF_LINE));
879 assert!(tokens.tokens[1].flags.has(TokenFlags::START_OF_LINE));
880 assert!(tokens.tokens[1].flags.has(TokenFlags::LEADING_SPACE));
881 }
882}