2 # Copyright (C) 2019-2023 Free Software Foundation, Inc.
3 # This file is part of the GNU C Library.
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.
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.
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/>.
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).
31 # Make available glibc Python modules.
32 sys
.path
.append(os
.path
.dirname(os
.path
.realpath(__file__
)))
37 # Base and generic classes for individual checks.
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.
48 def __init__(self
, reporter
):
49 self
.reporter
= reporter
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.
55 raise NotImplementedError
58 """Called once at the end of the stream. Subclasses need only
59 override this if it might have something to do."""
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
67 def examine(self
, tok
):
71 # Check for obsolete type names.
74 # The obsolete type names we're looking for:
75 OBSOLETE_TYPE_RE_
= re
.compile(r
"""\A
78 | u(?: short | int | long
79 | _(?: char | short | int(?:[0-9]+_t)? | long | quad_t )))
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}")
88 class ObsoletePrivateDefinitionsAllowed(ConstructChecker
):
89 """Allow definitions of the private versions of the
90 obsolete typedefs; that is, 'typedef [anything] __obsolete;'
92 def __init__(self
, reporter
):
93 super().__init
__(reporter
)
94 self
.in_typedef
= False
95 self
.prev_token
= None
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
113 self
.prev_token
= tok
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}")
124 class ObsoletePublicDefinitionsAllowed(ConstructChecker
):
125 """Allow definitions of the public versions of the obsolete
126 typedefs. Only specific forms of definition are allowed:
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;
134 def __init__(self
, reporter
):
135 super().__init
__(reporter
)
136 self
.typedef_tokens
= []
138 def examine(self
, tok
):
139 if tok
.kind
in ("WHITESPACE", "BLOCK_COMMENT",
140 "LINE_COMMENT", "NL", "ESCNL"):
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")
148 self
.typedef_tokens
.append(tok
)
150 elif tok
.kind
== "PUNCTUATOR" and tok
.text
== ";":
153 elif self
.typedef_tokens
:
154 self
.typedef_tokens
.append(tok
)
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}")
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
)
170 if self
._permissible
_public
_definition
(m
):
171 self
.typedef_tokens
.clear()
174 def _permissible_public_definition(self
, m
):
175 if m
.group(1) == "__": return False
177 toks
= self
.typedef_tokens
179 if ntok
== 3 and toks
[1].kind
== "IDENT":
181 n
= OBSOLETE_TYPE_RE_
.match(defn
)
182 if n
and n
.group(1) == "__" and n
.group(2) == name
:
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]):
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"):
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"):
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"):
211 def ObsoleteTypedefChecker(reporter
, fname
):
212 """Factory: produce an instance of the appropriate
213 obsolete-typedef checker for FNAME."""
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/")
222 or "/rpcsvc/" in fname
):
223 return NoCheck(reporter
)
225 # bits/types.h is allowed to define the __-versions of the
227 if (fname
== "bits/types.h"
228 or fname
.endswith("/bits/types.h")):
229 return ObsoletePrivateDefinitionsAllowed(reporter
)
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
)
237 return ObsoleteNotAllowed(reporter
)
244 """Perform all of the checks on each header. This is also the
245 "reporter" object expected by tokenize_c and ConstructChecker.
251 def error(self
, tok
, message
):
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
))
258 def check(self
, fname
):
261 with
open(fname
, "rt", encoding
="utf-8") as fp
:
264 sys
.stderr
.write("{}: {}\n".format(fname
, e
.strerror
))
268 typedef_checker
= ObsoleteTypedefChecker(self
, self
.fname
)
270 for tok
in glibcpp
.tokenize_c(contents
, self
):
271 typedef_checker
.examine(tok
)
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()
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
):
285 sys
.exit(checker
.status
)