[v3,0/7] Fix some libm static issues
[platform/upstream/glibc.git] / scripts / check-obsolete-constructs.py
1 #! /usr/bin/python3
2 # Copyright (C) 2019-2024 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 ConstructChecker:
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 NoCheck(ConstructChecker):
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 ObsoleteNotAllowed(ConstructChecker):
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 ObsoletePrivateDefinitionsAllowed(ConstructChecker):
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 ObsoletePublicDefinitionsAllowed(ConstructChecker):
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 HeaderChecker:
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()