Skip to content

Commit 488ba78

Browse files
committed
impl
1 parent 6c522de commit 488ba78

File tree

4 files changed

+120
-3
lines changed

4 files changed

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

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

Lines changed: 5 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,15 +149,15 @@ 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

Tools/c-analyzer/cpython/_parser.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,10 @@ def clean_lines(text):
351351

352352
# Catch-alls:
353353
_abs('Include/**/*.h'): (5_000, 500),
354+
355+
# Specific to clang
356+
_abs('Modules/selectmodule.c'): (40_000, 3000),
357+
_abs('Modules/_testcapi/pyatomic.c'): (30_000, 1000),
354358
}
355359

356360

0 commit comments

Comments
 (0)