riscv: Fix feenvupdate with FE_DFL_ENV (BZ 31022)
[glibc.git] / support / tst-glibcpp.py
blobc91733f946b9b7ee5f63c4a34dc8c1ffe93f82d0
1 #! /usr/bin/python3
2 # Tests for scripts/glibcpp.py
3 # Copyright (C) 2022-2023 Free Software Foundation, Inc.
4 # This file is part of the GNU C Library.
6 # The GNU C Library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # The GNU C Library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with the GNU C Library; if not, see
18 # <https://www.gnu.org/licenses/>.
20 import inspect
21 import sys
23 import glibcpp
25 # Error counter.
26 errors = 0
28 class TokenizerErrors:
29 """Used as the error reporter during tokenization."""
31 def __init__(self):
32 self.errors = []
34 def error(self, token, message):
35 self.errors.append((token, message))
37 def check_macro_definitions(source, expected):
38 reporter = TokenizerErrors()
39 tokens = glibcpp.tokenize_c(source, reporter)
41 actual = []
42 for md in glibcpp.macro_definitions(tokens):
43 if md.function:
44 md_name = '{}({})'.format(md.name, ','.join(md.args_lowered))
45 else:
46 md_name = md.name
47 actual.append((md_name, md.body_lowered))
49 if actual != expected or reporter.errors:
50 global errors
51 errors += 1
52 # Obtain python source line information.
53 frame = inspect.stack(2)[1]
54 print('{}:{}: error: macro definition mismatch, actual definitions:'
55 .format(frame[1], frame[2]))
56 for md in actual:
57 print('note: {} {!r}'.format(md[0], md[1]))
59 if reporter.errors:
60 for err in reporter.errors:
61 print('note: tokenizer error: {}: {}'.format(
62 err[0].line, err[1]))
64 def check_macro_eval(source, expected, expected_errors=''):
65 reporter = TokenizerErrors()
66 tokens = list(glibcpp.tokenize_c(source, reporter))
68 if reporter.errors:
69 # Obtain python source line information.
70 frame = inspect.stack(2)[1]
71 for err in reporter.errors:
72 print('{}:{}: tokenizer error: {}: {}'.format(
73 frame[1], frame[2], err[0].line, err[1]))
74 return
76 class EvalReporter:
77 """Used as the error reporter during evaluation."""
79 def __init__(self):
80 self.lines = []
82 def error(self, line, message):
83 self.lines.append('{}: error: {}\n'.format(line, message))
85 def note(self, line, message):
86 self.lines.append('{}: note: {}\n'.format(line, message))
88 reporter = EvalReporter()
89 actual = glibcpp.macro_eval(glibcpp.macro_definitions(tokens), reporter)
90 actual_errors = ''.join(reporter.lines)
91 if actual != expected or actual_errors != expected_errors:
92 global errors
93 errors += 1
94 # Obtain python source line information.
95 frame = inspect.stack(2)[1]
96 print('{}:{}: error: macro evaluation mismatch, actual results:'
97 .format(frame[1], frame[2]))
98 for k, v in actual.items():
99 print(' {}: {!r}'.format(k, v))
100 for msg in reporter.lines:
101 sys.stdout.write(' | ' + msg)
103 # Individual test cases follow.
105 check_macro_definitions('', [])
106 check_macro_definitions('int main()\n{\n{\n', [])
107 check_macro_definitions("""
108 #define A 1
109 #define B 2 /* ignored */
110 #define C 3 // also ignored
111 #define D \
113 #define STRING "string"
114 #define FUNCLIKE(a, b) (a + b)
115 #define FUNCLIKE2(a, b) (a + \
117 """, [('A', ['1']),
118 ('B', ['2']),
119 ('C', ['3']),
120 ('D', ['4']),
121 ('STRING', ['"string"']),
122 ('FUNCLIKE(a,b)', list('(a+b)')),
123 ('FUNCLIKE2(a,b)', list('(a+b)')),
125 check_macro_definitions('#define MACRO', [('MACRO', [])])
126 check_macro_definitions('#define MACRO\n', [('MACRO', [])])
127 check_macro_definitions('#define MACRO()', [('MACRO()', [])])
128 check_macro_definitions('#define MACRO()\n', [('MACRO()', [])])
130 check_macro_eval('#define A 1', {'A': 1})
131 check_macro_eval('#define A (1)', {'A': 1})
132 check_macro_eval('#define A (1 + 1)', {'A': 2})
133 check_macro_eval('#define A (1U << 31)', {'A': 1 << 31})
134 check_macro_eval('#define A (1 | 2)', {'A': 1 | 2})
135 check_macro_eval('''\
136 #define A (B + 1)
137 #define B 10
138 #define F(x) ignored
139 #define C "not ignored"
140 ''', {
141 'A': 11,
142 'B': 10,
143 'C': '"not ignored"',
146 # Checking for evaluation errors.
147 check_macro_eval('''\
148 #define A 1
149 #define A 2
150 ''', {
151 'A': 1,
152 }, '''\
153 2: error: macro A redefined
154 1: note: location of previous definition
155 ''')
157 check_macro_eval('''\
158 #define A A
159 #define B 1
160 ''', {
161 'A': None,
162 'B': 1,
163 }, '''\
164 1: error: macro definition A refers to itself
165 ''')
167 check_macro_eval('''\
168 #define A B
169 #define B A
170 ''', {
171 'A': None,
172 'B': None,
173 }, '''\
174 1: error: macro definition A refers to itself
175 2: note: evaluated from B
176 ''')
178 check_macro_eval('''\
179 #define A B
180 #define B C
181 #define C A
182 ''', {
183 'A': None,
184 'B': None,
185 'C': None,
186 }, '''\
187 1: error: macro definition A refers to itself
188 3: note: evaluated from C
189 2: note: evaluated from B
190 ''')
192 check_macro_eval('''\
193 #define A 1 +
194 ''', {
195 'A': None,
196 }, '''\
197 1: error: uninterpretable macro token sequence: 1 +
198 ''')
200 check_macro_eval('''\
201 #define A 3*5
202 ''', {
203 'A': None,
204 }, '''\
205 1: error: uninterpretable macro token sequence: 3 * 5
206 ''')
208 check_macro_eval('''\
209 #define A 3 + 5
210 ''', {
211 'A': 8,
212 }, '''\
213 1: error: missing parentheses around + expression
214 1: note: in definition of macro A
215 ''')
217 if errors:
218 sys.exit(1)