mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-23 01:33:36 +08:00
219 lines
5.6 KiB
Python
219 lines
5.6 KiB
Python
#! /usr/bin/python3
|
|
# Tests for scripts/glibcpp.py
|
|
# Copyright (C) 2022-2024 Free Software Foundation, Inc.
|
|
# This file is part of the GNU C Library.
|
|
#
|
|
# The GNU C Library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# The GNU C Library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with the GNU C Library; if not, see
|
|
# <https://www.gnu.org/licenses/>.
|
|
|
|
import inspect
|
|
import sys
|
|
|
|
import glibcpp
|
|
|
|
# Error counter.
|
|
errors = 0
|
|
|
|
class TokenizerErrors:
|
|
"""Used as the error reporter during tokenization."""
|
|
|
|
def __init__(self):
|
|
self.errors = []
|
|
|
|
def error(self, token, message):
|
|
self.errors.append((token, message))
|
|
|
|
def check_macro_definitions(source, expected):
|
|
reporter = TokenizerErrors()
|
|
tokens = glibcpp.tokenize_c(source, reporter)
|
|
|
|
actual = []
|
|
for md in glibcpp.macro_definitions(tokens):
|
|
if md.function:
|
|
md_name = '{}({})'.format(md.name, ','.join(md.args_lowered))
|
|
else:
|
|
md_name = md.name
|
|
actual.append((md_name, md.body_lowered))
|
|
|
|
if actual != expected or reporter.errors:
|
|
global errors
|
|
errors += 1
|
|
# Obtain python source line information.
|
|
frame = inspect.stack(2)[1]
|
|
print('{}:{}: error: macro definition mismatch, actual definitions:'
|
|
.format(frame[1], frame[2]))
|
|
for md in actual:
|
|
print('note: {} {!r}'.format(md[0], md[1]))
|
|
|
|
if reporter.errors:
|
|
for err in reporter.errors:
|
|
print('note: tokenizer error: {}: {}'.format(
|
|
err[0].line, err[1]))
|
|
|
|
def check_macro_eval(source, expected, expected_errors=''):
|
|
reporter = TokenizerErrors()
|
|
tokens = list(glibcpp.tokenize_c(source, reporter))
|
|
|
|
if reporter.errors:
|
|
# Obtain python source line information.
|
|
frame = inspect.stack(2)[1]
|
|
for err in reporter.errors:
|
|
print('{}:{}: tokenizer error: {}: {}'.format(
|
|
frame[1], frame[2], err[0].line, err[1]))
|
|
return
|
|
|
|
class EvalReporter:
|
|
"""Used as the error reporter during evaluation."""
|
|
|
|
def __init__(self):
|
|
self.lines = []
|
|
|
|
def error(self, line, message):
|
|
self.lines.append('{}: error: {}\n'.format(line, message))
|
|
|
|
def note(self, line, message):
|
|
self.lines.append('{}: note: {}\n'.format(line, message))
|
|
|
|
reporter = EvalReporter()
|
|
actual = glibcpp.macro_eval(glibcpp.macro_definitions(tokens), reporter)
|
|
actual_errors = ''.join(reporter.lines)
|
|
if actual != expected or actual_errors != expected_errors:
|
|
global errors
|
|
errors += 1
|
|
# Obtain python source line information.
|
|
frame = inspect.stack(2)[1]
|
|
print('{}:{}: error: macro evaluation mismatch, actual results:'
|
|
.format(frame[1], frame[2]))
|
|
for k, v in actual.items():
|
|
print(' {}: {!r}'.format(k, v))
|
|
for msg in reporter.lines:
|
|
sys.stdout.write(' | ' + msg)
|
|
|
|
# Individual test cases follow.
|
|
|
|
check_macro_definitions('', [])
|
|
check_macro_definitions('int main()\n{\n{\n', [])
|
|
check_macro_definitions("""
|
|
#define A 1
|
|
#define B 2 /* ignored */
|
|
#define C 3 // also ignored
|
|
#define D \
|
|
4
|
|
#define STRING "string"
|
|
#define FUNCLIKE(a, b) (a + b)
|
|
#define FUNCLIKE2(a, b) (a + \
|
|
b)
|
|
""", [('A', ['1']),
|
|
('B', ['2']),
|
|
('C', ['3']),
|
|
('D', ['4']),
|
|
('STRING', ['"string"']),
|
|
('FUNCLIKE(a,b)', list('(a+b)')),
|
|
('FUNCLIKE2(a,b)', list('(a+b)')),
|
|
])
|
|
check_macro_definitions('#define MACRO', [('MACRO', [])])
|
|
check_macro_definitions('#define MACRO\n', [('MACRO', [])])
|
|
check_macro_definitions('#define MACRO()', [('MACRO()', [])])
|
|
check_macro_definitions('#define MACRO()\n', [('MACRO()', [])])
|
|
|
|
check_macro_eval('#define A 1', {'A': 1})
|
|
check_macro_eval('#define A (1)', {'A': 1})
|
|
check_macro_eval('#define A (1 + 1)', {'A': 2})
|
|
check_macro_eval('#define A (1U << 31)', {'A': 1 << 31})
|
|
check_macro_eval('#define A (1 | 2)', {'A': 1 | 2})
|
|
check_macro_eval('''\
|
|
#define A (B + 1)
|
|
#define B 10
|
|
#define F(x) ignored
|
|
#define C "not ignored"
|
|
''', {
|
|
'A': 11,
|
|
'B': 10,
|
|
'C': '"not ignored"',
|
|
})
|
|
|
|
# Checking for evaluation errors.
|
|
check_macro_eval('''\
|
|
#define A 1
|
|
#define A 2
|
|
''', {
|
|
'A': 1,
|
|
}, '''\
|
|
2: error: macro A redefined
|
|
1: note: location of previous definition
|
|
''')
|
|
|
|
check_macro_eval('''\
|
|
#define A A
|
|
#define B 1
|
|
''', {
|
|
'A': None,
|
|
'B': 1,
|
|
}, '''\
|
|
1: error: macro definition A refers to itself
|
|
''')
|
|
|
|
check_macro_eval('''\
|
|
#define A B
|
|
#define B A
|
|
''', {
|
|
'A': None,
|
|
'B': None,
|
|
}, '''\
|
|
1: error: macro definition A refers to itself
|
|
2: note: evaluated from B
|
|
''')
|
|
|
|
check_macro_eval('''\
|
|
#define A B
|
|
#define B C
|
|
#define C A
|
|
''', {
|
|
'A': None,
|
|
'B': None,
|
|
'C': None,
|
|
}, '''\
|
|
1: error: macro definition A refers to itself
|
|
3: note: evaluated from C
|
|
2: note: evaluated from B
|
|
''')
|
|
|
|
check_macro_eval('''\
|
|
#define A 1 +
|
|
''', {
|
|
'A': None,
|
|
}, '''\
|
|
1: error: uninterpretable macro token sequence: 1 +
|
|
''')
|
|
|
|
check_macro_eval('''\
|
|
#define A 3*5
|
|
''', {
|
|
'A': None,
|
|
}, '''\
|
|
1: error: uninterpretable macro token sequence: 3 * 5
|
|
''')
|
|
|
|
check_macro_eval('''\
|
|
#define A 3 + 5
|
|
''', {
|
|
'A': 8,
|
|
}, '''\
|
|
1: error: missing parentheses around + expression
|
|
1: note: in definition of macro A
|
|
''')
|
|
|
|
if errors:
|
|
sys.exit(1)
|