Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Parser/lexer/layout.c
Line
Count
Source
1
#include "Python.h"
2
#include "errcode.h"
3
#include "pycore_token.h"
4
5
#include "lexer_internal.h"
6
#include "../tokenizer/helpers.h"
7
#include "../tokenizer/reader.h"
8
9
1.05k
#define TABSIZE 8
10
1.05k
#define ALTTABSIZE 1
11
12
int
13
_PyLexer_ContinueLine(struct tok_state *tok)
14
1.27k
{
15
1.27k
    int c = tok_nextc(tok);
16
1.27k
    if (c == '\r') {
17
0
        c = tok_nextc(tok);
18
0
    }
19
1.27k
    if (c != '\n') {
20
69
        tok->done = E_LINECONT;
21
69
        return -1;
22
69
    }
23
1.20k
    c = tok_nextc(tok);
24
1.20k
    if (tok->implicit_newline == _PyTokenizer_APPEND_NEWLINE) {
25
0
        tok->needs_newline |= _PyTokenizer_NEWLINE_CONTINUATION;
26
0
    }
27
1.20k
    if (c == EOF) {
28
51
        tok->done = E_EOF;
29
51
        tok->cur = tok->inp;
30
51
        return -1;
31
1.15k
    } else {
32
1.15k
        tok_backup(tok, c);
33
1.15k
    }
34
1.15k
    return c;
35
1.20k
}
36
37
38
static int
39
update_indentation(struct tok_state *tok, int col, int altcol)
40
210k
{
41
210k
    lexer_layout_state *layout = &tok->layout;
42
210k
    if (col == layout->stack[layout->depth].column) {
43
193k
        if (altcol != layout->stack[layout->depth].alternate_column) {
44
1
            _PyTokenizer_indenterror(tok);
45
1
            return -1;
46
1
        }
47
193k
    }
48
17.5k
    else if (col > layout->stack[layout->depth].column) {
49
9.64k
        if (layout->depth + 1 >= MAXINDENT) {
50
0
            tok->done = E_TOODEEP;
51
0
            tok->cur = tok->inp;
52
0
            return -1;
53
0
        }
54
9.64k
        if (altcol <= layout->stack[layout->depth].alternate_column) {
55
1
            _PyTokenizer_indenterror(tok);
56
1
            return -1;
57
1
        }
58
9.64k
        layout->pending++;
59
9.64k
        layout->stack[++layout->depth] = (indentation_level){col, altcol};
60
9.64k
    }
61
7.86k
    else {
62
16.9k
        while (layout->depth > 0 &&
63
12.7k
            col < layout->stack[layout->depth].column) {
64
9.10k
            layout->pending--;
65
9.10k
            layout->depth--;
66
9.10k
        }
67
7.86k
        if (col != layout->stack[layout->depth].column) {
68
10
            tok->done = E_DEDENT;
69
10
            tok->cur = tok->inp;
70
10
            return -1;
71
10
        }
72
7.85k
        if (altcol != layout->stack[layout->depth].alternate_column) {
73
1
            _PyTokenizer_indenterror(tok);
74
1
            return -1;
75
1
        }
76
7.85k
    }
77
210k
    return 0;
78
210k
}
79
80
int
81
_PyLexer_BeginLine(struct tok_state *tok)
82
329k
{
83
329k
    assert(tok->layout.at_bol);
84
329k
    int c;
85
329k
    int blankline = 0;
86
329k
    int col = 0;
87
329k
    int altcol = 0;
88
329k
    tok->layout.at_bol = 0;
89
329k
    int cont_line_col = 0;
90
583k
    for (;;) {
91
583k
        c = tok_nextc(tok);
92
583k
        if (c == ' ') {
93
251k
            col++, altcol++;
94
251k
        }
95
331k
        else if (c == '\t') {
96
527
            col = (col / TABSIZE + 1) * TABSIZE;
97
527
            altcol = (altcol / ALTTABSIZE + 1) * ALTTABSIZE;
98
527
        }
99
331k
        else if (c == '\014')  {/* Control-L (formfeed) */
100
806
            col = altcol = 0; /* For Emacs users */
101
806
        }
102
330k
        else if (c == '\\') {
103
            // Indentation cannot be split over multiple physical lines
104
            // using backslashes. This means that if we found a backslash
105
            // preceded by whitespace, **the first one we find** determines
106
            // the level of indentation of whatever comes next.
107
907
            cont_line_col = cont_line_col ? cont_line_col : col;
108
907
            if ((c = _PyLexer_ContinueLine(tok)) == -1) {
109
32
                return -1;
110
32
            }
111
907
        }
112
329k
        else if (c == EOF && PyErr_Occurred()) {
113
88
            return -1;
114
88
        }
115
329k
        else {
116
329k
            break;
117
329k
        }
118
583k
    }
119
329k
    tok_backup(tok, c);
120
329k
    if (c == '\n' && tok->implicit_newline == _PyTokenizer_IMPLY_NEWLINE &&
121
49
        tok->level == 0 && (cont_line_col || col)) {
122
        // Without the newline, a whitespace-only line affects indentation.
123
36
        tok->needs_newline |= _PyTokenizer_NEWLINE_INDENT;
124
36
    }
125
329k
    if (c == '#' || c == '\n' || c == '\r') {
126
82.4k
        int interactive = _PyTok_ReaderIsInteractive(tok);
127
        /* Lines with only whitespace and/or comments
128
           shouldn't affect the indentation and are
129
           not passed to the parser as NEWLINE tokens,
130
           except *totally* empty lines in interactive
131
           mode, which signal the end of a command group. */
132
82.4k
        if (col == 0 && c == '\n' && interactive) {
133
0
            blankline = 0; /* Let it through */
134
0
        }
135
82.4k
        else if (interactive && tok->lineno == 1) {
136
            /* In interactive mode, if the first line contains
137
               only spaces and/or a comment, let it through. */
138
0
            blankline = 0;
139
0
            col = altcol = 0;
140
0
        }
141
82.4k
        else {
142
82.4k
            blankline = 1; /* Ignore completely */
143
82.4k
        }
144
82.4k
    }
145
329k
    if (!blankline && tok->level == 0) {
146
210k
        col = cont_line_col ? cont_line_col : col;
147
210k
        altcol = cont_line_col ? cont_line_col : altcol;
148
210k
        if (update_indentation(tok, col, altcol) < 0) {
149
13
            return -1;
150
13
        }
151
210k
    }
152
329k
    return blankline;
153
329k
}
154
155
int
156
_PyLexer_IndentationToken(struct tok_state *tok, struct token *token)
157
18.7k
{
158
18.7k
    assert(tok->layout.pending != 0);
159
18.7k
    _PyTok_Off p_start = -1;
160
18.7k
    _PyTok_Off p_end = -1;
161
18.7k
    if (tok->layout.pending < 0) {
162
9.06k
        if (tok->tok_extra_tokens) {
163
60
            p_start = tok->cur;
164
60
            p_end = tok->cur;
165
60
        }
166
9.06k
        tok->layout.pending++;
167
9.06k
        return _PyLexer_token_setup(tok, token, DEDENT, p_start, p_end);
168
9.06k
    }
169
9.64k
    else {
170
9.64k
        if (tok->tok_extra_tokens) {
171
64
            p_start = tok->buf_offset;
172
64
            p_end = tok->cur;
173
64
        }
174
9.64k
        tok->layout.pending--;
175
9.64k
        return _PyLexer_token_setup(tok, token, INDENT, p_start, p_end);
176
9.64k
    }
177
18.7k
}
178
179
int
180
_PyLexer_Newline(struct tok_state *tok, struct token *token, int blankline)
181
256k
{
182
256k
    if (!blankline && tok->implicit_newline == _PyTokenizer_IMPLY_NEWLINE) {
183
        // The statement's terminating newline was not in the input.
184
68.1k
        tok->needs_newline |= _PyTokenizer_NEWLINE_STATEMENT;
185
68.1k
    }
186
256k
    tok->layout.at_bol = 1;
187
256k
    if (blankline || tok->level > 0) {
188
119k
        if (!tok->tok_extra_tokens) {
189
118k
            return 0;
190
118k
        }
191
119k
    }
192
137k
    else if (!tok->layout.comment_newline || !tok->tok_extra_tokens) {
193
137k
        return _PyLexer_token_setup(tok, token, NEWLINE,
194
137k
                                    tok->start, tok->cur - 1);
195
137k
    }
196
140
    tok->layout.comment_newline = 0;
197
140
    return _PyLexer_token_setup(tok, token, NL, tok->start, tok->cur);
198
256k
}
199
200
void
201
_PyLexer_ImplyDedents(struct tok_state *tok)
202
0
{
203
0
    if (tok->layout.depth != 0) {
204
0
        tok->layout.pending = -tok->layout.depth;
205
0
        tok->layout.depth = 0;
206
0
    }
207
0
}