Skip to main content

rucc_lex/
convert.rs

1//! Phase 7: preprocessing tokens become the tokens the parser reads.
2//!
3//! Design: `spec/06-lexer-and-parser.md` section 6.1.
4//!
5//! This is the join between the preprocessor and the parser, and it is the last place where a
6//! spelling means anything. An identifier becomes a keyword when the dialect says that spelling
7//! is one, a preprocessing number becomes a typed value, a literal becomes elements, a run of
8//! adjacent string literals becomes one literal, and everything else is already what it is.
9//!
10//! # Where the values live
11//!
12//! A [`Token`] is sixteen bytes, the same budget a pp-token has and for the same reason: a
13//! large translation unit is tens of millions of them and the parser walks them more than once.
14//! A converted constant does not fit in sixteen bytes, so it does not live in the token. The
15//! token holds a small index and the values live in four vectors beside them, which is also the
16//! shape the parser wants, since it reaches for a constant's value at one node out of a hundred
17//! and reads the kind at every one.
18//!
19//! # Where the warnings come from
20//!
21//! The conversions report what a constant did through [`Remarks`] and never decide that any of
22//! it is a warning, because they do not hold the span. This is the layer that holds it, so this
23//! is where a remark becomes a diagnostic. Which remarks are warnings at all depends on
24//! `-pedantic`, and the split was measured on gcc 13.3 rather than guessed: a multi-character
25//! constant, an escape out of range, an overflowing floating constant and a decimal constant
26//! that came out unsigned are warnings with no flag at all, and the extensions, the escape gcc
27//! invented, the imaginary suffix and everything the dialect does not have yet are quiet until
28//! `-pedantic` asks.
29//!
30//! # What is not here
31//!
32//! Nothing turns a token back into text. `-E` prints pp-tokens, which is the stage before this
33//! one, so a spelling is never reconstructed from a converted value.
34
35use 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/// What a token is.
47///
48/// Two bytes, so that a [`Token`] fits in sixteen. The categories that carry a value carry it
49/// in the token's `value` field instead of in the variant, which is what keeps it that small.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum TokenKind {
52    /// A keyword in this dialect.
53    Keyword(Keyword),
54    /// An identifier, whose symbol is in [`Token::value`].
55    Ident,
56    /// An integer constant, indexed by [`Token::value`] into [`Tokens::ints`].
57    Int,
58    /// A floating constant, indexed by [`Token::value`] into [`Tokens::floats`].
59    Float,
60    /// A character constant, indexed by [`Token::value`] into [`Tokens::chars`].
61    Char,
62    /// A string literal, indexed by [`Token::value`] into [`Tokens::strings`]. One token per run
63    /// of adjacent literals, because that is one literal.
64    Str,
65    /// A punctuator.
66    Punct(Punct),
67    /// End of the translation unit.
68    Eof,
69}
70
71/// One token, as the parser reads it.
72///
73/// Sixteen bytes, checked by a test below, and laid out the way [`PpToken`] is for the same
74/// reason.
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub struct Token {
77    /// What it is.
78    pub kind: TokenKind,
79    /// Whether it started a line and whether anything came before it, carried through from the
80    /// pp-token so that a diagnostic can say "did you mean to write this on its own line".
81    pub flags: TokenFlags,
82    /// The symbol for an identifier, the index into the matching vector of [`Tokens`] for a
83    /// constant or a literal, and zero for everything else.
84    pub value: u32,
85    /// Where it came from, which for a run of string literals covers the whole run.
86    pub span: Span,
87}
88
89impl Token {
90    /// Whether this is the end of the translation unit.
91    #[inline]
92    #[must_use]
93    pub const fn is_eof(self) -> bool {
94        matches!(self.kind, TokenKind::Eof)
95    }
96
97    /// The keyword, when this is one.
98    #[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    /// The punctuator, when this is one.
108    #[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    /// The identifier's symbol, when this is one.
118    #[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/// The tokens of a translation unit, with the values they refer to.
129#[derive(Debug, Default)]
130pub struct Tokens {
131    /// The tokens themselves, ending in one [`TokenKind::Eof`].
132    pub tokens: Vec<Token>,
133    /// The integer constants, in the order they were converted.
134    pub ints: Vec<IntConstant>,
135    /// The floating constants, in the order they were converted.
136    pub floats: Vec<FloatConstant>,
137    /// The character constants, in the order they were converted.
138    pub chars: Vec<CharConstant>,
139    /// The string literals, one per run of adjacent ones.
140    pub strings: Vec<StringLiteral>,
141    /// The `#pragma` lines, in the order they were written.
142    pub pragmas: Vec<Pragma>,
143}
144
145/// One `#pragma` line, and where in the token stream it stood.
146///
147/// The line is kept beside the tokens rather than in them. A pragma can appear anywhere a
148/// line can, which is between any two tokens at all, so leaving it in the stream would mean
149/// every rule in the parser had to know about a token that is not part of any grammar. What a
150/// consumer needs instead is where it was, and [`Pragma::before`] is that.
151///
152/// Nothing acts on one yet. The record is here so that when something does, `#pragma pack`
153/// most likely, the position it has to be applied at has not already been thrown away.
154#[derive(Debug, Clone)]
155pub struct Pragma {
156    /// The index in [`Tokens::tokens`] of the token this line came before.
157    pub before: u32,
158    /// The tokens of the line, with the `#` and the `pragma` taken off.
159    pub tokens: Vec<Token>,
160    /// The `#`, which is where a diagnostic about the line points.
161    pub span: Span,
162}
163
164impl Tokens {
165    /// The integer constant `token` refers to, and [`None`] when it is not an integer constant.
166    #[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    /// The floating constant `token` refers to, and [`None`] when it is not one.
175    #[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    /// The character constant `token` refers to, and [`None`] when it is not one.
184    #[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    /// The string literal `token` refers to, and [`None`] when it is not one.
193    #[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/// Everything phase 7 needs that is not the tokens.
203#[derive(Debug, Clone, Copy)]
204pub struct Convert<'a> {
205    /// The keyword table, built for this dialect before any source was read.
206    pub keywords: &'a Keywords,
207    /// Where the spellings are, since a pp-token carries a symbol rather than text.
208    pub interner: &'a Interner,
209    /// The target, which decides what a constant's type is and what a wide element is.
210    pub target: &'a TargetInfo,
211    /// The dialect, which decides what is a keyword and what earns a remark.
212    pub std: Std,
213    /// Whether the GNU extensions are on, which is `-std=gnu17` rather than `-std=c17`. gcc
214    /// offers the C11 encoding prefixes from gnu99 on, so this decides whether `u8"x"` in a
215    /// C99 program is a literal or an identifier next to one.
216    pub gnu: bool,
217    /// Whether `-pedantic` is on, which is the difference between a remark that is a warning
218    /// and one that is nothing at all.
219    pub pedantic: bool,
220}
221
222/// Converts a stream of preprocessing tokens into tokens.
223///
224/// The stream is what came out of macro expansion, so it has no directives left in it. A
225/// spelling that will not convert produces a diagnostic and a token that stands in for it, so
226/// that one bad constant does not cost the rest of the file its parse.
227#[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        // Every caller of this ends up indexing past the last real token, so the stream always
237        // ends in one of these even when the input did not.
238        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
250/// Converts the pp-token at `index`, appends what it became, and answers where the next one
251/// starts.
252///
253/// One call is one token out, except for a run of adjacent string literals, which is one
254/// literal, and for a `#pragma` line, which is one token followed by the line's own tokens.
255fn 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            // A run of adjacent literals is one literal, so the run is taken here rather
274            // than left for the parser, which would have to know the encoding rules to do
275            // it and would be the second place that knows them.
276            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        // `# pragma` at the start of a line. The preprocessor leaves these in the stream on
284        // purpose, because what a pragma means is not its business, and this is where the
285        // line stops being a `#` the parser would choke on and becomes a record of its own.
286        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        // A stray byte is a legal pp-token and never a token, and a header name cannot get
314        // here at all, because only a directive asks for one and no directive survives to
315        // this point. Both are reported and dropped, since there is nothing to stand in
316        // for either of them.
317        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
325/// Whether a pp-token is the word `pragma`, which is the only thing a `#` at the start of a
326/// line can be followed by this late: every other directive was acted on and removed.
327fn is_pragma(token: PpToken, cx: &Convert<'_>) -> bool {
328    token.kind == PpTokenKind::Ident && spelling(token, cx) == "pragma"
329}
330
331/// The spelling of a pp-token that has one.
332fn spelling<'a>(token: PpToken, cx: &Convert<'a>) -> &'a str {
333    token.value.map_or("", |symbol| cx.interner.resolve(symbol))
334}
335
336/// The spelling of a token as the bytes it was written with.
337///
338/// A literal is what this is for. Its body does not have to be text, so a `char c[] = "\xff";`
339/// written with the byte itself has a spelling that is not UTF-8 and an object one byte long,
340/// and reading it as text would give it three.
341fn spelling_bytes<'a>(token: PpToken, cx: &Convert<'a>) -> &'a [u8] {
342    token.value.map_or(&[][..], |symbol| cx.interner.resolve_bytes(symbol))
343}
344
345/// An identifier, which the dialect may have made a keyword.
346fn 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
355/// A preprocessing number, which is an integer constant, a floating one, or neither.
356fn 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    // Which of the two it is, is the integer path's answer rather than a guess made here: the
365    // grammars overlap at the front and only one of them can tell where the number stops.
366    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                // A zero of the right shape, so that the expression around it still parses and
384                // the user sees the one error they made rather than the ten it caused.
385                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                remarks: Remarks::NONE,
397            });
398            let index = u32::try_from(ints.len() - 1).expect("that many constants in one file");
399            Token { kind: TokenKind::Int, flags: token.flags, value: index, span: token.span }
400        }
401    }
402}
403
404/// The `0.0` that stands in for a floating constant that would not convert.
405fn zero_float(cx: &Convert<'_>) -> FloatConstant {
406    let ty = crate::number::FloatConstantType::Double;
407    FloatConstant {
408        value: rucc_base::float::Float::zero(ty.format(cx.target), false),
409        ty,
410        imaginary: false,
411        remarks: Remarks::NONE,
412    }
413}
414
415/// A character constant.
416fn char_const(
417    token: PpToken,
418    cx: &Convert<'_>,
419    chars: &mut Vec<CharConstant>,
420    diagnostics: &mut Vec<Diagnostic>,
421) -> Token {
422    let text = spelling_bytes(token, cx);
423    let value = match crate::literal::character(text, cx.std, cx.gnu, cx.target) {
424        Ok(value) => {
425            report(value.remarks, None, token.span, cx, diagnostics);
426            value
427        }
428        Err(error) => {
429            diagnostics.push(Diagnostic::error(error.message(), token.span));
430            CharConstant {
431                value: 0,
432                encoding: crate::literal::Encoding::Plain,
433                remarks: Remarks::NONE,
434            }
435        }
436    };
437    chars.push(value);
438    let index = u32::try_from(chars.len() - 1).expect("that many constants in one file");
439    Token { kind: TokenKind::Char, flags: token.flags, value: index, span: token.span }
440}
441
442/// A run of adjacent string literals, which is one literal.
443fn string_lit(
444    run: &[PpToken],
445    cx: &Convert<'_>,
446    strings: &mut Vec<StringLiteral>,
447    diagnostics: &mut Vec<Diagnostic>,
448) -> Token {
449    let first = run[0];
450    let span = first.span.to(run[run.len() - 1].span);
451    let texts: Vec<&[u8]> = run.iter().map(|token| spelling_bytes(*token, cx)).collect();
452    let value = match crate::literal::strings(&texts, cx.std, cx.gnu, cx.target) {
453        Ok(value) => {
454            report(value.remarks, None, span, cx, diagnostics);
455            value
456        }
457        Err(error) => {
458            diagnostics.push(Diagnostic::error(error.message(), span));
459            // An empty literal of the encoding the run asked for, if it managed to agree on
460            // one, so that a `char *` initialised from it is still a `char *`.
461            let encoding = if error == LiteralError::MixedEncodings {
462                crate::literal::Encoding::Plain
463            } else {
464                crate::literal::Encoding::read_prefix(texts[0])
465            };
466            StringLiteral { elements: Vec::new(), encoding, remarks: Remarks::NONE }
467        }
468    };
469    strings.push(value);
470    let index = u32::try_from(strings.len() - 1).expect("that many literals in one file");
471    Token { kind: TokenKind::Str, flags: first.flags, value: index, span }
472}
473
474/// Turns the remarks a conversion made into the diagnostics this dialect wants.
475///
476/// `type_name` is the type a floating constant came out as, which the overflow wording names.
477/// The split between the warnings that need `-pedantic` and the ones that do not was measured
478/// on gcc 13.3 rather than guessed at.
479fn report(
480    remarks: Remarks,
481    type_name: Option<&str>,
482    span: Span,
483    cx: &Convert<'_>,
484    diagnostics: &mut Vec<Diagnostic>,
485) {
486    if remarks.is_none() {
487        return;
488    }
489
490    // On by default in gcc, because every one of these is a value that is not what the source
491    // looks like it says.
492    let always: [(Remarks, &str); 6] = [
493        (Remarks::MULTICHARACTER, "multi-character character constant"),
494        (Remarks::TOO_LONG, "character constant too long for its type"),
495        (Remarks::UNKNOWN_ESCAPE, "unknown escape sequence"),
496        (Remarks::HEX_ESCAPE_OUT_OF_RANGE, "hex escape sequence out of range"),
497        (Remarks::OCTAL_ESCAPE_OUT_OF_RANGE, "octal escape sequence out of range"),
498        (Remarks::UNSIGNED, "integer constant is so large that it is unsigned"),
499    ];
500    for (remark, message) in always {
501        if remarks.has(remark) {
502            diagnostics.push(Diagnostic::warning(message, span));
503        }
504    }
505    if remarks.has(Remarks::OUT_OF_RANGE) {
506        let ty = type_name.unwrap_or("double");
507        diagnostics
508            .push(Diagnostic::warning(format!("floating constant exceeds range of '{ty}'"), span));
509    }
510    if remarks.has(Remarks::TRUNCATED) {
511        diagnostics.push(Diagnostic::warning("floating constant truncated to zero", span));
512    }
513
514    if !cx.pedantic {
515        return;
516    }
517    // Quiet without `-pedantic`, because each of these is a value the compiler understood
518    // perfectly well and only the standard objects to.
519    let pedantic: [(Remarks, &str); 9] = [
520        (Remarks::NON_ISO_ESCAPE, "non-ISO-standard escape sequence"),
521        (Remarks::DOUBLE_SUFFIX, "suffix for double constant is a GCC extension"),
522        (Remarks::IMAGINARY, "imaginary constants are a GCC extension"),
523        (Remarks::BINARY, "binary constants are a C23 feature or GCC extension"),
524        (Remarks::EXTENDED_SUFFIX, "non-standard suffix on floating constant"),
525        (Remarks::HEX_FLOAT, "use of C99 hexadecimal floating constant"),
526        (Remarks::LONG_LONG, "use of C99 long long integer constant"),
527        (Remarks::SEPARATORS, "digit separators are a C23 feature"),
528        (Remarks::BIT_INT, "'_BitInt' constants are a C23 feature"),
529    ];
530    for (remark, message) in pedantic {
531        if remarks.has(remark) {
532            diagnostics.push(Diagnostic::warning(message, span));
533        }
534    }
535    if remarks.has(Remarks::UCN) {
536        diagnostics.push(Diagnostic::warning(
537            "universal character names are only valid in C++ and C99",
538            span,
539        ));
540    }
541}
542
543#[cfg(test)]
544mod tests {
545    use rucc_target::Triple;
546
547    use super::*;
548    use crate::lexer::{Options, tokenize};
549
550    /// Everything a conversion needs, built the way a driver would build it: the keyword table
551    /// first, before any source has been interned.
552    struct Fixture {
553        interner: Interner,
554        keywords: Keywords,
555        target: TargetInfo,
556        std: Std,
557        gnu: bool,
558        pedantic: bool,
559    }
560
561    impl Fixture {
562        fn new(std: Std) -> Fixture {
563            let mut interner = Interner::new();
564            let keywords = Keywords::new(&mut interner, std, true);
565            let target =
566                TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
567            Fixture { interner, keywords, target, std, gnu: false, pedantic: false }
568        }
569
570        /// Lexes and converts `src`, which is what a translation unit with no directives does.
571        fn run(&mut self, src: &str) -> (Tokens, Vec<String>) {
572            let (pp, lex_diagnostics) =
573                tokenize(src.as_bytes(), 0, Options::new(), &mut self.interner);
574            assert!(lex_diagnostics.is_empty(), "the scanner disliked the source: {src}");
575            let cx = Convert {
576                keywords: &self.keywords,
577                interner: &self.interner,
578                target: &self.target,
579                std: self.std,
580                gnu: self.gnu,
581                pedantic: self.pedantic,
582            };
583            let (tokens, diagnostics) = convert(&pp, &cx);
584            (tokens, diagnostics.iter().map(|d| d.message.clone()).collect())
585        }
586    }
587
588    fn kinds(src: &str) -> Vec<TokenKind> {
589        Fixture::new(Std::C23).run(src).0.tokens.iter().map(|t| t.kind).collect()
590    }
591
592    #[test]
593    fn a_token_is_sixteen_bytes() {
594        // The same budget a pp-token has, and for the same reason: a large translation unit
595        // holds tens of millions of these and the parser walks them more than once.
596        assert_eq!(size_of::<Token>(), 16);
597    }
598
599    #[test]
600    fn a_declaration_converts_into_keywords_an_identifier_and_a_constant() {
601        assert_eq!(
602            kinds("int x = 1;"),
603            vec![
604                TokenKind::Keyword(Keyword::Int),
605                TokenKind::Ident,
606                TokenKind::Punct(Punct::Eq),
607                TokenKind::Int,
608                TokenKind::Punct(Punct::Semi),
609                TokenKind::Eof,
610            ]
611        );
612    }
613
614    /// Which spellings are keywords is the dialect's business, and phase 7 is where it lands.
615    #[test]
616    fn the_dialect_decides_which_identifiers_are_keywords() {
617        let mut c89 = Fixture::new(Std::C89);
618        let (tokens, _) = c89.run("restrict");
619        assert_eq!(tokens.tokens[0].kind, TokenKind::Ident);
620        let mut c99 = Fixture::new(Std::C99);
621        let (tokens, _) = c99.run("restrict");
622        assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::Restrict));
623    }
624
625    #[test]
626    fn a_number_becomes_whichever_kind_of_constant_it_is() {
627        let mut fixture = Fixture::new(Std::C23);
628        let (tokens, diagnostics) = fixture.run("1 2.5 0x1p3 1u");
629        assert!(diagnostics.is_empty());
630        let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
631        assert_eq!(
632            kinds,
633            vec![
634                TokenKind::Int,
635                TokenKind::Float,
636                TokenKind::Float,
637                TokenKind::Int,
638                TokenKind::Eof
639            ]
640        );
641        assert_eq!(tokens.int(tokens.tokens[0]).expect("an integer").value, 1);
642        assert!(tokens.float(tokens.tokens[1]).is_some());
643        // The index is into the vector for that kind, so the second float is the second entry
644        // and the second integer is not.
645        assert_eq!(tokens.tokens[2].value, 1);
646        assert_eq!(tokens.tokens[3].value, 1);
647        assert_eq!(tokens.int(tokens.tokens[3]).expect("an integer").value, 1);
648        // Asking the wrong kind for its value gets nothing rather than the wrong constant.
649        assert!(tokens.float(tokens.tokens[0]).is_none());
650        assert!(tokens.string(tokens.tokens[0]).is_none());
651    }
652
653    /// A run of adjacent literals is one token, because it is one object, and the span covers
654    /// all of it so that a diagnostic underlines the whole thing.
655    #[test]
656    fn adjacent_string_literals_become_one_token() {
657        let mut fixture = Fixture::new(Std::C23);
658        let (tokens, diagnostics) = fixture.run(r#"char *s = "a" "b" L"c";"#);
659        assert!(diagnostics.is_empty(), "{diagnostics:?}");
660        let literal = tokens
661            .tokens
662            .iter()
663            .find(|t| t.kind == TokenKind::Str)
664            .copied()
665            .expect("a string literal");
666        let value = tokens.string(literal).expect("the literal");
667        assert_eq!(value.elements, vec![0x61, 0x62, 0x63]);
668        assert_eq!(value.encoding, crate::literal::Encoding::Wide);
669        assert_eq!(tokens.tokens.iter().filter(|t| t.kind == TokenKind::Str).count(), 1);
670        // The span runs from the first quote to the last.
671        assert_eq!(literal.span.lo, 10);
672        assert_eq!(literal.span.hi, 22);
673    }
674
675    #[test]
676    fn a_character_constant_carries_its_value_and_its_warning() {
677        let mut fixture = Fixture::new(Std::C23);
678        let (tokens, diagnostics) = fixture.run("'ab'");
679        assert_eq!(diagnostics, vec!["multi-character character constant".to_owned()]);
680        assert_eq!(tokens.character(tokens.tokens[0]).expect("a constant").value, 0x6162);
681    }
682
683    /// Measured on gcc 13.3: these are warnings with no flag at all, because each one is a
684    /// value that is not what the source looks like it says.
685    #[test]
686    fn the_warnings_that_need_no_flag_are_given_without_one() {
687        let mut fixture = Fixture::new(Std::C17);
688        let (_, diagnostics) = fixture.run(r"'abcde' '\q' '\x1ff' '\400' 1e400 1e-400");
689        assert_eq!(
690            diagnostics,
691            vec![
692                "character constant too long for its type".to_owned(),
693                "unknown escape sequence".to_owned(),
694                "hex escape sequence out of range".to_owned(),
695                "octal escape sequence out of range".to_owned(),
696                "floating constant exceeds range of 'double'".to_owned(),
697                "floating constant truncated to zero".to_owned(),
698            ]
699        );
700    }
701
702    /// And these are quiet until `-pedantic` asks, which was measured the same way.
703    #[test]
704    fn the_warnings_that_need_pedantic_wait_for_it() {
705        let mut quiet = Fixture::new(Std::C17);
706        let (_, diagnostics) = quiet.run(r"1.0d 1.0i 0b1010 '\e'");
707        assert!(diagnostics.is_empty(), "{diagnostics:?}");
708
709        let mut loud = Fixture::new(Std::C17);
710        loud.pedantic = true;
711        let (_, diagnostics) = loud.run(r"1.0d 1.0i 0b1010 '\e'");
712        assert_eq!(
713            diagnostics,
714            vec![
715                "suffix for double constant is a GCC extension".to_owned(),
716                "imaginary constants are a GCC extension".to_owned(),
717                "binary constants are a C23 feature or GCC extension".to_owned(),
718                "non-ISO-standard escape sequence".to_owned(),
719            ]
720        );
721    }
722
723    #[test]
724    fn the_overflow_warning_names_the_type_the_constant_actually_has() {
725        let mut fixture = Fixture::new(Std::C23);
726        let (_, diagnostics) = fixture.run("1e400f");
727        assert_eq!(diagnostics, vec!["floating constant exceeds range of 'float'".to_owned()]);
728    }
729
730    /// One bad constant costs one diagnostic and nothing else, because the token it stands in
731    /// for is still there and the rest of the declaration still parses.
732    #[test]
733    fn a_constant_that_will_not_convert_still_leaves_a_token_behind() {
734        let mut fixture = Fixture::new(Std::C23);
735        let (tokens, diagnostics) = fixture.run("int x = 1.2.3;");
736        assert_eq!(diagnostics.len(), 1);
737        let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
738        assert_eq!(
739            kinds,
740            vec![
741                TokenKind::Keyword(Keyword::Int),
742                TokenKind::Ident,
743                TokenKind::Punct(Punct::Eq),
744                TokenKind::Float,
745                TokenKind::Punct(Punct::Semi),
746                TokenKind::Eof,
747            ]
748        );
749
750        let mut fixture = Fixture::new(Std::C23);
751        let (tokens, diagnostics) = fixture.run("int x = 42ux;");
752        assert_eq!(diagnostics, vec!["invalid suffix on integer constant".to_owned()]);
753        assert_eq!(tokens.int(tokens.tokens[3]).expect("a stand in").value, 0);
754    }
755
756    #[test]
757    fn a_run_of_literals_with_two_prefixes_is_refused_the_way_gcc_refuses_it() {
758        let mut fixture = Fixture::new(Std::C23);
759        let (tokens, diagnostics) = fixture.run(r#"u"a" L"b""#);
760        assert_eq!(
761            diagnostics,
762            vec!["unsupported non-standard concatenation of string literals".to_owned()]
763        );
764        assert!(tokens.string(tokens.tokens[0]).expect("a stand in").elements.is_empty());
765    }
766
767    /// A stray byte is a legal pp-token, so the scanner passes it through and this is the layer
768    /// that has to say no.
769    #[test]
770    fn a_stray_byte_is_an_error_here_and_nowhere_earlier() {
771        let mut fixture = Fixture::new(Std::C23);
772        let (tokens, diagnostics) = fixture.run("a ` b");
773        assert_eq!(diagnostics, vec!["stray '`' in program".to_owned()]);
774        let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
775        assert_eq!(kinds, vec![TokenKind::Ident, TokenKind::Ident, TokenKind::Eof]);
776    }
777
778    #[test]
779    fn the_stream_always_ends_in_end_of_file() {
780        let mut fixture = Fixture::new(Std::C23);
781        let (tokens, _) = fixture.run("");
782        assert_eq!(tokens.tokens.len(), 1);
783        assert!(tokens.tokens[0].is_eof());
784        // Even when the input had none, which is what a caller building a stream by hand does.
785        let (tokens, _) = convert(
786            &[],
787            &Convert {
788                keywords: &fixture.keywords,
789                interner: &fixture.interner,
790                target: &fixture.target,
791                std: fixture.std,
792                gnu: false,
793                pedantic: false,
794            },
795        );
796        assert_eq!(tokens.tokens.len(), 1);
797        assert!(tokens.tokens[0].is_eof());
798    }
799
800    #[test]
801    fn a_token_says_what_it_is_without_the_caller_matching_on_the_kind() {
802        let mut fixture = Fixture::new(Std::C23);
803        let (tokens, _) = fixture.run("int x;");
804        assert_eq!(tokens.tokens[0].keyword(), Some(Keyword::Int));
805        assert_eq!(tokens.tokens[0].ident(), None);
806        assert!(tokens.tokens[1].ident().is_some());
807        assert_eq!(tokens.tokens[2].punct(), Some(Punct::Semi));
808        assert_eq!(tokens.tokens[2].keyword(), None);
809    }
810
811    /// The preprocessor leaves a `#pragma` line alone on purpose, so this is the only layer
812    /// that can take it out, and a `#` reaching the parser is a syntax error every time.
813    #[test]
814    fn a_pragma_line_leaves_the_stream_and_is_kept_beside_it() {
815        let mut fixture = Fixture::new(Std::C23);
816        let (tokens, diagnostics) = fixture.run("int a;\n#pragma pack(4)\nint b;");
817        assert!(diagnostics.is_empty(), "{diagnostics:?}");
818        let kinds: Vec<_> = tokens.tokens.iter().map(|t| t.kind).collect();
819        assert_eq!(
820            kinds,
821            vec![
822                TokenKind::Keyword(Keyword::Int),
823                TokenKind::Ident,
824                TokenKind::Punct(Punct::Semi),
825                TokenKind::Keyword(Keyword::Int),
826                TokenKind::Ident,
827                TokenKind::Punct(Punct::Semi),
828                TokenKind::Eof,
829            ]
830        );
831        assert_eq!(tokens.pragmas.len(), 1);
832        let pragma = &tokens.pragmas[0];
833        // Three tokens in and three to go, which is the second declaration, which is the one
834        // a `#pragma pack` here would have to apply to.
835        assert_eq!(pragma.before, 3);
836        let kinds: Vec<_> = pragma.tokens.iter().map(|t| t.kind).collect();
837        assert_eq!(
838            kinds,
839            vec![
840                TokenKind::Ident,
841                TokenKind::Punct(Punct::LParen),
842                TokenKind::Int,
843                TokenKind::Punct(Punct::RParen),
844            ]
845        );
846    }
847
848    /// The two ends of a file are where an off-by-one in the line loop shows up, so both are
849    /// here: nothing before the first pragma, and nothing after the last.
850    #[test]
851    fn a_pragma_at_either_end_of_the_file_is_still_a_line() {
852        let mut fixture = Fixture::new(Std::C23);
853        let (tokens, diagnostics) = fixture.run("#pragma once\nint a;\n#pragma GCC poison x");
854        assert!(diagnostics.is_empty(), "{diagnostics:?}");
855        assert_eq!(tokens.tokens.len(), 4);
856        assert_eq!(tokens.pragmas.len(), 2);
857        assert_eq!(tokens.pragmas[0].before, 0);
858        assert_eq!(tokens.pragmas[0].tokens.len(), 1);
859        assert_eq!(tokens.pragmas[1].before, 3);
860        assert_eq!(tokens.pragmas[1].tokens.len(), 3);
861    }
862
863    /// A `#` that is not a pragma is still a token, because at this point every directive has
864    /// already been acted on and anything left is the program's own mistake to hear about.
865    #[test]
866    fn a_hash_that_is_not_a_pragma_is_left_where_it_is() {
867        let mut fixture = Fixture::new(Std::C23);
868        let (tokens, _) = fixture.run("#define x\nint pragma;\n# pragma");
869        assert_eq!(tokens.tokens[0].kind, TokenKind::Punct(Punct::Hash));
870        // The word alone is an identifier, and a `#` in the middle of a line is not a
871        // directive, so the only pragma here is the one written as one.
872        assert_eq!(tokens.pragmas.len(), 1);
873    }
874
875    /// gcc's own spellings of the 128 bit types, which are typedef names everywhere else and
876    /// keywords here because there is nowhere to write the typedef.
877    #[test]
878    fn the_two_extra_spellings_of_the_wide_integer_are_keywords() {
879        let kinds = kinds("__int128_t a; __uint128_t b;");
880        assert_eq!(kinds[0], TokenKind::Keyword(Keyword::Int128T));
881        assert_eq!(kinds[3], TokenKind::Keyword(Keyword::UInt128T));
882        // Not a dialect question. gcc offers them at every level and so do we.
883        let mut c89 = Fixture::new(Std::C89);
884        let (tokens, _) = c89.run("__uint128_t");
885        assert_eq!(tokens.tokens[0].kind, TokenKind::Keyword(Keyword::UInt128T));
886    }
887
888    /// The flags survive the conversion, because a diagnostic about a token that should have
889    /// been on its own line needs to know that it was not.
890    #[test]
891    fn the_flags_come_through_from_the_preprocessing_token() {
892        let mut fixture = Fixture::new(Std::C23);
893        let (tokens, _) = fixture.run("a\n b");
894        assert!(tokens.tokens[0].flags.has(TokenFlags::START_OF_LINE));
895        assert!(tokens.tokens[1].flags.has(TokenFlags::START_OF_LINE));
896        assert!(tokens.tokens[1].flags.has(TokenFlags::LEADING_SPACE));
897    }
898}