(root)/
glibc-2.38/
scripts/
check-obsolete-constructs.py
       1  #! /usr/bin/python3
       2  # Copyright (C) 2019-2023 Free Software Foundation, Inc.
       3  # This file is part of the GNU C Library.
       4  #
       5  # The GNU C Library is free software; you can redistribute it and/or
       6  # modify it under the terms of the GNU Lesser General Public
       7  # License as published by the Free Software Foundation; either
       8  # version 2.1 of the License, or (at your option) any later version.
       9  #
      10  # The GNU C Library is distributed in the hope that it will be useful,
      11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
      12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13  # Lesser General Public License for more details.
      14  #
      15  # You should have received a copy of the GNU Lesser General Public
      16  # License along with the GNU C Library; if not, see
      17  # <https://www.gnu.org/licenses/>.
      18  
      19  """Verifies that installed headers do not use any obsolete constructs:
      20   * legacy BSD typedefs superseded by <stdint.h>:
      21     ushort uint ulong u_char u_short u_int u_long u_intNN_t quad_t u_quad_t
      22     (sys/types.h is allowed to _define_ these types, but not to use them
      23      to define anything else).
      24  """
      25  
      26  import argparse
      27  import os
      28  import re
      29  import sys
      30  
      31  # Make available glibc Python modules.
      32  sys.path.append(os.path.dirname(os.path.realpath(__file__)))
      33  
      34  import glibcpp
      35  
      36  #
      37  # Base and generic classes for individual checks.
      38  #
      39  
      40  class ESC[4;38;5;81mConstructChecker:
      41      """Scan a stream of C preprocessing tokens and possibly report
      42         problems with them.  The REPORTER object passed to __init__ has
      43         one method, reporter.error(token, message), which should be
      44         called to indicate a problem detected at the position of TOKEN.
      45         If MESSAGE contains the four-character sequence '{!r}' then that
      46         will be replaced with a textual representation of TOKEN.
      47      """
      48      def __init__(self, reporter):
      49          self.reporter = reporter
      50  
      51      def examine(self, tok):
      52          """Called once for each token in a header file.
      53             Call self.reporter.error if a problem is detected.
      54          """
      55          raise NotImplementedError
      56  
      57      def eof(self):
      58          """Called once at the end of the stream.  Subclasses need only
      59             override this if it might have something to do."""
      60          pass
      61  
      62  class ESC[4;38;5;81mNoCheck(ESC[4;38;5;149mConstructChecker):
      63      """Generic checker class which doesn't do anything.  Substitute this
      64         class for a real checker when a particular check should be skipped
      65         for some file."""
      66  
      67      def examine(self, tok):
      68          pass
      69  
      70  #
      71  # Check for obsolete type names.
      72  #
      73  
      74  # The obsolete type names we're looking for:
      75  OBSOLETE_TYPE_RE_ = re.compile(r"""\A
      76    (__)?
      77    (   quad_t
      78      | u(?: short | int | long
      79           | _(?: char | short | int(?:[0-9]+_t)? | long | quad_t )))
      80  \Z""", re.VERBOSE)
      81  
      82  class ESC[4;38;5;81mObsoleteNotAllowed(ESC[4;38;5;149mConstructChecker):
      83      """Don't allow any use of the obsolete typedefs."""
      84      def examine(self, tok):
      85          if OBSOLETE_TYPE_RE_.match(tok.text):
      86              self.reporter.error(tok, "use of {!r}")
      87  
      88  class ESC[4;38;5;81mObsoletePrivateDefinitionsAllowed(ESC[4;38;5;149mConstructChecker):
      89      """Allow definitions of the private versions of the
      90         obsolete typedefs; that is, 'typedef [anything] __obsolete;'
      91      """
      92      def __init__(self, reporter):
      93          super().__init__(reporter)
      94          self.in_typedef = False
      95          self.prev_token = None
      96  
      97      def examine(self, tok):
      98          # bits/types.h hides 'typedef' in a macro sometimes.
      99          if (tok.kind == "IDENT"
     100              and tok.text in ("typedef", "__STD_TYPE")
     101              and tok.context is None):
     102              self.in_typedef = True
     103          elif tok.kind == "PUNCTUATOR" and tok.text == ";" and self.in_typedef:
     104              self.in_typedef = False
     105              if self.prev_token.kind == "IDENT":
     106                  m = OBSOLETE_TYPE_RE_.match(self.prev_token.text)
     107                  if m and m.group(1) != "__":
     108                      self.reporter.error(self.prev_token, "use of {!r}")
     109              self.prev_token = None
     110          else:
     111              self._check_prev()
     112  
     113          self.prev_token = tok
     114  
     115      def eof(self):
     116          self._check_prev()
     117  
     118      def _check_prev(self):
     119          if (self.prev_token is not None
     120              and self.prev_token.kind == "IDENT"
     121              and OBSOLETE_TYPE_RE_.match(self.prev_token.text)):
     122              self.reporter.error(self.prev_token, "use of {!r}")
     123  
     124  class ESC[4;38;5;81mObsoletePublicDefinitionsAllowed(ESC[4;38;5;149mConstructChecker):
     125      """Allow definitions of the public versions of the obsolete
     126         typedefs.  Only specific forms of definition are allowed:
     127  
     128             typedef __obsolete obsolete;  // identifiers must agree
     129             typedef __uintN_t u_intN_t;   // N must agree
     130             typedef unsigned long int ulong;
     131             typedef unsigned short int ushort;
     132             typedef unsigned int uint;
     133      """
     134      def __init__(self, reporter):
     135          super().__init__(reporter)
     136          self.typedef_tokens = []
     137  
     138      def examine(self, tok):
     139          if tok.kind in ("WHITESPACE", "BLOCK_COMMENT",
     140                          "LINE_COMMENT", "NL", "ESCNL"):
     141              pass
     142  
     143          elif (tok.kind == "IDENT" and tok.text == "typedef"
     144                and tok.context is None):
     145              if self.typedef_tokens:
     146                  self.reporter.error(tok, "typedef inside typedef")
     147                  self._reset()
     148              self.typedef_tokens.append(tok)
     149  
     150          elif tok.kind == "PUNCTUATOR" and tok.text == ";":
     151              self._finish()
     152  
     153          elif self.typedef_tokens:
     154              self.typedef_tokens.append(tok)
     155  
     156      def eof(self):
     157          self._reset()
     158  
     159      def _reset(self):
     160          while self.typedef_tokens:
     161              tok = self.typedef_tokens.pop(0)
     162              if tok.kind == "IDENT" and OBSOLETE_TYPE_RE_.match(tok.text):
     163                  self.reporter.error(tok, "use of {!r}")
     164  
     165      def _finish(self):
     166          if not self.typedef_tokens: return
     167          if self.typedef_tokens[-1].kind == "IDENT":
     168              m = OBSOLETE_TYPE_RE_.match(self.typedef_tokens[-1].text)
     169              if m:
     170                  if self._permissible_public_definition(m):
     171                      self.typedef_tokens.clear()
     172          self._reset()
     173  
     174      def _permissible_public_definition(self, m):
     175          if m.group(1) == "__": return False
     176          name = m.group(2)
     177          toks = self.typedef_tokens
     178          ntok = len(toks)
     179          if ntok == 3 and toks[1].kind == "IDENT":
     180              defn = toks[1].text
     181              n = OBSOLETE_TYPE_RE_.match(defn)
     182              if n and n.group(1) == "__" and n.group(2) == name:
     183                  return True
     184  
     185              if (name[:5] == "u_int" and name[-2:] == "_t"
     186                  and defn[:6] == "__uint" and defn[-2:] == "_t"
     187                  and name[5:-2] == defn[6:-2]):
     188                  return True
     189  
     190              return False
     191  
     192          if (name == "ulong" and ntok == 5
     193              and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
     194              and toks[2].kind == "IDENT" and toks[2].text == "long"
     195              and toks[3].kind == "IDENT" and toks[3].text == "int"):
     196              return True
     197  
     198          if (name == "ushort" and ntok == 5
     199              and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
     200              and toks[2].kind == "IDENT" and toks[2].text == "short"
     201              and toks[3].kind == "IDENT" and toks[3].text == "int"):
     202              return True
     203  
     204          if (name == "uint" and ntok == 4
     205              and toks[1].kind == "IDENT" and toks[1].text == "unsigned"
     206              and toks[2].kind == "IDENT" and toks[2].text == "int"):
     207              return True
     208  
     209          return False
     210  
     211  def ObsoleteTypedefChecker(reporter, fname):
     212      """Factory: produce an instance of the appropriate
     213         obsolete-typedef checker for FNAME."""
     214  
     215      # The obsolete rpc/ and rpcsvc/ headers are allowed to use the
     216      # obsolete types, because it would be more trouble than it's
     217      # worth to remove them from headers that we intend to stop
     218      # installing eventually anyway.
     219      if (fname.startswith("rpc/")
     220          or fname.startswith("rpcsvc/")
     221          or "/rpc/" in fname
     222          or "/rpcsvc/" in fname):
     223          return NoCheck(reporter)
     224  
     225      # bits/types.h is allowed to define the __-versions of the
     226      # obsolete types.
     227      if (fname == "bits/types.h"
     228          or fname.endswith("/bits/types.h")):
     229          return ObsoletePrivateDefinitionsAllowed(reporter)
     230  
     231      # sys/types.h is allowed to use the __-versions of the
     232      # obsolete types, but only to define the unprefixed versions.
     233      if (fname == "sys/types.h"
     234          or fname.endswith("/sys/types.h")):
     235          return ObsoletePublicDefinitionsAllowed(reporter)
     236  
     237      return ObsoleteNotAllowed(reporter)
     238  
     239  #
     240  # Master control
     241  #
     242  
     243  class ESC[4;38;5;81mHeaderChecker:
     244      """Perform all of the checks on each header.  This is also the
     245         "reporter" object expected by tokenize_c and ConstructChecker.
     246      """
     247      def __init__(self):
     248          self.fname = None
     249          self.status = 0
     250  
     251      def error(self, tok, message):
     252          self.status = 1
     253          if '{!r}' in message:
     254              message = message.format(tok.text)
     255          sys.stderr.write("{}:{}:{}: error: {}\n".format(
     256              self.fname, tok.line, tok.column, message))
     257  
     258      def check(self, fname):
     259          self.fname = fname
     260          try:
     261              with open(fname, "rt", encoding="utf-8") as fp:
     262                  contents = fp.read()
     263          except OSError as e:
     264              sys.stderr.write("{}: {}\n".format(fname, e.strerror))
     265              self.status = 1
     266              return
     267  
     268          typedef_checker = ObsoleteTypedefChecker(self, self.fname)
     269  
     270          for tok in glibcpp.tokenize_c(contents, self):
     271              typedef_checker.examine(tok)
     272  
     273  def main():
     274      ap = argparse.ArgumentParser(description=__doc__)
     275      ap.add_argument("headers", metavar="header", nargs="+",
     276                      help="one or more headers to scan for obsolete constructs")
     277      args = ap.parse_args()
     278  
     279      checker = HeaderChecker()
     280      for fname in args.headers:
     281          # Headers whose installed name begins with "finclude/" contain
     282          # Fortran, not C, and this program should completely ignore them.
     283          if not (fname.startswith("finclude/") or "/finclude/" in fname):
     284              checker.check(fname)
     285      sys.exit(checker.status)
     286  
     287  main()