Vendor import of clang trunk r321017:
https://llvm.org/svn/llvm-project/cfe/trunk@321017
This commit is contained in:
parent
7c2d533890
commit
fe6d824e6e
@ -1,4 +1,4 @@
|
||||
{
|
||||
"project_id" : "clang",
|
||||
"repository.callsign" : "C",
|
||||
"conduit_uri" : "https://reviews.llvm.org/"
|
||||
}
|
||||
|
@ -132,6 +132,9 @@ Please install Python or specify the PYTHON_EXECUTABLE CMake variable.")
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
# Note: path not really used, except for checking if lit was found
|
||||
set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/llvm-lit)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/llvm-lit utils/llvm-lit)
|
||||
endif()
|
||||
if(NOT LLVM_UTILS_PROVIDED)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/count utils/count)
|
||||
@ -181,13 +184,16 @@ endif()
|
||||
# we can include cmake files from this directory.
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
find_package(LibXml2 2.5.3 QUIET)
|
||||
if (LIBXML2_FOUND)
|
||||
set(CLANG_HAVE_LIBXML 1)
|
||||
# Don't look for libxml if we're using MSan, since uninstrumented third party
|
||||
# code may call MSan interceptors like strlen, leading to false positives.
|
||||
if(NOT LLVM_USE_SANITIZER MATCHES "Memory.*")
|
||||
set (LIBXML2_FOUND 0)
|
||||
find_package(LibXml2 2.5.3 QUIET)
|
||||
if (LIBXML2_FOUND)
|
||||
set(CLANG_HAVE_LIBXML 1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Z3 4.5)
|
||||
|
||||
include(CheckIncludeFile)
|
||||
check_include_file(sys/resource.h CLANG_HAVE_RLIMITS)
|
||||
|
||||
@ -229,9 +235,24 @@ if (NOT(CLANG_DEFAULT_RTLIB STREQUAL "" OR
|
||||
"Default runtime library to use (\"libgcc\" or \"compiler-rt\", empty for platform default)" FORCE)
|
||||
endif()
|
||||
|
||||
set(CLANG_DEFAULT_OBJCOPY "objcopy" CACHE STRING
|
||||
"Default objcopy executable to use.")
|
||||
|
||||
set(CLANG_DEFAULT_OPENMP_RUNTIME "libomp" CACHE STRING
|
||||
"Default OpenMP runtime used by -fopenmp.")
|
||||
|
||||
# OpenMP offloading requires at least sm_35 because we use shuffle instructions
|
||||
# to generate efficient code for reductions and the atomicMax instruction on
|
||||
# 64-bit integers in the implementation of conditional lastprivate.
|
||||
set(CLANG_OPENMP_NVPTX_DEFAULT_ARCH "sm_35" CACHE STRING
|
||||
"Default architecture for OpenMP offloading to Nvidia GPUs.")
|
||||
string(REGEX MATCH "^sm_([0-9]+)$" MATCHED_ARCH "${CLANG_OPENMP_NVPTX_DEFAULT_ARCH}")
|
||||
if (NOT DEFINED MATCHED_ARCH OR "${CMAKE_MATCH_1}" LESS 35)
|
||||
message(WARNING "Resetting default architecture for OpenMP offloading to Nvidia GPUs to sm_35")
|
||||
set(CLANG_OPENMP_NVPTX_DEFAULT_ARCH "sm_35" CACHE STRING
|
||||
"Default architecture for OpenMP offloading to Nvidia GPUs." FORCE)
|
||||
endif()
|
||||
|
||||
set(CLANG_VENDOR ${PACKAGE_VENDOR} CACHE STRING
|
||||
"Vendor-specific text for showing with version information.")
|
||||
|
||||
@ -376,11 +397,14 @@ option(CLANG_ENABLE_STATIC_ANALYZER "Build static analyzer." ON)
|
||||
option(CLANG_ANALYZER_BUILD_Z3
|
||||
"Build the static analyzer with the Z3 constraint manager." OFF)
|
||||
|
||||
option(CLANG_ENABLE_PROTO_FUZZER "Build Clang protobuf fuzzer." OFF)
|
||||
|
||||
if(NOT CLANG_ENABLE_STATIC_ANALYZER AND (CLANG_ENABLE_ARCMT OR CLANG_ANALYZER_BUILD_Z3))
|
||||
message(FATAL_ERROR "Cannot disable static analyzer while enabling ARCMT or Z3")
|
||||
endif()
|
||||
|
||||
if(CLANG_ANALYZER_BUILD_Z3)
|
||||
find_package(Z3 4.5)
|
||||
if(Z3_FOUND)
|
||||
set(CLANG_ANALYZER_WITH_Z3 1)
|
||||
else()
|
||||
@ -411,7 +435,16 @@ add_subdirectory(include)
|
||||
|
||||
# All targets below may depend on all tablegen'd files.
|
||||
get_property(CLANG_TABLEGEN_TARGETS GLOBAL PROPERTY CLANG_TABLEGEN_TARGETS)
|
||||
list(APPEND LLVM_COMMON_DEPENDS ${CLANG_TABLEGEN_TARGETS})
|
||||
add_custom_target(clang-tablegen-targets DEPENDS ${CLANG_TABLEGEN_TARGETS})
|
||||
set_target_properties(clang-tablegen-targets PROPERTIES FOLDER "Misc")
|
||||
list(APPEND LLVM_COMMON_DEPENDS clang-tablegen-targets)
|
||||
|
||||
# Force target to be built as soon as possible. Clang modules builds depend
|
||||
# header-wise on it as they ship all headers from the umbrella folders. Building
|
||||
# an entire module might include header, which depends on intrinsics_gen.
|
||||
if(LLVM_ENABLE_MODULES AND NOT CLANG_BUILT_STANDALONE)
|
||||
list(APPEND LLVM_COMMON_DEPENDS intrinsics_gen)
|
||||
endif()
|
||||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(tools)
|
||||
@ -508,8 +541,8 @@ if (CLANG_ENABLE_BOOTSTRAP)
|
||||
set(NEXT_CLANG_STAGE ${NEXT_CLANG_STAGE}-instrumented)
|
||||
endif()
|
||||
message(STATUS "Setting next clang stage to: ${NEXT_CLANG_STAGE}")
|
||||
|
||||
|
||||
|
||||
|
||||
set(STAMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NEXT_CLANG_STAGE}-stamps/)
|
||||
set(BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${NEXT_CLANG_STAGE}-bins/)
|
||||
|
||||
@ -571,7 +604,9 @@ if (CLANG_ENABLE_BOOTSTRAP)
|
||||
LLVM_BINUTILS_INCDIR
|
||||
CLANG_REPOSITORY_STRING
|
||||
CMAKE_MAKE_PROGRAM
|
||||
CMAKE_OSX_ARCHITECTURES)
|
||||
CMAKE_OSX_ARCHITECTURES
|
||||
LLVM_ENABLE_PROJECTS
|
||||
LLVM_ENABLE_RUNTIMES)
|
||||
|
||||
# We don't need to depend on compiler-rt if we're building instrumented
|
||||
# because the next stage will use the same compiler used to build this stage.
|
||||
@ -621,7 +656,7 @@ if (CLANG_ENABLE_BOOTSTRAP)
|
||||
foreach(variableName ${variableNames})
|
||||
if(variableName MATCHES "^BOOTSTRAP_")
|
||||
string(SUBSTRING ${variableName} 10 -1 varName)
|
||||
string(REPLACE ";" "\;" value "${${variableName}}")
|
||||
string(REPLACE ";" "|" value "${${variableName}}")
|
||||
list(APPEND PASSTHROUGH_VARIABLES
|
||||
-D${varName}=${value})
|
||||
endif()
|
||||
@ -637,7 +672,7 @@ if (CLANG_ENABLE_BOOTSTRAP)
|
||||
if("${${variableName}}" STREQUAL "")
|
||||
set(value "")
|
||||
else()
|
||||
string(REPLACE ";" "\;" value ${${variableName}})
|
||||
string(REPLACE ";" "|" value "${${variableName}}")
|
||||
endif()
|
||||
list(APPEND PASSTHROUGH_VARIABLES
|
||||
-D${variableName}=${value})
|
||||
@ -665,6 +700,7 @@ if (CLANG_ENABLE_BOOTSTRAP)
|
||||
USES_TERMINAL_CONFIGURE 1
|
||||
USES_TERMINAL_BUILD 1
|
||||
USES_TERMINAL_INSTALL 1
|
||||
LIST_SEPARATOR |
|
||||
)
|
||||
|
||||
# exclude really-install from main target
|
||||
|
@ -25,6 +25,10 @@ N: Eric Christopher
|
||||
E: echristo@gmail.com
|
||||
D: Debug Information, inline assembly
|
||||
|
||||
N: Devin Coughlin
|
||||
E: dcoughlin@apple.com
|
||||
D: Clang Static Analyzer
|
||||
|
||||
N: Doug Gregor
|
||||
E: dgregor@apple.com
|
||||
D: Emeritus owner
|
||||
@ -41,10 +45,6 @@ N: Anton Korobeynikov
|
||||
E: anton@korobeynikov.info
|
||||
D: Exception handling, Windows codegen, ARM EABI
|
||||
|
||||
N: Anna Zaks
|
||||
E: ganna@apple.com
|
||||
D: Clang Static Analyzer
|
||||
|
||||
N: John McCall
|
||||
E: rjmccall@apple.com
|
||||
D: Clang LLVM IR generation
|
||||
|
@ -13,10 +13,10 @@ different source-level tools. One example of this is the Clang Static Analyzer.
|
||||
If you're interested in more (including how to build Clang) it is best to read
|
||||
the relevant web sites. Here are some pointers:
|
||||
|
||||
Information on Clang: http://clang.llvm.org/
|
||||
Building and using Clang: http://clang.llvm.org/get_started.html
|
||||
Clang Static Analyzer: http://clang-analyzer.llvm.org/
|
||||
Information on the LLVM project: http://llvm.org/
|
||||
Information on Clang: http://clang.llvm.org/
|
||||
Building and using Clang: http://clang.llvm.org/get_started.html
|
||||
Clang Static Analyzer: http://clang-analyzer.llvm.org/
|
||||
Information on the LLVM project: http://llvm.org/
|
||||
|
||||
If you have questions or comments about Clang, a great place to discuss them is
|
||||
on the Clang development mailing list:
|
||||
@ -24,3 +24,4 @@ on the Clang development mailing list:
|
||||
|
||||
If you find a bug in Clang, please file it in the LLVM bug tracker:
|
||||
http://llvm.org/bugs/
|
||||
|
||||
|
@ -5,11 +5,12 @@
|
||||
This directory implements Python bindings for Clang.
|
||||
|
||||
You may need to alter LD_LIBRARY_PATH so that the Clang library can be
|
||||
found. The unit tests are designed to be run with 'nosetests'. For example:
|
||||
found. The unit tests are designed to be run with any standard test
|
||||
runner. For example:
|
||||
--
|
||||
$ env PYTHONPATH=$(echo ~/llvm/tools/clang/bindings/python/) \
|
||||
LD_LIBRARY_PATH=$(llvm-config --libdir) \
|
||||
nosetests -v
|
||||
python -m unittest discover -v
|
||||
tests.cindex.test_index.test_create ... ok
|
||||
...
|
||||
|
||||
|
@ -94,6 +94,9 @@ if sys.version_info[0] == 3:
|
||||
return cls(param)
|
||||
if isinstance(param, bytes):
|
||||
return cls(param)
|
||||
if param is None:
|
||||
# Support passing null to C functions expecting char arrays
|
||||
return None
|
||||
raise TypeError("Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__))
|
||||
|
||||
@staticmethod
|
||||
@ -1476,6 +1479,12 @@ class Cursor(Structure):
|
||||
"""
|
||||
return conf.lib.clang_CXXMethod_isVirtual(self)
|
||||
|
||||
def is_abstract_record(self):
|
||||
"""Returns True if the cursor refers to a C++ record declaration
|
||||
that has pure virtual member functions.
|
||||
"""
|
||||
return conf.lib.clang_CXXRecord_isAbstract(self)
|
||||
|
||||
def is_scoped_enum(self):
|
||||
"""Returns True if the cursor refers to a scoped enum declaration.
|
||||
"""
|
||||
@ -1548,6 +1557,22 @@ class Cursor(Structure):
|
||||
|
||||
return self._loc
|
||||
|
||||
@property
|
||||
def linkage(self):
|
||||
"""Return the linkage of this cursor."""
|
||||
if not hasattr(self, '_linkage'):
|
||||
self._linkage = conf.lib.clang_getCursorLinkage(self)
|
||||
|
||||
return LinkageKind.from_id(self._linkage)
|
||||
|
||||
@property
|
||||
def tls_kind(self):
|
||||
"""Return the thread-local storage (TLS) kind of this cursor."""
|
||||
if not hasattr(self, '_tls_kind'):
|
||||
self._tls_kind = conf.lib.clang_getCursorTLSKind(self)
|
||||
|
||||
return TLSKind.from_id(self._tls_kind)
|
||||
|
||||
@property
|
||||
def extent(self):
|
||||
"""
|
||||
@ -1570,6 +1595,16 @@ class Cursor(Structure):
|
||||
|
||||
return StorageClass.from_id(self._storage_class)
|
||||
|
||||
@property
|
||||
def availability(self):
|
||||
"""
|
||||
Retrieves the availability of the entity pointed at by the cursor.
|
||||
"""
|
||||
if not hasattr(self, '_availability'):
|
||||
self._availability = conf.lib.clang_getCursorAvailability(self)
|
||||
|
||||
return AvailabilityKind.from_id(self._availability)
|
||||
|
||||
@property
|
||||
def access_specifier(self):
|
||||
"""
|
||||
@ -1907,6 +1942,24 @@ StorageClass.OPENCLWORKGROUPLOCAL = StorageClass(5)
|
||||
StorageClass.AUTO = StorageClass(6)
|
||||
StorageClass.REGISTER = StorageClass(7)
|
||||
|
||||
### Availability Kinds ###
|
||||
|
||||
class AvailabilityKind(BaseEnumeration):
|
||||
"""
|
||||
Describes the availability of an entity.
|
||||
"""
|
||||
|
||||
# The unique kind objects, indexed by id.
|
||||
_kinds = []
|
||||
_name_map = None
|
||||
|
||||
def __repr__(self):
|
||||
return 'AvailabilityKind.%s' % (self.name,)
|
||||
|
||||
AvailabilityKind.AVAILABLE = AvailabilityKind(0)
|
||||
AvailabilityKind.DEPRECATED = AvailabilityKind(1)
|
||||
AvailabilityKind.NOT_AVAILABLE = AvailabilityKind(2)
|
||||
AvailabilityKind.NOT_ACCESSIBLE = AvailabilityKind(3)
|
||||
|
||||
### C++ access specifiers ###
|
||||
|
||||
@ -2061,6 +2114,42 @@ RefQualifierKind.NONE = RefQualifierKind(0)
|
||||
RefQualifierKind.LVALUE = RefQualifierKind(1)
|
||||
RefQualifierKind.RVALUE = RefQualifierKind(2)
|
||||
|
||||
class LinkageKind(BaseEnumeration):
|
||||
"""Describes the kind of linkage of a cursor."""
|
||||
|
||||
# The unique kind objects, indexed by id.
|
||||
_kinds = []
|
||||
_name_map = None
|
||||
|
||||
def from_param(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return 'LinkageKind.%s' % (self.name,)
|
||||
|
||||
LinkageKind.INVALID = LinkageKind(0)
|
||||
LinkageKind.NO_LINKAGE = LinkageKind(1)
|
||||
LinkageKind.INTERNAL = LinkageKind(2)
|
||||
LinkageKind.UNIQUE_EXTERNAL = LinkageKind(3)
|
||||
LinkageKind.EXTERNAL = LinkageKind(4)
|
||||
|
||||
class TLSKind(BaseEnumeration):
|
||||
"""Describes the kind of thread-local storage (TLS) of a cursor."""
|
||||
|
||||
# The unique kind objects, indexed by id.
|
||||
_kinds = []
|
||||
_name_map = None
|
||||
|
||||
def from_param(self):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return 'TLSKind.%s' % (self.name,)
|
||||
|
||||
TLSKind.NONE = TLSKind(0)
|
||||
TLSKind.DYNAMIC = TLSKind(1)
|
||||
TLSKind.STATIC = TLSKind(2)
|
||||
|
||||
class Type(Structure):
|
||||
"""
|
||||
The type of an element in the abstract syntax tree.
|
||||
@ -3191,6 +3280,7 @@ class Token(Structure):
|
||||
def cursor(self):
|
||||
"""The Cursor this Token corresponds to."""
|
||||
cursor = Cursor()
|
||||
cursor._tu = self._tu
|
||||
|
||||
conf.lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor))
|
||||
|
||||
@ -3317,6 +3407,10 @@ functionList = [
|
||||
[Cursor],
|
||||
bool),
|
||||
|
||||
("clang_CXXRecord_isAbstract",
|
||||
[Cursor],
|
||||
bool),
|
||||
|
||||
("clang_EnumDecl_isScoped",
|
||||
[Cursor],
|
||||
bool),
|
||||
@ -3438,6 +3532,10 @@ functionList = [
|
||||
[TranslationUnit, SourceLocation],
|
||||
Cursor),
|
||||
|
||||
("clang_getCursorAvailability",
|
||||
[Cursor],
|
||||
c_int),
|
||||
|
||||
("clang_getCursorDefinition",
|
||||
[Cursor],
|
||||
Cursor,
|
||||
@ -4053,6 +4151,7 @@ conf = Config()
|
||||
register_enumerations()
|
||||
|
||||
__all__ = [
|
||||
'AvailabilityKind',
|
||||
'Config',
|
||||
'CodeCompletionResults',
|
||||
'CompilationDatabase',
|
||||
@ -4064,8 +4163,10 @@ __all__ = [
|
||||
'File',
|
||||
'FixIt',
|
||||
'Index',
|
||||
'LinkageKind',
|
||||
'SourceLocation',
|
||||
'SourceRange',
|
||||
'TLSKind',
|
||||
'TokenKind',
|
||||
'Token',
|
||||
'TranslationUnitLoadError',
|
||||
|
@ -6,10 +6,14 @@ from clang.cindex import TranslationUnit
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
def test_access_specifiers():
|
||||
"""Ensure that C++ access specifiers are available on cursors"""
|
||||
import unittest
|
||||
|
||||
tu = get_tu("""
|
||||
|
||||
class TestAccessSpecifiers(unittest.TestCase):
|
||||
def test_access_specifiers(self):
|
||||
"""Ensure that C++ access specifiers are available on cursors"""
|
||||
|
||||
tu = get_tu("""
|
||||
class test_class {
|
||||
public:
|
||||
void public_member_function();
|
||||
@ -20,15 +24,14 @@ private:
|
||||
};
|
||||
""", lang = 'cpp')
|
||||
|
||||
test_class = get_cursor(tu, "test_class")
|
||||
assert test_class.access_specifier == AccessSpecifier.INVALID;
|
||||
test_class = get_cursor(tu, "test_class")
|
||||
self.assertEqual(test_class.access_specifier, AccessSpecifier.INVALID)
|
||||
|
||||
public = get_cursor(tu.cursor, "public_member_function")
|
||||
assert public.access_specifier == AccessSpecifier.PUBLIC
|
||||
public = get_cursor(tu.cursor, "public_member_function")
|
||||
self.assertEqual(public.access_specifier, AccessSpecifier.PUBLIC)
|
||||
|
||||
protected = get_cursor(tu.cursor, "protected_member_function")
|
||||
assert protected.access_specifier == AccessSpecifier.PROTECTED
|
||||
|
||||
private = get_cursor(tu.cursor, "private_member_function")
|
||||
assert private.access_specifier == AccessSpecifier.PRIVATE
|
||||
protected = get_cursor(tu.cursor, "protected_member_function")
|
||||
self.assertEqual(protected.access_specifier, AccessSpecifier.PROTECTED)
|
||||
|
||||
private = get_cursor(tu.cursor, "private_member_function")
|
||||
self.assertEqual(private.access_specifier, AccessSpecifier.PRIVATE)
|
||||
|
@ -4,114 +4,116 @@ from clang.cindex import CompileCommands
|
||||
from clang.cindex import CompileCommand
|
||||
import os
|
||||
import gc
|
||||
import unittest
|
||||
|
||||
|
||||
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
|
||||
|
||||
def test_create_fail():
|
||||
"""Check we fail loading a database with an assertion"""
|
||||
path = os.path.dirname(__file__)
|
||||
try:
|
||||
cdb = CompilationDatabase.fromDirectory(path)
|
||||
except CompilationDatabaseError as e:
|
||||
assert e.cdb_error == CompilationDatabaseError.ERROR_CANNOTLOADDATABASE
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_create():
|
||||
"""Check we can load a compilation database"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
class TestCDB(unittest.TestCase):
|
||||
def test_create_fail(self):
|
||||
"""Check we fail loading a database with an assertion"""
|
||||
path = os.path.dirname(__file__)
|
||||
with self.assertRaises(CompilationDatabaseError) as cm:
|
||||
cdb = CompilationDatabase.fromDirectory(path)
|
||||
e = cm.exception
|
||||
self.assertEqual(e.cdb_error,
|
||||
CompilationDatabaseError.ERROR_CANNOTLOADDATABASE)
|
||||
|
||||
def test_lookup_fail():
|
||||
"""Check file lookup failure"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
assert cdb.getCompileCommands('file_do_not_exist.cpp') == None
|
||||
def test_create(self):
|
||||
"""Check we can load a compilation database"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
|
||||
def test_lookup_succeed():
|
||||
"""Check we get some results if the file exists in the db"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
assert len(cmds) != 0
|
||||
def test_lookup_fail(self):
|
||||
"""Check file lookup failure"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
self.assertIsNone(cdb.getCompileCommands('file_do_not_exist.cpp'))
|
||||
|
||||
def test_all_compilecommand():
|
||||
"""Check we get all results from the db"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getAllCompileCommands()
|
||||
assert len(cmds) == 3
|
||||
expected = [
|
||||
{ 'wd': '/home/john.doe/MyProject',
|
||||
'file': '/home/john.doe/MyProject/project.cpp',
|
||||
'line': ['clang++', '-o', 'project.o', '-c',
|
||||
'/home/john.doe/MyProject/project.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectA',
|
||||
'file': '/home/john.doe/MyProject/project2.cpp',
|
||||
'line': ['clang++', '-o', 'project2.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectB',
|
||||
'file': '/home/john.doe/MyProject/project2.cpp',
|
||||
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
def test_lookup_succeed(self):
|
||||
"""Check we get some results if the file exists in the db"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
self.assertNotEqual(len(cmds), 0)
|
||||
|
||||
]
|
||||
for i in range(len(cmds)):
|
||||
assert cmds[i].directory == expected[i]['wd']
|
||||
assert cmds[i].filename == expected[i]['file']
|
||||
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
|
||||
assert arg == exp
|
||||
def test_all_compilecommand(self):
|
||||
"""Check we get all results from the db"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getAllCompileCommands()
|
||||
self.assertEqual(len(cmds), 3)
|
||||
expected = [
|
||||
{ 'wd': '/home/john.doe/MyProject',
|
||||
'file': '/home/john.doe/MyProject/project.cpp',
|
||||
'line': ['clang++', '-o', 'project.o', '-c',
|
||||
'/home/john.doe/MyProject/project.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectA',
|
||||
'file': '/home/john.doe/MyProject/project2.cpp',
|
||||
'line': ['clang++', '-o', 'project2.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectB',
|
||||
'file': '/home/john.doe/MyProject/project2.cpp',
|
||||
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
|
||||
def test_1_compilecommand():
|
||||
"""Check file with single compile command"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
file = '/home/john.doe/MyProject/project.cpp'
|
||||
cmds = cdb.getCompileCommands(file)
|
||||
assert len(cmds) == 1
|
||||
assert cmds[0].directory == os.path.dirname(file)
|
||||
assert cmds[0].filename == file
|
||||
expected = [ 'clang++', '-o', 'project.o', '-c',
|
||||
'/home/john.doe/MyProject/project.cpp']
|
||||
for arg, exp in zip(cmds[0].arguments, expected):
|
||||
assert arg == exp
|
||||
]
|
||||
for i in range(len(cmds)):
|
||||
self.assertEqual(cmds[i].directory, expected[i]['wd'])
|
||||
self.assertEqual(cmds[i].filename, expected[i]['file'])
|
||||
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
|
||||
self.assertEqual(arg, exp)
|
||||
|
||||
def test_2_compilecommand():
|
||||
"""Check file with 2 compile commands"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp')
|
||||
assert len(cmds) == 2
|
||||
expected = [
|
||||
{ 'wd': '/home/john.doe/MyProjectA',
|
||||
'line': ['clang++', '-o', 'project2.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectB',
|
||||
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']}
|
||||
]
|
||||
for i in range(len(cmds)):
|
||||
assert cmds[i].directory == expected[i]['wd']
|
||||
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
|
||||
assert arg == exp
|
||||
def test_1_compilecommand(self):
|
||||
"""Check file with single compile command"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
file = '/home/john.doe/MyProject/project.cpp'
|
||||
cmds = cdb.getCompileCommands(file)
|
||||
self.assertEqual(len(cmds), 1)
|
||||
self.assertEqual(cmds[0].directory, os.path.dirname(file))
|
||||
self.assertEqual(cmds[0].filename, file)
|
||||
expected = [ 'clang++', '-o', 'project.o', '-c',
|
||||
'/home/john.doe/MyProject/project.cpp']
|
||||
for arg, exp in zip(cmds[0].arguments, expected):
|
||||
self.assertEqual(arg, exp)
|
||||
|
||||
def test_compilecommand_iterator_stops():
|
||||
"""Check that iterator stops after the correct number of elements"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
count = 0
|
||||
for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'):
|
||||
count += 1
|
||||
assert count <= 2
|
||||
def test_2_compilecommand(self):
|
||||
"""Check file with 2 compile commands"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp')
|
||||
self.assertEqual(len(cmds), 2)
|
||||
expected = [
|
||||
{ 'wd': '/home/john.doe/MyProjectA',
|
||||
'line': ['clang++', '-o', 'project2.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']},
|
||||
{ 'wd': '/home/john.doe/MyProjectB',
|
||||
'line': ['clang++', '-DFEATURE=1', '-o', 'project2-feature.o', '-c',
|
||||
'/home/john.doe/MyProject/project2.cpp']}
|
||||
]
|
||||
for i in range(len(cmds)):
|
||||
self.assertEqual(cmds[i].directory, expected[i]['wd'])
|
||||
for arg, exp in zip(cmds[i].arguments, expected[i]['line']):
|
||||
self.assertEqual(arg, exp)
|
||||
|
||||
def test_compilationDB_references():
|
||||
"""Ensure CompilationsCommands are independent of the database"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
del cdb
|
||||
gc.collect()
|
||||
workingdir = cmds[0].directory
|
||||
def test_compilecommand_iterator_stops(self):
|
||||
"""Check that iterator stops after the correct number of elements"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
count = 0
|
||||
for cmd in cdb.getCompileCommands('/home/john.doe/MyProject/project2.cpp'):
|
||||
count += 1
|
||||
self.assertLessEqual(count, 2)
|
||||
|
||||
def test_compilationCommands_references():
|
||||
"""Ensure CompilationsCommand keeps a reference to CompilationCommands"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
del cdb
|
||||
cmd0 = cmds[0]
|
||||
del cmds
|
||||
gc.collect()
|
||||
workingdir = cmd0.directory
|
||||
def test_compilationDB_references(self):
|
||||
"""Ensure CompilationsCommands are independent of the database"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
del cdb
|
||||
gc.collect()
|
||||
workingdir = cmds[0].directory
|
||||
|
||||
def test_compilationCommands_references(self):
|
||||
"""Ensure CompilationsCommand keeps a reference to CompilationCommands"""
|
||||
cdb = CompilationDatabase.fromDirectory(kInputsDir)
|
||||
cmds = cdb.getCompileCommands('/home/john.doe/MyProject/project.cpp')
|
||||
del cdb
|
||||
cmd0 = cmds[0]
|
||||
del cmds
|
||||
gc.collect()
|
||||
workingdir = cmd0.directory
|
||||
|
@ -1,16 +1,20 @@
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
def check_completion_results(cr, expected):
|
||||
assert cr is not None
|
||||
assert len(cr.diagnostics) == 0
|
||||
import unittest
|
||||
|
||||
completions = [str(c) for c in cr.results]
|
||||
|
||||
for c in expected:
|
||||
assert c in completions
|
||||
class TestCodeCompletion(unittest.TestCase):
|
||||
def check_completion_results(self, cr, expected):
|
||||
self.assertIsNotNone(cr)
|
||||
self.assertEqual(len(cr.diagnostics), 0)
|
||||
|
||||
def test_code_complete():
|
||||
files = [('fake.c', """
|
||||
completions = [str(c) for c in cr.results]
|
||||
|
||||
for c in expected:
|
||||
self.assertIn(c, completions)
|
||||
|
||||
def test_code_complete(self):
|
||||
files = [('fake.c', """
|
||||
/// Aaa.
|
||||
int test1;
|
||||
|
||||
@ -22,20 +26,20 @@ void f() {
|
||||
}
|
||||
""")]
|
||||
|
||||
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
|
||||
cr = tu.codeComplete('fake.c', 9, 1, unsaved_files=files, include_brief_comments=True)
|
||||
cr = tu.codeComplete('fake.c', 9, 1, unsaved_files=files, include_brief_comments=True)
|
||||
|
||||
expected = [
|
||||
"{'int', ResultType} | {'test1', TypedText} || Priority: 50 || Availability: Available || Brief comment: Aaa.",
|
||||
"{'void', ResultType} | {'test2', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 50 || Availability: Available || Brief comment: Bbb.",
|
||||
"{'return', TypedText} || Priority: 40 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
check_completion_results(cr, expected)
|
||||
expected = [
|
||||
"{'int', ResultType} | {'test1', TypedText} || Priority: 50 || Availability: Available || Brief comment: Aaa.",
|
||||
"{'void', ResultType} | {'test2', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 50 || Availability: Available || Brief comment: Bbb.",
|
||||
"{'return', TypedText} || Priority: 40 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
||||
|
||||
def test_code_complete_availability():
|
||||
files = [('fake.cpp', """
|
||||
def test_code_complete_availability(self):
|
||||
files = [('fake.cpp', """
|
||||
class P {
|
||||
protected:
|
||||
int member;
|
||||
@ -52,24 +56,24 @@ void f(P x, Q y) {
|
||||
}
|
||||
""")]
|
||||
|
||||
tu = TranslationUnit.from_source('fake.cpp', ['-std=c++98'], unsaved_files=files)
|
||||
tu = TranslationUnit.from_source('fake.cpp', ['-std=c++98'], unsaved_files=files)
|
||||
|
||||
cr = tu.codeComplete('fake.cpp', 12, 5, unsaved_files=files)
|
||||
cr = tu.codeComplete('fake.cpp', 12, 5, unsaved_files=files)
|
||||
|
||||
expected = [
|
||||
"{'const', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'volatile', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'operator', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
|
||||
"{'Q', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
check_completion_results(cr, expected)
|
||||
expected = [
|
||||
"{'const', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'volatile', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'operator', TypedText} || Priority: 40 || Availability: Available || Brief comment: None",
|
||||
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
|
||||
"{'Q', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
||||
|
||||
cr = tu.codeComplete('fake.cpp', 13, 5, unsaved_files=files)
|
||||
expected = [
|
||||
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
|
||||
"{'P &', ResultType} | {'operator=', TypedText} | {'(', LeftParen} | {'const P &', Placeholder} | {')', RightParen} || Priority: 34 || Availability: Available || Brief comment: None",
|
||||
"{'int', ResultType} | {'member', TypedText} || Priority: 35 || Availability: NotAccessible || Brief comment: None",
|
||||
"{'void', ResultType} | {'~P', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 34 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
check_completion_results(cr, expected)
|
||||
cr = tu.codeComplete('fake.cpp', 13, 5, unsaved_files=files)
|
||||
expected = [
|
||||
"{'P', TypedText} | {'::', Text} || Priority: 75 || Availability: Available || Brief comment: None",
|
||||
"{'P &', ResultType} | {'operator=', TypedText} | {'(', LeftParen} | {'const P &', Placeholder} | {')', RightParen} || Priority: 79 || Availability: Available || Brief comment: None",
|
||||
"{'int', ResultType} | {'member', TypedText} || Priority: 35 || Availability: NotAccessible || Brief comment: None",
|
||||
"{'void', ResultType} | {'~P', TypedText} | {'(', LeftParen} | {')', RightParen} || Priority: 79 || Availability: Available || Brief comment: None"
|
||||
]
|
||||
self.check_completion_results(cr, expected)
|
||||
|
@ -1,8 +1,12 @@
|
||||
from clang.cindex import TranslationUnit
|
||||
from tests.cindex.util import get_cursor
|
||||
|
||||
def test_comment():
|
||||
files = [('fake.c', """
|
||||
import unittest
|
||||
|
||||
|
||||
class TestComment(unittest.TestCase):
|
||||
def test_comment(self):
|
||||
files = [('fake.c', """
|
||||
/// Aaa.
|
||||
int test1;
|
||||
|
||||
@ -14,27 +18,25 @@ void f() {
|
||||
|
||||
}
|
||||
""")]
|
||||
# make a comment-aware TU
|
||||
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
test1 = get_cursor(tu, 'test1')
|
||||
assert test1 is not None, "Could not find test1."
|
||||
assert test1.type.is_pod()
|
||||
raw = test1.raw_comment
|
||||
brief = test1.brief_comment
|
||||
assert raw == """/// Aaa."""
|
||||
assert brief == """Aaa."""
|
||||
|
||||
test2 = get_cursor(tu, 'test2')
|
||||
raw = test2.raw_comment
|
||||
brief = test2.brief_comment
|
||||
assert raw == """/// Bbb.\n/// x"""
|
||||
assert brief == """Bbb. x"""
|
||||
|
||||
f = get_cursor(tu, 'f')
|
||||
raw = f.raw_comment
|
||||
brief = f.brief_comment
|
||||
assert raw is None
|
||||
assert brief is None
|
||||
# make a comment-aware TU
|
||||
tu = TranslationUnit.from_source('fake.c', ['-std=c99'], unsaved_files=files,
|
||||
options=TranslationUnit.PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION)
|
||||
test1 = get_cursor(tu, 'test1')
|
||||
self.assertIsNotNone(test1, "Could not find test1.")
|
||||
self.assertTrue(test1.type.is_pod())
|
||||
raw = test1.raw_comment
|
||||
brief = test1.brief_comment
|
||||
self.assertEqual(raw, """/// Aaa.""")
|
||||
self.assertEqual(brief, """Aaa.""")
|
||||
|
||||
test2 = get_cursor(tu, 'test2')
|
||||
raw = test2.raw_comment
|
||||
brief = test2.brief_comment
|
||||
self.assertEqual(raw, """/// Bbb.\n/// x""")
|
||||
self.assertEqual(brief, """Bbb. x""")
|
||||
|
||||
f = get_cursor(tu, 'f')
|
||||
raw = f.raw_comment
|
||||
brief = f.brief_comment
|
||||
self.assertIsNone(raw)
|
||||
self.assertIsNone(brief)
|
||||
|
@ -1,6 +1,8 @@
|
||||
import ctypes
|
||||
import gc
|
||||
import unittest
|
||||
|
||||
from clang.cindex import AvailabilityKind
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import TemplateArgumentKind
|
||||
from clang.cindex import TranslationUnit
|
||||
@ -9,6 +11,7 @@ from .util import get_cursor
|
||||
from .util import get_cursors
|
||||
from .util import get_tu
|
||||
|
||||
|
||||
kInput = """\
|
||||
struct s0 {
|
||||
int a;
|
||||
@ -29,257 +32,6 @@ void f0(int a0, int a1) {
|
||||
}
|
||||
"""
|
||||
|
||||
def test_get_children():
|
||||
tu = get_tu(kInput)
|
||||
|
||||
it = tu.cursor.get_children()
|
||||
tu_nodes = list(it)
|
||||
|
||||
assert len(tu_nodes) == 3
|
||||
for cursor in tu_nodes:
|
||||
assert cursor.translation_unit is not None
|
||||
|
||||
assert tu_nodes[0] != tu_nodes[1]
|
||||
assert tu_nodes[0].kind == CursorKind.STRUCT_DECL
|
||||
assert tu_nodes[0].spelling == 's0'
|
||||
assert tu_nodes[0].is_definition() == True
|
||||
assert tu_nodes[0].location.file.name == 't.c'
|
||||
assert tu_nodes[0].location.line == 1
|
||||
assert tu_nodes[0].location.column == 8
|
||||
assert tu_nodes[0].hash > 0
|
||||
assert tu_nodes[0].translation_unit is not None
|
||||
|
||||
s0_nodes = list(tu_nodes[0].get_children())
|
||||
assert len(s0_nodes) == 2
|
||||
assert s0_nodes[0].kind == CursorKind.FIELD_DECL
|
||||
assert s0_nodes[0].spelling == 'a'
|
||||
assert s0_nodes[0].type.kind == TypeKind.INT
|
||||
assert s0_nodes[1].kind == CursorKind.FIELD_DECL
|
||||
assert s0_nodes[1].spelling == 'b'
|
||||
assert s0_nodes[1].type.kind == TypeKind.INT
|
||||
|
||||
assert tu_nodes[1].kind == CursorKind.STRUCT_DECL
|
||||
assert tu_nodes[1].spelling == 's1'
|
||||
assert tu_nodes[1].displayname == 's1'
|
||||
assert tu_nodes[1].is_definition() == False
|
||||
|
||||
assert tu_nodes[2].kind == CursorKind.FUNCTION_DECL
|
||||
assert tu_nodes[2].spelling == 'f0'
|
||||
assert tu_nodes[2].displayname == 'f0(int, int)'
|
||||
assert tu_nodes[2].is_definition() == True
|
||||
|
||||
def test_references():
|
||||
"""Ensure that references to TranslationUnit are kept."""
|
||||
tu = get_tu('int x;')
|
||||
cursors = list(tu.cursor.get_children())
|
||||
assert len(cursors) > 0
|
||||
|
||||
cursor = cursors[0]
|
||||
assert isinstance(cursor.translation_unit, TranslationUnit)
|
||||
|
||||
# Delete reference to TU and perform a full GC.
|
||||
del tu
|
||||
gc.collect()
|
||||
assert isinstance(cursor.translation_unit, TranslationUnit)
|
||||
|
||||
# If the TU was destroyed, this should cause a segfault.
|
||||
parent = cursor.semantic_parent
|
||||
|
||||
def test_canonical():
|
||||
source = 'struct X; struct X; struct X { int member; };'
|
||||
tu = get_tu(source)
|
||||
|
||||
cursors = []
|
||||
for cursor in tu.cursor.get_children():
|
||||
if cursor.spelling == 'X':
|
||||
cursors.append(cursor)
|
||||
|
||||
assert len(cursors) == 3
|
||||
assert cursors[1].canonical == cursors[2].canonical
|
||||
|
||||
def test_is_const_method():
|
||||
"""Ensure Cursor.is_const_method works."""
|
||||
source = 'class X { void foo() const; void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
assert cls is not None
|
||||
assert foo is not None
|
||||
assert bar is not None
|
||||
|
||||
assert foo.is_const_method()
|
||||
assert not bar.is_const_method()
|
||||
|
||||
def test_is_converting_constructor():
|
||||
"""Ensure Cursor.is_converting_constructor works."""
|
||||
source = 'class X { explicit X(int); X(double); X(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
|
||||
assert len(xs) == 4
|
||||
assert xs[0].kind == CursorKind.CLASS_DECL
|
||||
cs = xs[1:]
|
||||
assert cs[0].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[1].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[2].kind == CursorKind.CONSTRUCTOR
|
||||
|
||||
assert not cs[0].is_converting_constructor()
|
||||
assert cs[1].is_converting_constructor()
|
||||
assert not cs[2].is_converting_constructor()
|
||||
|
||||
|
||||
def test_is_copy_constructor():
|
||||
"""Ensure Cursor.is_copy_constructor works."""
|
||||
source = 'class X { X(); X(const X&); X(X&&); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
assert xs[0].kind == CursorKind.CLASS_DECL
|
||||
cs = xs[1:]
|
||||
assert cs[0].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[1].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[2].kind == CursorKind.CONSTRUCTOR
|
||||
|
||||
assert not cs[0].is_copy_constructor()
|
||||
assert cs[1].is_copy_constructor()
|
||||
assert not cs[2].is_copy_constructor()
|
||||
|
||||
def test_is_default_constructor():
|
||||
"""Ensure Cursor.is_default_constructor works."""
|
||||
source = 'class X { X(); X(int); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
assert xs[0].kind == CursorKind.CLASS_DECL
|
||||
cs = xs[1:]
|
||||
assert cs[0].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[1].kind == CursorKind.CONSTRUCTOR
|
||||
|
||||
assert cs[0].is_default_constructor()
|
||||
assert not cs[1].is_default_constructor()
|
||||
|
||||
def test_is_move_constructor():
|
||||
"""Ensure Cursor.is_move_constructor works."""
|
||||
source = 'class X { X(); X(const X&); X(X&&); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
assert xs[0].kind == CursorKind.CLASS_DECL
|
||||
cs = xs[1:]
|
||||
assert cs[0].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[1].kind == CursorKind.CONSTRUCTOR
|
||||
assert cs[2].kind == CursorKind.CONSTRUCTOR
|
||||
|
||||
assert not cs[0].is_move_constructor()
|
||||
assert not cs[1].is_move_constructor()
|
||||
assert cs[2].is_move_constructor()
|
||||
|
||||
def test_is_default_method():
|
||||
"""Ensure Cursor.is_default_method works."""
|
||||
source = 'class X { X() = default; }; class Y { Y(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
ys = get_cursors(tu, 'Y')
|
||||
|
||||
assert len(xs) == 2
|
||||
assert len(ys) == 2
|
||||
|
||||
xc = xs[1]
|
||||
yc = ys[1]
|
||||
|
||||
assert xc.is_default_method()
|
||||
assert not yc.is_default_method()
|
||||
|
||||
def test_is_mutable_field():
|
||||
"""Ensure Cursor.is_mutable_field works."""
|
||||
source = 'class X { int x_; mutable int y_; };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
x_ = get_cursor(tu, 'x_')
|
||||
y_ = get_cursor(tu, 'y_')
|
||||
assert cls is not None
|
||||
assert x_ is not None
|
||||
assert y_ is not None
|
||||
|
||||
assert not x_.is_mutable_field()
|
||||
assert y_.is_mutable_field()
|
||||
|
||||
def test_is_static_method():
|
||||
"""Ensure Cursor.is_static_method works."""
|
||||
|
||||
source = 'class X { static void foo(); void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
assert cls is not None
|
||||
assert foo is not None
|
||||
assert bar is not None
|
||||
|
||||
assert foo.is_static_method()
|
||||
assert not bar.is_static_method()
|
||||
|
||||
def test_is_pure_virtual_method():
|
||||
"""Ensure Cursor.is_pure_virtual_method works."""
|
||||
source = 'class X { virtual void foo() = 0; virtual void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
assert cls is not None
|
||||
assert foo is not None
|
||||
assert bar is not None
|
||||
|
||||
assert foo.is_pure_virtual_method()
|
||||
assert not bar.is_pure_virtual_method()
|
||||
|
||||
def test_is_virtual_method():
|
||||
"""Ensure Cursor.is_virtual_method works."""
|
||||
source = 'class X { virtual void foo(); void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
assert cls is not None
|
||||
assert foo is not None
|
||||
assert bar is not None
|
||||
|
||||
assert foo.is_virtual_method()
|
||||
assert not bar.is_virtual_method()
|
||||
|
||||
def test_is_scoped_enum():
|
||||
"""Ensure Cursor.is_scoped_enum works."""
|
||||
source = 'class X {}; enum RegularEnum {}; enum class ScopedEnum {};'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
regular_enum = get_cursor(tu, 'RegularEnum')
|
||||
scoped_enum = get_cursor(tu, 'ScopedEnum')
|
||||
assert cls is not None
|
||||
assert regular_enum is not None
|
||||
assert scoped_enum is not None
|
||||
|
||||
assert not cls.is_scoped_enum()
|
||||
assert not regular_enum.is_scoped_enum()
|
||||
assert scoped_enum.is_scoped_enum()
|
||||
|
||||
def test_underlying_type():
|
||||
tu = get_tu('typedef int foo;')
|
||||
typedef = get_cursor(tu, 'foo')
|
||||
assert typedef is not None
|
||||
|
||||
assert typedef.kind.is_declaration()
|
||||
underlying = typedef.underlying_typedef_type
|
||||
assert underlying.kind == TypeKind.INT
|
||||
|
||||
kParentTest = """\
|
||||
class C {
|
||||
void f();
|
||||
@ -287,122 +39,6 @@ kParentTest = """\
|
||||
|
||||
void C::f() { }
|
||||
"""
|
||||
def test_semantic_parent():
|
||||
tu = get_tu(kParentTest, 'cpp')
|
||||
curs = get_cursors(tu, 'f')
|
||||
decl = get_cursor(tu, 'C')
|
||||
assert(len(curs) == 2)
|
||||
assert(curs[0].semantic_parent == curs[1].semantic_parent)
|
||||
assert(curs[0].semantic_parent == decl)
|
||||
|
||||
def test_lexical_parent():
|
||||
tu = get_tu(kParentTest, 'cpp')
|
||||
curs = get_cursors(tu, 'f')
|
||||
decl = get_cursor(tu, 'C')
|
||||
assert(len(curs) == 2)
|
||||
assert(curs[0].lexical_parent != curs[1].lexical_parent)
|
||||
assert(curs[0].lexical_parent == decl)
|
||||
assert(curs[1].lexical_parent == tu.cursor)
|
||||
|
||||
def test_enum_type():
|
||||
tu = get_tu('enum TEST { FOO=1, BAR=2 };')
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
assert enum is not None
|
||||
|
||||
assert enum.kind == CursorKind.ENUM_DECL
|
||||
enum_type = enum.enum_type
|
||||
assert enum_type.kind == TypeKind.UINT
|
||||
|
||||
def test_enum_type_cpp():
|
||||
tu = get_tu('enum TEST : long long { FOO=1, BAR=2 };', lang="cpp")
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
assert enum is not None
|
||||
|
||||
assert enum.kind == CursorKind.ENUM_DECL
|
||||
assert enum.enum_type.kind == TypeKind.LONGLONG
|
||||
|
||||
def test_objc_type_encoding():
|
||||
tu = get_tu('int i;', lang='objc')
|
||||
i = get_cursor(tu, 'i')
|
||||
|
||||
assert i is not None
|
||||
assert i.objc_type_encoding == 'i'
|
||||
|
||||
def test_enum_values():
|
||||
tu = get_tu('enum TEST { SPAM=1, EGG, HAM = EGG * 20};')
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
assert enum is not None
|
||||
|
||||
assert enum.kind == CursorKind.ENUM_DECL
|
||||
|
||||
enum_constants = list(enum.get_children())
|
||||
assert len(enum_constants) == 3
|
||||
|
||||
spam, egg, ham = enum_constants
|
||||
|
||||
assert spam.kind == CursorKind.ENUM_CONSTANT_DECL
|
||||
assert spam.enum_value == 1
|
||||
assert egg.kind == CursorKind.ENUM_CONSTANT_DECL
|
||||
assert egg.enum_value == 2
|
||||
assert ham.kind == CursorKind.ENUM_CONSTANT_DECL
|
||||
assert ham.enum_value == 40
|
||||
|
||||
def test_enum_values_cpp():
|
||||
tu = get_tu('enum TEST : long long { SPAM = -1, HAM = 0x10000000000};', lang="cpp")
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
assert enum is not None
|
||||
|
||||
assert enum.kind == CursorKind.ENUM_DECL
|
||||
|
||||
enum_constants = list(enum.get_children())
|
||||
assert len(enum_constants) == 2
|
||||
|
||||
spam, ham = enum_constants
|
||||
|
||||
assert spam.kind == CursorKind.ENUM_CONSTANT_DECL
|
||||
assert spam.enum_value == -1
|
||||
assert ham.kind == CursorKind.ENUM_CONSTANT_DECL
|
||||
assert ham.enum_value == 0x10000000000
|
||||
|
||||
def test_annotation_attribute():
|
||||
tu = get_tu('int foo (void) __attribute__ ((annotate("here be annotation attribute")));')
|
||||
|
||||
foo = get_cursor(tu, 'foo')
|
||||
assert foo is not None
|
||||
|
||||
for c in foo.get_children():
|
||||
if c.kind == CursorKind.ANNOTATE_ATTR:
|
||||
assert c.displayname == "here be annotation attribute"
|
||||
break
|
||||
else:
|
||||
assert False, "Couldn't find annotation"
|
||||
|
||||
def test_result_type():
|
||||
tu = get_tu('int foo();')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
assert foo is not None
|
||||
t = foo.result_type
|
||||
assert t.kind == TypeKind.INT
|
||||
|
||||
def test_get_tokens():
|
||||
"""Ensure we can map cursors back to tokens."""
|
||||
tu = get_tu('int foo(int i);')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
tokens = list(foo.get_tokens())
|
||||
assert len(tokens) == 6
|
||||
assert tokens[0].spelling == 'int'
|
||||
assert tokens[1].spelling == 'foo'
|
||||
|
||||
def test_get_arguments():
|
||||
tu = get_tu('void foo(int i, int j);')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
arguments = list(foo.get_arguments())
|
||||
|
||||
assert len(arguments) == 2
|
||||
assert arguments[0].spelling == "i"
|
||||
assert arguments[1].spelling == "j"
|
||||
|
||||
kTemplateArgTest = """\
|
||||
template <int kInt, typename T, bool kBool>
|
||||
@ -412,59 +48,505 @@ kTemplateArgTest = """\
|
||||
void foo<-7, float, true>();
|
||||
"""
|
||||
|
||||
def test_get_num_template_arguments():
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
class TestCursor(unittest.TestCase):
|
||||
def test_get_children(self):
|
||||
tu = get_tu(kInput)
|
||||
|
||||
assert foos[1].get_num_template_arguments() == 3
|
||||
it = tu.cursor.get_children()
|
||||
tu_nodes = list(it)
|
||||
|
||||
def test_get_template_argument_kind():
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
self.assertEqual(len(tu_nodes), 3)
|
||||
for cursor in tu_nodes:
|
||||
self.assertIsNotNone(cursor.translation_unit)
|
||||
|
||||
assert foos[1].get_template_argument_kind(0) == TemplateArgumentKind.INTEGRAL
|
||||
assert foos[1].get_template_argument_kind(1) == TemplateArgumentKind.TYPE
|
||||
assert foos[1].get_template_argument_kind(2) == TemplateArgumentKind.INTEGRAL
|
||||
self.assertNotEqual(tu_nodes[0], tu_nodes[1])
|
||||
self.assertEqual(tu_nodes[0].kind, CursorKind.STRUCT_DECL)
|
||||
self.assertEqual(tu_nodes[0].spelling, 's0')
|
||||
self.assertEqual(tu_nodes[0].is_definition(), True)
|
||||
self.assertEqual(tu_nodes[0].location.file.name, 't.c')
|
||||
self.assertEqual(tu_nodes[0].location.line, 1)
|
||||
self.assertEqual(tu_nodes[0].location.column, 8)
|
||||
self.assertGreater(tu_nodes[0].hash, 0)
|
||||
self.assertIsNotNone(tu_nodes[0].translation_unit)
|
||||
|
||||
def test_get_template_argument_type():
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
s0_nodes = list(tu_nodes[0].get_children())
|
||||
self.assertEqual(len(s0_nodes), 2)
|
||||
self.assertEqual(s0_nodes[0].kind, CursorKind.FIELD_DECL)
|
||||
self.assertEqual(s0_nodes[0].spelling, 'a')
|
||||
self.assertEqual(s0_nodes[0].type.kind, TypeKind.INT)
|
||||
self.assertEqual(s0_nodes[1].kind, CursorKind.FIELD_DECL)
|
||||
self.assertEqual(s0_nodes[1].spelling, 'b')
|
||||
self.assertEqual(s0_nodes[1].type.kind, TypeKind.INT)
|
||||
|
||||
assert foos[1].get_template_argument_type(1).kind == TypeKind.FLOAT
|
||||
self.assertEqual(tu_nodes[1].kind, CursorKind.STRUCT_DECL)
|
||||
self.assertEqual(tu_nodes[1].spelling, 's1')
|
||||
self.assertEqual(tu_nodes[1].displayname, 's1')
|
||||
self.assertEqual(tu_nodes[1].is_definition(), False)
|
||||
|
||||
def test_get_template_argument_value():
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
self.assertEqual(tu_nodes[2].kind, CursorKind.FUNCTION_DECL)
|
||||
self.assertEqual(tu_nodes[2].spelling, 'f0')
|
||||
self.assertEqual(tu_nodes[2].displayname, 'f0(int, int)')
|
||||
self.assertEqual(tu_nodes[2].is_definition(), True)
|
||||
|
||||
assert foos[1].get_template_argument_value(0) == -7
|
||||
assert foos[1].get_template_argument_value(2) == True
|
||||
def test_references(self):
|
||||
"""Ensure that references to TranslationUnit are kept."""
|
||||
tu = get_tu('int x;')
|
||||
cursors = list(tu.cursor.get_children())
|
||||
self.assertGreater(len(cursors), 0)
|
||||
|
||||
def test_get_template_argument_unsigned_value():
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
cursor = cursors[0]
|
||||
self.assertIsInstance(cursor.translation_unit, TranslationUnit)
|
||||
|
||||
assert foos[1].get_template_argument_unsigned_value(0) == 2 ** 32 - 7
|
||||
assert foos[1].get_template_argument_unsigned_value(2) == True
|
||||
# Delete reference to TU and perform a full GC.
|
||||
del tu
|
||||
gc.collect()
|
||||
self.assertIsInstance(cursor.translation_unit, TranslationUnit)
|
||||
|
||||
def test_referenced():
|
||||
tu = get_tu('void foo(); void bar() { foo(); }')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
for c in bar.get_children():
|
||||
if c.kind == CursorKind.CALL_EXPR:
|
||||
assert c.referenced.spelling == foo.spelling
|
||||
break
|
||||
# If the TU was destroyed, this should cause a segfault.
|
||||
parent = cursor.semantic_parent
|
||||
|
||||
def test_mangled_name():
|
||||
kInputForMangling = """\
|
||||
int foo(int, int);
|
||||
"""
|
||||
tu = get_tu(kInputForMangling, lang='cpp')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
def test_canonical(self):
|
||||
source = 'struct X; struct X; struct X { int member; };'
|
||||
tu = get_tu(source)
|
||||
|
||||
# Since libclang does not link in targets, we cannot pass a triple to it
|
||||
# and force the target. To enable this test to pass on all platforms, accept
|
||||
# all valid manglings.
|
||||
# [c-index-test handles this by running the source through clang, emitting
|
||||
# an AST file and running libclang on that AST file]
|
||||
assert foo.mangled_name in ('_Z3fooii', '__Z3fooii', '?foo@@YAHHH')
|
||||
cursors = []
|
||||
for cursor in tu.cursor.get_children():
|
||||
if cursor.spelling == 'X':
|
||||
cursors.append(cursor)
|
||||
|
||||
self.assertEqual(len(cursors), 3)
|
||||
self.assertEqual(cursors[1].canonical, cursors[2].canonical)
|
||||
|
||||
def test_is_const_method(self):
|
||||
"""Ensure Cursor.is_const_method works."""
|
||||
source = 'class X { void foo() const; void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_const_method())
|
||||
self.assertFalse(bar.is_const_method())
|
||||
|
||||
def test_is_converting_constructor(self):
|
||||
"""Ensure Cursor.is_converting_constructor works."""
|
||||
source = 'class X { explicit X(int); X(double); X(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
|
||||
self.assertEqual(len(xs), 4)
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertFalse(cs[0].is_converting_constructor())
|
||||
self.assertTrue(cs[1].is_converting_constructor())
|
||||
self.assertFalse(cs[2].is_converting_constructor())
|
||||
|
||||
|
||||
def test_is_copy_constructor(self):
|
||||
"""Ensure Cursor.is_copy_constructor works."""
|
||||
source = 'class X { X(); X(const X&); X(X&&); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertFalse(cs[0].is_copy_constructor())
|
||||
self.assertTrue(cs[1].is_copy_constructor())
|
||||
self.assertFalse(cs[2].is_copy_constructor())
|
||||
|
||||
def test_is_default_constructor(self):
|
||||
"""Ensure Cursor.is_default_constructor works."""
|
||||
source = 'class X { X(); X(int); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertTrue(cs[0].is_default_constructor())
|
||||
self.assertFalse(cs[1].is_default_constructor())
|
||||
|
||||
def test_is_move_constructor(self):
|
||||
"""Ensure Cursor.is_move_constructor works."""
|
||||
source = 'class X { X(); X(const X&); X(X&&); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
||||
cs = xs[1:]
|
||||
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
||||
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
||||
|
||||
self.assertFalse(cs[0].is_move_constructor())
|
||||
self.assertFalse(cs[1].is_move_constructor())
|
||||
self.assertTrue(cs[2].is_move_constructor())
|
||||
|
||||
def test_is_default_method(self):
|
||||
"""Ensure Cursor.is_default_method works."""
|
||||
source = 'class X { X() = default; }; class Y { Y(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
xs = get_cursors(tu, 'X')
|
||||
ys = get_cursors(tu, 'Y')
|
||||
|
||||
self.assertEqual(len(xs), 2)
|
||||
self.assertEqual(len(ys), 2)
|
||||
|
||||
xc = xs[1]
|
||||
yc = ys[1]
|
||||
|
||||
self.assertTrue(xc.is_default_method())
|
||||
self.assertFalse(yc.is_default_method())
|
||||
|
||||
def test_is_mutable_field(self):
|
||||
"""Ensure Cursor.is_mutable_field works."""
|
||||
source = 'class X { int x_; mutable int y_; };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
x_ = get_cursor(tu, 'x_')
|
||||
y_ = get_cursor(tu, 'y_')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(x_)
|
||||
self.assertIsNotNone(y_)
|
||||
|
||||
self.assertFalse(x_.is_mutable_field())
|
||||
self.assertTrue(y_.is_mutable_field())
|
||||
|
||||
def test_is_static_method(self):
|
||||
"""Ensure Cursor.is_static_method works."""
|
||||
|
||||
source = 'class X { static void foo(); void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_static_method())
|
||||
self.assertFalse(bar.is_static_method())
|
||||
|
||||
def test_is_pure_virtual_method(self):
|
||||
"""Ensure Cursor.is_pure_virtual_method works."""
|
||||
source = 'class X { virtual void foo() = 0; virtual void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_pure_virtual_method())
|
||||
self.assertFalse(bar.is_pure_virtual_method())
|
||||
|
||||
def test_is_virtual_method(self):
|
||||
"""Ensure Cursor.is_virtual_method works."""
|
||||
source = 'class X { virtual void foo(); void bar(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertTrue(foo.is_virtual_method())
|
||||
self.assertFalse(bar.is_virtual_method())
|
||||
|
||||
def test_is_abstract_record(self):
|
||||
"""Ensure Cursor.is_abstract_record works."""
|
||||
source = 'struct X { virtual void x() = 0; }; struct Y : X { void x(); };'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
self.assertTrue(cls.is_abstract_record())
|
||||
|
||||
cls = get_cursor(tu, 'Y')
|
||||
self.assertFalse(cls.is_abstract_record())
|
||||
|
||||
def test_is_scoped_enum(self):
|
||||
"""Ensure Cursor.is_scoped_enum works."""
|
||||
source = 'class X {}; enum RegularEnum {}; enum class ScopedEnum {};'
|
||||
tu = get_tu(source, lang='cpp')
|
||||
|
||||
cls = get_cursor(tu, 'X')
|
||||
regular_enum = get_cursor(tu, 'RegularEnum')
|
||||
scoped_enum = get_cursor(tu, 'ScopedEnum')
|
||||
self.assertIsNotNone(cls)
|
||||
self.assertIsNotNone(regular_enum)
|
||||
self.assertIsNotNone(scoped_enum)
|
||||
|
||||
self.assertFalse(cls.is_scoped_enum())
|
||||
self.assertFalse(regular_enum.is_scoped_enum())
|
||||
self.assertTrue(scoped_enum.is_scoped_enum())
|
||||
|
||||
def test_underlying_type(self):
|
||||
tu = get_tu('typedef int foo;')
|
||||
typedef = get_cursor(tu, 'foo')
|
||||
self.assertIsNotNone(typedef)
|
||||
|
||||
self.assertTrue(typedef.kind.is_declaration())
|
||||
underlying = typedef.underlying_typedef_type
|
||||
self.assertEqual(underlying.kind, TypeKind.INT)
|
||||
|
||||
def test_semantic_parent(self):
|
||||
tu = get_tu(kParentTest, 'cpp')
|
||||
curs = get_cursors(tu, 'f')
|
||||
decl = get_cursor(tu, 'C')
|
||||
self.assertEqual(len(curs), 2)
|
||||
self.assertEqual(curs[0].semantic_parent, curs[1].semantic_parent)
|
||||
self.assertEqual(curs[0].semantic_parent, decl)
|
||||
|
||||
def test_lexical_parent(self):
|
||||
tu = get_tu(kParentTest, 'cpp')
|
||||
curs = get_cursors(tu, 'f')
|
||||
decl = get_cursor(tu, 'C')
|
||||
self.assertEqual(len(curs), 2)
|
||||
self.assertNotEqual(curs[0].lexical_parent, curs[1].lexical_parent)
|
||||
self.assertEqual(curs[0].lexical_parent, decl)
|
||||
self.assertEqual(curs[1].lexical_parent, tu.cursor)
|
||||
|
||||
def test_enum_type(self):
|
||||
tu = get_tu('enum TEST { FOO=1, BAR=2 };')
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
enum_type = enum.enum_type
|
||||
self.assertEqual(enum_type.kind, TypeKind.UINT)
|
||||
|
||||
def test_enum_type_cpp(self):
|
||||
tu = get_tu('enum TEST : long long { FOO=1, BAR=2 };', lang="cpp")
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
self.assertEqual(enum.enum_type.kind, TypeKind.LONGLONG)
|
||||
|
||||
def test_objc_type_encoding(self):
|
||||
tu = get_tu('int i;', lang='objc')
|
||||
i = get_cursor(tu, 'i')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertEqual(i.objc_type_encoding, 'i')
|
||||
|
||||
def test_enum_values(self):
|
||||
tu = get_tu('enum TEST { SPAM=1, EGG, HAM = EGG * 20};')
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
|
||||
enum_constants = list(enum.get_children())
|
||||
self.assertEqual(len(enum_constants), 3)
|
||||
|
||||
spam, egg, ham = enum_constants
|
||||
|
||||
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(spam.enum_value, 1)
|
||||
self.assertEqual(egg.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(egg.enum_value, 2)
|
||||
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(ham.enum_value, 40)
|
||||
|
||||
def test_enum_values_cpp(self):
|
||||
tu = get_tu('enum TEST : long long { SPAM = -1, HAM = 0x10000000000};', lang="cpp")
|
||||
enum = get_cursor(tu, 'TEST')
|
||||
self.assertIsNotNone(enum)
|
||||
|
||||
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
||||
|
||||
enum_constants = list(enum.get_children())
|
||||
self.assertEqual(len(enum_constants), 2)
|
||||
|
||||
spam, ham = enum_constants
|
||||
|
||||
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(spam.enum_value, -1)
|
||||
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
||||
self.assertEqual(ham.enum_value, 0x10000000000)
|
||||
|
||||
def test_annotation_attribute(self):
|
||||
tu = get_tu('int foo (void) __attribute__ ((annotate("here be annotation attribute")));')
|
||||
|
||||
foo = get_cursor(tu, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
|
||||
for c in foo.get_children():
|
||||
if c.kind == CursorKind.ANNOTATE_ATTR:
|
||||
self.assertEqual(c.displayname, "here be annotation attribute")
|
||||
break
|
||||
else:
|
||||
self.fail("Couldn't find annotation")
|
||||
|
||||
def test_annotation_template(self):
|
||||
annotation = '__attribute__ ((annotate("annotation")))'
|
||||
for source, kind in [
|
||||
('int foo (T value) %s;', CursorKind.FUNCTION_TEMPLATE),
|
||||
('class %s foo {};', CursorKind.CLASS_TEMPLATE),
|
||||
]:
|
||||
source = 'template<typename T> ' + (source % annotation)
|
||||
tu = get_tu(source, lang="cpp")
|
||||
|
||||
foo = get_cursor(tu, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertEqual(foo.kind, kind)
|
||||
|
||||
for c in foo.get_children():
|
||||
if c.kind == CursorKind.ANNOTATE_ATTR:
|
||||
self.assertEqual(c.displayname, "annotation")
|
||||
break
|
||||
else:
|
||||
self.fail("Couldn't find annotation for {}".format(kind))
|
||||
|
||||
def test_result_type(self):
|
||||
tu = get_tu('int foo();')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
self.assertIsNotNone(foo)
|
||||
t = foo.result_type
|
||||
self.assertEqual(t.kind, TypeKind.INT)
|
||||
|
||||
def test_availability(self):
|
||||
tu = get_tu('class A { A(A const&) = delete; };', lang='cpp')
|
||||
|
||||
# AvailabilityKind.AVAILABLE
|
||||
cursor = get_cursor(tu, 'A')
|
||||
self.assertEqual(cursor.kind, CursorKind.CLASS_DECL)
|
||||
self.assertEqual(cursor.availability, AvailabilityKind.AVAILABLE)
|
||||
|
||||
# AvailabilityKind.NOT_AVAILABLE
|
||||
cursors = get_cursors(tu, 'A')
|
||||
for c in cursors:
|
||||
if c.kind == CursorKind.CONSTRUCTOR:
|
||||
self.assertEqual(c.availability, AvailabilityKind.NOT_AVAILABLE)
|
||||
break
|
||||
else:
|
||||
self.fail("Could not find cursor for deleted constructor")
|
||||
|
||||
# AvailabilityKind.DEPRECATED
|
||||
tu = get_tu('void test() __attribute__((deprecated));', lang='cpp')
|
||||
cursor = get_cursor(tu, 'test')
|
||||
self.assertEqual(cursor.availability, AvailabilityKind.DEPRECATED)
|
||||
|
||||
# AvailabilityKind.NOT_ACCESSIBLE is only used in the code completion results
|
||||
|
||||
def test_get_tokens(self):
|
||||
"""Ensure we can map cursors back to tokens."""
|
||||
tu = get_tu('int foo(int i);')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
tokens = list(foo.get_tokens())
|
||||
self.assertEqual(len(tokens), 6)
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
self.assertEqual(tokens[1].spelling, 'foo')
|
||||
|
||||
def test_get_token_cursor(self):
|
||||
"""Ensure we can map tokens to cursors."""
|
||||
tu = get_tu('class A {}; int foo(A var = A());', lang='cpp')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
for cursor in foo.walk_preorder():
|
||||
if cursor.kind.is_expression() and not cursor.kind.is_statement():
|
||||
break
|
||||
else:
|
||||
self.fail("Could not find default value expression")
|
||||
|
||||
tokens = list(cursor.get_tokens())
|
||||
self.assertEqual(len(tokens), 4, [t.spelling for t in tokens])
|
||||
self.assertEqual(tokens[0].spelling, '=')
|
||||
self.assertEqual(tokens[1].spelling, 'A')
|
||||
self.assertEqual(tokens[2].spelling, '(')
|
||||
self.assertEqual(tokens[3].spelling, ')')
|
||||
t_cursor = tokens[1].cursor
|
||||
self.assertEqual(t_cursor.kind, CursorKind.TYPE_REF)
|
||||
r_cursor = t_cursor.referenced # should not raise an exception
|
||||
self.assertEqual(r_cursor.kind, CursorKind.CLASS_DECL)
|
||||
|
||||
def test_get_arguments(self):
|
||||
tu = get_tu('void foo(int i, int j);')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
arguments = list(foo.get_arguments())
|
||||
|
||||
self.assertEqual(len(arguments), 2)
|
||||
self.assertEqual(arguments[0].spelling, "i")
|
||||
self.assertEqual(arguments[1].spelling, "j")
|
||||
|
||||
def test_get_num_template_arguments(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_num_template_arguments(), 3)
|
||||
|
||||
def test_get_template_argument_kind(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_kind(0), TemplateArgumentKind.INTEGRAL)
|
||||
self.assertEqual(foos[1].get_template_argument_kind(1), TemplateArgumentKind.TYPE)
|
||||
self.assertEqual(foos[1].get_template_argument_kind(2), TemplateArgumentKind.INTEGRAL)
|
||||
|
||||
def test_get_template_argument_type(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_type(1).kind, TypeKind.FLOAT)
|
||||
|
||||
def test_get_template_argument_value(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_value(0), -7)
|
||||
self.assertEqual(foos[1].get_template_argument_value(2), True)
|
||||
|
||||
def test_get_template_argument_unsigned_value(self):
|
||||
tu = get_tu(kTemplateArgTest, lang='cpp')
|
||||
foos = get_cursors(tu, 'foo')
|
||||
|
||||
self.assertEqual(foos[1].get_template_argument_unsigned_value(0), 2 ** 32 - 7)
|
||||
self.assertEqual(foos[1].get_template_argument_unsigned_value(2), True)
|
||||
|
||||
def test_referenced(self):
|
||||
tu = get_tu('void foo(); void bar() { foo(); }')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
for c in bar.get_children():
|
||||
if c.kind == CursorKind.CALL_EXPR:
|
||||
self.assertEqual(c.referenced.spelling, foo.spelling)
|
||||
break
|
||||
|
||||
def test_mangled_name(self):
|
||||
kInputForMangling = """\
|
||||
int foo(int, int);
|
||||
"""
|
||||
tu = get_tu(kInputForMangling, lang='cpp')
|
||||
foo = get_cursor(tu, 'foo')
|
||||
|
||||
# Since libclang does not link in targets, we cannot pass a triple to it
|
||||
# and force the target. To enable this test to pass on all platforms, accept
|
||||
# all valid manglings.
|
||||
# [c-index-test handles this by running the source through clang, emitting
|
||||
# an AST file and running libclang on that AST file]
|
||||
self.assertIn(foo.mangled_name, ('_Z3fooii', '__Z3fooii', '?foo@@YAHHH'))
|
||||
|
@ -1,49 +1,53 @@
|
||||
from clang.cindex import CursorKind
|
||||
|
||||
def test_name():
|
||||
assert CursorKind.UNEXPOSED_DECL.name is 'UNEXPOSED_DECL'
|
||||
import unittest
|
||||
|
||||
def test_get_all_kinds():
|
||||
kinds = CursorKind.get_all_kinds()
|
||||
assert CursorKind.UNEXPOSED_DECL in kinds
|
||||
assert CursorKind.TRANSLATION_UNIT in kinds
|
||||
assert CursorKind.VARIABLE_REF in kinds
|
||||
assert CursorKind.LAMBDA_EXPR in kinds
|
||||
assert CursorKind.OBJ_BOOL_LITERAL_EXPR in kinds
|
||||
assert CursorKind.OBJ_SELF_EXPR in kinds
|
||||
assert CursorKind.MS_ASM_STMT in kinds
|
||||
assert CursorKind.MODULE_IMPORT_DECL in kinds
|
||||
assert CursorKind.TYPE_ALIAS_TEMPLATE_DECL in kinds
|
||||
|
||||
def test_kind_groups():
|
||||
"""Check that every kind classifies to exactly one group."""
|
||||
class TestCursorKind(unittest.TestCase):
|
||||
def test_name(self):
|
||||
self.assertTrue(CursorKind.UNEXPOSED_DECL.name is 'UNEXPOSED_DECL')
|
||||
|
||||
assert CursorKind.UNEXPOSED_DECL.is_declaration()
|
||||
assert CursorKind.TYPE_REF.is_reference()
|
||||
assert CursorKind.DECL_REF_EXPR.is_expression()
|
||||
assert CursorKind.UNEXPOSED_STMT.is_statement()
|
||||
assert CursorKind.INVALID_FILE.is_invalid()
|
||||
def test_get_all_kinds(self):
|
||||
kinds = CursorKind.get_all_kinds()
|
||||
self.assertIn(CursorKind.UNEXPOSED_DECL, kinds)
|
||||
self.assertIn(CursorKind.TRANSLATION_UNIT, kinds)
|
||||
self.assertIn(CursorKind.VARIABLE_REF, kinds)
|
||||
self.assertIn(CursorKind.LAMBDA_EXPR, kinds)
|
||||
self.assertIn(CursorKind.OBJ_BOOL_LITERAL_EXPR, kinds)
|
||||
self.assertIn(CursorKind.OBJ_SELF_EXPR, kinds)
|
||||
self.assertIn(CursorKind.MS_ASM_STMT, kinds)
|
||||
self.assertIn(CursorKind.MODULE_IMPORT_DECL, kinds)
|
||||
self.assertIn(CursorKind.TYPE_ALIAS_TEMPLATE_DECL, kinds)
|
||||
|
||||
assert CursorKind.TRANSLATION_UNIT.is_translation_unit()
|
||||
assert not CursorKind.TYPE_REF.is_translation_unit()
|
||||
def test_kind_groups(self):
|
||||
"""Check that every kind classifies to exactly one group."""
|
||||
|
||||
assert CursorKind.PREPROCESSING_DIRECTIVE.is_preprocessing()
|
||||
assert not CursorKind.TYPE_REF.is_preprocessing()
|
||||
self.assertTrue(CursorKind.UNEXPOSED_DECL.is_declaration())
|
||||
self.assertTrue(CursorKind.TYPE_REF.is_reference())
|
||||
self.assertTrue(CursorKind.DECL_REF_EXPR.is_expression())
|
||||
self.assertTrue(CursorKind.UNEXPOSED_STMT.is_statement())
|
||||
self.assertTrue(CursorKind.INVALID_FILE.is_invalid())
|
||||
|
||||
assert CursorKind.UNEXPOSED_DECL.is_unexposed()
|
||||
assert not CursorKind.TYPE_REF.is_unexposed()
|
||||
self.assertTrue(CursorKind.TRANSLATION_UNIT.is_translation_unit())
|
||||
self.assertFalse(CursorKind.TYPE_REF.is_translation_unit())
|
||||
|
||||
for k in CursorKind.get_all_kinds():
|
||||
group = [n for n in ('is_declaration', 'is_reference', 'is_expression',
|
||||
'is_statement', 'is_invalid', 'is_attribute')
|
||||
if getattr(k, n)()]
|
||||
self.assertTrue(CursorKind.PREPROCESSING_DIRECTIVE.is_preprocessing())
|
||||
self.assertFalse(CursorKind.TYPE_REF.is_preprocessing())
|
||||
|
||||
if k in ( CursorKind.TRANSLATION_UNIT,
|
||||
CursorKind.MACRO_DEFINITION,
|
||||
CursorKind.MACRO_INSTANTIATION,
|
||||
CursorKind.INCLUSION_DIRECTIVE,
|
||||
CursorKind.PREPROCESSING_DIRECTIVE,
|
||||
CursorKind.OVERLOAD_CANDIDATE):
|
||||
assert len(group) == 0
|
||||
else:
|
||||
assert len(group) == 1
|
||||
self.assertTrue(CursorKind.UNEXPOSED_DECL.is_unexposed())
|
||||
self.assertFalse(CursorKind.TYPE_REF.is_unexposed())
|
||||
|
||||
for k in CursorKind.get_all_kinds():
|
||||
group = [n for n in ('is_declaration', 'is_reference', 'is_expression',
|
||||
'is_statement', 'is_invalid', 'is_attribute')
|
||||
if getattr(k, n)()]
|
||||
|
||||
if k in ( CursorKind.TRANSLATION_UNIT,
|
||||
CursorKind.MACRO_DEFINITION,
|
||||
CursorKind.MACRO_INSTANTIATION,
|
||||
CursorKind.INCLUSION_DIRECTIVE,
|
||||
CursorKind.PREPROCESSING_DIRECTIVE,
|
||||
CursorKind.OVERLOAD_CANDIDATE):
|
||||
self.assertEqual(len(group), 0)
|
||||
else:
|
||||
self.assertEqual(len(group), 1)
|
||||
|
@ -1,102 +1,105 @@
|
||||
from clang.cindex import *
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
# FIXME: We need support for invalid translation units to test better.
|
||||
|
||||
def test_diagnostic_warning():
|
||||
tu = get_tu('int f0() {}\n')
|
||||
assert len(tu.diagnostics) == 1
|
||||
assert tu.diagnostics[0].severity == Diagnostic.Warning
|
||||
assert tu.diagnostics[0].location.line == 1
|
||||
assert tu.diagnostics[0].location.column == 11
|
||||
assert (tu.diagnostics[0].spelling ==
|
||||
'control reaches end of non-void function')
|
||||
|
||||
def test_diagnostic_note():
|
||||
# FIXME: We aren't getting notes here for some reason.
|
||||
tu = get_tu('#define A x\nvoid *A = 1;\n')
|
||||
assert len(tu.diagnostics) == 1
|
||||
assert tu.diagnostics[0].severity == Diagnostic.Warning
|
||||
assert tu.diagnostics[0].location.line == 2
|
||||
assert tu.diagnostics[0].location.column == 7
|
||||
assert 'incompatible' in tu.diagnostics[0].spelling
|
||||
# assert tu.diagnostics[1].severity == Diagnostic.Note
|
||||
# assert tu.diagnostics[1].location.line == 1
|
||||
# assert tu.diagnostics[1].location.column == 11
|
||||
# assert tu.diagnostics[1].spelling == 'instantiated from'
|
||||
class TestDiagnostics(unittest.TestCase):
|
||||
def test_diagnostic_warning(self):
|
||||
tu = get_tu('int f0() {}\n')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 11)
|
||||
self.assertEqual(tu.diagnostics[0].spelling,
|
||||
'control reaches end of non-void function')
|
||||
|
||||
def test_diagnostic_fixit():
|
||||
tu = get_tu('struct { int f0; } x = { f0 : 1 };')
|
||||
assert len(tu.diagnostics) == 1
|
||||
assert tu.diagnostics[0].severity == Diagnostic.Warning
|
||||
assert tu.diagnostics[0].location.line == 1
|
||||
assert tu.diagnostics[0].location.column == 26
|
||||
assert tu.diagnostics[0].spelling.startswith('use of GNU old-style')
|
||||
assert len(tu.diagnostics[0].fixits) == 1
|
||||
assert tu.diagnostics[0].fixits[0].range.start.line == 1
|
||||
assert tu.diagnostics[0].fixits[0].range.start.column == 26
|
||||
assert tu.diagnostics[0].fixits[0].range.end.line == 1
|
||||
assert tu.diagnostics[0].fixits[0].range.end.column == 30
|
||||
assert tu.diagnostics[0].fixits[0].value == '.f0 = '
|
||||
def test_diagnostic_note(self):
|
||||
# FIXME: We aren't getting notes here for some reason.
|
||||
tu = get_tu('#define A x\nvoid *A = 1;\n')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 2)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 7)
|
||||
self.assertIn('incompatible', tu.diagnostics[0].spelling)
|
||||
# self.assertEqual(tu.diagnostics[1].severity, Diagnostic.Note)
|
||||
# self.assertEqual(tu.diagnostics[1].location.line, 1)
|
||||
# self.assertEqual(tu.diagnostics[1].location.column, 11)
|
||||
# self.assertEqual(tu.diagnostics[1].spelling, 'instantiated from')
|
||||
|
||||
def test_diagnostic_range():
|
||||
tu = get_tu('void f() { int i = "a" + 1; }')
|
||||
assert len(tu.diagnostics) == 1
|
||||
assert tu.diagnostics[0].severity == Diagnostic.Warning
|
||||
assert tu.diagnostics[0].location.line == 1
|
||||
assert tu.diagnostics[0].location.column == 16
|
||||
assert tu.diagnostics[0].spelling.startswith('incompatible pointer to')
|
||||
assert len(tu.diagnostics[0].fixits) == 0
|
||||
assert len(tu.diagnostics[0].ranges) == 1
|
||||
assert tu.diagnostics[0].ranges[0].start.line == 1
|
||||
assert tu.diagnostics[0].ranges[0].start.column == 20
|
||||
assert tu.diagnostics[0].ranges[0].end.line == 1
|
||||
assert tu.diagnostics[0].ranges[0].end.column == 27
|
||||
try:
|
||||
tu.diagnostics[0].ranges[1].start.line
|
||||
except IndexError:
|
||||
assert True
|
||||
else:
|
||||
assert False
|
||||
def test_diagnostic_fixit(self):
|
||||
tu = get_tu('struct { int f0; } x = { f0 : 1 };')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 26)
|
||||
self.assertRegexpMatches(tu.diagnostics[0].spelling,
|
||||
'use of GNU old-style.*')
|
||||
self.assertEqual(len(tu.diagnostics[0].fixits), 1)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.column, 26)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.end.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].range.end.column, 30)
|
||||
self.assertEqual(tu.diagnostics[0].fixits[0].value, '.f0 = ')
|
||||
|
||||
def test_diagnostic_category():
|
||||
"""Ensure that category properties work."""
|
||||
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
|
||||
assert len(tu.diagnostics) == 1
|
||||
d = tu.diagnostics[0]
|
||||
def test_diagnostic_range(self):
|
||||
tu = get_tu('void f() { int i = "a" + 1; }')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
self.assertEqual(tu.diagnostics[0].severity, Diagnostic.Warning)
|
||||
self.assertEqual(tu.diagnostics[0].location.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].location.column, 16)
|
||||
self.assertRegexpMatches(tu.diagnostics[0].spelling,
|
||||
'incompatible pointer to.*')
|
||||
self.assertEqual(len(tu.diagnostics[0].fixits), 0)
|
||||
self.assertEqual(len(tu.diagnostics[0].ranges), 1)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].start.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].start.column, 20)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].end.line, 1)
|
||||
self.assertEqual(tu.diagnostics[0].ranges[0].end.column, 27)
|
||||
with self.assertRaises(IndexError):
|
||||
tu.diagnostics[0].ranges[1].start.line
|
||||
|
||||
assert d.severity == Diagnostic.Warning
|
||||
assert d.location.line == 1
|
||||
assert d.location.column == 11
|
||||
def test_diagnostic_category(self):
|
||||
"""Ensure that category properties work."""
|
||||
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
assert d.category_number == 2
|
||||
assert d.category_name == 'Semantic Issue'
|
||||
self.assertEqual(d.severity, Diagnostic.Warning)
|
||||
self.assertEqual(d.location.line, 1)
|
||||
self.assertEqual(d.location.column, 11)
|
||||
|
||||
def test_diagnostic_option():
|
||||
"""Ensure that category option properties work."""
|
||||
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
|
||||
assert len(tu.diagnostics) == 1
|
||||
d = tu.diagnostics[0]
|
||||
self.assertEqual(d.category_number, 2)
|
||||
self.assertEqual(d.category_name, 'Semantic Issue')
|
||||
|
||||
assert d.option == '-Wunused-parameter'
|
||||
assert d.disable_option == '-Wno-unused-parameter'
|
||||
def test_diagnostic_option(self):
|
||||
"""Ensure that category option properties work."""
|
||||
tu = get_tu('int f(int i) { return 7; }', all_warnings=True)
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
def test_diagnostic_children():
|
||||
tu = get_tu('void f(int x) {} void g() { f(); }')
|
||||
assert len(tu.diagnostics) == 1
|
||||
d = tu.diagnostics[0]
|
||||
self.assertEqual(d.option, '-Wunused-parameter')
|
||||
self.assertEqual(d.disable_option, '-Wno-unused-parameter')
|
||||
|
||||
children = d.children
|
||||
assert len(children) == 1
|
||||
assert children[0].severity == Diagnostic.Note
|
||||
assert children[0].spelling.endswith('declared here')
|
||||
assert children[0].location.line == 1
|
||||
assert children[0].location.column == 1
|
||||
def test_diagnostic_children(self):
|
||||
tu = get_tu('void f(int x) {} void g() { f(); }')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
def test_diagnostic_string_repr():
|
||||
tu = get_tu('struct MissingSemicolon{}')
|
||||
assert len(tu.diagnostics) == 1
|
||||
d = tu.diagnostics[0]
|
||||
children = d.children
|
||||
self.assertEqual(len(children), 1)
|
||||
self.assertEqual(children[0].severity, Diagnostic.Note)
|
||||
self.assertRegexpMatches(children[0].spelling,
|
||||
'.*declared here')
|
||||
self.assertEqual(children[0].location.line, 1)
|
||||
self.assertEqual(children[0].location.column, 1)
|
||||
|
||||
assert repr(d) == '<Diagnostic severity 3, location <SourceLocation file \'t.c\', line 1, column 26>, spelling "expected \';\' after struct">'
|
||||
|
||||
def test_diagnostic_string_repr(self):
|
||||
tu = get_tu('struct MissingSemicolon{}')
|
||||
self.assertEqual(len(tu.diagnostics), 1)
|
||||
d = tu.diagnostics[0]
|
||||
|
||||
self.assertEqual(repr(d), '<Diagnostic severity 3, location <SourceLocation file \'t.c\', line 1, column 26>, spelling "expected \';\' after struct">')
|
||||
|
@ -2,6 +2,8 @@ import clang.cindex
|
||||
from clang.cindex import ExceptionSpecificationKind
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
def find_function_declarations(node, declarations=[]):
|
||||
if node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
|
||||
@ -11,17 +13,18 @@ def find_function_declarations(node, declarations=[]):
|
||||
return declarations
|
||||
|
||||
|
||||
def test_exception_specification_kind():
|
||||
source = """int square1(int x);
|
||||
int square2(int x) noexcept;
|
||||
int square3(int x) noexcept(noexcept(x * x));"""
|
||||
class TestExceptionSpecificationKind(unittest.TestCase):
|
||||
def test_exception_specification_kind(self):
|
||||
source = """int square1(int x);
|
||||
int square2(int x) noexcept;
|
||||
int square3(int x) noexcept(noexcept(x * x));"""
|
||||
|
||||
tu = get_tu(source, lang='cpp', flags=['-std=c++14'])
|
||||
tu = get_tu(source, lang='cpp', flags=['-std=c++14'])
|
||||
|
||||
declarations = find_function_declarations(tu.cursor)
|
||||
expected = [
|
||||
('square1', ExceptionSpecificationKind.NONE),
|
||||
('square2', ExceptionSpecificationKind.BASIC_NOEXCEPT),
|
||||
('square3', ExceptionSpecificationKind.COMPUTED_NOEXCEPT)
|
||||
]
|
||||
assert declarations == expected
|
||||
declarations = find_function_declarations(tu.cursor)
|
||||
expected = [
|
||||
('square1', ExceptionSpecificationKind.NONE),
|
||||
('square2', ExceptionSpecificationKind.BASIC_NOEXCEPT),
|
||||
('square3', ExceptionSpecificationKind.COMPUTED_NOEXCEPT)
|
||||
]
|
||||
self.assertListEqual(declarations, expected)
|
||||
|
@ -1,9 +1,13 @@
|
||||
from clang.cindex import Index, File
|
||||
|
||||
def test_file():
|
||||
index = Index.create()
|
||||
tu = index.parse('t.c', unsaved_files = [('t.c', "")])
|
||||
file = File.from_name(tu, "t.c")
|
||||
assert str(file) == "t.c"
|
||||
assert file.name == "t.c"
|
||||
assert repr(file) == "<File: t.c>"
|
||||
import unittest
|
||||
|
||||
|
||||
class TestFile(unittest.TestCase):
|
||||
def test_file(self):
|
||||
index = Index.create()
|
||||
tu = index.parse('t.c', unsaved_files = [('t.c', "")])
|
||||
file = File.from_name(tu, "t.c")
|
||||
self.assertEqual(str(file), "t.c")
|
||||
self.assertEqual(file.name, "t.c")
|
||||
self.assertEqual(repr(file), "<File: t.c>")
|
||||
|
@ -1,15 +1,21 @@
|
||||
from clang.cindex import *
|
||||
import os
|
||||
import unittest
|
||||
|
||||
|
||||
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
|
||||
|
||||
def test_create():
|
||||
index = Index.create()
|
||||
|
||||
# FIXME: test Index.read
|
||||
class TestIndex(unittest.TestCase):
|
||||
def test_create(self):
|
||||
index = Index.create()
|
||||
|
||||
def test_parse():
|
||||
index = Index.create()
|
||||
assert isinstance(index, Index)
|
||||
tu = index.parse(os.path.join(kInputsDir, 'hello.cpp'))
|
||||
assert isinstance(tu, TranslationUnit)
|
||||
# FIXME: test Index.read
|
||||
|
||||
def test_parse(self):
|
||||
index = Index.create()
|
||||
self.assertIsInstance(index, Index)
|
||||
tu = index.parse(os.path.join(kInputsDir, 'hello.cpp'))
|
||||
self.assertIsInstance(tu, TranslationUnit)
|
||||
tu = index.parse(None, ['-c', os.path.join(kInputsDir, 'hello.cpp')])
|
||||
self.assertIsInstance(tu, TranslationUnit)
|
||||
|
33
bindings/python/tests/cindex/test_linkage.py
Normal file
33
bindings/python/tests/cindex/test_linkage.py
Normal file
@ -0,0 +1,33 @@
|
||||
from clang.cindex import LinkageKind
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestLinkage(unittest.TestCase):
|
||||
def test_linkage(self):
|
||||
"""Ensure that linkage specifers are available on cursors"""
|
||||
|
||||
tu = get_tu("""
|
||||
void foo() { int no_linkage; }
|
||||
static int internal;
|
||||
namespace { struct unique_external_type {} }
|
||||
unique_external_type unique_external;
|
||||
extern int external;
|
||||
""", lang = 'cpp')
|
||||
|
||||
no_linkage = get_cursor(tu.cursor, 'no_linkage')
|
||||
self.assertEqual(no_linkage.linkage, LinkageKind.NO_LINKAGE)
|
||||
|
||||
internal = get_cursor(tu.cursor, 'internal')
|
||||
self.assertEqual(internal.linkage, LinkageKind.INTERNAL)
|
||||
|
||||
unique_external = get_cursor(tu.cursor, 'unique_external')
|
||||
self.assertEqual(unique_external.linkage, LinkageKind.UNIQUE_EXTERNAL)
|
||||
|
||||
external = get_cursor(tu.cursor, 'external')
|
||||
self.assertEqual(external.linkage, LinkageKind.EXTERNAL)
|
@ -5,91 +5,96 @@ from clang.cindex import SourceRange
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
baseInput="int one;\nint two;\n"
|
||||
|
||||
def assert_location(loc, line, column, offset):
|
||||
assert loc.line == line
|
||||
assert loc.column == column
|
||||
assert loc.offset == offset
|
||||
|
||||
def test_location():
|
||||
tu = get_tu(baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
class TestLocation(unittest.TestCase):
|
||||
def assert_location(self, loc, line, column, offset):
|
||||
self.assertEqual(loc.line, line)
|
||||
self.assertEqual(loc.column, column)
|
||||
self.assertEqual(loc.offset, offset)
|
||||
|
||||
assert one is not None
|
||||
assert two is not None
|
||||
def test_location(self):
|
||||
tu = get_tu(baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
assert_location(one.location,line=1,column=5,offset=4)
|
||||
assert_location(two.location,line=2,column=5,offset=13)
|
||||
self.assertIsNotNone(one)
|
||||
self.assertIsNotNone(two)
|
||||
|
||||
# adding a linebreak at top should keep columns same
|
||||
tu = get_tu('\n' + baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
self.assert_location(one.location,line=1,column=5,offset=4)
|
||||
self.assert_location(two.location,line=2,column=5,offset=13)
|
||||
|
||||
assert one is not None
|
||||
assert two is not None
|
||||
# adding a linebreak at top should keep columns same
|
||||
tu = get_tu('\n' + baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
assert_location(one.location,line=2,column=5,offset=5)
|
||||
assert_location(two.location,line=3,column=5,offset=14)
|
||||
self.assertIsNotNone(one)
|
||||
self.assertIsNotNone(two)
|
||||
|
||||
# adding a space should affect column on first line only
|
||||
tu = get_tu(' ' + baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
self.assert_location(one.location,line=2,column=5,offset=5)
|
||||
self.assert_location(two.location,line=3,column=5,offset=14)
|
||||
|
||||
assert_location(one.location,line=1,column=6,offset=5)
|
||||
assert_location(two.location,line=2,column=5,offset=14)
|
||||
# adding a space should affect column on first line only
|
||||
tu = get_tu(' ' + baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
# define the expected location ourselves and see if it matches
|
||||
# the returned location
|
||||
tu = get_tu(baseInput)
|
||||
self.assert_location(one.location,line=1,column=6,offset=5)
|
||||
self.assert_location(two.location,line=2,column=5,offset=14)
|
||||
|
||||
file = File.from_name(tu, 't.c')
|
||||
location = SourceLocation.from_position(tu, file, 1, 5)
|
||||
cursor = Cursor.from_location(tu, location)
|
||||
# define the expected location ourselves and see if it matches
|
||||
# the returned location
|
||||
tu = get_tu(baseInput)
|
||||
|
||||
one = get_cursor(tu, 'one')
|
||||
assert one is not None
|
||||
assert one == cursor
|
||||
file = File.from_name(tu, 't.c')
|
||||
location = SourceLocation.from_position(tu, file, 1, 5)
|
||||
cursor = Cursor.from_location(tu, location)
|
||||
|
||||
# Ensure locations referring to the same entity are equivalent.
|
||||
location2 = SourceLocation.from_position(tu, file, 1, 5)
|
||||
assert location == location2
|
||||
location3 = SourceLocation.from_position(tu, file, 1, 4)
|
||||
assert location2 != location3
|
||||
one = get_cursor(tu, 'one')
|
||||
self.assertIsNotNone(one)
|
||||
self.assertEqual(one, cursor)
|
||||
|
||||
offset_location = SourceLocation.from_offset(tu, file, 5)
|
||||
cursor = Cursor.from_location(tu, offset_location)
|
||||
verified = False
|
||||
for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']:
|
||||
assert n == cursor
|
||||
verified = True
|
||||
# Ensure locations referring to the same entity are equivalent.
|
||||
location2 = SourceLocation.from_position(tu, file, 1, 5)
|
||||
self.assertEqual(location, location2)
|
||||
location3 = SourceLocation.from_position(tu, file, 1, 4)
|
||||
self.assertNotEqual(location2, location3)
|
||||
|
||||
assert verified
|
||||
offset_location = SourceLocation.from_offset(tu, file, 5)
|
||||
cursor = Cursor.from_location(tu, offset_location)
|
||||
verified = False
|
||||
for n in [n for n in tu.cursor.get_children() if n.spelling == 'one']:
|
||||
self.assertEqual(n, cursor)
|
||||
verified = True
|
||||
|
||||
def test_extent():
|
||||
tu = get_tu(baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
self.assertTrue(verified)
|
||||
|
||||
assert_location(one.extent.start,line=1,column=1,offset=0)
|
||||
assert_location(one.extent.end,line=1,column=8,offset=7)
|
||||
assert baseInput[one.extent.start.offset:one.extent.end.offset] == "int one"
|
||||
def test_extent(self):
|
||||
tu = get_tu(baseInput)
|
||||
one = get_cursor(tu, 'one')
|
||||
two = get_cursor(tu, 'two')
|
||||
|
||||
assert_location(two.extent.start,line=2,column=1,offset=9)
|
||||
assert_location(two.extent.end,line=2,column=8,offset=16)
|
||||
assert baseInput[two.extent.start.offset:two.extent.end.offset] == "int two"
|
||||
self.assert_location(one.extent.start,line=1,column=1,offset=0)
|
||||
self.assert_location(one.extent.end,line=1,column=8,offset=7)
|
||||
self.assertEqual(baseInput[one.extent.start.offset:one.extent.end.offset], "int one")
|
||||
|
||||
file = File.from_name(tu, 't.c')
|
||||
location1 = SourceLocation.from_position(tu, file, 1, 1)
|
||||
location2 = SourceLocation.from_position(tu, file, 1, 8)
|
||||
self.assert_location(two.extent.start,line=2,column=1,offset=9)
|
||||
self.assert_location(two.extent.end,line=2,column=8,offset=16)
|
||||
self.assertEqual(baseInput[two.extent.start.offset:two.extent.end.offset], "int two")
|
||||
|
||||
range1 = SourceRange.from_locations(location1, location2)
|
||||
range2 = SourceRange.from_locations(location1, location2)
|
||||
assert range1 == range2
|
||||
file = File.from_name(tu, 't.c')
|
||||
location1 = SourceLocation.from_position(tu, file, 1, 1)
|
||||
location2 = SourceLocation.from_position(tu, file, 1, 8)
|
||||
|
||||
location3 = SourceLocation.from_position(tu, file, 1, 6)
|
||||
range3 = SourceRange.from_locations(location1, location3)
|
||||
assert range1 != range3
|
||||
range1 = SourceRange.from_locations(location1, location2)
|
||||
range2 = SourceRange.from_locations(location1, location2)
|
||||
self.assertEqual(range1, range2)
|
||||
|
||||
location3 = SourceLocation.from_position(tu, file, 1, 6)
|
||||
range3 = SourceRange.from_locations(location1, location3)
|
||||
self.assertNotEqual(range1, range3)
|
||||
|
49
bindings/python/tests/cindex/test_tls_kind.py
Normal file
49
bindings/python/tests/cindex/test_tls_kind.py
Normal file
@ -0,0 +1,49 @@
|
||||
from clang.cindex import TLSKind
|
||||
from clang.cindex import Cursor
|
||||
from clang.cindex import TranslationUnit
|
||||
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
import unittest
|
||||
|
||||
|
||||
class TestTLSKind(unittest.TestCase):
|
||||
def test_tls_kind(self):
|
||||
"""Ensure that thread-local storage kinds are available on cursors."""
|
||||
|
||||
tu = get_tu("""
|
||||
int tls_none;
|
||||
thread_local int tls_dynamic;
|
||||
_Thread_local int tls_static;
|
||||
""", lang = 'cpp')
|
||||
|
||||
tls_none = get_cursor(tu.cursor, 'tls_none')
|
||||
self.assertEqual(tls_none.tls_kind, TLSKind.NONE)
|
||||
|
||||
tls_dynamic = get_cursor(tu.cursor, 'tls_dynamic')
|
||||
self.assertEqual(tls_dynamic.tls_kind, TLSKind.DYNAMIC)
|
||||
|
||||
tls_static = get_cursor(tu.cursor, 'tls_static')
|
||||
self.assertEqual(tls_static.tls_kind, TLSKind.STATIC)
|
||||
|
||||
# The following case tests '__declspec(thread)'. Since it is a Microsoft
|
||||
# specific extension, specific flags are required for the parser to pick
|
||||
# these up.
|
||||
flags = ['-fms-extensions', '-target', 'x86_64-unknown-windows-win32',
|
||||
'-fms-compatibility-version=18']
|
||||
tu = get_tu("""
|
||||
__declspec(thread) int tls_declspec_msvc18;
|
||||
""", lang = 'cpp', flags=flags)
|
||||
|
||||
tls_declspec_msvc18 = get_cursor(tu.cursor, 'tls_declspec_msvc18')
|
||||
self.assertEqual(tls_declspec_msvc18.tls_kind, TLSKind.STATIC)
|
||||
|
||||
flags = ['-fms-extensions', '-target', 'x86_64-unknown-windows-win32',
|
||||
'-fms-compatibility-version=19']
|
||||
tu = get_tu("""
|
||||
__declspec(thread) int tls_declspec_msvc19;
|
||||
""", lang = 'cpp', flags=flags)
|
||||
|
||||
tls_declspec_msvc19 = get_cursor(tu.cursor, 'tls_declspec_msvc19')
|
||||
self.assertEqual(tls_declspec_msvc19.tls_kind, TLSKind.DYNAMIC)
|
@ -1,43 +1,44 @@
|
||||
from clang.cindex import TokenKind
|
||||
from nose.tools import eq_
|
||||
from nose.tools import ok_
|
||||
from nose.tools import raises
|
||||
|
||||
def test_constructor():
|
||||
"""Ensure TokenKind constructor works as expected."""
|
||||
import unittest
|
||||
|
||||
t = TokenKind(5, 'foo')
|
||||
|
||||
eq_(t.value, 5)
|
||||
eq_(t.name, 'foo')
|
||||
class TestTokenKind(unittest.TestCase):
|
||||
def test_constructor(self):
|
||||
"""Ensure TokenKind constructor works as expected."""
|
||||
|
||||
@raises(ValueError)
|
||||
def test_bad_register():
|
||||
"""Ensure a duplicate value is rejected for registration."""
|
||||
t = TokenKind(5, 'foo')
|
||||
|
||||
TokenKind.register(2, 'foo')
|
||||
self.assertEqual(t.value, 5)
|
||||
self.assertEqual(t.name, 'foo')
|
||||
|
||||
@raises(ValueError)
|
||||
def test_unknown_value():
|
||||
"""Ensure trying to fetch an unknown value raises."""
|
||||
def test_bad_register(self):
|
||||
"""Ensure a duplicate value is rejected for registration."""
|
||||
|
||||
TokenKind.from_value(-1)
|
||||
with self.assertRaises(ValueError):
|
||||
TokenKind.register(2, 'foo')
|
||||
|
||||
def test_registration():
|
||||
"""Ensure that items registered appear as class attributes."""
|
||||
ok_(hasattr(TokenKind, 'LITERAL'))
|
||||
literal = TokenKind.LITERAL
|
||||
def test_unknown_value(self):
|
||||
"""Ensure trying to fetch an unknown value raises."""
|
||||
|
||||
ok_(isinstance(literal, TokenKind))
|
||||
with self.assertRaises(ValueError):
|
||||
TokenKind.from_value(-1)
|
||||
|
||||
def test_from_value():
|
||||
"""Ensure registered values can be obtained from from_value()."""
|
||||
t = TokenKind.from_value(3)
|
||||
ok_(isinstance(t, TokenKind))
|
||||
eq_(t, TokenKind.LITERAL)
|
||||
def test_registration(self):
|
||||
"""Ensure that items registered appear as class attributes."""
|
||||
self.assertTrue(hasattr(TokenKind, 'LITERAL'))
|
||||
literal = TokenKind.LITERAL
|
||||
|
||||
def test_repr():
|
||||
"""Ensure repr() works."""
|
||||
self.assertIsInstance(literal, TokenKind)
|
||||
|
||||
r = repr(TokenKind.LITERAL)
|
||||
eq_(r, 'TokenKind.LITERAL')
|
||||
def test_from_value(self):
|
||||
"""Ensure registered values can be obtained from from_value()."""
|
||||
t = TokenKind.from_value(3)
|
||||
self.assertIsInstance(t, TokenKind)
|
||||
self.assertEqual(t, TokenKind.LITERAL)
|
||||
|
||||
def test_repr(self):
|
||||
"""Ensure repr() works."""
|
||||
|
||||
r = repr(TokenKind.LITERAL)
|
||||
self.assertEqual(r, 'TokenKind.LITERAL')
|
||||
|
@ -3,50 +3,52 @@ from clang.cindex import Index
|
||||
from clang.cindex import SourceLocation
|
||||
from clang.cindex import SourceRange
|
||||
from clang.cindex import TokenKind
|
||||
from nose.tools import eq_
|
||||
from nose.tools import ok_
|
||||
|
||||
from .util import get_tu
|
||||
|
||||
def test_token_to_cursor():
|
||||
"""Ensure we can obtain a Cursor from a Token instance."""
|
||||
tu = get_tu('int i = 5;')
|
||||
r = tu.get_extent('t.c', (0, 9))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
import unittest
|
||||
|
||||
assert len(tokens) == 4
|
||||
assert tokens[1].spelling == 'i'
|
||||
assert tokens[1].kind == TokenKind.IDENTIFIER
|
||||
|
||||
cursor = tokens[1].cursor
|
||||
assert cursor.kind == CursorKind.VAR_DECL
|
||||
assert tokens[1].cursor == tokens[2].cursor
|
||||
class TestTokens(unittest.TestCase):
|
||||
def test_token_to_cursor(self):
|
||||
"""Ensure we can obtain a Cursor from a Token instance."""
|
||||
tu = get_tu('int i = 5;')
|
||||
r = tu.get_extent('t.c', (0, 9))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
|
||||
def test_token_location():
|
||||
"""Ensure Token.location works."""
|
||||
self.assertEqual(len(tokens), 4)
|
||||
self.assertEqual(tokens[1].spelling, 'i')
|
||||
self.assertEqual(tokens[1].kind, TokenKind.IDENTIFIER)
|
||||
|
||||
tu = get_tu('int foo = 10;')
|
||||
r = tu.get_extent('t.c', (0, 11))
|
||||
cursor = tokens[1].cursor
|
||||
self.assertEqual(cursor.kind, CursorKind.VAR_DECL)
|
||||
self.assertEqual(tokens[1].cursor, tokens[2].cursor)
|
||||
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
eq_(len(tokens), 4)
|
||||
def test_token_location(self):
|
||||
"""Ensure Token.location works."""
|
||||
|
||||
loc = tokens[1].location
|
||||
ok_(isinstance(loc, SourceLocation))
|
||||
eq_(loc.line, 1)
|
||||
eq_(loc.column, 5)
|
||||
eq_(loc.offset, 4)
|
||||
tu = get_tu('int foo = 10;')
|
||||
r = tu.get_extent('t.c', (0, 11))
|
||||
|
||||
def test_token_extent():
|
||||
"""Ensure Token.extent works."""
|
||||
tu = get_tu('int foo = 10;')
|
||||
r = tu.get_extent('t.c', (0, 11))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
self.assertEqual(len(tokens), 4)
|
||||
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
eq_(len(tokens), 4)
|
||||
loc = tokens[1].location
|
||||
self.assertIsInstance(loc, SourceLocation)
|
||||
self.assertEqual(loc.line, 1)
|
||||
self.assertEqual(loc.column, 5)
|
||||
self.assertEqual(loc.offset, 4)
|
||||
|
||||
extent = tokens[1].extent
|
||||
ok_(isinstance(extent, SourceRange))
|
||||
def test_token_extent(self):
|
||||
"""Ensure Token.extent works."""
|
||||
tu = get_tu('int foo = 10;')
|
||||
r = tu.get_extent('t.c', (0, 11))
|
||||
|
||||
eq_(extent.start.offset, 4)
|
||||
eq_(extent.end.offset, 7)
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
self.assertEqual(len(tokens), 4)
|
||||
|
||||
extent = tokens[1].extent
|
||||
self.assertIsInstance(extent, SourceRange)
|
||||
|
||||
self.assertEqual(extent.start.offset, 4)
|
||||
self.assertEqual(extent.end.offset, 7)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import gc
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import Cursor
|
||||
@ -14,83 +15,9 @@ from clang.cindex import TranslationUnit
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
|
||||
kInputsDir = os.path.join(os.path.dirname(__file__), 'INPUTS')
|
||||
|
||||
def test_spelling():
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
tu = TranslationUnit.from_source(path)
|
||||
assert tu.spelling == path
|
||||
|
||||
def test_cursor():
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
tu = get_tu(path)
|
||||
c = tu.cursor
|
||||
assert isinstance(c, Cursor)
|
||||
assert c.kind is CursorKind.TRANSLATION_UNIT
|
||||
|
||||
def test_parse_arguments():
|
||||
path = os.path.join(kInputsDir, 'parse_arguments.c')
|
||||
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
assert spellings[-2] == 'hello'
|
||||
assert spellings[-1] == 'hi'
|
||||
|
||||
def test_reparse_arguments():
|
||||
path = os.path.join(kInputsDir, 'parse_arguments.c')
|
||||
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
|
||||
tu.reparse()
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
assert spellings[-2] == 'hello'
|
||||
assert spellings[-1] == 'hi'
|
||||
|
||||
def test_unsaved_files():
|
||||
tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
|
||||
('fake.c', """
|
||||
#include "fake.h"
|
||||
int x;
|
||||
int SOME_DEFINE;
|
||||
"""),
|
||||
('./fake.h', """
|
||||
#define SOME_DEFINE y
|
||||
""")
|
||||
])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
assert spellings[-2] == 'x'
|
||||
assert spellings[-1] == 'y'
|
||||
|
||||
def test_unsaved_files_2():
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except:
|
||||
from io import StringIO
|
||||
tu = TranslationUnit.from_source('fake.c', unsaved_files = [
|
||||
('fake.c', StringIO('int x;'))])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
assert spellings[-1] == 'x'
|
||||
|
||||
def normpaths_equal(path1, path2):
|
||||
""" Compares two paths for equality after normalizing them with
|
||||
os.path.normpath
|
||||
"""
|
||||
return os.path.normpath(path1) == os.path.normpath(path2)
|
||||
|
||||
def test_includes():
|
||||
def eq(expected, actual):
|
||||
if not actual.is_input_file:
|
||||
return normpaths_equal(expected[0], actual.source.name) and \
|
||||
normpaths_equal(expected[1], actual.include.name)
|
||||
else:
|
||||
return normpaths_equal(expected[1], actual.include.name)
|
||||
|
||||
src = os.path.join(kInputsDir, 'include.cpp')
|
||||
h1 = os.path.join(kInputsDir, "header1.h")
|
||||
h2 = os.path.join(kInputsDir, "header2.h")
|
||||
h3 = os.path.join(kInputsDir, "header3.h")
|
||||
inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
|
||||
|
||||
tu = TranslationUnit.from_source(src)
|
||||
for i in zip(inc, tu.get_includes()):
|
||||
assert eq(i[0], i[1])
|
||||
|
||||
def save_tu(tu):
|
||||
"""Convenience API to save a TranslationUnit to a file.
|
||||
@ -102,153 +29,227 @@ def save_tu(tu):
|
||||
|
||||
return path
|
||||
|
||||
def test_save():
|
||||
"""Ensure TranslationUnit.save() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
path = save_tu(tu)
|
||||
assert os.path.exists(path)
|
||||
assert os.path.getsize(path) > 0
|
||||
os.unlink(path)
|
||||
|
||||
def test_save_translation_errors():
|
||||
"""Ensure that saving to an invalid directory raises."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
path = '/does/not/exist/llvm-test.ast'
|
||||
assert not os.path.exists(os.path.dirname(path))
|
||||
|
||||
try:
|
||||
tu.save(path)
|
||||
assert False
|
||||
except TranslationUnitSaveError as ex:
|
||||
expected = TranslationUnitSaveError.ERROR_UNKNOWN
|
||||
assert ex.save_error == expected
|
||||
|
||||
def test_load():
|
||||
"""Ensure TranslationUnits can be constructed from saved files."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
assert len(tu.diagnostics) == 0
|
||||
path = save_tu(tu)
|
||||
|
||||
assert os.path.exists(path)
|
||||
assert os.path.getsize(path) > 0
|
||||
|
||||
tu2 = TranslationUnit.from_ast_file(filename=path)
|
||||
assert len(tu2.diagnostics) == 0
|
||||
|
||||
foo = get_cursor(tu2, 'foo')
|
||||
assert foo is not None
|
||||
|
||||
# Just in case there is an open file descriptor somewhere.
|
||||
del tu2
|
||||
|
||||
os.unlink(path)
|
||||
|
||||
def test_index_parse():
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
index = Index.create()
|
||||
tu = index.parse(path)
|
||||
assert isinstance(tu, TranslationUnit)
|
||||
|
||||
def test_get_file():
|
||||
"""Ensure tu.get_file() works appropriately."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
f = tu.get_file('t.c')
|
||||
assert isinstance(f, File)
|
||||
assert f.name == 't.c'
|
||||
|
||||
try:
|
||||
f = tu.get_file('foobar.cpp')
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
assert False
|
||||
|
||||
def test_get_source_location():
|
||||
"""Ensure tu.get_source_location() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
location = tu.get_location('t.c', 2)
|
||||
assert isinstance(location, SourceLocation)
|
||||
assert location.offset == 2
|
||||
assert location.file.name == 't.c'
|
||||
|
||||
location = tu.get_location('t.c', (1, 3))
|
||||
assert isinstance(location, SourceLocation)
|
||||
assert location.line == 1
|
||||
assert location.column == 3
|
||||
assert location.file.name == 't.c'
|
||||
|
||||
def test_get_source_range():
|
||||
"""Ensure tu.get_source_range() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
r = tu.get_extent('t.c', (1,4))
|
||||
assert isinstance(r, SourceRange)
|
||||
assert r.start.offset == 1
|
||||
assert r.end.offset == 4
|
||||
assert r.start.file.name == 't.c'
|
||||
assert r.end.file.name == 't.c'
|
||||
|
||||
r = tu.get_extent('t.c', ((1,2), (1,3)))
|
||||
assert isinstance(r, SourceRange)
|
||||
assert r.start.line == 1
|
||||
assert r.start.column == 2
|
||||
assert r.end.line == 1
|
||||
assert r.end.column == 3
|
||||
assert r.start.file.name == 't.c'
|
||||
assert r.end.file.name == 't.c'
|
||||
|
||||
start = tu.get_location('t.c', 0)
|
||||
end = tu.get_location('t.c', 5)
|
||||
|
||||
r = tu.get_extent('t.c', (start, end))
|
||||
assert isinstance(r, SourceRange)
|
||||
assert r.start.offset == 0
|
||||
assert r.end.offset == 5
|
||||
assert r.start.file.name == 't.c'
|
||||
assert r.end.file.name == 't.c'
|
||||
|
||||
def test_get_tokens_gc():
|
||||
"""Ensures get_tokens() works properly with garbage collection."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
r = tu.get_extent('t.c', (0, 10))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
|
||||
assert tokens[0].spelling == 'int'
|
||||
gc.collect()
|
||||
assert tokens[0].spelling == 'int'
|
||||
|
||||
del tokens[1]
|
||||
gc.collect()
|
||||
assert tokens[0].spelling == 'int'
|
||||
|
||||
# May trigger segfault if we don't do our job properly.
|
||||
del tokens
|
||||
gc.collect()
|
||||
gc.collect() # Just in case.
|
||||
|
||||
def test_fail_from_source():
|
||||
path = os.path.join(kInputsDir, 'non-existent.cpp')
|
||||
try:
|
||||
class TestTranslationUnit(unittest.TestCase):
|
||||
def test_spelling(self):
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
tu = TranslationUnit.from_source(path)
|
||||
except TranslationUnitLoadError:
|
||||
tu = None
|
||||
assert tu == None
|
||||
self.assertEqual(tu.spelling, path)
|
||||
|
||||
def test_fail_from_ast_file():
|
||||
path = os.path.join(kInputsDir, 'non-existent.ast')
|
||||
try:
|
||||
tu = TranslationUnit.from_ast_file(path)
|
||||
except TranslationUnitLoadError:
|
||||
tu = None
|
||||
assert tu == None
|
||||
def test_cursor(self):
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
tu = get_tu(path)
|
||||
c = tu.cursor
|
||||
self.assertIsInstance(c, Cursor)
|
||||
self.assertIs(c.kind, CursorKind.TRANSLATION_UNIT)
|
||||
|
||||
def test_parse_arguments(self):
|
||||
path = os.path.join(kInputsDir, 'parse_arguments.c')
|
||||
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'hello')
|
||||
self.assertEqual(spellings[-1], 'hi')
|
||||
|
||||
def test_reparse_arguments(self):
|
||||
path = os.path.join(kInputsDir, 'parse_arguments.c')
|
||||
tu = TranslationUnit.from_source(path, ['-DDECL_ONE=hello', '-DDECL_TWO=hi'])
|
||||
tu.reparse()
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'hello')
|
||||
self.assertEqual(spellings[-1], 'hi')
|
||||
|
||||
def test_unsaved_files(self):
|
||||
tu = TranslationUnit.from_source('fake.c', ['-I./'], unsaved_files = [
|
||||
('fake.c', """
|
||||
#include "fake.h"
|
||||
int x;
|
||||
int SOME_DEFINE;
|
||||
"""),
|
||||
('./fake.h', """
|
||||
#define SOME_DEFINE y
|
||||
""")
|
||||
])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-2], 'x')
|
||||
self.assertEqual(spellings[-1], 'y')
|
||||
|
||||
def test_unsaved_files_2(self):
|
||||
try:
|
||||
from StringIO import StringIO
|
||||
except:
|
||||
from io import StringIO
|
||||
tu = TranslationUnit.from_source('fake.c', unsaved_files = [
|
||||
('fake.c', StringIO('int x;'))])
|
||||
spellings = [c.spelling for c in tu.cursor.get_children()]
|
||||
self.assertEqual(spellings[-1], 'x')
|
||||
|
||||
def assert_normpaths_equal(self, path1, path2):
|
||||
""" Compares two paths for equality after normalizing them with
|
||||
os.path.normpath
|
||||
"""
|
||||
self.assertEqual(os.path.normpath(path1),
|
||||
os.path.normpath(path2))
|
||||
|
||||
def test_includes(self):
|
||||
def eq(expected, actual):
|
||||
if not actual.is_input_file:
|
||||
self.assert_normpaths_equal(expected[0], actual.source.name)
|
||||
self.assert_normpaths_equal(expected[1], actual.include.name)
|
||||
else:
|
||||
self.assert_normpaths_equal(expected[1], actual.include.name)
|
||||
|
||||
src = os.path.join(kInputsDir, 'include.cpp')
|
||||
h1 = os.path.join(kInputsDir, "header1.h")
|
||||
h2 = os.path.join(kInputsDir, "header2.h")
|
||||
h3 = os.path.join(kInputsDir, "header3.h")
|
||||
inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)]
|
||||
|
||||
tu = TranslationUnit.from_source(src)
|
||||
for i in zip(inc, tu.get_includes()):
|
||||
eq(i[0], i[1])
|
||||
|
||||
def test_save(self):
|
||||
"""Ensure TranslationUnit.save() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
path = save_tu(tu)
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.assertGreater(os.path.getsize(path), 0)
|
||||
os.unlink(path)
|
||||
|
||||
def test_save_translation_errors(self):
|
||||
"""Ensure that saving to an invalid directory raises."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
path = '/does/not/exist/llvm-test.ast'
|
||||
self.assertFalse(os.path.exists(os.path.dirname(path)))
|
||||
|
||||
with self.assertRaises(TranslationUnitSaveError) as cm:
|
||||
tu.save(path)
|
||||
ex = cm.exception
|
||||
expected = TranslationUnitSaveError.ERROR_UNKNOWN
|
||||
self.assertEqual(ex.save_error, expected)
|
||||
|
||||
def test_load(self):
|
||||
"""Ensure TranslationUnits can be constructed from saved files."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
self.assertEqual(len(tu.diagnostics), 0)
|
||||
path = save_tu(tu)
|
||||
|
||||
self.assertTrue(os.path.exists(path))
|
||||
self.assertGreater(os.path.getsize(path), 0)
|
||||
|
||||
tu2 = TranslationUnit.from_ast_file(filename=path)
|
||||
self.assertEqual(len(tu2.diagnostics), 0)
|
||||
|
||||
foo = get_cursor(tu2, 'foo')
|
||||
self.assertIsNotNone(foo)
|
||||
|
||||
# Just in case there is an open file descriptor somewhere.
|
||||
del tu2
|
||||
|
||||
os.unlink(path)
|
||||
|
||||
def test_index_parse(self):
|
||||
path = os.path.join(kInputsDir, 'hello.cpp')
|
||||
index = Index.create()
|
||||
tu = index.parse(path)
|
||||
self.assertIsInstance(tu, TranslationUnit)
|
||||
|
||||
def test_get_file(self):
|
||||
"""Ensure tu.get_file() works appropriately."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
f = tu.get_file('t.c')
|
||||
self.assertIsInstance(f, File)
|
||||
self.assertEqual(f.name, 't.c')
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
f = tu.get_file('foobar.cpp')
|
||||
|
||||
def test_get_source_location(self):
|
||||
"""Ensure tu.get_source_location() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
location = tu.get_location('t.c', 2)
|
||||
self.assertIsInstance(location, SourceLocation)
|
||||
self.assertEqual(location.offset, 2)
|
||||
self.assertEqual(location.file.name, 't.c')
|
||||
|
||||
location = tu.get_location('t.c', (1, 3))
|
||||
self.assertIsInstance(location, SourceLocation)
|
||||
self.assertEqual(location.line, 1)
|
||||
self.assertEqual(location.column, 3)
|
||||
self.assertEqual(location.file.name, 't.c')
|
||||
|
||||
def test_get_source_range(self):
|
||||
"""Ensure tu.get_source_range() works."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
|
||||
r = tu.get_extent('t.c', (1,4))
|
||||
self.assertIsInstance(r, SourceRange)
|
||||
self.assertEqual(r.start.offset, 1)
|
||||
self.assertEqual(r.end.offset, 4)
|
||||
self.assertEqual(r.start.file.name, 't.c')
|
||||
self.assertEqual(r.end.file.name, 't.c')
|
||||
|
||||
r = tu.get_extent('t.c', ((1,2), (1,3)))
|
||||
self.assertIsInstance(r, SourceRange)
|
||||
self.assertEqual(r.start.line, 1)
|
||||
self.assertEqual(r.start.column, 2)
|
||||
self.assertEqual(r.end.line, 1)
|
||||
self.assertEqual(r.end.column, 3)
|
||||
self.assertEqual(r.start.file.name, 't.c')
|
||||
self.assertEqual(r.end.file.name, 't.c')
|
||||
|
||||
start = tu.get_location('t.c', 0)
|
||||
end = tu.get_location('t.c', 5)
|
||||
|
||||
r = tu.get_extent('t.c', (start, end))
|
||||
self.assertIsInstance(r, SourceRange)
|
||||
self.assertEqual(r.start.offset, 0)
|
||||
self.assertEqual(r.end.offset, 5)
|
||||
self.assertEqual(r.start.file.name, 't.c')
|
||||
self.assertEqual(r.end.file.name, 't.c')
|
||||
|
||||
def test_get_tokens_gc(self):
|
||||
"""Ensures get_tokens() works properly with garbage collection."""
|
||||
|
||||
tu = get_tu('int foo();')
|
||||
r = tu.get_extent('t.c', (0, 10))
|
||||
tokens = list(tu.get_tokens(extent=r))
|
||||
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
gc.collect()
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
|
||||
del tokens[1]
|
||||
gc.collect()
|
||||
self.assertEqual(tokens[0].spelling, 'int')
|
||||
|
||||
# May trigger segfault if we don't do our job properly.
|
||||
del tokens
|
||||
gc.collect()
|
||||
gc.collect() # Just in case.
|
||||
|
||||
def test_fail_from_source(self):
|
||||
path = os.path.join(kInputsDir, 'non-existent.cpp')
|
||||
try:
|
||||
tu = TranslationUnit.from_source(path)
|
||||
except TranslationUnitLoadError:
|
||||
tu = None
|
||||
self.assertEqual(tu, None)
|
||||
|
||||
def test_fail_from_ast_file(self):
|
||||
path = os.path.join(kInputsDir, 'non-existent.ast')
|
||||
try:
|
||||
tu = TranslationUnit.from_ast_file(path)
|
||||
except TranslationUnitLoadError:
|
||||
tu = None
|
||||
self.assertEqual(tu, None)
|
||||
|
@ -1,12 +1,13 @@
|
||||
import gc
|
||||
import unittest
|
||||
|
||||
from clang.cindex import CursorKind
|
||||
from clang.cindex import TranslationUnit
|
||||
from clang.cindex import TypeKind
|
||||
from nose.tools import raises
|
||||
from .util import get_cursor
|
||||
from .util import get_tu
|
||||
|
||||
|
||||
kInput = """\
|
||||
|
||||
typedef int I;
|
||||
@ -24,400 +25,414 @@ struct teststruct {
|
||||
|
||||
"""
|
||||
|
||||
def test_a_struct():
|
||||
tu = get_tu(kInput)
|
||||
|
||||
teststruct = get_cursor(tu, 'teststruct')
|
||||
assert teststruct is not None, "Could not find teststruct."
|
||||
fields = list(teststruct.get_children())
|
||||
assert all(x.kind == CursorKind.FIELD_DECL for x in fields)
|
||||
assert all(x.translation_unit is not None for x in fields)
|
||||
|
||||
assert fields[0].spelling == 'a'
|
||||
assert not fields[0].type.is_const_qualified()
|
||||
assert fields[0].type.kind == TypeKind.INT
|
||||
assert fields[0].type.get_canonical().kind == TypeKind.INT
|
||||
assert fields[0].type.get_typedef_name() == ''
|
||||
|
||||
assert fields[1].spelling == 'b'
|
||||
assert not fields[1].type.is_const_qualified()
|
||||
assert fields[1].type.kind == TypeKind.TYPEDEF
|
||||
assert fields[1].type.get_canonical().kind == TypeKind.INT
|
||||
assert fields[1].type.get_declaration().spelling == 'I'
|
||||
assert fields[1].type.get_typedef_name() == 'I'
|
||||
|
||||
assert fields[2].spelling == 'c'
|
||||
assert not fields[2].type.is_const_qualified()
|
||||
assert fields[2].type.kind == TypeKind.LONG
|
||||
assert fields[2].type.get_canonical().kind == TypeKind.LONG
|
||||
assert fields[2].type.get_typedef_name() == ''
|
||||
|
||||
assert fields[3].spelling == 'd'
|
||||
assert not fields[3].type.is_const_qualified()
|
||||
assert fields[3].type.kind == TypeKind.ULONG
|
||||
assert fields[3].type.get_canonical().kind == TypeKind.ULONG
|
||||
assert fields[3].type.get_typedef_name() == ''
|
||||
|
||||
assert fields[4].spelling == 'e'
|
||||
assert not fields[4].type.is_const_qualified()
|
||||
assert fields[4].type.kind == TypeKind.LONG
|
||||
assert fields[4].type.get_canonical().kind == TypeKind.LONG
|
||||
assert fields[4].type.get_typedef_name() == ''
|
||||
|
||||
assert fields[5].spelling == 'f'
|
||||
assert fields[5].type.is_const_qualified()
|
||||
assert fields[5].type.kind == TypeKind.INT
|
||||
assert fields[5].type.get_canonical().kind == TypeKind.INT
|
||||
assert fields[5].type.get_typedef_name() == ''
|
||||
|
||||
assert fields[6].spelling == 'g'
|
||||
assert not fields[6].type.is_const_qualified()
|
||||
assert fields[6].type.kind == TypeKind.POINTER
|
||||
assert fields[6].type.get_pointee().kind == TypeKind.INT
|
||||
assert fields[6].type.get_typedef_name() == ''
|
||||
|
||||
assert fields[7].spelling == 'h'
|
||||
assert not fields[7].type.is_const_qualified()
|
||||
assert fields[7].type.kind == TypeKind.POINTER
|
||||
assert fields[7].type.get_pointee().kind == TypeKind.POINTER
|
||||
assert fields[7].type.get_pointee().get_pointee().kind == TypeKind.POINTER
|
||||
assert fields[7].type.get_pointee().get_pointee().get_pointee().kind == TypeKind.INT
|
||||
assert fields[7].type.get_typedef_name() == ''
|
||||
|
||||
def test_references():
|
||||
"""Ensure that a Type maintains a reference to a TranslationUnit."""
|
||||
|
||||
tu = get_tu('int x;')
|
||||
children = list(tu.cursor.get_children())
|
||||
assert len(children) > 0
|
||||
|
||||
cursor = children[0]
|
||||
t = cursor.type
|
||||
|
||||
assert isinstance(t.translation_unit, TranslationUnit)
|
||||
|
||||
# Delete main TranslationUnit reference and force a GC.
|
||||
del tu
|
||||
gc.collect()
|
||||
assert isinstance(t.translation_unit, TranslationUnit)
|
||||
|
||||
# If the TU was destroyed, this should cause a segfault.
|
||||
decl = t.get_declaration()
|
||||
|
||||
constarrayInput="""
|
||||
struct teststruct {
|
||||
void *A[2];
|
||||
};
|
||||
"""
|
||||
def testConstantArray():
|
||||
tu = get_tu(constarrayInput)
|
||||
|
||||
teststruct = get_cursor(tu, 'teststruct')
|
||||
assert teststruct is not None, "Didn't find teststruct??"
|
||||
fields = list(teststruct.get_children())
|
||||
assert fields[0].spelling == 'A'
|
||||
assert fields[0].type.kind == TypeKind.CONSTANTARRAY
|
||||
assert fields[0].type.get_array_element_type() is not None
|
||||
assert fields[0].type.get_array_element_type().kind == TypeKind.POINTER
|
||||
assert fields[0].type.get_array_size() == 2
|
||||
|
||||
def test_equal():
|
||||
"""Ensure equivalence operators work on Type."""
|
||||
source = 'int a; int b; void *v;'
|
||||
tu = get_tu(source)
|
||||
class TestType(unittest.TestCase):
|
||||
def test_a_struct(self):
|
||||
tu = get_tu(kInput)
|
||||
|
||||
a = get_cursor(tu, 'a')
|
||||
b = get_cursor(tu, 'b')
|
||||
v = get_cursor(tu, 'v')
|
||||
|
||||
assert a is not None
|
||||
assert b is not None
|
||||
assert v is not None
|
||||
|
||||
assert a.type == b.type
|
||||
assert a.type != v.type
|
||||
|
||||
assert a.type != None
|
||||
assert a.type != 'foo'
|
||||
|
||||
def test_type_spelling():
|
||||
"""Ensure Type.spelling works."""
|
||||
tu = get_tu('int c[5]; void f(int i[]); int x; int v[x];')
|
||||
c = get_cursor(tu, 'c')
|
||||
i = get_cursor(tu, 'i')
|
||||
x = get_cursor(tu, 'x')
|
||||
v = get_cursor(tu, 'v')
|
||||
assert c is not None
|
||||
assert i is not None
|
||||
assert x is not None
|
||||
assert v is not None
|
||||
assert c.type.spelling == "int [5]"
|
||||
assert i.type.spelling == "int []"
|
||||
assert x.type.spelling == "int"
|
||||
assert v.type.spelling == "int [x]"
|
||||
|
||||
def test_typekind_spelling():
|
||||
"""Ensure TypeKind.spelling works."""
|
||||
tu = get_tu('int a;')
|
||||
a = get_cursor(tu, 'a')
|
||||
|
||||
assert a is not None
|
||||
assert a.type.kind.spelling == 'Int'
|
||||
|
||||
def test_function_argument_types():
|
||||
"""Ensure that Type.argument_types() works as expected."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
assert f is not None
|
||||
|
||||
args = f.type.argument_types()
|
||||
assert args is not None
|
||||
assert len(args) == 2
|
||||
|
||||
t0 = args[0]
|
||||
assert t0 is not None
|
||||
assert t0.kind == TypeKind.INT
|
||||
|
||||
t1 = args[1]
|
||||
assert t1 is not None
|
||||
assert t1.kind == TypeKind.INT
|
||||
|
||||
args2 = list(args)
|
||||
assert len(args2) == 2
|
||||
assert t0 == args2[0]
|
||||
assert t1 == args2[1]
|
||||
|
||||
@raises(TypeError)
|
||||
def test_argument_types_string_key():
|
||||
"""Ensure that non-int keys raise a TypeError."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
assert f is not None
|
||||
|
||||
args = f.type.argument_types()
|
||||
assert len(args) == 2
|
||||
|
||||
args['foo']
|
||||
|
||||
@raises(IndexError)
|
||||
def test_argument_types_negative_index():
|
||||
"""Ensure that negative indexes on argument_types Raises an IndexError."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
args = f.type.argument_types()
|
||||
|
||||
args[-1]
|
||||
|
||||
@raises(IndexError)
|
||||
def test_argument_types_overflow_index():
|
||||
"""Ensure that indexes beyond the length of Type.argument_types() raise."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
args = f.type.argument_types()
|
||||
|
||||
args[2]
|
||||
|
||||
@raises(Exception)
|
||||
def test_argument_types_invalid_type():
|
||||
"""Ensure that obtaining argument_types on a Type without them raises."""
|
||||
tu = get_tu('int i;')
|
||||
i = get_cursor(tu, 'i')
|
||||
assert i is not None
|
||||
|
||||
i.type.argument_types()
|
||||
|
||||
def test_is_pod():
|
||||
"""Ensure Type.is_pod() works."""
|
||||
tu = get_tu('int i; void f();')
|
||||
i = get_cursor(tu, 'i')
|
||||
f = get_cursor(tu, 'f')
|
||||
|
||||
assert i is not None
|
||||
assert f is not None
|
||||
|
||||
assert i.type.is_pod()
|
||||
assert not f.type.is_pod()
|
||||
|
||||
def test_function_variadic():
|
||||
"""Ensure Type.is_function_variadic works."""
|
||||
|
||||
source ="""
|
||||
#include <stdarg.h>
|
||||
|
||||
void foo(int a, ...);
|
||||
void bar(int a, int b);
|
||||
"""
|
||||
|
||||
tu = get_tu(source)
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
|
||||
assert foo is not None
|
||||
assert bar is not None
|
||||
|
||||
assert isinstance(foo.type.is_function_variadic(), bool)
|
||||
assert foo.type.is_function_variadic()
|
||||
assert not bar.type.is_function_variadic()
|
||||
|
||||
def test_element_type():
|
||||
"""Ensure Type.element_type works."""
|
||||
tu = get_tu('int c[5]; void f(int i[]); int x; int v[x];')
|
||||
c = get_cursor(tu, 'c')
|
||||
i = get_cursor(tu, 'i')
|
||||
v = get_cursor(tu, 'v')
|
||||
assert c is not None
|
||||
assert i is not None
|
||||
assert v is not None
|
||||
|
||||
assert c.type.kind == TypeKind.CONSTANTARRAY
|
||||
assert c.type.element_type.kind == TypeKind.INT
|
||||
assert i.type.kind == TypeKind.INCOMPLETEARRAY
|
||||
assert i.type.element_type.kind == TypeKind.INT
|
||||
assert v.type.kind == TypeKind.VARIABLEARRAY
|
||||
assert v.type.element_type.kind == TypeKind.INT
|
||||
|
||||
@raises(Exception)
|
||||
def test_invalid_element_type():
|
||||
"""Ensure Type.element_type raises if type doesn't have elements."""
|
||||
tu = get_tu('int i;')
|
||||
i = get_cursor(tu, 'i')
|
||||
assert i is not None
|
||||
i.element_type
|
||||
|
||||
def test_element_count():
|
||||
"""Ensure Type.element_count works."""
|
||||
tu = get_tu('int i[5]; int j;')
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
assert i is not None
|
||||
assert j is not None
|
||||
|
||||
assert i.type.element_count == 5
|
||||
|
||||
try:
|
||||
j.type.element_count
|
||||
assert False
|
||||
except:
|
||||
assert True
|
||||
|
||||
def test_is_volatile_qualified():
|
||||
"""Ensure Type.is_volatile_qualified works."""
|
||||
|
||||
tu = get_tu('volatile int i = 4; int j = 2;')
|
||||
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
assert i is not None
|
||||
assert j is not None
|
||||
|
||||
assert isinstance(i.type.is_volatile_qualified(), bool)
|
||||
assert i.type.is_volatile_qualified()
|
||||
assert not j.type.is_volatile_qualified()
|
||||
|
||||
def test_is_restrict_qualified():
|
||||
"""Ensure Type.is_restrict_qualified works."""
|
||||
|
||||
tu = get_tu('struct s { void * restrict i; void * j; };')
|
||||
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
assert i is not None
|
||||
assert j is not None
|
||||
|
||||
assert isinstance(i.type.is_restrict_qualified(), bool)
|
||||
assert i.type.is_restrict_qualified()
|
||||
assert not j.type.is_restrict_qualified()
|
||||
|
||||
def test_record_layout():
|
||||
"""Ensure Cursor.type.get_size, Cursor.type.get_align and
|
||||
Cursor.type.get_offset works."""
|
||||
|
||||
source ="""
|
||||
struct a {
|
||||
long a1;
|
||||
long a2:3;
|
||||
long a3:4;
|
||||
long long a4;
|
||||
};
|
||||
"""
|
||||
tries=[(['-target','i386-linux-gnu'],(4,16,0,32,35,64)),
|
||||
(['-target','nvptx64-unknown-unknown'],(8,24,0,64,67,128)),
|
||||
(['-target','i386-pc-win32'],(8,16,0,32,35,64)),
|
||||
(['-target','msp430-none-none'],(2,14,0,32,35,48))]
|
||||
for flags, values in tries:
|
||||
align,total,a1,a2,a3,a4 = values
|
||||
|
||||
tu = get_tu(source, flags=flags)
|
||||
teststruct = get_cursor(tu, 'a')
|
||||
teststruct = get_cursor(tu, 'teststruct')
|
||||
self.assertIsNotNone(teststruct, "Could not find teststruct.")
|
||||
fields = list(teststruct.get_children())
|
||||
|
||||
assert teststruct.type.get_align() == align
|
||||
assert teststruct.type.get_size() == total
|
||||
assert teststruct.type.get_offset(fields[0].spelling) == a1
|
||||
assert teststruct.type.get_offset(fields[1].spelling) == a2
|
||||
assert teststruct.type.get_offset(fields[2].spelling) == a3
|
||||
assert teststruct.type.get_offset(fields[3].spelling) == a4
|
||||
assert fields[0].is_bitfield() == False
|
||||
assert fields[1].is_bitfield() == True
|
||||
assert fields[1].get_bitfield_width() == 3
|
||||
assert fields[2].is_bitfield() == True
|
||||
assert fields[2].get_bitfield_width() == 4
|
||||
assert fields[3].is_bitfield() == False
|
||||
self.assertEqual(fields[0].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[0].translation_unit)
|
||||
self.assertEqual(fields[0].spelling, 'a')
|
||||
self.assertFalse(fields[0].type.is_const_qualified())
|
||||
self.assertEqual(fields[0].type.kind, TypeKind.INT)
|
||||
self.assertEqual(fields[0].type.get_canonical().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[0].type.get_typedef_name(), '')
|
||||
|
||||
def test_offset():
|
||||
"""Ensure Cursor.get_record_field_offset works in anonymous records"""
|
||||
source="""
|
||||
struct Test {
|
||||
struct {int a;} typeanon;
|
||||
struct {
|
||||
int bariton;
|
||||
union {
|
||||
int foo;
|
||||
};
|
||||
};
|
||||
int bar;
|
||||
};"""
|
||||
tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64,96)),
|
||||
(['-target','nvptx64-unknown-unknown'],(8,24,0,32,64,96)),
|
||||
(['-target','i386-pc-win32'],(8,16,0,32,64,96)),
|
||||
(['-target','msp430-none-none'],(2,14,0,32,64,96))]
|
||||
for flags, values in tries:
|
||||
align,total,f1,bariton,foo,bar = values
|
||||
self.assertEqual(fields[1].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[1].translation_unit)
|
||||
self.assertEqual(fields[1].spelling, 'b')
|
||||
self.assertFalse(fields[1].type.is_const_qualified())
|
||||
self.assertEqual(fields[1].type.kind, TypeKind.TYPEDEF)
|
||||
self.assertEqual(fields[1].type.get_canonical().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[1].type.get_declaration().spelling, 'I')
|
||||
self.assertEqual(fields[1].type.get_typedef_name(), 'I')
|
||||
|
||||
self.assertEqual(fields[2].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[2].translation_unit)
|
||||
self.assertEqual(fields[2].spelling, 'c')
|
||||
self.assertFalse(fields[2].type.is_const_qualified())
|
||||
self.assertEqual(fields[2].type.kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[2].type.get_canonical().kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[2].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[3].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[3].translation_unit)
|
||||
self.assertEqual(fields[3].spelling, 'd')
|
||||
self.assertFalse(fields[3].type.is_const_qualified())
|
||||
self.assertEqual(fields[3].type.kind, TypeKind.ULONG)
|
||||
self.assertEqual(fields[3].type.get_canonical().kind, TypeKind.ULONG)
|
||||
self.assertEqual(fields[3].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[4].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[4].translation_unit)
|
||||
self.assertEqual(fields[4].spelling, 'e')
|
||||
self.assertFalse(fields[4].type.is_const_qualified())
|
||||
self.assertEqual(fields[4].type.kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[4].type.get_canonical().kind, TypeKind.LONG)
|
||||
self.assertEqual(fields[4].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[5].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[5].translation_unit)
|
||||
self.assertEqual(fields[5].spelling, 'f')
|
||||
self.assertTrue(fields[5].type.is_const_qualified())
|
||||
self.assertEqual(fields[5].type.kind, TypeKind.INT)
|
||||
self.assertEqual(fields[5].type.get_canonical().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[5].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[6].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[6].translation_unit)
|
||||
self.assertEqual(fields[6].spelling, 'g')
|
||||
self.assertFalse(fields[6].type.is_const_qualified())
|
||||
self.assertEqual(fields[6].type.kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[6].type.get_pointee().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[6].type.get_typedef_name(), '')
|
||||
|
||||
self.assertEqual(fields[7].kind, CursorKind.FIELD_DECL)
|
||||
self.assertIsNotNone(fields[7].translation_unit)
|
||||
self.assertEqual(fields[7].spelling, 'h')
|
||||
self.assertFalse(fields[7].type.is_const_qualified())
|
||||
self.assertEqual(fields[7].type.kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[7].type.get_pointee().kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[7].type.get_pointee().get_pointee().kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[7].type.get_pointee().get_pointee().get_pointee().kind, TypeKind.INT)
|
||||
self.assertEqual(fields[7].type.get_typedef_name(), '')
|
||||
|
||||
def test_references(self):
|
||||
"""Ensure that a Type maintains a reference to a TranslationUnit."""
|
||||
|
||||
tu = get_tu('int x;')
|
||||
children = list(tu.cursor.get_children())
|
||||
self.assertGreater(len(children), 0)
|
||||
|
||||
cursor = children[0]
|
||||
t = cursor.type
|
||||
|
||||
self.assertIsInstance(t.translation_unit, TranslationUnit)
|
||||
|
||||
# Delete main TranslationUnit reference and force a GC.
|
||||
del tu
|
||||
gc.collect()
|
||||
self.assertIsInstance(t.translation_unit, TranslationUnit)
|
||||
|
||||
# If the TU was destroyed, this should cause a segfault.
|
||||
decl = t.get_declaration()
|
||||
|
||||
def testConstantArray(self):
|
||||
tu = get_tu(constarrayInput)
|
||||
|
||||
teststruct = get_cursor(tu, 'teststruct')
|
||||
self.assertIsNotNone(teststruct, "Didn't find teststruct??")
|
||||
fields = list(teststruct.get_children())
|
||||
self.assertEqual(fields[0].spelling, 'A')
|
||||
self.assertEqual(fields[0].type.kind, TypeKind.CONSTANTARRAY)
|
||||
self.assertIsNotNone(fields[0].type.get_array_element_type())
|
||||
self.assertEqual(fields[0].type.get_array_element_type().kind, TypeKind.POINTER)
|
||||
self.assertEqual(fields[0].type.get_array_size(), 2)
|
||||
|
||||
def test_equal(self):
|
||||
"""Ensure equivalence operators work on Type."""
|
||||
source = 'int a; int b; void *v;'
|
||||
tu = get_tu(source)
|
||||
teststruct = get_cursor(tu, 'Test')
|
||||
children = list(teststruct.get_children())
|
||||
fields = list(teststruct.type.get_fields())
|
||||
assert children[0].kind == CursorKind.STRUCT_DECL
|
||||
assert children[0].spelling != "typeanon"
|
||||
assert children[1].spelling == "typeanon"
|
||||
assert fields[0].kind == CursorKind.FIELD_DECL
|
||||
assert fields[1].kind == CursorKind.FIELD_DECL
|
||||
assert fields[1].is_anonymous()
|
||||
assert teststruct.type.get_offset("typeanon") == f1
|
||||
assert teststruct.type.get_offset("bariton") == bariton
|
||||
assert teststruct.type.get_offset("foo") == foo
|
||||
assert teststruct.type.get_offset("bar") == bar
|
||||
|
||||
a = get_cursor(tu, 'a')
|
||||
b = get_cursor(tu, 'b')
|
||||
v = get_cursor(tu, 'v')
|
||||
|
||||
def test_decay():
|
||||
"""Ensure decayed types are handled as the original type"""
|
||||
self.assertIsNotNone(a)
|
||||
self.assertIsNotNone(b)
|
||||
self.assertIsNotNone(v)
|
||||
|
||||
tu = get_tu("void foo(int a[]);")
|
||||
foo = get_cursor(tu, 'foo')
|
||||
a = foo.type.argument_types()[0]
|
||||
self.assertEqual(a.type, b.type)
|
||||
self.assertNotEqual(a.type, v.type)
|
||||
|
||||
assert a.kind == TypeKind.INCOMPLETEARRAY
|
||||
assert a.element_type.kind == TypeKind.INT
|
||||
assert a.get_canonical().kind == TypeKind.INCOMPLETEARRAY
|
||||
self.assertNotEqual(a.type, None)
|
||||
self.assertNotEqual(a.type, 'foo')
|
||||
|
||||
def test_addrspace():
|
||||
"""Ensure the address space can be queried"""
|
||||
tu = get_tu('__attribute__((address_space(2))) int testInteger = 3;', 'c')
|
||||
def test_type_spelling(self):
|
||||
"""Ensure Type.spelling works."""
|
||||
tu = get_tu('int c[5]; void f(int i[]); int x; int v[x];')
|
||||
c = get_cursor(tu, 'c')
|
||||
i = get_cursor(tu, 'i')
|
||||
x = get_cursor(tu, 'x')
|
||||
v = get_cursor(tu, 'v')
|
||||
self.assertIsNotNone(c)
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(x)
|
||||
self.assertIsNotNone(v)
|
||||
self.assertEqual(c.type.spelling, "int [5]")
|
||||
self.assertEqual(i.type.spelling, "int []")
|
||||
self.assertEqual(x.type.spelling, "int")
|
||||
self.assertEqual(v.type.spelling, "int [x]")
|
||||
|
||||
testInteger = get_cursor(tu, 'testInteger')
|
||||
def test_typekind_spelling(self):
|
||||
"""Ensure TypeKind.spelling works."""
|
||||
tu = get_tu('int a;')
|
||||
a = get_cursor(tu, 'a')
|
||||
|
||||
assert testInteger is not None, "Could not find testInteger."
|
||||
assert testInteger.type.get_address_space() == 2
|
||||
self.assertIsNotNone(a)
|
||||
self.assertEqual(a.type.kind.spelling, 'Int')
|
||||
|
||||
def test_function_argument_types(self):
|
||||
"""Ensure that Type.argument_types() works as expected."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
self.assertIsNotNone(f)
|
||||
|
||||
args = f.type.argument_types()
|
||||
self.assertIsNotNone(args)
|
||||
self.assertEqual(len(args), 2)
|
||||
|
||||
t0 = args[0]
|
||||
self.assertIsNotNone(t0)
|
||||
self.assertEqual(t0.kind, TypeKind.INT)
|
||||
|
||||
t1 = args[1]
|
||||
self.assertIsNotNone(t1)
|
||||
self.assertEqual(t1.kind, TypeKind.INT)
|
||||
|
||||
args2 = list(args)
|
||||
self.assertEqual(len(args2), 2)
|
||||
self.assertEqual(t0, args2[0])
|
||||
self.assertEqual(t1, args2[1])
|
||||
|
||||
def test_argument_types_string_key(self):
|
||||
"""Ensure that non-int keys raise a TypeError."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
self.assertIsNotNone(f)
|
||||
|
||||
args = f.type.argument_types()
|
||||
self.assertEqual(len(args), 2)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
args['foo']
|
||||
|
||||
def test_argument_types_negative_index(self):
|
||||
"""Ensure that negative indexes on argument_types Raises an IndexError."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
args = f.type.argument_types()
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
args[-1]
|
||||
|
||||
def test_argument_types_overflow_index(self):
|
||||
"""Ensure that indexes beyond the length of Type.argument_types() raise."""
|
||||
tu = get_tu('void f(int, int);')
|
||||
f = get_cursor(tu, 'f')
|
||||
args = f.type.argument_types()
|
||||
|
||||
with self.assertRaises(IndexError):
|
||||
args[2]
|
||||
|
||||
def test_argument_types_invalid_type(self):
|
||||
"""Ensure that obtaining argument_types on a Type without them raises."""
|
||||
tu = get_tu('int i;')
|
||||
i = get_cursor(tu, 'i')
|
||||
self.assertIsNotNone(i)
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
i.type.argument_types()
|
||||
|
||||
def test_is_pod(self):
|
||||
"""Ensure Type.is_pod() works."""
|
||||
tu = get_tu('int i; void f();')
|
||||
i = get_cursor(tu, 'i')
|
||||
f = get_cursor(tu, 'f')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(f)
|
||||
|
||||
self.assertTrue(i.type.is_pod())
|
||||
self.assertFalse(f.type.is_pod())
|
||||
|
||||
def test_function_variadic(self):
|
||||
"""Ensure Type.is_function_variadic works."""
|
||||
|
||||
source ="""
|
||||
#include <stdarg.h>
|
||||
|
||||
void foo(int a, ...);
|
||||
void bar(int a, int b);
|
||||
"""
|
||||
|
||||
tu = get_tu(source)
|
||||
foo = get_cursor(tu, 'foo')
|
||||
bar = get_cursor(tu, 'bar')
|
||||
|
||||
self.assertIsNotNone(foo)
|
||||
self.assertIsNotNone(bar)
|
||||
|
||||
self.assertIsInstance(foo.type.is_function_variadic(), bool)
|
||||
self.assertTrue(foo.type.is_function_variadic())
|
||||
self.assertFalse(bar.type.is_function_variadic())
|
||||
|
||||
def test_element_type(self):
|
||||
"""Ensure Type.element_type works."""
|
||||
tu = get_tu('int c[5]; void f(int i[]); int x; int v[x];')
|
||||
c = get_cursor(tu, 'c')
|
||||
i = get_cursor(tu, 'i')
|
||||
v = get_cursor(tu, 'v')
|
||||
self.assertIsNotNone(c)
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(v)
|
||||
|
||||
self.assertEqual(c.type.kind, TypeKind.CONSTANTARRAY)
|
||||
self.assertEqual(c.type.element_type.kind, TypeKind.INT)
|
||||
self.assertEqual(i.type.kind, TypeKind.INCOMPLETEARRAY)
|
||||
self.assertEqual(i.type.element_type.kind, TypeKind.INT)
|
||||
self.assertEqual(v.type.kind, TypeKind.VARIABLEARRAY)
|
||||
self.assertEqual(v.type.element_type.kind, TypeKind.INT)
|
||||
|
||||
def test_invalid_element_type(self):
|
||||
"""Ensure Type.element_type raises if type doesn't have elements."""
|
||||
tu = get_tu('int i;')
|
||||
i = get_cursor(tu, 'i')
|
||||
self.assertIsNotNone(i)
|
||||
with self.assertRaises(Exception):
|
||||
i.element_type
|
||||
|
||||
def test_element_count(self):
|
||||
"""Ensure Type.element_count works."""
|
||||
tu = get_tu('int i[5]; int j;')
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(j)
|
||||
|
||||
self.assertEqual(i.type.element_count, 5)
|
||||
|
||||
with self.assertRaises(Exception):
|
||||
j.type.element_count
|
||||
|
||||
def test_is_volatile_qualified(self):
|
||||
"""Ensure Type.is_volatile_qualified works."""
|
||||
|
||||
tu = get_tu('volatile int i = 4; int j = 2;')
|
||||
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(j)
|
||||
|
||||
self.assertIsInstance(i.type.is_volatile_qualified(), bool)
|
||||
self.assertTrue(i.type.is_volatile_qualified())
|
||||
self.assertFalse(j.type.is_volatile_qualified())
|
||||
|
||||
def test_is_restrict_qualified(self):
|
||||
"""Ensure Type.is_restrict_qualified works."""
|
||||
|
||||
tu = get_tu('struct s { void * restrict i; void * j; };')
|
||||
|
||||
i = get_cursor(tu, 'i')
|
||||
j = get_cursor(tu, 'j')
|
||||
|
||||
self.assertIsNotNone(i)
|
||||
self.assertIsNotNone(j)
|
||||
|
||||
self.assertIsInstance(i.type.is_restrict_qualified(), bool)
|
||||
self.assertTrue(i.type.is_restrict_qualified())
|
||||
self.assertFalse(j.type.is_restrict_qualified())
|
||||
|
||||
def test_record_layout(self):
|
||||
"""Ensure Cursor.type.get_size, Cursor.type.get_align and
|
||||
Cursor.type.get_offset works."""
|
||||
|
||||
source ="""
|
||||
struct a {
|
||||
long a1;
|
||||
long a2:3;
|
||||
long a3:4;
|
||||
long long a4;
|
||||
};
|
||||
"""
|
||||
tries=[(['-target','i386-linux-gnu'],(4,16,0,32,35,64)),
|
||||
(['-target','nvptx64-unknown-unknown'],(8,24,0,64,67,128)),
|
||||
(['-target','i386-pc-win32'],(8,16,0,32,35,64)),
|
||||
(['-target','msp430-none-none'],(2,14,0,32,35,48))]
|
||||
for flags, values in tries:
|
||||
align,total,a1,a2,a3,a4 = values
|
||||
|
||||
tu = get_tu(source, flags=flags)
|
||||
teststruct = get_cursor(tu, 'a')
|
||||
fields = list(teststruct.get_children())
|
||||
|
||||
self.assertEqual(teststruct.type.get_align(), align)
|
||||
self.assertEqual(teststruct.type.get_size(), total)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[0].spelling), a1)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[1].spelling), a2)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[2].spelling), a3)
|
||||
self.assertEqual(teststruct.type.get_offset(fields[3].spelling), a4)
|
||||
self.assertEqual(fields[0].is_bitfield(), False)
|
||||
self.assertEqual(fields[1].is_bitfield(), True)
|
||||
self.assertEqual(fields[1].get_bitfield_width(), 3)
|
||||
self.assertEqual(fields[2].is_bitfield(), True)
|
||||
self.assertEqual(fields[2].get_bitfield_width(), 4)
|
||||
self.assertEqual(fields[3].is_bitfield(), False)
|
||||
|
||||
def test_offset(self):
|
||||
"""Ensure Cursor.get_record_field_offset works in anonymous records"""
|
||||
source="""
|
||||
struct Test {
|
||||
struct {int a;} typeanon;
|
||||
struct {
|
||||
int bariton;
|
||||
union {
|
||||
int foo;
|
||||
};
|
||||
};
|
||||
int bar;
|
||||
};"""
|
||||
tries=[(['-target','i386-linux-gnu'],(4,16,0,32,64,96)),
|
||||
(['-target','nvptx64-unknown-unknown'],(8,24,0,32,64,96)),
|
||||
(['-target','i386-pc-win32'],(8,16,0,32,64,96)),
|
||||
(['-target','msp430-none-none'],(2,14,0,32,64,96))]
|
||||
for flags, values in tries:
|
||||
align,total,f1,bariton,foo,bar = values
|
||||
tu = get_tu(source)
|
||||
teststruct = get_cursor(tu, 'Test')
|
||||
children = list(teststruct.get_children())
|
||||
fields = list(teststruct.type.get_fields())
|
||||
self.assertEqual(children[0].kind, CursorKind.STRUCT_DECL)
|
||||
self.assertNotEqual(children[0].spelling, "typeanon")
|
||||
self.assertEqual(children[1].spelling, "typeanon")
|
||||
self.assertEqual(fields[0].kind, CursorKind.FIELD_DECL)
|
||||
self.assertEqual(fields[1].kind, CursorKind.FIELD_DECL)
|
||||
self.assertTrue(fields[1].is_anonymous())
|
||||
self.assertEqual(teststruct.type.get_offset("typeanon"), f1)
|
||||
self.assertEqual(teststruct.type.get_offset("bariton"), bariton)
|
||||
self.assertEqual(teststruct.type.get_offset("foo"), foo)
|
||||
self.assertEqual(teststruct.type.get_offset("bar"), bar)
|
||||
|
||||
def test_decay(self):
|
||||
"""Ensure decayed types are handled as the original type"""
|
||||
|
||||
tu = get_tu("void foo(int a[]);")
|
||||
foo = get_cursor(tu, 'foo')
|
||||
a = foo.type.argument_types()[0]
|
||||
|
||||
self.assertEqual(a.kind, TypeKind.INCOMPLETEARRAY)
|
||||
self.assertEqual(a.element_type.kind, TypeKind.INT)
|
||||
self.assertEqual(a.get_canonical().kind, TypeKind.INCOMPLETEARRAY)
|
||||
|
||||
def test_addrspace(self):
|
||||
"""Ensure the address space can be queried"""
|
||||
tu = get_tu('__attribute__((address_space(2))) int testInteger = 3;', 'c')
|
||||
|
||||
testInteger = get_cursor(tu, 'testInteger')
|
||||
|
||||
self.assertIsNotNone(testInteger, "Could not find testInteger.")
|
||||
self.assertEqual(testInteger.type.get_address_space(), 2)
|
||||
|
52
cmake/caches/Android-stage2.cmake
Normal file
52
cmake/caches/Android-stage2.cmake
Normal file
@ -0,0 +1,52 @@
|
||||
set(LLVM_TARGETS_TO_BUILD X86;ARM;AArch64 CACHE STRING "")
|
||||
|
||||
set(CLANG_VENDOR Android CACHE STRING "")
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
set(LLVM_ENABLE_THREADS OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(LLVM_LIBDIR_SUFFIX 64 CACHE STRING "")
|
||||
set(LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
|
||||
set(ANDROID_RUNTIMES_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(ANDROID_RUNTIMES_BUILD_TYPE Release CACHE STRING "")
|
||||
set(ANDROID_BUILTINS_BUILD_TYPE Release CACHE STRING "")
|
||||
|
||||
set(LLVM_BUILTIN_TARGETS "i686-linux-android;x86_64-linux-android;aarch64-linux-android;armv7-linux-android" CACHE STRING "")
|
||||
foreach(target i686;x86_64;aarch64;armv7)
|
||||
set(BUILTINS_${target}-linux-android_ANDROID 1 CACHE STRING "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_BUILD_TYPE ${ANDROID_BUILTINS_BUILD_TYPE} CACHE STRING "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_ASM_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_C_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_SYSROOT ${ANDROID_${target}_SYSROOT} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_EXE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_SHARED_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(BUILTINS_${target}-linux-android_CMAKE_MOUDLE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
endforeach()
|
||||
|
||||
|
||||
set(LLVM_RUNTIME_TARGETS "i686-linux-android;x86_64-linux-android;aarch64-linux-android;armv7-linux-android" CACHE STRING "")
|
||||
foreach(target i686;x86_64;aarch64;armv7)
|
||||
set(RUNTIMES_${target}-linux-android_ANDROID 1 CACHE STRING "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_ASM_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_BUILD_TYPE ${ANDROID_RUNTIMES_BUILD_TYPE} CACHE STRING "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_C_FLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_CXX_FLAGS ${ANDROID_${target}_CXX_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_SYSROOT ${ANDROID_${target}_SYSROOT} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_EXE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_SHARED_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_CMAKE_MODULE_LINKER_FLAGS ${ANDROID_${target}_LINKER_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_COMPILER_RT_ENABLE_WERROR ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_COMPILER_RT_TEST_COMPILER_CFLAGS ${ANDROID_${target}_C_FLAGS} CACHE PATH "")
|
||||
set(RUNTIMES_${target}-linux-android_COMPILER_RT_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_ENABLE_ASSERTIONS ${ANDROID_RUNTIMES_ENABLE_ASSERTIONS} CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_ENABLE_THREADS OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBUNWIND_HAS_NO_EXCEPTIONS_FLAG ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-linux-android_LIBUNWIND_HAS_FUNWIND_TABLES ON CACHE BOOL "")
|
||||
endforeach()
|
||||
|
||||
set(RUNTIMES_armv7-linux-android_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
|
43
cmake/caches/Android.cmake
Normal file
43
cmake/caches/Android.cmake
Normal file
@ -0,0 +1,43 @@
|
||||
# This file sets up a CMakeCache for an Android toolchain build.
|
||||
|
||||
set(LLVM_TARGETS_TO_BUILD X86 CACHE STRING "")
|
||||
|
||||
set(CLANG_ENABLE_ARCMT OFF CACHE BOOL "")
|
||||
set(CLANG_ENABLE_STATIC_ANALYZER OFF CACHE BOOL "")
|
||||
set(CLANG_VENDOR Android CACHE STRING "")
|
||||
|
||||
set(CMAKE_BUILD_TYPE RELEASE CACHE STRING "")
|
||||
|
||||
set(HAVE_LIBCXXABI ON CACHE BOOL "")
|
||||
set(LLVM_BUILD_TOOLS OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ASSERTIONS ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_THREADS OFF CACHE BOOL "")
|
||||
set(LLVM_LIBDIR_SUFFIX 64 CACHE STRING "")
|
||||
set(LLVM_TOOL_CLANG_TOOLS_EXTRA_BUILD OFF CACHE BOOL "")
|
||||
set(LLVM_TOOL_OPENMP_BUILD OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
|
||||
if (LIBCXX_ENABLE_ABI_LINKER_SCRIPT)
|
||||
list(APPEND EXTRA_ARGS -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT=${LIBCXX_ENABLE_ABI_LINKER_SCRIPT})
|
||||
endif()
|
||||
|
||||
if (LIBCXX_ENABLE_STATIC_ABI_LIBRARY)
|
||||
list(APPEND EXTRA_ARGS -DLIBCXX_ENABLE_STATIC_ABI_LIBRARY=${LIBCXX_ENABLE_STATIC_ABI_LIBRARY})
|
||||
endif()
|
||||
|
||||
if (LLVM_BUILD_EXTERNAL_COMPILER_RT)
|
||||
set(APPEND EXTRA_ARGS -DLLVM_BUILD_EXTERNAL_COMPILER_RT=${LLVM_BUILD_EXTERNAL_COMPILER_RT})
|
||||
endif()
|
||||
|
||||
get_cmake_property(variableNames VARIABLES)
|
||||
foreach(variableName ${variableNames})
|
||||
if(variableName MATCHES "^STAGE2_")
|
||||
string(REPLACE "STAGE2_" "" new_name ${variableName})
|
||||
list(APPEND EXTRA_ARGS "-D${new_name}=${${variableName}}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(CLANG_ENABLE_BOOTSTRAP ON CACHE BOOL "")
|
||||
set(CLANG_BOOTSTRAP_CMAKE_ARGS
|
||||
${EXTRA_ARGS}
|
||||
-C${CMAKE_CURRENT_LIST_DIR}/Android-stage2.cmake CACHE STRING "")
|
@ -24,6 +24,12 @@ set(CLANG_BOOTSTRAP_PASSTHROUGH
|
||||
CMAKE_OSX_ARCHITECTURES
|
||||
CACHE STRING "")
|
||||
|
||||
# Disabling embedded darwin compiler-rt on stage1 builds is required because we
|
||||
# don't build stage1 to support arm code generation.
|
||||
set(COMPILER_RT_ENABLE_IOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_WATCHOS OFF CACHE BOOL "")
|
||||
set(COMPILER_RT_ENABLE_TVOS OFF CACHE BOOL "")
|
||||
|
||||
set(BOOTSTRAP_LLVM_ENABLE_LTO ON CACHE BOOL "")
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
|
||||
@ -39,6 +45,8 @@ set(CLANG_BOOTSTRAP_TARGETS
|
||||
clang-test-depends
|
||||
distribution
|
||||
install-distribution
|
||||
install-xcode-toolchain
|
||||
install-distribution-toolchain
|
||||
clang CACHE STRING "")
|
||||
|
||||
#bootstrap
|
||||
|
@ -40,6 +40,10 @@ set(LIBCXX_INSTALL_HEADERS ON CACHE BOOL "")
|
||||
set(LIBCXX_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(LLVM_LTO_VERSION_OFFSET 3000 CACHE STRING "")
|
||||
|
||||
# Generating Xcode toolchains is useful for developers wanting to build and use
|
||||
# clang without installing over existing tools.
|
||||
set(LLVM_CREATE_XCODE_TOOLCHAIN ON CACHE BOOL "")
|
||||
|
||||
# setup toolchain
|
||||
set(LLVM_INSTALL_TOOLCHAIN_ONLY ON CACHE BOOL "")
|
||||
set(LLVM_TOOLCHAIN_TOOLS
|
||||
@ -57,7 +61,7 @@ set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
LTO
|
||||
clang-format
|
||||
clang-headers
|
||||
libcxx-headers
|
||||
cxx-headers
|
||||
${LLVM_TOOLCHAIN_TOOLS}
|
||||
CACHE STRING "")
|
||||
|
||||
|
@ -7,8 +7,9 @@ set(PACKAGE_VENDOR Fuchsia CACHE STRING "")
|
||||
|
||||
set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
|
||||
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB ON CACHE BOOL "")
|
||||
set(LLVM_EXTERNALIZE_DEBUGINFO ON CACHE BOOL "")
|
||||
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
|
||||
|
||||
@ -23,19 +24,16 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -gline-tables-only -DNDEBUG" CACHE STRING "")
|
||||
|
||||
set(LLVM_BUILTIN_TARGETS "x86_64-fuchsia;aarch64-fuchsia" CACHE STRING "")
|
||||
set(LLVM_BUILTIN_TARGETS "default;x86_64-fuchsia;aarch64-fuchsia" CACHE STRING "")
|
||||
foreach(target x86_64;aarch64)
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_SYSROOT ${FUCHSIA_${target}_SYSROOT} CACHE PATH "")
|
||||
set(BUILTINS_${target}-fuchsia_CMAKE_SYSTEM_NAME Fuchsia CACHE STRING "")
|
||||
endforeach()
|
||||
if(NOT APPLE)
|
||||
list(APPEND LLVM_BUILTIN_TARGETS "default")
|
||||
endif()
|
||||
|
||||
set(LLVM_RUNTIME_TARGETS "default;x86_64-fuchsia;aarch64-fuchsia" CACHE STRING "")
|
||||
set(LLVM_RUNTIME_TARGETS "default;x86_64-fuchsia;aarch64-fuchsia;x86_64-fuchsia-asan:x86_64-fuchsia;aarch64-fuchsia-asan:aarch64-fuchsia" CACHE STRING "")
|
||||
foreach(target x86_64;aarch64)
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_CMAKE_SYSROOT ${FUCHSIA_${target}_SYSROOT} CACHE PATH "")
|
||||
@ -43,9 +41,19 @@ foreach(target x86_64;aarch64)
|
||||
set(RUNTIMES_${target}-fuchsia_UNIX 1 CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LLVM_ENABLE_LIBCXX ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBUNWIND_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBUNWIND_ENABLE_STATIC OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_USE_LLVM_UNWINDER ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXXABI_ENABLE_STATIC OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_ABI_VERSION 2 CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia_LIBCXX_ENABLE_STATIC OFF CACHE BOOL "")
|
||||
set(RUNTIMES_${target}-fuchsia_SANITIZER_USE_COMPILER_RT ON CACHE BOOL "")
|
||||
|
||||
set(RUNTIMES_${target}-fuchsia-asan_LLVM_USE_SANITIZER Address CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia-asan_LLVM_RUNTIMES_PREFIX "${target}-fuchsia/" CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia-asan_LLVM_RUNTIMES_LIBDIR_SUFFIX "/asan" CACHE STRING "")
|
||||
set(RUNTIMES_${target}-fuchsia-asan_LIBCXX_INSTALL_HEADERS OFF CACHE BOOL "")
|
||||
endforeach()
|
||||
|
||||
# Setup toolchain.
|
||||
@ -59,6 +67,7 @@ set(LLVM_TOOLCHAIN_TOOLS
|
||||
llvm-dsymutil
|
||||
llvm-lib
|
||||
llvm-nm
|
||||
llvm-objcopy
|
||||
llvm-objdump
|
||||
llvm-profdata
|
||||
llvm-ranlib
|
||||
@ -67,6 +76,7 @@ set(LLVM_TOOLCHAIN_TOOLS
|
||||
llvm-size
|
||||
llvm-symbolizer
|
||||
opt
|
||||
sancov
|
||||
CACHE STRING "")
|
||||
|
||||
set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
@ -77,6 +87,7 @@ set(LLVM_DISTRIBUTION_COMPONENTS
|
||||
LTO
|
||||
clang-format
|
||||
clang-headers
|
||||
clang-refactor
|
||||
clang-tidy
|
||||
clangd
|
||||
builtins
|
||||
|
@ -8,8 +8,9 @@ set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
|
||||
set(CLANG_INCLUDE_TESTS OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_BACKTRACES OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_TERMINFO OFF CACHE BOOL "")
|
||||
set(LLVM_ENABLE_ZLIB OFF CACHE BOOL "")
|
||||
set(CLANG_PLUGIN_SUPPORT OFF CACHE BOOL "")
|
||||
|
||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "")
|
||||
|
@ -104,11 +104,9 @@ macro(add_clang_library name)
|
||||
RUNTIME DESTINATION bin)
|
||||
|
||||
if (${ARG_SHARED} AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_target(install-${name}
|
||||
DEPENDS ${name}
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DCMAKE_INSTALL_COMPONENT=${name}
|
||||
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
|
||||
add_llvm_install_targets(install-${name}
|
||||
DEPENDS ${name}
|
||||
COMPONENT ${name})
|
||||
endif()
|
||||
endif()
|
||||
set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name})
|
||||
@ -147,11 +145,9 @@ macro(add_clang_tool name)
|
||||
COMPONENT ${name})
|
||||
|
||||
if(NOT CMAKE_CONFIGURATION_TYPES)
|
||||
add_custom_target(install-${name}
|
||||
DEPENDS ${name}
|
||||
COMMAND "${CMAKE_COMMAND}"
|
||||
-DCMAKE_INSTALL_COMPONENT=${name}
|
||||
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
|
||||
add_llvm_install_targets(install-${name}
|
||||
DEPENDS ${name}
|
||||
COMPONENT ${name})
|
||||
endif()
|
||||
set_property(GLOBAL APPEND PROPERTY CLANG_EXPORTS ${name})
|
||||
endif()
|
||||
|
@ -11,3 +11,10 @@ set(CLANG_INCLUDE_DIRS "@CLANG_CONFIG_INCLUDE_DIRS@")
|
||||
|
||||
# Provide all our library targets to users.
|
||||
include("@CLANG_CONFIG_EXPORTS_FILE@")
|
||||
|
||||
# By creating clang-tablegen-targets here, subprojects that depend on Clang's
|
||||
# tablegen-generated headers can always depend on this target whether building
|
||||
# in-tree with Clang or not.
|
||||
if(NOT TARGET clang-tablegen-targets)
|
||||
add_custom_target(clang-tablegen-targets)
|
||||
endif()
|
||||
|
19
cmake/modules/ProtobufMutator.cmake
Normal file
19
cmake/modules/ProtobufMutator.cmake
Normal file
@ -0,0 +1,19 @@
|
||||
set(PBM_PREFIX protobuf_mutator)
|
||||
set(PBM_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PBM_PREFIX}/src/${PBM_PREFIX})
|
||||
set(PBM_LIB_PATH ${PBM_PATH}-build/src/libprotobuf-mutator.a)
|
||||
set(PBM_FUZZ_LIB_PATH ${PBM_PATH}-build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a)
|
||||
|
||||
ExternalProject_Add(${PBM_PREFIX}
|
||||
PREFIX ${PBM_PREFIX}
|
||||
GIT_REPOSITORY https://github.com/google/libprotobuf-mutator.git
|
||||
GIT_TAG master
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
|
||||
CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
|
||||
BUILD_BYPRODUCTS ${PBM_LIB_PATH} ${PBM_FUZZ_LIB_PATH}
|
||||
UPDATE_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
set(ProtobufMutator_INCLUDE_DIRS ${PBM_PATH})
|
||||
set(ProtobufMutator_LIBRARIES ${PBM_FUZZ_LIB_PATH} ${PBM_LIB_PATH})
|
@ -140,7 +140,8 @@ Memory leak detection
|
||||
---------------------
|
||||
|
||||
For more information on leak detector in AddressSanitizer, see
|
||||
:doc:`LeakSanitizer`. The leak detection is turned on by default on Linux;
|
||||
:doc:`LeakSanitizer`. The leak detection is turned on by default on Linux,
|
||||
and can be enabled using ``ASAN_OPTIONS=detect_leaks=1`` on OS X;
|
||||
however, it is not yet supported on other platforms.
|
||||
|
||||
Issue Suppression
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -56,6 +56,16 @@ Pass <arg> to fatbinary invocation
|
||||
|
||||
Pass <arg> to the ptxas assembler
|
||||
|
||||
.. option:: -Xopenmp-target <arg>
|
||||
|
||||
Pass <arg> to the target offloading toolchain.
|
||||
|
||||
.. program:: clang1
|
||||
.. option:: -Xopenmp-target=<arg> <arg2>
|
||||
.. program:: clang
|
||||
|
||||
Pass <arg> to the specified target offloading toolchain. The triple that identifies the toolchain must be provided after the equals sign.
|
||||
|
||||
.. option:: -Z<arg>
|
||||
|
||||
.. option:: -a<arg>, --profile-blocks
|
||||
@ -312,6 +322,10 @@ Disable standard #include directories for the C++ standard library
|
||||
|
||||
.. option:: -nostdlib, --no-standard-libraries
|
||||
|
||||
.. program:: clang1
|
||||
.. option:: -nostdlib++
|
||||
.. program:: clang
|
||||
|
||||
.. option:: -nostdlibinc
|
||||
|
||||
.. option:: -o<file>, --output <arg>, --output=<arg>
|
||||
@ -510,10 +524,10 @@ Serialize compiler diagnostics to a file
|
||||
|
||||
.. option:: -shared, --shared
|
||||
|
||||
.. option:: -shared-libasan
|
||||
|
||||
.. option:: -shared-libgcc
|
||||
|
||||
.. option:: -shared-libsan, -shared-libasan
|
||||
|
||||
.. option:: -single\_module
|
||||
|
||||
.. option:: -specs=<arg>, --specs=<arg>
|
||||
@ -522,6 +536,8 @@ Serialize compiler diagnostics to a file
|
||||
|
||||
.. option:: -static-libgcc
|
||||
|
||||
.. option:: -static-libsan
|
||||
|
||||
.. option:: -static-libstdc++
|
||||
|
||||
.. option:: -std-default=<arg>
|
||||
@ -574,6 +590,8 @@ Verify the binary representation of debug output
|
||||
|
||||
.. option:: --version
|
||||
|
||||
Print version information
|
||||
|
||||
.. option:: -w, --no-warnings
|
||||
|
||||
Suppress all warnings
|
||||
@ -688,6 +706,10 @@ Print source range spans in numeric form
|
||||
|
||||
Enables an experimental new pass manager in LLVM.
|
||||
|
||||
.. option:: -ffine-grained-bitfield-accesses, -fno-fine-grained-bitfield-accesses
|
||||
|
||||
Use separate accesses for bitfields with legal widths and alignments.
|
||||
|
||||
.. option:: -finline-functions, -fno-inline-functions
|
||||
|
||||
Inline suitable functions
|
||||
@ -726,6 +748,10 @@ Path to blacklist file for sanitizers
|
||||
|
||||
Enable control flow integrity (CFI) checks for cross-DSO calls.
|
||||
|
||||
.. option:: -fsanitize-cfi-icall-generalize-pointers
|
||||
|
||||
Generalize pointers in CFI indirect call type signature checks
|
||||
|
||||
.. option:: -fsanitize-coverage=<arg1>,<arg2>..., -fno-sanitize-coverage=<arg1>,<arg2>...
|
||||
|
||||
Specify the type of coverage instrumentation for Sanitizers
|
||||
@ -742,10 +768,12 @@ Enable origins tracking in MemorySanitizer
|
||||
|
||||
Enable origins tracking in MemorySanitizer
|
||||
|
||||
.. option:: -fsanitize-memory-use-after-dtor
|
||||
.. option:: -fsanitize-memory-use-after-dtor, -fno-sanitize-memory-use-after-dtor
|
||||
|
||||
Enable use-after-destroy detection in MemorySanitizer
|
||||
|
||||
.. option:: -fsanitize-minimal-runtime, -fno-sanitize-minimal-runtime
|
||||
|
||||
.. option:: -fsanitize-recover, -fno-sanitize-recover
|
||||
|
||||
.. program:: clang1
|
||||
@ -856,6 +884,10 @@ Use the last modification time of <file> as the build session timestamp
|
||||
|
||||
Time when the current build session started
|
||||
|
||||
.. option:: -fmodule-file=\[<name>=\]<file>
|
||||
|
||||
Specify the mapping of module name to precompiled module file, or load a module file if name is omitted.
|
||||
|
||||
.. option:: -fmodules-cache-path=<directory>
|
||||
|
||||
Specify the module cache path
|
||||
@ -1243,8 +1275,16 @@ Print a template comparison tree for differing templates
|
||||
|
||||
Allow '$' in identifiers
|
||||
|
||||
.. option:: -fdouble-square-bracket-attributes, -fno-double-square-bracket-attributes
|
||||
|
||||
Enable '\[\[\]\]' attributes in all C and C++ language modes
|
||||
|
||||
.. option:: -fdwarf-directory-asm, -fno-dwarf-directory-asm
|
||||
|
||||
.. option:: -fdwarf-exceptions
|
||||
|
||||
Use DWARF style exceptions
|
||||
|
||||
.. option:: -felide-constructors, -fno-elide-constructors
|
||||
|
||||
.. option:: -feliminate-unused-debug-symbols, -fno-eliminate-unused-debug-symbols
|
||||
@ -1321,10 +1361,18 @@ Implicitly search the file system for module map files.
|
||||
|
||||
.. option:: -finput-charset=<arg>
|
||||
|
||||
.. option:: -finstrument-function-entry-bare
|
||||
|
||||
Instrument function entry only, after inlining, without arguments to the instrumentation call
|
||||
|
||||
.. option:: -finstrument-functions
|
||||
|
||||
Generate calls to instrument function entry and exit
|
||||
|
||||
.. option:: -finstrument-functions-after-inlining
|
||||
|
||||
Like -finstrument-functions, but insert the calls after inlining
|
||||
|
||||
.. option:: -fintegrated-as, -fno-integrated-as, -integrated-as
|
||||
|
||||
Enable the integrated assembler
|
||||
@ -1365,10 +1413,6 @@ Specify the maximum alignment to enforce on pointers lacking an explicit alignme
|
||||
|
||||
.. option:: -fmodule-file-deps, -fno-module-file-deps
|
||||
|
||||
.. option:: -fmodule-file=<file>
|
||||
|
||||
Load this precompiled module file
|
||||
|
||||
.. option:: -fmodule-map-file=<file>
|
||||
|
||||
Load this module map file
|
||||
@ -1451,6 +1495,10 @@ Do not treat C++ operator name keywords as synonyms for operators
|
||||
|
||||
.. option:: -fno-working-directory
|
||||
|
||||
.. option:: -fnoopenmp-relocatable-target
|
||||
|
||||
Do not compile OpenMP target code as relocatable.
|
||||
|
||||
.. option:: -fnoopenmp-use-tls
|
||||
|
||||
.. option:: -fobjc-abi-version=<arg>
|
||||
@ -1493,6 +1541,10 @@ Enable ARC-style weak references in Objective-C
|
||||
|
||||
.. option:: -fopenmp-dump-offload-linker-script
|
||||
|
||||
.. option:: -fopenmp-relocatable-target
|
||||
|
||||
OpenMP target code is compiled as relocatable using the -c flag. For OpenMP targets the code is relocatable by default.
|
||||
|
||||
.. option:: -fopenmp-use-tls
|
||||
|
||||
.. option:: -fopenmp-version=<arg>
|
||||
@ -1533,6 +1585,10 @@ Override the default ABI to return all structs on the stack
|
||||
|
||||
.. option:: -fpie, -fno-pie
|
||||
|
||||
.. option:: -fplt, -fno-plt
|
||||
|
||||
Use the PLT to make function calls
|
||||
|
||||
.. option:: -fplugin=<dsopath>
|
||||
|
||||
Load the named plugin (dynamic shared object)
|
||||
@ -1571,6 +1627,13 @@ Generate instrumented code to collect execution counts into <file> (overridden b
|
||||
|
||||
Use instrumentation data for profile-guided optimization
|
||||
|
||||
.. option:: -fprofile-sample-accurate, -fauto-profile-accurate, -fno-profile-sample-accurate
|
||||
|
||||
Specifies that the sample profile is accurate. If the sample
|
||||
profile is accurate, callsites without profile samples are marked
|
||||
as cold. Otherwise, treat callsites without profile samples as if
|
||||
we have no profile
|
||||
|
||||
.. option:: -fprofile-sample-use, -fauto-profile, -fno-profile-sample-use
|
||||
|
||||
.. program:: clang1
|
||||
@ -1623,6 +1686,10 @@ Turn on loop reroller
|
||||
|
||||
Generate a YAML optimization record file
|
||||
|
||||
.. option:: -fseh-exceptions
|
||||
|
||||
Use SEH style exceptions
|
||||
|
||||
.. option:: -fshort-enums, -fno-short-enums
|
||||
|
||||
Allocate to an enum type only as many bytes as it needs for the declared range of possible values
|
||||
@ -1807,6 +1874,10 @@ Treat signed integer overflow as two's complement
|
||||
|
||||
Store string literals as writable data
|
||||
|
||||
.. option:: -fxray-always-emit-customevents, -fno-xray-always-emit-customevents
|
||||
|
||||
Determine whether to always emit \_\_xray\_customevent(...) calls even if the function it appears in is not always instrumented.
|
||||
|
||||
.. option:: -fxray-always-instrument=<arg>
|
||||
|
||||
Filename defining the whitelist for imbuing the 'always instrument' XRay attribute.
|
||||
@ -1905,6 +1976,8 @@ Put objects of at most <size> bytes into small data section (MIPS / Hexagon)
|
||||
|
||||
Enable SVR4-style position-independent code (Mips only)
|
||||
|
||||
.. option:: -mabs=<arg>
|
||||
|
||||
.. option:: -malign-double
|
||||
|
||||
Align doubles to two words in structs (x86 only)
|
||||
@ -1943,6 +2016,14 @@ Link stack frames through backchain on System Z
|
||||
|
||||
Set EABI type, e.g. 4, 5 or gnu (default depends on triple)
|
||||
|
||||
.. option:: -membedded-data, -mno-embedded-data
|
||||
|
||||
Place constants in the .rodata section instead of the .sdata section even if they meet the -G <size> threshold (MIPS)
|
||||
|
||||
.. option:: -mextern-sdata, -mno-extern-sdata
|
||||
|
||||
Assume that externally defined data is in the small data if it meets the -G <size> threshold (MIPS)
|
||||
|
||||
.. option:: -mfentry
|
||||
|
||||
Insert calls to fentry at function entry (x86 only)
|
||||
@ -1965,6 +2046,10 @@ Use 64-bit floating point registers (MIPS only)
|
||||
|
||||
Enable merging of globals
|
||||
|
||||
.. option:: -mgpopt, -mno-gpopt
|
||||
|
||||
Use GP relative accesses for symbols known to be in a small data section (MIPS)
|
||||
|
||||
.. option:: -mhard-float
|
||||
|
||||
.. option:: -mhwdiv=<arg>, --mhwdiv <arg>, --mhwdiv=<arg>
|
||||
@ -1989,6 +2074,10 @@ Use Intel MCU ABI
|
||||
|
||||
.. option:: -mldc1-sdc1, -mno-ldc1-sdc1
|
||||
|
||||
.. option:: -mlocal-sdata, -mno-local-sdata
|
||||
|
||||
Extend the -G behaviour to object local data (MIPS)
|
||||
|
||||
.. option:: -mlong-calls, -mno-long-calls
|
||||
|
||||
Generate branches with extended addressability, usually via indirect jumps.
|
||||
@ -2031,6 +2120,10 @@ Omit frame pointer setup for leaf functions
|
||||
|
||||
Use copy relocations support for PIE builds
|
||||
|
||||
.. option:: -mprefer-vector-width=<arg>
|
||||
|
||||
Specifies preferred vector width for auto-vectorization. Defaults to 'none' which allows target specific decisions.
|
||||
|
||||
.. option:: -mqdsp6-compat
|
||||
|
||||
Enable hexagon-qdsp6 backward compatibility
|
||||
@ -2113,6 +2206,10 @@ Generate code which only uses the general purpose registers (AArch64 only)
|
||||
|
||||
AMDGPU
|
||||
------
|
||||
.. option:: -mxnack, -mno-xnack
|
||||
|
||||
Enable XNACK (AMDGPU only)
|
||||
|
||||
ARM
|
||||
---
|
||||
.. option:: -ffixed-r9
|
||||
@ -2143,10 +2240,18 @@ Disallow use of CRC instructions (ARM only)
|
||||
|
||||
Disallow generation of deprecated IT blocks for ARMv8. It is on by default for ARMv8 Thumb mode.
|
||||
|
||||
.. option:: -mtp=<arg>
|
||||
|
||||
Read thread pointer from coprocessor register (ARM only)
|
||||
|
||||
.. option:: -munaligned-access, -mno-unaligned-access
|
||||
|
||||
Allow memory accesses to be unaligned (AArch32/AArch64 only)
|
||||
|
||||
Hexagon
|
||||
-------
|
||||
.. option:: -mieee-rnd-near
|
||||
|
||||
Hexagon
|
||||
-------
|
||||
.. option:: -mhvx, -mno-hvx
|
||||
@ -2157,7 +2262,15 @@ Enable Hexagon Vector eXtensions
|
||||
|
||||
Enable Hexagon Double Vector eXtensions
|
||||
|
||||
.. option:: -mieee-rnd-near
|
||||
.. option:: -mhvx-length=<arg>
|
||||
|
||||
Set Hexagon Vector Length
|
||||
|
||||
.. program:: clang1
|
||||
.. option:: -mhvx=<arg>
|
||||
.. program:: clang
|
||||
|
||||
Enable Hexagon Vector eXtensions
|
||||
|
||||
PowerPC
|
||||
-------
|
||||
@ -2197,6 +2310,8 @@ PowerPC
|
||||
|
||||
WebAssembly
|
||||
-----------
|
||||
.. option:: -mnontrapping-fptoint, -mno-nontrapping-fptoint
|
||||
|
||||
.. option:: -msimd128, -mno-simd128
|
||||
|
||||
X86
|
||||
@ -2255,6 +2370,8 @@ X86
|
||||
|
||||
.. option:: -mfxsr, -mno-fxsr
|
||||
|
||||
.. option:: -mibt, -mno-ibt
|
||||
|
||||
.. option:: -mlwp, -mno-lwp
|
||||
|
||||
.. option:: -mlzcnt, -mno-lzcnt
|
||||
@ -2287,6 +2404,8 @@ X86
|
||||
|
||||
.. option:: -msha, -mno-sha
|
||||
|
||||
.. option:: -mshstk, -mno-shstk
|
||||
|
||||
.. option:: -msse, -mno-sse
|
||||
|
||||
.. option:: -msse2, -mno-sse2
|
||||
|
@ -11,7 +11,7 @@ Standalone Tool
|
||||
===============
|
||||
|
||||
:program:`clang-format` is located in `clang/tools/clang-format` and can be used
|
||||
to format C/C++/Obj-C code.
|
||||
to format C/C++/Java/JavaScript/Objective-C/Protobuf code.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@ -71,6 +71,7 @@ to format C/C++/Obj-C code.
|
||||
Use -style="{key: value, ...}" to set specific
|
||||
parameters, e.g.:
|
||||
-style="{BasedOnStyle: llvm, IndentWidth: 8}"
|
||||
-verbose - If set, shows the list of processed files
|
||||
|
||||
Generic Options:
|
||||
|
||||
|
@ -271,15 +271,22 @@ the configuration (without a prefix: ``Auto``).
|
||||
int b = 2; // comment b int b = 2; // comment about b
|
||||
|
||||
**AllowAllParametersOfDeclarationOnNextLine** (``bool``)
|
||||
Allow putting all parameters of a function declaration onto
|
||||
If the function declaration doesn't fit on a line,
|
||||
allow putting all parameters of a function declaration onto
|
||||
the next line even if ``BinPackParameters`` is ``false``.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
true: false:
|
||||
myFunction(foo, vs. myFunction(foo, bar, plop);
|
||||
bar,
|
||||
plop);
|
||||
true:
|
||||
void myFunction(
|
||||
int a, int b, int c, int d, int e);
|
||||
|
||||
false:
|
||||
void myFunction(int a,
|
||||
int b,
|
||||
int c,
|
||||
int d,
|
||||
int e);
|
||||
|
||||
**AllowShortBlocksOnASingleLine** (``bool``)
|
||||
Allows contracting simple braced statements to a single line.
|
||||
@ -533,6 +540,15 @@ the configuration (without a prefix: ``Auto``).
|
||||
If ``BreakBeforeBraces`` is set to ``BS_Custom``, use this to specify how
|
||||
each individual brace case should be handled. Otherwise, this is ignored.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Example of usage:
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterEnum: true
|
||||
AfterStruct: false
|
||||
SplitEmptyFunction: false
|
||||
|
||||
Nested configuration flags:
|
||||
|
||||
|
||||
@ -645,6 +661,21 @@ the configuration (without a prefix: ``Auto``).
|
||||
int x;
|
||||
}
|
||||
|
||||
* ``bool AfterExternBlock`` Wrap extern blocks.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
true:
|
||||
extern "C"
|
||||
{
|
||||
int foo();
|
||||
}
|
||||
|
||||
false:
|
||||
extern "C" {
|
||||
int foo();
|
||||
}
|
||||
|
||||
* ``bool BeforeCatch`` Wrap before ``catch``.
|
||||
|
||||
.. code-block:: c++
|
||||
@ -679,7 +710,7 @@ the configuration (without a prefix: ``Auto``).
|
||||
|
||||
* ``bool IndentBraces`` Indent the wrapped braces themselves.
|
||||
|
||||
* ``bool SplitEmptyFunctionBody`` If ``false``, empty function body can be put on a single line.
|
||||
* ``bool SplitEmptyFunction`` If ``false``, empty function body can be put on a single line.
|
||||
This option is used only if the opening brace of the function has
|
||||
already been wrapped, i.e. the `AfterFunction` brace wrapping mode is
|
||||
set, and the function could/should not be put on a single line (as per
|
||||
@ -691,6 +722,28 @@ the configuration (without a prefix: ``Auto``).
|
||||
{} {
|
||||
}
|
||||
|
||||
* ``bool SplitEmptyRecord`` If ``false``, empty record (e.g. class, struct or union) body
|
||||
can be put on a single line. This option is used only if the opening
|
||||
brace of the record has already been wrapped, i.e. the `AfterClass`
|
||||
(for classes) brace wrapping mode is set.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class Foo vs. class Foo
|
||||
{} {
|
||||
}
|
||||
|
||||
* ``bool SplitEmptyNamespace`` If ``false``, empty namespace body can be put on a single line.
|
||||
This option is used only if the opening brace of the namespace has
|
||||
already been wrapped, i.e. the `AfterNamespace` brace wrapping mode is
|
||||
set.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace Foo vs. namespace Foo
|
||||
{} {
|
||||
}
|
||||
|
||||
|
||||
**BreakAfterJavaFieldAnnotations** (``bool``)
|
||||
Break after each annotation on a field in Java files.
|
||||
@ -941,9 +994,9 @@ the configuration (without a prefix: ``Auto``).
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
Constructor()
|
||||
: initializer1(),
|
||||
initializer2()
|
||||
Constructor()
|
||||
: initializer1(),
|
||||
initializer2()
|
||||
|
||||
* ``BCIS_BeforeComma`` (in configuration: ``BeforeComma``)
|
||||
Break constructor initializers before the colon and commas, and align
|
||||
@ -951,18 +1004,18 @@ the configuration (without a prefix: ``Auto``).
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
Constructor()
|
||||
: initializer1()
|
||||
, initializer2()
|
||||
Constructor()
|
||||
: initializer1()
|
||||
, initializer2()
|
||||
|
||||
* ``BCIS_AfterColon`` (in configuration: ``AfterColon``)
|
||||
Break constructor initializers after the colon and commas.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
Constructor() :
|
||||
initializer1(),
|
||||
initializer2()
|
||||
Constructor() :
|
||||
initializer1(),
|
||||
initializer2()
|
||||
|
||||
|
||||
|
||||
@ -1120,6 +1173,45 @@ the configuration (without a prefix: ``Auto``).
|
||||
|
||||
For example: BOOST_FOREACH.
|
||||
|
||||
**IncludeBlocks** (``IncludeBlocksStyle``)
|
||||
Dependent on the value, multiple ``#include`` blocks can be sorted
|
||||
as one and divided based on category.
|
||||
|
||||
Possible values:
|
||||
|
||||
* ``IBS_Preserve`` (in configuration: ``Preserve``)
|
||||
Sort each ``#include`` block separately.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include "b.h" into #include "b.h"
|
||||
|
||||
#include <lib/main.h> #include "a.h"
|
||||
#include "a.h" #include <lib/main.h>
|
||||
|
||||
* ``IBS_Merge`` (in configuration: ``Merge``)
|
||||
Merge multiple ``#include`` blocks together and sort as one.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include "b.h" into #include "a.h"
|
||||
#include "b.h"
|
||||
#include <lib/main.h> #include <lib/main.h>
|
||||
#include "a.h"
|
||||
|
||||
* ``IBS_Regroup`` (in configuration: ``Regroup``)
|
||||
Merge multiple ``#include`` blocks together and sort as one.
|
||||
Then split into groups based on category priority. See ``IncludeCategories``.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#include "b.h" into #include "a.h"
|
||||
#include "b.h"
|
||||
#include <lib/main.h>
|
||||
#include "a.h" #include <lib/main.h>
|
||||
|
||||
|
||||
|
||||
**IncludeCategories** (``std::vector<IncludeCategory>``)
|
||||
Regular expressions denoting the different ``#include`` categories
|
||||
used for ordering ``#includes``.
|
||||
@ -1144,7 +1236,7 @@ the configuration (without a prefix: ``Auto``).
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|isl|json)/)'
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
@ -1179,6 +1271,35 @@ the configuration (without a prefix: ``Auto``).
|
||||
plop(); plop();
|
||||
} }
|
||||
|
||||
**IndentPPDirectives** (``PPDirectiveIndentStyle``)
|
||||
The preprocessor directive indenting style to use.
|
||||
|
||||
Possible values:
|
||||
|
||||
* ``PPDIS_None`` (in configuration: ``None``)
|
||||
Does not indent any directives.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#if FOO
|
||||
#if BAR
|
||||
#include <foo>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
* ``PPDIS_AfterHash`` (in configuration: ``AfterHash``)
|
||||
Indents directives after the hash.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
#if FOO
|
||||
# if BAR
|
||||
# include <foo>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
**IndentWidth** (``unsigned``)
|
||||
The number of columns to use for indentation.
|
||||
|
||||
@ -1291,6 +1412,10 @@ the configuration (without a prefix: ``Auto``).
|
||||
* ``LK_TableGen`` (in configuration: ``TableGen``)
|
||||
Should be used for TableGen code.
|
||||
|
||||
* ``LK_TextProto`` (in configuration: ``TextProto``)
|
||||
Should be used for Protocol Buffer messages in text format
|
||||
(https://developers.google.com/protocol-buffers/).
|
||||
|
||||
|
||||
|
||||
**MacroBlockBegin** (``std::string``)
|
||||
@ -1451,6 +1576,26 @@ the configuration (without a prefix: ``Auto``).
|
||||
|
||||
|
||||
|
||||
**RawStringFormats** (``std::vector<RawStringFormat>``)
|
||||
Raw string delimiters denoting that the raw string contents are
|
||||
code in a particular language and can be reformatted.
|
||||
|
||||
A raw string with a matching delimiter will be reformatted assuming the
|
||||
specified language based on a predefined style given by 'BasedOnStyle'.
|
||||
If 'BasedOnStyle' is not found, the formatting is based on llvm style.
|
||||
|
||||
To configure this in the .clang-format file, use:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
RawStringFormats:
|
||||
- Delimiter: 'pb'
|
||||
Language: TextProto
|
||||
BasedOnStyle: llvm
|
||||
- Delimiter: 'proto'
|
||||
Language: TextProto
|
||||
BasedOnStyle: google
|
||||
|
||||
**ReflowComments** (``bool``)
|
||||
If ``true``, clang-format will attempt to re-flow comments.
|
||||
|
||||
@ -1478,6 +1623,14 @@ the configuration (without a prefix: ``Auto``).
|
||||
**SortUsingDeclarations** (``bool``)
|
||||
If ``true``, clang-format will sort using declarations.
|
||||
|
||||
The order of using declarations is defined as follows:
|
||||
Split the strings by "::" and discard any initial empty strings. The last
|
||||
element of each list is a non-namespace name; all others are namespace
|
||||
names. Sort the lists of names lexicographically, where the sort order of
|
||||
individual names is that all non-namespace names come before all namespace
|
||||
names, and within those groups, names are in case-insensitive
|
||||
lexicographic order.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
false: true:
|
||||
|
@ -215,6 +215,23 @@ shared library boundaries are handled as if the callee was not compiled with
|
||||
|
||||
This scheme is currently only supported on the x86 and x86_64 architectures.
|
||||
|
||||
``-fsanitize-cfi-icall-generalize-pointers``
|
||||
--------------------------------------------
|
||||
|
||||
Mismatched pointer types are a common cause of cfi-icall check failures.
|
||||
Translation units compiled with the ``-fsanitize-cfi-icall-generalize-pointers``
|
||||
flag relax pointer type checking for call sites in that translation unit,
|
||||
applied across all functions compiled with ``-fsanitize=cfi-icall``.
|
||||
|
||||
Specifically, pointers in return and argument types are treated as equivalent as
|
||||
long as the qualifiers for the type they point to match. For example, ``char*``
|
||||
``char**`, and ``int*`` are considered equivalent types. However, ``char*`` and
|
||||
``const char*`` are considered separate types.
|
||||
|
||||
``-fsanitize-cfi-icall-generalize-pointers`` is not compatible with
|
||||
``-fsanitize-cfi-cross-dso``.
|
||||
|
||||
|
||||
``-fsanitize=cfi-icall`` and ``-fsanitize=function``
|
||||
----------------------------------------------------
|
||||
|
||||
@ -243,17 +260,25 @@ Blacklist
|
||||
|
||||
A :doc:`SanitizerSpecialCaseList` can be used to relax CFI checks for certain
|
||||
source files, functions and types using the ``src``, ``fun`` and ``type``
|
||||
entity types.
|
||||
entity types. Specific CFI modes can be be specified using ``[section]``
|
||||
headers.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# Suppress checking for code in a file.
|
||||
# Suppress all CFI checking for code in a file.
|
||||
src:bad_file.cpp
|
||||
src:bad_header.h
|
||||
# Ignore all functions with names containing MyFooBar.
|
||||
fun:*MyFooBar*
|
||||
# Ignore all types in the standard library.
|
||||
type:std::*
|
||||
# Disable only unrelated cast checks for this function
|
||||
[cfi-unrelated-cast]
|
||||
fun:*UnrelatedCast*
|
||||
# Disable CFI call checks for this function without affecting cast checks
|
||||
[cfi-vcall|cfi-nvcall|cfi-icall]
|
||||
fun:*BadCall*
|
||||
|
||||
|
||||
.. _cfi-cross-dso:
|
||||
|
||||
|
@ -92,7 +92,7 @@ The compiler relies on co-operation from the linker in order to assemble
|
||||
the bit vectors for the whole program. It currently does this using LLVM's
|
||||
`type metadata`_ mechanism together with link-time optimization.
|
||||
|
||||
.. _address point: https://mentorembedded.github.io/cxx-abi/abi.html#vtable-general
|
||||
.. _address point: http://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-general
|
||||
.. _type metadata: http://llvm.org/docs/TypeMetadata.html
|
||||
.. _ByteArrayBuilder: http://llvm.org/docs/doxygen/html/structllvm_1_1ByteArrayBuilder.html
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
139
docs/HardwareAssistedAddressSanitizerDesign.rst
Normal file
139
docs/HardwareAssistedAddressSanitizerDesign.rst
Normal file
@ -0,0 +1,139 @@
|
||||
=======================================================
|
||||
Hardware-assisted AddressSanitizer Design Documentation
|
||||
=======================================================
|
||||
|
||||
This page is a design document for
|
||||
**hardware-assisted AddressSanitizer** (or **HWASAN**)
|
||||
a tool similar to :doc:`AddressSanitizer`,
|
||||
but based on partial hardware assistance.
|
||||
|
||||
The document is a draft, suggestions are welcome.
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
:doc:`AddressSanitizer`
|
||||
tags every 8 bytes of the application memory with a 1 byte tag (using *shadow memory*),
|
||||
uses *redzones* to find buffer-overflows and
|
||||
*quarantine* to find use-after-free.
|
||||
The redzones, the quarantine, and, to a less extent, the shadow, are the
|
||||
sources of AddressSanitizer's memory overhead.
|
||||
See the `AddressSanitizer paper`_ for details.
|
||||
|
||||
AArch64 has the `Address Tagging`_, a hardware feature that allows
|
||||
software to use 8 most significant bits of a 64-bit pointer as
|
||||
a tag. HWASAN uses `Address Tagging`_
|
||||
to implement a memory safety tool, similar to :doc:`AddressSanitizer`,
|
||||
but with smaller memory overhead and slightly different (mostly better)
|
||||
accuracy guarantees.
|
||||
|
||||
Algorithm
|
||||
=========
|
||||
* Every heap/stack/global memory object is forcibly aligned by `N` bytes
|
||||
(`N` is e.g. 16 or 64)
|
||||
* For every such object a random `K`-bit tag `T` is chosen (`K` is e.g. 4 or 8)
|
||||
* The pointer to the object is tagged with `T`.
|
||||
* The memory for the object is also tagged with `T`
|
||||
(using a `N=>1` shadow memory)
|
||||
* Every load and store is instrumented to read the memory tag and compare it
|
||||
with the pointer tag, exception is raised on tag mismatch.
|
||||
|
||||
Instrumentation
|
||||
===============
|
||||
|
||||
Memory Accesses
|
||||
---------------
|
||||
All memory accesses are prefixed with a call to a run-time function.
|
||||
The function encodes the type and the size of access in its name;
|
||||
it receives the address as a parameter, e.g. `__hwasan_load4(void *ptr)`;
|
||||
it loads the memory tag, compares it with the
|
||||
pointer tag, and executes `__builtin_trap` (or calls `__hwasan_error_load4(void *ptr)`) on mismatch.
|
||||
|
||||
It's possible to inline this callback too.
|
||||
|
||||
Heap
|
||||
----
|
||||
|
||||
Tagging the heap memory/pointers is done by `malloc`.
|
||||
This can be based on any malloc that forces all objects to be N-aligned.
|
||||
|
||||
Stack
|
||||
-----
|
||||
|
||||
Special compiler instrumentation is required to align the local variables
|
||||
by N, tag the memory and the pointers.
|
||||
Stack instrumentation is expected to be a major source of overhead,
|
||||
but could be optional.
|
||||
TODO: details.
|
||||
|
||||
Globals
|
||||
-------
|
||||
|
||||
TODO: details.
|
||||
|
||||
Error reporting
|
||||
---------------
|
||||
|
||||
Errors are generated by `__builtin_trap` and are handled by a signal handler.
|
||||
|
||||
Attribute
|
||||
---------
|
||||
|
||||
HWASAN uses its own LLVM IR Attribute `sanitize_hwaddress` and a matching
|
||||
C function attribute. An alternative would be to re-use ASAN's attribute
|
||||
`sanitize_address`. The reasons to use a separate attribute are:
|
||||
|
||||
* Users may need to disable ASAN but not HWASAN, or vise versa,
|
||||
because the tools have different trade-offs and compatibility issues.
|
||||
* LLVM (ideally) does not use flags to decide which pass is being used,
|
||||
ASAN or HWASAN are being applied, based on the function attributes.
|
||||
|
||||
This does mean that users of HWASAN may need to add the new attribute
|
||||
to the code that already uses the old attribute.
|
||||
|
||||
|
||||
Comparison with AddressSanitizer
|
||||
================================
|
||||
|
||||
HWASAN:
|
||||
* Is less portable than :doc:`AddressSanitizer`
|
||||
as it relies on hardware `Address Tagging`_ (AArch64).
|
||||
Address Tagging can be emulated with compiler instrumentation,
|
||||
but it will require the instrumentation to remove the tags before
|
||||
any load or store, which is infeasible in any realistic environment
|
||||
that contains non-instrumented code.
|
||||
* May have compatibility problems if the target code uses higher
|
||||
pointer bits for other purposes.
|
||||
* May require changes in the OS kernels (e.g. Linux seems to dislike
|
||||
tagged pointers passed from address space:
|
||||
https://www.kernel.org/doc/Documentation/arm64/tagged-pointers.txt).
|
||||
* **Does not require redzones to detect buffer overflows**,
|
||||
but the buffer overflow detection is probabilistic, with roughly
|
||||
`(2**K-1)/(2**K)` probability of catching a bug.
|
||||
* **Does not require quarantine to detect heap-use-after-free,
|
||||
or stack-use-after-return**.
|
||||
The detection is similarly probabilistic.
|
||||
|
||||
The memory overhead of HWASAN is expected to be much smaller
|
||||
than that of AddressSanitizer:
|
||||
`1/N` extra memory for the shadow
|
||||
and some overhead due to `N`-aligning all objects.
|
||||
|
||||
|
||||
Related Work
|
||||
============
|
||||
* `SPARC ADI`_ implements a similar tool mostly in hardware.
|
||||
* `Effective and Efficient Memory Protection Using Dynamic Tainting`_ discusses
|
||||
similar approaches ("lock & key").
|
||||
* `Watchdog`_ discussed a heavier, but still somewhat similar
|
||||
"lock & key" approach.
|
||||
* *TODO: add more "related work" links. Suggestions are welcome.*
|
||||
|
||||
|
||||
.. _Watchdog: http://www.cis.upenn.edu/acg/papers/isca12_watchdog.pdf
|
||||
.. _Effective and Efficient Memory Protection Using Dynamic Tainting: https://www.cc.gatech.edu/~orso/papers/clause.doudalis.orso.prvulovic.pdf
|
||||
.. _SPARC ADI: https://lazytyped.blogspot.com/2017/09/getting-started-with-adi.html
|
||||
.. _AddressSanitizer paper: https://www.usenix.org/system/files/conference/atc12/atc12-final39.pdf
|
||||
.. _Address Tagging: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ch12s05s01.html
|
||||
|
@ -49,7 +49,7 @@ when the code is incorrect or dubious. In Clang, each diagnostic produced has
|
||||
(at the minimum) a unique ID, an English translation associated with it, a
|
||||
:ref:`SourceLocation <SourceLocation>` to "put the caret", and a severity
|
||||
(e.g., ``WARNING`` or ``ERROR``). They can also optionally include a number of
|
||||
arguments to the dianostic (which fill in "%0"'s in the string) as well as a
|
||||
arguments to the diagnostic (which fill in "%0"'s in the string) as well as a
|
||||
number of source ranges that related to the diagnostic.
|
||||
|
||||
In this section, we'll be giving examples produced by the Clang command line
|
||||
@ -493,11 +493,11 @@ source code of the program. Important design points include:
|
||||
|
||||
In practice, the ``SourceLocation`` works together with the ``SourceManager``
|
||||
class to encode two pieces of information about a location: its spelling
|
||||
location and its instantiation location. For most tokens, these will be the
|
||||
location and its expansion location. For most tokens, these will be the
|
||||
same. However, for a macro expansion (or tokens that came from a ``_Pragma``
|
||||
directive) these will describe the location of the characters corresponding to
|
||||
the token and the location where the token was used (i.e., the macro
|
||||
instantiation point or the location of the ``_Pragma`` itself).
|
||||
expansion point or the location of the ``_Pragma`` itself).
|
||||
|
||||
The Clang front-end inherently depends on the location of a token being tracked
|
||||
correctly. If it is ever incorrect, the front-end may get confused and die.
|
||||
@ -795,7 +795,7 @@ preprocessor and notifies a client of the parsing progress.
|
||||
Historically, the parser used to talk to an abstract ``Action`` interface that
|
||||
had virtual methods for parse events, for example ``ActOnBinOp()``. When Clang
|
||||
grew C++ support, the parser stopped supporting general ``Action`` clients --
|
||||
it now always talks to the :ref:`Sema libray <Sema>`. However, the Parser
|
||||
it now always talks to the :ref:`Sema library <Sema>`. However, the Parser
|
||||
still accesses AST objects only through opaque types like ``ExprResult`` and
|
||||
``StmtResult``. Only :ref:`Sema <Sema>` looks at the AST node contents of these
|
||||
wrappers.
|
||||
@ -1324,9 +1324,9 @@ range of iterators over declarations of "``f``".
|
||||
function ``DeclContext::getPrimaryContext`` retrieves the "primary" context for
|
||||
a given ``DeclContext`` instance, which is the ``DeclContext`` responsible for
|
||||
maintaining the lookup table used for the semantics-centric view. Given a
|
||||
DeclContext, one can obtain the set of declaration contexts that are semanticaly
|
||||
connected to this declaration context, in source order, including this context
|
||||
(which will be the only result, for non-namespace contexts) via
|
||||
DeclContext, one can obtain the set of declaration contexts that are
|
||||
semantically connected to this declaration context, in source order, including
|
||||
this context (which will be the only result, for non-namespace contexts) via
|
||||
``DeclContext::collectAllContexts``. Note that these functions are used
|
||||
internally within the lookup and insertion methods of the ``DeclContext``, so
|
||||
the vast majority of clients can ignore them.
|
||||
@ -1514,7 +1514,7 @@ use an i-c-e where one is required, but accepting the code unless running with
|
||||
Things get a little bit more tricky when it comes to compatibility with
|
||||
real-world source code. Specifically, GCC has historically accepted a huge
|
||||
superset of expressions as i-c-e's, and a lot of real world code depends on
|
||||
this unfortuate accident of history (including, e.g., the glibc system
|
||||
this unfortunate accident of history (including, e.g., the glibc system
|
||||
headers). GCC accepts anything its "fold" optimizer is capable of reducing to
|
||||
an integer constant, which means that the definition of what it accepts changes
|
||||
as its optimizer does. One example is that GCC accepts things like "``case
|
||||
@ -1540,7 +1540,7 @@ Implementation Approach
|
||||
After trying several different approaches, we've finally converged on a design
|
||||
(Note, at the time of this writing, not all of this has been implemented,
|
||||
consider this a design goal!). Our basic approach is to define a single
|
||||
recursive method evaluation method (``Expr::Evaluate``), which is implemented
|
||||
recursive evaluation method (``Expr::Evaluate``), which is implemented
|
||||
in ``AST/ExprConstant.cpp``. Given an expression with "scalar" type (integer,
|
||||
fp, complex, or pointer) this method returns the following information:
|
||||
|
||||
@ -2037,7 +2037,7 @@ are similar.
|
||||
* ``CodeGenFunction`` contains functions ``ConvertType`` and
|
||||
``ConvertTypeForMem`` that convert Clang's types (``clang::Type*`` or
|
||||
``clang::QualType``) to LLVM types. Use the former for values, and the
|
||||
later for memory locations: test with the C++ "``bool``" type to check
|
||||
latter for memory locations: test with the C++ "``bool``" type to check
|
||||
this. If you find that you are having to use LLVM bitcasts to make the
|
||||
subexpressions of your expression have the type that your expression
|
||||
expects, STOP! Go fix semantic analysis and the AST so that you don't
|
||||
|
@ -91,3 +91,9 @@ The convention is to name the file compile\_commands.json and put it at
|
||||
the top of the build directory. Clang tools are pointed to the top of
|
||||
the build directory to detect the file and use the compilation database
|
||||
to parse C++ code in the source tree.
|
||||
|
||||
Alternatives
|
||||
============
|
||||
For simple projects, Clang tools also recognize a compile_flags.txt file.
|
||||
This should contain one flag per line. The same flags will be used to compile
|
||||
any file.
|
||||
|
@ -139,6 +139,35 @@ and following ``__`` (double underscore) to avoid interference from a macro with
|
||||
the same name. For instance, ``gnu::__const__`` can be used instead of
|
||||
``gnu::const``.
|
||||
|
||||
``__has_c_attribute``
|
||||
---------------------
|
||||
|
||||
This function-like macro takes a single argument that is the name of an
|
||||
attribute exposed with the double square-bracket syntax in C mode. The argument
|
||||
can either be a single identifier or a scoped identifier. If the attribute is
|
||||
supported, a nonzero value is returned. If the attribute is not supported by the
|
||||
current compilation target, this macro evaluates to 0. It can be used like this:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifndef __has_c_attribute // Optional of course.
|
||||
#define __has_c_attribute(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
...
|
||||
#if __has_c_attribute(fallthrough)
|
||||
#define FALLTHROUGH [[fallthrough]]
|
||||
#else
|
||||
#define FALLTHROUGH
|
||||
#endif
|
||||
...
|
||||
|
||||
The attribute identifier (but not scope) can also be specified with a preceding
|
||||
and following ``__`` (double underscore) to avoid interference from a macro with
|
||||
the same name. For instance, ``gnu::__const__`` can be used instead of
|
||||
``gnu::const``.
|
||||
|
||||
|
||||
``__has_attribute``
|
||||
-------------------
|
||||
|
||||
@ -436,6 +465,49 @@ const_cast no no no no
|
||||
|
||||
See also :ref:`langext-__builtin_shufflevector`, :ref:`langext-__builtin_convertvector`.
|
||||
|
||||
Half-Precision Floating Point
|
||||
=============================
|
||||
|
||||
Clang supports two half-precision (16-bit) floating point types: ``__fp16`` and
|
||||
``_Float16``. ``__fp16`` is defined in the ARM C Language Extensions (`ACLE
|
||||
<http://infocenter.arm.com/help/topic/com.arm.doc.ihi0053d/IHI0053D_acle_2_1.pdf>`_)
|
||||
and ``_Float16`` in ISO/IEC TS 18661-3:2015.
|
||||
|
||||
``__fp16`` is a storage and interchange format only. This means that values of
|
||||
``__fp16`` promote to (at least) float when used in arithmetic operations.
|
||||
There are two ``__fp16`` formats. Clang supports the IEEE 754-2008 format and
|
||||
not the ARM alternative format.
|
||||
|
||||
ISO/IEC TS 18661-3:2015 defines C support for additional floating point types.
|
||||
``_FloatN`` is defined as a binary floating type, where the N suffix denotes
|
||||
the number of bits and is 16, 32, 64, or greater and equal to 128 and a
|
||||
multiple of 32. Clang supports ``_Float16``. The difference from ``__fp16`` is
|
||||
that arithmetic on ``_Float16`` is performed in half-precision, thus it is not
|
||||
a storage-only format. ``_Float16`` is available as a source language type in
|
||||
both C and C++ mode.
|
||||
|
||||
It is recommended that portable code use the ``_Float16`` type because
|
||||
``__fp16`` is an ARM C-Language Extension (ACLE), whereas ``_Float16`` is
|
||||
defined by the C standards committee, so using ``_Float16`` will not prevent
|
||||
code from being ported to architectures other than Arm. Also, ``_Float16``
|
||||
arithmetic and operations will directly map on half-precision instructions when
|
||||
they are available (e.g. Armv8.2-A), avoiding conversions to/from
|
||||
single-precision, and thus will result in more performant code. If
|
||||
half-precision instructions are unavailable, values will be promoted to
|
||||
single-precision, similar to the semantics of ``__fp16`` except that the
|
||||
results will be stored in single-precision.
|
||||
|
||||
In an arithmetic operation where one operand is of ``__fp16`` type and the
|
||||
other is of ``_Float16`` type, the ``_Float16`` type is first converted to
|
||||
``__fp16`` type and then the operation is completed as if both operands were of
|
||||
``__fp16`` type.
|
||||
|
||||
To define a ``_Float16`` literal, suffix ``f16`` can be appended to the compile-time
|
||||
constant declaration. There is no default argument promotion for ``_Float16``; this
|
||||
applies to the standard floating types only. As a consequence, for example, an
|
||||
explicit cast is required for printing a ``_Float16`` value (there is no string
|
||||
format specifier for ``_Float16``).
|
||||
|
||||
Messages on ``deprecated`` and ``unavailable`` Attributes
|
||||
=========================================================
|
||||
|
||||
@ -1929,7 +2001,13 @@ provided, with values corresponding to the enumerators of C11's
|
||||
``memory_order`` enumeration.
|
||||
|
||||
(Note that Clang additionally provides GCC-compatible ``__atomic_*``
|
||||
builtins)
|
||||
builtins and OpenCL 2.0 ``__opencl_atomic_*`` builtins. The OpenCL 2.0
|
||||
atomic builtins are an explicit form of the corresponding OpenCL 2.0
|
||||
builtin function, and are named with a ``__opencl_`` prefix. The macros
|
||||
``__OPENCL_MEMORY_SCOPE_WORK_ITEM``, ``__OPENCL_MEMORY_SCOPE_WORK_GROUP``,
|
||||
``__OPENCL_MEMORY_SCOPE_DEVICE``, ``__OPENCL_MEMORY_SCOPE_ALL_SVM_DEVICES``,
|
||||
and ``__OPENCL_MEMORY_SCOPE_SUB_GROUP`` are provided, with values
|
||||
corresponding to the enumerators of OpenCL's ``memory_scope`` enumeration.)
|
||||
|
||||
Low-level ARM exclusive memory builtins
|
||||
---------------------------------------
|
||||
|
@ -17,7 +17,7 @@ detection phase.
|
||||
Usage
|
||||
=====
|
||||
|
||||
LeakSanitizer is only supported on x86\_64 Linux. In order to use it,
|
||||
LeakSanitizer is supported on x86\_64 Linux and OS X. In order to use it,
|
||||
simply build your program with :doc:`AddressSanitizer`:
|
||||
|
||||
.. code-block:: console
|
||||
@ -30,7 +30,7 @@ simply build your program with :doc:`AddressSanitizer`:
|
||||
p = 0; // The memory is leaked here.
|
||||
return 0;
|
||||
}
|
||||
% clang -fsanitize=address -g memory-leak.c ; ./a.out
|
||||
% clang -fsanitize=address -g memory-leak.c ; ASAN_OPTIONS=detect_leaks=1 ./a.out
|
||||
==23646==ERROR: LeakSanitizer: detected memory leaks
|
||||
Direct leak of 7 byte(s) in 1 object(s) allocated from:
|
||||
#0 0x4af01b in __interceptor_malloc /projects/compiler-rt/lib/asan/asan_malloc_linux.cc:52:3
|
||||
|
@ -346,6 +346,24 @@ Example matches Foo (Additions)
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('objcCategoryImplDecl0')"><a name="objcCategoryImplDecl0Anchor">objcCategoryImplDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCCategoryImplDecl.html">ObjCCategoryImplDecl</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcCategoryImplDecl0"><pre>Matches Objective-C category definitions.
|
||||
|
||||
Example matches Foo (Additions)
|
||||
@implementation Foo (Additions)
|
||||
@end
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('objcImplementationDecl0')"><a name="objcImplementationDecl0Anchor">objcImplementationDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCImplementationDecl.html">ObjCImplementationDecl</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcImplementationDecl0"><pre>Matches Objective-C implementation declarations.
|
||||
|
||||
Example matches Foo
|
||||
@implementation Foo
|
||||
@end
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>></td><td class="name" onclick="toggle('objcInterfaceDecl0')"><a name="objcInterfaceDecl0Anchor">objcInterfaceDecl</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCInterfaceDecl.html">ObjCInterfaceDecl</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcInterfaceDecl0"><pre>Matches Objective-C interface declarations.
|
||||
|
||||
@ -1207,6 +1225,24 @@ nullStmt()
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('objcCatchStmt0')"><a name="objcCatchStmt0Anchor">objcCatchStmt</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCAtCatchStmt.html">ObjCAtCatchStmt</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcCatchStmt0"><pre>Matches Objective-C @catch statements.
|
||||
|
||||
Example matches @catch
|
||||
@try {}
|
||||
@catch (...) {}
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('objcFinallyStmt0')"><a name="objcFinallyStmt0Anchor">objcFinallyStmt</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCAtFinallyStmt.html">ObjCAtFinallyStmt</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcFinallyStmt0"><pre>Matches Objective-C @finally statements.
|
||||
|
||||
Example matches @finally
|
||||
@try {}
|
||||
@finally {}
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('objcMessageExpr0')"><a name="objcMessageExpr0Anchor">objcMessageExpr</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcMessageExpr0"><pre>Matches ObjectiveC Message invocation expressions.
|
||||
|
||||
@ -1218,6 +1254,23 @@ NSString's "alloc". This matcher should match both message sends.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('objcThrowStmt0')"><a name="objcThrowStmt0Anchor">objcThrowStmt</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCAtThrowStmt.html">ObjCAtThrowStmt</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcThrowStmt0"><pre>Matches Objective-C @throw statements.
|
||||
|
||||
Example matches @throw
|
||||
@throw obj;
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('objcTryStmt0')"><a name="objcTryStmt0Anchor">objcTryStmt</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCAtTryStmt.html">ObjCAtTryStmt</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="objcTryStmt0"><pre>Matches Objective-C @try statements.
|
||||
|
||||
Example matches @try
|
||||
@try {}
|
||||
@catch (...) {}
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Stmt.html">Stmt</a>></td><td class="name" onclick="toggle('opaqueValueExpr0')"><a name="opaqueValueExpr0Anchor">opaqueValueExpr</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1OpaqueValueExpr.html">OpaqueValueExpr</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="opaqueValueExpr0"><pre>Matches opaque value expressions. They are used as helpers
|
||||
to reference another expressions and can be met
|
||||
@ -1729,6 +1782,21 @@ substTemplateTypeParmType() matches the type of 't' but not '1'
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('tagType0')"><a name="tagType0Anchor">tagType</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagType.html">TagType</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="tagType0"><pre>Matches tag types (record and enum types).
|
||||
|
||||
Given
|
||||
enum E {};
|
||||
class C {};
|
||||
|
||||
E e;
|
||||
C c;
|
||||
|
||||
tagType() matches the type of the variable declarations of both e
|
||||
and c.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Type.html">Type</a>></td><td class="name" onclick="toggle('templateSpecializationType0')"><a name="templateSpecializationType0Anchor">templateSpecializationType</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateSpecializationType.html">TemplateSpecializationType</a>>...</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="templateSpecializationType0"><pre>Matches template specialization types.
|
||||
|
||||
@ -2208,6 +2276,16 @@ Given
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXNewExpr.html">CXXNewExpr</a>></td><td class="name" onclick="toggle('isArray0')"><a name="isArray0Anchor">isArray</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isArray0"><pre>Matches array new expressions.
|
||||
|
||||
Given:
|
||||
MyClass *p1 = new MyClass[10];
|
||||
cxxNewExpr(isArray())
|
||||
matches the expression 'new MyClass[10]'.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>></td><td class="name" onclick="toggle('hasOverloadedOperatorName1')"><a name="hasOverloadedOperatorName1Anchor">hasOverloadedOperatorName</a></td><td>StringRef Name</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasOverloadedOperatorName1"><pre>Matches overloaded operator names.
|
||||
|
||||
@ -2229,6 +2307,15 @@ Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXOp
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>></td><td class="name" onclick="toggle('hasDefinition0')"><a name="hasDefinition0Anchor">hasDefinition</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasDefinition0"><pre>Matches a class declaration that is defined.
|
||||
|
||||
Example matches x (matcher = cxxRecordDecl(hasDefinition()))
|
||||
class x {};
|
||||
class y;
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>></td><td class="name" onclick="toggle('isDerivedFrom1')"><a name="isDerivedFrom1Anchor">isDerivedFrom</a></td><td>std::string BaseName</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isDerivedFrom1"><pre>Overloaded method as shortcut for isDerivedFrom(hasName(...)).
|
||||
</pre></td></tr>
|
||||
@ -2678,8 +2765,8 @@ functionDecl(isDefaulted())
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>></td><td class="name" onclick="toggle('isDefinition2')"><a name="isDefinition2Anchor">isDefinition</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isDefinition2"><pre>Matches if a declaration has a body attached.
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>></td><td class="name" onclick="toggle('isDefinition3')"><a name="isDefinition3Anchor">isDefinition</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isDefinition3"><pre>Matches if a declaration has a body attached.
|
||||
|
||||
Example matches A, va, fa
|
||||
class A {};
|
||||
@ -2688,8 +2775,15 @@ Example matches A, va, fa
|
||||
extern int vb; Doesn't match, as it doesn't define the variable.
|
||||
void fa() {}
|
||||
void fb(); Doesn't match, as it has no body.
|
||||
@interface X
|
||||
- (void)ma; Doesn't match, interface is declaration.
|
||||
@end
|
||||
@implementation X
|
||||
- (void)ma {}
|
||||
@end
|
||||
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>,
|
||||
Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMethodDecl.html">ObjCMethodDecl</a>>
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
@ -2717,19 +2811,22 @@ functionDecl(isExplicitTemplateSpecialization())
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>>
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>></td><td class="name" onclick="toggle('isExternC0')"><a name="isExternC0Anchor">isExternC</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isExternC0"><pre>Matches extern "C" function declarations.
|
||||
<tr><td colspan="4" class="doc" id="isExternC0"><pre>Matches extern "C" function or variable declarations.
|
||||
|
||||
Given:
|
||||
extern "C" void f() {}
|
||||
extern "C" { void g() {} }
|
||||
void h() {}
|
||||
extern "C" int x = 1;
|
||||
extern "C" int y = 2;
|
||||
int z = 3;
|
||||
functionDecl(isExternC())
|
||||
matches the declaration of f and g, but not the declaration h
|
||||
matches the declaration of f and g, but not the declaration of h.
|
||||
varDecl(isExternC())
|
||||
matches the declaration of x and y, but not the declaration of z.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>></td><td class="name" onclick="toggle('isInline1')"><a name="isInline1Anchor">isInline</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isInline1"><pre>Matches function and namespace declarations that are marked with
|
||||
the inline keyword.
|
||||
@ -3083,6 +3180,37 @@ a substring matched by the given RegExp.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMethodDecl.html">ObjCMethodDecl</a>></td><td class="name" onclick="toggle('isDefinition2')"><a name="isDefinition2Anchor">isDefinition</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isDefinition2"><pre>Matches if a declaration has a body attached.
|
||||
|
||||
Example matches A, va, fa
|
||||
class A {};
|
||||
class B; Doesn't match, as it has no body.
|
||||
int va;
|
||||
extern int vb; Doesn't match, as it doesn't define the variable.
|
||||
void fa() {}
|
||||
void fb(); Doesn't match, as it has no body.
|
||||
@interface X
|
||||
- (void)ma; Doesn't match, interface is declaration.
|
||||
@end
|
||||
@implementation X
|
||||
- (void)ma {}
|
||||
@end
|
||||
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>,
|
||||
Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMethodDecl.html">ObjCMethodDecl</a>>
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ParmVarDecl.html">ParmVarDecl</a>></td><td class="name" onclick="toggle('hasDefaultArgument0')"><a name="hasDefaultArgument0Anchor">hasDefaultArgument</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasDefaultArgument0"><pre>Matches a declaration that has default arguments.
|
||||
|
||||
Example matches y (matcher = parmVarDecl(hasDefaultArgument()))
|
||||
void x(int val) {}
|
||||
void y(int val = 0) {}
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>></td><td class="name" onclick="toggle('asString0')"><a name="asString0Anchor">asString</a></td><td>std::string Name</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="asString0"><pre>Matches if the matched type is represented by the given string.
|
||||
|
||||
@ -3362,8 +3490,15 @@ Example matches A, va, fa
|
||||
extern int vb; Doesn't match, as it doesn't define the variable.
|
||||
void fa() {}
|
||||
void fb(); Doesn't match, as it has no body.
|
||||
@interface X
|
||||
- (void)ma; Doesn't match, interface is declaration.
|
||||
@end
|
||||
@implementation X
|
||||
- (void)ma {}
|
||||
@end
|
||||
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>,
|
||||
Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMethodDecl.html">ObjCMethodDecl</a>>
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
@ -3625,8 +3760,15 @@ Example matches A, va, fa
|
||||
extern int vb; Doesn't match, as it doesn't define the variable.
|
||||
void fa() {}
|
||||
void fb(); Doesn't match, as it has no body.
|
||||
@interface X
|
||||
- (void)ma; Doesn't match, interface is declaration.
|
||||
@end
|
||||
@implementation X
|
||||
- (void)ma {}
|
||||
@end
|
||||
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TagDecl.html">TagDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>,
|
||||
Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ObjCMethodDecl.html">ObjCMethodDecl</a>>
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
@ -3656,19 +3798,22 @@ functionDecl(isExplicitTemplateSpecialization())
|
||||
Usable as: Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1FunctionDecl.html">FunctionDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>>, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>>
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>></td><td class="name" onclick="toggle('isExternC1')"><a name="isExternC1Anchor">isExternC</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isExternC1"><pre>Matches extern "C" function declarations.
|
||||
<tr><td colspan="4" class="doc" id="isExternC1"><pre>Matches extern "C" function or variable declarations.
|
||||
|
||||
Given:
|
||||
extern "C" void f() {}
|
||||
extern "C" { void g() {} }
|
||||
void h() {}
|
||||
extern "C" int x = 1;
|
||||
extern "C" int y = 2;
|
||||
int z = 3;
|
||||
functionDecl(isExternC())
|
||||
matches the declaration of f and g, but not the declaration h
|
||||
matches the declaration of f and g, but not the declaration of h.
|
||||
varDecl(isExternC())
|
||||
matches the declaration of x and y, but not the declaration of z.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>></td><td class="name" onclick="toggle('isStaticStorageClass1')"><a name="isStaticStorageClass1Anchor">isStaticStorageClass</a></td><td></td></tr>
|
||||
<tr><td colspan="4" class="doc" id="isStaticStorageClass1"><pre>Matches variablefunction declarations that have "static" storage
|
||||
class specifier ("static" keyword) written in the source.
|
||||
@ -4350,6 +4495,16 @@ Example matches A() in the last line
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXNewExpr.html">CXXNewExpr</a>></td><td class="name" onclick="toggle('hasArraySize0')"><a name="hasArraySize0Anchor">hasArraySize</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasArraySize0"><pre>Matches array new expressions with a given array size.
|
||||
|
||||
Given:
|
||||
MyClass *p1 = new MyClass[10];
|
||||
cxxNewExpr(hasArraySize(intgerLiteral(equals(10))))
|
||||
matches the expression 'new MyClass[10]'.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXNewExpr.html">CXXNewExpr</a>></td><td class="name" onclick="toggle('hasDeclaration12')"><a name="hasDeclaration12Anchor">hasDeclaration</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasDeclaration12"><pre>Matches a node if the declaration associated with that node
|
||||
matches the given matcher.
|
||||
@ -4546,6 +4701,18 @@ functionDecl(hasAnyTemplateArgument(refersToType(asString("int"))))
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>></td><td class="name" onclick="toggle('hasSpecializedTemplate0')"><a name="hasSpecializedTemplate0Anchor">hasSpecializedTemplate</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateDecl.html">ClassTemplateDecl</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasSpecializedTemplate0"><pre>Matches the specialized template of a specialization declaration.
|
||||
|
||||
Given
|
||||
tempalate<typename T> class A {};
|
||||
typedef A<int> B;
|
||||
classTemplateSpecializationDecl(hasSpecializedTemplate(classTemplateDecl()))
|
||||
matches 'B' with classTemplateDecl() matching the class template
|
||||
declaration of 'A'.
|
||||
</pre></td></tr>
|
||||
|
||||
|
||||
<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1ClassTemplateSpecializationDecl.html">ClassTemplateSpecializationDecl</a>></td><td class="name" onclick="toggle('hasTemplateArgument0')"><a name="hasTemplateArgument0Anchor">hasTemplateArgument</a></td><td>unsigned N, Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1TemplateArgument.html">TemplateArgument</a>> InnerMatcher</td></tr>
|
||||
<tr><td colspan="4" class="doc" id="hasTemplateArgument0"><pre>Matches classTemplateSpecializations, templateSpecializationType and
|
||||
functionDecl where the n'th TemplateArgument matches the given InnerMatcher.
|
||||
|
@ -213,8 +213,14 @@ Command-line parameters
|
||||
``-fno-implicit-modules``
|
||||
All modules used by the build must be specified with ``-fmodule-file``.
|
||||
|
||||
``-fmodule-file=<file>``
|
||||
Load the given precompiled module file.
|
||||
``-fmodule-file=[<name>=]<file>``
|
||||
Specify the mapping of module names to precompiled module files. If the
|
||||
name is omitted, then the module file is loaded whether actually required
|
||||
or not. If the name is specified, then the mapping is treated as another
|
||||
prebuilt module search mechanism (in addition to ``-fprebuilt-module-path``)
|
||||
and the module is only loaded if required. Note that in this case the
|
||||
specified file also overrides this module's paths that might be embedded
|
||||
in other precompiled module files.
|
||||
|
||||
``-fprebuilt-module-path=<directory>``
|
||||
Specify the path to the prebuilt modules. If specified, we will look for modules in this directory for a given top-level module name. We don't need a module map for loading prebuilt modules in this directory and the compiler will not try to rebuild these modules. This can be specified multiple times.
|
||||
@ -317,11 +323,12 @@ Module map files use a simplified form of the C99 lexer, with the same rules for
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
``config_macros`` ``export`` ``private``
|
||||
``config_macros`` ``export_as`` ``private``
|
||||
``conflict`` ``framework`` ``requires``
|
||||
``exclude`` ``header`` ``textual``
|
||||
``explicit`` ``link`` ``umbrella``
|
||||
``extern`` ``module`` ``use``
|
||||
``export``
|
||||
|
||||
Module map file
|
||||
---------------
|
||||
@ -381,6 +388,7 @@ Modules can have a number of different kinds of members, each of which is descri
|
||||
*umbrella-dir-declaration*
|
||||
*submodule-declaration*
|
||||
*export-declaration*
|
||||
*export-as-declaration*
|
||||
*use-declaration*
|
||||
*link-declaration*
|
||||
*config-macros-declaration*
|
||||
@ -660,6 +668,30 @@ Note that, if ``Derived.h`` includes ``Base.h``, one can simply use a wildcard e
|
||||
compatibility for programs that rely on transitive inclusion (i.e.,
|
||||
all of them).
|
||||
|
||||
Re-export Declaration
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
An *export-as-declaration* specifies that the current module will have
|
||||
its interface re-exported by the named module.
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
*export-as-declaration*:
|
||||
``export_as`` *identifier*
|
||||
|
||||
The *export-as-declaration* names the module that the current
|
||||
module will be re-exported through. Only top-level modules
|
||||
can be re-exported, and any given module may only be re-exported
|
||||
through a single module.
|
||||
|
||||
**Example:** In the following example, the module ``MyFrameworkCore``
|
||||
will be re-exported via the module ``MyFramework``:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
module MyFrameworkCore {
|
||||
export_as MyFramework
|
||||
}
|
||||
|
||||
Use declaration
|
||||
~~~~~~~~~~~~~~~
|
||||
A *use-declaration* specifies another module that the current top-level module
|
||||
@ -945,4 +977,3 @@ PCHInternals_
|
||||
.. [#] The preprocessing context in which the modules are parsed is actually dependent on the command-line options provided to the compiler, including the language dialect and any ``-D`` options. However, the compiled modules for different command-line options are kept distinct, and any preprocessor directives that occur within the translation unit are ignored. See the section on the `Configuration macros declaration`_ for more information.
|
||||
|
||||
.. _PCHInternals: PCHInternals.html
|
||||
|
||||
|
253
docs/RefactoringEngine.rst
Normal file
253
docs/RefactoringEngine.rst
Normal file
@ -0,0 +1,253 @@
|
||||
==========================
|
||||
Clang's refactoring engine
|
||||
==========================
|
||||
|
||||
This document describes the design of Clang's refactoring engine and provides
|
||||
a couple of examples that show how various primitives in the refactoring API
|
||||
can be used to implement different refactoring actions. The :doc:`LibTooling`
|
||||
library provides several other APIs that are used when developing a
|
||||
refactoring action.
|
||||
|
||||
Refactoring engine can be used to implement local refactorings that are
|
||||
initiated using a selection in an editor or an IDE. You can combine
|
||||
:doc:`AST matchers<LibASTMatchers>` and the refactoring engine to implement
|
||||
refactorings that don't lend themselves well to source selection and/or have to
|
||||
query ASTs for some particular nodes.
|
||||
|
||||
We assume basic knowledge about the Clang AST. See the :doc:`Introduction
|
||||
to the Clang AST <IntroductionToTheClangAST>` if you want to learn more
|
||||
about how the AST is structured.
|
||||
|
||||
.. FIXME: create new refactoring action tutorial and link to the tutorial
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Clang's refactoring engine defines a set refactoring actions that implement
|
||||
a number of different source transformations. The ``clang-refactor``
|
||||
command-line tool can be used to perform these refactorings. Certain
|
||||
refactorings are also available in other clients like text editors and IDEs.
|
||||
|
||||
A refactoring action is a class that defines a list of related refactoring
|
||||
operations (rules). These rules are grouped under a common umbrella - a single
|
||||
``clang-refactor`` command. In addition to rules, the refactoring action
|
||||
provides the action's command name and description to ``clang-refactor``.
|
||||
Each action must implement the ``RefactoringAction`` interface. Here's an
|
||||
outline of a ``local-rename`` action:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class LocalRename final : public RefactoringAction {
|
||||
public:
|
||||
StringRef getCommand() const override { return "local-rename"; }
|
||||
|
||||
StringRef getDescription() const override {
|
||||
return "Finds and renames symbols in code with no indexer support";
|
||||
}
|
||||
|
||||
RefactoringActionRules createActionRules() const override {
|
||||
...
|
||||
}
|
||||
};
|
||||
|
||||
Refactoring Action Rules
|
||||
------------------------
|
||||
|
||||
An individual refactoring action is responsible for creating the set of
|
||||
grouped refactoring action rules that represent one refactoring operation.
|
||||
Although the rules in one action may have a number of different implementations,
|
||||
they should strive to produce a similar result. It should be easy for users to
|
||||
identify which refactoring action produced the result regardless of which
|
||||
refactoring action rule was used.
|
||||
|
||||
The distinction between actions and rules enables the creation of actions
|
||||
that define a set of different rules that produce similar results. For example,
|
||||
the "add missing switch cases" refactoring operation typically adds missing
|
||||
cases to one switch at a time. However, it could be useful to have a
|
||||
refactoring that works on all switches that operate on a particular enum, as
|
||||
one could then automatically update all of them after adding a new enum
|
||||
constant. To achieve that, we can create two different rules that will use one
|
||||
``clang-refactor`` subcommand. The first rule will describe a local operation
|
||||
that's initiated when the user selects a single switch. The second rule will
|
||||
describe a global operation that works across translation units and is initiated
|
||||
when the user provides the name of the enum to clang-refactor (or the user could
|
||||
select the enum declaration instead). The clang-refactor tool will then analyze
|
||||
the selection and other options passed to the refactoring action, and will pick
|
||||
the most appropriate rule for the given selection and other options.
|
||||
|
||||
Rule Types
|
||||
^^^^^^^^^^
|
||||
|
||||
Clang's refactoring engine supports several different refactoring rules:
|
||||
|
||||
- ``SourceChangeRefactoringRule`` produces source replacements that are applied
|
||||
to the source files. Subclasses that choose to implement this rule have to
|
||||
implement the ``createSourceReplacements`` member function. This type of
|
||||
rule is typically used to implement local refactorings that transform the
|
||||
source in one translation unit only.
|
||||
|
||||
- ``FindSymbolOccurrencesRefactoringRule`` produces a "partial" refactoring
|
||||
result: a set of occurrences that refer to a particular symbol. This type
|
||||
of rule is typically used to implement an interactive renaming action that
|
||||
allows users to specify which occurrences should be renamed during the
|
||||
refactoring. Subclasses that choose to implement this rule have to implement
|
||||
the ``findSymbolOccurrences`` member function.
|
||||
|
||||
The following set of quick checks might help if you are unsure about the type
|
||||
of rule you should use:
|
||||
|
||||
#. If you would like to transform the source in one translation unit and if
|
||||
you don't need any cross-TU information, then the
|
||||
``SourceChangeRefactoringRule`` should work for you.
|
||||
|
||||
#. If you would like to implement a rename-like operation with potential
|
||||
interactive components, then ``FindSymbolOccurrencesRefactoringRule`` might
|
||||
work for you.
|
||||
|
||||
How to Create a Rule
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you determine which type of rule is suitable for your needs you can
|
||||
implement the refactoring by subclassing the rule and implementing its
|
||||
interface. The subclass should have a constructor that takes the inputs that
|
||||
are needed to perform the refactoring. For example, if you want to implement a
|
||||
rule that simply deletes a selection, you should create a subclass of
|
||||
``SourceChangeRefactoringRule`` with a constructor that accepts the selection
|
||||
range:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class DeleteSelectedRange final : public SourceChangeRefactoringRule {
|
||||
public:
|
||||
DeleteSelection(SourceRange Selection) : Selection(Selection) {}
|
||||
|
||||
Expected<AtomicChanges>
|
||||
createSourceReplacements(RefactoringRuleContext &Context) override {
|
||||
AtomicChange Replacement(Context.getSources(), Selection.getBegin());
|
||||
Replacement.replace(Context.getSource,
|
||||
CharSourceRange::getCharRange(Selection), "");
|
||||
return { Replacement };
|
||||
}
|
||||
private:
|
||||
SourceRange Selection;
|
||||
};
|
||||
|
||||
The rule's subclass can then be added to the list of refactoring action's
|
||||
rules for a particular action using the ``createRefactoringActionRule``
|
||||
function. For example, the class that's shown above can be added to the
|
||||
list of action rules using the following code:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
RefactoringActionRules Rules;
|
||||
Rules.push_back(
|
||||
createRefactoringActionRule<DeleteSelectedRange>(
|
||||
SourceRangeSelectionRequirement())
|
||||
);
|
||||
|
||||
The ``createRefactoringActionRule`` function takes in a list of refactoring
|
||||
action rule requirement values. These values describe the initiation
|
||||
requirements that have to be satisfied by the refactoring engine before the
|
||||
provided action rule can be constructed and invoked. The next section
|
||||
describes how these requirements are evaluated and lists all the possible
|
||||
requirements that can be used to construct a refactoring action rule.
|
||||
|
||||
Refactoring Action Rule Requirements
|
||||
------------------------------------
|
||||
|
||||
A refactoring action rule requirement is a value whose type derives from the
|
||||
``RefactoringActionRuleRequirement`` class. The type must define an
|
||||
``evaluate`` member function that returns a value of type ``Expected<...>``.
|
||||
When a requirement value is used as an argument to
|
||||
``createRefactoringActionRule``, that value is evaluated during the initiation
|
||||
of the action rule. The evaluated result is then passed to the rule's
|
||||
constructor unless the evaluation produced an error. For example, the
|
||||
``DeleteSelectedRange`` sample rule that's defined in the previous section
|
||||
will be evaluated using the following steps:
|
||||
|
||||
#. ``SourceRangeSelectionRequirement``'s ``evaluate`` member function will be
|
||||
called first. It will return an ``Expected<SourceRange>``.
|
||||
|
||||
#. If the return value is an error the initiation will fail and the error
|
||||
will be reported to the client. Note that the client may not report the
|
||||
error to the user.
|
||||
|
||||
#. Otherwise the source range return value will be used to construct the
|
||||
``DeleteSelectedRange`` rule. The rule will then be invoked as the initiation
|
||||
succeeded (all requirements were evaluated successfully).
|
||||
|
||||
The same series of steps applies to any refactoring rule. Firstly, the engine
|
||||
will evaluate all of the requirements. Then it will check if these requirements
|
||||
are satisfied (they should not produce an error). Then it will construct the
|
||||
rule and invoke it.
|
||||
|
||||
The separation of requirements, their evaluation and the invocation of the
|
||||
refactoring action rule allows the refactoring clients to:
|
||||
|
||||
- Disable refactoring action rules whose requirements are not supported.
|
||||
|
||||
- Gather the set of options and define a command-line / visual interface
|
||||
that allows users to input these options without ever invoking the
|
||||
action.
|
||||
|
||||
Selection Requirements
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The refactoring rule requirements that require some form of source selection
|
||||
are listed below:
|
||||
|
||||
- ``SourceRangeSelectionRequirement`` evaluates to a source range when the
|
||||
action is invoked with some sort of selection. This requirement should be
|
||||
satisfied when a refactoring is initiated in an editor, even when the user
|
||||
has not selected anything (the range will contain the cursor's location in
|
||||
that case).
|
||||
|
||||
.. FIXME: Future selection requirements
|
||||
|
||||
.. FIXME: Maybe mention custom selection requirements?
|
||||
|
||||
Other Requirements
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There are several other requirements types that can be used when creating
|
||||
a refactoring rule:
|
||||
|
||||
- The ``RefactoringOptionsRequirement`` requirement is an abstract class that
|
||||
should be subclassed by requirements working with options. The more
|
||||
concrete ``OptionRequirement`` requirement is a simple implementation of the
|
||||
aforementioned class that returns the value of the specified option when
|
||||
it's evaluated. The next section talks more about refactoring options and
|
||||
how they can be used when creating a rule.
|
||||
|
||||
Refactoring Options
|
||||
-------------------
|
||||
|
||||
Refactoring options are values that affect a refactoring operation and are
|
||||
specified either using command-line options or another client-specific
|
||||
mechanism. Options should be created using a class that derives either from
|
||||
the ``OptionalRequiredOption`` or ``RequiredRefactoringOption``. The following
|
||||
example shows how one can created a required string option that corresponds to
|
||||
the ``-new-name`` command-line option in clang-refactor:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
class NewNameOption : public RequiredRefactoringOption<std::string> {
|
||||
public:
|
||||
StringRef getName() const override { return "new-name"; }
|
||||
StringRef getDescription() const override {
|
||||
return "The new name to change the symbol to";
|
||||
}
|
||||
};
|
||||
|
||||
The option that's shown in the example above can then be used to create
|
||||
a requirement for a refactoring rule using a requirement like
|
||||
``OptionRequirement``:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
createRefactoringActionRule<RenameOccurrences>(
|
||||
...,
|
||||
OptionRequirement<NewNameOption>())
|
||||
);
|
||||
|
||||
.. FIXME: Editor Bindings section
|
@ -1,6 +1,6 @@
|
||||
=========================
|
||||
Clang 5.0.0 Release Notes
|
||||
=========================
|
||||
=======================================
|
||||
Clang 6.0.0 (In-Progress) Release Notes
|
||||
=======================================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
@ -8,11 +8,17 @@ Clang 5.0.0 Release Notes
|
||||
|
||||
Written by the `LLVM Team <http://llvm.org/>`_
|
||||
|
||||
.. warning::
|
||||
|
||||
These are in-progress notes for the upcoming Clang 6 release.
|
||||
Release notes for previous releases can be found on
|
||||
`the Download Page <http://releases.llvm.org/download.html>`_.
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This document contains the release notes for the Clang C/C++/Objective-C
|
||||
frontend, part of the LLVM Compiler Infrastructure, release 5.0.0. Here we
|
||||
frontend, part of the LLVM Compiler Infrastructure, release 6.0.0. Here we
|
||||
describe the status of Clang in some detail, including major
|
||||
improvements from the previous release and new feature work. For the
|
||||
general LLVM release notes, see `the LLVM
|
||||
@ -24,7 +30,12 @@ For more information about Clang or LLVM, including information about the
|
||||
latest release, please see the `Clang Web Site <http://clang.llvm.org>`_ or the
|
||||
`LLVM Web Site <http://llvm.org>`_.
|
||||
|
||||
What's New in Clang 5.0.0?
|
||||
Note that if you are reading this file from a Subversion checkout or the
|
||||
main Clang web page, this document applies to the *next* release, not
|
||||
the current one. To see the release notes for a specific release, please
|
||||
see the `releases page <http://llvm.org/releases/>`_.
|
||||
|
||||
What's New in Clang 6.0.0?
|
||||
==========================
|
||||
|
||||
Some of the major new features and improvements to Clang are listed
|
||||
@ -35,48 +46,91 @@ sections with improvements to Clang's support for those languages.
|
||||
Major New Features
|
||||
------------------
|
||||
|
||||
C++ coroutines
|
||||
^^^^^^^^^^^^^^
|
||||
`C++ coroutines TS
|
||||
<http://open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4680.pdf>`_
|
||||
implementation has landed. Use ``-fcoroutines-ts -stdlib=libc++`` to enable
|
||||
coroutine support. Here is `an example
|
||||
<https://wandbox.org/permlink/Dth1IO5q8Oe31ew2>`_ to get you started.
|
||||
|
||||
- ...
|
||||
|
||||
Improvements to Clang's diagnostics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- ``-Wcast-qual`` was implemented for C++. C-style casts are now properly
|
||||
diagnosed.
|
||||
- ``-Wpragma-pack`` is a new warning that warns in the following cases:
|
||||
|
||||
- ``-Wunused-lambda-capture`` warns when a variable explicitly captured
|
||||
by a lambda is not used in the body of the lambda.
|
||||
- When a translation unit is missing terminating ``#pragma pack (pop)``
|
||||
directives.
|
||||
|
||||
- ``-Wstrict-prototypes`` is a new warning that warns about non-prototype
|
||||
function and block declarations and types in C and Objective-C.
|
||||
- When leaving an included file that changes the current alignment value,
|
||||
i.e. when the alignment before ``#include`` is different to the alignment
|
||||
after ``#include``.
|
||||
|
||||
- ``-Wunguarded-availability`` is a new warning that warns about uses of new
|
||||
APIs that were introduced in a system whose version is newer than the
|
||||
deployment target version. A new Objective-C expression ``@available`` has
|
||||
been introduced to perform system version checking at runtime. This warning
|
||||
is off by default to prevent unexpected warnings in existing projects.
|
||||
However, its less strict sibling ``-Wunguarded-availability-new`` is on by
|
||||
default. It warns about unguarded uses of APIs only when they were introduced
|
||||
in or after macOS 10.13, iOS 11, tvOS 11 or watchOS 4.
|
||||
- ``-Wpragma-pack-suspicious-include`` (disabled by default) warns on an
|
||||
``#include`` when the included file contains structures or unions affected by
|
||||
a non-default alignment that has been specified using a ``#pragma pack``
|
||||
directive prior to the ``#include``.
|
||||
|
||||
- The ``-Wdocumentation`` warning now allows the use of ``\param`` and
|
||||
``\returns`` documentation directives in the documentation comments for
|
||||
declarations with a function or a block pointer type.
|
||||
- ``-Wobjc-messaging-id`` is a new, non-default warning that warns about
|
||||
message sends to unqualified ``id`` in Objective-C. This warning is useful
|
||||
for projects that would like to avoid any potential future compiler
|
||||
errors/warnings, as the system frameworks might add a method with the same
|
||||
selector which could make the message send to ``id`` ambiguous.
|
||||
|
||||
- The compiler no longer warns about unreachable ``__builtin_unreachable``
|
||||
statements.
|
||||
- ``-Wtautological-compare`` now warns when comparing an unsigned integer and 0
|
||||
regardless of whether the constant is signed or unsigned."
|
||||
|
||||
- ``-Wtautological-compare`` now warns about comparing a signed integer and 0
|
||||
when the signed integer is coerced to an unsigned type for the comparison.
|
||||
``-Wsign-compare`` was adjusted not to warn in this case.
|
||||
|
||||
- ``-Wtautological-constant-compare`` is a new warning that warns on
|
||||
tautological comparisons between integer variable of the type ``T`` and the
|
||||
largest/smallest possible integer constant of that same type.
|
||||
|
||||
- For C code, ``-Wsign-compare``, ``-Wsign-conversion``,
|
||||
``-Wtautological-constant-compare`` and
|
||||
``-Wtautological-constant-out-of-range-compare`` were adjusted to use the
|
||||
underlying datatype of ``enum``.
|
||||
|
||||
- ``-Wnull-pointer-arithmetic`` now warns about performing pointer arithmetic
|
||||
on a null pointer. Such pointer arithmetic has an undefined behavior if the
|
||||
offset is nonzero. It also now warns about arithmetic on a null pointer
|
||||
treated as a cast from integer to pointer (GNU extension).
|
||||
|
||||
- ``-Wzero-as-null-pointer-constant`` was adjusted not to warn on null pointer
|
||||
constants that originate from system macros, except ``NULL`` macro.
|
||||
|
||||
Non-comprehensive list of changes in this release
|
||||
-------------------------------------------------
|
||||
|
||||
- Bitrig OS was merged back into OpenBSD, so Bitrig support has been
|
||||
removed from Clang/LLVM.
|
||||
|
||||
- The default value of _MSC_VER was raised from 1800 to 1911, making it
|
||||
compatible with the Visual Studio 2015 and 2017 C++ standard library headers.
|
||||
Users should generally expect this to be regularly raised to match the most
|
||||
recently released version of the Visual C++ compiler.
|
||||
|
||||
- clang now defaults to ``.init_array`` if no gcc installation can be found.
|
||||
If a gcc installation is found, it still prefers ``.ctors`` if the found
|
||||
gcc is older than 4.7.0.
|
||||
|
||||
- The new builtin preprocessor macros ``__is_target_arch``,
|
||||
``__is_target_vendor``, ``__is_target_os``, and ``__is_target_environment``
|
||||
can be used to to examine the individual components of the target triple.
|
||||
|
||||
New Compiler Flags
|
||||
------------------
|
||||
|
||||
- ``--autocomplete`` was implemented to obtain a list of flags and its arguments.
|
||||
This is used for shell autocompletion.
|
||||
- --autocomplete was implemented to obtain a list of flags and its arguments. This is used for shell autocompletion.
|
||||
|
||||
- The ``-fdouble-square-bracket-attributes`` and corresponding
|
||||
``-fno-double-square-bracket-attributes`` flags were added to enable or
|
||||
disable [[]] attributes in any language mode. Currently, only a limited
|
||||
number of attributes are supported outside of C++ mode. See the Clang
|
||||
attribute documentation for more information about which attributes are
|
||||
supported for each syntax.
|
||||
|
||||
- Added the ``-std=c17``, ``-std=gnu17``, and ``-std=iso9899:2017`` language
|
||||
mode flags for compatibility with GCC. This enables support for the next
|
||||
version of the C standard, expected to be published by ISO in 2018. The only
|
||||
difference between the ``-std=c17`` and ``-std=c11`` language modes is the
|
||||
value of the ``__STDC_VERSION__`` macro, as C17 is a bug fix release.
|
||||
|
||||
Deprecated Compiler Flags
|
||||
-------------------------
|
||||
@ -84,278 +138,194 @@ Deprecated Compiler Flags
|
||||
The following options are deprecated and ignored. They will be removed in
|
||||
future versions of Clang.
|
||||
|
||||
- ``-fslp-vectorize-aggressive`` used to enable the BB vectorizing pass. They have been superseeded
|
||||
by the normal SLP vectorizer.
|
||||
- ``-fno-slp-vectorize-aggressive`` used to be the default behavior of clang.
|
||||
- ...
|
||||
|
||||
New Pragmas in Clang
|
||||
-----------------------
|
||||
|
||||
- Clang now supports the ``clang attribute`` pragma that allows users to apply
|
||||
an attribute to multiple declarations.
|
||||
Clang now supports the ...
|
||||
|
||||
- ``pragma pack`` directives that are included in a precompiled header are now
|
||||
applied correctly to the declarations in the compilation unit that includes
|
||||
that precompiled header.
|
||||
|
||||
Attribute Changes in Clang
|
||||
--------------------------
|
||||
|
||||
- The ``overloadable`` attribute now allows at most one function with a given
|
||||
name to lack the ``overloadable`` attribute. This unmarked function will not
|
||||
have its name mangled.
|
||||
- The ``ms_abi`` attribute and the ``__builtin_ms_va_list`` types and builtins
|
||||
are now supported on AArch64.
|
||||
- Clang now supports the majority of its attributes under both the GNU-style
|
||||
spelling (``__attribute((name))``) and the double square-bracket spelling
|
||||
in the ``clang`` vendor namespace (``[[clang::name]]``). Attributes whose
|
||||
syntax is specified by some other standard (such as CUDA and OpenCL
|
||||
attributes) continue to follow their respective specification.
|
||||
|
||||
- Added the ``__has_c_attribute()`` builtin preprocessor macro which allows
|
||||
users to dynamically detect whether a double square-bracket attribute is
|
||||
supported in C mode. This attribute syntax can be enabled with the
|
||||
``-fdouble-square-bracket-attributes`` flag.
|
||||
|
||||
- The presence of __attribute__((availability(...))) on a declaration no longer
|
||||
implies default visibility for that declaration on macOS.
|
||||
|
||||
- ...
|
||||
|
||||
Windows Support
|
||||
---------------
|
||||
|
||||
Clang's support for building native Windows programs ...
|
||||
|
||||
|
||||
C Language Changes in Clang
|
||||
---------------------------
|
||||
|
||||
- Added near complete support for implicit scalar to vector conversion, a GNU
|
||||
C/C++ language extension. With this extension, the following code is
|
||||
considered valid:
|
||||
- ...
|
||||
|
||||
.. code-block:: c
|
||||
...
|
||||
|
||||
typedef unsigned v4i32 __attribute__((vector_size(16)));
|
||||
|
||||
v4i32 foo(v4i32 a) {
|
||||
// Here 5 is implicitly casted to an unsigned value and replicated into a
|
||||
// vector with as many elements as 'a'.
|
||||
return a + 5;
|
||||
}
|
||||
|
||||
The implicit conversion of a scalar value to a vector value--in the context of
|
||||
a vector expression--occurs when:
|
||||
|
||||
- The type of the vector is that of a ``__attribute__((vector_size(size)))``
|
||||
vector, not an OpenCL ``__attribute__((ext_vector_type(size)))`` vector type.
|
||||
|
||||
- The scalar value can be casted to that of the vector element's type without
|
||||
the loss of precision based on the type of the scalar and the type of the
|
||||
vector's elements.
|
||||
|
||||
- For compile time constant values, the above rule is weakened to consider the
|
||||
value of the scalar constant rather than the constant's type. However,
|
||||
for compatibility with GCC, floating point constants with precise integral
|
||||
representations are not implicitly converted to integer values.
|
||||
|
||||
Currently the basic integer and floating point types with the following
|
||||
operators are supported: ``+``, ``/``, ``-``, ``*``, ``%``, ``>``, ``<``,
|
||||
``>=``, ``<=``, ``==``, ``!=``, ``&``, ``|``, ``^`` and the corresponding
|
||||
assignment operators where applicable.
|
||||
C11 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
...
|
||||
|
||||
C++ Language Changes in Clang
|
||||
-----------------------------
|
||||
|
||||
- We expect this to be the last Clang release that defaults to ``-std=gnu++98``
|
||||
when using the GCC-compatible ``clang++`` driver. From Clang 6 onwards we
|
||||
expect to use ``-std=gnu++14`` or a later standard by default, to match the
|
||||
behavior of recent GCC releases. Users are encouraged to change their build
|
||||
files to explicitly specify their desired C++ standard.
|
||||
- Clang's default C++ dialect is now ``gnu++14`` instead of ``gnu++98``. This
|
||||
means Clang will by default accept code using features from C++14 and
|
||||
conforming GNU extensions. Projects incompatible with C++14 can add
|
||||
``-std=gnu++98`` to their build settings to restore the previous behaviour.
|
||||
|
||||
- Support for the C++17 standard has been completed. This mode can be enabled
|
||||
using ``-std=c++17`` (the old flag ``-std=c++1z`` is still supported for
|
||||
compatibility).
|
||||
C++1z Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- When targeting a platform that uses the Itanium C++ ABI, Clang implements a
|
||||
`recent change to the ABI`__ that passes objects of class type indirectly if they
|
||||
have a non-trivial move constructor. Previous versions of Clang only
|
||||
considered the copy constructor, resulting in an ABI change in rare cases,
|
||||
but GCC has already implemented this change for several releases.
|
||||
This affects all targets other than Windows and PS4. You can opt out of this
|
||||
ABI change with ``-fclang-abi-compat=4.0``.
|
||||
|
||||
- As mentioned in `C Language Changes in Clang`_, Clang's support for
|
||||
implicit scalar to vector conversions also applies to C++. Additionally
|
||||
the following operators are also supported: ``&&`` and ``||``.
|
||||
|
||||
.. __: https://github.com/itanium-cxx-abi/cxx-abi/commit/7099637aba11fed6bdad7ee65bf4fd3f97fbf076
|
||||
...
|
||||
|
||||
Objective-C Language Changes in Clang
|
||||
-------------------------------------
|
||||
|
||||
- Clang now guarantees that a ``readwrite`` property is synthesized when an
|
||||
ambiguous property (i.e. a property that's declared in multiple protocols)
|
||||
is synthesized. The ``-Wprotocol-property-synthesis-ambiguity`` warning that
|
||||
warns about incompatible property types is now promoted to an error when
|
||||
there's an ambiguity between ``readwrite`` and ``readonly`` properties.
|
||||
|
||||
- Clang now prohibits synthesis of ambiguous properties with incompatible
|
||||
explicit property attributes. The following property attributes are
|
||||
checked for differences: ``copy``, ``retain``/``strong``, ``atomic``,
|
||||
``getter`` and ``setter``.
|
||||
...
|
||||
|
||||
OpenCL C Language Changes in Clang
|
||||
----------------------------------
|
||||
|
||||
Various bug fixes and improvements:
|
||||
...
|
||||
|
||||
- Extended OpenCL-related Clang tests.
|
||||
OpenMP Support in Clang
|
||||
----------------------------------
|
||||
|
||||
- Improved diagnostics across several areas: scoped address space
|
||||
qualified variables, function pointers, atomics, type rank for overloading,
|
||||
block captures, ``reserve_id_t``.
|
||||
...
|
||||
|
||||
- Several address space related fixes for constant address space function scope variables,
|
||||
IR generation, mangling of ``generic`` and alloca (post-fix from general Clang
|
||||
refactoring of address spaces).
|
||||
Internal API Changes
|
||||
--------------------
|
||||
|
||||
- Several improvements in extensions: fixed OpenCL version for ``cl_khr_mipmap_image``,
|
||||
added missing ``cl_khr_3d_image_writes``.
|
||||
These are major API changes that have happened since the 4.0.0 release of
|
||||
Clang. If upgrading an external codebase that uses Clang as a library,
|
||||
this section should help get you past the largest hurdles of upgrading.
|
||||
|
||||
- Improvements in ``enqueue_kernel``, especially the implementation of ``ndrange_t`` and blocks.
|
||||
- ...
|
||||
|
||||
- OpenCL type related fixes: global samplers, the ``pipe_t`` size, internal type redefinition,
|
||||
and type compatibility checking in ternary and other operations.
|
||||
AST Matchers
|
||||
------------
|
||||
|
||||
- The OpenCL header has been extended with missing extension guards, and direct mapping of ``as_type``
|
||||
to ``__builtin_astype``.
|
||||
The hasDeclaration matcher now works the same for Type and QualType and only
|
||||
ever looks through one level of sugaring in a limited number of cases.
|
||||
|
||||
- Fixed ``kernel_arg_type_qual`` and OpenCL/SPIR version in metadata.
|
||||
There are two main patterns affected by this:
|
||||
|
||||
- Added proper use of the kernel calling convention to various targets.
|
||||
- qualType(hasDeclaration(recordDecl(...))): previously, we would look through
|
||||
sugar like TypedefType to get at the underlying recordDecl; now, we need
|
||||
to explicitly remove the sugaring:
|
||||
qualType(hasUnqualifiedDesugaredType(hasDeclaration(recordDecl(...))))
|
||||
|
||||
The following new functionalities have been added:
|
||||
- hasType(recordDecl(...)): hasType internally uses hasDeclaration; previously,
|
||||
this matcher used to match for example TypedefTypes of the RecordType, but
|
||||
after the change they don't; to fix, use:
|
||||
|
||||
- Added documentation on OpenCL to Clang user manual.
|
||||
::
|
||||
hasType(hasUnqualifiedDesugaredType(
|
||||
recordType(hasDeclaration(recordDecl(...)))))
|
||||
|
||||
- Extended Clang builtins with required ``cl_khr_subgroups`` support.
|
||||
|
||||
- Add ``intel_reqd_sub_group_size`` attribute support.
|
||||
|
||||
- Added OpenCL types to ``CIndex``.
|
||||
- templateSpecializationType(hasDeclaration(classTemplateDecl(...))):
|
||||
previously, we would directly match the underlying ClassTemplateDecl;
|
||||
now, we can explicitly match the ClassTemplateSpecializationDecl, but that
|
||||
requires to explicitly get the ClassTemplateDecl:
|
||||
|
||||
::
|
||||
templateSpecializationType(hasDeclaration(
|
||||
classTemplateSpecializationDecl(
|
||||
hasSpecializedTemplate(classTemplateDecl(...)))))
|
||||
|
||||
clang-format
|
||||
------------
|
||||
|
||||
* Option **BreakBeforeInheritanceComma** added to break before ``:`` and ``,`` in case of
|
||||
multiple inheritance in a class declaration. Enabled by default in the Mozilla coding style.
|
||||
* Option *IndentPPDirectives* added to indent preprocessor directives on
|
||||
conditionals.
|
||||
|
||||
+---------------------+----------------------------------------+
|
||||
| true | false |
|
||||
+=====================+========================================+
|
||||
| .. code-block:: c++ | .. code-block:: c++ |
|
||||
| | |
|
||||
| class MyClass | class MyClass : public X, public Y { |
|
||||
| : public X | }; |
|
||||
| , public Y { | |
|
||||
| }; | |
|
||||
+---------------------+----------------------------------------+
|
||||
+----------------------+----------------------+
|
||||
| Before | After |
|
||||
+======================+======================+
|
||||
| .. code-block:: c++ | .. code-block:: c++ |
|
||||
| | |
|
||||
| #if FOO | #if FOO |
|
||||
| #if BAR | # if BAR |
|
||||
| #include <foo> | # include <foo> |
|
||||
| #endif | # endif |
|
||||
| #endif | #endif |
|
||||
+----------------------+----------------------+
|
||||
|
||||
* Align block comment decorations.
|
||||
* Option -verbose added to the command line.
|
||||
Shows the list of processed files.
|
||||
|
||||
+----------------------+---------------------+
|
||||
| Before | After |
|
||||
+======================+=====================+
|
||||
| .. code-block:: c++ | .. code-block:: c++ |
|
||||
| | |
|
||||
| /* line 1 | /* line 1 |
|
||||
| * line 2 | * line 2 |
|
||||
| */ | */ |
|
||||
+----------------------+---------------------+
|
||||
* Option *IncludeBlocks* added to merge and regroup multiple ``#include`` blocks during sorting.
|
||||
|
||||
* The :doc:`ClangFormatStyleOptions` documentation provides detailed examples for most options.
|
||||
|
||||
* Namespace end comments are now added or updated automatically.
|
||||
|
||||
+---------------------+---------------------+
|
||||
| Before | After |
|
||||
+=====================+=====================+
|
||||
| .. code-block:: c++ | .. code-block:: c++ |
|
||||
| | |
|
||||
| namespace A { | namespace A { |
|
||||
| int i; | int i; |
|
||||
| int j; | int j; |
|
||||
| } | } // namespace A |
|
||||
+---------------------+---------------------+
|
||||
|
||||
* Comment reflow support added. Overly long comment lines will now be reflown with the rest of
|
||||
the paragraph instead of just broken. Option **ReflowComments** added and enabled by default.
|
||||
+-------------------------+-------------------------+-------------------------+
|
||||
| Before (Preserve) | Merge | Regroup |
|
||||
+=========================+=========================+=========================+
|
||||
| .. code-block:: c++ | .. code-block:: c++ | .. code-block:: c++ |
|
||||
| | | |
|
||||
| #include "b.h" | #include "a.h" | #include "a.h" |
|
||||
| | #include "b.h" | #include "b.h" |
|
||||
| #include "a.b" | #include <lib/main.h> | |
|
||||
| #include <lib/main.h> | | #include <lib/main.h> |
|
||||
+-------------------------+-------------------------+-------------------------+
|
||||
|
||||
libclang
|
||||
--------
|
||||
|
||||
- Libclang now provides code-completion results for more C++ constructs
|
||||
and keywords. The following keywords/identifiers are now included in the
|
||||
code-completion results: ``static_assert``, ``alignas``, ``constexpr``,
|
||||
``final``, ``noexcept``, ``override`` and ``thread_local``.
|
||||
...
|
||||
|
||||
- Libclang now provides code-completion results for members from dependent
|
||||
classes. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template<typename T>
|
||||
void appendValue(std::vector<T> &dest, const T &value) {
|
||||
dest. // Relevant completion results are now shown after '.'
|
||||
}
|
||||
|
||||
Note that code-completion results are still not provided when the member
|
||||
expression includes a dependent base expression. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
template<typename T>
|
||||
void appendValue(std::vector<std::vector<T>> &dest, const T &value) {
|
||||
dest.at(0). // Libclang fails to provide completion results after '.'
|
||||
}
|
||||
|
||||
Static Analyzer
|
||||
---------------
|
||||
|
||||
- The static analyzer now supports using the
|
||||
`z3 theorem prover <https://github.com/z3prover/z3>`_ from Microsoft Research
|
||||
as an external constraint solver. This allows reasoning over more complex
|
||||
queries, but performance is ~15x slower than the default range-based
|
||||
constraint solver. To enable the z3 solver backend, clang must be built with
|
||||
the ``CLANG_ANALYZER_BUILD_Z3=ON`` option, and the
|
||||
``-Xanalyzer -analyzer-constraints=z3`` arguments passed at runtime.
|
||||
- Static Analyzer can now properly detect and diagnose unary pre-/post-
|
||||
increment/decrement on an uninitialized value.
|
||||
|
||||
...
|
||||
|
||||
Undefined Behavior Sanitizer (UBSan)
|
||||
------------------------------------
|
||||
|
||||
- The Undefined Behavior Sanitizer has a new check for pointer overflow. This
|
||||
check is on by default. The flag to control this functionality is
|
||||
``-fsanitize=pointer-overflow``.
|
||||
* A minimal runtime is now available. It is suitable for use in production
|
||||
environments, and has a small attack surface. It only provides very basic
|
||||
issue logging and deduplication, and does not support ``-fsanitize=vptr``
|
||||
checking.
|
||||
|
||||
Pointer overflow is an indicator of undefined behavior: when a pointer
|
||||
indexing expression wraps around the address space, or produces other
|
||||
unexpected results, its result may not point to a valid object.
|
||||
Core Analysis Improvements
|
||||
==========================
|
||||
|
||||
- UBSan has several new checks which detect violations of nullability
|
||||
annotations. These checks are off by default. The flag to control this group
|
||||
of checks is ``-fsanitize=nullability``. The checks can be individially enabled
|
||||
by ``-fsanitize=nullability-arg`` (which checks calls),
|
||||
``-fsanitize=nullability-assign`` (which checks assignments), and
|
||||
``-fsanitize=nullability-return`` (which checks return statements).
|
||||
- ...
|
||||
|
||||
- UBSan can now detect invalid loads from bitfields and from ObjC BOOLs.
|
||||
|
||||
- UBSan can now avoid emitting unnecessary type checks in C++ class methods and
|
||||
in several other cases where the result is known at compile-time. UBSan can
|
||||
also avoid emitting unnecessary overflow checks in arithmetic expressions
|
||||
with promoted integer operands.
|
||||
New Issues Found
|
||||
================
|
||||
|
||||
- ...
|
||||
|
||||
Python Binding Changes
|
||||
----------------------
|
||||
|
||||
Python bindings now support both Python 2 and Python 3.
|
||||
|
||||
The following methods have been added:
|
||||
|
||||
- ``is_scoped_enum`` has been added to ``Cursor``.
|
||||
|
||||
- ``exception_specification_kind`` has been added to ``Cursor``.
|
||||
|
||||
- ``get_address_space`` has been added to ``Type``.
|
||||
|
||||
- ``get_typedef_name`` has been added to ``Type``.
|
||||
|
||||
- ``get_exception_specification_kind`` has been added to ``Type``.
|
||||
- ...
|
||||
|
||||
Significant Known Problems
|
||||
==========================
|
||||
|
||||
Additional Information
|
||||
======================
|
||||
|
@ -119,6 +119,53 @@ Example:
|
||||
guard: 0x71bcdc 4 PC 0x4ecdc7 in main trace-pc-guard-example.cc:4:17
|
||||
guard: 0x71bcd0 1 PC 0x4ecd20 in foo() trace-pc-guard-example.cc:2:14
|
||||
|
||||
Inline 8bit-counters
|
||||
====================
|
||||
|
||||
**Experimental, may change or disappear in future**
|
||||
|
||||
With ``-fsanitize-coverage=inline-8bit-counters`` the compiler will insert
|
||||
inline counter increments on every edge.
|
||||
This is similar to ``-fsanitize-coverage=trace-pc-guard`` but instead of a
|
||||
callback the instrumentation simply increments a counter.
|
||||
|
||||
Users need to implement a single function to capture the counters at startup.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
extern "C"
|
||||
void __sanitizer_cov_8bit_counters_init(char *start, char *end) {
|
||||
// [start,end) is the array of 8-bit counters created for the current DSO.
|
||||
// Capture this array in order to read/modify the counters.
|
||||
}
|
||||
|
||||
PC-Table
|
||||
========
|
||||
|
||||
**Experimental, may change or disappear in future**
|
||||
|
||||
With ``-fsanitize-coverage=pc-table`` the compiler will create a table of
|
||||
instrumented PCs. Requires either ``-fsanitize-coverage=inline-8bit-counters`` or
|
||||
``-fsanitize-coverage=trace-pc-guard``.
|
||||
|
||||
Users need to implement a single function to capture the PC table at startup:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
extern "C"
|
||||
void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
|
||||
const uintptr_t *pcs_end) {
|
||||
// [pcs_beg,pcs_end) is the array of ptr-sized integers representing
|
||||
// pairs [PC,PCFlags] for every instrumented block in the current DSO.
|
||||
// Capture this array in order to read the PCs and their Flags.
|
||||
// The number of PCs and PCFlags for a given DSO is the same as the number
|
||||
// of 8-bit counters (-fsanitize-coverage=inline-8bit-counters) or
|
||||
// trace_pc_guard callbacks (-fsanitize-coverage=trace-pc-guard)
|
||||
// A PCFlags describes the basic block:
|
||||
// * bit0: 1 if the block is the function entry block, 0 otherwise.
|
||||
}
|
||||
|
||||
|
||||
Tracing PCs
|
||||
===========
|
||||
|
||||
@ -131,7 +178,6 @@ by the user.
|
||||
This mechanism is used for fuzzing the Linux kernel
|
||||
(https://github.com/google/syzkaller).
|
||||
|
||||
|
||||
Instrumentation points
|
||||
======================
|
||||
Sanitizer Coverage offers different levels of instrumentation.
|
||||
@ -211,6 +257,14 @@ the `LLVM GEP instructions <http://llvm.org/docs/GetElementPtr.html>`_
|
||||
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2);
|
||||
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2);
|
||||
|
||||
// Called before a comparison instruction if exactly one of the arguments is constant.
|
||||
// Arg1 and Arg2 are arguments of the comparison, Arg1 is a compile-time constant.
|
||||
// These callbacks are emitted by -fsanitize-coverage=trace-cmp since 2017-08-11
|
||||
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2);
|
||||
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2);
|
||||
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2);
|
||||
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2);
|
||||
|
||||
// Called before a switch statement.
|
||||
// Val is the switch operand.
|
||||
// Cases[0] is the number of case constants.
|
||||
@ -227,9 +281,6 @@ the `LLVM GEP instructions <http://llvm.org/docs/GetElementPtr.html>`_
|
||||
// for every non-constant array index.
|
||||
void __sanitizer_cov_trace_gep(uintptr_t Idx);
|
||||
|
||||
|
||||
This interface is a subject to change.
|
||||
|
||||
Default implementation
|
||||
======================
|
||||
|
||||
|
@ -51,14 +51,23 @@ Example
|
||||
Format
|
||||
======
|
||||
|
||||
Each line contains an entity type, followed by a colon and a regular
|
||||
expression, specifying the names of the entities, optionally followed by
|
||||
an equals sign and a tool-specific category. Empty lines and lines starting
|
||||
with "#" are ignored. The meanining of ``*`` in regular expression for entity
|
||||
names is different - it is treated as in shell wildcarding. Two generic
|
||||
entity types are ``src`` and ``fun``, which allow user to add, respectively,
|
||||
source files and functions to special case list. Some sanitizer tools may
|
||||
introduce custom entity types - refer to tool-specific docs.
|
||||
Blacklists consist of entries, optionally grouped into sections. Empty lines and
|
||||
lines starting with "#" are ignored.
|
||||
|
||||
Section names are regular expressions written in square brackets that denote
|
||||
which sanitizer the following entries apply to. For example, ``[address]``
|
||||
specifies AddressSanitizer while ``[cfi-vcall|cfi-icall]`` specifies Control
|
||||
Flow Integrity virtual and indirect call checking. Entries without a section
|
||||
will be placed under the ``[*]`` section applying to all enabled sanitizers.
|
||||
|
||||
Entries contain an entity type, followed by a colon and a regular expression,
|
||||
specifying the names of the entities, optionally followed by an equals sign and
|
||||
a tool-specific category, e.g. ``fun:*ExampleFunc=example_category``. The
|
||||
meaning of ``*`` in regular expression for entity names is different - it is
|
||||
treated as in shell wildcarding. Two generic entity types are ``src`` and
|
||||
``fun``, which allow users to specify source files and functions, respectively.
|
||||
Some sanitizer tools may introduce custom entity types and categories - refer to
|
||||
tool-specific docs.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@ -77,3 +86,10 @@ introduce custom entity types - refer to tool-specific docs.
|
||||
fun:*BadFunction*
|
||||
# Specific sanitizer tools may introduce categories.
|
||||
src:/special/path/*=special_sources
|
||||
# Sections can be used to limit blacklist entries to specific sanitizers
|
||||
[address]
|
||||
fun:*BadASanFunc*
|
||||
# Section names are regular expressions
|
||||
[cfi-vcall|cfi-icall]
|
||||
fun:*BadCfiCall
|
||||
# Entries without sections are placed into [*] and apply to all sanitizers
|
||||
|
@ -63,7 +63,7 @@ ThinLTO is currently supported for the following linkers:
|
||||
- **ld64**:
|
||||
Starting with `Xcode 8 <https://developer.apple.com/xcode/>`_.
|
||||
- **lld**:
|
||||
Starting with r284050 (ELF only).
|
||||
Starting with r284050 for ELF, r298942 for COFF.
|
||||
|
||||
Usage
|
||||
=====
|
||||
@ -78,6 +78,13 @@ To utilize ThinLTO, simply add the -flto=thin option to compile and link. E.g.
|
||||
% clang -flto=thin -O2 file1.c file2.c -c
|
||||
% clang -flto=thin -O2 file1.o file2.o -o a.out
|
||||
|
||||
When using lld-link, the -flto option need only be added to the compile step:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
% clang-cl -flto=thin -O2 -c file1.c file2.c
|
||||
% lld-link /out:a.exe file1.obj file2.obj
|
||||
|
||||
As mentioned earlier, by default the linkers will launch the ThinLTO backend
|
||||
threads in parallel, passing the resulting native object files back to the
|
||||
linker for the final native link. As such, the usage model the same as
|
||||
@ -111,6 +118,8 @@ be reduced to ``N`` via:
|
||||
``-Wl,-mllvm,-threads=N``
|
||||
- lld:
|
||||
``-Wl,--thinlto-jobs=N``
|
||||
- lld-link:
|
||||
``/opt:lldltojobs=N``
|
||||
|
||||
Incremental
|
||||
-----------
|
||||
@ -119,23 +128,29 @@ Incremental
|
||||
ThinLTO supports fast incremental builds through the use of a cache,
|
||||
which currently must be enabled through a linker option.
|
||||
|
||||
- gold (as of LLVM r279883):
|
||||
- gold (as of LLVM 4.0):
|
||||
``-Wl,-plugin-opt,cache-dir=/path/to/cache``
|
||||
- ld64 (support in clang 3.9 and Xcode 8):
|
||||
``-Wl,-cache_path_lto,/path/to/cache``
|
||||
- lld (as of LLVM r296702):
|
||||
- ELF lld (as of LLVM 5.0):
|
||||
``-Wl,--thinlto-cache-dir=/path/to/cache``
|
||||
- COFF lld-link (as of LLVM 6.0):
|
||||
``/lldltocache:/path/to/cache``
|
||||
|
||||
Cache Pruning
|
||||
-------------
|
||||
|
||||
To help keep the size of the cache under control, ThinLTO supports cache
|
||||
pruning. Cache pruning is supported with ld64 and ELF lld, but currently only
|
||||
ELF lld allows you to control the policy with a policy string. The cache
|
||||
policy must be specified with a linker option.
|
||||
pruning. Cache pruning is supported with gold, ld64 and ELF and COFF lld, but
|
||||
currently only gold, ELF and COFF lld allow you to control the policy with a
|
||||
policy string. The cache policy must be specified with a linker option.
|
||||
|
||||
- ELF lld (as of LLVM r298036):
|
||||
- gold (as of LLVM 6.0):
|
||||
``-Wl,-plugin-opt,cache-policy=POLICY``
|
||||
- ELF lld (as of LLVM 5.0):
|
||||
``-Wl,--thinlto-cache-policy,POLICY``
|
||||
- COFF lld-link (as of LLVM 6.0):
|
||||
``/lldltocachepolicy:POLICY``
|
||||
|
||||
A policy string is a series of key-value pairs separated by ``:`` characters.
|
||||
Possible key-value pairs are:
|
||||
@ -158,6 +173,10 @@ Possible key-value pairs are:
|
||||
``cache_size_bytes=1g`` on its own will cause both the 1GB and default 75%
|
||||
policies to be applied unless the default ``cache_size`` is overridden.
|
||||
|
||||
- ``cache_size_files=X``:
|
||||
Set the maximum number of files in the cache directory. Set to 0 to indicate
|
||||
no limit. The default is 1000000 files.
|
||||
|
||||
- ``prune_after=Xs``, ``prune_after=Xm``, ``prune_after=Xh``: Sets the
|
||||
expiration time for cache files to ``X`` seconds (or minutes, hours
|
||||
respectively). When a file hasn't been accessed for ``prune_after`` seconds,
|
||||
@ -183,13 +202,20 @@ To bootstrap clang/LLVM with ThinLTO, follow these steps:
|
||||
when configuring the bootstrap compiler build:
|
||||
|
||||
* ``-DLLVM_ENABLE_LTO=Thin``
|
||||
* ``-DLLVM_PARALLEL_LINK_JOBS=1``
|
||||
(since the ThinLTO link invokes parallel backend jobs)
|
||||
* ``-DCMAKE_C_COMPILER=/path/to/host/clang``
|
||||
* ``-DCMAKE_CXX_COMPILER=/path/to/host/clang++``
|
||||
* ``-DCMAKE_RANLIB=/path/to/host/llvm-ranlib``
|
||||
* ``-DCMAKE_AR=/path/to/host/llvm-ar``
|
||||
|
||||
Or, on Windows:
|
||||
|
||||
* ``-DLLVM_ENABLE_LTO=Thin``
|
||||
* ``-DCMAKE_C_COMPILER=/path/to/host/clang-cl.exe``
|
||||
* ``-DCMAKE_CXX_COMPILER=/path/to/host/clang-cl.exe``
|
||||
* ``-DCMAKE_LINKER=/path/to/host/lld-link.exe``
|
||||
* ``-DCMAKE_RANLIB=/path/to/host/llvm-ranlib.exe``
|
||||
* ``-DCMAKE_AR=/path/to/host/llvm-ar.exe``
|
||||
|
||||
#. To use additional linker arguments for controlling the backend
|
||||
parallelism_ or enabling incremental_ builds of the bootstrap compiler,
|
||||
after configuring the build, modify the resulting CMakeCache.txt file in the
|
||||
|
@ -222,7 +222,7 @@ Unwind library
|
||||
|
||||
The unwind library provides a family of ``_Unwind_*`` functions implementing
|
||||
the language-neutral stack unwinding portion of the Itanium C++ ABI
|
||||
(`Level I <http://mentorembedded.github.io/cxx-abi/abi-eh.html#base-abi>`_).
|
||||
(`Level I <http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-abi>`_).
|
||||
It is a dependency of the C++ ABI library, and sometimes is a dependency
|
||||
of other runtimes.
|
||||
|
||||
@ -288,9 +288,9 @@ C++ ABI library
|
||||
The C++ ABI library provides an implementation of the library portion of
|
||||
the Itanium C++ ABI, covering both the
|
||||
`support functionality in the main Itanium C++ ABI document
|
||||
<http://mentorembedded.github.io/cxx-abi/abi.html>`_ and
|
||||
<http://itanium-cxx-abi.github.io/cxx-abi/abi.html>`_ and
|
||||
`Level II of the exception handling support
|
||||
<http://mentorembedded.github.io/cxx-abi/abi-eh.html#cxx-abi>`_.
|
||||
<http://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#cxx-abi>`_.
|
||||
References to the functions and objects in this library are implicitly
|
||||
generated by Clang when compiling C++ code.
|
||||
|
||||
|
@ -75,6 +75,7 @@ Available checks are:
|
||||
of a misaligned reference.
|
||||
- ``-fsanitize=bool``: Load of a ``bool`` value which is neither
|
||||
``true`` nor ``false``.
|
||||
- ``-fsanitize=builtin``: Passing invalid values to compiler builtins.
|
||||
- ``-fsanitize=bounds``: Out of bounds array indexing, in cases
|
||||
where the array bound can be statically determined.
|
||||
- ``-fsanitize=enum``: Load of a value of an enumerated type which
|
||||
@ -86,7 +87,8 @@ Available checks are:
|
||||
- ``-fsanitize=float-divide-by-zero``: Floating point division by
|
||||
zero.
|
||||
- ``-fsanitize=function``: Indirect call of a function through a
|
||||
function pointer of the wrong type (Linux, C++ and x86/x86_64 only).
|
||||
function pointer of the wrong type (Darwin/Linux, C++ and x86/x86_64
|
||||
only).
|
||||
- ``-fsanitize=integer-divide-by-zero``: Integer division by zero.
|
||||
- ``-fsanitize=nonnull-attribute``: Passing null pointer as a function
|
||||
parameter which is declared to never be null.
|
||||
@ -130,11 +132,11 @@ Available checks are:
|
||||
it is often unintentional, so UBSan offers to catch it.
|
||||
- ``-fsanitize=vla-bound``: A variable-length array whose bound
|
||||
does not evaluate to a positive value.
|
||||
- ``-fsanitize=vptr``: Use of an object whose vptr indicates that
|
||||
it is of the wrong dynamic type, or that its lifetime has not
|
||||
begun or has ended. Incompatible with ``-fno-rtti``. Link must
|
||||
be performed by ``clang++``, not ``clang``, to make sure C++-specific
|
||||
parts of the runtime library and C++ standard libraries are present.
|
||||
- ``-fsanitize=vptr``: Use of an object whose vptr indicates that it is of
|
||||
the wrong dynamic type, or that its lifetime has not begun or has ended.
|
||||
Incompatible with ``-fno-rtti``. Link must be performed by ``clang++``, not
|
||||
``clang``, to make sure C++-specific parts of the runtime library and C++
|
||||
standard libraries are present.
|
||||
|
||||
You can also use the following check groups:
|
||||
- ``-fsanitize=undefined``: All of the checks listed above other than
|
||||
@ -154,6 +156,19 @@ Volatile
|
||||
The ``null``, ``alignment``, ``object-size``, and ``vptr`` checks do not apply
|
||||
to pointers to types with the ``volatile`` qualifier.
|
||||
|
||||
Minimal Runtime
|
||||
===============
|
||||
|
||||
There is a minimal UBSan runtime available suitable for use in production
|
||||
environments. This runtime has a small attack surface. It only provides very
|
||||
basic issue logging and deduplication, and does not support ``-fsanitize=vptr``
|
||||
checking.
|
||||
|
||||
To use the minimal runtime, add ``-fsanitize-minimal-runtime`` to the clang
|
||||
command line options. For example, if you're used to compiling with
|
||||
``-fsanitize=undefined``, you could enable the minimal runtime with
|
||||
``-fsanitize=undefined -fsanitize-minimal-runtime``.
|
||||
|
||||
Stack traces and report symbolization
|
||||
=====================================
|
||||
If you want UBSan to print symbolized stack trace for each error report, you
|
||||
|
@ -107,7 +107,7 @@ Options to Control Error and Warning Messages
|
||||
|
||||
.. option:: -Wno-error=foo
|
||||
|
||||
Turn warning "foo" into an warning even if :option:`-Werror` is specified.
|
||||
Turn warning "foo" into a warning even if :option:`-Werror` is specified.
|
||||
|
||||
.. option:: -Wfoo
|
||||
|
||||
@ -677,7 +677,7 @@ Current limitations
|
||||
|
||||
Other Options
|
||||
-------------
|
||||
Clang options that that don't fit neatly into other categories.
|
||||
Clang options that don't fit neatly into other categories.
|
||||
|
||||
.. option:: -MV
|
||||
|
||||
@ -1147,6 +1147,11 @@ are listed below.
|
||||
the behavior of sanitizers in the ``cfi`` group to allow checking
|
||||
of cross-DSO virtual and indirect calls.
|
||||
|
||||
.. option:: -fsanitize-cfi-icall-generalize-pointers
|
||||
|
||||
Generalize pointers in return and argument types in function type signatures
|
||||
checked by Control Flow Integrity indirect call checking. See
|
||||
:doc:`ControlFlowIntegrity` for more details.
|
||||
|
||||
.. option:: -fstrict-vtable-pointers
|
||||
|
||||
@ -1438,7 +1443,7 @@ Sample Profile Text Format
|
||||
|
||||
This section describes the ASCII text format for sampling profiles. It is,
|
||||
arguably, the easiest one to generate. If you are interested in generating any
|
||||
of the other two, consult the ``ProfileData`` library in in LLVM's source tree
|
||||
of the other two, consult the ``ProfileData`` library in LLVM's source tree
|
||||
(specifically, ``include/llvm/ProfileData/SampleProfReader.h``).
|
||||
|
||||
.. code-block:: console
|
||||
@ -1454,7 +1459,7 @@ of the other two, consult the ``ProfileData`` library in in LLVM's source tree
|
||||
offsetB[.discriminator]: fnB:num_of_total_samples
|
||||
offsetB1[.discriminator]: number_of_samples [fn11:num fn12:num ... ]
|
||||
|
||||
This is a nested tree in which the identation represents the nesting level
|
||||
This is a nested tree in which the indentation represents the nesting level
|
||||
of the inline stack. There are no blank lines in the file. And the spacing
|
||||
within a single line is fixed. Additional spaces will result in an error
|
||||
while reading the file.
|
||||
|
@ -74,7 +74,7 @@ inspects expressions.)
|
||||
ExprInspection checks
|
||||
---------------------
|
||||
|
||||
- void clang_analyzer_eval(bool);
|
||||
- ``void clang_analyzer_eval(bool);``
|
||||
|
||||
Prints TRUE if the argument is known to have a non-zero value, FALSE if the
|
||||
argument is known to have a zero or null value, and UNKNOWN if the argument
|
||||
@ -93,7 +93,7 @@ ExprInspection checks
|
||||
clang_analyzer_eval(x); // expected-warning{{TRUE}}
|
||||
|
||||
|
||||
- void clang_analyzer_checkInlined(bool);
|
||||
- ``void clang_analyzer_checkInlined(bool);``
|
||||
|
||||
If a call occurs within an inlined function, prints TRUE or FALSE according to
|
||||
the value of its argument. If a call occurs outside an inlined function,
|
||||
@ -125,7 +125,7 @@ ExprInspection checks
|
||||
clang_analyzer_eval(value == 42); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
- void clang_analyzer_warnIfReached();
|
||||
- ``void clang_analyzer_warnIfReached();``
|
||||
|
||||
Generate a warning if this line of code gets reached by the analyzer.
|
||||
|
||||
@ -138,7 +138,7 @@ ExprInspection checks
|
||||
clang_analyzer_warnIfReached(); // no-warning
|
||||
}
|
||||
|
||||
- void clang_analyzer_numTimesReached();
|
||||
- ``void clang_analyzer_numTimesReached();``
|
||||
|
||||
Same as above, but include the number of times this call expression
|
||||
gets reached by the analyzer during the current analysis.
|
||||
@ -149,7 +149,7 @@ ExprInspection checks
|
||||
clang_analyzer_numTimesReached(); // expected-warning{{3}}
|
||||
}
|
||||
|
||||
- void clang_analyzer_warnOnDeadSymbol(int);
|
||||
- ``void clang_analyzer_warnOnDeadSymbol(int);``
|
||||
|
||||
Subscribe for a delayed warning when the symbol that represents the value of
|
||||
the argument is garbage-collected by the analyzer.
|
||||
@ -173,7 +173,7 @@ ExprInspection checks
|
||||
} while(0); // expected-warning{{SYMBOL DEAD}}
|
||||
|
||||
|
||||
- void clang_analyzer_explain(a single argument of any type);
|
||||
- ``void clang_analyzer_explain(a single argument of any type);``
|
||||
|
||||
This function explains the value of its argument in a human-readable manner
|
||||
in the warning message. You can make as many overrides of its prototype
|
||||
@ -197,7 +197,7 @@ ExprInspection checks
|
||||
clang_analyzer_explain(ptr); // expected-warning{{memory address '0'}}
|
||||
}
|
||||
|
||||
- void clang_analyzer_dump(a single argument of any type);
|
||||
- ``void clang_analyzer_dump( /* a single argument of any type */);``
|
||||
|
||||
Similar to clang_analyzer_explain, but produces a raw dump of the value,
|
||||
same as SVal::dump().
|
||||
@ -209,7 +209,7 @@ ExprInspection checks
|
||||
clang_analyzer_dump(x); // expected-warning{{reg_$0<x>}}
|
||||
}
|
||||
|
||||
- size_t clang_analyzer_getExtent(void *);
|
||||
- ``size_t clang_analyzer_getExtent(void *);``
|
||||
|
||||
This function returns the value that represents the extent of a memory region
|
||||
pointed to by the argument. This value is often difficult to obtain otherwise,
|
||||
@ -226,7 +226,7 @@ ExprInspection checks
|
||||
clang_analyzer_explain(ys); // expected-warning{{'8'}}
|
||||
}
|
||||
|
||||
- void clang_analyzer_printState();
|
||||
- ``void clang_analyzer_printState();``
|
||||
|
||||
Dumps the current ProgramState to the stderr. Quickly lookup the program state
|
||||
at any execution point without ViewExplodedGraph or re-compiling the program.
|
||||
@ -242,6 +242,19 @@ ExprInspection checks
|
||||
clang_analyzer_printState(); // Read the stderr!
|
||||
}
|
||||
|
||||
- ``void clang_analyzer_hashDump(int);``
|
||||
|
||||
The analyzer can generate a hash to identify reports. To debug what information
|
||||
is used to calculate this hash it is possible to dump the hashed string as a
|
||||
warning of an arbitrary expression using the function above.
|
||||
|
||||
Example usage::
|
||||
|
||||
void foo() {
|
||||
int x = 1;
|
||||
clang_analyzer_hashDump(x); // expected-warning{{hashed string for x}}
|
||||
}
|
||||
|
||||
Statistics
|
||||
==========
|
||||
|
||||
|
321
docs/analyzer/DesignDiscussions/InitializerLists.rst
Normal file
321
docs/analyzer/DesignDiscussions/InitializerLists.rst
Normal file
@ -0,0 +1,321 @@
|
||||
This discussion took place in https://reviews.llvm.org/D35216
|
||||
"Escape symbols when creating std::initializer_list".
|
||||
|
||||
It touches problems of modelling C++ standard library constructs in general,
|
||||
including modelling implementation-defined fields within C++ standard library
|
||||
objects, in particular constructing objects into pointers held by such fields,
|
||||
and separation of responsibilities between analyzer's core and checkers.
|
||||
|
||||
**Artem:**
|
||||
|
||||
I've seen a few false positives that appear because we construct
|
||||
C++11 std::initializer_list objects with brace initializers, and such
|
||||
construction is not properly modeled. For instance, if a new object is
|
||||
constructed on the heap only to be put into a brace-initialized STL container,
|
||||
the object is reported to be leaked.
|
||||
|
||||
Approach (0): This can be trivially fixed by this patch, which causes pointers
|
||||
passed into initializer list expressions to immediately escape.
|
||||
|
||||
This fix is overly conservative though. So i did a bit of investigation as to
|
||||
how model std::initializer_list better.
|
||||
|
||||
According to the standard, std::initializer_list<T> is an object that has
|
||||
methods begin(), end(), and size(), where begin() returns a pointer to continous
|
||||
array of size() objects of type T, and end() is equal to begin() plus size().
|
||||
The standard does hint that it should be possible to implement
|
||||
std::initializer_list<T> as a pair of pointers, or as a pointer and a size
|
||||
integer, however specific fields that the object would contain are an
|
||||
implementation detail.
|
||||
|
||||
Ideally, we should be able to model the initializer list's methods precisely.
|
||||
Or, at least, it should be possible to explain to the analyzer that the list
|
||||
somehow "takes hold" of the values put into it. Initializer lists can also be
|
||||
copied, which is a separate story that i'm not trying to address here.
|
||||
|
||||
The obvious approach to modeling std::initializer_list in a checker would be to
|
||||
construct a SymbolMetadata for the memory region of the initializer list object,
|
||||
which would be of type T* and represent begin(), so we'd trivially model begin()
|
||||
as a function that returns this symbol. The array pointed to by that symbol
|
||||
would be bindLoc()ed to contain the list's contents (probably as a CompoundVal
|
||||
to produce less bindings in the store). Extent of this array would represent
|
||||
size() and would be equal to the length of the list as written.
|
||||
|
||||
So this sounds good, however apparently it does nothing to address our false
|
||||
positives: when the list escapes, our RegionStoreManager is not magically
|
||||
guessing that the metadata symbol attached to it, together with its contents,
|
||||
should also escape. In fact, it's impossible to trigger a pointer escape from
|
||||
within the checker.
|
||||
|
||||
Approach (1): If only we enabled ProgramState::bindLoc(..., notifyChanges=true)
|
||||
to cause pointer escapes (not only region changes) (which sounds like the right
|
||||
thing to do anyway) such checker would be able to solve the false positives by
|
||||
triggering escapes when binding list elements to the list. However, it'd be as
|
||||
conservative as the current patch's solution. Ideally, we do not want escapes to
|
||||
happen so early. Instead, we'd prefer them to be delayed until the list itself
|
||||
escapes.
|
||||
|
||||
So i believe that escaping metadata symbols whenever their base regions escape
|
||||
would be the right thing to do. Currently we didn't think about that because we
|
||||
had neither pointer-type metadatas nor non-pointer escapes.
|
||||
|
||||
Approach (2): We could teach the Store to scan itself for bindings to
|
||||
metadata-symbolic-based regions during scanReachableSymbols() whenever a region
|
||||
turns out to be reachable. This requires no work on checker side, but it sounds
|
||||
performance-heavy.
|
||||
|
||||
Approach (3): We could let checkers maintain the set of active metadata symbols
|
||||
in the program state (ideally somewhere in the Store, which sounds weird but
|
||||
causes the smallest amount of layering violations), so that the core knew what
|
||||
to escape. This puts a stress on the checkers, but with a smart data map it
|
||||
wouldn't be a problem.
|
||||
|
||||
Approach (4): We could allow checkers to trigger pointer escapes in arbitrary
|
||||
moments. If we allow doing this within checkPointerEscape callback itself, we
|
||||
would be able to express facts like "when this region escapes, that metadata
|
||||
symbol attached to it should also escape". This sounds like an ultimate freedom,
|
||||
with maximum stress on the checkers - still not too much stress when we have
|
||||
smart data maps.
|
||||
|
||||
I'm personally liking the approach (2) - it should be possible to avoid
|
||||
performance overhead, and clarity seems nice.
|
||||
|
||||
**Gabor:**
|
||||
|
||||
At this point, I am a bit wondering about two questions.
|
||||
|
||||
- When should something belong to a checker and when should something belong
|
||||
to the engine? Sometimes we model library aspects in the engine and model
|
||||
language constructs in checkers.
|
||||
- What is the checker programming model that we are aiming for? Maximum
|
||||
freedom or more easy checker development?
|
||||
|
||||
I think if we aim for maximum freedom, we do not need to worry about the
|
||||
potential stress on checkers, and we can introduce abstractions to mitigate that
|
||||
later on.
|
||||
If we want to simplify the API, then maybe it makes more sense to move language
|
||||
construct modeling to the engine when the checker API is not sufficient instead
|
||||
of complicating the API.
|
||||
|
||||
Right now I have no preference or objections between the alternatives but there
|
||||
are some random thoughts:
|
||||
|
||||
- Maybe it would be great to have a guideline how to evolve the analyzer and
|
||||
follow it, so it can help us to decide in similar situations
|
||||
- I do care about performance in this case. The reason is that we have a
|
||||
limited performance budget. And I think we should not expect most of the checker
|
||||
writers to add modeling of language constructs. So, in my opinion, it is ok to
|
||||
have less nice/more verbose API for language modeling if we can have better
|
||||
performance this way, since it only needs to be done once, and is done by the
|
||||
framework developers.
|
||||
|
||||
**Artem:** These are some great questions, i guess it'd be better to discuss
|
||||
them more openly. As a quick dump of my current mood:
|
||||
|
||||
- To me it seems obvious that we need to aim for a checker API that is both
|
||||
simple and powerful. This can probably by keeping the API as powerful as
|
||||
necessary while providing a layer of simple ready-made solutions on top of it.
|
||||
Probably a few reusable components for assembling checkers. And this layer
|
||||
should ideally be pleasant enough to work with, so that people would prefer to
|
||||
extend it when something is lacking, instead of falling back to the complex
|
||||
omnipotent API. I'm thinking of AST matchers vs. AST visitors as a roughly
|
||||
similar situation: matchers are not omnipotent, but they're so nice.
|
||||
|
||||
- Separation between core and checkers is usually quite strange. Once we have
|
||||
shared state traits, i generally wouldn't mind having region store or range
|
||||
constraint manager as checkers (though it's probably not worth it to transform
|
||||
them - just a mood). The main thing to avoid here would be the situation when
|
||||
the checker overwrites stuff written by the core because it thinks it has a
|
||||
better idea what's going on, so the core should provide a good default behavior.
|
||||
|
||||
- Yeah, i totally care about performance as well, and if i try to implement
|
||||
approach, i'd make sure it's good.
|
||||
|
||||
**Artem:**
|
||||
|
||||
> Approach (2): We could teach the Store to scan itself for bindings to
|
||||
> metadata-symbolic-based regions during scanReachableSymbols() whenever
|
||||
> a region turns out to be reachable. This requires no work on checker side,
|
||||
> but it sounds performance-heavy.
|
||||
|
||||
Nope, this approach is wrong. Metadata symbols may become out-of-date: when the
|
||||
object changes, metadata symbols attached to it aren't changing (because symbols
|
||||
simply don't change). The same metadata may have different symbols to denote its
|
||||
value in different moments of time, but at most one of them represents the
|
||||
actual metadata value. So we'd be escaping more stuff than necessary.
|
||||
|
||||
If only we had "ghost fields"
|
||||
(http://lists.llvm.org/pipermail/cfe-dev/2016-May/049000.html), it would have
|
||||
been much easier, because the ghost field would only contain the actual
|
||||
metadata, and the Store would always know about it. This example adds to my
|
||||
belief that ghost fields are exactly what we need for most C++ checkers.
|
||||
|
||||
**Devin:**
|
||||
|
||||
In this case, I would be fine with some sort of
|
||||
AbstractStorageMemoryRegion that meant "here is a memory region and somewhere
|
||||
reachable from here exists another region of type T". Or even multiple regions
|
||||
with different identifiers. This wouldn't specify how the memory is reachable,
|
||||
but it would allow for transfer functions to get at those regions and it would
|
||||
allow for invalidation.
|
||||
|
||||
For std::initializer_list this reachable region would the region for the backing
|
||||
array and the transfer functions for begin() and end() yield the beginning and
|
||||
end element regions for it.
|
||||
|
||||
In my view this differs from ghost variables in that (1) this storage does
|
||||
actually exist (it is just a library implementation detail where that storage
|
||||
lives) and (2) it is perfectly valid for a pointer into that storage to be
|
||||
returned and for another part of the program to read or write from that storage.
|
||||
(Well, in this case just read since it is allowed to be read-only memory).
|
||||
|
||||
What I'm not OK with is modeling abstract analysis state (for example, the count
|
||||
of a NSMutableArray or the typestate of a file handle) as a value stored in some
|
||||
ginned up region in the store. This takes an easy problem that the analyzer does
|
||||
well at (modeling typestate) and turns it into a hard one that the analyzer is
|
||||
bad at (reasoning about the contents of the heap).
|
||||
|
||||
I think the key criterion here is: "is the region accessible from outside the
|
||||
library". That is, does the library expose the region as a pointer that can be
|
||||
read to or written from in the client program? If so, then it makes sense for
|
||||
this to be in the store: we are modeling reachable storage as storage. But if
|
||||
we're just modeling arbitrary analysis facts that need to be invalidated when a
|
||||
pointer escapes then we shouldn't try to gin up storage for them just to get
|
||||
invalidation for free.
|
||||
|
||||
**Artem:**
|
||||
|
||||
> In this case, I would be fine with some sort of AbstractStorageMemoryRegion
|
||||
> that meant "here is a memory region and somewhere reachable from here exists
|
||||
> another region of type T". Or even multiple regions with different
|
||||
> identifiers. This wouldn't specify how the memory is reachable, but it would
|
||||
> allow for transfer functions to get at those regions and it would allow for
|
||||
> invalidation.
|
||||
|
||||
Yeah, this is what we can easily implement now as a
|
||||
symbolic-region-based-on-a-metadata-symbol (though we can make a new region
|
||||
class for that if we eg. want it typed). The problem is that the relation
|
||||
between such storage region and its parent object region is essentially
|
||||
immaterial, similarly to the relation between SymbolRegionValue and its parent
|
||||
region. Region contents are mutable: today the abstract storage is reachable
|
||||
from its parent object, tomorrow it's not, and maybe something else becomes
|
||||
reachable, something that isn't even abstract. So the parent region for the
|
||||
abstract storage is most of the time at best a "nice to know" thing - we cannot
|
||||
rely on it to do any actual work. We'd anyway need to rely on the checker to do
|
||||
the job.
|
||||
|
||||
> For std::initializer_list this reachable region would the region for the
|
||||
> backing array and the transfer functions for begin() and end() yield the
|
||||
> beginning and end element regions for it.
|
||||
|
||||
So maybe in fact for std::initializer_list it may work fine because you cannot
|
||||
change the data after the object is constructed - so this region's contents are
|
||||
essentially immutable. For the future, i feel as if it is a dead end.
|
||||
|
||||
I'd like to consider another funny example. Suppose we're trying to model
|
||||
std::unique_ptr. Consider::
|
||||
|
||||
void bar(const std::unique_ptr<int> &x);
|
||||
|
||||
void foo(std::unique_ptr<int> &x) {
|
||||
int *a = x.get(); // (a, 0, direct): &AbstractStorageRegion
|
||||
*a = 1; // (AbstractStorageRegion, 0, direct): 1 S32b
|
||||
int *b = new int;
|
||||
*b = 2; // (SymRegion{conj_$0<int *>}, 0 ,direct): 2 S32b
|
||||
x.reset(b); // Checker map: x -> SymRegion{conj_$0<int *>}
|
||||
bar(x); // 'a' doesn't escape (the pointer was unique), 'b' does.
|
||||
clang_analyzer_eval(*a == 1); // Making this true is up to the checker.
|
||||
clang_analyzer_eval(*b == 2); // Making this unknown is up to the checker.
|
||||
}
|
||||
|
||||
The checker doesn't totally need to ensure that *a == 1 passes - even though the
|
||||
pointer was unique, it could theoretically have .get()-ed above and the code
|
||||
could of course break the uniqueness invariant (though we'd probably want it).
|
||||
The checker can say that "even if *a did escape, it was not because it was
|
||||
stuffed directly into bar()".
|
||||
|
||||
The checker's direct responsibility, however, is to solve the *b == 2 thing
|
||||
(which is in fact the problem we're dealing with in this patch - escaping the
|
||||
storage region of the object).
|
||||
|
||||
So we're talking about one more operation over the program state (scanning
|
||||
reachable symbols and regions) that cannot work without checker support.
|
||||
|
||||
We can probably add a new callback "checkReachableSymbols" to solve this. This
|
||||
is in fact also related to the dead symbols problem (we're scanning for live
|
||||
symbols in the store and in the checkers separately, but we need to do so
|
||||
simultaneously with a single worklist). Hmm, in fact this sounds like a good
|
||||
idea; we can replace checkLiveSymbols with checkReachableSymbols.
|
||||
|
||||
Or we could just have ghost member variables, and no checker support required at
|
||||
all. For ghost member variables, the relation with their parent region (which
|
||||
would be their superregion) is actually useful, the mutability of their contents
|
||||
is expressed naturally, and the store automagically sees reachable symbols, live
|
||||
symbols, escapes, invalidations, whatever.
|
||||
|
||||
> In my view this differs from ghost variables in that (1) this storage does
|
||||
> actually exist (it is just a library implementation detail where that storage
|
||||
> lives) and (2) it is perfectly valid for a pointer into that storage to be
|
||||
> returned and for another part of the program to read or write from that
|
||||
> storage. (Well, in this case just read since it is allowed to be read-only
|
||||
> memory).
|
||||
|
||||
> What I'm not OK with is modeling abstract analysis state (for example, the
|
||||
> count of a NSMutableArray or the typestate of a file handle) as a value stored
|
||||
> in some ginned up region in the store.This takes an easy problem that the
|
||||
> analyzer does well at (modeling typestate) and turns it into a hard one that
|
||||
> the analyzer is bad at (reasoning about the contents of the heap).
|
||||
|
||||
Yeah, i tend to agree on that. For simple typestates, this is probably an
|
||||
overkill, so let's definitely put aside the idea of "ghost symbolic regions"
|
||||
that i had earlier.
|
||||
|
||||
But, to summarize a bit, in our current case, however, the typestate we're
|
||||
looking for is the contents of the heap. And when we try to model such
|
||||
typestates (complex in this specific manner, i.e. heap-like) in any checker, we
|
||||
have a choice between re-doing this modeling in every such checker (which is
|
||||
something analyzer is indeed good at, but at a price of making checkers heavy)
|
||||
or instead relying on the Store to do exactly what it's designed to do.
|
||||
|
||||
> I think the key criterion here is: "is the region accessible from outside
|
||||
> the library". That is, does the library expose the region as a pointer that
|
||||
> can be read to or written from in the client program? If so, then it makes
|
||||
> sense for this to be in the store: we are modeling reachable storage as
|
||||
> storage. But if we're just modeling arbitrary analysis facts that need to be
|
||||
> invalidated when a pointer escapes then we shouldn't try to gin up storage
|
||||
> for them just to get invalidation for free.
|
||||
|
||||
As a metaphor, i'd probably compare it to body farms - the difference between
|
||||
ghost member variables and metadata symbols seems to me like the difference
|
||||
between body farms and evalCall. Both are nice to have, and body farms are very
|
||||
pleasant to work with, even if not omnipotent. I think it's fine for a
|
||||
FunctionDecl's body in a body farm to have a local variable, even if such
|
||||
variable doesn't actually exist, even if it cannot be seen from outside the
|
||||
function call. I'm not seeing immediate practical difference between "it does
|
||||
actually exist" and "it doesn't actually exist, just a handy abstraction".
|
||||
Similarly, i think it's fine if we have a CXXRecordDecl with
|
||||
implementation-defined contents, and try to farm up a member variable as a handy
|
||||
abstraction (we don't even need to know its name or offset, only that it's there
|
||||
somewhere).
|
||||
|
||||
**Artem:**
|
||||
|
||||
We've discussed it in person with Devin, and he provided more points to think
|
||||
about:
|
||||
|
||||
- If the initializer list consists of non-POD data, constructors of list's
|
||||
objects need to take the sub-region of the list's region as this-region In the
|
||||
current (v2) version of this patch, these objects are constructed elsewhere and
|
||||
then trivial-copied into the list's metadata pointer region, which may be
|
||||
incorrect. This is our overall problem with C++ constructors, which manifests in
|
||||
this case as well. Additionally, objects would need to be constructed in the
|
||||
analyzer's core, which would not be able to predict that it needs to take a
|
||||
checker-specific region as this-region, which makes it harder, though it might
|
||||
be mitigated by sharing the checker state traits.
|
||||
|
||||
- Because "ghost variables" are not material to the user, we need to somehow
|
||||
make super sure that they don't make it into the diagnostic messages.
|
||||
|
||||
So, because this needs further digging into overall C++ support and rises too
|
||||
many questions, i'm delaying a better approach to this problem and will fall
|
||||
back to the original trivial patch.
|
@ -49,9 +49,9 @@ copyright = u'2013-%d, Analyzer Team' % date.today().year
|
||||
# built documents.
|
||||
#
|
||||
# The short version.
|
||||
version = '5'
|
||||
version = '6'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '5'
|
||||
release = '6'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -49,9 +49,9 @@ copyright = u'2007-%d, The Clang Team' % date.today().year
|
||||
# built documents.
|
||||
#
|
||||
# The short version.
|
||||
version = '5'
|
||||
version = '6'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '5'
|
||||
release = '6'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -60,6 +60,7 @@ Using Clang as a Library
|
||||
LibASTMatchers
|
||||
HowToSetupToolingForLLVM
|
||||
JSONCompilationDatabase
|
||||
RefactoringEngine
|
||||
|
||||
Using Clang Tools
|
||||
=================
|
||||
@ -83,6 +84,7 @@ Design Documents
|
||||
PTHInternals
|
||||
PCHInternals
|
||||
ItaniumMangleAbiTags
|
||||
HardwareAssistedAddressSanitizerDesign.rst
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
@ -177,7 +177,8 @@ def read_options(header):
|
||||
for option in options:
|
||||
if not option.type in ['bool', 'unsigned', 'int', 'std::string',
|
||||
'std::vector<std::string>',
|
||||
'std::vector<IncludeCategory>']:
|
||||
'std::vector<IncludeCategory>',
|
||||
'std::vector<RawStringFormat>']:
|
||||
if enums.has_key(option.type):
|
||||
option.enum = enums[option.type]
|
||||
elif nested_structs.has_key(option.type):
|
||||
|
@ -17,6 +17,7 @@ add_dependencies(clang-interpreter
|
||||
)
|
||||
|
||||
target_link_libraries(clang-interpreter
|
||||
PRIVATE
|
||||
clangBasic
|
||||
clangCodeGen
|
||||
clangDriver
|
||||
|
@ -32,7 +32,7 @@
|
||||
* compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
|
||||
*/
|
||||
#define CINDEX_VERSION_MAJOR 0
|
||||
#define CINDEX_VERSION_MINOR 43
|
||||
#define CINDEX_VERSION_MINOR 45
|
||||
|
||||
#define CINDEX_VERSION_ENCODE(major, minor) ( \
|
||||
((major) * 10000) \
|
||||
@ -333,6 +333,16 @@ CINDEX_LINKAGE void clang_CXIndex_setGlobalOptions(CXIndex, unsigned options);
|
||||
*/
|
||||
CINDEX_LINKAGE unsigned clang_CXIndex_getGlobalOptions(CXIndex);
|
||||
|
||||
/**
|
||||
* \brief Sets the invocation emission path option in a CXIndex.
|
||||
*
|
||||
* The invocation emission path specifies a path which will contain log
|
||||
* files for certain libclang invocations. A null value (default) implies that
|
||||
* libclang invocations are not logged..
|
||||
*/
|
||||
CINDEX_LINKAGE void
|
||||
clang_CXIndex_setInvocationEmissionPathOption(CXIndex, const char *Path);
|
||||
|
||||
/**
|
||||
* \defgroup CINDEX_FILES File manipulation routines
|
||||
*
|
||||
@ -393,6 +403,21 @@ clang_isFileMultipleIncludeGuarded(CXTranslationUnit tu, CXFile file);
|
||||
CINDEX_LINKAGE CXFile clang_getFile(CXTranslationUnit tu,
|
||||
const char *file_name);
|
||||
|
||||
/**
|
||||
* \brief Retrieve the buffer associated with the given file.
|
||||
*
|
||||
* \param tu the translation unit
|
||||
*
|
||||
* \param file the file for which to retrieve the buffer.
|
||||
*
|
||||
* \param size [out] if non-NULL, will be set to the size of the buffer.
|
||||
*
|
||||
* \returns a pointer to the buffer in memory that holds the contents of
|
||||
* \p file, or a NULL pointer when the file is not loaded.
|
||||
*/
|
||||
CINDEX_LINKAGE const char *clang_getFileContents(CXTranslationUnit tu,
|
||||
CXFile file, size_t *size);
|
||||
|
||||
/**
|
||||
* \brief Returns non-zero if the \c file1 and \c file2 point to the same file,
|
||||
* or they are both NULL.
|
||||
@ -2836,6 +2861,22 @@ enum CXLanguageKind {
|
||||
*/
|
||||
CINDEX_LINKAGE enum CXLanguageKind clang_getCursorLanguage(CXCursor cursor);
|
||||
|
||||
/**
|
||||
* \brief Describe the "thread-local storage (TLS) kind" of the declaration
|
||||
* referred to by a cursor.
|
||||
*/
|
||||
enum CXTLSKind {
|
||||
CXTLS_None = 0,
|
||||
CXTLS_Dynamic,
|
||||
CXTLS_Static
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Determine the "thread-local storage (TLS) kind" of the declaration
|
||||
* referred to by a cursor.
|
||||
*/
|
||||
CINDEX_LINKAGE enum CXTLSKind clang_getCursorTLSKind(CXCursor cursor);
|
||||
|
||||
/**
|
||||
* \brief Returns the translation unit that a cursor originated from.
|
||||
*/
|
||||
@ -3115,8 +3156,9 @@ enum CXTypeKind {
|
||||
CXType_ObjCSel = 29,
|
||||
CXType_Float128 = 30,
|
||||
CXType_Half = 31,
|
||||
CXType_Float16 = 32,
|
||||
CXType_FirstBuiltin = CXType_Void,
|
||||
CXType_LastBuiltin = CXType_Half,
|
||||
CXType_LastBuiltin = CXType_Float16,
|
||||
|
||||
CXType_Complex = 100,
|
||||
CXType_Pointer = 101,
|
||||
@ -4275,6 +4317,12 @@ CINDEX_LINKAGE CXString clang_Cursor_getMangling(CXCursor);
|
||||
*/
|
||||
CINDEX_LINKAGE CXStringSet *clang_Cursor_getCXXManglings(CXCursor);
|
||||
|
||||
/**
|
||||
* \brief Retrieve the CXStrings representing the mangled symbols of the ObjC
|
||||
* class interface or implementation at the cursor.
|
||||
*/
|
||||
CINDEX_LINKAGE CXStringSet *clang_Cursor_getObjCManglings(CXCursor);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@ -4418,6 +4466,12 @@ CINDEX_LINKAGE unsigned clang_CXXMethod_isStatic(CXCursor C);
|
||||
*/
|
||||
CINDEX_LINKAGE unsigned clang_CXXMethod_isVirtual(CXCursor C);
|
||||
|
||||
/**
|
||||
* \brief Determine if a C++ record is abstract, i.e. whether a class or struct
|
||||
* has a pure virtual member function.
|
||||
*/
|
||||
CINDEX_LINKAGE unsigned clang_CXXRecord_isAbstract(CXCursor C);
|
||||
|
||||
/**
|
||||
* \brief Determine if an enum declaration refers to a scoped enum.
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- ASTContext.h - Context to hold long-lived AST nodes ----*- C++ -*-===//
|
||||
//===- ASTContext.h - Context to hold long-lived AST nodes ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -6,10 +6,10 @@
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
//
|
||||
/// \file
|
||||
/// \brief Defines the clang::ASTContext interface.
|
||||
///
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_ASTCONTEXT_H
|
||||
@ -19,8 +19,8 @@
|
||||
#include "clang/AST/CanonicalType.h"
|
||||
#include "clang/AST/CommentCommandTraits.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/ExternalASTSource.h"
|
||||
#include "clang/AST/NestedNameSpecifier.h"
|
||||
#include "clang/AST/PrettyPrinter.h"
|
||||
@ -30,32 +30,32 @@
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/AddressSpaces.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/Linkage.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "clang/Basic/OperatorKinds.h"
|
||||
#include "clang/Basic/PartialDiagnostic.h"
|
||||
#include "clang/Basic/SanitizerBlacklist.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "clang/Basic/XRayLists.h"
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/None.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/Support/AlignOf.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
@ -65,7 +65,6 @@
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@ -75,50 +74,72 @@ namespace llvm {
|
||||
|
||||
struct fltSemantics;
|
||||
|
||||
} // end namespace llvm
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
|
||||
class APValue;
|
||||
class ASTMutationListener;
|
||||
class ASTRecordLayout;
|
||||
class AtomicExpr;
|
||||
class BlockExpr;
|
||||
class BuiltinTemplateDecl;
|
||||
class CharUnits;
|
||||
class CXXABI;
|
||||
class CXXConstructorDecl;
|
||||
class CXXMethodDecl;
|
||||
class CXXRecordDecl;
|
||||
class DiagnosticsEngine;
|
||||
class Expr;
|
||||
class MangleContext;
|
||||
class MangleNumberingContext;
|
||||
class MaterializeTemporaryExpr;
|
||||
class TargetInfo;
|
||||
// Decls
|
||||
class MangleContext;
|
||||
class MemberSpecializationInfo;
|
||||
class Module;
|
||||
class ObjCCategoryDecl;
|
||||
class ObjCCategoryImplDecl;
|
||||
class ObjCContainerDecl;
|
||||
class ObjCImplDecl;
|
||||
class ObjCImplementationDecl;
|
||||
class ObjCInterfaceDecl;
|
||||
class ObjCIvarDecl;
|
||||
class ObjCMethodDecl;
|
||||
class ObjCPropertyDecl;
|
||||
class ObjCPropertyImplDecl;
|
||||
class ObjCProtocolDecl;
|
||||
class ObjCTypeParamDecl;
|
||||
class Preprocessor;
|
||||
class Stmt;
|
||||
class StoredDeclsMap;
|
||||
class TemplateDecl;
|
||||
class TemplateParameterList;
|
||||
class TemplateTemplateParmDecl;
|
||||
class TemplateTypeParmDecl;
|
||||
class UnresolvedSetIterator;
|
||||
class UsingDecl;
|
||||
class UsingShadowDecl;
|
||||
class VarTemplateDecl;
|
||||
class VTableContextBase;
|
||||
|
||||
namespace Builtin {
|
||||
|
||||
class Context;
|
||||
class Context;
|
||||
|
||||
} // end namespace Builtin
|
||||
} // namespace Builtin
|
||||
|
||||
enum BuiltinTemplateKind : int;
|
||||
|
||||
namespace comments {
|
||||
|
||||
class FullComment;
|
||||
class FullComment;
|
||||
|
||||
} // end namespace comments
|
||||
} // namespace comments
|
||||
|
||||
struct TypeInfo {
|
||||
uint64_t Width;
|
||||
unsigned Align;
|
||||
uint64_t Width = 0;
|
||||
unsigned Align = 0;
|
||||
bool AlignIsRequired : 1;
|
||||
|
||||
TypeInfo() : Width(0), Align(0), AlignIsRequired(false) {}
|
||||
TypeInfo() : AlignIsRequired(false) {}
|
||||
TypeInfo(uint64_t Width, unsigned Align, bool AlignIsRequired)
|
||||
: Width(Width), Align(Align), AlignIsRequired(AlignIsRequired) {}
|
||||
};
|
||||
@ -126,7 +147,7 @@ struct TypeInfo {
|
||||
/// \brief Holds long-lived AST nodes (such as types and decls) that can be
|
||||
/// referred to throughout the semantic analysis of a file.
|
||||
class ASTContext : public RefCountedBase<ASTContext> {
|
||||
ASTContext &this_() { return *this; }
|
||||
friend class NestedNameSpecifier;
|
||||
|
||||
mutable SmallVector<Type *, 0> Types;
|
||||
mutable llvm::FoldingSet<ExtQuals> ExtQualNodes;
|
||||
@ -143,6 +164,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
mutable llvm::FoldingSet<DependentSizedArrayType> DependentSizedArrayTypes;
|
||||
mutable llvm::FoldingSet<DependentSizedExtVectorType>
|
||||
DependentSizedExtVectorTypes;
|
||||
mutable llvm::FoldingSet<DependentAddressSpaceType>
|
||||
DependentAddressSpaceTypes;
|
||||
mutable llvm::FoldingSet<VectorType> VectorTypes;
|
||||
mutable llvm::FoldingSet<FunctionNoProtoType> FunctionNoProtoTypes;
|
||||
mutable llvm::ContextualFoldingSet<FunctionProtoType, ASTContext&>
|
||||
@ -187,8 +210,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
///
|
||||
/// This set is managed by the NestedNameSpecifier class.
|
||||
mutable llvm::FoldingSet<NestedNameSpecifier> NestedNameSpecifiers;
|
||||
mutable NestedNameSpecifier *GlobalNestedNameSpecifier;
|
||||
friend class NestedNameSpecifier;
|
||||
mutable NestedNameSpecifier *GlobalNestedNameSpecifier = nullptr;
|
||||
|
||||
/// \brief A cache mapping from RecordDecls to ASTRecordLayouts.
|
||||
///
|
||||
@ -199,7 +221,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
ObjCLayouts;
|
||||
|
||||
/// \brief A cache from types to size and alignment information.
|
||||
typedef llvm::DenseMap<const Type *, struct TypeInfo> TypeInfoMap;
|
||||
using TypeInfoMap = llvm::DenseMap<const Type *, struct TypeInfo>;
|
||||
mutable TypeInfoMap MemoizedTypeInfo;
|
||||
|
||||
/// \brief A cache mapping from CXXRecordDecls to key functions.
|
||||
@ -233,7 +255,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
|
||||
public:
|
||||
CanonicalTemplateTemplateParm(TemplateTemplateParmDecl *Parm)
|
||||
: Parm(Parm) { }
|
||||
: Parm(Parm) {}
|
||||
|
||||
TemplateTemplateParmDecl *getParam() const { return Parm; }
|
||||
|
||||
@ -249,32 +271,32 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
getCanonicalTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTP) const;
|
||||
|
||||
/// \brief The typedef for the __int128_t type.
|
||||
mutable TypedefDecl *Int128Decl;
|
||||
mutable TypedefDecl *Int128Decl = nullptr;
|
||||
|
||||
/// \brief The typedef for the __uint128_t type.
|
||||
mutable TypedefDecl *UInt128Decl;
|
||||
mutable TypedefDecl *UInt128Decl = nullptr;
|
||||
|
||||
/// \brief The typedef for the target specific predefined
|
||||
/// __builtin_va_list type.
|
||||
mutable TypedefDecl *BuiltinVaListDecl;
|
||||
mutable TypedefDecl *BuiltinVaListDecl = nullptr;
|
||||
|
||||
/// The typedef for the predefined \c __builtin_ms_va_list type.
|
||||
mutable TypedefDecl *BuiltinMSVaListDecl;
|
||||
mutable TypedefDecl *BuiltinMSVaListDecl = nullptr;
|
||||
|
||||
/// \brief The typedef for the predefined \c id type.
|
||||
mutable TypedefDecl *ObjCIdDecl;
|
||||
mutable TypedefDecl *ObjCIdDecl = nullptr;
|
||||
|
||||
/// \brief The typedef for the predefined \c SEL type.
|
||||
mutable TypedefDecl *ObjCSelDecl;
|
||||
mutable TypedefDecl *ObjCSelDecl = nullptr;
|
||||
|
||||
/// \brief The typedef for the predefined \c Class type.
|
||||
mutable TypedefDecl *ObjCClassDecl;
|
||||
mutable TypedefDecl *ObjCClassDecl = nullptr;
|
||||
|
||||
/// \brief The typedef for the predefined \c Protocol class in Objective-C.
|
||||
mutable ObjCInterfaceDecl *ObjCProtocolClassDecl;
|
||||
mutable ObjCInterfaceDecl *ObjCProtocolClassDecl = nullptr;
|
||||
|
||||
/// \brief The typedef for the predefined 'BOOL' type.
|
||||
mutable TypedefDecl *BOOLDecl;
|
||||
mutable TypedefDecl *BOOLDecl = nullptr;
|
||||
|
||||
// Typedefs which may be provided defining the structure of Objective-C
|
||||
// pseudo-builtins
|
||||
@ -298,42 +320,42 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
mutable IdentifierInfo *TypePackElementName = nullptr;
|
||||
|
||||
QualType ObjCConstantStringType;
|
||||
mutable RecordDecl *CFConstantStringTagDecl;
|
||||
mutable TypedefDecl *CFConstantStringTypeDecl;
|
||||
mutable RecordDecl *CFConstantStringTagDecl = nullptr;
|
||||
mutable TypedefDecl *CFConstantStringTypeDecl = nullptr;
|
||||
|
||||
mutable QualType ObjCSuperType;
|
||||
|
||||
QualType ObjCNSStringType;
|
||||
|
||||
/// \brief The typedef declaration for the Objective-C "instancetype" type.
|
||||
TypedefDecl *ObjCInstanceTypeDecl;
|
||||
TypedefDecl *ObjCInstanceTypeDecl = nullptr;
|
||||
|
||||
/// \brief The type for the C FILE type.
|
||||
TypeDecl *FILEDecl;
|
||||
TypeDecl *FILEDecl = nullptr;
|
||||
|
||||
/// \brief The type for the C jmp_buf type.
|
||||
TypeDecl *jmp_bufDecl;
|
||||
TypeDecl *jmp_bufDecl = nullptr;
|
||||
|
||||
/// \brief The type for the C sigjmp_buf type.
|
||||
TypeDecl *sigjmp_bufDecl;
|
||||
TypeDecl *sigjmp_bufDecl = nullptr;
|
||||
|
||||
/// \brief The type for the C ucontext_t type.
|
||||
TypeDecl *ucontext_tDecl;
|
||||
TypeDecl *ucontext_tDecl = nullptr;
|
||||
|
||||
/// \brief Type for the Block descriptor for Blocks CodeGen.
|
||||
///
|
||||
/// Since this is only used for generation of debug info, it is not
|
||||
/// serialized.
|
||||
mutable RecordDecl *BlockDescriptorType;
|
||||
mutable RecordDecl *BlockDescriptorType = nullptr;
|
||||
|
||||
/// \brief Type for the Block descriptor for Blocks CodeGen.
|
||||
///
|
||||
/// Since this is only used for generation of debug info, it is not
|
||||
/// serialized.
|
||||
mutable RecordDecl *BlockDescriptorExtendedType;
|
||||
mutable RecordDecl *BlockDescriptorExtendedType = nullptr;
|
||||
|
||||
/// \brief Declaration for the CUDA cudaConfigureCall function.
|
||||
FunctionDecl *cudaConfigureCallDecl;
|
||||
FunctionDecl *cudaConfigureCallDecl = nullptr;
|
||||
|
||||
/// \brief Keeps track of all declaration attributes.
|
||||
///
|
||||
@ -363,12 +385,19 @@ class ASTContext : public RefCountedBase<ASTContext> {
|
||||
};
|
||||
llvm::DenseMap<Module*, PerModuleInitializers*> ModuleInitializers;
|
||||
|
||||
ASTContext &this_() { return *this; }
|
||||
|
||||
public:
|
||||
/// \brief A type synonym for the TemplateOrInstantiation mapping.
|
||||
typedef llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>
|
||||
TemplateOrSpecializationInfo;
|
||||
using TemplateOrSpecializationInfo =
|
||||
llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *>;
|
||||
|
||||
private:
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTReader;
|
||||
friend class ASTWriter;
|
||||
friend class CXXRecordDecl;
|
||||
|
||||
/// \brief A mapping to contain the template or declaration that
|
||||
/// a variable declaration describes or was instantiated from,
|
||||
/// respectively.
|
||||
@ -438,7 +467,7 @@ private:
|
||||
/// Since most C++ member functions aren't virtual and therefore
|
||||
/// don't override anything, we store the overridden functions in
|
||||
/// this map on the side rather than within the CXXMethodDecl structure.
|
||||
typedef llvm::TinyPtrVector<const CXXMethodDecl*> CXXMethodVector;
|
||||
using CXXMethodVector = llvm::TinyPtrVector<const CXXMethodDecl *>;
|
||||
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
|
||||
|
||||
/// \brief Mapping from each declaration context to its corresponding
|
||||
@ -454,18 +483,18 @@ private:
|
||||
|
||||
/// \brief Mapping that stores parameterIndex values for ParmVarDecls when
|
||||
/// that value exceeds the bitfield size of ParmVarDeclBits.ParameterIndex.
|
||||
typedef llvm::DenseMap<const VarDecl *, unsigned> ParameterIndexTable;
|
||||
using ParameterIndexTable = llvm::DenseMap<const VarDecl *, unsigned>;
|
||||
ParameterIndexTable ParamIndices;
|
||||
|
||||
ImportDecl *FirstLocalImport;
|
||||
ImportDecl *LastLocalImport;
|
||||
ImportDecl *FirstLocalImport = nullptr;
|
||||
ImportDecl *LastLocalImport = nullptr;
|
||||
|
||||
TranslationUnitDecl *TUDecl;
|
||||
mutable ExternCContextDecl *ExternCContext;
|
||||
mutable BuiltinTemplateDecl *MakeIntegerSeqDecl;
|
||||
mutable BuiltinTemplateDecl *TypePackElementDecl;
|
||||
mutable ExternCContextDecl *ExternCContext = nullptr;
|
||||
mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
|
||||
mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
|
||||
|
||||
/// \brief The associated SourceManager object.a
|
||||
/// \brief The associated SourceManager object.
|
||||
SourceManager &SourceMgr;
|
||||
|
||||
/// \brief The language options used to create the AST associated with
|
||||
@ -494,19 +523,14 @@ private:
|
||||
CXXABI *createCXXABI(const TargetInfo &T);
|
||||
|
||||
/// \brief The logical -> physical address space map.
|
||||
const LangAS::Map *AddrSpaceMap;
|
||||
const LangASMap *AddrSpaceMap = nullptr;
|
||||
|
||||
/// \brief Address space map mangling must be used with language specific
|
||||
/// address spaces (e.g. OpenCL/CUDA)
|
||||
bool AddrSpaceMapMangling;
|
||||
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTReader;
|
||||
friend class ASTWriter;
|
||||
friend class CXXRecordDecl;
|
||||
|
||||
const TargetInfo *Target;
|
||||
const TargetInfo *AuxTarget;
|
||||
const TargetInfo *Target = nullptr;
|
||||
const TargetInfo *AuxTarget = nullptr;
|
||||
clang::PrintingPolicy PrintingPolicy;
|
||||
|
||||
public:
|
||||
@ -515,31 +539,33 @@ public:
|
||||
Builtin::Context &BuiltinInfo;
|
||||
mutable DeclarationNameTable DeclarationNames;
|
||||
IntrusiveRefCntPtr<ExternalASTSource> ExternalSource;
|
||||
ASTMutationListener *Listener;
|
||||
ASTMutationListener *Listener = nullptr;
|
||||
|
||||
/// \brief Contains parents of a node.
|
||||
typedef llvm::SmallVector<ast_type_traits::DynTypedNode, 2> ParentVector;
|
||||
using ParentVector = llvm::SmallVector<ast_type_traits::DynTypedNode, 2>;
|
||||
|
||||
/// \brief Maps from a node to its parents. This is used for nodes that have
|
||||
/// pointer identity only, which are more common and we can save space by
|
||||
/// only storing a unique pointer to them.
|
||||
typedef llvm::DenseMap<const void *,
|
||||
llvm::PointerUnion4<const Decl *, const Stmt *,
|
||||
ast_type_traits::DynTypedNode *,
|
||||
ParentVector *>> ParentMapPointers;
|
||||
using ParentMapPointers =
|
||||
llvm::DenseMap<const void *,
|
||||
llvm::PointerUnion4<const Decl *, const Stmt *,
|
||||
ast_type_traits::DynTypedNode *,
|
||||
ParentVector *>>;
|
||||
|
||||
/// Parent map for nodes without pointer identity. We store a full
|
||||
/// DynTypedNode for all keys.
|
||||
typedef llvm::DenseMap<
|
||||
ast_type_traits::DynTypedNode,
|
||||
llvm::PointerUnion4<const Decl *, const Stmt *,
|
||||
ast_type_traits::DynTypedNode *, ParentVector *>>
|
||||
ParentMapOtherNodes;
|
||||
using ParentMapOtherNodes =
|
||||
llvm::DenseMap<ast_type_traits::DynTypedNode,
|
||||
llvm::PointerUnion4<const Decl *, const Stmt *,
|
||||
ast_type_traits::DynTypedNode *,
|
||||
ParentVector *>>;
|
||||
|
||||
/// Container for either a single DynTypedNode or for an ArrayRef to
|
||||
/// DynTypedNode. For use with ParentMap.
|
||||
class DynTypedNodeList {
|
||||
typedef ast_type_traits::DynTypedNode DynTypedNode;
|
||||
using DynTypedNode = ast_type_traits::DynTypedNode;
|
||||
|
||||
llvm::AlignedCharArrayUnion<ast_type_traits::DynTypedNode,
|
||||
ArrayRef<DynTypedNode>> Storage;
|
||||
bool IsSingleNode;
|
||||
@ -548,6 +574,7 @@ public:
|
||||
DynTypedNodeList(const DynTypedNode &N) : IsSingleNode(true) {
|
||||
new (Storage.buffer) DynTypedNode(N);
|
||||
}
|
||||
|
||||
DynTypedNodeList(ArrayRef<DynTypedNode> A) : IsSingleNode(false) {
|
||||
new (Storage.buffer) ArrayRef<DynTypedNode>(A);
|
||||
}
|
||||
@ -626,13 +653,14 @@ public:
|
||||
template <typename T> T *Allocate(size_t Num = 1) const {
|
||||
return static_cast<T *>(Allocate(Num * sizeof(T), alignof(T)));
|
||||
}
|
||||
void Deallocate(void *Ptr) const { }
|
||||
void Deallocate(void *Ptr) const {}
|
||||
|
||||
/// Return the total amount of physical memory allocated for representing
|
||||
/// AST nodes and type information.
|
||||
size_t getASTAllocatedMemory() const {
|
||||
return BumpAlloc.getTotalMemory();
|
||||
}
|
||||
|
||||
/// Return the total memory used for various side tables.
|
||||
size_t getSideTableAllocatedMemory() const;
|
||||
|
||||
@ -649,6 +677,7 @@ public:
|
||||
/// Returns empty type if there is no appropriate target types.
|
||||
QualType getIntTypeForBitwidth(unsigned DestWidth,
|
||||
unsigned Signed) const;
|
||||
|
||||
/// getRealTypeForBitwidth -
|
||||
/// sets floating point QualTy according to specified bitwidth.
|
||||
/// Returns empty type if there is no appropriate target types.
|
||||
@ -676,7 +705,7 @@ public:
|
||||
RawCommentList Comments;
|
||||
|
||||
/// \brief True if comments are already loaded from ExternalASTSource.
|
||||
mutable bool CommentsLoaded;
|
||||
mutable bool CommentsLoaded = false;
|
||||
|
||||
class RawCommentAndCacheFlags {
|
||||
public:
|
||||
@ -759,24 +788,24 @@ public:
|
||||
}
|
||||
|
||||
/// \brief Return the documentation comment attached to a given declaration.
|
||||
/// Returns NULL if no comment is attached.
|
||||
/// Returns nullptr if no comment is attached.
|
||||
///
|
||||
/// \param OriginalDecl if not NULL, is set to declaration AST node that had
|
||||
/// the comment, if the comment we found comes from a redeclaration.
|
||||
/// \param OriginalDecl if not nullptr, is set to declaration AST node that
|
||||
/// had the comment, if the comment we found comes from a redeclaration.
|
||||
const RawComment *
|
||||
getRawCommentForAnyRedecl(const Decl *D,
|
||||
const Decl **OriginalDecl = nullptr) const;
|
||||
|
||||
/// Return parsed documentation comment attached to a given declaration.
|
||||
/// Returns NULL if no comment is attached.
|
||||
/// Returns nullptr if no comment is attached.
|
||||
///
|
||||
/// \param PP the Preprocessor used with this TU. Could be NULL if
|
||||
/// \param PP the Preprocessor used with this TU. Could be nullptr if
|
||||
/// preprocessor is not available.
|
||||
comments::FullComment *getCommentForDecl(const Decl *D,
|
||||
const Preprocessor *PP) const;
|
||||
|
||||
/// Return parsed documentation comment attached to a given declaration.
|
||||
/// Returns NULL if no comment is attached. Does not look at any
|
||||
/// Returns nullptr if no comment is attached. Does not look at any
|
||||
/// redeclarations of the declaration.
|
||||
comments::FullComment *getLocalCommentForDeclUncached(const Decl *D) const;
|
||||
|
||||
@ -788,16 +817,16 @@ private:
|
||||
|
||||
/// \brief Iterator that visits import declarations.
|
||||
class import_iterator {
|
||||
ImportDecl *Import;
|
||||
ImportDecl *Import = nullptr;
|
||||
|
||||
public:
|
||||
typedef ImportDecl *value_type;
|
||||
typedef ImportDecl *reference;
|
||||
typedef ImportDecl *pointer;
|
||||
typedef int difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
using value_type = ImportDecl *;
|
||||
using reference = ImportDecl *;
|
||||
using pointer = ImportDecl *;
|
||||
using difference_type = int;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
import_iterator() : Import() {}
|
||||
import_iterator() = default;
|
||||
explicit import_iterator(ImportDecl *Import) : Import(Import) {}
|
||||
|
||||
reference operator*() const { return Import; }
|
||||
@ -876,7 +905,7 @@ public:
|
||||
void setInstantiatedFromUnnamedFieldDecl(FieldDecl *Inst, FieldDecl *Tmpl);
|
||||
|
||||
// Access to the set of methods overridden by the given C++ method.
|
||||
typedef CXXMethodVector::const_iterator overridden_cxx_method_iterator;
|
||||
using overridden_cxx_method_iterator = CXXMethodVector::const_iterator;
|
||||
overridden_cxx_method_iterator
|
||||
overridden_methods_begin(const CXXMethodDecl *Method) const;
|
||||
|
||||
@ -884,8 +913,10 @@ public:
|
||||
overridden_methods_end(const CXXMethodDecl *Method) const;
|
||||
|
||||
unsigned overridden_methods_size(const CXXMethodDecl *Method) const;
|
||||
typedef llvm::iterator_range<overridden_cxx_method_iterator>
|
||||
overridden_method_range;
|
||||
|
||||
using overridden_method_range =
|
||||
llvm::iterator_range<overridden_cxx_method_iterator>;
|
||||
|
||||
overridden_method_range overridden_methods(const CXXMethodDecl *Method) const;
|
||||
|
||||
/// \brief Note that the given C++ \p Method overrides the given \p
|
||||
@ -912,7 +943,8 @@ public:
|
||||
return Import->NextLocalImport;
|
||||
}
|
||||
|
||||
typedef llvm::iterator_range<import_iterator> import_range;
|
||||
using import_range = llvm::iterator_range<import_iterator>;
|
||||
|
||||
import_range local_imports() const {
|
||||
return import_range(import_iterator(FirstLocalImport), import_iterator());
|
||||
}
|
||||
@ -929,6 +961,7 @@ public:
|
||||
/// and should be visible whenever \p M is visible.
|
||||
void mergeDefinitionIntoModule(NamedDecl *ND, Module *M,
|
||||
bool NotifyListeners = true);
|
||||
|
||||
/// \brief Clean up the merged definition list. Call this if you might have
|
||||
/// added duplicates into the list.
|
||||
void deduplicateMergedDefinitonsFor(NamedDecl *ND);
|
||||
@ -973,6 +1006,7 @@ public:
|
||||
CanQualType UnsignedLongLongTy, UnsignedInt128Ty;
|
||||
CanQualType FloatTy, DoubleTy, LongDoubleTy, Float128Ty;
|
||||
CanQualType HalfTy; // [OpenCL 6.1.1.1], ARM NEON
|
||||
CanQualType Float16Ty; // C11 extension ISO/IEC TS 18661-3
|
||||
CanQualType FloatComplexTy, DoubleComplexTy, LongDoubleComplexTy;
|
||||
CanQualType Float128ComplexTy;
|
||||
CanQualType VoidPtrTy, NullPtrTy;
|
||||
@ -1067,7 +1101,14 @@ public:
|
||||
/// The resulting type has a union of the qualifiers from T and the address
|
||||
/// space. If T already has an address space specifier, it is silently
|
||||
/// replaced.
|
||||
QualType getAddrSpaceQualType(QualType T, unsigned AddressSpace) const;
|
||||
QualType getAddrSpaceQualType(QualType T, LangAS AddressSpace) const;
|
||||
|
||||
/// \brief Remove any existing address space on the type and returns the type
|
||||
/// with qualifiers intact (or that's the idea anyway)
|
||||
///
|
||||
/// The return type should be T with all prior qualifiers minus the address
|
||||
/// space.
|
||||
QualType removeAddrSpaceQualType(QualType T) const;
|
||||
|
||||
/// \brief Apply Objective-C protocol qualifiers to the given type.
|
||||
/// \param allowOnPointerType specifies if we can apply protocol
|
||||
@ -1175,6 +1216,7 @@ public:
|
||||
|
||||
/// \brief Return a read_only pipe type for the specified type.
|
||||
QualType getReadPipeType(QualType T) const;
|
||||
|
||||
/// \brief Return a write_only pipe type for the specified type.
|
||||
QualType getWritePipeType(QualType T) const;
|
||||
|
||||
@ -1182,9 +1224,16 @@ public:
|
||||
/// pointer to blocks.
|
||||
QualType getBlockDescriptorExtendedType() const;
|
||||
|
||||
/// Map an AST Type to an OpenCLTypeKind enum value.
|
||||
TargetInfo::OpenCLTypeKind getOpenCLTypeKind(const Type *T) const;
|
||||
|
||||
/// Get address space for OpenCL type.
|
||||
LangAS getOpenCLTypeAddrSpace(const Type *T) const;
|
||||
|
||||
void setcudaConfigureCallDecl(FunctionDecl *FD) {
|
||||
cudaConfigureCallDecl = FD;
|
||||
}
|
||||
|
||||
FunctionDecl *getcudaConfigureCallDecl() {
|
||||
return cudaConfigureCallDecl;
|
||||
}
|
||||
@ -1192,7 +1241,6 @@ public:
|
||||
/// Returns true iff we need copy/dispose helpers for the given type.
|
||||
bool BlockRequiresCopying(QualType Ty, const VarDecl *D);
|
||||
|
||||
|
||||
/// Returns true, if given type has a known lifetime. HasByrefExtendedLayout is set
|
||||
/// to false in this case. If HasByrefExtendedLayout returns true, byref variable
|
||||
/// has extended lifetime.
|
||||
@ -1269,6 +1317,10 @@ public:
|
||||
Expr *SizeExpr,
|
||||
SourceLocation AttrLoc) const;
|
||||
|
||||
QualType getDependentAddressSpaceType(QualType PointeeType,
|
||||
Expr *AddrSpaceExpr,
|
||||
SourceLocation AttrLoc) const;
|
||||
|
||||
/// \brief Return a K&R style C function type like 'int()'.
|
||||
QualType getFunctionNoProtoType(QualType ResultTy,
|
||||
const FunctionType::ExtInfo &Info) const;
|
||||
@ -1396,6 +1448,7 @@ public:
|
||||
QualType Canonical = QualType()) const;
|
||||
|
||||
bool ObjCObjectAdoptsQTypeProtocols(QualType QT, ObjCInterfaceDecl *Decl);
|
||||
|
||||
/// QIdProtocolsAdoptObjCObjectProtocols - Checks that protocols in
|
||||
/// QT's qualified-id protocol list adopt all protocols in IDecl's list
|
||||
/// of protocols.
|
||||
@ -1426,7 +1479,7 @@ public:
|
||||
/// \brief C++11 deduction pattern for 'auto &&' type.
|
||||
QualType getAutoRRefDeductType() const;
|
||||
|
||||
/// \brief C++1z deduced class template specialization type.
|
||||
/// \brief C++17 deduced class template specialization type.
|
||||
QualType getDeducedTemplateSpecializationType(TemplateName Template,
|
||||
QualType DeducedType,
|
||||
bool IsDependent) const;
|
||||
@ -1488,6 +1541,11 @@ public:
|
||||
/// <stddef.h>. Pointer - pointer requires this (C99 6.5.6p9).
|
||||
QualType getPointerDiffType() const;
|
||||
|
||||
/// \brief Return the unique unsigned counterpart of "ptrdiff_t"
|
||||
/// integer type. The standard (C11 7.21.6.1p7) refers to this type
|
||||
/// in the definition of %tu format specifier.
|
||||
QualType getUnsignedPointerDiffType() const;
|
||||
|
||||
/// \brief Return the unique type for "pid_t" defined in
|
||||
/// <sys/types.h>. We need this to compute the correct type for vfork().
|
||||
QualType getProcessIDType() const;
|
||||
@ -1581,6 +1639,24 @@ public:
|
||||
return NSCopyingName;
|
||||
}
|
||||
|
||||
CanQualType getNSUIntegerType() const {
|
||||
assert(Target && "Expected target to be initialized");
|
||||
const llvm::Triple &T = Target->getTriple();
|
||||
// Windows is LLP64 rather than LP64
|
||||
if (T.isOSWindows() && T.isArch64Bit())
|
||||
return UnsignedLongLongTy;
|
||||
return UnsignedLongTy;
|
||||
}
|
||||
|
||||
CanQualType getNSIntegerType() const {
|
||||
assert(Target && "Expected target to be initialized");
|
||||
const llvm::Triple &T = Target->getTriple();
|
||||
// Windows is LLP64 rather than LP64
|
||||
if (T.isOSWindows() && T.isArch64Bit())
|
||||
return LongLongTy;
|
||||
return LongTy;
|
||||
}
|
||||
|
||||
/// Retrieve the identifier 'bool'.
|
||||
IdentifierInfo *getBoolName() const {
|
||||
if (!BoolName)
|
||||
@ -1865,10 +1941,17 @@ public:
|
||||
const TemplateArgument &ArgPack) const;
|
||||
|
||||
enum GetBuiltinTypeError {
|
||||
GE_None, ///< No error
|
||||
GE_Missing_stdio, ///< Missing a type from <stdio.h>
|
||||
GE_Missing_setjmp, ///< Missing a type from <setjmp.h>
|
||||
GE_Missing_ucontext ///< Missing a type from <ucontext.h>
|
||||
/// No error
|
||||
GE_None,
|
||||
|
||||
/// Missing a type from <stdio.h>
|
||||
GE_Missing_stdio,
|
||||
|
||||
/// Missing a type from <setjmp.h>
|
||||
GE_Missing_setjmp,
|
||||
|
||||
/// Missing a type from <ucontext.h>
|
||||
GE_Missing_ucontext
|
||||
};
|
||||
|
||||
/// \brief Return the type for the specified builtin.
|
||||
@ -2019,7 +2102,7 @@ public:
|
||||
getASTObjCImplementationLayout(const ObjCImplementationDecl *D) const;
|
||||
|
||||
/// \brief Get our current best idea for the key function of the
|
||||
/// given record decl, or NULL if there isn't one.
|
||||
/// given record decl, or nullptr if there isn't one.
|
||||
///
|
||||
/// The key function is, according to the Itanium C++ ABI section 5.2.3:
|
||||
/// ...the first non-pure virtual function that is not inline at the
|
||||
@ -2072,6 +2155,10 @@ public:
|
||||
void CollectInheritedProtocols(const Decl *CDecl,
|
||||
llvm::SmallPtrSet<ObjCProtocolDecl*, 8> &Protocols);
|
||||
|
||||
/// \brief Return true if the specified type has unique object representations
|
||||
/// according to (C++17 [meta.unary.prop]p9)
|
||||
bool hasUniqueObjectRepresentations(QualType Ty) const;
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Type Operators
|
||||
//===--------------------------------------------------------------------===//
|
||||
@ -2103,7 +2190,6 @@ public:
|
||||
bool hasSameType(QualType T1, QualType T2) const {
|
||||
return getCanonicalType(T1) == getCanonicalType(T2);
|
||||
}
|
||||
|
||||
bool hasSameType(const Type *T1, const Type *T2) const {
|
||||
return getCanonicalType(T1) == getCanonicalType(T2);
|
||||
}
|
||||
@ -2192,7 +2278,7 @@ public:
|
||||
getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const;
|
||||
|
||||
/// \brief Retrieves the default calling convention for the current target.
|
||||
CallingConv getDefaultCallingConvention(bool isVariadic,
|
||||
CallingConv getDefaultCallingConvention(bool IsVariadic,
|
||||
bool IsCXXMethod) const;
|
||||
|
||||
/// \brief Retrieves the "canonical" template name that refers to a
|
||||
@ -2326,14 +2412,14 @@ public:
|
||||
return getTargetAddressSpace(Q.getAddressSpace());
|
||||
}
|
||||
|
||||
unsigned getTargetAddressSpace(unsigned AS) const;
|
||||
unsigned getTargetAddressSpace(LangAS AS) const;
|
||||
|
||||
/// Get target-dependent integer value for null pointer which is used for
|
||||
/// constant folding.
|
||||
uint64_t getTargetNullPointerValue(QualType QT) const;
|
||||
|
||||
bool addressSpaceMapManglingFor(unsigned AS) const {
|
||||
return AddrSpaceMapMangling || AS >= LangAS::FirstTargetAddressSpace;
|
||||
bool addressSpaceMapManglingFor(LangAS AS) const {
|
||||
return AddrSpaceMapMangling || isTargetAddressSpace(AS);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -2355,12 +2441,15 @@ public:
|
||||
bool isObjCIdType(QualType T) const {
|
||||
return T == getObjCIdType();
|
||||
}
|
||||
|
||||
bool isObjCClassType(QualType T) const {
|
||||
return T == getObjCClassType();
|
||||
}
|
||||
|
||||
bool isObjCSelType(QualType T) const {
|
||||
return T == getObjCSelType();
|
||||
}
|
||||
|
||||
bool ObjCQualifiedIdTypesAreCompatible(QualType LHS, QualType RHS,
|
||||
bool ForCompare);
|
||||
|
||||
@ -2394,9 +2483,30 @@ public:
|
||||
|
||||
QualType mergeObjCGCQualifiers(QualType, QualType);
|
||||
|
||||
bool doFunctionTypesMatchOnExtParameterInfos(
|
||||
const FunctionProtoType *FromFunctionType,
|
||||
const FunctionProtoType *ToFunctionType);
|
||||
/// This function merges the ExtParameterInfo lists of two functions. It
|
||||
/// returns true if the lists are compatible. The merged list is returned in
|
||||
/// NewParamInfos.
|
||||
///
|
||||
/// \param FirstFnType The type of the first function.
|
||||
///
|
||||
/// \param SecondFnType The type of the second function.
|
||||
///
|
||||
/// \param CanUseFirst This flag is set to true if the first function's
|
||||
/// ExtParameterInfo list can be used as the composite list of
|
||||
/// ExtParameterInfo.
|
||||
///
|
||||
/// \param CanUseSecond This flag is set to true if the second function's
|
||||
/// ExtParameterInfo list can be used as the composite list of
|
||||
/// ExtParameterInfo.
|
||||
///
|
||||
/// \param NewParamInfos The composite list of ExtParameterInfo. The list is
|
||||
/// empty if none of the flags are set.
|
||||
///
|
||||
bool mergeExtParameterInfo(
|
||||
const FunctionProtoType *FirstFnType,
|
||||
const FunctionProtoType *SecondFnType,
|
||||
bool &CanUseFirst, bool &CanUseSecond,
|
||||
SmallVectorImpl<FunctionProtoType::ExtParameterInfo> &NewParamInfos);
|
||||
|
||||
void ResetObjCLayout(const ObjCContainerDecl *CD);
|
||||
|
||||
@ -2432,12 +2542,13 @@ public:
|
||||
|
||||
bool isSentinelNullExpr(const Expr *E);
|
||||
|
||||
/// \brief Get the implementation of the ObjCInterfaceDecl \p D, or NULL if
|
||||
/// \brief Get the implementation of the ObjCInterfaceDecl \p D, or nullptr if
|
||||
/// none exists.
|
||||
ObjCImplementationDecl *getObjCImplementation(ObjCInterfaceDecl *D);
|
||||
/// \brief Get the implementation of the ObjCCategoryDecl \p D, or NULL if
|
||||
|
||||
/// \brief Get the implementation of the ObjCCategoryDecl \p D, or nullptr if
|
||||
/// none exists.
|
||||
ObjCCategoryImplDecl *getObjCImplementation(ObjCCategoryDecl *D);
|
||||
ObjCCategoryImplDecl *getObjCImplementation(ObjCCategoryDecl *D);
|
||||
|
||||
/// \brief Return true if there is at least one \@implementation in the TU.
|
||||
bool AnyObjCImplementation() {
|
||||
@ -2447,6 +2558,7 @@ public:
|
||||
/// \brief Set the implementation of ObjCInterfaceDecl.
|
||||
void setObjCImplementation(ObjCInterfaceDecl *IFaceD,
|
||||
ObjCImplementationDecl *ImplD);
|
||||
|
||||
/// \brief Set the implementation of ObjCCategoryDecl.
|
||||
void setObjCImplementation(ObjCCategoryDecl *CatD,
|
||||
ObjCCategoryImplDecl *ImplD);
|
||||
@ -2466,8 +2578,9 @@ public:
|
||||
|
||||
/// \brief Set the copy inialization expression of a block var decl.
|
||||
void setBlockVarCopyInits(VarDecl*VD, Expr* Init);
|
||||
|
||||
/// \brief Get the copy initialization expression of the VarDecl \p VD, or
|
||||
/// NULL if none exists.
|
||||
/// nullptr if none exists.
|
||||
Expr *getBlockVarCopyInits(const VarDecl* VD);
|
||||
|
||||
/// \brief Allocate an uninitialized TypeSourceInfo.
|
||||
@ -2636,6 +2749,7 @@ private:
|
||||
const FieldDecl *Field,
|
||||
bool includeVBases = true,
|
||||
QualType *NotEncodedT=nullptr) const;
|
||||
|
||||
public:
|
||||
// Adds the encoding of a method parameter or return type.
|
||||
void getObjCEncodingForMethodParameter(Decl::ObjCDeclQualifier QT,
|
||||
@ -2647,11 +2761,19 @@ public:
|
||||
bool isMSStaticDataMemberInlineDefinition(const VarDecl *VD) const;
|
||||
|
||||
enum class InlineVariableDefinitionKind {
|
||||
None, ///< Not an inline variable.
|
||||
Weak, ///< Weak definition of inline variable.
|
||||
WeakUnknown, ///< Weak for now, might become strong later in this TU.
|
||||
Strong ///< Strong definition.
|
||||
/// Not an inline variable.
|
||||
None,
|
||||
|
||||
/// Weak definition of inline variable.
|
||||
Weak,
|
||||
|
||||
/// Weak for now, might become strong later in this TU.
|
||||
WeakUnknown,
|
||||
|
||||
/// Strong definition.
|
||||
Strong
|
||||
};
|
||||
|
||||
/// \brief Determine whether a definition of this inline variable should
|
||||
/// be treated as a weak or strong definition. For compatibility with
|
||||
/// C++14 and before, for a constexpr static data member, if there is an
|
||||
@ -2661,6 +2783,9 @@ public:
|
||||
getInlineVariableDefinitionKind(const VarDecl *VD) const;
|
||||
|
||||
private:
|
||||
friend class DeclarationNameTable;
|
||||
friend class DeclContext;
|
||||
|
||||
const ASTRecordLayout &
|
||||
getObjCLayout(const ObjCInterfaceDecl *D,
|
||||
const ObjCImplementationDecl *Impl) const;
|
||||
@ -2673,26 +2798,23 @@ private:
|
||||
// into the datastructures which avoids this mess during deallocation but is
|
||||
// wasteful of memory, and here we require a lot of error prone book keeping
|
||||
// in order to track and run destructors while we're tearing things down.
|
||||
typedef llvm::SmallVector<std::pair<void (*)(void *), void *>, 16>
|
||||
DeallocationFunctionsAndArguments;
|
||||
using DeallocationFunctionsAndArguments =
|
||||
llvm::SmallVector<std::pair<void (*)(void *), void *>, 16>;
|
||||
DeallocationFunctionsAndArguments Deallocations;
|
||||
|
||||
// FIXME: This currently contains the set of StoredDeclMaps used
|
||||
// by DeclContext objects. This probably should not be in ASTContext,
|
||||
// but we include it here so that ASTContext can quickly deallocate them.
|
||||
llvm::PointerIntPair<StoredDeclsMap*,1> LastSDM;
|
||||
|
||||
friend class DeclContext;
|
||||
friend class DeclarationNameTable;
|
||||
|
||||
void ReleaseDeclContextMaps();
|
||||
void ReleaseParentMapEntries();
|
||||
llvm::PointerIntPair<StoredDeclsMap *, 1> LastSDM;
|
||||
|
||||
std::unique_ptr<ParentMapPointers> PointerParents;
|
||||
std::unique_ptr<ParentMapOtherNodes> OtherParents;
|
||||
|
||||
std::unique_ptr<VTableContextBase> VTContext;
|
||||
|
||||
void ReleaseDeclContextMaps();
|
||||
void ReleaseParentMapEntries();
|
||||
|
||||
public:
|
||||
enum PragmaSectionFlag : unsigned {
|
||||
PSF_None = 0,
|
||||
@ -2712,27 +2834,26 @@ public:
|
||||
SectionInfo(DeclaratorDecl *Decl,
|
||||
SourceLocation PragmaSectionLocation,
|
||||
int SectionFlags)
|
||||
: Decl(Decl),
|
||||
PragmaSectionLocation(PragmaSectionLocation),
|
||||
SectionFlags(SectionFlags) {}
|
||||
: Decl(Decl), PragmaSectionLocation(PragmaSectionLocation),
|
||||
SectionFlags(SectionFlags) {}
|
||||
};
|
||||
|
||||
llvm::StringMap<SectionInfo> SectionInfos;
|
||||
};
|
||||
|
||||
/// \brief Utility function for constructing a nullary selector.
|
||||
static inline Selector GetNullarySelector(StringRef name, ASTContext& Ctx) {
|
||||
inline Selector GetNullarySelector(StringRef name, ASTContext &Ctx) {
|
||||
IdentifierInfo* II = &Ctx.Idents.get(name);
|
||||
return Ctx.Selectors.getSelector(0, &II);
|
||||
}
|
||||
|
||||
/// \brief Utility function for constructing an unary selector.
|
||||
static inline Selector GetUnarySelector(StringRef name, ASTContext& Ctx) {
|
||||
inline Selector GetUnarySelector(StringRef name, ASTContext &Ctx) {
|
||||
IdentifierInfo* II = &Ctx.Idents.get(name);
|
||||
return Ctx.Selectors.getSelector(1, &II);
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
// operator new and delete aren't allowed inside namespaces.
|
||||
|
||||
@ -2763,11 +2884,12 @@ static inline Selector GetUnarySelector(StringRef name, ASTContext& Ctx) {
|
||||
/// @param C The ASTContext that provides the allocator.
|
||||
/// @param Alignment The alignment of the allocated memory (if the underlying
|
||||
/// allocator supports it).
|
||||
/// @return The allocated memory. Could be NULL.
|
||||
/// @return The allocated memory. Could be nullptr.
|
||||
inline void *operator new(size_t Bytes, const clang::ASTContext &C,
|
||||
size_t Alignment) {
|
||||
return C.Allocate(Bytes, Alignment);
|
||||
}
|
||||
|
||||
/// @brief Placement delete companion to the new above.
|
||||
///
|
||||
/// This operator is just a companion to the new above. There is no way of
|
||||
@ -2800,7 +2922,7 @@ inline void operator delete(void *Ptr, const clang::ASTContext &C, size_t) {
|
||||
/// @param C The ASTContext that provides the allocator.
|
||||
/// @param Alignment The alignment of the allocated memory (if the underlying
|
||||
/// allocator supports it).
|
||||
/// @return The allocated memory. Could be NULL.
|
||||
/// @return The allocated memory. Could be nullptr.
|
||||
inline void *operator new[](size_t Bytes, const clang::ASTContext& C,
|
||||
size_t Alignment = 8) {
|
||||
return C.Allocate(Bytes, Alignment);
|
||||
|
@ -22,6 +22,7 @@ namespace clang {
|
||||
class CXXRecordDecl;
|
||||
class Decl;
|
||||
class DeclContext;
|
||||
class Expr;
|
||||
class FieldDecl;
|
||||
class FunctionDecl;
|
||||
class FunctionTemplateDecl;
|
||||
@ -35,6 +36,7 @@ namespace clang {
|
||||
class QualType;
|
||||
class RecordDecl;
|
||||
class TagDecl;
|
||||
class ValueDecl;
|
||||
class VarDecl;
|
||||
class VarTemplateDecl;
|
||||
class VarTemplateSpecializationDecl;
|
||||
@ -80,13 +82,19 @@ public:
|
||||
|
||||
/// \brief A virtual destructor's operator delete has been resolved.
|
||||
virtual void ResolvedOperatorDelete(const CXXDestructorDecl *DD,
|
||||
const FunctionDecl *Delete) {}
|
||||
const FunctionDecl *Delete,
|
||||
Expr *ThisArg) {}
|
||||
|
||||
/// \brief An implicit member got a definition.
|
||||
virtual void CompletedImplicitDefinition(const FunctionDecl *D) {}
|
||||
|
||||
/// \brief A static data member was implicitly instantiated.
|
||||
virtual void StaticDataMemberInstantiated(const VarDecl *D) {}
|
||||
/// \brief The instantiation of a templated function or variable was
|
||||
/// requested. In particular, the point of instantiation and template
|
||||
/// specialization kind of \p D may have changed.
|
||||
virtual void InstantiationRequested(const ValueDecl *D) {}
|
||||
|
||||
/// \brief A templated variable's definition was implicitly instantiated.
|
||||
virtual void VariableDefinitionInstantiated(const VarDecl *D) {}
|
||||
|
||||
/// \brief A function template's definition was instantiated.
|
||||
virtual void FunctionDefinitionInstantiated(const FunctionDecl *D) {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- ASTUnresolvedSet.h - Unresolved sets of declarations ---*- C++ -*-===//
|
||||
//===- ASTUnresolvedSet.h - Unresolved sets of declarations -----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,14 +16,22 @@
|
||||
#define LLVM_CLANG_AST_ASTUNRESOLVEDSET_H
|
||||
|
||||
#include "clang/AST/ASTVector.h"
|
||||
#include "clang/AST/DeclAccessPair.h"
|
||||
#include "clang/AST/UnresolvedSet.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class NamedDecl;
|
||||
|
||||
/// \brief An UnresolvedSet-like class which uses the ASTContext's allocator.
|
||||
class ASTUnresolvedSet {
|
||||
friend class LazyASTUnresolvedSet;
|
||||
|
||||
struct DeclsTy : ASTVector<DeclAccessPair> {
|
||||
DeclsTy() {}
|
||||
DeclsTy() = default;
|
||||
DeclsTy(ASTContext &C, unsigned N) : ASTVector<DeclAccessPair>(C, N) {}
|
||||
|
||||
bool isLazy() const { return getTag(); }
|
||||
@ -32,14 +40,12 @@ class ASTUnresolvedSet {
|
||||
|
||||
DeclsTy Decls;
|
||||
|
||||
friend class LazyASTUnresolvedSet;
|
||||
|
||||
public:
|
||||
ASTUnresolvedSet() {}
|
||||
ASTUnresolvedSet() = default;
|
||||
ASTUnresolvedSet(ASTContext &C, unsigned N) : Decls(C, N) {}
|
||||
|
||||
typedef UnresolvedSetIterator iterator;
|
||||
typedef UnresolvedSetIterator const_iterator;
|
||||
using iterator = UnresolvedSetIterator;
|
||||
using const_iterator = UnresolvedSetIterator;
|
||||
|
||||
iterator begin() { return iterator(Decls.begin()); }
|
||||
iterator end() { return iterator(Decls.end()); }
|
||||
@ -98,13 +104,14 @@ public:
|
||||
}
|
||||
|
||||
void reserve(ASTContext &C, unsigned N) { Impl.reserve(C, N); }
|
||||
|
||||
void addLazyDecl(ASTContext &C, uintptr_t ID, AccessSpecifier AS) {
|
||||
assert(Impl.empty() || Impl.Decls.isLazy());
|
||||
Impl.Decls.setLazy(true);
|
||||
Impl.addDecl(C, reinterpret_cast<NamedDecl*>(ID << 2), AS);
|
||||
Impl.addDecl(C, reinterpret_cast<NamedDecl *>(ID << 2), AS);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_ASTUNRESOLVEDSET_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===- ASTVector.h - Vector that uses ASTContext for allocation --*- C++ -*-=//
|
||||
//===- ASTVector.h - Vector that uses ASTContext for allocation ---*- C++ -*-=//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -18,22 +18,26 @@
|
||||
#ifndef LLVM_CLANG_AST_ASTVECTOR_H
|
||||
#define LLVM_CLANG_AST_ASTVECTOR_H
|
||||
|
||||
#include "clang/AST/AttrIterator.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
class ASTContext;
|
||||
|
||||
class ASTContext;
|
||||
|
||||
template<typename T>
|
||||
class ASTVector {
|
||||
private:
|
||||
T *Begin, *End;
|
||||
llvm::PointerIntPair<T*, 1, bool> Capacity;
|
||||
T *Begin = nullptr;
|
||||
T *End = nullptr;
|
||||
llvm::PointerIntPair<T *, 1, bool> Capacity;
|
||||
|
||||
void setEnd(T *P) { this->End = P; }
|
||||
|
||||
@ -45,7 +49,7 @@ protected:
|
||||
|
||||
public:
|
||||
// Default ctor - Initialize to empty.
|
||||
ASTVector() : Begin(nullptr), End(nullptr), Capacity(nullptr, false) {}
|
||||
ASTVector() : Capacity(nullptr, false) {}
|
||||
|
||||
ASTVector(ASTVector &&O) : Begin(O.Begin), End(O.End), Capacity(O.Capacity) {
|
||||
O.Begin = O.End = nullptr;
|
||||
@ -53,14 +57,15 @@ public:
|
||||
O.Capacity.setInt(false);
|
||||
}
|
||||
|
||||
ASTVector(const ASTContext &C, unsigned N)
|
||||
: Begin(nullptr), End(nullptr), Capacity(nullptr, false) {
|
||||
ASTVector(const ASTContext &C, unsigned N) : Capacity(nullptr, false) {
|
||||
reserve(C, N);
|
||||
}
|
||||
|
||||
ASTVector &operator=(ASTVector &&RHS) {
|
||||
ASTVector O(std::move(RHS));
|
||||
|
||||
using std::swap;
|
||||
|
||||
swap(Begin, O.Begin);
|
||||
swap(End, O.End);
|
||||
swap(Capacity, O.Capacity);
|
||||
@ -74,19 +79,19 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
typedef size_t size_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef T value_type;
|
||||
typedef T* iterator;
|
||||
typedef const T* const_iterator;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using value_type = T;
|
||||
using iterator = T *;
|
||||
using const_iterator = const T *;
|
||||
|
||||
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
|
||||
typedef std::reverse_iterator<iterator> reverse_iterator;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
|
||||
typedef T& reference;
|
||||
typedef const T& const_reference;
|
||||
typedef T* pointer;
|
||||
typedef const T* const_pointer;
|
||||
using reference = T &;
|
||||
using const_reference = const T &;
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
|
||||
// forward iterator creation methods.
|
||||
iterator begin() { return Begin; }
|
||||
@ -175,7 +180,6 @@ public:
|
||||
size_t capacity() const { return this->capacity_ptr() - Begin; }
|
||||
|
||||
/// append - Add the specified range to the end of the SmallVector.
|
||||
///
|
||||
template<typename in_iter>
|
||||
void append(const ASTContext &C, in_iter in_start, in_iter in_end) {
|
||||
size_type NumInputs = std::distance(in_start, in_end);
|
||||
@ -195,7 +199,6 @@ public:
|
||||
}
|
||||
|
||||
/// append - Add the specified range to the end of the SmallVector.
|
||||
///
|
||||
void append(const ASTContext &C, size_type NumInputs, const T &Elt) {
|
||||
// Grow allocated space if needed.
|
||||
if (NumInputs > size_type(this->capacity_ptr()-this->end()))
|
||||
@ -368,6 +371,7 @@ protected:
|
||||
const_iterator capacity_ptr() const {
|
||||
return (iterator) Capacity.getPointer();
|
||||
}
|
||||
|
||||
iterator capacity_ptr() { return (iterator)Capacity.getPointer(); }
|
||||
};
|
||||
|
||||
@ -401,5 +405,6 @@ void ASTVector<T>::grow(const ASTContext &C, size_t MinSize) {
|
||||
Capacity.setPointer(Begin+NewCapacity);
|
||||
}
|
||||
|
||||
} // end: clang namespace
|
||||
#endif
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_ASTVECTOR_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- AttrIterator.h - Classes for attribute iteration -------*- C++ -*-===//
|
||||
//===- AttrIterator.h - Classes for attribute iteration ---------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -15,16 +15,23 @@
|
||||
#define LLVM_CLANG_AST_ATTRITERATOR_H
|
||||
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace clang {
|
||||
class ASTContext;
|
||||
class Attr;
|
||||
}
|
||||
|
||||
class ASTContext;
|
||||
class Attr;
|
||||
|
||||
} // namespace clang
|
||||
|
||||
// Defined in ASTContext.h
|
||||
void *operator new(size_t Bytes, const clang::ASTContext &C,
|
||||
size_t Alignment = 8);
|
||||
|
||||
// FIXME: Being forced to not have a default argument here due to redeclaration
|
||||
// rules on default arguments sucks
|
||||
void *operator new[](size_t Bytes, const clang::ASTContext &C,
|
||||
@ -39,13 +46,13 @@ void operator delete[](void *Ptr, const clang::ASTContext &C, size_t);
|
||||
namespace clang {
|
||||
|
||||
/// AttrVec - A vector of Attr, which is how they are stored on the AST.
|
||||
typedef SmallVector<Attr *, 4> AttrVec;
|
||||
using AttrVec = SmallVector<Attr *, 4>;
|
||||
|
||||
/// specific_attr_iterator - Iterates over a subrange of an AttrVec, only
|
||||
/// providing attributes that are of a specific type.
|
||||
template <typename SpecificAttr, typename Container = AttrVec>
|
||||
class specific_attr_iterator {
|
||||
typedef typename Container::const_iterator Iterator;
|
||||
using Iterator = typename Container::const_iterator;
|
||||
|
||||
/// Current - The current, underlying iterator.
|
||||
/// In order to ensure we don't dereference an invalid iterator unless
|
||||
@ -67,14 +74,14 @@ class specific_attr_iterator {
|
||||
}
|
||||
|
||||
public:
|
||||
typedef SpecificAttr* value_type;
|
||||
typedef SpecificAttr* reference;
|
||||
typedef SpecificAttr* pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
using value_type = SpecificAttr *;
|
||||
using reference = SpecificAttr *;
|
||||
using pointer = SpecificAttr *;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
specific_attr_iterator() : Current() { }
|
||||
explicit specific_attr_iterator(Iterator i) : Current(i) { }
|
||||
specific_attr_iterator() = default;
|
||||
explicit specific_attr_iterator(Iterator i) : Current(i) {}
|
||||
|
||||
reference operator*() const {
|
||||
AdvanceToNext();
|
||||
@ -136,6 +143,6 @@ inline SpecificAttr *getSpecificAttr(const Container& container) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_ATTRITERATOR_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- BaseSubobject.h - BaseSubobject class ----------------------------===//
|
||||
//===- BaseSubobject.h - BaseSubobject class --------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -15,12 +15,15 @@
|
||||
#define LLVM_CLANG_AST_BASESUBOBJECT_H
|
||||
|
||||
#include "clang/AST/CharUnits.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class CXXRecordDecl;
|
||||
|
||||
// BaseSubobject - Uniquely identifies a direct or indirect base class.
|
||||
// Stores both the base class decl and the offset from the most derived class to
|
||||
// the base class. Used for vtable and VTT generation.
|
||||
@ -32,9 +35,9 @@ class BaseSubobject {
|
||||
CharUnits BaseOffset;
|
||||
|
||||
public:
|
||||
BaseSubobject() { }
|
||||
BaseSubobject() = default;
|
||||
BaseSubobject(const CXXRecordDecl *Base, CharUnits BaseOffset)
|
||||
: Base(Base), BaseOffset(BaseOffset) { }
|
||||
: Base(Base), BaseOffset(BaseOffset) {}
|
||||
|
||||
/// getBase - Returns the base class declaration.
|
||||
const CXXRecordDecl *getBase() const { return Base; }
|
||||
@ -47,7 +50,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
||||
@ -65,7 +68,8 @@ template<> struct DenseMapInfo<clang::BaseSubobject> {
|
||||
}
|
||||
|
||||
static unsigned getHashValue(const clang::BaseSubobject &Base) {
|
||||
typedef std::pair<const clang::CXXRecordDecl *, clang::CharUnits> PairTy;
|
||||
using PairTy = std::pair<const clang::CXXRecordDecl *, clang::CharUnits>;
|
||||
|
||||
return DenseMapInfo<PairTy>::getHashValue(PairTy(Base.getBase(),
|
||||
Base.getBaseOffset()));
|
||||
}
|
||||
@ -81,6 +85,6 @@ template <> struct isPodLike<clang::BaseSubobject> {
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_BASESUBOBJECT_H
|
||||
|
@ -133,6 +133,9 @@ FLOATING_TYPE(Double, DoubleTy)
|
||||
// 'long double'
|
||||
FLOATING_TYPE(LongDouble, LongDoubleTy)
|
||||
|
||||
// '_Float16'
|
||||
FLOATING_TYPE(Float16, HalfTy)
|
||||
|
||||
// '__float128'
|
||||
FLOATING_TYPE(Float128, Float128Ty)
|
||||
|
||||
|
@ -50,3 +50,6 @@ clang_tablegen(CommentCommandList.inc -gen-clang-comment-command-list
|
||||
SOURCE CommentCommands.td
|
||||
TARGET ClangCommentCommandList)
|
||||
|
||||
clang_tablegen(StmtDataCollectors.inc -gen-clang-data-collectors
|
||||
SOURCE StmtDataCollectors.td
|
||||
TARGET StmtDataCollectors)
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===------ CXXInheritance.h - C++ Inheritance ------------------*- C++ -*-===//
|
||||
//===- CXXInheritance.h - C++ Inheritance -----------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,19 +16,23 @@
|
||||
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include <cassert>
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class CXXBaseSpecifier;
|
||||
class CXXMethodDecl;
|
||||
class CXXRecordDecl;
|
||||
|
||||
class ASTContext;
|
||||
class NamedDecl;
|
||||
|
||||
/// \brief Represents an element in a path from a derived class to a
|
||||
@ -66,12 +70,12 @@ struct CXXBasePathElement {
|
||||
/// subobject is being used.
|
||||
class CXXBasePath : public SmallVector<CXXBasePathElement, 4> {
|
||||
public:
|
||||
CXXBasePath() : Access(AS_public) {}
|
||||
|
||||
/// \brief The access along this inheritance path. This is only
|
||||
/// calculated when recording paths. AS_none is a special value
|
||||
/// used to indicate a path which permits no legal access.
|
||||
AccessSpecifier Access;
|
||||
AccessSpecifier Access = AS_public;
|
||||
|
||||
CXXBasePath() = default;
|
||||
|
||||
/// \brief The set of declarations found inside this base class
|
||||
/// subobject.
|
||||
@ -113,8 +117,10 @@ public:
|
||||
/// refer to the same base class subobject of type A (the virtual
|
||||
/// one), there is no ambiguity.
|
||||
class CXXBasePaths {
|
||||
friend class CXXRecordDecl;
|
||||
|
||||
/// \brief The type from which this search originated.
|
||||
CXXRecordDecl *Origin;
|
||||
CXXRecordDecl *Origin = nullptr;
|
||||
|
||||
/// Paths - The actual set of paths that can be taken from the
|
||||
/// derived class to the same base class.
|
||||
@ -152,15 +158,13 @@ class CXXBasePaths {
|
||||
CXXBasePath ScratchPath;
|
||||
|
||||
/// DetectedVirtual - The base class that is virtual.
|
||||
const RecordType *DetectedVirtual;
|
||||
const RecordType *DetectedVirtual = nullptr;
|
||||
|
||||
/// \brief Array of the declarations that have been found. This
|
||||
/// array is constructed only if needed, e.g., to iterate over the
|
||||
/// results within LookupResult.
|
||||
std::unique_ptr<NamedDecl *[]> DeclsFound;
|
||||
unsigned NumDeclsFound;
|
||||
|
||||
friend class CXXRecordDecl;
|
||||
unsigned NumDeclsFound = 0;
|
||||
|
||||
void ComputeDeclsFound();
|
||||
|
||||
@ -169,17 +173,16 @@ class CXXBasePaths {
|
||||
bool LookupInDependent = false);
|
||||
|
||||
public:
|
||||
typedef std::list<CXXBasePath>::iterator paths_iterator;
|
||||
typedef std::list<CXXBasePath>::const_iterator const_paths_iterator;
|
||||
typedef NamedDecl **decl_iterator;
|
||||
using paths_iterator = std::list<CXXBasePath>::iterator;
|
||||
using const_paths_iterator = std::list<CXXBasePath>::const_iterator;
|
||||
using decl_iterator = NamedDecl **;
|
||||
|
||||
/// BasePaths - Construct a new BasePaths structure to record the
|
||||
/// paths for a derived-to-base search.
|
||||
explicit CXXBasePaths(bool FindAmbiguities = true, bool RecordPaths = true,
|
||||
bool DetectVirtual = true)
|
||||
: Origin(), FindAmbiguities(FindAmbiguities), RecordPaths(RecordPaths),
|
||||
DetectVirtual(DetectVirtual), DetectedVirtual(nullptr),
|
||||
NumDeclsFound(0) {}
|
||||
: FindAmbiguities(FindAmbiguities), RecordPaths(RecordPaths),
|
||||
DetectVirtual(DetectVirtual) {}
|
||||
|
||||
paths_iterator begin() { return Paths.begin(); }
|
||||
paths_iterator end() { return Paths.end(); }
|
||||
@ -189,7 +192,8 @@ public:
|
||||
CXXBasePath& front() { return Paths.front(); }
|
||||
const CXXBasePath& front() const { return Paths.front(); }
|
||||
|
||||
typedef llvm::iterator_range<decl_iterator> decl_range;
|
||||
using decl_range = llvm::iterator_range<decl_iterator>;
|
||||
|
||||
decl_range found_decls();
|
||||
|
||||
/// \brief Determine whether the path from the most-derived type to the
|
||||
@ -231,25 +235,24 @@ public:
|
||||
/// \brief Uniquely identifies a virtual method within a class
|
||||
/// hierarchy by the method itself and a class subobject number.
|
||||
struct UniqueVirtualMethod {
|
||||
UniqueVirtualMethod()
|
||||
: Method(nullptr), Subobject(0), InVirtualSubobject(nullptr) { }
|
||||
|
||||
UniqueVirtualMethod(CXXMethodDecl *Method, unsigned Subobject,
|
||||
const CXXRecordDecl *InVirtualSubobject)
|
||||
: Method(Method), Subobject(Subobject),
|
||||
InVirtualSubobject(InVirtualSubobject) { }
|
||||
|
||||
/// \brief The overriding virtual method.
|
||||
CXXMethodDecl *Method;
|
||||
CXXMethodDecl *Method = nullptr;
|
||||
|
||||
/// \brief The subobject in which the overriding virtual method
|
||||
/// resides.
|
||||
unsigned Subobject;
|
||||
unsigned Subobject = 0;
|
||||
|
||||
/// \brief The virtual base class subobject of which this overridden
|
||||
/// virtual method is a part. Note that this records the closest
|
||||
/// derived virtual base class subobject.
|
||||
const CXXRecordDecl *InVirtualSubobject;
|
||||
const CXXRecordDecl *InVirtualSubobject = nullptr;
|
||||
|
||||
UniqueVirtualMethod() = default;
|
||||
|
||||
UniqueVirtualMethod(CXXMethodDecl *Method, unsigned Subobject,
|
||||
const CXXRecordDecl *InVirtualSubobject)
|
||||
: Method(Method), Subobject(Subobject),
|
||||
InVirtualSubobject(InVirtualSubobject) {}
|
||||
|
||||
friend bool operator==(const UniqueVirtualMethod &X,
|
||||
const UniqueVirtualMethod &Y) {
|
||||
@ -271,14 +274,16 @@ struct UniqueVirtualMethod {
|
||||
/// pair is the virtual method that overrides it (including the
|
||||
/// subobject in which that virtual function occurs).
|
||||
class OverridingMethods {
|
||||
typedef SmallVector<UniqueVirtualMethod, 4> ValuesT;
|
||||
typedef llvm::MapVector<unsigned, ValuesT> MapType;
|
||||
using ValuesT = SmallVector<UniqueVirtualMethod, 4>;
|
||||
using MapType = llvm::MapVector<unsigned, ValuesT>;
|
||||
|
||||
MapType Overrides;
|
||||
|
||||
public:
|
||||
// Iterate over the set of subobjects that have overriding methods.
|
||||
typedef MapType::iterator iterator;
|
||||
typedef MapType::const_iterator const_iterator;
|
||||
using iterator = MapType::iterator;
|
||||
using const_iterator = MapType::const_iterator;
|
||||
|
||||
iterator begin() { return Overrides.begin(); }
|
||||
const_iterator begin() const { return Overrides.begin(); }
|
||||
iterator end() { return Overrides.end(); }
|
||||
@ -287,10 +292,10 @@ public:
|
||||
|
||||
// Iterate over the set of overriding virtual methods in a given
|
||||
// subobject.
|
||||
typedef SmallVectorImpl<UniqueVirtualMethod>::iterator
|
||||
overriding_iterator;
|
||||
typedef SmallVectorImpl<UniqueVirtualMethod>::const_iterator
|
||||
overriding_const_iterator;
|
||||
using overriding_iterator =
|
||||
SmallVectorImpl<UniqueVirtualMethod>::iterator;
|
||||
using overriding_const_iterator =
|
||||
SmallVectorImpl<UniqueVirtualMethod>::const_iterator;
|
||||
|
||||
// Add a new overriding method for a particular subobject.
|
||||
void add(unsigned OverriddenSubobject, UniqueVirtualMethod Overriding);
|
||||
@ -357,12 +362,12 @@ public:
|
||||
/// subobject numbers greater than 0 refer to non-virtual base class
|
||||
/// subobjects of that type.
|
||||
class CXXFinalOverriderMap
|
||||
: public llvm::MapVector<const CXXMethodDecl *, OverridingMethods> { };
|
||||
: public llvm::MapVector<const CXXMethodDecl *, OverridingMethods> {};
|
||||
|
||||
/// \brief A set of all the primary bases for a class.
|
||||
class CXXIndirectPrimaryBaseSet
|
||||
: public llvm::SmallSet<const CXXRecordDecl*, 32> { };
|
||||
: public llvm::SmallSet<const CXXRecordDecl*, 32> {};
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_CXXINHERITANCE_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- CanonicalType.h - C Language Family Type Representation -*- C++ -*-===//
|
||||
//===- CanonicalType.h - C Language Family Type Representation --*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,13 +16,29 @@
|
||||
#define LLVM_CLANG_AST_CANONICALTYPE_H
|
||||
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/PointerLikeTypeTraits.h"
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace clang {
|
||||
|
||||
template<typename T> class CanProxy;
|
||||
template<typename T> struct CanProxyAdaptor;
|
||||
class CXXRecordDecl;
|
||||
class EnumDecl;
|
||||
class Expr;
|
||||
class IdentifierInfo;
|
||||
class ObjCInterfaceDecl;
|
||||
class RecordDecl;
|
||||
class TagDecl;
|
||||
class TemplateTypeParmDecl;
|
||||
|
||||
//----------------------------------------------------------------------------//
|
||||
// Canonical, qualified type template
|
||||
@ -46,8 +62,6 @@ template<typename T> struct CanProxyAdaptor;
|
||||
/// converted to @c CanQual<ReferenceType>. Note that any @c CanQual type can
|
||||
/// be implicitly converted to a QualType, but the reverse operation requires
|
||||
/// a call to ASTContext::getCanonicalType().
|
||||
///
|
||||
///
|
||||
template<typename T = Type>
|
||||
class CanQual {
|
||||
/// \brief The actual, canonical type.
|
||||
@ -55,7 +69,7 @@ class CanQual {
|
||||
|
||||
public:
|
||||
/// \brief Constructs a NULL canonical type.
|
||||
CanQual() : Stored() { }
|
||||
CanQual() = default;
|
||||
|
||||
/// \brief Converting constructor that permits implicit upcasting of
|
||||
/// canonical type pointers.
|
||||
@ -66,12 +80,11 @@ public:
|
||||
/// \brief Retrieve the underlying type pointer, which refers to a
|
||||
/// canonical type.
|
||||
///
|
||||
/// The underlying pointer must not be NULL.
|
||||
/// The underlying pointer must not be nullptr.
|
||||
const T *getTypePtr() const { return cast<T>(Stored.getTypePtr()); }
|
||||
|
||||
/// \brief Retrieve the underlying type pointer, which refers to a
|
||||
/// canonical type, or NULL.
|
||||
///
|
||||
/// canonical type, or nullptr.
|
||||
const T *getTypePtrOrNull() const {
|
||||
return cast_or_null<T>(Stored.getTypePtrOrNull());
|
||||
}
|
||||
@ -125,9 +138,11 @@ public:
|
||||
bool isConstQualified() const {
|
||||
return Stored.isLocalConstQualified();
|
||||
}
|
||||
|
||||
bool isVolatileQualified() const {
|
||||
return Stored.isLocalVolatileQualified();
|
||||
}
|
||||
|
||||
bool isRestrictQualified() const {
|
||||
return Stored.isLocalRestrictQualified();
|
||||
}
|
||||
@ -195,7 +210,7 @@ inline bool operator!=(CanQual<T> x, CanQual<U> y) {
|
||||
}
|
||||
|
||||
/// \brief Represents a canonical, potentially-qualified type.
|
||||
typedef CanQual<Type> CanQualType;
|
||||
using CanQualType = CanQual<Type>;
|
||||
|
||||
inline CanQualType Type::getCanonicalTypeUnqualified() const {
|
||||
return CanQualType::CreateUnsafe(getCanonicalTypeInternal());
|
||||
@ -320,7 +335,7 @@ public:
|
||||
/// than the more typical @c QualType, to propagate the notion of "canonical"
|
||||
/// through the system.
|
||||
template<typename T>
|
||||
struct CanProxyAdaptor : CanProxyBase<T> { };
|
||||
struct CanProxyAdaptor : CanProxyBase<T> {};
|
||||
|
||||
/// \brief Canonical proxy type returned when retrieving the members of a
|
||||
/// canonical type or as the result of the @c CanQual<T>::getAs member
|
||||
@ -333,7 +348,7 @@ template<typename T>
|
||||
class CanProxy : public CanProxyAdaptor<T> {
|
||||
public:
|
||||
/// \brief Build a NULL proxy.
|
||||
CanProxy() { }
|
||||
CanProxy() = default;
|
||||
|
||||
/// \brief Build a proxy to the given canonical type.
|
||||
CanProxy(CanQual<T> Stored) { this->Stored = Stored; }
|
||||
@ -342,7 +357,7 @@ public:
|
||||
operator CanQual<T>() const { return this->Stored; }
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
||||
@ -350,8 +365,9 @@ namespace llvm {
|
||||
/// CanQual<T> to a specific Type class. We're prefer isa/dyn_cast/cast/etc.
|
||||
/// to return smart pointer (proxies?).
|
||||
template<typename T>
|
||||
struct simplify_type< ::clang::CanQual<T> > {
|
||||
typedef const T *SimpleType;
|
||||
struct simplify_type< ::clang::CanQual<T>> {
|
||||
using SimpleType = const T *;
|
||||
|
||||
static SimpleType getSimplifiedValue(::clang::CanQual<T> Val) {
|
||||
return Val.getTypePtr();
|
||||
}
|
||||
@ -359,19 +375,20 @@ struct simplify_type< ::clang::CanQual<T> > {
|
||||
|
||||
// Teach SmallPtrSet that CanQual<T> is "basically a pointer".
|
||||
template<typename T>
|
||||
class PointerLikeTypeTraits<clang::CanQual<T> > {
|
||||
public:
|
||||
static inline void *getAsVoidPointer(clang::CanQual<T> P) {
|
||||
struct PointerLikeTypeTraits<clang::CanQual<T>> {
|
||||
static void *getAsVoidPointer(clang::CanQual<T> P) {
|
||||
return P.getAsOpaquePtr();
|
||||
}
|
||||
static inline clang::CanQual<T> getFromVoidPointer(void *P) {
|
||||
|
||||
static clang::CanQual<T> getFromVoidPointer(void *P) {
|
||||
return clang::CanQual<T>::getFromOpaquePtr(P);
|
||||
}
|
||||
|
||||
// qualifier information is encoded in the low bits.
|
||||
enum { NumLowBitsAvailable = 0 };
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -389,7 +406,7 @@ struct CanTypeIterator
|
||||
CanQualType,
|
||||
typename std::iterator_traits<InputIterator>::difference_type,
|
||||
CanProxy<Type>, CanQualType> {
|
||||
CanTypeIterator() {}
|
||||
CanTypeIterator() = default;
|
||||
explicit CanTypeIterator(InputIterator Iter)
|
||||
: CanTypeIterator::iterator_adaptor_base(std::move(Iter)) {}
|
||||
|
||||
@ -487,6 +504,7 @@ struct CanProxyAdaptor<FunctionProtoType>
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, hasExtParameterInfos)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(
|
||||
ArrayRef<FunctionProtoType::ExtParameterInfo>, getExtParameterInfos)
|
||||
|
||||
CanQualType getParamType(unsigned i) const {
|
||||
return CanQualType::CreateUnsafe(this->getTypePtr()->getParamType(i));
|
||||
}
|
||||
@ -494,8 +512,8 @@ struct CanProxyAdaptor<FunctionProtoType>
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isVariadic)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(unsigned, getTypeQuals)
|
||||
|
||||
typedef CanTypeIterator<FunctionProtoType::param_type_iterator>
|
||||
param_type_iterator;
|
||||
using param_type_iterator =
|
||||
CanTypeIterator<FunctionProtoType::param_type_iterator>;
|
||||
|
||||
param_type_iterator param_type_begin() const {
|
||||
return param_type_iterator(this->getTypePtr()->param_type_begin());
|
||||
@ -567,7 +585,8 @@ struct CanProxyAdaptor<ObjCObjectType>
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjCQualifiedId)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjCQualifiedClass)
|
||||
|
||||
typedef ObjCObjectPointerType::qual_iterator qual_iterator;
|
||||
using qual_iterator = ObjCObjectPointerType::qual_iterator;
|
||||
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(qual_iterator, qual_begin)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(qual_iterator, qual_end)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, qual_empty)
|
||||
@ -585,7 +604,8 @@ struct CanProxyAdaptor<ObjCObjectPointerType>
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjCQualifiedIdType)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjCQualifiedClassType)
|
||||
|
||||
typedef ObjCObjectPointerType::qual_iterator qual_iterator;
|
||||
using qual_iterator = ObjCObjectPointerType::qual_iterator;
|
||||
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(qual_iterator, qual_begin)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(qual_iterator, qual_end)
|
||||
LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, qual_empty)
|
||||
@ -662,7 +682,6 @@ CanProxy<Type> CanTypeIterator<InputIterator>::operator->() const {
|
||||
return CanProxy<Type>(*this);
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace clang
|
||||
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_CANONICALTYPE_H
|
||||
|
@ -40,14 +40,14 @@ namespace clang {
|
||||
typedef int64_t QuantityType;
|
||||
|
||||
private:
|
||||
QuantityType Quantity;
|
||||
QuantityType Quantity = 0;
|
||||
|
||||
explicit CharUnits(QuantityType C) : Quantity(C) {}
|
||||
|
||||
public:
|
||||
|
||||
/// CharUnits - A default constructor.
|
||||
CharUnits() : Quantity(0) {}
|
||||
CharUnits() = default;
|
||||
|
||||
/// Zero - Construct a CharUnits quantity of zero.
|
||||
static CharUnits Zero() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- CommentVisitor.h - Visitor for Comment subclasses ------*- C++ -*-===//
|
||||
//===- CommentVisitor.h - Visitor for Comment subclasses --------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,8 +16,8 @@
|
||||
namespace clang {
|
||||
namespace comments {
|
||||
|
||||
template <typename T> struct make_ptr { typedef T *type; };
|
||||
template <typename T> struct make_const_ptr { typedef const T *type; };
|
||||
template <typename T> struct make_ptr { using type = T *; };
|
||||
template <typename T> struct make_const_ptr { using type = const T *; };
|
||||
|
||||
template<template <typename> class Ptr, typename ImplClass, typename RetTy=void>
|
||||
class CommentVisitorBase {
|
||||
@ -64,7 +64,7 @@ template<typename ImplClass, typename RetTy=void>
|
||||
class ConstCommentVisitor :
|
||||
public CommentVisitorBase<make_const_ptr, ImplClass, RetTy> {};
|
||||
|
||||
} // end namespace comments
|
||||
} // end namespace clang
|
||||
} // namespace comments
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_COMMENTVISITOR_H
|
||||
|
65
include/clang/AST/DataCollection.h
Normal file
65
include/clang/AST/DataCollection.h
Normal file
@ -0,0 +1,65 @@
|
||||
//===--- DatatCollection.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
/// \file
|
||||
/// \brief This file declares helper methods for collecting data from AST nodes.
|
||||
///
|
||||
/// To collect data from Stmt nodes, subclass ConstStmtVisitor and include
|
||||
/// StmtDataCollectors.inc after defining the macros that you need. This
|
||||
/// provides data collection implementations for most Stmt kinds. Note
|
||||
/// that that code requires some conditions to be met:
|
||||
///
|
||||
/// - There must be a method addData(const T &Data) that accepts strings,
|
||||
/// integral types as well as QualType. All data is forwarded using
|
||||
/// to this method.
|
||||
/// - The ASTContext of the Stmt must be accessible by the name Context.
|
||||
///
|
||||
/// It is also possible to override individual visit methods. Have a look at
|
||||
/// the DataCollector in lib/Analysis/CloneDetection.cpp for a usage example.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_DATACOLLECTION_H
|
||||
#define LLVM_CLANG_AST_DATACOLLECTION_H
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
|
||||
namespace clang {
|
||||
namespace data_collection {
|
||||
|
||||
/// Returns a string that represents all macro expansions that expanded into the
|
||||
/// given SourceLocation.
|
||||
///
|
||||
/// If 'getMacroStack(A) == getMacroStack(B)' is true, then the SourceLocations
|
||||
/// A and B are expanded from the same macros in the same order.
|
||||
std::string getMacroStack(SourceLocation Loc, ASTContext &Context);
|
||||
|
||||
/// Utility functions for implementing addData() for a consumer that has a
|
||||
/// method update(StringRef)
|
||||
template <class T>
|
||||
void addDataToConsumer(T &DataConsumer, llvm::StringRef Str) {
|
||||
DataConsumer.update(Str);
|
||||
}
|
||||
|
||||
template <class T> void addDataToConsumer(T &DataConsumer, const QualType &QT) {
|
||||
addDataToConsumer(DataConsumer, QT.getAsString());
|
||||
}
|
||||
|
||||
template <class T, class Type>
|
||||
typename std::enable_if<
|
||||
std::is_integral<Type>::value || std::is_enum<Type>::value ||
|
||||
std::is_convertible<Type, size_t>::value // for llvm::hash_code
|
||||
>::type
|
||||
addDataToConsumer(T &DataConsumer, Type Data) {
|
||||
DataConsumer.update(StringRef(reinterpret_cast<char *>(&Data), sizeof(Data)));
|
||||
}
|
||||
|
||||
} // end namespace data_collection
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_DATACOLLECTION_H
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
//===-- DeclBase.h - Base Classes for representing declarations -*- C++ -*-===//
|
||||
//===- DeclBase.h - Base Classes for representing declarations --*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -16,33 +16,40 @@
|
||||
|
||||
#include "clang/AST/AttrIterator.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "clang/Basic/VersionTuple.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/ADT/iterator_range.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/PrettyStackTrace.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class ASTMutationListener;
|
||||
class BlockDecl;
|
||||
class CXXRecordDecl;
|
||||
class CompoundStmt;
|
||||
class Attr;
|
||||
class DeclContext;
|
||||
class DeclarationName;
|
||||
class DependentDiagnostic;
|
||||
class EnumDecl;
|
||||
class ExportDecl;
|
||||
class ExternalSourceSymbolAttr;
|
||||
class FunctionDecl;
|
||||
class FunctionType;
|
||||
class IdentifierInfo;
|
||||
enum Linkage : unsigned char;
|
||||
class LinkageComputer;
|
||||
class LinkageSpecDecl;
|
||||
class Module;
|
||||
class NamedDecl;
|
||||
class NamespaceDecl;
|
||||
class ObjCCategoryDecl;
|
||||
class ObjCCategoryImplDecl;
|
||||
class ObjCContainerDecl;
|
||||
@ -53,23 +60,21 @@ class ObjCMethodDecl;
|
||||
class ObjCProtocolDecl;
|
||||
struct PrintingPolicy;
|
||||
class RecordDecl;
|
||||
class SourceManager;
|
||||
class Stmt;
|
||||
class StoredDeclsMap;
|
||||
class TemplateDecl;
|
||||
class TranslationUnitDecl;
|
||||
class UsingDirectiveDecl;
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
|
||||
/// \brief Captures the result of checking the availability of a
|
||||
/// declaration.
|
||||
enum AvailabilityResult {
|
||||
AR_Available = 0,
|
||||
AR_NotYetIntroduced,
|
||||
AR_Deprecated,
|
||||
AR_Unavailable
|
||||
};
|
||||
/// \brief Captures the result of checking the availability of a
|
||||
/// declaration.
|
||||
enum AvailabilityResult {
|
||||
AR_Available = 0,
|
||||
AR_NotYetIntroduced,
|
||||
AR_Deprecated,
|
||||
AR_Unavailable
|
||||
};
|
||||
|
||||
/// Decl - This represents one declaration (or definition), e.g. a variable,
|
||||
/// typedef, function, struct, etc.
|
||||
@ -94,7 +99,7 @@ public:
|
||||
/// \brief A placeholder type used to construct an empty shell of a
|
||||
/// decl-derived type that will be filled in later (e.g., by some
|
||||
/// deserialization method).
|
||||
struct EmptyShell { };
|
||||
struct EmptyShell {};
|
||||
|
||||
/// IdentifierNamespace - The different namespaces in which
|
||||
/// declarations may appear. According to C99 6.2.3, there are
|
||||
@ -208,15 +213,18 @@ public:
|
||||
enum class ModuleOwnershipKind : unsigned {
|
||||
/// This declaration is not owned by a module.
|
||||
Unowned,
|
||||
|
||||
/// This declaration has an owning module, but is globally visible
|
||||
/// (typically because its owning module is visible and we know that
|
||||
/// modules cannot later become hidden in this compilation).
|
||||
/// After serialization and deserialization, this will be converted
|
||||
/// to VisibleWhenImported.
|
||||
Visible,
|
||||
|
||||
/// This declaration has an owning module, and is visible when that
|
||||
/// module is imported.
|
||||
VisibleWhenImported,
|
||||
|
||||
/// This declaration has an owning module, but is only visible to
|
||||
/// lookups that occur within that module.
|
||||
ModulePrivate
|
||||
@ -238,7 +246,6 @@ private:
|
||||
DeclContext *LexicalDC;
|
||||
};
|
||||
|
||||
|
||||
/// DeclCtx - Holds either a DeclContext* or a MultipleDC*.
|
||||
/// For declarations that don't contain C++ scope specifiers, it contains
|
||||
/// the DeclContext where the Decl was declared.
|
||||
@ -254,12 +261,14 @@ private:
|
||||
/// // LexicalDC == global namespace
|
||||
llvm::PointerUnion<DeclContext*, MultipleDC*> DeclCtx;
|
||||
|
||||
inline bool isInSemaDC() const { return DeclCtx.is<DeclContext*>(); }
|
||||
inline bool isOutOfSemaDC() const { return DeclCtx.is<MultipleDC*>(); }
|
||||
inline MultipleDC *getMultipleDC() const {
|
||||
bool isInSemaDC() const { return DeclCtx.is<DeclContext*>(); }
|
||||
bool isOutOfSemaDC() const { return DeclCtx.is<MultipleDC*>(); }
|
||||
|
||||
MultipleDC *getMultipleDC() const {
|
||||
return DeclCtx.get<MultipleDC*>();
|
||||
}
|
||||
inline DeclContext *getSemanticDC() const {
|
||||
|
||||
DeclContext *getSemanticDC() const {
|
||||
return DeclCtx.get<DeclContext*>();
|
||||
}
|
||||
|
||||
@ -298,10 +307,16 @@ private:
|
||||
static bool StatisticsEnabled;
|
||||
|
||||
protected:
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTDeclWriter;
|
||||
friend class ASTReader;
|
||||
friend class CXXClassMemberWrapper;
|
||||
friend class LinkageComputer;
|
||||
template<typename decl_type> friend class Redeclarable;
|
||||
|
||||
/// Access - Used by C++ decls for the access specifier.
|
||||
// NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum
|
||||
unsigned Access : 2;
|
||||
friend class CXXClassMemberWrapper;
|
||||
|
||||
/// \brief Whether this declaration was loaded from an AST file.
|
||||
unsigned FromASTFile : 1;
|
||||
@ -313,13 +328,6 @@ protected:
|
||||
/// Otherwise, it is the linkage + 1.
|
||||
mutable unsigned CacheValidAndLinkage : 3;
|
||||
|
||||
friend class ASTDeclWriter;
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTReader;
|
||||
friend class LinkageComputer;
|
||||
|
||||
template<typename decl_type> friend class Redeclarable;
|
||||
|
||||
/// \brief Allocate memory for a deserialized declaration.
|
||||
///
|
||||
/// This routine must be used to allocate memory for any declaration that is
|
||||
@ -357,7 +365,7 @@ private:
|
||||
protected:
|
||||
Decl(Kind DK, DeclContext *DC, SourceLocation L)
|
||||
: NextInContextAndBits(nullptr, getModuleOwnershipKindForChildOf(DC)),
|
||||
DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(0), HasAttrs(false),
|
||||
DeclCtx(DC), Loc(L), DeclKind(DK), InvalidDecl(false), HasAttrs(false),
|
||||
Implicit(false), Used(false), Referenced(false),
|
||||
TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
|
||||
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
|
||||
@ -366,9 +374,9 @@ protected:
|
||||
}
|
||||
|
||||
Decl(Kind DK, EmptyShell Empty)
|
||||
: NextInContextAndBits(), DeclKind(DK), InvalidDecl(0), HasAttrs(false),
|
||||
Implicit(false), Used(false), Referenced(false),
|
||||
TopLevelDeclInObjCContainer(false), Access(AS_none), FromASTFile(0),
|
||||
: DeclKind(DK), InvalidDecl(false), HasAttrs(false), Implicit(false),
|
||||
Used(false), Referenced(false), TopLevelDeclInObjCContainer(false),
|
||||
Access(AS_none), FromASTFile(0),
|
||||
IdentifierNamespace(getIdentifierNamespaceForKind(DK)),
|
||||
CacheValidAndLinkage(0) {
|
||||
if (StatisticsEnabled) add(DK);
|
||||
@ -392,14 +400,15 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// \brief Source range that this declaration covers.
|
||||
virtual SourceRange getSourceRange() const LLVM_READONLY {
|
||||
return SourceRange(getLocation(), getLocation());
|
||||
}
|
||||
|
||||
SourceLocation getLocStart() const LLVM_READONLY {
|
||||
return getSourceRange().getBegin();
|
||||
}
|
||||
|
||||
SourceLocation getLocEnd() const LLVM_READONLY {
|
||||
return getSourceRange().getEnd();
|
||||
}
|
||||
@ -460,12 +469,15 @@ public:
|
||||
}
|
||||
|
||||
bool hasAttrs() const { return HasAttrs; }
|
||||
|
||||
void setAttrs(const AttrVec& Attrs) {
|
||||
return setAttrsImpl(Attrs, getASTContext());
|
||||
}
|
||||
|
||||
AttrVec &getAttrs() {
|
||||
return const_cast<AttrVec&>(const_cast<const Decl*>(this)->getAttrs());
|
||||
}
|
||||
|
||||
const AttrVec &getAttrs() const;
|
||||
void dropAttrs();
|
||||
|
||||
@ -476,8 +488,8 @@ public:
|
||||
setAttrs(AttrVec(1, A));
|
||||
}
|
||||
|
||||
typedef AttrVec::const_iterator attr_iterator;
|
||||
typedef llvm::iterator_range<attr_iterator> attr_range;
|
||||
using attr_iterator = AttrVec::const_iterator;
|
||||
using attr_range = llvm::iterator_range<attr_iterator>;
|
||||
|
||||
attr_range attrs() const {
|
||||
return attr_range(attr_begin(), attr_end());
|
||||
@ -510,6 +522,7 @@ public:
|
||||
specific_attr_iterator<T> specific_attr_begin() const {
|
||||
return specific_attr_iterator<T>(attr_begin());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
specific_attr_iterator<T> specific_attr_end() const {
|
||||
return specific_attr_iterator<T>(attr_end());
|
||||
@ -518,6 +531,7 @@ public:
|
||||
template<typename T> T *getAttr() const {
|
||||
return hasAttrs() ? getSpecificAttr<T>(getAttrs()) : nullptr;
|
||||
}
|
||||
|
||||
template<typename T> bool hasAttr() const {
|
||||
return hasAttrs() && hasSpecificAttr<T>(getAttrs());
|
||||
}
|
||||
@ -616,7 +630,6 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// \brief Determine the availability of the given declaration.
|
||||
///
|
||||
/// This routine will determine the most restrictive availability of
|
||||
@ -698,6 +711,7 @@ public:
|
||||
|
||||
private:
|
||||
Module *getOwningModuleSlow() const;
|
||||
|
||||
protected:
|
||||
bool hasLocalOwningModuleStorage() const;
|
||||
|
||||
@ -733,11 +747,18 @@ public:
|
||||
return getModuleOwnershipKind() != ModuleOwnershipKind::Unowned;
|
||||
}
|
||||
|
||||
/// Get the module that owns this declaration.
|
||||
/// Get the module that owns this declaration (for visibility purposes).
|
||||
Module *getOwningModule() const {
|
||||
return isFromASTFile() ? getImportedOwningModule() : getLocalOwningModule();
|
||||
}
|
||||
|
||||
/// Get the module that owns this declaration for linkage purposes.
|
||||
/// There only ever is such a module under the C++ Modules TS.
|
||||
///
|
||||
/// \param IgnoreLinkage Ignore the linkage of the entity; assume that
|
||||
/// all declarations in a global module fragment are unowned.
|
||||
Module *getOwningModuleForLinkage(bool IgnoreLinkage = false) const;
|
||||
|
||||
/// \brief Determine whether this declaration might be hidden from name
|
||||
/// lookup. Note that the declaration might be visible even if this returns
|
||||
/// \c false, if the owning module is visible within the query context.
|
||||
@ -770,14 +791,17 @@ public:
|
||||
unsigned getIdentifierNamespace() const {
|
||||
return IdentifierNamespace;
|
||||
}
|
||||
|
||||
bool isInIdentifierNamespace(unsigned NS) const {
|
||||
return getIdentifierNamespace() & NS;
|
||||
}
|
||||
|
||||
static unsigned getIdentifierNamespaceForKind(Kind DK);
|
||||
|
||||
bool hasTagIdentifierNamespace() const {
|
||||
return isTagIdentifierNamespace(getIdentifierNamespace());
|
||||
}
|
||||
|
||||
static bool isTagIdentifierNamespace(unsigned NS) {
|
||||
// TagDecls have Tag and Type set and may also have TagFriend.
|
||||
return (NS & ~IDNS_TagFriend) == (IDNS_Tag | IDNS_Type);
|
||||
@ -865,18 +889,18 @@ public:
|
||||
/// \brief Iterates through all the redeclarations of the same decl.
|
||||
class redecl_iterator {
|
||||
/// Current - The current declaration.
|
||||
Decl *Current;
|
||||
Decl *Current = nullptr;
|
||||
Decl *Starter;
|
||||
|
||||
public:
|
||||
typedef Decl *value_type;
|
||||
typedef const value_type &reference;
|
||||
typedef const value_type *pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
using value_type = Decl *;
|
||||
using reference = const value_type &;
|
||||
using pointer = const value_type *;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
redecl_iterator() : Current(nullptr) { }
|
||||
explicit redecl_iterator(Decl *C) : Current(C), Starter(C) { }
|
||||
redecl_iterator() = default;
|
||||
explicit redecl_iterator(Decl *C) : Current(C), Starter(C) {}
|
||||
|
||||
reference operator*() const { return Current; }
|
||||
value_type operator->() const { return Current; }
|
||||
@ -899,12 +923,13 @@ public:
|
||||
friend bool operator==(redecl_iterator x, redecl_iterator y) {
|
||||
return x.Current == y.Current;
|
||||
}
|
||||
|
||||
friend bool operator!=(redecl_iterator x, redecl_iterator y) {
|
||||
return x.Current != y.Current;
|
||||
}
|
||||
};
|
||||
|
||||
typedef llvm::iterator_range<redecl_iterator> redecl_range;
|
||||
using redecl_range = llvm::iterator_range<redecl_iterator>;
|
||||
|
||||
/// \brief Returns an iterator range for all the redeclarations of the same
|
||||
/// decl. It will iterate at least once (when this decl is the only one).
|
||||
@ -915,6 +940,7 @@ public:
|
||||
redecl_iterator redecls_begin() const {
|
||||
return redecl_iterator(const_cast<Decl *>(this));
|
||||
}
|
||||
|
||||
redecl_iterator redecls_end() const { return redecl_iterator(); }
|
||||
|
||||
/// \brief Retrieve the previous declaration that declares the same entity
|
||||
@ -1002,13 +1028,15 @@ public:
|
||||
/// declaration, but in the semantic context of the enclosing namespace
|
||||
/// scope.
|
||||
void setLocalExternDecl() {
|
||||
assert((IdentifierNamespace == IDNS_Ordinary ||
|
||||
IdentifierNamespace == IDNS_OrdinaryFriend) &&
|
||||
"namespace is not ordinary");
|
||||
|
||||
Decl *Prev = getPreviousDecl();
|
||||
IdentifierNamespace &= ~IDNS_Ordinary;
|
||||
|
||||
// It's OK for the declaration to still have the "invisible friend" flag or
|
||||
// the "conflicts with tag declarations in this scope" flag for the outer
|
||||
// scope.
|
||||
assert((IdentifierNamespace & ~(IDNS_OrdinaryFriend | IDNS_Tag)) == 0 &&
|
||||
"namespace is not ordinary");
|
||||
|
||||
IdentifierNamespace |= IDNS_LocalExtern;
|
||||
if (Prev && Prev->getIdentifierNamespace() & IDNS_Ordinary)
|
||||
IdentifierNamespace |= IDNS_Ordinary;
|
||||
@ -1094,10 +1122,13 @@ public:
|
||||
static void printGroup(Decl** Begin, unsigned NumDecls,
|
||||
raw_ostream &Out, const PrintingPolicy &Policy,
|
||||
unsigned Indentation = 0);
|
||||
|
||||
// Debuggers don't usually respect default arguments.
|
||||
void dump() const;
|
||||
|
||||
// Same as dump(), but forces color printing.
|
||||
void dumpColor() const;
|
||||
|
||||
void dump(raw_ostream &Out, bool Deserialize = false) const;
|
||||
|
||||
/// \brief Looks through the Decl's underlying type to extract a FunctionType
|
||||
@ -1132,10 +1163,11 @@ class PrettyStackTraceDecl : public llvm::PrettyStackTraceEntry {
|
||||
SourceLocation Loc;
|
||||
SourceManager &SM;
|
||||
const char *Message;
|
||||
|
||||
public:
|
||||
PrettyStackTraceDecl(const Decl *theDecl, SourceLocation L,
|
||||
SourceManager &sm, const char *Msg)
|
||||
: TheDecl(theDecl), Loc(L), SM(sm), Message(Msg) {}
|
||||
: TheDecl(theDecl), Loc(L), SM(sm), Message(Msg) {}
|
||||
|
||||
void print(raw_ostream &OS) const override;
|
||||
};
|
||||
@ -1144,30 +1176,35 @@ public:
|
||||
/// single result (with no stable storage) or a collection of results (with
|
||||
/// stable storage provided by the lookup table).
|
||||
class DeclContextLookupResult {
|
||||
typedef ArrayRef<NamedDecl *> ResultTy;
|
||||
using ResultTy = ArrayRef<NamedDecl *>;
|
||||
|
||||
ResultTy Result;
|
||||
|
||||
// If there is only one lookup result, it would be invalidated by
|
||||
// reallocations of the name table, so store it separately.
|
||||
NamedDecl *Single;
|
||||
NamedDecl *Single = nullptr;
|
||||
|
||||
static NamedDecl *const SingleElementDummyList;
|
||||
|
||||
public:
|
||||
DeclContextLookupResult() : Result(), Single() {}
|
||||
DeclContextLookupResult() = default;
|
||||
DeclContextLookupResult(ArrayRef<NamedDecl *> Result)
|
||||
: Result(Result), Single() {}
|
||||
: Result(Result) {}
|
||||
DeclContextLookupResult(NamedDecl *Single)
|
||||
: Result(SingleElementDummyList), Single(Single) {}
|
||||
|
||||
class iterator;
|
||||
typedef llvm::iterator_adaptor_base<iterator, ResultTy::iterator,
|
||||
std::random_access_iterator_tag,
|
||||
NamedDecl *const> IteratorBase;
|
||||
|
||||
using IteratorBase =
|
||||
llvm::iterator_adaptor_base<iterator, ResultTy::iterator,
|
||||
std::random_access_iterator_tag,
|
||||
NamedDecl *const>;
|
||||
|
||||
class iterator : public IteratorBase {
|
||||
value_type SingleElement;
|
||||
|
||||
public:
|
||||
iterator() : IteratorBase(), SingleElement() {}
|
||||
iterator() = default;
|
||||
explicit iterator(pointer Pos, value_type Single = nullptr)
|
||||
: IteratorBase(Pos), SingleElement(Single) {}
|
||||
|
||||
@ -1175,9 +1212,10 @@ public:
|
||||
return SingleElement ? SingleElement : IteratorBase::operator*();
|
||||
}
|
||||
};
|
||||
typedef iterator const_iterator;
|
||||
typedef iterator::pointer pointer;
|
||||
typedef iterator::reference reference;
|
||||
|
||||
using const_iterator = iterator;
|
||||
using pointer = iterator::pointer;
|
||||
using reference = iterator::reference;
|
||||
|
||||
iterator begin() const { return iterator(Result.begin(), Single); }
|
||||
iterator end() const { return iterator(Result.end(), Single); }
|
||||
@ -1211,7 +1249,6 @@ public:
|
||||
/// ExportDecl
|
||||
/// BlockDecl
|
||||
/// OMPDeclareReductionDecl
|
||||
///
|
||||
class DeclContext {
|
||||
/// DeclKind - This indicates which class this is.
|
||||
unsigned DeclKind : 8;
|
||||
@ -1251,22 +1288,22 @@ class DeclContext {
|
||||
/// contains an entry for a DeclarationName (and we haven't lazily
|
||||
/// omitted anything), then it contains all relevant entries for that
|
||||
/// name (modulo the hasExternalDecls() flag).
|
||||
mutable StoredDeclsMap *LookupPtr;
|
||||
mutable StoredDeclsMap *LookupPtr = nullptr;
|
||||
|
||||
protected:
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTWriter;
|
||||
friend class ExternalASTSource;
|
||||
|
||||
/// FirstDecl - The first declaration stored within this declaration
|
||||
/// context.
|
||||
mutable Decl *FirstDecl;
|
||||
mutable Decl *FirstDecl = nullptr;
|
||||
|
||||
/// LastDecl - The last declaration stored within this declaration
|
||||
/// context. FIXME: We could probably cache this value somewhere
|
||||
/// outside of the DeclContext, to reduce the size of DeclContext by
|
||||
/// another pointer.
|
||||
mutable Decl *LastDecl;
|
||||
|
||||
friend class ExternalASTSource;
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTWriter;
|
||||
mutable Decl *LastDecl = nullptr;
|
||||
|
||||
/// \brief Build up a chain of declarations.
|
||||
///
|
||||
@ -1279,8 +1316,7 @@ protected:
|
||||
ExternalVisibleStorage(false),
|
||||
NeedToReconcileExternalVisibleStorage(false),
|
||||
HasLazyLocalLexicalLookups(false), HasLazyExternalLexicalLookups(false),
|
||||
UseQualifiedLookup(false),
|
||||
LookupPtr(nullptr), FirstDecl(nullptr), LastDecl(nullptr) {}
|
||||
UseQualifiedLookup(false) {}
|
||||
|
||||
public:
|
||||
~DeclContext();
|
||||
@ -1288,6 +1324,7 @@ public:
|
||||
Decl::Kind getDeclKind() const {
|
||||
return static_cast<Decl::Kind>(DeclKind);
|
||||
}
|
||||
|
||||
const char *getDeclKindName() const;
|
||||
|
||||
/// getParent - Returns the containing DeclContext.
|
||||
@ -1495,19 +1532,20 @@ public:
|
||||
/// within this context.
|
||||
class decl_iterator {
|
||||
/// Current - The current declaration.
|
||||
Decl *Current;
|
||||
Decl *Current = nullptr;
|
||||
|
||||
public:
|
||||
typedef Decl *value_type;
|
||||
typedef const value_type &reference;
|
||||
typedef const value_type *pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
using value_type = Decl *;
|
||||
using reference = const value_type &;
|
||||
using pointer = const value_type *;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
decl_iterator() : Current(nullptr) { }
|
||||
explicit decl_iterator(Decl *C) : Current(C) { }
|
||||
decl_iterator() = default;
|
||||
explicit decl_iterator(Decl *C) : Current(C) {}
|
||||
|
||||
reference operator*() const { return Current; }
|
||||
|
||||
// This doesn't meet the iterator requirements, but it's convenient
|
||||
value_type operator->() const { return Current; }
|
||||
|
||||
@ -1525,12 +1563,13 @@ public:
|
||||
friend bool operator==(decl_iterator x, decl_iterator y) {
|
||||
return x.Current == y.Current;
|
||||
}
|
||||
|
||||
friend bool operator!=(decl_iterator x, decl_iterator y) {
|
||||
return x.Current != y.Current;
|
||||
}
|
||||
};
|
||||
|
||||
typedef llvm::iterator_range<decl_iterator> decl_range;
|
||||
using decl_range = llvm::iterator_range<decl_iterator>;
|
||||
|
||||
/// decls_begin/decls_end - Iterate over the declarations stored in
|
||||
/// this context.
|
||||
@ -1569,16 +1608,16 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
typedef SpecificDecl *value_type;
|
||||
// TODO: Add reference and pointer typedefs (with some appropriate proxy
|
||||
// type) if we ever have a need for them.
|
||||
typedef void reference;
|
||||
typedef void pointer;
|
||||
typedef std::iterator_traits<DeclContext::decl_iterator>::difference_type
|
||||
difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
using value_type = SpecificDecl *;
|
||||
// TODO: Add reference and pointer types (with some appropriate proxy type)
|
||||
// if we ever have a need for them.
|
||||
using reference = void;
|
||||
using pointer = void;
|
||||
using difference_type =
|
||||
std::iterator_traits<DeclContext::decl_iterator>::difference_type;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
specific_decl_iterator() : Current() { }
|
||||
specific_decl_iterator() = default;
|
||||
|
||||
/// specific_decl_iterator - Construct a new iterator over a
|
||||
/// subset of the declarations the range [C,
|
||||
@ -1593,6 +1632,7 @@ public:
|
||||
}
|
||||
|
||||
value_type operator*() const { return cast<SpecificDecl>(*Current); }
|
||||
|
||||
// This doesn't meet the iterator requirements, but it's convenient
|
||||
value_type operator->() const { return **this; }
|
||||
|
||||
@ -1646,16 +1686,16 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
typedef SpecificDecl *value_type;
|
||||
// TODO: Add reference and pointer typedefs (with some appropriate proxy
|
||||
// type) if we ever have a need for them.
|
||||
typedef void reference;
|
||||
typedef void pointer;
|
||||
typedef std::iterator_traits<DeclContext::decl_iterator>::difference_type
|
||||
difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
using value_type = SpecificDecl *;
|
||||
// TODO: Add reference and pointer types (with some appropriate proxy type)
|
||||
// if we ever have a need for them.
|
||||
using reference = void;
|
||||
using pointer = void;
|
||||
using difference_type =
|
||||
std::iterator_traits<DeclContext::decl_iterator>::difference_type;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
filtered_decl_iterator() : Current() { }
|
||||
filtered_decl_iterator() = default;
|
||||
|
||||
/// filtered_decl_iterator - Construct a new iterator over a
|
||||
/// subset of the declarations the range [C,
|
||||
@ -1733,8 +1773,8 @@ public:
|
||||
/// @brief Checks whether a declaration is in this context.
|
||||
bool containsDecl(Decl *D) const;
|
||||
|
||||
typedef DeclContextLookupResult lookup_result;
|
||||
typedef lookup_result::iterator lookup_iterator;
|
||||
using lookup_result = DeclContextLookupResult;
|
||||
using lookup_iterator = lookup_result::iterator;
|
||||
|
||||
/// lookup - Find the declarations (if any) with the given Name in
|
||||
/// this context. Returns a range of iterators that contains all of
|
||||
@ -1780,7 +1820,7 @@ public:
|
||||
/// of looking up every possible name.
|
||||
class all_lookups_iterator;
|
||||
|
||||
typedef llvm::iterator_range<all_lookups_iterator> lookups_range;
|
||||
using lookups_range = llvm::iterator_range<all_lookups_iterator>;
|
||||
|
||||
lookups_range lookups() const;
|
||||
lookups_range noload_lookups() const;
|
||||
@ -1796,21 +1836,26 @@ public:
|
||||
all_lookups_iterator noload_lookups_end() const;
|
||||
|
||||
struct udir_iterator;
|
||||
typedef llvm::iterator_adaptor_base<udir_iterator, lookup_iterator,
|
||||
std::random_access_iterator_tag,
|
||||
UsingDirectiveDecl *> udir_iterator_base;
|
||||
|
||||
using udir_iterator_base =
|
||||
llvm::iterator_adaptor_base<udir_iterator, lookup_iterator,
|
||||
std::random_access_iterator_tag,
|
||||
UsingDirectiveDecl *>;
|
||||
|
||||
struct udir_iterator : udir_iterator_base {
|
||||
udir_iterator(lookup_iterator I) : udir_iterator_base(I) {}
|
||||
|
||||
UsingDirectiveDecl *operator*() const;
|
||||
};
|
||||
|
||||
typedef llvm::iterator_range<udir_iterator> udir_range;
|
||||
using udir_range = llvm::iterator_range<udir_iterator>;
|
||||
|
||||
udir_range using_directives() const;
|
||||
|
||||
// These are all defined in DependentDiagnostic.h.
|
||||
class ddiag_iterator;
|
||||
typedef llvm::iterator_range<DeclContext::ddiag_iterator> ddiag_range;
|
||||
|
||||
using ddiag_range = llvm::iterator_range<DeclContext::ddiag_iterator>;
|
||||
|
||||
inline ddiag_range ddiags() const;
|
||||
|
||||
@ -1883,6 +1928,8 @@ public:
|
||||
bool Deserialize = false) const;
|
||||
|
||||
private:
|
||||
friend class DependentDiagnostic;
|
||||
|
||||
void reconcileExternalVisibleStorage() const;
|
||||
bool LoadLexicalDeclsFromExternalStorage() const;
|
||||
|
||||
@ -1894,7 +1941,6 @@ private:
|
||||
/// use of addDeclInternal().
|
||||
void makeDeclVisibleInContextInternal(NamedDecl *D);
|
||||
|
||||
friend class DependentDiagnostic;
|
||||
StoredDeclsMap *CreateStoredDeclsMap(ASTContext &C) const;
|
||||
|
||||
void buildLookupImpl(DeclContext *DCtx, bool Internal);
|
||||
@ -1933,8 +1979,7 @@ struct cast_convert_decl_context<ToTy, true> {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // end clang.
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
||||
@ -1954,12 +1999,14 @@ struct cast_convert_val<ToTy,
|
||||
return *::clang::cast_convert_decl_context<ToTy>::doit(&Val);
|
||||
}
|
||||
};
|
||||
|
||||
template<class ToTy>
|
||||
struct cast_convert_val<ToTy, ::clang::DeclContext, ::clang::DeclContext> {
|
||||
static ToTy &doit(::clang::DeclContext &Val) {
|
||||
return *::clang::cast_convert_decl_context<ToTy>::doit(&Val);
|
||||
}
|
||||
};
|
||||
|
||||
template<class ToTy>
|
||||
struct cast_convert_val<ToTy,
|
||||
const ::clang::DeclContext*, const ::clang::DeclContext*> {
|
||||
@ -1967,6 +2014,7 @@ struct cast_convert_val<ToTy,
|
||||
return ::clang::cast_convert_decl_context<ToTy>::doit(Val);
|
||||
}
|
||||
};
|
||||
|
||||
template<class ToTy>
|
||||
struct cast_convert_val<ToTy, ::clang::DeclContext*, ::clang::DeclContext*> {
|
||||
static ToTy *doit(::clang::DeclContext *Val) {
|
||||
@ -2003,6 +2051,6 @@ struct cast_convert_val< const ::clang::DeclContext, FromTy*, FromTy*> {
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_DECLBASE_H
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
//===-- DeclContextInternals.h - DeclContext Representation -----*- C++ -*-===//
|
||||
//===- DeclContextInternals.h - DeclContext Representation ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -11,10 +11,12 @@
|
||||
// of DeclContext.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_DECLCONTEXTINTERNALS_H
|
||||
#define LLVM_CLANG_AST_DECLCONTEXTINTERNALS_H
|
||||
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
@ -22,6 +24,7 @@
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -30,21 +33,20 @@ class DependentDiagnostic;
|
||||
/// \brief An array of decls optimized for the common case of only containing
|
||||
/// one entry.
|
||||
struct StoredDeclsList {
|
||||
|
||||
/// \brief When in vector form, this is what the Data pointer points to.
|
||||
typedef SmallVector<NamedDecl *, 4> DeclsTy;
|
||||
using DeclsTy = SmallVector<NamedDecl *, 4>;
|
||||
|
||||
/// \brief A collection of declarations, with a flag to indicate if we have
|
||||
/// further external declarations.
|
||||
typedef llvm::PointerIntPair<DeclsTy *, 1, bool> DeclsAndHasExternalTy;
|
||||
using DeclsAndHasExternalTy = llvm::PointerIntPair<DeclsTy *, 1, bool>;
|
||||
|
||||
/// \brief The stored data, which will be either a pointer to a NamedDecl,
|
||||
/// or a pointer to a vector with a flag to indicate if there are further
|
||||
/// external declarations.
|
||||
llvm::PointerUnion<NamedDecl*, DeclsAndHasExternalTy> Data;
|
||||
llvm::PointerUnion<NamedDecl *, DeclsAndHasExternalTy> Data;
|
||||
|
||||
public:
|
||||
StoredDeclsList() {}
|
||||
StoredDeclsList() = default;
|
||||
|
||||
StoredDeclsList(StoredDeclsList &&RHS) : Data(RHS.Data) {
|
||||
RHS.Data = (NamedDecl *)nullptr;
|
||||
@ -186,7 +188,6 @@ public:
|
||||
|
||||
/// AddSubsequentDecl - This is called on the second and later decl when it is
|
||||
/// not a redeclaration to merge it into the appropriate place in our list.
|
||||
///
|
||||
void AddSubsequentDecl(NamedDecl *D) {
|
||||
assert(!isNull() && "don't AddSubsequentDecl when we have no decls");
|
||||
|
||||
@ -237,28 +238,28 @@ public:
|
||||
};
|
||||
|
||||
class StoredDeclsMap
|
||||
: public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
|
||||
|
||||
: public llvm::SmallDenseMap<DeclarationName, StoredDeclsList, 4> {
|
||||
public:
|
||||
static void DestroyAll(StoredDeclsMap *Map, bool Dependent);
|
||||
|
||||
private:
|
||||
friend class ASTContext; // walks the chain deleting these
|
||||
friend class DeclContext;
|
||||
|
||||
llvm::PointerIntPair<StoredDeclsMap*, 1> Previous;
|
||||
};
|
||||
|
||||
class DependentStoredDeclsMap : public StoredDeclsMap {
|
||||
public:
|
||||
DependentStoredDeclsMap() : FirstDiagnostic(nullptr) {}
|
||||
DependentStoredDeclsMap() = default;
|
||||
|
||||
private:
|
||||
friend class DependentDiagnostic;
|
||||
friend class DeclContext; // iterates over diagnostics
|
||||
friend class DependentDiagnostic;
|
||||
|
||||
DependentDiagnostic *FirstDiagnostic;
|
||||
DependentDiagnostic *FirstDiagnostic = nullptr;
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_DECLCONTEXTINTERNALS_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- DeclFriend.h - Classes for C++ friend declarations -*- C++ -*------===//
|
||||
//===- DeclFriend.h - Classes for C++ friend declarations -------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -15,16 +15,30 @@
|
||||
#ifndef LLVM_CLANG_AST_DECLFRIEND_H
|
||||
#define LLVM_CLANG_AST_DECLFRIEND_H
|
||||
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/ExternalASTSource.h"
|
||||
#include "clang/AST/TypeLoc.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/None.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/TrailingObjects.h"
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
|
||||
/// FriendDecl - Represents the declaration of a friend entity,
|
||||
/// which can be a function, a type, or a templated function or type.
|
||||
// For example:
|
||||
/// For example:
|
||||
///
|
||||
/// @code
|
||||
/// template <typename T> class A {
|
||||
@ -41,10 +55,14 @@ class FriendDecl final
|
||||
: public Decl,
|
||||
private llvm::TrailingObjects<FriendDecl, TemplateParameterList *> {
|
||||
virtual void anchor();
|
||||
|
||||
public:
|
||||
typedef llvm::PointerUnion<NamedDecl*,TypeSourceInfo*> FriendUnion;
|
||||
using FriendUnion = llvm::PointerUnion<NamedDecl *, TypeSourceInfo *>;
|
||||
|
||||
private:
|
||||
friend class CXXRecordDecl;
|
||||
friend class CXXRecordDecl::friend_iterator;
|
||||
|
||||
// The declaration that's a friend of this class.
|
||||
FriendUnion Friend;
|
||||
|
||||
@ -64,35 +82,33 @@ private:
|
||||
// template <class T> friend class A<T>::B;
|
||||
unsigned NumTPLists : 31;
|
||||
|
||||
friend class CXXRecordDecl::friend_iterator;
|
||||
friend class CXXRecordDecl;
|
||||
|
||||
FriendDecl(DeclContext *DC, SourceLocation L, FriendUnion Friend,
|
||||
SourceLocation FriendL,
|
||||
ArrayRef<TemplateParameterList*> FriendTypeTPLists)
|
||||
: Decl(Decl::Friend, DC, L),
|
||||
Friend(Friend),
|
||||
NextFriend(),
|
||||
FriendLoc(FriendL),
|
||||
UnsupportedFriend(false),
|
||||
NumTPLists(FriendTypeTPLists.size()) {
|
||||
ArrayRef<TemplateParameterList *> FriendTypeTPLists)
|
||||
: Decl(Decl::Friend, DC, L), Friend(Friend), FriendLoc(FriendL),
|
||||
UnsupportedFriend(false), NumTPLists(FriendTypeTPLists.size()) {
|
||||
for (unsigned i = 0; i < NumTPLists; ++i)
|
||||
getTrailingObjects<TemplateParameterList *>()[i] = FriendTypeTPLists[i];
|
||||
}
|
||||
|
||||
FriendDecl(EmptyShell Empty, unsigned NumFriendTypeTPLists)
|
||||
: Decl(Decl::Friend, Empty), NextFriend(),
|
||||
UnsupportedFriend(false),
|
||||
NumTPLists(NumFriendTypeTPLists) { }
|
||||
: Decl(Decl::Friend, Empty), UnsupportedFriend(false),
|
||||
NumTPLists(NumFriendTypeTPLists) {}
|
||||
|
||||
FriendDecl *getNextFriend() {
|
||||
if (!NextFriend.isOffset())
|
||||
return cast_or_null<FriendDecl>(NextFriend.get(nullptr));
|
||||
return getNextFriendSlowCase();
|
||||
}
|
||||
|
||||
FriendDecl *getNextFriendSlowCase();
|
||||
|
||||
public:
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTDeclWriter;
|
||||
friend class ASTNodeImporter;
|
||||
friend TrailingObjects;
|
||||
|
||||
static FriendDecl *Create(ASTContext &C, DeclContext *DC,
|
||||
SourceLocation L, FriendUnion Friend_,
|
||||
SourceLocation FriendL,
|
||||
@ -108,9 +124,11 @@ public:
|
||||
TypeSourceInfo *getFriendType() const {
|
||||
return Friend.dyn_cast<TypeSourceInfo*>();
|
||||
}
|
||||
|
||||
unsigned getFriendTypeNumTemplateParameterLists() const {
|
||||
return NumTPLists;
|
||||
}
|
||||
|
||||
TemplateParameterList *getFriendTypeTemplateParameterList(unsigned N) const {
|
||||
assert(N < NumTPLists);
|
||||
return getTrailingObjects<TemplateParameterList *>()[N];
|
||||
@ -119,7 +137,7 @@ public:
|
||||
/// If this friend declaration doesn't name a type, return the inner
|
||||
/// declaration.
|
||||
NamedDecl *getFriendDecl() const {
|
||||
return Friend.dyn_cast<NamedDecl*>();
|
||||
return Friend.dyn_cast<NamedDecl *>();
|
||||
}
|
||||
|
||||
/// Retrieves the location of the 'friend' keyword.
|
||||
@ -164,27 +182,24 @@ public:
|
||||
// Implement isa/cast/dyncast/etc.
|
||||
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
|
||||
static bool classofKind(Kind K) { return K == Decl::Friend; }
|
||||
|
||||
friend class ASTDeclReader;
|
||||
friend class ASTDeclWriter;
|
||||
friend class ASTNodeImporter;
|
||||
friend TrailingObjects;
|
||||
};
|
||||
|
||||
/// An iterator over the friend declarations of a class.
|
||||
class CXXRecordDecl::friend_iterator {
|
||||
friend class CXXRecordDecl;
|
||||
|
||||
FriendDecl *Ptr;
|
||||
|
||||
friend class CXXRecordDecl;
|
||||
explicit friend_iterator(FriendDecl *Ptr) : Ptr(Ptr) {}
|
||||
public:
|
||||
friend_iterator() {}
|
||||
|
||||
typedef FriendDecl *value_type;
|
||||
typedef FriendDecl *reference;
|
||||
typedef FriendDecl *pointer;
|
||||
typedef int difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
public:
|
||||
friend_iterator() = default;
|
||||
|
||||
using value_type = FriendDecl *;
|
||||
using reference = FriendDecl *;
|
||||
using pointer = FriendDecl *;
|
||||
using difference_type = int;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
reference operator*() const { return Ptr; }
|
||||
|
||||
@ -240,6 +255,6 @@ inline void CXXRecordDecl::pushFriendDecl(FriendDecl *FD) {
|
||||
data().FirstFriend = FD;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_DECLFRIEND_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- DeclGroup.h - Classes for representing groups of Decls -*- C++ -*-===//
|
||||
//===- DeclGroup.h - Classes for representing groups of Decls ---*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -14,26 +14,26 @@
|
||||
#ifndef LLVM_CLANG_AST_DECLGROUP_H
|
||||
#define LLVM_CLANG_AST_DECLGROUP_H
|
||||
|
||||
#include "llvm/Support/DataTypes.h"
|
||||
#include "llvm/Support/TrailingObjects.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class Decl;
|
||||
class DeclGroup;
|
||||
class DeclGroupIterator;
|
||||
|
||||
class DeclGroup final : private llvm::TrailingObjects<DeclGroup, Decl *> {
|
||||
// FIXME: Include a TypeSpecifier object.
|
||||
unsigned NumDecls;
|
||||
unsigned NumDecls = 0;
|
||||
|
||||
private:
|
||||
DeclGroup() : NumDecls(0) {}
|
||||
DeclGroup() = default;
|
||||
DeclGroup(unsigned numdecls, Decl** decls);
|
||||
|
||||
public:
|
||||
friend TrailingObjects;
|
||||
|
||||
static DeclGroup *Create(ASTContext &C, Decl **Decls, unsigned NumDecls);
|
||||
|
||||
unsigned size() const { return NumDecls; }
|
||||
@ -47,23 +47,21 @@ public:
|
||||
assert (i < NumDecls && "Out-of-bounds access.");
|
||||
return getTrailingObjects<Decl *>()[i];
|
||||
}
|
||||
|
||||
friend TrailingObjects;
|
||||
};
|
||||
|
||||
class DeclGroupRef {
|
||||
// Note this is not a PointerIntPair because we need the address of the
|
||||
// non-group case to be valid as a Decl** for iteration.
|
||||
enum Kind { SingleDeclKind=0x0, DeclGroupKind=0x1, Mask=0x1 };
|
||||
Decl* D;
|
||||
|
||||
Decl* D = nullptr;
|
||||
|
||||
Kind getKind() const {
|
||||
return (Kind) (reinterpret_cast<uintptr_t>(D) & Mask);
|
||||
}
|
||||
|
||||
public:
|
||||
DeclGroupRef() : D(nullptr) {}
|
||||
|
||||
DeclGroupRef() = default;
|
||||
explicit DeclGroupRef(Decl* d) : D(d) {}
|
||||
explicit DeclGroupRef(DeclGroup* dg)
|
||||
: D((Decl*) (reinterpret_cast<uintptr_t>(dg) | DeclGroupKind)) {}
|
||||
@ -76,8 +74,8 @@ public:
|
||||
return DeclGroupRef(DeclGroup::Create(C, Decls, NumDecls));
|
||||
}
|
||||
|
||||
typedef Decl** iterator;
|
||||
typedef Decl* const * const_iterator;
|
||||
using iterator = Decl **;
|
||||
using const_iterator = Decl * const *;
|
||||
|
||||
bool isNull() const { return D == nullptr; }
|
||||
bool isSingleDecl() const { return getKind() == SingleDeclKind; }
|
||||
@ -133,22 +131,26 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // end clang namespace
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
||||
// DeclGroupRef is "like a pointer", implement PointerLikeTypeTraits.
|
||||
template <typename T>
|
||||
class PointerLikeTypeTraits;
|
||||
struct PointerLikeTypeTraits;
|
||||
template <>
|
||||
class PointerLikeTypeTraits<clang::DeclGroupRef> {
|
||||
public:
|
||||
struct PointerLikeTypeTraits<clang::DeclGroupRef> {
|
||||
static inline void *getAsVoidPointer(clang::DeclGroupRef P) {
|
||||
return P.getAsOpaquePtr();
|
||||
}
|
||||
|
||||
static inline clang::DeclGroupRef getFromVoidPointer(void *P) {
|
||||
return clang::DeclGroupRef::getFromOpaquePtr(P);
|
||||
}
|
||||
|
||||
enum { NumLowBitsAvailable = 0 };
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_CLANG_AST_DECLGROUP_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- DeclLookups.h - Low-level interface to all names in a DC-*- C++ -*-===//
|
||||
//===- DeclLookups.h - Low-level interface to all names in a DC -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -18,6 +18,9 @@
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclContextInternals.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/ExternalASTSource.h"
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -25,14 +28,15 @@ namespace clang {
|
||||
/// of looking up every possible name.
|
||||
class DeclContext::all_lookups_iterator {
|
||||
StoredDeclsMap::iterator It, End;
|
||||
public:
|
||||
typedef lookup_result value_type;
|
||||
typedef lookup_result reference;
|
||||
typedef lookup_result pointer;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::ptrdiff_t difference_type;
|
||||
|
||||
all_lookups_iterator() {}
|
||||
public:
|
||||
using value_type = lookup_result;
|
||||
using reference = lookup_result;
|
||||
using pointer = lookup_result;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
all_lookups_iterator() = default;
|
||||
all_lookups_iterator(StoredDeclsMap::iterator It,
|
||||
StoredDeclsMap::iterator End)
|
||||
: It(It), End(End) {}
|
||||
@ -63,6 +67,7 @@ public:
|
||||
friend bool operator==(all_lookups_iterator x, all_lookups_iterator y) {
|
||||
return x.It == y.It;
|
||||
}
|
||||
|
||||
friend bool operator!=(all_lookups_iterator x, all_lookups_iterator y) {
|
||||
return x.It != y.It;
|
||||
}
|
||||
@ -110,6 +115,6 @@ DeclContext::all_lookups_iterator DeclContext::noload_lookups_end() const {
|
||||
return noload_lookups().end();
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_DECLLOOKUPS_H
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -100,12 +100,22 @@ public:
|
||||
///
|
||||
/// Here 'omp_out += omp_in' is a combiner and 'omp_priv = 0' is an initializer.
|
||||
class OMPDeclareReductionDecl final : public ValueDecl, public DeclContext {
|
||||
public:
|
||||
enum InitKind {
|
||||
CallInit, // Initialized by function call.
|
||||
DirectInit, // omp_priv(<expr>)
|
||||
CopyInit // omp_priv = <expr>
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ASTDeclReader;
|
||||
/// \brief Combiner for declare reduction construct.
|
||||
Expr *Combiner;
|
||||
/// \brief Initializer for declare reduction construct.
|
||||
Expr *Initializer;
|
||||
/// Kind of initializer - function call or omp_priv<init_expr> initializtion.
|
||||
InitKind InitializerKind = CallInit;
|
||||
|
||||
/// \brief Reference to the previous declare reduction construct in the same
|
||||
/// scope with the same name. Required for proper templates instantiation if
|
||||
/// the declare reduction construct is declared inside compound statement.
|
||||
@ -117,7 +127,8 @@ private:
|
||||
DeclarationName Name, QualType Ty,
|
||||
OMPDeclareReductionDecl *PrevDeclInScope)
|
||||
: ValueDecl(DK, DC, L, Name, Ty), DeclContext(DK), Combiner(nullptr),
|
||||
Initializer(nullptr), PrevDeclInScope(PrevDeclInScope) {}
|
||||
Initializer(nullptr), InitializerKind(CallInit),
|
||||
PrevDeclInScope(PrevDeclInScope) {}
|
||||
|
||||
void setPrevDeclInScope(OMPDeclareReductionDecl *Prev) {
|
||||
PrevDeclInScope = Prev;
|
||||
@ -142,8 +153,13 @@ public:
|
||||
/// construct.
|
||||
Expr *getInitializer() { return Initializer; }
|
||||
const Expr *getInitializer() const { return Initializer; }
|
||||
/// Get initializer kind.
|
||||
InitKind getInitializerKind() const { return InitializerKind; }
|
||||
/// \brief Set initializer expression for the declare reduction construct.
|
||||
void setInitializer(Expr *E) { Initializer = E; }
|
||||
void setInitializer(Expr *E, InitKind IK) {
|
||||
Initializer = E;
|
||||
InitializerKind = IK;
|
||||
}
|
||||
|
||||
/// \brief Get reference to previous declare reduction construct in the same
|
||||
/// scope with the same name.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
//===--- DeclVisitor.h - Visitor for Decl subclasses ------------*- C++ -*-===//
|
||||
//===- DeclVisitor.h - Visitor for Decl subclasses --------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -10,27 +10,30 @@
|
||||
// This file defines the DeclVisitor interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_DECLVISITOR_H
|
||||
#define LLVM_CLANG_AST_DECLVISITOR_H
|
||||
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/DeclFriend.h"
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/DeclOpenMP.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
namespace declvisitor {
|
||||
|
||||
template <typename T> struct make_ptr { typedef T *type; };
|
||||
template <typename T> struct make_const_ptr { typedef const T *type; };
|
||||
template <typename T> struct make_ptr { using type = T *; };
|
||||
template <typename T> struct make_const_ptr { using type = const T *; };
|
||||
|
||||
/// \brief A simple visitor class that helps create declaration visitors.
|
||||
template<template <typename> class Ptr, typename ImplClass, typename RetTy=void>
|
||||
class Base {
|
||||
public:
|
||||
|
||||
#define PTR(CLASS) typename Ptr<CLASS>::type
|
||||
#define DISPATCH(NAME, CLASS) \
|
||||
return static_cast<ImplClass*>(this)->Visit##NAME(static_cast<PTR(CLASS)>(D))
|
||||
@ -57,23 +60,23 @@ public:
|
||||
#undef DISPATCH
|
||||
};
|
||||
|
||||
} // end namespace declvisitor
|
||||
} // namespace declvisitor
|
||||
|
||||
/// \brief A simple visitor class that helps create declaration visitors.
|
||||
///
|
||||
/// This class does not preserve constness of Decl pointers (see also
|
||||
/// ConstDeclVisitor).
|
||||
template<typename ImplClass, typename RetTy=void>
|
||||
template<typename ImplClass, typename RetTy = void>
|
||||
class DeclVisitor
|
||||
: public declvisitor::Base<declvisitor::make_ptr, ImplClass, RetTy> {};
|
||||
|
||||
/// \brief A simple visitor class that helps create declaration visitors.
|
||||
///
|
||||
/// This class preserves constness of Decl pointers (see also DeclVisitor).
|
||||
template<typename ImplClass, typename RetTy=void>
|
||||
template<typename ImplClass, typename RetTy = void>
|
||||
class ConstDeclVisitor
|
||||
: public declvisitor::Base<declvisitor::make_const_ptr, ImplClass, RetTy> {};
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_DECLVISITOR_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- DeclarationName.h - Representation of declaration names -*- C++ -*-===//
|
||||
//===- DeclarationName.h - Representation of declaration names --*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -10,36 +10,42 @@
|
||||
// This file declares the DeclarationName and DeclarationNameTable classes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_DECLARATIONNAME_H
|
||||
#define LLVM_CLANG_AST_DECLARATIONNAME_H
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/PartialDiagnostic.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace llvm {
|
||||
template <typename T> struct DenseMapInfo;
|
||||
}
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
class ASTContext;
|
||||
class CXXDeductionGuideNameExtra;
|
||||
class CXXLiteralOperatorIdName;
|
||||
class CXXOperatorIdName;
|
||||
class CXXSpecialName;
|
||||
class DeclarationNameExtra;
|
||||
class IdentifierInfo;
|
||||
class MultiKeywordSelector;
|
||||
enum OverloadedOperatorKind : int;
|
||||
struct PrintingPolicy;
|
||||
class QualType;
|
||||
class TemplateDecl;
|
||||
class Type;
|
||||
class TypeSourceInfo;
|
||||
class UsingDirectiveDecl;
|
||||
|
||||
template <typename> class CanQual;
|
||||
typedef CanQual<Type> CanQualType;
|
||||
class ASTContext;
|
||||
template <typename> class CanQual;
|
||||
class CXXDeductionGuideNameExtra;
|
||||
class CXXLiteralOperatorIdName;
|
||||
class CXXOperatorIdName;
|
||||
class CXXSpecialName;
|
||||
class DeclarationNameExtra;
|
||||
class IdentifierInfo;
|
||||
class MultiKeywordSelector;
|
||||
enum OverloadedOperatorKind : int;
|
||||
struct PrintingPolicy;
|
||||
class QualType;
|
||||
class TemplateDecl;
|
||||
class Type;
|
||||
class TypeSourceInfo;
|
||||
class UsingDirectiveDecl;
|
||||
|
||||
using CanQualType = CanQual<Type>;
|
||||
|
||||
/// DeclarationName - The name of a declaration. In the common case,
|
||||
/// this just stores an IdentifierInfo pointer to a normal
|
||||
@ -63,9 +69,13 @@ public:
|
||||
CXXLiteralOperatorName,
|
||||
CXXUsingDirective
|
||||
};
|
||||
|
||||
static const unsigned NumNameKinds = CXXUsingDirective + 1;
|
||||
|
||||
private:
|
||||
friend class DeclarationNameTable;
|
||||
friend class NamedDecl;
|
||||
|
||||
/// StoredNameKind - The kind of name that is actually stored in the
|
||||
/// upper bits of the Ptr field. This is only used internally.
|
||||
///
|
||||
@ -99,7 +109,18 @@ private:
|
||||
/// DeclarationNameExtra structure, whose first value will tell us
|
||||
/// whether this is an Objective-C selector, C++ operator-id name,
|
||||
/// or special C++ name.
|
||||
uintptr_t Ptr;
|
||||
uintptr_t Ptr = 0;
|
||||
|
||||
// Construct a declaration name from the name of a C++ constructor,
|
||||
// destructor, or conversion function.
|
||||
DeclarationName(DeclarationNameExtra *Name)
|
||||
: Ptr(reinterpret_cast<uintptr_t>(Name)) {
|
||||
assert((Ptr & PtrMask) == 0 && "Improperly aligned DeclarationNameExtra");
|
||||
Ptr |= StoredDeclarationNameExtra;
|
||||
}
|
||||
|
||||
/// Construct a declaration name from a raw pointer.
|
||||
DeclarationName(uintptr_t Ptr) : Ptr(Ptr) {}
|
||||
|
||||
/// getStoredNameKind - Return the kind of object that is stored in
|
||||
/// Ptr.
|
||||
@ -146,36 +167,22 @@ private:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Construct a declaration name from the name of a C++ constructor,
|
||||
// destructor, or conversion function.
|
||||
DeclarationName(DeclarationNameExtra *Name)
|
||||
: Ptr(reinterpret_cast<uintptr_t>(Name)) {
|
||||
assert((Ptr & PtrMask) == 0 && "Improperly aligned DeclarationNameExtra");
|
||||
Ptr |= StoredDeclarationNameExtra;
|
||||
}
|
||||
|
||||
/// Construct a declaration name from a raw pointer.
|
||||
DeclarationName(uintptr_t Ptr) : Ptr(Ptr) { }
|
||||
|
||||
friend class DeclarationNameTable;
|
||||
friend class NamedDecl;
|
||||
|
||||
/// getFETokenInfoAsVoidSlow - Retrieves the front end-specified pointer
|
||||
/// for this name as a void pointer if it's not an identifier.
|
||||
void *getFETokenInfoAsVoidSlow() const;
|
||||
|
||||
public:
|
||||
/// DeclarationName - Used to create an empty selector.
|
||||
DeclarationName() : Ptr(0) { }
|
||||
DeclarationName() = default;
|
||||
|
||||
// Construct a declaration name from an IdentifierInfo *.
|
||||
DeclarationName(const IdentifierInfo *II)
|
||||
: Ptr(reinterpret_cast<uintptr_t>(II)) {
|
||||
: Ptr(reinterpret_cast<uintptr_t>(II)) {
|
||||
assert((Ptr & PtrMask) == 0 && "Improperly aligned IdentifierInfo");
|
||||
}
|
||||
|
||||
// Construct a declaration name from an Objective-C selector.
|
||||
DeclarationName(Selector Sel) : Ptr(Sel.InfoPtr) { }
|
||||
DeclarationName(Selector Sel) : Ptr(Sel.InfoPtr) {}
|
||||
|
||||
/// getUsingDirectiveName - Return name for all using-directives.
|
||||
static DeclarationName getUsingDirectiveName();
|
||||
@ -344,16 +351,24 @@ inline bool operator>=(DeclarationName LHS, DeclarationName RHS) {
|
||||
/// getCXXConstructorName).
|
||||
class DeclarationNameTable {
|
||||
const ASTContext &Ctx;
|
||||
void *CXXSpecialNamesImpl; // Actually a FoldingSet<CXXSpecialName> *
|
||||
CXXOperatorIdName *CXXOperatorNames; // Operator names
|
||||
void *CXXLiteralOperatorNames; // Actually a CXXOperatorIdName*
|
||||
void *CXXDeductionGuideNames; // FoldingSet<CXXDeductionGuideNameExtra> *
|
||||
|
||||
DeclarationNameTable(const DeclarationNameTable&) = delete;
|
||||
void operator=(const DeclarationNameTable&) = delete;
|
||||
// Actually a FoldingSet<CXXSpecialName> *
|
||||
void *CXXSpecialNamesImpl;
|
||||
|
||||
// Operator names
|
||||
CXXOperatorIdName *CXXOperatorNames;
|
||||
|
||||
// Actually a CXXOperatorIdName*
|
||||
void *CXXLiteralOperatorNames;
|
||||
|
||||
// FoldingSet<CXXDeductionGuideNameExtra> *
|
||||
void *CXXDeductionGuideNames;
|
||||
|
||||
public:
|
||||
DeclarationNameTable(const ASTContext &C);
|
||||
DeclarationNameTable(const DeclarationNameTable &) = delete;
|
||||
DeclarationNameTable &operator=(const DeclarationNameTable &) = delete;
|
||||
|
||||
~DeclarationNameTable();
|
||||
|
||||
/// getIdentifier - Create a declaration name that is a simple
|
||||
@ -428,10 +443,10 @@ struct DeclarationNameLoc {
|
||||
};
|
||||
|
||||
DeclarationNameLoc(DeclarationName Name);
|
||||
|
||||
// FIXME: this should go away once all DNLocs are properly initialized.
|
||||
DeclarationNameLoc() { memset((void*) this, 0, sizeof(*this)); }
|
||||
}; // struct DeclarationNameLoc
|
||||
|
||||
};
|
||||
|
||||
/// DeclarationNameInfo - A collector data type for bundling together
|
||||
/// a DeclarationName and the correspnding source/type location info.
|
||||
@ -439,29 +454,33 @@ struct DeclarationNameInfo {
|
||||
private:
|
||||
/// Name - The declaration name, also encoding name kind.
|
||||
DeclarationName Name;
|
||||
|
||||
/// Loc - The main source location for the declaration name.
|
||||
SourceLocation NameLoc;
|
||||
|
||||
/// Info - Further source/type location info for special kinds of names.
|
||||
DeclarationNameLoc LocInfo;
|
||||
|
||||
public:
|
||||
// FIXME: remove it.
|
||||
DeclarationNameInfo() {}
|
||||
DeclarationNameInfo() = default;
|
||||
|
||||
DeclarationNameInfo(DeclarationName Name, SourceLocation NameLoc)
|
||||
: Name(Name), NameLoc(NameLoc), LocInfo(Name) {}
|
||||
: Name(Name), NameLoc(NameLoc), LocInfo(Name) {}
|
||||
|
||||
DeclarationNameInfo(DeclarationName Name, SourceLocation NameLoc,
|
||||
DeclarationNameLoc LocInfo)
|
||||
: Name(Name), NameLoc(NameLoc), LocInfo(LocInfo) {}
|
||||
: Name(Name), NameLoc(NameLoc), LocInfo(LocInfo) {}
|
||||
|
||||
/// getName - Returns the embedded declaration name.
|
||||
DeclarationName getName() const { return Name; }
|
||||
|
||||
/// setName - Sets the embedded declaration name.
|
||||
void setName(DeclarationName N) { Name = N; }
|
||||
|
||||
/// getLoc - Returns the main location of the declaration name.
|
||||
SourceLocation getLoc() const { return NameLoc; }
|
||||
|
||||
/// setLoc - Sets the main location of the declaration name.
|
||||
void setLoc(SourceLocation L) { NameLoc = L; }
|
||||
|
||||
@ -477,6 +496,7 @@ public:
|
||||
Name.getNameKind() == DeclarationName::CXXConversionFunctionName);
|
||||
return LocInfo.NamedType.TInfo;
|
||||
}
|
||||
|
||||
/// setNamedTypeInfo - Sets the source type info associated to
|
||||
/// the name. Assumes it is a constructor, destructor or conversion.
|
||||
void setNamedTypeInfo(TypeSourceInfo *TInfo) {
|
||||
@ -495,6 +515,7 @@ public:
|
||||
SourceLocation::getFromRawEncoding(LocInfo.CXXOperatorName.EndOpNameLoc)
|
||||
);
|
||||
}
|
||||
|
||||
/// setCXXOperatorNameRange - Sets the range of the operator name
|
||||
/// (without the operator keyword). Assumes it is a C++ operator.
|
||||
void setCXXOperatorNameRange(SourceRange R) {
|
||||
@ -511,6 +532,7 @@ public:
|
||||
return SourceLocation::
|
||||
getFromRawEncoding(LocInfo.CXXLiteralOperatorName.OpNameLoc);
|
||||
}
|
||||
|
||||
/// setCXXLiteralOperatorNameLoc - Sets the location of the literal
|
||||
/// operator name (not the operator keyword).
|
||||
/// Assumes it is a literal operator.
|
||||
@ -534,15 +556,19 @@ public:
|
||||
|
||||
/// getBeginLoc - Retrieve the location of the first token.
|
||||
SourceLocation getBeginLoc() const { return NameLoc; }
|
||||
|
||||
/// getEndLoc - Retrieve the location of the last token.
|
||||
SourceLocation getEndLoc() const;
|
||||
|
||||
/// getSourceRange - The range of the declaration name.
|
||||
SourceRange getSourceRange() const LLVM_READONLY {
|
||||
return SourceRange(getLocStart(), getLocEnd());
|
||||
}
|
||||
|
||||
SourceLocation getLocStart() const LLVM_READONLY {
|
||||
return getBeginLoc();
|
||||
}
|
||||
|
||||
SourceLocation getLocEnd() const LLVM_READONLY {
|
||||
SourceLocation EndLoc = getEndLoc();
|
||||
return EndLoc.isValid() ? EndLoc : getLocStart();
|
||||
@ -573,9 +599,10 @@ inline raw_ostream &operator<<(raw_ostream &OS,
|
||||
return OS;
|
||||
}
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Define DenseMapInfo so that DeclarationNames can be used as keys
|
||||
/// in DenseMap and DenseSets.
|
||||
template<>
|
||||
@ -601,6 +628,6 @@ struct DenseMapInfo<clang::DeclarationName> {
|
||||
template <>
|
||||
struct isPodLike<clang::DeclarationName> { static const bool value = true; };
|
||||
|
||||
} // end namespace llvm
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_DECLARATIONNAME_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===-- DependentDiagnostic.h - Dependently-generated diagnostics -*- C++ -*-=//
|
||||
//==- DependentDiagnostic.h - Dependently-generated diagnostics --*- C++ -*-==//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -23,6 +23,9 @@
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/PartialDiagnostic.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -94,6 +97,9 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
friend class DeclContext::ddiag_iterator;
|
||||
friend class DependentStoredDeclsMap;
|
||||
|
||||
DependentDiagnostic(const PartialDiagnostic &PDiag,
|
||||
PartialDiagnostic::Storage *Storage)
|
||||
: Diag(PDiag, Storage) {}
|
||||
@ -102,8 +108,6 @@ private:
|
||||
DeclContext *Parent,
|
||||
const PartialDiagnostic &PDiag);
|
||||
|
||||
friend class DependentStoredDeclsMap;
|
||||
friend class DeclContext::ddiag_iterator;
|
||||
DependentDiagnostic *NextDiagnostic;
|
||||
|
||||
PartialDiagnostic Diag;
|
||||
@ -118,19 +122,17 @@ private:
|
||||
} AccessData;
|
||||
};
|
||||
|
||||
///
|
||||
|
||||
/// An iterator over the dependent diagnostics in a dependent context.
|
||||
class DeclContext::ddiag_iterator {
|
||||
public:
|
||||
ddiag_iterator() : Ptr(nullptr) {}
|
||||
ddiag_iterator() = default;
|
||||
explicit ddiag_iterator(DependentDiagnostic *Ptr) : Ptr(Ptr) {}
|
||||
|
||||
typedef DependentDiagnostic *value_type;
|
||||
typedef DependentDiagnostic *reference;
|
||||
typedef DependentDiagnostic *pointer;
|
||||
typedef int difference_type;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
using value_type = DependentDiagnostic *;
|
||||
using reference = DependentDiagnostic *;
|
||||
using pointer = DependentDiagnostic *;
|
||||
using difference_type = int;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
reference operator*() const { return Ptr; }
|
||||
|
||||
@ -168,7 +170,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
DependentDiagnostic *Ptr;
|
||||
DependentDiagnostic *Ptr = nullptr;
|
||||
};
|
||||
|
||||
inline DeclContext::ddiag_range DeclContext::ddiags() const {
|
||||
@ -184,6 +186,6 @@ inline DeclContext::ddiag_range DeclContext::ddiags() const {
|
||||
return ddiag_range(ddiag_iterator(Map->FirstDiagnostic), ddiag_iterator());
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_DEPENDENTDIAGNOSTIC_H
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Basic/CharInfo.h"
|
||||
#include "clang/Basic/LangOptions.h"
|
||||
#include "clang/Basic/SyncScope.h"
|
||||
#include "clang/Basic/TypeTraits.h"
|
||||
#include "llvm/ADT/APFloat.h"
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
@ -274,6 +275,7 @@ public:
|
||||
MLV_LValueCast, // Specialized form of MLV_InvalidExpression.
|
||||
MLV_IncompleteType,
|
||||
MLV_ConstQualified,
|
||||
MLV_ConstQualifiedField,
|
||||
MLV_ConstAddrSpace,
|
||||
MLV_ArrayType,
|
||||
MLV_NoSetterProperty,
|
||||
@ -323,6 +325,7 @@ public:
|
||||
CM_LValueCast, // Same as CM_RValue, but indicates GCC cast-as-lvalue ext
|
||||
CM_NoSetterProperty,// Implicit assignment to ObjC property without setter
|
||||
CM_ConstQualified,
|
||||
CM_ConstQualifiedField,
|
||||
CM_ConstAddrSpace,
|
||||
CM_ArrayType,
|
||||
CM_IncompleteType
|
||||
@ -2345,6 +2348,12 @@ public:
|
||||
SourceLocation getLocStart() const LLVM_READONLY;
|
||||
SourceLocation getLocEnd() const LLVM_READONLY;
|
||||
|
||||
bool isCallToStdMove() const {
|
||||
const FunctionDecl* FD = getDirectCallee();
|
||||
return getNumArgs() == 1 && FD && FD->isInStdNamespace() &&
|
||||
FD->getIdentifier() && FD->getIdentifier()->isStr("move");
|
||||
}
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return T->getStmtClass() >= firstCallExprConstant &&
|
||||
T->getStmtClass() <= lastCallExprConstant;
|
||||
@ -2733,7 +2742,6 @@ protected:
|
||||
ty->containsUnexpandedParameterPack()) ||
|
||||
(op && op->containsUnexpandedParameterPack()))),
|
||||
Op(op) {
|
||||
assert(kind != CK_Invalid && "creating cast with invalid cast kind");
|
||||
CastExprBits.Kind = kind;
|
||||
setBasePathSize(BasePathSize);
|
||||
assert(CastConsistency());
|
||||
@ -2771,6 +2779,16 @@ public:
|
||||
path_const_iterator path_begin() const { return path_buffer(); }
|
||||
path_const_iterator path_end() const { return path_buffer() + path_size(); }
|
||||
|
||||
const FieldDecl *getTargetUnionField() const {
|
||||
assert(getCastKind() == CK_ToUnion);
|
||||
return getTargetFieldForToUnionCast(getType(), getSubExpr()->getType());
|
||||
}
|
||||
|
||||
static const FieldDecl *getTargetFieldForToUnionCast(QualType unionType,
|
||||
QualType opType);
|
||||
static const FieldDecl *getTargetFieldForToUnionCast(const RecordDecl *RD,
|
||||
QualType opType);
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return T->getStmtClass() >= firstCastExprConstant &&
|
||||
T->getStmtClass() <= lastCastExprConstant;
|
||||
@ -3054,7 +3072,7 @@ public:
|
||||
static bool isEqualityOp(Opcode Opc) { return Opc == BO_EQ || Opc == BO_NE; }
|
||||
bool isEqualityOp() const { return isEqualityOp(getOpcode()); }
|
||||
|
||||
static bool isComparisonOp(Opcode Opc) { return Opc >= BO_LT && Opc<=BO_NE; }
|
||||
static bool isComparisonOp(Opcode Opc) { return Opc >= BO_Cmp && Opc<=BO_NE; }
|
||||
bool isComparisonOp() const { return isComparisonOp(getOpcode()); }
|
||||
|
||||
static Opcode negateComparisonOp(Opcode Opc) {
|
||||
@ -3113,6 +3131,12 @@ public:
|
||||
return isShiftAssignOp(getOpcode());
|
||||
}
|
||||
|
||||
// Return true if a binary operator using the specified opcode and operands
|
||||
// would match the 'p = (i8*)nullptr + n' idiom for casting a pointer-sized
|
||||
// integer to a pointer.
|
||||
static bool isNullPointerArithmeticExtension(ASTContext &Ctx, Opcode Opc,
|
||||
Expr *LHS, Expr *RHS);
|
||||
|
||||
static bool classof(const Stmt *S) {
|
||||
return S->getStmtClass() >= firstBinaryOperatorConstant &&
|
||||
S->getStmtClass() <= lastBinaryOperatorConstant;
|
||||
@ -3986,6 +4010,10 @@ public:
|
||||
/// initializer)?
|
||||
bool isTransparent() const;
|
||||
|
||||
/// Is this the zero initializer {0} in a language which considers it
|
||||
/// idiomatic?
|
||||
bool isIdiomaticZeroInitializer(const LangOptions &LangOpts) const;
|
||||
|
||||
SourceLocation getLBraceLoc() const { return LBraceLoc; }
|
||||
void setLBraceLoc(SourceLocation Loc) { LBraceLoc = Loc; }
|
||||
SourceLocation getRBraceLoc() const { return RBraceLoc; }
|
||||
@ -3995,6 +4023,9 @@ public:
|
||||
InitListExpr *getSemanticForm() const {
|
||||
return isSemanticForm() ? nullptr : AltForm.getPointer();
|
||||
}
|
||||
bool isSyntacticForm() const {
|
||||
return !AltForm.getInt() || !AltForm.getPointer();
|
||||
}
|
||||
InitListExpr *getSyntacticForm() const {
|
||||
return isSemanticForm() ? AltForm.getPointer() : nullptr;
|
||||
}
|
||||
@ -5064,9 +5095,11 @@ public:
|
||||
|
||||
/// AtomicExpr - Variadic atomic builtins: __atomic_exchange, __atomic_fetch_*,
|
||||
/// __atomic_load, __atomic_store, and __atomic_compare_exchange_*, for the
|
||||
/// similarly-named C++11 instructions, and __c11 variants for <stdatomic.h>.
|
||||
/// All of these instructions take one primary pointer and at least one memory
|
||||
/// order.
|
||||
/// similarly-named C++11 instructions, and __c11 variants for <stdatomic.h>,
|
||||
/// and corresponding __opencl_atomic_* for OpenCL 2.0.
|
||||
/// All of these instructions take one primary pointer, at least one memory
|
||||
/// order. The instructions for which getScopeModel returns non-null value
|
||||
/// take one synch scope.
|
||||
class AtomicExpr : public Expr {
|
||||
public:
|
||||
enum AtomicOp {
|
||||
@ -5078,14 +5111,16 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
/// \brief Location of sub-expressions.
|
||||
/// The location of Scope sub-expression is NumSubExprs - 1, which is
|
||||
/// not fixed, therefore is not defined in enum.
|
||||
enum { PTR, ORDER, VAL1, ORDER_FAIL, VAL2, WEAK, END_EXPR };
|
||||
Stmt* SubExprs[END_EXPR];
|
||||
Stmt *SubExprs[END_EXPR + 1];
|
||||
unsigned NumSubExprs;
|
||||
SourceLocation BuiltinLoc, RParenLoc;
|
||||
AtomicOp Op;
|
||||
|
||||
friend class ASTStmtReader;
|
||||
|
||||
public:
|
||||
AtomicExpr(SourceLocation BLoc, ArrayRef<Expr*> args, QualType t,
|
||||
AtomicOp op, SourceLocation RP);
|
||||
@ -5103,8 +5138,12 @@ public:
|
||||
Expr *getOrder() const {
|
||||
return cast<Expr>(SubExprs[ORDER]);
|
||||
}
|
||||
Expr *getScope() const {
|
||||
assert(getScopeModel() && "No scope");
|
||||
return cast<Expr>(SubExprs[NumSubExprs - 1]);
|
||||
}
|
||||
Expr *getVal1() const {
|
||||
if (Op == AO__c11_atomic_init)
|
||||
if (Op == AO__c11_atomic_init || Op == AO__opencl_atomic_init)
|
||||
return cast<Expr>(SubExprs[ORDER]);
|
||||
assert(NumSubExprs > VAL1);
|
||||
return cast<Expr>(SubExprs[VAL1]);
|
||||
@ -5123,6 +5162,7 @@ public:
|
||||
assert(NumSubExprs > WEAK);
|
||||
return cast<Expr>(SubExprs[WEAK]);
|
||||
}
|
||||
QualType getValueType() const;
|
||||
|
||||
AtomicOp getOp() const { return Op; }
|
||||
unsigned getNumSubExprs() const { return NumSubExprs; }
|
||||
@ -5139,10 +5179,17 @@ public:
|
||||
bool isCmpXChg() const {
|
||||
return getOp() == AO__c11_atomic_compare_exchange_strong ||
|
||||
getOp() == AO__c11_atomic_compare_exchange_weak ||
|
||||
getOp() == AO__opencl_atomic_compare_exchange_strong ||
|
||||
getOp() == AO__opencl_atomic_compare_exchange_weak ||
|
||||
getOp() == AO__atomic_compare_exchange ||
|
||||
getOp() == AO__atomic_compare_exchange_n;
|
||||
}
|
||||
|
||||
bool isOpenCL() const {
|
||||
return getOp() >= AO__opencl_atomic_init &&
|
||||
getOp() <= AO__opencl_atomic_fetch_max;
|
||||
}
|
||||
|
||||
SourceLocation getBuiltinLoc() const { return BuiltinLoc; }
|
||||
SourceLocation getRParenLoc() const { return RParenLoc; }
|
||||
|
||||
@ -5160,6 +5207,24 @@ public:
|
||||
const_child_range children() const {
|
||||
return const_child_range(SubExprs, SubExprs + NumSubExprs);
|
||||
}
|
||||
|
||||
/// \brief Get atomic scope model for the atomic op code.
|
||||
/// \return empty atomic scope model if the atomic op code does not have
|
||||
/// scope operand.
|
||||
static std::unique_ptr<AtomicScopeModel> getScopeModel(AtomicOp Op) {
|
||||
auto Kind =
|
||||
(Op >= AO__opencl_atomic_load && Op <= AO__opencl_atomic_fetch_max)
|
||||
? AtomicScopeModelKind::OpenCL
|
||||
: AtomicScopeModelKind::None;
|
||||
return AtomicScopeModel::create(Kind);
|
||||
}
|
||||
|
||||
/// \brief Get atomic scope model.
|
||||
/// \return empty atomic scope model if this atomic expression does not have
|
||||
/// scope operand.
|
||||
std::unique_ptr<AtomicScopeModel> getScopeModel() const {
|
||||
return getScopeModel(getOp());
|
||||
}
|
||||
};
|
||||
|
||||
/// TypoExpr - Internal placeholder for expressions where typo correction
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,34 +16,159 @@
|
||||
|
||||
#include "clang/AST/ASTImporter.h"
|
||||
#include "clang/AST/ExternalASTSource.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
/// ExternalASTSource implementation that merges information from several
|
||||
/// ASTContexts.
|
||||
///
|
||||
/// ExtermalASTMerger maintains a vector of ASTImporters that it uses to import
|
||||
/// (potentially incomplete) Decls and DeclContexts from the source ASTContexts
|
||||
/// in response to ExternalASTSource API calls.
|
||||
///
|
||||
/// When lookup occurs in the resulting imported DeclContexts, the original
|
||||
/// DeclContexts need to be queried. Roughly, there are three cases here:
|
||||
///
|
||||
/// - The DeclContext of origin can be found by simple name lookup. In this
|
||||
/// case, no additional state is required.
|
||||
///
|
||||
/// - The DeclContext of origin is different from what would be found by name
|
||||
/// lookup. In this case, Origins contains an entry overriding lookup and
|
||||
/// specifying the correct pair of DeclContext/ASTContext.
|
||||
///
|
||||
/// - The DeclContext of origin was determined by another ExterenalASTMerger.
|
||||
/// (This is possible when the source ASTContext for one of the Importers has
|
||||
/// its own ExternalASTMerger). The origin must be properly forwarded in this
|
||||
/// case.
|
||||
///
|
||||
/// ExternalASTMerger's job is to maintain the data structures necessary to
|
||||
/// allow this. The data structures themselves can be extracted (read-only) and
|
||||
/// copied for re-use.
|
||||
class ExternalASTMerger : public ExternalASTSource {
|
||||
public:
|
||||
struct ImporterPair {
|
||||
std::unique_ptr<ASTImporter> Forward;
|
||||
std::unique_ptr<ASTImporter> Reverse;
|
||||
/// A single origin for a DeclContext. Unlike Decls, DeclContexts do
|
||||
/// not allow their containing ASTContext to be determined in all cases.
|
||||
struct DCOrigin {
|
||||
DeclContext *DC;
|
||||
ASTContext *AST;
|
||||
};
|
||||
|
||||
typedef std::map<const DeclContext *, DCOrigin> OriginMap;
|
||||
typedef std::vector<std::unique_ptr<ASTImporter>> ImporterVector;
|
||||
private:
|
||||
std::vector<ImporterPair> Importers;
|
||||
/// One importer exists for each source.
|
||||
ImporterVector Importers;
|
||||
/// Overrides in case name lookup would return nothing or would return
|
||||
/// the wrong thing.
|
||||
OriginMap Origins;
|
||||
/// The installed log stream.
|
||||
llvm::raw_ostream *LogStream;
|
||||
|
||||
public:
|
||||
struct ImporterEndpoint {
|
||||
/// The target for an ExternalASTMerger.
|
||||
///
|
||||
/// ASTImporters require both ASTContext and FileManager to be able to
|
||||
/// import SourceLocations properly.
|
||||
struct ImporterTarget {
|
||||
ASTContext &AST;
|
||||
FileManager &FM;
|
||||
};
|
||||
ExternalASTMerger(const ImporterEndpoint &Target,
|
||||
llvm::ArrayRef<ImporterEndpoint> Sources);
|
||||
/// A source for an ExternalASTMerger.
|
||||
///
|
||||
/// ASTImporters require both ASTContext and FileManager to be able to
|
||||
/// import SourceLocations properly. Additionally, when import occurs for
|
||||
/// a DeclContext whose origin has been overridden, then this
|
||||
/// ExternalASTMerger must be able to determine that.
|
||||
struct ImporterSource {
|
||||
ASTContext &AST;
|
||||
FileManager &FM;
|
||||
const OriginMap &OM;
|
||||
};
|
||||
|
||||
private:
|
||||
/// The target for this ExtenralASTMerger.
|
||||
ImporterTarget Target;
|
||||
|
||||
public:
|
||||
ExternalASTMerger(const ImporterTarget &Target,
|
||||
llvm::ArrayRef<ImporterSource> Sources);
|
||||
|
||||
/// Add a set of ASTContexts as possible origins.
|
||||
///
|
||||
/// Usually the set will be initialized in the constructor, but long-lived
|
||||
/// ExternalASTMergers may neeed to import from new sources (for example,
|
||||
/// newly-parsed source files).
|
||||
///
|
||||
/// Ensures that Importers does not gain duplicate entries as a result.
|
||||
void AddSources(llvm::ArrayRef<ImporterSource> Sources);
|
||||
|
||||
/// Remove a set of ASTContexts as possible origins.
|
||||
///
|
||||
/// Sometimes an origin goes away (for example, if a source file gets
|
||||
/// superseded by a newer version).
|
||||
///
|
||||
/// The caller is responsible for ensuring that this doesn't leave
|
||||
/// DeclContexts that can't be completed.
|
||||
void RemoveSources(llvm::ArrayRef<ImporterSource> Sources);
|
||||
|
||||
/// Implementation of the ExternalASTSource API.
|
||||
bool FindExternalVisibleDeclsByName(const DeclContext *DC,
|
||||
DeclarationName Name) override;
|
||||
|
||||
/// Implementation of the ExternalASTSource API.
|
||||
void
|
||||
FindExternalLexicalDecls(const DeclContext *DC,
|
||||
llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
|
||||
SmallVectorImpl<Decl *> &Result) override;
|
||||
|
||||
/// Implementation of the ExternalASTSource API.
|
||||
void CompleteType(TagDecl *Tag) override;
|
||||
|
||||
/// Implementation of the ExternalASTSource API.
|
||||
void CompleteType(ObjCInterfaceDecl *Interface) override;
|
||||
|
||||
/// Returns true if DC can be found in any source AST context.
|
||||
bool CanComplete(DeclContext *DC);
|
||||
|
||||
/// Records an origin in Origins only if name lookup would find
|
||||
/// something different or nothing at all.
|
||||
void MaybeRecordOrigin(const DeclContext *ToDC, DCOrigin Origin);
|
||||
|
||||
/// Regardless of any checks, override the Origin for a DeclContext.
|
||||
void ForceRecordOrigin(const DeclContext *ToDC, DCOrigin Origin);
|
||||
|
||||
/// Get a read-only view of the Origins map, for use in constructing
|
||||
/// an ImporterSource for another ExternalASTMerger.
|
||||
const OriginMap &GetOrigins() { return Origins; }
|
||||
|
||||
/// Returns true if Importers contains an ASTImporter whose source is
|
||||
/// OriginContext.
|
||||
bool HasImporterForOrigin(ASTContext &OriginContext);
|
||||
|
||||
/// Returns a reference to the ASTRImporter from Importers whose origin
|
||||
/// is OriginContext. This allows manual import of ASTs while preserving the
|
||||
/// OriginMap correctly.
|
||||
ASTImporter &ImporterForOrigin(ASTContext &OriginContext);
|
||||
|
||||
/// Sets the current log stream.
|
||||
void SetLogStream(llvm::raw_string_ostream &Stream) { LogStream = &Stream; }
|
||||
private:
|
||||
/// Records and origin in Origins.
|
||||
void RecordOriginImpl(const DeclContext *ToDC, DCOrigin Origin,
|
||||
ASTImporter &importer);
|
||||
|
||||
/// Performs an action for every DeclContext that is identified as
|
||||
/// corresponding (either by forced origin or by name lookup) to DC.
|
||||
template <typename CallbackType>
|
||||
void ForEachMatchingDC(const DeclContext *DC, CallbackType Callback);
|
||||
|
||||
public:
|
||||
/// Log something if there is a logging callback installed.
|
||||
llvm::raw_ostream &logs() { return *LogStream; }
|
||||
|
||||
/// True if the log stream is not llvm::nulls();
|
||||
bool LoggingEnabled() { return LogStream != &llvm::nulls(); }
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- ExternalASTSource.h - Abstract External AST Interface --*- C++ -*-===//
|
||||
//===- ExternalASTSource.h - Abstract External AST Interface ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -11,24 +11,44 @@
|
||||
// construction of AST nodes from some external source.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_EXTERNALASTSOURCE_H
|
||||
#define LLVM_CLANG_AST_EXTERNALASTSOURCE_H
|
||||
|
||||
#include "clang/AST/CharUnits.h"
|
||||
#include "clang/AST/DeclBase.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/Module.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/PointerUnion.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/iterator.h"
|
||||
#include "llvm/Support/PointerLikeTypeTraits.h"
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTConsumer;
|
||||
class ASTContext;
|
||||
class CXXBaseSpecifier;
|
||||
class CXXCtorInitializer;
|
||||
class CXXRecordDecl;
|
||||
class DeclarationName;
|
||||
class ExternalSemaSource; // layering violation required for downcasting
|
||||
class FieldDecl;
|
||||
class Module;
|
||||
class IdentifierInfo;
|
||||
class NamedDecl;
|
||||
class ObjCInterfaceDecl;
|
||||
class RecordDecl;
|
||||
class Selector;
|
||||
class Stmt;
|
||||
@ -42,30 +62,31 @@ class TagDecl;
|
||||
/// actual type and declaration nodes, and read parts of declaration
|
||||
/// contexts.
|
||||
class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
|
||||
friend class ExternalSemaSource;
|
||||
|
||||
/// Generation number for this external AST source. Must be increased
|
||||
/// whenever we might have added new redeclarations for existing decls.
|
||||
uint32_t CurrentGeneration;
|
||||
uint32_t CurrentGeneration = 0;
|
||||
|
||||
/// \brief Whether this AST source also provides information for
|
||||
/// semantic analysis.
|
||||
bool SemaSource;
|
||||
|
||||
friend class ExternalSemaSource;
|
||||
bool SemaSource = false;
|
||||
|
||||
public:
|
||||
ExternalASTSource() : CurrentGeneration(0), SemaSource(false) { }
|
||||
|
||||
ExternalASTSource() = default;
|
||||
virtual ~ExternalASTSource();
|
||||
|
||||
/// \brief RAII class for safely pairing a StartedDeserializing call
|
||||
/// with FinishedDeserializing.
|
||||
class Deserializing {
|
||||
ExternalASTSource *Source;
|
||||
|
||||
public:
|
||||
explicit Deserializing(ExternalASTSource *source) : Source(source) {
|
||||
assert(Source);
|
||||
Source->StartedDeserializing();
|
||||
}
|
||||
|
||||
~Deserializing() {
|
||||
Source->FinishedDeserializing();
|
||||
}
|
||||
@ -122,7 +143,7 @@ public:
|
||||
virtual CXXBaseSpecifier *GetExternalCXXBaseSpecifiers(uint64_t Offset);
|
||||
|
||||
/// \brief Update an out-of-date identifier.
|
||||
virtual void updateOutOfDateIdentifier(IdentifierInfo &II) { }
|
||||
virtual void updateOutOfDateIdentifier(IdentifierInfo &II) {}
|
||||
|
||||
/// \brief Find all declarations with the given name in the given context,
|
||||
/// and add them to the context by calling SetExternalVisibleDeclsForName
|
||||
@ -154,12 +175,13 @@ public:
|
||||
const Module *ClangModule = nullptr;
|
||||
|
||||
public:
|
||||
ASTSourceDescriptor(){};
|
||||
ASTSourceDescriptor() = default;
|
||||
ASTSourceDescriptor(StringRef Name, StringRef Path, StringRef ASTFile,
|
||||
ASTFileSignature Signature)
|
||||
: PCHModuleName(std::move(Name)), Path(std::move(Path)),
|
||||
ASTFile(std::move(ASTFile)), Signature(Signature){};
|
||||
ASTFile(std::move(ASTFile)), Signature(Signature) {}
|
||||
ASTSourceDescriptor(const Module &M);
|
||||
|
||||
std::string getModuleName() const;
|
||||
StringRef getPath() const { return Path; }
|
||||
StringRef getASTFile() const { return ASTFile; }
|
||||
@ -246,7 +268,6 @@ public:
|
||||
/// The default implementation of this method is a no-op.
|
||||
virtual void PrintStats();
|
||||
|
||||
|
||||
/// \brief Perform layout on the given record.
|
||||
///
|
||||
/// This routine allows the external AST source to provide an specific
|
||||
@ -289,7 +310,7 @@ public:
|
||||
size_t mmap_bytes;
|
||||
|
||||
MemoryBufferSizes(size_t malloc_bytes, size_t mmap_bytes)
|
||||
: malloc_bytes(malloc_bytes), mmap_bytes(mmap_bytes) {}
|
||||
: malloc_bytes(malloc_bytes), mmap_bytes(mmap_bytes) {}
|
||||
};
|
||||
|
||||
/// Return the amount of memory used by memory buffers, breaking down
|
||||
@ -329,12 +350,12 @@ struct LazyOffsetPtr {
|
||||
///
|
||||
/// If the low bit is clear, a pointer to the AST node. If the low
|
||||
/// bit is set, the upper 63 bits are the offset.
|
||||
mutable uint64_t Ptr;
|
||||
mutable uint64_t Ptr = 0;
|
||||
|
||||
public:
|
||||
LazyOffsetPtr() : Ptr(0) { }
|
||||
LazyOffsetPtr() = default;
|
||||
explicit LazyOffsetPtr(T *Ptr) : Ptr(reinterpret_cast<uint64_t>(Ptr)) {}
|
||||
|
||||
explicit LazyOffsetPtr(T *Ptr) : Ptr(reinterpret_cast<uint64_t>(Ptr)) { }
|
||||
explicit LazyOffsetPtr(uint64_t Offset) : Ptr((Offset << 1) | 0x01) {
|
||||
assert((Offset << 1 >> 1) == Offset && "Offsets must require < 63 bits");
|
||||
if (Offset == 0)
|
||||
@ -392,15 +413,16 @@ struct LazyGenerationalUpdatePtr {
|
||||
/// A cache of the value of this pointer, in the most recent generation in
|
||||
/// which we queried it.
|
||||
struct LazyData {
|
||||
LazyData(ExternalASTSource *Source, T Value)
|
||||
: ExternalSource(Source), LastGeneration(0), LastValue(Value) {}
|
||||
ExternalASTSource *ExternalSource;
|
||||
uint32_t LastGeneration;
|
||||
uint32_t LastGeneration = 0;
|
||||
T LastValue;
|
||||
|
||||
LazyData(ExternalASTSource *Source, T Value)
|
||||
: ExternalSource(Source), LastValue(Value) {}
|
||||
};
|
||||
|
||||
// Our value is represented as simply T if there is no external AST source.
|
||||
typedef llvm::PointerUnion<T, LazyData*> ValueType;
|
||||
using ValueType = llvm::PointerUnion<T, LazyData*>;
|
||||
ValueType Value;
|
||||
|
||||
LazyGenerationalUpdatePtr(ValueType V) : Value(V) {}
|
||||
@ -459,25 +481,31 @@ public:
|
||||
return LazyGenerationalUpdatePtr(ValueType::getFromOpaqueValue(Ptr));
|
||||
}
|
||||
};
|
||||
} // end namespace clang
|
||||
|
||||
} // namespace clang
|
||||
|
||||
/// Specialize PointerLikeTypeTraits to allow LazyGenerationalUpdatePtr to be
|
||||
/// placed into a PointerUnion.
|
||||
namespace llvm {
|
||||
|
||||
template<typename Owner, typename T,
|
||||
void (clang::ExternalASTSource::*Update)(Owner)>
|
||||
struct PointerLikeTypeTraits<
|
||||
clang::LazyGenerationalUpdatePtr<Owner, T, Update>> {
|
||||
typedef clang::LazyGenerationalUpdatePtr<Owner, T, Update> Ptr;
|
||||
using Ptr = clang::LazyGenerationalUpdatePtr<Owner, T, Update>;
|
||||
|
||||
static void *getAsVoidPointer(Ptr P) { return P.getOpaqueValue(); }
|
||||
static Ptr getFromVoidPointer(void *P) { return Ptr::getFromOpaqueValue(P); }
|
||||
|
||||
enum {
|
||||
NumLowBitsAvailable = PointerLikeTypeTraits<T>::NumLowBitsAvailable - 1
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
namespace clang {
|
||||
|
||||
/// \brief Represents a lazily-loaded vector of data.
|
||||
///
|
||||
/// The lazily-loaded vector of data contains data that is partially loaded
|
||||
@ -511,13 +539,14 @@ public:
|
||||
class iterator
|
||||
: public llvm::iterator_adaptor_base<
|
||||
iterator, int, std::random_access_iterator_tag, T, int, T *, T &> {
|
||||
friend class LazyVector;
|
||||
|
||||
LazyVector *Self;
|
||||
|
||||
iterator(LazyVector *Self, int Position)
|
||||
: iterator::iterator_adaptor_base(Position), Self(Self) {}
|
||||
|
||||
bool isLoaded() const { return this->I < 0; }
|
||||
friend class LazyVector;
|
||||
|
||||
public:
|
||||
iterator() : iterator(nullptr, 0) {}
|
||||
@ -562,23 +591,23 @@ public:
|
||||
};
|
||||
|
||||
/// \brief A lazy pointer to a statement.
|
||||
typedef LazyOffsetPtr<Stmt, uint64_t, &ExternalASTSource::GetExternalDeclStmt>
|
||||
LazyDeclStmtPtr;
|
||||
using LazyDeclStmtPtr =
|
||||
LazyOffsetPtr<Stmt, uint64_t, &ExternalASTSource::GetExternalDeclStmt>;
|
||||
|
||||
/// \brief A lazy pointer to a declaration.
|
||||
typedef LazyOffsetPtr<Decl, uint32_t, &ExternalASTSource::GetExternalDecl>
|
||||
LazyDeclPtr;
|
||||
using LazyDeclPtr =
|
||||
LazyOffsetPtr<Decl, uint32_t, &ExternalASTSource::GetExternalDecl>;
|
||||
|
||||
/// \brief A lazy pointer to a set of CXXCtorInitializers.
|
||||
typedef LazyOffsetPtr<CXXCtorInitializer *, uint64_t,
|
||||
&ExternalASTSource::GetExternalCXXCtorInitializers>
|
||||
LazyCXXCtorInitializersPtr;
|
||||
using LazyCXXCtorInitializersPtr =
|
||||
LazyOffsetPtr<CXXCtorInitializer *, uint64_t,
|
||||
&ExternalASTSource::GetExternalCXXCtorInitializers>;
|
||||
|
||||
/// \brief A lazy pointer to a set of CXXBaseSpecifiers.
|
||||
typedef LazyOffsetPtr<CXXBaseSpecifier, uint64_t,
|
||||
&ExternalASTSource::GetExternalCXXBaseSpecifiers>
|
||||
LazyCXXBaseSpecifiersPtr;
|
||||
using LazyCXXBaseSpecifiersPtr =
|
||||
LazyOffsetPtr<CXXBaseSpecifier, uint64_t,
|
||||
&ExternalASTSource::GetExternalCXXBaseSpecifiers>;
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_EXTERNALASTSOURCE_H
|
||||
|
@ -1,4 +1,4 @@
|
||||
//===--- GlobalDecl.h - Global declaration holder ---------------*- C++ -*-===//
|
||||
//===- GlobalDecl.h - Global declaration holder -----------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -19,6 +19,12 @@
|
||||
#include "clang/AST/DeclObjC.h"
|
||||
#include "clang/AST/DeclOpenMP.h"
|
||||
#include "clang/Basic/ABI.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/type_traits.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -27,7 +33,7 @@ namespace clang {
|
||||
/// a CXXDestructorDecl and the destructor type (Base, Complete) or
|
||||
/// a VarDecl, a FunctionDecl or a BlockDecl.
|
||||
class GlobalDecl {
|
||||
llvm::PointerIntPair<const Decl*, 2> Value;
|
||||
llvm::PointerIntPair<const Decl *, 2> Value;
|
||||
|
||||
void Init(const Decl *D) {
|
||||
assert(!isa<CXXConstructorDecl>(D) && "Use other ctor with ctor decls!");
|
||||
@ -37,19 +43,15 @@ class GlobalDecl {
|
||||
}
|
||||
|
||||
public:
|
||||
GlobalDecl() {}
|
||||
|
||||
GlobalDecl() = default;
|
||||
GlobalDecl(const VarDecl *D) { Init(D);}
|
||||
GlobalDecl(const FunctionDecl *D) { Init(D); }
|
||||
GlobalDecl(const BlockDecl *D) { Init(D); }
|
||||
GlobalDecl(const CapturedDecl *D) { Init(D); }
|
||||
GlobalDecl(const ObjCMethodDecl *D) { Init(D); }
|
||||
GlobalDecl(const OMPDeclareReductionDecl *D) { Init(D); }
|
||||
|
||||
GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type)
|
||||
: Value(D, Type) {}
|
||||
GlobalDecl(const CXXDestructorDecl *D, CXXDtorType Type)
|
||||
: Value(D, Type) {}
|
||||
GlobalDecl(const CXXConstructorDecl *D, CXXCtorType Type) : Value(D, Type) {}
|
||||
GlobalDecl(const CXXDestructorDecl *D, CXXDtorType Type) : Value(D, Type) {}
|
||||
|
||||
GlobalDecl getCanonicalDecl() const {
|
||||
GlobalDecl CanonGD;
|
||||
@ -90,10 +92,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
} // namespace clang
|
||||
|
||||
namespace llvm {
|
||||
template<class> struct DenseMapInfo;
|
||||
|
||||
template<> struct DenseMapInfo<clang::GlobalDecl> {
|
||||
static inline clang::GlobalDecl getEmptyKey() {
|
||||
@ -113,7 +114,6 @@ namespace llvm {
|
||||
clang::GlobalDecl RHS) {
|
||||
return LHS == RHS;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// GlobalDecl isn't *technically* a POD type. However, its copy constructor,
|
||||
@ -122,6 +122,7 @@ namespace llvm {
|
||||
struct isPodLike<clang::GlobalDecl> {
|
||||
static const bool value = true;
|
||||
};
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_CLANG_AST_GLOBALDECL_H
|
||||
|
164
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
Normal file
164
include/clang/AST/LexicallyOrderedRecursiveASTVisitor.h
Normal file
@ -0,0 +1,164 @@
|
||||
//===--- LexicallyOrderedRecursiveASTVisitor.h - ----------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the LexicallyOrderedRecursiveASTVisitor interface, which
|
||||
// recursively traverses the entire AST in a lexical order.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_LEXICALLY_ORDERED_RECURSIVEASTVISITOR_H
|
||||
#define LLVM_CLANG_AST_LEXICALLY_ORDERED_RECURSIVEASTVISITOR_H
|
||||
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "llvm/Support/SaveAndRestore.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
/// A RecursiveASTVisitor subclass that guarantees that AST traversal is
|
||||
/// performed in a lexical order (i.e. the order in which declarations are
|
||||
/// written in the source).
|
||||
///
|
||||
/// RecursiveASTVisitor doesn't guarantee lexical ordering because there are
|
||||
/// some declarations, like Objective-C @implementation declarations
|
||||
/// that might be represented in the AST differently to how they were written
|
||||
/// in the source.
|
||||
/// In particular, Objective-C @implementation declarations may contain
|
||||
/// non-Objective-C declarations, like functions:
|
||||
///
|
||||
/// @implementation MyClass
|
||||
///
|
||||
/// - (void) method { }
|
||||
/// void normalFunction() { }
|
||||
///
|
||||
/// @end
|
||||
///
|
||||
/// Clang's AST stores these declarations outside of the @implementation
|
||||
/// declaration, so the example above would be represented using the following
|
||||
/// AST:
|
||||
/// |-ObjCImplementationDecl ... MyClass
|
||||
/// | `-ObjCMethodDecl ... method
|
||||
/// | ...
|
||||
/// `-FunctionDecl ... normalFunction
|
||||
/// ...
|
||||
///
|
||||
/// This class ensures that these declarations are traversed before the
|
||||
/// corresponding TraverseDecl for the @implementation returns. This ensures
|
||||
/// that the lexical parent relationship between these declarations and the
|
||||
/// @implementation is preserved while traversing the AST. Note that the
|
||||
/// current implementation doesn't mix these declarations with the declarations
|
||||
/// contained in the @implementation, so the traversal of all of the
|
||||
/// declarations in the @implementation still doesn't follow the lexical order.
|
||||
template <typename Derived>
|
||||
class LexicallyOrderedRecursiveASTVisitor
|
||||
: public RecursiveASTVisitor<Derived> {
|
||||
using BaseType = RecursiveASTVisitor<Derived>;
|
||||
|
||||
public:
|
||||
LexicallyOrderedRecursiveASTVisitor(const SourceManager &SM) : SM(SM) {}
|
||||
|
||||
bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
|
||||
// Objective-C @implementation declarations should not trigger early exit
|
||||
// until the additional decls are traversed as their children are not
|
||||
// lexically ordered.
|
||||
bool Result = BaseType::TraverseObjCImplementationDecl(D);
|
||||
return TraverseAdditionalLexicallyNestedDeclarations() ? Result : false;
|
||||
}
|
||||
|
||||
bool TraverseObjCCategoryImplDecl(ObjCCategoryImplDecl *D) {
|
||||
bool Result = BaseType::TraverseObjCCategoryImplDecl(D);
|
||||
return TraverseAdditionalLexicallyNestedDeclarations() ? Result : false;
|
||||
}
|
||||
|
||||
bool TraverseDeclContextHelper(DeclContext *DC) {
|
||||
if (!DC)
|
||||
return true;
|
||||
|
||||
for (auto I = DC->decls_begin(), E = DC->decls_end(); I != E;) {
|
||||
Decl *Child = *I;
|
||||
if (BaseType::canIgnoreChildDeclWhileTraversingDeclContext(Child)) {
|
||||
++I;
|
||||
continue;
|
||||
}
|
||||
if (!isa<ObjCImplementationDecl>(Child) &&
|
||||
!isa<ObjCCategoryImplDecl>(Child)) {
|
||||
if (!BaseType::getDerived().TraverseDecl(Child))
|
||||
return false;
|
||||
++I;
|
||||
continue;
|
||||
}
|
||||
// Gather declarations that follow the Objective-C implementation
|
||||
// declarations but are lexically contained in the implementation.
|
||||
LexicallyNestedDeclarations.clear();
|
||||
for (++I; I != E; ++I) {
|
||||
Decl *Sibling = *I;
|
||||
if (!SM.isBeforeInTranslationUnit(Sibling->getLocStart(),
|
||||
Child->getLocEnd()))
|
||||
break;
|
||||
if (!BaseType::canIgnoreChildDeclWhileTraversingDeclContext(Sibling))
|
||||
LexicallyNestedDeclarations.push_back(Sibling);
|
||||
}
|
||||
if (!BaseType::getDerived().TraverseDecl(Child))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Stmt::child_range getStmtChildren(Stmt *S) { return S->children(); }
|
||||
|
||||
SmallVector<Stmt *, 8> getStmtChildren(CXXOperatorCallExpr *CE) {
|
||||
SmallVector<Stmt *, 8> Children(CE->children());
|
||||
bool Swap;
|
||||
// Switch the operator and the first operand for all infix and postfix
|
||||
// operations.
|
||||
switch (CE->getOperator()) {
|
||||
case OO_Arrow:
|
||||
case OO_Call:
|
||||
case OO_Subscript:
|
||||
Swap = true;
|
||||
break;
|
||||
case OO_PlusPlus:
|
||||
case OO_MinusMinus:
|
||||
// These are postfix unless there is exactly one argument.
|
||||
Swap = Children.size() != 2;
|
||||
break;
|
||||
default:
|
||||
Swap = CE->isInfixBinaryOp();
|
||||
break;
|
||||
}
|
||||
if (Swap && Children.size() > 1)
|
||||
std::swap(Children[0], Children[1]);
|
||||
return Children;
|
||||
}
|
||||
|
||||
private:
|
||||
bool TraverseAdditionalLexicallyNestedDeclarations() {
|
||||
// FIXME: Ideally the gathered declarations and the declarations in the
|
||||
// @implementation should be mixed and sorted to get a true lexical order,
|
||||
// but right now we only care about getting the correct lexical parent, so
|
||||
// we can traverse the gathered nested declarations after the declarations
|
||||
// in the decl context.
|
||||
assert(!BaseType::getDerived().shouldTraversePostOrder() &&
|
||||
"post-order traversal is not supported for lexically ordered "
|
||||
"recursive ast visitor");
|
||||
for (Decl *D : LexicallyNestedDeclarations) {
|
||||
if (!BaseType::getDerived().TraverseDecl(D))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const SourceManager &SM;
|
||||
llvm::SmallVector<Decl *, 8> LexicallyNestedDeclarations;
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_AST_LEXICALLY_ORDERED_RECURSIVEASTVISITOR_H
|
@ -1,4 +1,4 @@
|
||||
//===--- NestedNameSpecifier.h - C++ nested name specifiers -----*- C++ -*-===//
|
||||
//===- NestedNameSpecifier.h - C++ nested name specifiers -------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
@ -11,38 +11,42 @@
|
||||
// a C++ nested-name-specifier.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
|
||||
#define LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/ADT/FoldingSet.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class CXXRecordDecl;
|
||||
class IdentifierInfo;
|
||||
class LangOptions;
|
||||
class NamespaceAliasDecl;
|
||||
class NamespaceDecl;
|
||||
class IdentifierInfo;
|
||||
struct PrintingPolicy;
|
||||
class Type;
|
||||
class TypeLoc;
|
||||
class LangOptions;
|
||||
|
||||
/// \brief Represents a C++ nested name specifier, such as
|
||||
/// "\::std::vector<int>::".
|
||||
///
|
||||
/// C++ nested name specifiers are the prefixes to qualified
|
||||
/// namespaces. For example, "foo::" in "foo::x" is a nested name
|
||||
/// names. For example, "foo::" in "foo::x" is a nested name
|
||||
/// specifier. Nested name specifiers are made up of a sequence of
|
||||
/// specifiers, each of which can be a namespace, type, identifier
|
||||
/// (for dependent names), decltype specifier, or the global specifier ('::').
|
||||
/// The last two specifiers can only appear at the start of a
|
||||
/// nested-namespace-specifier.
|
||||
class NestedNameSpecifier : public llvm::FoldingSetNode {
|
||||
|
||||
/// \brief Enumeration describing
|
||||
enum StoredSpecifierKind {
|
||||
StoredIdentifier = 0,
|
||||
@ -66,7 +70,7 @@ class NestedNameSpecifier : public llvm::FoldingSetNode {
|
||||
/// specifier '::'. Otherwise, the pointer is one of
|
||||
/// IdentifierInfo*, Namespace*, or Type*, depending on the kind of
|
||||
/// specifier as encoded within the prefix.
|
||||
void* Specifier;
|
||||
void* Specifier = nullptr;
|
||||
|
||||
public:
|
||||
/// \brief The kind of specifier that completes this nested name
|
||||
@ -74,17 +78,23 @@ public:
|
||||
enum SpecifierKind {
|
||||
/// \brief An identifier, stored as an IdentifierInfo*.
|
||||
Identifier,
|
||||
|
||||
/// \brief A namespace, stored as a NamespaceDecl*.
|
||||
Namespace,
|
||||
|
||||
/// \brief A namespace alias, stored as a NamespaceAliasDecl*.
|
||||
NamespaceAlias,
|
||||
|
||||
/// \brief A type, stored as a Type*.
|
||||
TypeSpec,
|
||||
|
||||
/// \brief A type that was preceded by the 'template' keyword,
|
||||
/// stored as a Type*.
|
||||
TypeSpecWithTemplate,
|
||||
|
||||
/// \brief The global specifier '::'. There is no stored value.
|
||||
Global,
|
||||
|
||||
/// \brief Microsoft's '__super' specifier, stored as a CXXRecordDecl* of
|
||||
/// the class it appeared in.
|
||||
Super
|
||||
@ -92,17 +102,11 @@ public:
|
||||
|
||||
private:
|
||||
/// \brief Builds the global specifier.
|
||||
NestedNameSpecifier()
|
||||
: Prefix(nullptr, StoredIdentifier), Specifier(nullptr) {}
|
||||
NestedNameSpecifier() : Prefix(nullptr, StoredIdentifier) {}
|
||||
|
||||
/// \brief Copy constructor used internally to clone nested name
|
||||
/// specifiers.
|
||||
NestedNameSpecifier(const NestedNameSpecifier &Other)
|
||||
: llvm::FoldingSetNode(Other), Prefix(Other.Prefix),
|
||||
Specifier(Other.Specifier) {
|
||||
}
|
||||
|
||||
void operator=(const NestedNameSpecifier &) = delete;
|
||||
NestedNameSpecifier(const NestedNameSpecifier &Other) = default;
|
||||
|
||||
/// \brief Either find or insert the given nested name specifier
|
||||
/// mockup in the given context.
|
||||
@ -110,6 +114,8 @@ private:
|
||||
const NestedNameSpecifier &Mockup);
|
||||
|
||||
public:
|
||||
NestedNameSpecifier &operator=(const NestedNameSpecifier &) = delete;
|
||||
|
||||
/// \brief Builds a specifier combining a prefix and an identifier.
|
||||
///
|
||||
/// The prefix must be dependent, since nested name specifiers
|
||||
@ -224,8 +230,8 @@ public:
|
||||
/// \brief A C++ nested-name-specifier augmented with source location
|
||||
/// information.
|
||||
class NestedNameSpecifierLoc {
|
||||
NestedNameSpecifier *Qualifier;
|
||||
void *Data;
|
||||
NestedNameSpecifier *Qualifier = nullptr;
|
||||
void *Data = nullptr;
|
||||
|
||||
/// \brief Determines the data length for the last component in the
|
||||
/// given nested-name-specifier.
|
||||
@ -237,12 +243,12 @@ class NestedNameSpecifierLoc {
|
||||
|
||||
public:
|
||||
/// \brief Construct an empty nested-name-specifier.
|
||||
NestedNameSpecifierLoc() : Qualifier(nullptr), Data(nullptr) { }
|
||||
NestedNameSpecifierLoc() = default;
|
||||
|
||||
/// \brief Construct a nested-name-specifier with source location information
|
||||
/// from
|
||||
NestedNameSpecifierLoc(NestedNameSpecifier *Qualifier, void *Data)
|
||||
: Qualifier(Qualifier), Data(Data) { }
|
||||
: Qualifier(Qualifier), Data(Data) {}
|
||||
|
||||
/// \brief Evalutes true when this nested-name-specifier location is
|
||||
/// non-empty.
|
||||
@ -339,7 +345,7 @@ public:
|
||||
class NestedNameSpecifierLocBuilder {
|
||||
/// \brief The current representation of the nested-name-specifier we're
|
||||
/// building.
|
||||
NestedNameSpecifier *Representation;
|
||||
NestedNameSpecifier *Representation = nullptr;
|
||||
|
||||
/// \brief Buffer used to store source-location information for the
|
||||
/// nested-name-specifier.
|
||||
@ -347,21 +353,18 @@ class NestedNameSpecifierLocBuilder {
|
||||
/// Note that we explicitly manage the buffer (rather than using a
|
||||
/// SmallVector) because \c Declarator expects it to be possible to memcpy()
|
||||
/// a \c CXXScopeSpec, and CXXScopeSpec uses a NestedNameSpecifierLocBuilder.
|
||||
char *Buffer;
|
||||
char *Buffer = nullptr;
|
||||
|
||||
/// \brief The size of the buffer used to store source-location information
|
||||
/// for the nested-name-specifier.
|
||||
unsigned BufferSize;
|
||||
unsigned BufferSize = 0;
|
||||
|
||||
/// \brief The capacity of the buffer used to store source-location
|
||||
/// information for the nested-name-specifier.
|
||||
unsigned BufferCapacity;
|
||||
unsigned BufferCapacity = 0;
|
||||
|
||||
public:
|
||||
NestedNameSpecifierLocBuilder()
|
||||
: Representation(nullptr), Buffer(nullptr), BufferSize(0),
|
||||
BufferCapacity(0) {}
|
||||
|
||||
NestedNameSpecifierLocBuilder() = default;
|
||||
NestedNameSpecifierLocBuilder(const NestedNameSpecifierLocBuilder &Other);
|
||||
|
||||
NestedNameSpecifierLocBuilder &
|
||||
@ -451,6 +454,7 @@ public:
|
||||
/// \param ColonColonLoc The location of the trailing '::'.
|
||||
void MakeSuper(ASTContext &Context, CXXRecordDecl *RD,
|
||||
SourceLocation SuperLoc, SourceLocation ColonColonLoc);
|
||||
|
||||
/// \brief Make a new nested-name-specifier from incomplete source-location
|
||||
/// information.
|
||||
///
|
||||
@ -511,6 +515,6 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
|
||||
return DB;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
||||
#endif // LLVM_CLANG_AST_NESTEDNAMESPECIFIER_H
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -327,12 +327,13 @@ CAST_OPERATION(ZeroToOCLQueue)
|
||||
// Convert a pointer to a different address space.
|
||||
CAST_OPERATION(AddressSpaceConversion)
|
||||
|
||||
// Convert an integer initializer to an OpenCL sampler.
|
||||
// Convert an integer initializer to an OpenCL sampler.
|
||||
CAST_OPERATION(IntToOCLSampler)
|
||||
|
||||
//===- Binary Operations -------------------------------------------------===//
|
||||
// Operators listed in order of precedence.
|
||||
// Note that additions to this should also update the StmtVisitor class.
|
||||
// Note that additions to this should also update the StmtVisitor class and
|
||||
// BinaryOperator::getOverloadedOperator.
|
||||
|
||||
// [C++ 5.5] Pointer-to-member operators.
|
||||
BINARY_OPERATION(PtrMemD, ".*")
|
||||
@ -347,6 +348,8 @@ BINARY_OPERATION(Sub, "-")
|
||||
// [C99 6.5.7] Bitwise shift operators.
|
||||
BINARY_OPERATION(Shl, "<<")
|
||||
BINARY_OPERATION(Shr, ">>")
|
||||
// C++20 [expr.spaceship] Three-way comparison operator.
|
||||
BINARY_OPERATION(Cmp, "<=>")
|
||||
// [C99 6.5.8] Relational operators.
|
||||
BINARY_OPERATION(LT, "<")
|
||||
BINARY_OPERATION(GT, ">")
|
||||
@ -382,7 +385,8 @@ BINARY_OPERATION(Comma, ",")
|
||||
|
||||
|
||||
//===- Unary Operations ---------------------------------------------------===//
|
||||
// Note that additions to this should also update the StmtVisitor class.
|
||||
// Note that additions to this should also update the StmtVisitor class and
|
||||
// UnaryOperator::getOverloadedOperator.
|
||||
|
||||
// [C99 6.5.2.4] Postfix increment and decrement
|
||||
UNARY_OPERATION(PostInc, "++")
|
||||
|
@ -23,8 +23,6 @@ enum CastKind {
|
||||
#include "clang/AST/OperationKinds.def"
|
||||
};
|
||||
|
||||
static const CastKind CK_Invalid = static_cast<CastKind>(-1);
|
||||
|
||||
enum BinaryOperatorKind {
|
||||
#define BINARY_OPERATION(Name, Spelling) BO_##Name,
|
||||
#include "clang/AST/OperationKinds.def"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user