Skip to content

Commit 5ec03cf

Browse files
authored
gh-133228: c-analyzer clang preprocessor (GH-133229)
* impl * included 2 failures to tsvs next to similar entries * added fix/hack for curses.h fails * fix leftover from debug
1 parent 656a64b commit 5ec03cf

File tree

6 files changed

+125
-3
lines changed

6 files changed

+125
-3
lines changed

Tools/c-analyzer/c_parser/preprocessor/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from . import (
1717
pure as _pure,
1818
gcc as _gcc,
19+
clang as _clang,
1920
)
2021

2122

@@ -234,7 +235,7 @@ def handling_errors(ignore_exc=None, *, log_err=None):
234235
'bcpp': None,
235236
# aliases/extras:
236237
'gcc': _gcc.preprocess,
237-
'clang': None,
238+
'clang': _clang.preprocess,
238239
}
239240

240241

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import os.path
2+
import re, sys
3+
4+
from . import common as _common
5+
from . import gcc as _gcc
6+
7+
_normpath = _gcc._normpath
8+
9+
TOOL = 'clang'
10+
11+
META_FILES = {
12+
'<built-in>',
13+
'<command line>',
14+
}
15+
16+
17+
def preprocess(filename,
18+
incldirs=None,
19+
includes=None,
20+
macros=None,
21+
samefiles=None,
22+
cwd=None,
23+
):
24+
if not cwd or not os.path.isabs(cwd):
25+
cwd = os.path.abspath(cwd or '.')
26+
filename = _normpath(filename, cwd)
27+
28+
postargs = _gcc.POST_ARGS
29+
basename = os.path.basename(filename)
30+
dirname = os.path.basename(os.path.dirname(filename))
31+
if (basename not in _gcc.FILES_WITHOUT_INTERNAL_CAPI
32+
and dirname not in _gcc.DIRS_WITHOUT_INTERNAL_CAPI):
33+
postargs += ('-DPy_BUILD_CORE=1',)
34+
35+
text = _common.preprocess(
36+
TOOL,
37+
filename,
38+
incldirs=incldirs,
39+
includes=includes,
40+
macros=macros,
41+
#preargs=PRE_ARGS,
42+
postargs=postargs,
43+
executable=['clang'],
44+
compiler='unix',
45+
cwd=cwd,
46+
)
47+
return _iter_lines(text, filename, samefiles, cwd)
48+
49+
50+
EXIT_MARKERS = {'# 2 "<built-in>" 2', '# 3 "<built-in>" 2', '# 4 "<built-in>" 2'}
51+
52+
53+
def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
54+
lines = iter(text.splitlines())
55+
56+
# The first line is special.
57+
# The subsequent lines are consistent.
58+
firstlines = [
59+
f'# 1 "{reqfile}"',
60+
'# 1 "<built-in>" 1',
61+
'# 1 "<built-in>" 3',
62+
'# 370 "<built-in>" 3',
63+
'# 1 "<command line>" 1',
64+
'# 1 "<built-in>" 2',
65+
]
66+
for expected in firstlines:
67+
line = next(lines)
68+
if line != expected:
69+
raise NotImplementedError((line, expected))
70+
71+
# Do all the CLI-provided includes.
72+
filter_reqfile = (lambda f: _gcc._filter_reqfile(f, reqfile, samefiles))
73+
make_info = (lambda lno: _common.FileInfo(reqfile, lno))
74+
last = None
75+
for line in lines:
76+
assert last != reqfile, (last,)
77+
# NOTE:condition is clang specific
78+
if not line:
79+
continue
80+
lno, included, flags = _gcc._parse_marker_line(line, reqfile)
81+
if not included:
82+
raise NotImplementedError((line,))
83+
if included == reqfile:
84+
# This will be the last one.
85+
assert 2 in flags, (line, flags)
86+
else:
87+
# NOTE:first condition is specific to clang
88+
if _normpath(included, cwd) == reqfile:
89+
assert 1 in flags or 2 in flags, (line, flags, included, reqfile)
90+
else:
91+
assert 1 in flags, (line, flags, included, reqfile)
92+
yield from _gcc._iter_top_include_lines(
93+
lines,
94+
_normpath(included, cwd),
95+
cwd,
96+
filter_reqfile,
97+
make_info,
98+
raw,
99+
EXIT_MARKERS
100+
)
101+
last = included
102+
# The last one is always the requested file.
103+
# NOTE:_normpath is clang specific
104+
assert _normpath(included, cwd) == reqfile, (line,)

Tools/c-analyzer/c_parser/preprocessor/gcc.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
'-E',
6666
)
6767

68+
EXIT_MARKERS = {'# 0 "<command-line>" 2', '# 1 "<command-line>" 2'}
69+
6870

6971
def preprocess(filename,
7072
incldirs=None,
@@ -138,6 +140,7 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
138140
filter_reqfile,
139141
make_info,
140142
raw,
143+
EXIT_MARKERS
141144
)
142145
last = included
143146
# The last one is always the requested file.
@@ -146,20 +149,28 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
146149

147150
def _iter_top_include_lines(lines, topfile, cwd,
148151
filter_reqfile, make_info,
149-
raw):
152+
raw, exit_markers):
150153
partial = 0 # depth
151154
files = [topfile]
152155
# We start at 1 in case there are source lines (including blank ones)
153156
# before the first marker line. Also, we already verified in
154157
# _parse_marker_line() that the preprocessor reported lno as 1.
155158
lno = 1
156159
for line in lines:
157-
if line == '# 0 "<command-line>" 2' or line == '# 1 "<command-line>" 2':
160+
if line in exit_markers:
158161
# We're done with this top-level include.
159162
return
160163

161164
_lno, included, flags = _parse_marker_line(line)
162165
if included:
166+
# HACK:
167+
# Mixes curses.h and ncurses.h marker lines
168+
# gcc silently passes this, while clang fails
169+
# See: /Include/py_curses.h #if-elif directives
170+
# And compare with preprocessor output
171+
if os.path.basename(included) == 'curses.h':
172+
included = os.path.join(os.path.dirname(included), 'ncurses.h')
173+
163174
lno = _lno
164175
included = _normpath(included, cwd)
165176
# We hit a marker line.

Tools/c-analyzer/cpython/_parser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,10 @@ def format_tsv_lines(lines):
340340

341341
# Catch-alls:
342342
_abs('Include/**/*.h'): (5_000, 500),
343+
344+
# Specific to clang
345+
_abs('Modules/selectmodule.c'): (40_000, 3000),
346+
_abs('Modules/_testcapi/pyatomic.c'): (30_000, 1000),
343347
}
344348

345349

Tools/c-analyzer/cpython/globals-to-fix.tsv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ Modules/_tkinter.c - tcl_lock -
400400
Modules/_tkinter.c - excInCmd -
401401
Modules/_tkinter.c - valInCmd -
402402
Modules/_tkinter.c - trbInCmd -
403+
Modules/socketmodule.c - netdb_lock -
403404

404405

405406
##################################

Tools/c-analyzer/cpython/ignored.tsv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ filename funcname name reason
1616
## indicators for resource availability/capability
1717
# (set during first init)
1818
Python/bootstrap_hash.c py_getrandom getrandom_works -
19+
Python/bootstrap_hash.c py_getentropy getentropy_works -
1920
Python/fileutils.c - _Py_open_cloexec_works -
2021
Python/fileutils.c set_inheritable ioctl_works -
2122
# (set lazily, *after* first init)

0 commit comments

Comments
 (0)