Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Tools/c-analyzer/c_parser/preprocessor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from . import (
pure as _pure,
gcc as _gcc,
clang as _clang,
)


Expand Down Expand Up @@ -234,7 +235,7 @@ def handling_errors(ignore_exc=None, *, log_err=None):
'bcpp': None,
# aliases/extras:
'gcc': _gcc.preprocess,
'clang': None,
'clang': _clang.preprocess,
}


Expand Down
109 changes: 109 additions & 0 deletions Tools/c-analyzer/c_parser/preprocessor/clang.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import os.path
import re

from . import common as _common
from . import gcc as _gcc


TOOL = 'clang'

META_FILES = {
'<built-in>',
'<command line>',
}


def preprocess(filename,
incldirs=None,
includes=None,
macros=None,
samefiles=None,
cwd=None,
):
if not cwd or not os.path.isabs(cwd):
cwd = os.path.abspath(cwd or '.')
filename = _gcc._normpath(filename, cwd)

postargs = _gcc.POST_ARGS
basename = os.path.basename(filename)
dirname = os.path.basename(os.path.dirname(filename))
if (basename not in _gcc.FILES_WITHOUT_INTERNAL_CAPI
and dirname not in _gcc.DIRS_WITHOUT_INTERNAL_CAPI):
postargs += ('-DPy_BUILD_CORE=1',)

text = _common.preprocess(
TOOL,
filename,
incldirs=incldirs,
includes=includes,
macros=macros,
#preargs=PRE_ARGS,
postargs=postargs,
executable=['clang'],
compiler='unix',
cwd=cwd,
)
return _iter_lines(text, filename, samefiles, cwd)


EXIT_MARKERS = {'# 2 "<built-in>" 2', '# 3 "<built-in>" 2', '# 4 "<built-in>" 2'}


def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
# NOTE:HACK: has a stack return in unusual order for /include/curses.h
if reqfile.endswith(('/Include/py_curses.h',
'/Modules/_cursesmodule.c',
'/Modules/_curses_panel.c')):
return

lines = iter(text.splitlines())

# The first line is special.
# The subsequent lines are consistent.
firstlines = [
f'# 1 "{reqfile}"',
'# 1 "<built-in>" 1',
'# 1 "<built-in>" 3',
'# 370 "<built-in>" 3',
'# 1 "<command line>" 1',
'# 1 "<built-in>" 2',
]
for expected in firstlines:
line = next(lines)
if line != expected:
raise NotImplementedError((line, expected))

# Do all the CLI-provided includes.
filter_reqfile = (lambda f: _gcc._filter_reqfile(f, reqfile, samefiles))
make_info = (lambda lno: _common.FileInfo(reqfile, lno))
last = None
for line in lines:
assert last != reqfile, (last,)
# NOTE:clang specific
if not line:
continue
lno, included, flags = _gcc._parse_marker_line(line, reqfile)
if not included:
raise NotImplementedError((line,))
if included == reqfile:
# This will be the last one.
assert 2 in flags, (line, flags)
else:
# NOTE:clang specific
if _gcc._normpath(included, cwd) == reqfile:
assert 1 in flags or 2 in flags, (line, flags, included, reqfile)
else:
assert 1 in flags, (line, flags, included, reqfile)
yield from _gcc._iter_top_include_lines(
lines,
_gcc._normpath(included, cwd),
cwd,
filter_reqfile,
make_info,
raw,
EXIT_MARKERS
)
last = included
# The last one is always the requested file.
# NOTE:clang specific
assert _gcc._normpath(included, cwd) == reqfile, (line,)
7 changes: 5 additions & 2 deletions Tools/c-analyzer/c_parser/preprocessor/gcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
'-E',
)

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


def preprocess(filename,
incldirs=None,
Expand Down Expand Up @@ -138,6 +140,7 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False):
filter_reqfile,
make_info,
raw,
EXIT_MARKERS
)
last = included
# The last one is always the requested file.
Expand All @@ -146,15 +149,15 @@ def _iter_lines(text, reqfile, samefiles, cwd, raw=False):

def _iter_top_include_lines(lines, topfile, cwd,
filter_reqfile, make_info,
raw):
raw, exit_markers):
partial = 0 # depth
files = [topfile]
# We start at 1 in case there are source lines (including blank ones)
# before the first marker line. Also, we already verified in
# _parse_marker_line() that the preprocessor reported lno as 1.
lno = 1
for line in lines:
if line == '# 0 "<command-line>" 2' or line == '# 1 "<command-line>" 2':
if line in exit_markers:
# We're done with this top-level include.
return

Expand Down
4 changes: 4 additions & 0 deletions Tools/c-analyzer/cpython/_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,10 @@ def clean_lines(text):

# Catch-alls:
_abs('Include/**/*.h'): (5_000, 500),

# Specific to clang
_abs('Modules/selectmodule.c'): (40_000, 3000),
_abs('Modules/_testcapi/pyatomic.c'): (30_000, 1000),
}


Expand Down
Loading