(root)/
glibc-2.38/
support/
tst-glibcpp.py
       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.
       5  #
       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.
      10  #
      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.
      15  #
      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/>.
      19  
      20  import inspect
      21  import sys
      22  
      23  import glibcpp
      24  
      25  # Error counter.
      26  errors = 0
      27  
      28  class ESC[4;38;5;81mTokenizerErrors:
      29      """Used as the error reporter during tokenization."""
      30  
      31      def __init__(self):
      32          self.errors = []
      33  
      34      def error(self, token, message):
      35          self.errors.append((token, message))
      36  
      37  def check_macro_definitions(source, expected):
      38      reporter = TokenizerErrors()
      39      tokens = glibcpp.tokenize_c(source, reporter)
      40  
      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))
      48  
      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]))
      58  
      59          if reporter.errors:
      60              for err in reporter.errors:
      61                  print('note: tokenizer error: {}: {}'.format(
      62                      err[0].line, err[1]))
      63  
      64  def check_macro_eval(source, expected, expected_errors=''):
      65      reporter = TokenizerErrors()
      66      tokens = list(glibcpp.tokenize_c(source, reporter))
      67  
      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
      75  
      76      class ESC[4;38;5;81mEvalReporter:
      77          """Used as the error reporter during evaluation."""
      78  
      79          def __init__(self):
      80              self.lines = []
      81  
      82          def error(self, line, message):
      83              self.lines.append('{}: error: {}\n'.format(line, message))
      84  
      85          def note(self, line, message):
      86              self.lines.append('{}: note: {}\n'.format(line, message))
      87  
      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)
     102  
     103  # Individual test cases follow.
     104  
     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 \
     112   4
     113  #define STRING "string"
     114  #define FUNCLIKE(a, b) (a + b)
     115  #define FUNCLIKE2(a, b) (a + \
     116   b)
     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)')),
     124        ])
     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()', [])])
     129  
     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"',
     144  })
     145  
     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  ''')
     156  
     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  ''')
     166  
     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  ''')
     177  
     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  ''')
     191  
     192  check_macro_eval('''\
     193  #define A 1 +
     194  ''', {
     195      'A': None,
     196  }, '''\
     197  1: error: uninterpretable macro token sequence: 1 +
     198  ''')
     199  
     200  check_macro_eval('''\
     201  #define A 3*5
     202  ''', {
     203      'A': None,
     204  }, '''\
     205  1: error: uninterpretable macro token sequence: 3 * 5
     206  ''')
     207  
     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  ''')
     216  
     217  if errors:
     218      sys.exit(1)