Propagating prior merge from 'llvm.org/release_50'.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2667b1d..f16c8eb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -411,7 +411,8 @@
# 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})
+list(APPEND LLVM_COMMON_DEPENDS clang-tablegen-targets)
add_subdirectory(lib)
add_subdirectory(tools)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..a0c1644
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+By submitting a pull request, you represent that you have the right to license
+your contribution to Apple and the community, and agree by submitting the patch
+that your contributions are licensed under the [Swift
+license](https://swift.org/LICENSE.txt).
+
+---
+
+Changes to this repository follow special considerations as described on
+Swift.org under "[LLVM and Swift](https://swift.org/contributing/#llvm-and-swift)".
+Please make sure your change is appropriate for this repository.
+
+Before submitting a pull request, please make sure you have tested your
+changes and that they follow the Swift project [guidelines for contributing
+code](https://swift.org/contributing/#contributing-code).
diff --git a/cmake/modules/ClangConfig.cmake.in b/cmake/modules/ClangConfig.cmake.in
index 03bca69..a5a7eae 100644
--- a/cmake/modules/ClangConfig.cmake.in
+++ b/cmake/modules/ClangConfig.cmake.in
@@ -11,3 +11,10 @@
# 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()
diff --git a/docs/ClangCommandLineReference.rst b/docs/ClangCommandLineReference.rst
index d964e34..a7b485a 100644
--- a/docs/ClangCommandLineReference.rst
+++ b/docs/ClangCommandLineReference.rst
@@ -656,10 +656,6 @@
Pass <arg> to the clang compiler
-.. option:: -fclang-abi-compat=<version>
-
-Attempt to match the ABI of Clang <version>
-
.. option:: -fcomment-block-commands=<arg>,<arg2>...
Treat each comma separated argument in <arg> as a documentation comment block command
diff --git a/docs/Modules.rst b/docs/Modules.rst
index ed6f817..af42461 100644
--- a/docs/Modules.rst
+++ b/docs/Modules.rst
@@ -317,11 +317,12 @@
.. 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 +382,7 @@
*umbrella-dir-declaration*
*submodule-declaration*
*export-declaration*
+ *export-as-declaration*
*use-declaration*
*link-declaration*
*config-macros-declaration*
@@ -660,6 +662,30 @@
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
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index 6e8b005..4af76a7 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -316,28 +316,10 @@
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``.
-
- 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.
-
-- 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.
-
+* 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.
Python Binding Changes
----------------------
diff --git a/docs/UndefinedBehaviorSanitizer.rst b/docs/UndefinedBehaviorSanitizer.rst
index 85dd549..81e4059 100644
--- a/docs/UndefinedBehaviorSanitizer.rst
+++ b/docs/UndefinedBehaviorSanitizer.rst
@@ -75,6 +75,7 @@
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 @@
- ``-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 @@
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 @@
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
diff --git a/include/clang-c/CXErrorCode.h b/include/clang-c/CXErrorCode.h
index aff73b7..9bee50b 100644
--- a/include/clang-c/CXErrorCode.h
+++ b/include/clang-c/CXErrorCode.h
@@ -54,7 +54,25 @@
/**
* \brief An AST deserialization error has occurred.
*/
- CXError_ASTReadError = 4
+ CXError_ASTReadError = 4,
+
+ /**
+ * \brief A refactoring action is not available at the given location
+ * or in the given source range.
+ */
+ CXError_RefactoringActionUnavailable = 5,
+
+ /**
+ * \brief A refactoring action is not able to use the given name because
+ * it contains an unexpected number of strings.
+ */
+ CXError_RefactoringNameSizeMismatch = 6,
+
+ /**
+ * \brief A name of a symbol is invalid, i.e. it is reserved by the source
+ * language and can't be used as a name for this symbol.
+ */
+ CXError_RefactoringNameInvalid = 7
};
#ifdef __cplusplus
diff --git a/include/clang-c/Refactor.h b/include/clang-c/Refactor.h
new file mode 100644
index 0000000..b11cfb8
--- /dev/null
+++ b/include/clang-c/Refactor.h
@@ -0,0 +1,1306 @@
+/*==-- clang-c/Refactor.h - Refactoring Public C Interface --------*- C -*-===*\
+|* *|
+|* The LLVM Compiler Infrastructure *|
+|* *|
+|* This file is distributed under the University of Illinois Open Source *|
+|* License. See LICENSE.TXT for details. *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This header provides a public inferface to a Clang library for performing *|
+|* refactoring actions on projects without exposing the full Clang C++ API. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_CLANG_C_REFACTOR_H
+#define LLVM_CLANG_C_REFACTOR_H
+
+#include "clang-c/Index.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup CINDEX_REFACTOR Refactoring options.
+ *
+ * @{
+ */
+
+/**
+ * \brief The refactoring options that can be specified for each refactoring
+ * action.
+ */
+enum CXRefactoringOption {
+ /**
+ * \brief The refactoring actions like 'rename' will avoid looking for
+ * occurrences of the renamed symbol in comments if this option is enabled.
+ */
+ CXRefactorOption_AvoidTextualMatches = 1
+};
+
+/**
+ * \brief Opaque pointer representing a set of options that can be given to
+ * a refactoring action.
+ */
+typedef void *CXRefactoringOptionSet;
+
+/**
+ * \brief Returns a new option set.
+ */
+CINDEX_LINKAGE
+CXRefactoringOptionSet clang_RefactoringOptionSet_create(void);
+
+/**
+ * \brief Parses and returns a new option set or NULL if the given string is
+ * invalid.
+ */
+CINDEX_LINKAGE
+CXRefactoringOptionSet
+clang_RefactoringOptionSet_createFromString(const char *String);
+
+/**
+ * \brief Adds a new option to the given refactoring option set.
+ */
+CINDEX_LINKAGE
+void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set,
+ enum CXRefactoringOption Option);
+
+/**
+ * \brief Converts the given refactoring option set to a string value.
+ */
+CINDEX_LINKAGE
+CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set);
+
+/**
+ * \brief Free the given option set.
+ *
+ * Option sets should be freed by this function only when they were created
+ * using the \c clang_RefactoringOptionSet_create* methods.
+ */
+CINDEX_LINKAGE
+void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_REFACTOR Refactoring actions.
+ *
+ * @{
+ */
+
+/**
+ * \brief The refactoring actions that can be performed by libclang.
+ */
+enum CXRefactoringActionType {
+ /**
+ * \brief The 'rename' refactoring action.
+ */
+ CXRefactor_Rename = 0,
+
+ /**
+ * \brief The local 'rename' refactoring action.
+ */
+ CXRefactor_Rename_Local = 1,
+
+ /**
+ * \brief The 'extract' refactoring action extracts source code into a
+ * new function.
+ */
+ CXRefactor_Extract = 2,
+
+ /**
+ * \brief The sub-action of 'extract' that extracts source code into a new
+ * method.
+ */
+ CXRefactor_Extract_Method = 3,
+
+ /**
+ * \brief The action that converts an if/else constructs to a switch block.
+ */
+ CXRefactor_IfSwitchConversion = 4,
+
+ /**
+ * \brief The action that wraps an Objective-C string literal in an
+ * NSLocalizedString macro.
+ */
+ CXRefactor_LocalizeObjCStringLiteral = 5,
+
+ /**
+ * \brief The action that adds missing switch cases to an switch over an enum.
+ */
+ CXRefactor_FillInEnumSwitchCases = 6,
+
+ /**
+ * \brief The action that adds missing protocol methods to an Objective-C
+ * class.
+ */
+ CXRefactor_FillInMissingProtocolStubs = 7,
+
+ /**
+ * \brief The action that extracts an expression that's repeated in a function
+ * into a new variable.
+ */
+ CXRefactor_ExtractRepeatedExpressionIntoVariable = 8,
+
+ /**
+ * \brief The action that adds missing abstract class method overrides to a
+ * class.
+ */
+ CXRefactor_FillInMissingMethodStubsFromAbstractClasses = 9,
+
+ /**
+ * \brief The action that generates dummy method definitions for method
+ * declarations without respective definitions.
+ */
+ CXRefactor_ImplementDeclaredMethods = 10,
+
+ /**
+ * \brief The sub-action of 'extract' that extracts source expression into a
+ * new variable.
+ */
+ CXRefactor_Extract_Expression = 11,
+};
+
+/**
+ * \brief Return the name of the given refactoring action.
+ */
+CINDEX_LINKAGE
+CXString
+clang_RefactoringActionType_getName(enum CXRefactoringActionType Action);
+
+/**
+ * \brief A set of refactoring actions that can be performed at some specific
+ * location in a source file.
+ *
+ * The actions in the action set are ordered by their priority: most important
+ * actions are placed before the less important ones.
+ */
+typedef struct {
+ const enum CXRefactoringActionType *Actions;
+ unsigned NumActions;
+} CXRefactoringActionSet;
+
+/**
+ * \brief Free the given refactoring action set.
+ */
+CINDEX_LINKAGE void
+clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set);
+
+typedef struct {
+ enum CXRefactoringActionType Action;
+ /**
+ * \brief The set of diagnostics that describes the reason why this action
+ * couldn't be initiated. This set of diagnostics is managed by the
+ * \c CXRefactoringActionSetWithDiagnostics and shouldn't be freed manually.
+ */
+ CXDiagnosticSet Diagnostics;
+} CXRefactoringActionWithDiagnostics;
+
+/**
+ * \brief A set of refactoring actions that couldn't be initiated at some
+ * location and their respective diagnostics that describe the reason why
+ * the initiation failed.
+ */
+typedef struct {
+ CXRefactoringActionWithDiagnostics *Actions;
+ unsigned NumActions;
+} CXRefactoringActionSetWithDiagnostics;
+
+/**
+ * \brief Free the given refactoring action set with diagnostics.
+ */
+CINDEX_LINKAGE void clang_RefactoringActionSetWithDiagnostics_dispose(
+ CXRefactoringActionSetWithDiagnostics *Set);
+
+/**
+ * \brief Find the set of refactoring actions that can be performed at the given
+ * location.
+ *
+ * This function examines the AST around the given source range and creates a
+ * \c CXRefactoringActionSet that contains all of the actions that can be
+ * performed in the given source range.
+ *
+ * \param TU The translation unit which contains the given source range.
+ *
+ * \param Location The location at which the refactoring action will be
+ * performed.
+ *
+ * \param SelectionRange The range in which the AST should be checked. Usually
+ * corresponds to the selection range or location of the cursor in the editor.
+ * Can be a null range.
+ *
+ * \param Options The optional refactoring options that might influence the way
+ * the search is performed.
+ *
+ * \param[out] OutSet A non-NULL pointer to store the created
+ * \c CXRefactoringActionSet.
+ *
+ * \returns Zero on success, CXError_RefactoringActionUnavailable when
+ * there are no actions available in the given range, or an error code
+ * otherwise.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode
+clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange,
+ CXRefactoringOptionSet Options,
+ CXRefactoringActionSet *OutSet);
+
+/**
+ * \brief Find the set of refactoring actions that can be performed at the given
+ * location.
+ *
+ * This function examines the AST around the given source range and creates a
+ * \c CXRefactoringActionSet that contains all of the actions that can be
+ * performed in the given source range. It also creates a
+ * \c CXRefactoringActionSetWithDiagnostics that might describe the reason why
+ * some refactoring actions are not be available.
+ *
+ * \param TU The translation unit which contains the given source range.
+ *
+ * \param Location The location at which the refactoring action will be
+ * performed.
+ *
+ * \param SelectionRange The range in which the AST should be checked. Usually
+ * corresponds to the selection range or location of the cursor in the editor.
+ * Can be a null range.
+ *
+ * \param Options The optional refactoring options that might influence the way
+ * the search is performed.
+ *
+ * \param[out] OutSet A non-NULL pointer to store the created
+ * \c CXRefactoringActionSet.
+ *
+ * \param[out] OutFailureSet An optional pointer to store the created
+ * \c CXRefactoringActionSetWithDiagnostics that describes the failures reasons
+ * for some of the refactoring actions.
+ *
+ * \returns Zero on success, CXError_RefactoringActionUnavailable when
+ * there are no actions available in the given range, or an error code
+ * otherwise.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, CXRefactoringOptionSet Options,
+ CXRefactoringActionSet *OutSet,
+ CXRefactoringActionSetWithDiagnostics *OutFailureSet);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_REFACTOR_INITIATE Refactoring initiation
+ *
+ * @{
+ */
+
+/**
+ * \brief Opaque pointer representing the initiated refactoring action.
+ */
+typedef void *CXRefactoringAction;
+
+/**
+ * \brief Free the given refactoring action.
+ *
+ * The refactoring action should be freed before the initiation and/or
+ * implementation translation units.
+ */
+CINDEX_LINKAGE void clang_RefactoringAction_dispose(CXRefactoringAction Action);
+
+/**
+ * \brief Return the source range that's associated with the initiated
+ * refactoring action.
+ *
+ * The returned source range covers the source that will be modified by the
+ * given refactoring action. If the action has no associated source range,
+ * then this function will return a null \c CXSourceRange.
+ */
+CINDEX_LINKAGE CXSourceRange
+clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action);
+
+/**
+ * \brief Return the type of the initiated action, which might be different
+ * to the type of the requested action. For an operation 'rename', the action
+ * could actually initiate the local 'rename' operation.
+ */
+CINDEX_LINKAGE
+enum CXRefactoringActionType
+clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action);
+
+/**
+ * \brief Return a non-zero value when the refactoring action requires access
+ * to an additional translation unit that contains an implementation of some
+ * declaration.
+ */
+// TODO: Remove (this is no longer needed due to refactoring continuations).
+CINDEX_LINKAGE
+int clang_RefactoringAction_requiresImplementationTU(
+ CXRefactoringAction Action);
+
+/**
+ * \brief Return a USR that corresponds to the declaration whose implementation
+ * is required in order for the given refactoring action to work correctly.
+ */
+// TODO: Remove (this is no longer needed due to refactoring continuations).
+CINDEX_LINKAGE
+CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU(
+ CXRefactoringAction Action);
+
+/**
+ * \brief Set the translation unit that contains the declaration whose
+ * implementation is required for the given refactoring action to work
+ * correctly.
+ */
+// TODO: Remove (this is no longer needed due to refactoring continuations).
+CINDEX_LINKAGE
+enum CXErrorCode
+clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action,
+ CXTranslationUnit TU);
+
+/**
+ * \brief A refactoring candidate determines on which piece of source code the
+ * action should be applied.
+ *
+ * Most refactoring actions have just one candidate, but some actions, like
+ * 'Extract' can produce multiple candidates.
+ *
+ * The candidates are managed by the refactoring action, and their description
+ * string doesn't need to be freed manually.
+ */
+typedef struct { CXString Description; } CXRefactoringCandidate;
+
+/**
+ * \brief A set of refactoring candidates on which the previously initiatied
+ * refactoring action can be performed.
+ *
+ * The candidates in the candidate set are ordered by their priority: the
+ * ones that are more likely to be selected are placed before the other ones.
+ *
+ * A non-empty refactoring candidate set always has more than one refactoring
+ * candidate, because when a refactoring action has just one candidate,
+ * \c clang_RefactoringAction_getRefactoringCandidates will return an empty
+ * candidate set.
+ */
+typedef struct {
+ const CXRefactoringCandidate *Candidates;
+ unsigned NumCandidates;
+} CXRefactoringCandidateSet;
+
+/**
+ * \brief Returns the given action's refactoring candidates.
+ *
+ * The resulting refactoring candidate set will be empty when the given \c
+ * CXRefactoringAction has just one refactoring candidate.
+ *
+ * \param Action A previously initiated \c CXRefactoringAction.
+ *
+ * \param[out] OutRefactoringCandidateSet An pointer to store the action's
+ * refactoring candidate set.
+ *
+ * \returns Zero on success, or an error code otherwise.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates(
+ CXRefactoringAction Action,
+ CXRefactoringCandidateSet *OutRefactoringCandidateSet);
+
+/**
+ * \brief Tells the given refactoring action that it has to perform the
+ * operation on the refactoring candidate that's located at \p Index in the \c
+ * CXRefactoringCandidateSet.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode
+clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action,
+ unsigned Index);
+
+// TODO: Remove.
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_initiateActionAt(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType,
+ CXRefactoringOptionSet Options, CXRefactoringAction *OutAction,
+ CXString *OutFailureReason);
+
+/**
+ * \brief Initiate a specific refactoring action at the given location.
+ *
+ * This function initiates an \p ActionType refactoring action when it can
+ * be initiated at the given location and creates a \c CXRefactoringAction
+ * action that will allow the control.
+ *
+ * \param TU The translation unit in which the action should be initiated.
+ *
+ * \param Location The location at which the refactoring action will be
+ * performed.
+ *
+ * \param SelectionRange The range in which the AST should be checked. Usually
+ * corresponds to the selection range or location of the cursor in the editor.
+ * Can be a null range.
+ *
+ * \param ActionType The type of action that should be initiated.
+ *
+ * \param Options The optional refactoring options that might have an influence
+ * on the initiation process.
+ *
+ * \param[out] OutAction A non-NULL pointer to store the created
+ * \c CXRefactoringAction.
+ *
+ * \param[out] OutDiagnostics An optional pointer to store any diagnostics that
+ * describe why the action wasn't initiated.
+ *
+ * \returns Zero on success, CXError_RefactoringActionUnavailable when
+ * the given refactoring action can't be performed at the given location, or an
+ * error code otherwise.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_initiateAction(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType,
+ CXRefactoringOptionSet Options, CXRefactoringAction *OutAction,
+ CXDiagnosticSet *OutDiagnostics);
+
+/**
+ * \brief Initiate a specific refactoring action on a particular declaration.
+ *
+ * This function searches for the declaration that corresponds to \p DeclUSR
+ * and initiates an \p ActionType a refactoring action on that declaration
+ * if possible.
+ *
+ * \param TU The translation unit in which the declaration is defined.
+ *
+ * \param DeclUSR The USR that corresponds to the declaration of interest.
+ *
+ * \param ActionType The type of action that should be initiated.
+ *
+ * \param Options The optional refactoring options that might have an influence
+ * on the initiation process.
+ *
+ * \param[out] OutAction A non-NULL pointer to store the created
+ * \c CXRefactoringAction.
+ *
+ * \returns Zero on success, CXError_RefactoringActionUnavailable when
+ * the given refactoring action can't be performed on the found declaration, or
+ * an error code otherwise.
+ */
+// TODO: Remove (not needed).
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_initiateActionOnDecl(
+ CXTranslationUnit TU, const char *DeclUSR,
+ enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options,
+ CXRefactoringAction *OutAction, CXString *OutFailureReason);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_REFACTOR_REPLACEMENT Refactoring replacement
+ *
+ * @{
+ */
+
+/**
+ * \brief A source location in a single file that is independent of \c
+ * CXTranslationUnit.
+ */
+typedef struct { unsigned Line, Column; } CXFileLocation;
+
+/**
+ * \brief A source range in a single file that is independent of \c
+ * CXTranslationUnit.
+ */
+typedef struct { CXFileLocation Begin, End; } CXFileRange;
+
+// TODO: Remove
+typedef struct {
+ CXFileRange Range;
+ CXString ReplacementString;
+} CXRefactoringReplacement_Old;
+
+// TODO: Remove
+typedef struct {
+ CXString Filename;
+ const CXRefactoringReplacement_Old *Replacements;
+ unsigned NumReplacements;
+} CXRefactoringFileReplacementSet_Old;
+
+// TODO: Remove
+typedef struct {
+ const CXRefactoringFileReplacementSet_Old *FileReplacementSets;
+ unsigned NumFileReplacementSets;
+} CXRefactoringReplacements_Old;
+
+/**
+ * \brief Identifies a character range in the source code of a single file that
+ * should be replaced with the replacement string.
+ *
+ * Replacements are managed by the result of a specific refactoring action,
+ * like \c CXRenamingResult, and are invalidated when the refactoring result is
+ * destroyed.
+ */
+typedef struct {
+ CXFileRange Range;
+ CXString ReplacementString;
+ void *AssociatedData;
+} CXRefactoringReplacement;
+
+/**
+* \brief A set of refactoring replacements that are applicable to a certain
+ * file.
+ */
+typedef struct {
+ CXString Filename;
+ const CXRefactoringReplacement *Replacements;
+ unsigned NumReplacements;
+} CXRefactoringFileReplacementSet;
+
+/**
+ * \brief A set of refactoring replacements that have been produced by a
+ * refactoring operation.
+ *
+ * The refactoring replacements depend on \c CXRefactoringResult, and can't be
+ * used after the refactoring result is freed.
+ */
+typedef struct {
+ const CXRefactoringFileReplacementSet *FileReplacementSets;
+ unsigned NumFileReplacementSets;
+} CXRefactoringReplacements;
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_SYMBOL_OPERATION Symbol-based refactoring operation
+ * (e.g. Rename).
+ *
+ * @{
+ */
+
+/**
+ * \brief The type of a symbol occurrence.
+ *
+ * The occurrence kind determines if an occurrence can be renamed automatically
+ * or if the user has to make the decision whether or not this occurrence
+ * should be renamed.
+ */
+enum CXSymbolOccurrenceKind {
+ /**
+ * \brief This occurrence is an exact match and can be renamed automatically.
+ */
+ CXSymbolOccurrence_MatchingSymbol = 0,
+
+ /**
+ * \brief This is an occurrence of a matching selector. It can't be renamed
+ * automatically unless the indexer proves that this selector refers only
+ * to the declarations that correspond to the renamed symbol.
+ */
+ CXSymbolOccurrence_MatchingSelector = 1,
+
+ /**
+ * \brief This is an occurrence of an implicit property that uses the
+ * renamed method.
+ */
+ CXSymbolOccurrence_MatchingImplicitProperty = 2,
+
+ /**
+ * \brief This is an occurrence of an symbol name in a comment.
+ */
+ CXSymbolOccurrence_MatchingCommentString = 3,
+
+ /**
+ * \brief This is an occurrence of an symbol name in a documentation comment.
+ */
+ CXSymbolOccurrence_MatchingDocCommentString = 4,
+
+ /**
+ * \brief This is an occurrence of an symbol name in a filename in an inclusion
+ * directive.
+ */
+ CXSymbolOccurrence_MatchingFilename = 5,
+
+ /**
+ * \brief This is an occurrence of a symbol name that belongs to the extracted
+ * declaration. Note: this occurrence can be in two replacements as we might
+ * extract an out-of-line method that will be both declared and defined.
+ */
+ CXSymbolOccurrence_ExtractedDeclaration = 100,
+
+ /**
+ * \brief This is an occurrence of a symbol name that references the extracted
+ * declaration.
+ */
+ CXSymbolOccurrence_ExtractedDeclaration_Reference = 101,
+};
+
+// TODO: Remove
+typedef struct {
+ const CXRefactoringReplacement_Old *Replacements;
+ unsigned ReplacementCount;
+ enum CXSymbolOccurrenceKind Kind;
+ /**
+ * Whether or not this occurrence is inside a macro. When this is true, the
+ * replacements of the occurrence contain just a single empty replacement that
+ * points to the location of the macro expansion.
+ */
+ int IsMacroExpansion;
+} CXRenamedSymbolOccurrence;
+
+/**
+ * \brief An occurrence of a symbol.
+ *
+ * Contains the source ranges that represent the pieces of the name of the
+ * symbol. The occurrences are managed by \c CXRenamingResult, and are
+ * invalidated when \c CXRenamingResult is destroyed.
+ */
+typedef struct {
+ const CXFileRange *NamePieces;
+ unsigned NumNamePieces;
+ enum CXSymbolOccurrenceKind Kind;
+ /**
+ * Whether or not this occurrence is inside a macro. When this is true, the
+ * replacements of the occurrence contain just a single empty replacement that
+ * points to the location of the macro expansion.
+ */
+ int IsMacroExpansion;
+ unsigned SymbolIndex;
+} CXSymbolOccurrence;
+
+// TODO: Remove
+typedef struct {
+ CXString Filename;
+ const CXRenamedSymbolOccurrence *Occurrences;
+ unsigned NumOccurrences;
+} CXFileRenamingResult; // TODO: Remove
+
+/**
+* \brief A set of symbol occurrences that occur in a single file.
+ */
+typedef struct {
+ CXString Filename;
+ /**
+ * The set of occurrences for each symbol of interest.
+ */
+ const CXSymbolOccurrence *Occurrences;
+ unsigned NumOccurrences;
+} CXSymbolOccurrencesInFile;
+
+/**
+ * \brief Opaque pointer representing all of the renames that should take place
+ * in a single translation unit.
+ *
+ * The result of a renaming action is indepedent from \c CXRenamingAction, and
+ * remains valid after \c CXRenamingAction is destroyed.
+ */
+typedef void *CXRenamingResult;
+
+/**
+ * \brief Opaque pointer representing all of the symbol occurrences from a
+ * single TU/file.
+ *
+ * The result of a symbol search occurrence search operation is indepedent from
+ * \c CXRefactoringAction, and remains valid after \c CXRefactoringAction is
+ * destroyed.
+ */
+typedef void *CXSymbolOccurrencesResult;
+
+/**
+ * \brief Find the cursor that's being renamed at the given location.
+ *
+ * \param TU The translation unit in which the cursor is present.
+ *
+ * \param Location The location at which the refactoring action will be
+ * performed.
+ *
+ * \param SelectionRange The range in which the AST should be checked. Usually
+ * corresponds to the selection range or location of the cursor in the editor.
+ * Can be a null range.
+ *
+ * \returns Zero on success, CXError_RefactoringActionUnavailable when
+ * there's no suitable cursor at the given location, or an error code otherwise.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_findRenamedCursor(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, CXCursor *OutCursor);
+
+/**
+ * \brief Initiates a renaming operation on a previously initiated refactoring
+ * action.
+ *
+ * The initiation process finds the symbols that have to be renamed for a
+ * previously initiated \c CXRefactor_Rename refactoring action.
+ *
+ * \returns Zero on success, or an error code otherwise.
+ */
+// TODO: Remove
+CINDEX_LINKAGE
+enum CXErrorCode
+clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action);
+
+/**
+ * \brief Set the new name of the renamed symbol in the given \c
+ * RenamingAction.
+ *
+ * \returns Zero on success, CXError_RefactoringNameInvalid when the new name
+ * isn't a valid identifier, CXError_RefactoringNameSizeMismatch when the new
+ * name has an incorrect number of pieces or a different error code otherwise.
+ */
+// TODO: Remove
+CINDEX_LINKAGE
+enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action,
+ const char *NewName);
+
+/**
+ * \brief Return the number of symbols that are renamed by the given renaming
+ * action.
+ *
+ * A renaming action typically works on just one symbol. However, there are
+ * certain language constructs that require work with more than one symbol in
+ * order for them to be renamed correctly. Property declarations in Objective-C
+ * are the perfect example: in addition to the actual property, the action has
+ * to rename the corresponding getters and setters, as well as the backing ivar.
+ */
+// TODO: Remove
+CINDEX_LINKAGE
+unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action);
+
+/**
+ * \brief Return the USR of the declaration that was found for the symbol at the
+ * given \p Index in the given renaming action.
+ */
+// TODO: Remove
+CINDEX_LINKAGE
+CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action,
+ unsigned Index);
+
+// TODO: Remove
+CINDEX_LINKAGE
+CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs(
+ CXRefactoringAction Action, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles);
+
+/**
+ * \brief Find all of the occurrences of the symbol that is being searched for
+ * by the given refactoring action in the translation unit that was used to
+ * initiate the refactoring action.
+ *
+ * This function searches for all of the \c CXSymbolOccurrence in the
+ * translation units that are referenced by the given \c CXRefactoringAction by
+ * iterating through the AST of the each translation unit. The occurrences that
+ * are found don't have to be from the main file in the translation unit, they
+ * can be from files included in that translation unit.
+ *
+ * \param Action The \c CXRefactoringAction operation that was inititated by
+ * \c clang_Refactoring_initiateActionAt().
+ *
+ * \param CommandLineArgs The command-line arguments that would be
+ * passed to the \c clang executable if it were being invoked out-of-process.
+ *
+ * \param NumCommandLineArgs The number of command-line arguments in
+ * \c CommandLineArgs.
+ *
+ * \param UnsavedFiles the files that have not yet been saved to disk
+ * but may be required for parsing, including the contents of
+ * those files. The contents and name of these files (as specified by
+ * CXUnsavedFile) are copied when necessary, so the client only needs to
+ * guarantee their validity until the call to this function returns.
+ *
+ * \param NumUnsavedFiles the number of unsaved file entries in \p
+ * UnsavedFiles.
+ *
+ * \returns If successful, a new \c CXSymbolOccurrencesResult structure
+ * containing the occurrences of the symbol in the initiation translation unit,
+ * which should eventually be freed with \c clang_SymbolOccurrences_dispose().
+ * If symbol search fails, returns NULL.
+ */
+CINDEX_LINKAGE
+CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU(
+ CXRefactoringAction Action, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles);
+
+// TODO: Remove
+typedef struct {
+ CXFileLocation Location;
+ /**
+ * The kind of the declaration/expression that was indexed at this location.
+ * This is particularly important for Objective-C selectors. The refactoring
+ * engine requires the following cursor kinds for the following indexed
+ * occurrences:
+ * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl
+ * - ObjC method message send: CXCursor_ObjCMessageExpr
+ * Other occurrences can use any other cursor cursor kinds.
+ */
+ enum CXCursorKind CursorKind;
+} CXRenamedIndexedSymbolLocation;
+
+// TODO: Remove
+typedef struct {
+ /**
+ * An array of occurrences that represent indexed occurrences of a symbol.
+ * It's valid to pass-in no indexed locations, the refactoring engine will
+ * just perform textual search in that case.
+ */
+ const CXRenamedIndexedSymbolLocation *IndexedLocations;
+ unsigned IndexedLocationCount;
+ /**
+ * The kind of the declaration that is being renamed.
+ * This is particularly important for Objective-C selectors. The refactoring
+ * engine requires the following cursor kinds for the following renamed
+ * declaration:
+ * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl
+ * Other declarations can use any other cursor cursor kinds.
+ */
+ enum CXCursorKind CursorKind;
+ const char *Name;
+ const char *NewName;
+} CXRenamedIndexedSymbol;
+
+// TODO: Remove
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile(
+ const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx,
+ const char *Filename, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles, CXRefactoringOptionSet Options,
+ CXRenamingResult *OutResult);
+
+/**
+ * \brief A location of an already known occurrence of a symbol.
+ *
+ * Used for rename-indexed operation where the renaming is performed on an
+ * already indexed source file.
+ */
+typedef struct {
+ CXFileLocation Location;
+ /**
+ * The kind of the declaration/expression that was indexed at this location.
+ * This is particularly important for Objective-C selectors. The refactoring
+ * engine requires the following cursor kinds for the following indexed
+ * occurrences:
+ * - ObjC method declaration: CXCursor_ObjC(Instance/Class)MethodDecl
+ * - ObjC method message send: CXCursor_ObjCMessageExpr
+ * - filename in an #include: CXCursor_InclusionDirective
+ * Other occurrences can use any other cursor cursor kinds.
+ */
+ enum CXCursorKind CursorKind;
+} CXIndexedSymbolLocation;
+
+/**
+ * \brief A symbol that should be found the an indexer symbol search operation.
+ *
+ * Used for rename-indexed operation where the renaming is performed on an
+ * already indexed source file.
+ */
+typedef struct {
+ /**
+ * An array of occurrences that represent indexed occurrences of a symbol.
+ * It's valid to pass-in no indexed locations, the refactoring engine will
+ * just perform textual search in that case.
+ */
+ const CXIndexedSymbolLocation *IndexedLocations;
+ unsigned IndexedLocationCount;
+ /**
+ * The kind of the declaration that is being renamed.
+ * This is particularly important for Objective-C selectors. The refactoring
+ * engine requires the following cursor kinds for the following renamed
+ * declaration:
+ * - ObjC methods: CXCursor_ObjC(Instance/Class)MethodDecl
+ * Other declarations can use any other cursor cursor kinds.
+ */
+ enum CXCursorKind CursorKind;
+ /**
+ * The name of the symbol. Objective-C selector names should be specified
+ * using the ':' separator for selector pieces.
+ */
+ const char *Name;
+} CXIndexedSymbol;
+
+/**
+ * \brief Find all of the occurrences of a symbol in an indexed file.
+ *
+ * This function searches for all of the \c CXIndexedSymbol in the
+ * given file by inspecting the source code at the given indexed locations.
+ *
+ * The indexed operations are thread-safe and can be performed concurrently.
+ *
+ * \param Symbols The information about the symbols that includes the locations
+ * for a symbol in the file as determined by the indexer.
+ *
+ * \param NumSymbols The number of symbols in \p Symbols.
+ *
+ * \param CIdx The index object with which the translation unit will be
+ * associated.
+ *
+ * \param Filename The name of the source file that contains the given
+ * \p Locations.
+ *
+ * \param CommandLineArgs The command-line arguments that would be
+ * passed to the \c clang executable if it were being invoked out-of-process.
+ * These command-line options will be parsed and will affect how the translation
+ * unit is parsed.
+ *
+ * \param NumCommandLineArgs The number of command-line arguments in
+ * \c CommandLineArgs.
+ *
+ * \param UnsavedFiles the files that have not yet been saved to disk
+ * but may be required for parsing, including the contents of
+ * those files. The contents and name of these files (as specified by
+ * CXUnsavedFile) are copied when necessary, so the client only needs to
+ * guarantee their validity until the call to this function returns.
+ *
+ * \param NumUnsavedFiles the number of unsaved file entries in \p
+ * UnsavedFiles.
+ *
+ * \param Options The optional refactoring options that might have an influence
+ * on the initiation process.
+ *
+ * \param[out] OutResult A non-NULL pointer to store the created
+ * \c CXSymbolOccurrencesResult.
+ *
+ * \returns Zero on success, or a different error code otherwise.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile(
+ const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx,
+ const char *Filename, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles, CXRefactoringOptionSet Options,
+ CXSymbolOccurrencesResult *OutResult);
+
+// TODO: Remove
+CINDEX_LINKAGE
+unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result);
+
+// TODO: Remove
+CINDEX_LINKAGE
+void clang_RenamingResult_getResultForFile(CXRenamingResult Result,
+ unsigned FileIndex,
+ CXFileRenamingResult *OutResult);
+
+// TODO: Remove
+CINDEX_LINKAGE
+void clang_RenamingResult_dispose(CXRenamingResult Result);
+
+/**
+ * \brief Return the number of files that have occurrences of the specific
+ * symbol.
+ */
+CINDEX_LINKAGE
+unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result);
+
+/**
+ * \brief Return the set of symbol occurrences in a single file.
+ *
+ * The resulting \c CXSymbolOccurrencesInFile is managed by the
+ * \c CXSymbolOccurrencesResult and doesn't have to be disposed of manually.
+ */
+CINDEX_LINKAGE
+void clang_SymbolOccurrences_getOccurrencesForFile(
+ CXSymbolOccurrencesResult Result, unsigned FileIndex,
+ CXSymbolOccurrencesInFile *OutResult);
+
+// TODO: Support refactoring continuations for \c CXSymbolOccurrencesResult,
+// e.g. for function parameter name rename.
+
+/**
+ * \brief Free the given symbol occurrences result.
+ */
+CINDEX_LINKAGE
+void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_REFACTOR_PERFORM Performing refactoring operations.
+ *
+ * @{
+ */
+
+/**
+ * \brief Opaque pointer representing the results of the refactoring operation.
+ *
+ * The result of a refactoring action depends on the \c CXRefactoringAction, and
+ * is invalidated after \c CXRefactoringAction is destroyed.
+ */
+typedef void *CXRefactoringResult;
+
+/**
+ * \brief Opaque pointer representing a refactoring continuation.
+ *
+ * Refactoring continuations allow refactoring operations to run in external
+ * AST units with some results that were obtained after querying the indexer.
+ *
+ * The refactoring continuation is not dependent on the \c CXRefactoringAction
+ * or \c CXRefactoringResult. It does depend on the initiation
+ * \c CXTranslationUnit initially, but that dependency can be terminated.
+ */
+typedef void *CXRefactoringContinuation;
+
+/**
+ * \brief Opaque pointer representing a query to the indexer.
+ */
+typedef void *CXIndexerQuery;
+
+/**
+ * \brief Performs the previously initiated refactoring operation.
+ *
+ * This function executes the refactoring operation which produces a set of
+ * candidate source replacements that can be applied to the source files.
+ *
+ * \param Action The refactoring action.
+ *
+ * \param CommandLineArgs The command-line arguments that would be
+ * passed to the \c clang executable if it were being invoked out-of-process.
+ * These command-line options will be parsed and will affect how the translation
+ * unit is parsed.
+ *
+ * \param NumCommandLineArgs The number of command-line arguments in
+ * \c CommandLineArgs.
+ *
+ * \param UnsavedFiles the files that have not yet been saved to disk
+ * but may be required for parsing, including the contents of
+ * those files. The contents and name of these files (as specified by
+ * CXUnsavedFile) are copied when necessary, so the client only needs to
+ * guarantee their validity until the call to this function returns.
+ *
+ * \param NumUnsavedFiles the number of unsaved file entries in \p
+ * UnsavedFiles.
+ *
+ * \param Options The optional refactoring options that might have an influence
+ * on the way the particular action will be performed.
+ *
+ * \param[out] OutFailureReason An optional pointer to store a message that
+ * describes why the action wasn't performed.
+ *
+ * \returns If successful, a new \c CXRefactoringResult structure containing the
+ * source replacement candidates, which should eventually be freed with
+ * \c clang_RefactoringResult_dispose(). If the refactoring operation fails,
+ * returns NULL.
+ */
+CINDEX_LINKAGE
+CXRefactoringResult clang_Refactoring_performOperation(
+ CXRefactoringAction Action, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles, CXRefactoringOptionSet Options,
+ CXString *OutFailureReason);
+
+// TODO: Remove. This is the deprecated API.
+CINDEX_LINKAGE
+void clang_RefactoringResult_getReplacements(
+ CXRefactoringResult Result, CXRefactoringReplacements_Old *OutReplacements);
+
+/**
+ * \brief Return the set of refactoring source replacements.
+ *
+ * The resulting \c CXRefactoringReplacements are managed by the
+ * \c CXRefactoringResult and don't have to be disposed of manually.
+ */
+CINDEX_LINKAGE
+CXRefactoringReplacements
+clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result);
+
+/**
+ * \brief Represents a set of symbol occurrences that are associated with a
+ * single refactoring replacement.
+ *
+ * The symbol occurrences depend on \c CXRefactoringResult, and can't be
+ * used after the refactoring result is freed.
+ */
+typedef struct {
+ const CXSymbolOccurrence *AssociatedSymbolOccurrences;
+ unsigned NumAssociatedSymbolOccurrences;
+} CXRefactoringReplacementAssociatedSymbolOccurrences;
+
+/**
+ * \brief Return the set of symbol occurrences that are associated with the
+ * given \p Replacement.
+ */
+CXRefactoringReplacementAssociatedSymbolOccurrences
+clang_RefactoringReplacement_getAssociatedSymbolOccurrences(
+ CXRefactoringReplacement Replacement);
+
+/**
+ * \brief Returns the refactoring continuation associated with this result, or
+ * NULL if this result has no refactoring continuation.
+ */
+CINDEX_LINKAGE
+CXRefactoringContinuation
+clang_RefactoringResult_getContinuation(CXRefactoringResult Result);
+
+/**
+ * \brief Free the given refactoring result.
+ */
+CINDEX_LINKAGE
+void clang_RefactoringResult_dispose(CXRefactoringResult Result);
+
+/**
+ * \brief Load the indexer query results from a YAML string.
+ *
+ * Mainly used for testing.
+ */
+CINDEX_LINKAGE
+enum CXErrorCode
+clang_RefactoringContinuation_loadSerializedIndexerQueryResults(
+ CXRefactoringContinuation Continuation, const char *Source);
+
+/**
+ * \brief Return the number of indexer queries that a refactoring continuation
+ * has.
+ */
+CINDEX_LINKAGE
+unsigned clang_RefactoringContinuation_getNumIndexerQueries(
+ CXRefactoringContinuation Continuation);
+
+/**
+ * \brief Return the indexer query at index \p Index.
+ */
+CINDEX_LINKAGE
+CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery(
+ CXRefactoringContinuation Continuation, unsigned Index);
+
+/**
+ * \brief Verify that the all of the indexer queries are satisfied by the
+ * continuation.
+ *
+ * \returns Null if all of the queries are satisfied an no errors have been
+ * reported, or a set of diagnostics that describes why the continuation should
+ * not be run.
+ */
+CINDEX_LINKAGE
+CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing(
+ CXRefactoringContinuation Continuation);
+
+/**
+ * \brief Terminate the connection between the initiation TU and the refactoring
+ * continuation.
+ *
+ * The continuation converts all the TU-specific state to TU-independent state.
+ * The indexer queries that are associate with this continuation are also
+ * invalidated.
+ */
+CINDEX_LINKAGE
+void clang_RefactoringContinuation_finalizeEvaluationInInitationTU(
+ CXRefactoringContinuation Continuation);
+
+/**
+ * \brief Continue performing the previously initiated and performed refactoring
+ * operation in the given translation unit \p TU.
+ */
+CINDEX_LINKAGE
+CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU(
+ CXRefactoringContinuation Continuation, CXTranslationUnit TU,
+ CXString *OutFailureReason);
+
+/**
+ * \brief Free the given refactoring continuation.
+ */
+CINDEX_LINKAGE
+void clang_RefactoringContinuation_dispose(
+ CXRefactoringContinuation Continuation);
+
+/**
+ * @}
+ */
+
+/**
+ * \defgroup CINDEX_REFACTOR_INDEXER_QUERY Indexer Queries.
+ *
+ * @{
+ */
+
+/**
+ * \brief The types of indexer queries.
+ */
+enum CXIndexerQueryKind {
+ CXIndexerQuery_Unknown = 0,
+
+ /**
+ * \brief The indexer should find the file that contains/should contain the
+ * implementation of some declaration.
+ * A file result is expected.
+ */
+ CXIndexerQuery_Decl_FileThatShouldImplement = 1,
+
+ /**
+ * \brief The indexer should determine if the some declaration is defined.
+ * An integer result is expected.
+ */
+ CXIndexerQuery_Decl_IsDefined = 2,
+};
+
+/**
+ * \brief Return the kind of the indexer query \p Query.
+ */
+CINDEX_LINKAGE
+enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query);
+
+/**
+ * \brief Return the number of cursors that the \p Query has.
+ */
+CINDEX_LINKAGE
+unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query);
+
+/**
+ * \brief Return the cursor at the given \p CursorIndex.
+ */
+CINDEX_LINKAGE
+CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query,
+ unsigned CursorIndex);
+
+/**
+ * \brief The action that the indexer should take after evaluating the query.
+ */
+enum CXIndexerQueryAction {
+ /**
+ * \brief This result requires no further action.
+ */
+ CXIndexerQueryAction_None = 0,
+
+ /**
+ * \brief The indexer should run the \c CXRefactoringContinuaton in a
+ * translation unit that contains this file.
+ */
+ CXIndexerQueryAction_RunContinuationInTUThatHasThisFile = 1,
+};
+
+/**
+ * \brief Consumes an integer/boolean query result.
+ */
+CINDEX_LINKAGE
+enum CXIndexerQueryAction
+clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex,
+ int Value);
+
+/**
+ * \brief Consumes a filename query result.
+ *
+ * This function may return
+ * \c CXIndexerQueryAction_RunContinuationInTUThatHasThisFile which
+ * should tell the indexer that it has to run the refactoring continuation in
+ * the TU that contains this file.
+ */
+CINDEX_LINKAGE
+enum CXIndexerQueryAction
+clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex,
+ const char *Filename);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LLVM_CLANG_C_REFACTOR_H */
diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h
new file mode 100644
index 0000000..6eb0534
--- /dev/null
+++ b/include/clang/APINotes/APINotesManager.h
@@ -0,0 +1,144 @@
+//===--- APINotesManager.h - Manage API Notes Files -------------*- 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 APINotesManager interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_APINOTESMANAGER_H
+#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/Module.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+#include <string>
+
+namespace clang {
+
+class DirectoryEntry;
+class FileEntry;
+class LangOptions;
+class SourceManager;
+
+namespace api_notes {
+
+class APINotesReader;
+
+/// The API notes manager helps find API notes associated with declarations.
+///
+/// API notes are externally-provided annotations for declarations that can
+/// introduce new attributes (covering availability, nullability of
+/// parameters/results, and so on) for specific declarations without directly
+/// modifying the headers that contain those declarations.
+///
+/// The API notes manager is responsible for finding and loading the
+/// external API notes files that correspond to a given header. Its primary
+/// operation is \c findAPINotes(), which finds the API notes reader that
+/// provides information about the declarations at that location.
+class APINotesManager {
+ typedef llvm::PointerUnion<const DirectoryEntry *, APINotesReader *>
+ ReaderEntry;
+
+ SourceManager &SourceMgr;
+
+ /// Whether to implicitly search for API notes files based on the
+ /// source file from which an entity was declared.
+ bool ImplicitAPINotes;
+
+ /// The Swift version to use when interpreting versioned API notes.
+ VersionTuple SwiftVersion;
+
+ /// API notes readers for the current module.
+ ///
+ /// There can be up to two of these, one for public headers and one
+ /// for private headers.
+ APINotesReader *CurrentModuleReaders[2] = { nullptr, nullptr };
+
+ /// A mapping from header file directories to the API notes reader for
+ /// that directory, or a redirection to another directory entry that may
+ /// have more information, or NULL to indicate that there is no API notes
+ /// reader for this directory.
+ llvm::DenseMap<const DirectoryEntry *, ReaderEntry> Readers;
+
+ /// Load the API notes associated with the given file, whether it is
+ /// the binary or source form of API notes.
+ ///
+ /// \returns the API notes reader for this file, or null if there is
+ /// a failure.
+ std::unique_ptr<APINotesReader> loadAPINotes(const FileEntry *apiNotesFile);
+
+ /// Load the given API notes file for the given header directory.
+ ///
+ /// \param HeaderDir The directory at which we
+ ///
+ /// \returns true if an error occurred.
+ bool loadAPINotes(const DirectoryEntry *HeaderDir,
+ const FileEntry *APINotesFile);
+
+ /// Look for API notes in the given directory.
+ ///
+ /// This might find either a binary or source API notes.
+ const FileEntry *findAPINotesFile(const DirectoryEntry *directory,
+ StringRef filename,
+ bool wantPublic = true);
+
+ /// Attempt to load API notes for the given framework.
+ ///
+ /// \param FrameworkPath The path to the framework.
+ /// \param Public Whether to load the public API notes. Otherwise, attempt
+ /// to load the private API notes.
+ ///
+ /// \returns the header directory entry (e.g., for Headers or PrivateHeaders)
+ /// for which the API notes were successfully loaded, or NULL if API notes
+ /// could not be loaded for any reason.
+ const DirectoryEntry *loadFrameworkAPINotes(llvm::StringRef FrameworkPath,
+ llvm::StringRef FrameworkName,
+ bool Public);
+
+public:
+ APINotesManager(SourceManager &sourceMgr, const LangOptions &langOpts);
+ ~APINotesManager();
+
+ /// Set the Swift version to use when filtering API notes.
+ void setSwiftVersion(VersionTuple swiftVersion) {
+ SwiftVersion = swiftVersion;
+ }
+
+ /// Load the API notes for the current module.
+ ///
+ /// \param module The current module.
+ /// \param lookInModule Whether to look inside the module itself.
+ /// \param searchPaths The paths in which we should search for API notes
+ /// for the current module.
+ ///
+ /// \returns true if API notes were successfully loaded, \c false otherwise.
+ bool loadCurrentModuleAPINotes(const Module *module,
+ bool lookInModule,
+ ArrayRef<std::string> searchPaths);
+
+ /// Retrieve the set of API notes readers for the current module.
+ ArrayRef<APINotesReader *> getCurrentModuleReaders() const {
+ unsigned numReaders = static_cast<unsigned>(CurrentModuleReaders[0] != nullptr) +
+ static_cast<unsigned>(CurrentModuleReaders[1] != nullptr);
+ return llvm::makeArrayRef(CurrentModuleReaders).slice(0, numReaders);
+ }
+
+ /// Find the API notes readers that correspond to the given source location.
+ llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif
diff --git a/include/clang/APINotes/APINotesOptions.h b/include/clang/APINotes/APINotesOptions.h
new file mode 100644
index 0000000..24bb913
--- /dev/null
+++ b/include/clang/APINotes/APINotesOptions.h
@@ -0,0 +1,41 @@
+//===--- APINotesOptions.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 APINotesOptions class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_APINOTESOPTIONS_H
+#define LLVM_CLANG_APINOTES_APINOTESOPTIONS_H
+
+#include "clang/Basic/VersionTuple.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+
+/// APINotesOptions - Track various options which control how API
+/// notes are found and handled.
+class APINotesOptions {
+public:
+ /// The Swift version which should be used for API notes.
+ VersionTuple SwiftVersion;
+
+ /// The set of search paths where we API notes can be found for
+ /// particular modules.
+ ///
+ /// The API notes in this directory are stored as
+ /// <ModuleName>.apinotes or <ModuleName>.apinotesc, and are only
+ /// applied when building the module <ModuleName>.
+ std::vector<std::string> ModuleSearchPaths;
+};
+
+} // end namespace clang
+
+#endif
diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h
new file mode 100644
index 0000000..a71c74d
--- /dev/null
+++ b/include/clang/APINotes/APINotesReader.h
@@ -0,0 +1,269 @@
+//===--- APINotesReader.h - API Notes Reader ----------------------*- 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 \c APINotesReader class that reads source
+// API notes data providing additional information about source code as
+// a separate input, such as the non-nil/nilable annotations for
+// method parameters.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_API_NOTES_READER_H
+#define LLVM_CLANG_API_NOTES_READER_H
+
+#include "clang/APINotes/Types.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <memory>
+
+namespace clang {
+namespace api_notes {
+
+/// A class that reads API notes data from a binary file that was written by
+/// the \c APINotesWriter.
+class APINotesReader {
+ class Implementation;
+
+ Implementation &Impl;
+
+ APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer,
+ VersionTuple swiftVersion, bool &failed);
+
+public:
+ /// Create a new API notes reader from the given member buffer, which
+ /// contains the contents of a binary API notes file.
+ ///
+ /// \returns the new API notes reader, or null if an error occurred.
+ static std::unique_ptr<APINotesReader>
+ get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer,
+ VersionTuple swiftVersion);
+
+ /// Create a new API notes reader from the given member buffer, which
+ /// contains the contents of a binary API notes file.
+ ///
+ /// \returns the new API notes reader, or null if an error occurred.
+ static std::unique_ptr<APINotesReader>
+ getUnmanaged(llvm::MemoryBuffer *inputBuffer,
+ VersionTuple swiftVersion);
+
+ ~APINotesReader();
+
+ APINotesReader(const APINotesReader &) = delete;
+ APINotesReader &operator=(const APINotesReader &) = delete;
+
+ /// Retrieve the name of the module for which this reader is providing API
+ /// notes.
+ StringRef getModuleName() const;
+
+ /// Retrieve the size and modification time of the source file from
+ /// which this API notes file was created, if known.
+ Optional<std::pair<off_t, time_t>> getSourceFileSizeAndModTime() const;
+
+ /// Retrieve the module options
+ ModuleOptions getModuleOptions() const;
+
+ /// Captures the completed versioned information for a particular part of
+ /// API notes, including both unversioned API notes and each versioned API
+ /// note for that particular entity.
+ template<typename T>
+ class VersionedInfo {
+ /// The complete set of results.
+ SmallVector<std::pair<VersionTuple, T>, 1> Results;
+
+ /// The index of the result that is the "selected" set based on the desired
+ /// Swift version, or \c Results.size() if nothing matched.
+ unsigned Selected;
+
+ public:
+ /// Form an empty set of versioned information.
+ VersionedInfo(llvm::NoneType) : Selected(0) { }
+
+ /// Form a versioned info set given the desired version and a set of
+ /// results.
+ VersionedInfo(VersionTuple version,
+ SmallVector<std::pair<VersionTuple, T>, 1> results);
+
+ /// Determine whether there is a result that should be applied directly
+ /// to the AST.
+ explicit operator bool() const { return Selected != size(); }
+
+ /// Retrieve the information to apply directly to the AST.
+ const T& operator*() const {
+ assert(*this && "No result to apply directly");
+ return (*this)[Selected].second;
+ }
+
+ /// Retrieve the selected index in the result set.
+ Optional<unsigned> getSelected() const {
+ if (Selected == Results.size()) return None;
+ return Selected;
+ }
+
+ /// Return the number of versioned results we know about.
+ unsigned size() const { return Results.size(); }
+
+ /// Access all versioned results.
+ const std::pair<VersionTuple, T> *begin() const { return Results.begin(); }
+ const std::pair<VersionTuple, T> *end() const { return Results.end(); }
+
+ /// Access a specific versioned result.
+ const std::pair<VersionTuple, T> &operator[](unsigned index) const {
+ return Results[index];
+ }
+ };
+
+ /// Look for the context ID of the given Objective-C class.
+ ///
+ /// \param name The name of the class we're looking for.
+ ///
+ /// \returns The ID, if known.
+ Optional<ContextID> lookupObjCClassID(StringRef name);
+
+ /// Look for information regarding the given Objective-C class.
+ ///
+ /// \param name The name of the class we're looking for.
+ ///
+ /// \returns The information about the class, if known.
+ VersionedInfo<ObjCContextInfo> lookupObjCClassInfo(StringRef name);
+
+ /// Look for the context ID of the given Objective-C protocol.
+ ///
+ /// \param name The name of the protocol we're looking for.
+ ///
+ /// \returns The ID of the protocol, if known.
+ Optional<ContextID> lookupObjCProtocolID(StringRef name);
+
+ /// Look for information regarding the given Objective-C protocol.
+ ///
+ /// \param name The name of the protocol we're looking for.
+ ///
+ /// \returns The information about the protocol, if known.
+ VersionedInfo<ObjCContextInfo> lookupObjCProtocolInfo(StringRef name);
+
+ /// Look for information regarding the given Objective-C property in
+ /// the given context.
+ ///
+ /// \param contextID The ID that references the context we are looking for.
+ /// \param name The name of the property we're looking for.
+ /// \param isInstance Whether we are looking for an instance property (vs.
+ /// a class property).
+ ///
+ /// \returns Information about the property, if known.
+ VersionedInfo<ObjCPropertyInfo> lookupObjCProperty(ContextID contextID,
+ StringRef name,
+ bool isInstance);
+
+ /// Look for information regarding the given Objective-C method in
+ /// the given context.
+ ///
+ /// \param contextID The ID that references the context we are looking for.
+ /// \param selector The selector naming the method we're looking for.
+ /// \param isInstanceMethod Whether we are looking for an instance method.
+ ///
+ /// \returns Information about the method, if known.
+ VersionedInfo<ObjCMethodInfo> lookupObjCMethod(ContextID contextID,
+ ObjCSelectorRef selector,
+ bool isInstanceMethod);
+
+ /// Look for information regarding the given global variable.
+ ///
+ /// \param name The name of the global variable.
+ ///
+ /// \returns information about the global variable, if known.
+ VersionedInfo<GlobalVariableInfo> lookupGlobalVariable(StringRef name);
+
+ /// Look for information regarding the given global function.
+ ///
+ /// \param name The name of the global function.
+ ///
+ /// \returns information about the global function, if known.
+ VersionedInfo<GlobalFunctionInfo> lookupGlobalFunction(StringRef name);
+
+ /// Look for information regarding the given enumerator.
+ ///
+ /// \param name The name of the enumerator.
+ ///
+ /// \returns information about the enumerator, if known.
+ VersionedInfo<EnumConstantInfo> lookupEnumConstant(StringRef name);
+
+ /// Look for information regarding the given tag
+ /// (struct/union/enum/C++ class).
+ ///
+ /// \param name The name of the tag.
+ ///
+ /// \returns information about the tag, if known.
+ VersionedInfo<TagInfo> lookupTag(StringRef name);
+
+ /// Look for information regarding the given typedef.
+ ///
+ /// \param name The name of the typedef.
+ ///
+ /// \returns information about the typedef, if known.
+ VersionedInfo<TypedefInfo> lookupTypedef(StringRef name);
+
+ /// Visitor used when walking the contents of the API notes file.
+ class Visitor {
+ public:
+ virtual ~Visitor();
+
+ /// Visit an Objective-C class.
+ virtual void visitObjCClass(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an Objective-C protocol.
+ virtual void visitObjCProtocol(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an Objective-C method.
+ virtual void visitObjCMethod(ContextID contextID, StringRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an Objective-C property.
+ virtual void visitObjCProperty(ContextID contextID, StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a global variable.
+ virtual void visitGlobalVariable(StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a global function.
+ virtual void visitGlobalFunction(StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit an enumerator.
+ virtual void visitEnumConstant(StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a tag.
+ virtual void visitTag(StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Visit a typedef.
+ virtual void visitTypedef(StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion);
+ };
+
+ /// Visit the contents of the API notes file, passing each entity to the
+ /// given visitor.
+ void visit(Visitor &visitor);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_READER_H
diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h
new file mode 100644
index 0000000..62defc1
--- /dev/null
+++ b/include/clang/APINotes/APINotesWriter.h
@@ -0,0 +1,126 @@
+//===--- APINotesWriter.h - API Notes Writer ----------------------*- 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 \c APINotesWriter class that writes out source
+// API notes data providing additional information about source code as
+// a separate input, such as the non-nil/nilable annotations for
+// method parameters.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_API_NOTES_WRITER_H
+#define LLVM_CLANG_API_NOTES_WRITER_H
+
+#include "clang/Basic/VersionTuple.h"
+#include "clang/APINotes/Types.h"
+
+namespace llvm {
+ class raw_ostream;
+}
+
+namespace clang {
+
+class FileEntry;
+
+namespace api_notes {
+
+/// A class that writes API notes data to a binary representation that can be
+/// read by the \c APINotesReader.
+class APINotesWriter {
+ class Implementation;
+ Implementation &Impl;
+
+public:
+ /// Create a new API notes writer with the given module name and
+ /// (optional) source file.
+ APINotesWriter(StringRef moduleName, const FileEntry *sourceFile);
+ ~APINotesWriter();
+
+ APINotesWriter(const APINotesWriter &) = delete;
+ APINotesWriter &operator=(const APINotesWriter &) = delete;
+
+ /// Write the API notes data to the given stream.
+ void writeToStream(llvm::raw_ostream &os);
+
+ /// Add information about a specific Objective-C class or protocol.
+ ///
+ /// \param name The name of this class/protocol.
+ /// \param isClass Whether this is a class (vs. a protocol).
+ /// \param info Information about this class/protocol.
+ ///
+ /// \returns the ID of the class or protocol, which can be used to add
+ /// properties and methods to the class/protocol.
+ ContextID addObjCContext(StringRef name, bool isClass,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a specific Objective-C property.
+ ///
+ /// \param contextID The context in which this property resides.
+ /// \param name The name of this property.
+ /// \param info Information about this property.
+ void addObjCProperty(ContextID contextID, StringRef name,
+ bool isInstanceProperty,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a specific Objective-C method.
+ ///
+ /// \param contextID The context in which this method resides.
+ /// \param selector The selector that names this method.
+ /// \param isInstanceMethod Whether this method is an instance method
+ /// (vs. a class method).
+ /// \param info Information about this method.
+ void addObjCMethod(ContextID contextID, ObjCSelectorRef selector,
+ bool isInstanceMethod, const ObjCMethodInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a global variable.
+ ///
+ /// \param name The name of this global variable.
+ /// \param info Information about this global variable.
+ void addGlobalVariable(StringRef name, const GlobalVariableInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a global function.
+ ///
+ /// \param name The name of this global function.
+ /// \param info Information about this global function.
+ void addGlobalFunction(StringRef name, const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about an enumerator.
+ ///
+ /// \param name The name of this enumerator.
+ /// \param info Information about this enumerator.
+ void addEnumConstant(StringRef name, const EnumConstantInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a tag (struct/union/enum/C++ class).
+ ///
+ /// \param name The name of this tag.
+ /// \param info Information about this tag.
+ void addTag(StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add information about a typedef.
+ ///
+ /// \param name The name of this typedef.
+ /// \param info Information about this typedef.
+ void addTypedef(StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion);
+
+ /// Add module options
+ void addModuleOptions(ModuleOptions opts);
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_WRITER_H
+
diff --git a/include/clang/APINotes/APINotesYAMLCompiler.h b/include/clang/APINotes/APINotesYAMLCompiler.h
new file mode 100644
index 0000000..508da65
--- /dev/null
+++ b/include/clang/APINotes/APINotesYAMLCompiler.h
@@ -0,0 +1,62 @@
+//=== APINotesYAMLCompiler.h - API Notes YAML to binary compiler *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file reads sidecar API notes specified in YAML format.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_API_NOTES_YAML_COMPILER_H
+#define LLVM_CLANG_API_NOTES_YAML_COMPILER_H
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/SourceMgr.h"
+#include <memory>
+
+namespace llvm {
+ class raw_ostream;
+ class MemoryBuffer;
+}
+
+namespace clang {
+
+class FileEntry;
+
+namespace api_notes {
+
+ enum class ActionType {
+ None,
+ YAMLToBinary,
+ BinaryToYAML,
+ Dump,
+ };
+
+ enum class OSType {
+ OSX,
+ IOS,
+ TvOS,
+ WatchOS,
+ Absent
+ };
+
+ /// Converts API notes from YAML format to binary format.
+ bool compileAPINotes(llvm::StringRef yamlInput,
+ const FileEntry *sourceFile,
+ llvm::raw_ostream &os,
+ OSType targetOS,
+ llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr,
+ void *diagHandlerCtxt = nullptr);
+
+ bool parseAndDumpAPINotes(llvm::StringRef yamlInput);
+
+ /// Converts API notes from the compiled binary format to the YAML format.
+ bool decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input,
+ llvm::raw_ostream &os);
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_YAML_COMPILER_H
diff --git a/include/clang/APINotes/Types.h b/include/clang/APINotes/Types.h
new file mode 100644
index 0000000..2213bad
--- /dev/null
+++ b/include/clang/APINotes/Types.h
@@ -0,0 +1,745 @@
+//===--- Types.h - API Notes Data Types --------------------------*- 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 data types used in the representation of API notes data.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_API_NOTES_TYPES_H
+#define LLVM_CLANG_API_NOTES_TYPES_H
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include <cassert>
+#include <climits>
+
+namespace llvm {
+ class raw_ostream;
+}
+
+namespace clang {
+namespace api_notes {
+
+/// The file extension used for the source representation of API notes.
+static const char SOURCE_APINOTES_EXTENSION[] = "apinotes";
+
+/// The file extension used for the binary representation of API notes.
+static const char BINARY_APINOTES_EXTENSION[] = "apinotesc";
+
+using llvm::ArrayRef;
+using llvm::StringRef;
+using llvm::Optional;
+using llvm::None;
+
+/// Opaque context ID used to refer to an Objective-C class or protocol.
+class ContextID {
+public:
+ unsigned Value;
+
+ explicit ContextID(unsigned value) : Value(value) { }
+};
+
+/// Describes API notes data for any entity.
+///
+/// This is used as the base of all API notes.
+class CommonEntityInfo {
+public:
+ /// Message to use when this entity is unavailable.
+ std::string UnavailableMsg;
+
+ /// Whether this entity is marked unavailable.
+ unsigned Unavailable : 1;
+
+ /// Whether this entity is marked unavailable in Swift.
+ unsigned UnavailableInSwift : 1;
+
+private:
+ /// Whether SwiftPrivate was specified.
+ unsigned SwiftPrivateSpecified : 1;
+
+ /// Whether this entity is considered "private" to a Swift overlay.
+ unsigned SwiftPrivate : 1;
+
+public:
+ /// Swift name of this entity.
+ std::string SwiftName;
+
+ CommonEntityInfo()
+ : Unavailable(0), UnavailableInSwift(0), SwiftPrivateSpecified(0),
+ SwiftPrivate(0) { }
+
+ Optional<bool> isSwiftPrivate() const {
+ if (!SwiftPrivateSpecified) return None;
+ return SwiftPrivate;
+ }
+
+ void setSwiftPrivate(Optional<bool> swiftPrivate) {
+ if (swiftPrivate) {
+ SwiftPrivateSpecified = 1;
+ SwiftPrivate = *swiftPrivate;
+ } else {
+ SwiftPrivateSpecified = 0;
+ SwiftPrivate = 0;
+ }
+ }
+
+ friend bool operator==(const CommonEntityInfo &lhs,
+ const CommonEntityInfo &rhs) {
+ return lhs.UnavailableMsg == rhs.UnavailableMsg &&
+ lhs.Unavailable == rhs.Unavailable &&
+ lhs.UnavailableInSwift == rhs.UnavailableInSwift &&
+ lhs.SwiftPrivateSpecified == rhs.SwiftPrivateSpecified &&
+ lhs.SwiftPrivate == rhs.SwiftPrivate &&
+ lhs.SwiftName == rhs.SwiftName;
+ }
+
+ friend bool operator!=(const CommonEntityInfo &lhs,
+ const CommonEntityInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend CommonEntityInfo &operator|=(CommonEntityInfo &lhs,
+ const CommonEntityInfo &rhs) {
+ // Merge unavailability.
+ if (rhs.Unavailable) {
+ lhs.Unavailable = true;
+ if (rhs.UnavailableMsg.length() != 0 &&
+ lhs.UnavailableMsg.length() == 0) {
+ lhs.UnavailableMsg = rhs.UnavailableMsg;
+ }
+ }
+
+ if (rhs.UnavailableInSwift) {
+ lhs.UnavailableInSwift = true;
+ if (rhs.UnavailableMsg.length() != 0 &&
+ lhs.UnavailableMsg.length() == 0) {
+ lhs.UnavailableMsg = rhs.UnavailableMsg;
+ }
+ }
+
+ if (rhs.SwiftPrivateSpecified && !lhs.SwiftPrivateSpecified) {
+ lhs.SwiftPrivateSpecified = 1;
+ lhs.SwiftPrivate = rhs.SwiftPrivate;
+ }
+
+ if (rhs.SwiftName.length() != 0 &&
+ lhs.SwiftName.length() == 0)
+ lhs.SwiftName = rhs.SwiftName;
+
+ return lhs;
+ }
+};
+
+/// Describes API notes for types.
+class CommonTypeInfo : public CommonEntityInfo {
+ /// The Swift type to which a given type is bridged.
+ ///
+ /// Reflects the swift_bridge attribute.
+ Optional<std::string> SwiftBridge;
+
+ /// The NS error domain for this type.
+ Optional<std::string> NSErrorDomain;
+
+public:
+ CommonTypeInfo() : CommonEntityInfo() { }
+
+ const Optional<std::string> &getSwiftBridge() const { return SwiftBridge; }
+
+ void setSwiftBridge(const Optional<std::string> &swiftType) {
+ SwiftBridge = swiftType;
+ }
+
+ void setSwiftBridge(const Optional<StringRef> &swiftType) {
+ if (swiftType)
+ SwiftBridge = *swiftType;
+ else
+ SwiftBridge = None;
+ }
+
+ const Optional<std::string> &getNSErrorDomain() const {
+ return NSErrorDomain;
+ }
+
+ void setNSErrorDomain(const Optional<std::string> &domain) {
+ NSErrorDomain = domain;
+ }
+
+ void setNSErrorDomain(const Optional<StringRef> &domain) {
+ if (domain)
+ NSErrorDomain = *domain;
+ else
+ NSErrorDomain = None;
+ }
+
+ friend CommonTypeInfo &operator|=(CommonTypeInfo &lhs,
+ const CommonTypeInfo &rhs) {
+ static_cast<CommonEntityInfo &>(lhs) |= rhs;
+ if (!lhs.SwiftBridge && rhs.SwiftBridge)
+ lhs.SwiftBridge = rhs.SwiftBridge;
+ if (!lhs.NSErrorDomain && rhs.NSErrorDomain)
+ lhs.NSErrorDomain = rhs.NSErrorDomain;
+ return lhs;
+ }
+
+ friend bool operator==(const CommonTypeInfo &lhs,
+ const CommonTypeInfo &rhs) {
+ return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
+ lhs.SwiftBridge == rhs.SwiftBridge &&
+ lhs.NSErrorDomain == rhs.NSErrorDomain;
+ }
+
+ friend bool operator!=(const CommonTypeInfo &lhs,
+ const CommonTypeInfo &rhs) {
+ return !(lhs == rhs);
+ }
+};
+
+/// Describes API notes data for an Objective-C class or protocol.
+class ObjCContextInfo : public CommonTypeInfo {
+ /// Whether this class has a default nullability.
+ unsigned HasDefaultNullability : 1;
+
+ /// The default nullability.
+ unsigned DefaultNullability : 2;
+
+ /// Whether this class has designated initializers recorded.
+ unsigned HasDesignatedInits : 1;
+
+ unsigned SwiftImportAsNonGenericSpecified : 1;
+ unsigned SwiftImportAsNonGeneric : 1;
+
+ unsigned SwiftObjCMembersSpecified : 1;
+ unsigned SwiftObjCMembers : 1;
+
+public:
+ ObjCContextInfo()
+ : CommonTypeInfo(),
+ HasDefaultNullability(0),
+ DefaultNullability(0),
+ HasDesignatedInits(0),
+ SwiftImportAsNonGenericSpecified(false),
+ SwiftImportAsNonGeneric(false),
+ SwiftObjCMembersSpecified(false),
+ SwiftObjCMembers(false)
+ { }
+
+ /// Determine the default nullability for properties and methods of this
+ /// class.
+ ///
+ /// \returns the default nullability, if implied, or None if there is no
+ Optional<NullabilityKind> getDefaultNullability() const {
+ if (HasDefaultNullability)
+ return static_cast<NullabilityKind>(DefaultNullability);
+
+ return None;
+ }
+
+ /// Set the default nullability for properties and methods of this class.
+ void setDefaultNullability(NullabilityKind kind) {
+ HasDefaultNullability = true;
+ DefaultNullability = static_cast<unsigned>(kind);
+ }
+
+ bool hasDesignatedInits() const { return HasDesignatedInits; }
+ void setHasDesignatedInits(bool value) { HasDesignatedInits = value; }
+
+ Optional<bool> getSwiftImportAsNonGeneric() const {
+ if (SwiftImportAsNonGenericSpecified)
+ return SwiftImportAsNonGeneric;
+ return None;
+ }
+ void setSwiftImportAsNonGeneric(Optional<bool> value) {
+ if (value.hasValue()) {
+ SwiftImportAsNonGenericSpecified = true;
+ SwiftImportAsNonGeneric = value.getValue();
+ } else {
+ SwiftImportAsNonGenericSpecified = false;
+ SwiftImportAsNonGeneric = false;
+ }
+ }
+
+ Optional<bool> getSwiftObjCMembers() const {
+ if (SwiftObjCMembersSpecified)
+ return SwiftObjCMembers;
+ return None;
+ }
+ void setSwiftObjCMembers(Optional<bool> value) {
+ SwiftObjCMembersSpecified = value.hasValue();
+ SwiftObjCMembers = value.hasValue() ? *value : false;
+ }
+
+ /// Strip off any information within the class information structure that is
+ /// module-local, such as 'audited' flags.
+ void stripModuleLocalInfo() {
+ HasDefaultNullability = false;
+ DefaultNullability = 0;
+ }
+
+ friend bool operator==(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) {
+ return static_cast<const CommonTypeInfo &>(lhs) == rhs &&
+ lhs.getDefaultNullability() == rhs.getDefaultNullability() &&
+ lhs.HasDesignatedInits == rhs.HasDesignatedInits &&
+ lhs.getSwiftImportAsNonGeneric() ==
+ rhs.getSwiftImportAsNonGeneric() &&
+ lhs.getSwiftObjCMembers() == rhs.getSwiftObjCMembers();
+ }
+
+ friend bool operator!=(const ObjCContextInfo &lhs, const ObjCContextInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend ObjCContextInfo &operator|=(ObjCContextInfo &lhs,
+ const ObjCContextInfo &rhs) {
+ // Merge inherited info.
+ static_cast<CommonTypeInfo &>(lhs) |= rhs;
+
+ // Merge nullability.
+ if (!lhs.getDefaultNullability()) {
+ if (auto nullable = rhs.getDefaultNullability()) {
+ lhs.setDefaultNullability(*nullable);
+ }
+ }
+
+ if (!lhs.SwiftImportAsNonGenericSpecified &&
+ rhs.SwiftImportAsNonGenericSpecified) {
+ lhs.SwiftImportAsNonGenericSpecified = true;
+ lhs.SwiftImportAsNonGeneric = rhs.SwiftImportAsNonGeneric;
+ }
+
+ if (!lhs.SwiftObjCMembersSpecified && rhs.SwiftObjCMembersSpecified) {
+ lhs.SwiftObjCMembersSpecified = true;
+ lhs.SwiftObjCMembers = rhs.SwiftObjCMembers;
+ }
+
+ lhs.HasDesignatedInits |= rhs.HasDesignatedInits;
+
+ return lhs;
+ }
+
+ void dump(llvm::raw_ostream &os);
+};
+
+/// API notes for a variable/property.
+class VariableInfo : public CommonEntityInfo {
+ /// Whether this property has been audited for nullability.
+ unsigned NullabilityAudited : 1;
+
+ /// The kind of nullability for this property. Only valid if the nullability
+ /// has been audited.
+ unsigned Nullable : 2;
+
+ /// The C type of the variable, as a string.
+ std::string Type;
+
+public:
+ VariableInfo()
+ : CommonEntityInfo(),
+ NullabilityAudited(false),
+ Nullable(0) { }
+
+ Optional<NullabilityKind> getNullability() const {
+ if (NullabilityAudited)
+ return static_cast<NullabilityKind>(Nullable);
+
+ return None;
+ }
+
+ void setNullabilityAudited(NullabilityKind kind) {
+ NullabilityAudited = true;
+ Nullable = static_cast<unsigned>(kind);
+ }
+
+ const std::string &getType() const { return Type; }
+ void setType(const std::string &type) { Type = type; }
+
+ friend bool operator==(const VariableInfo &lhs, const VariableInfo &rhs) {
+ return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
+ lhs.NullabilityAudited == rhs.NullabilityAudited &&
+ lhs.Nullable == rhs.Nullable &&
+ lhs.Type == rhs.Type;
+ }
+
+ friend bool operator!=(const VariableInfo &lhs, const VariableInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ friend VariableInfo &operator|=(VariableInfo &lhs,
+ const VariableInfo &rhs) {
+ static_cast<CommonEntityInfo &>(lhs) |= rhs;
+ if (!lhs.NullabilityAudited && rhs.NullabilityAudited)
+ lhs.setNullabilityAudited(*rhs.getNullability());
+ if (lhs.Type.empty() && !rhs.Type.empty())
+ lhs.Type = rhs.Type;
+ return lhs;
+ }
+};
+
+/// Describes API notes data for an Objective-C property.
+class ObjCPropertyInfo : public VariableInfo {
+ unsigned SwiftImportAsAccessorsSpecified : 1;
+ unsigned SwiftImportAsAccessors : 1;
+
+public:
+ ObjCPropertyInfo()
+ : VariableInfo(), SwiftImportAsAccessorsSpecified(false),
+ SwiftImportAsAccessors(false) {}
+
+ /// Merge class-wide information into the given property.
+ friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs,
+ const ObjCContextInfo &rhs) {
+ static_cast<VariableInfo &>(lhs) |= rhs;
+
+ // Merge nullability.
+ if (!lhs.getNullability()) {
+ if (auto nullable = rhs.getDefaultNullability()) {
+ lhs.setNullabilityAudited(*nullable);
+ }
+ }
+
+ return lhs;
+ }
+
+ Optional<bool> getSwiftImportAsAccessors() const {
+ if (SwiftImportAsAccessorsSpecified)
+ return SwiftImportAsAccessors;
+ return None;
+ }
+ void setSwiftImportAsAccessors(Optional<bool> value) {
+ if (value.hasValue()) {
+ SwiftImportAsAccessorsSpecified = true;
+ SwiftImportAsAccessors = value.getValue();
+ } else {
+ SwiftImportAsAccessorsSpecified = false;
+ SwiftImportAsAccessors = false;
+ }
+ }
+
+ friend ObjCPropertyInfo &operator|=(ObjCPropertyInfo &lhs,
+ const ObjCPropertyInfo &rhs) {
+ lhs |= static_cast<const VariableInfo &>(rhs);
+ if (!lhs.SwiftImportAsAccessorsSpecified &&
+ rhs.SwiftImportAsAccessorsSpecified) {
+ lhs.SwiftImportAsAccessorsSpecified = true;
+ lhs.SwiftImportAsAccessors = rhs.SwiftImportAsAccessors;
+ }
+ return lhs;
+ }
+
+ friend bool operator==(const ObjCPropertyInfo &lhs,
+ const ObjCPropertyInfo &rhs) {
+ return static_cast<const VariableInfo &>(lhs) == rhs &&
+ lhs.getSwiftImportAsAccessors() == rhs.getSwiftImportAsAccessors();
+ }
+};
+
+/// Describes a function or method parameter.
+class ParamInfo : public VariableInfo {
+ /// Whether noescape was specified.
+ unsigned NoEscapeSpecified : 1;
+
+ /// Whether the this parameter has the 'noescape' attribute.
+ unsigned NoEscape : 1;
+
+public:
+ ParamInfo() : VariableInfo(), NoEscapeSpecified(false), NoEscape(false) { }
+
+ Optional<bool> isNoEscape() const {
+ if (!NoEscapeSpecified) return None;
+ return NoEscape;
+ }
+ void setNoEscape(Optional<bool> noescape) {
+ if (noescape) {
+ NoEscapeSpecified = true;
+ NoEscape = *noescape;
+ } else {
+ NoEscapeSpecified = false;
+ NoEscape = false;
+ }
+ }
+
+ friend ParamInfo &operator|=(ParamInfo &lhs, const ParamInfo &rhs) {
+ static_cast<VariableInfo &>(lhs) |= rhs;
+ if (!lhs.NoEscapeSpecified && rhs.NoEscapeSpecified) {
+ lhs.NoEscapeSpecified = true;
+ lhs.NoEscape = rhs.NoEscape;
+ }
+ return lhs;
+ }
+
+ friend bool operator==(const ParamInfo &lhs, const ParamInfo &rhs) {
+ return static_cast<const VariableInfo &>(lhs) == rhs &&
+ lhs.NoEscapeSpecified == rhs.NoEscapeSpecified &&
+ lhs.NoEscape == rhs.NoEscape;
+ }
+
+ friend bool operator!=(const ParamInfo &lhs, const ParamInfo &rhs) {
+ return !(lhs == rhs);
+ }
+};
+
+/// A temporary reference to an Objective-C selector, suitable for
+/// referencing selector data on the stack.
+///
+/// Instances of this struct do not store references to any of the
+/// data they contain; it is up to the user to ensure that the data
+/// referenced by the identifier list persists.
+struct ObjCSelectorRef {
+ unsigned NumPieces;
+ ArrayRef<StringRef> Identifiers;
+};
+
+/// API notes for a function or method.
+class FunctionInfo : public CommonEntityInfo {
+private:
+ static unsigned const NullabilityKindMask = 0x3;
+ static unsigned const NullabilityKindSize = 2;
+
+public:
+ /// Whether the signature has been audited with respect to nullability.
+ /// If yes, we consider all types to be non-nullable unless otherwise noted.
+ /// If this flag is not set, the pointer types are considered to have
+ /// unknown nullability.
+ unsigned NullabilityAudited : 1;
+
+ /// Number of types whose nullability is encoded with the NullabilityPayload.
+ unsigned NumAdjustedNullable : 8;
+
+ /// Stores the nullability of the return type and the parameters.
+ // NullabilityKindSize bits are used to encode the nullability. The info
+ // about the return type is stored at position 0, followed by the nullability
+ // of the parameters.
+ uint64_t NullabilityPayload = 0;
+
+ /// The result type of this function, as a C type.
+ std::string ResultType;
+
+ /// The function parameters.
+ std::vector<ParamInfo> Params;
+
+ FunctionInfo()
+ : CommonEntityInfo(),
+ NullabilityAudited(false),
+ NumAdjustedNullable(0) { }
+
+ static unsigned getMaxNullabilityIndex() {
+ return ((sizeof(NullabilityPayload) * CHAR_BIT)/NullabilityKindSize);
+ }
+
+ void addTypeInfo(unsigned index, NullabilityKind kind) {
+ assert(index <= getMaxNullabilityIndex());
+ assert(static_cast<unsigned>(kind) < NullabilityKindMask);
+ NullabilityAudited = true;
+ if (NumAdjustedNullable < index + 1)
+ NumAdjustedNullable = index + 1;
+
+ // Mask the bits.
+ NullabilityPayload &= ~(NullabilityKindMask << (index * NullabilityKindSize));
+
+ // Set the value.
+ unsigned kindValue =
+ (static_cast<unsigned>(kind)) << (index * NullabilityKindSize);
+ NullabilityPayload |= kindValue;
+ }
+
+ /// Adds the return type info.
+ void addReturnTypeInfo(NullabilityKind kind) {
+ addTypeInfo(0, kind);
+ }
+
+ /// Adds the parameter type info.
+ void addParamTypeInfo(unsigned index, NullabilityKind kind) {
+ addTypeInfo(index + 1, kind);
+ }
+
+private:
+ NullabilityKind getTypeInfo(unsigned index) const {
+ assert(NullabilityAudited &&
+ "Checking the type adjustment on non-audited method.");
+ // If we don't have info about this parameter, return the default.
+ if (index > NumAdjustedNullable)
+ return NullabilityKind::NonNull;
+ return static_cast<NullabilityKind>(( NullabilityPayload
+ >> (index * NullabilityKindSize) )
+ & NullabilityKindMask);
+ }
+
+public:
+ NullabilityKind getParamTypeInfo(unsigned index) const {
+ return getTypeInfo(index + 1);
+ }
+
+ NullabilityKind getReturnTypeInfo() const {
+ return getTypeInfo(0);
+ }
+
+ friend bool operator==(const FunctionInfo &lhs, const FunctionInfo &rhs) {
+ return static_cast<const CommonEntityInfo &>(lhs) == rhs &&
+ lhs.NullabilityAudited == rhs.NullabilityAudited &&
+ lhs.NumAdjustedNullable == rhs.NumAdjustedNullable &&
+ lhs.NullabilityPayload == rhs.NullabilityPayload &&
+ lhs.ResultType == rhs.ResultType &&
+ lhs.Params == rhs.Params;
+ }
+
+ friend bool operator!=(const FunctionInfo &lhs, const FunctionInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+};
+
+/// Describes API notes data for an Objective-C method.
+class ObjCMethodInfo : public FunctionInfo {
+public:
+ /// Whether this is a designated initializer of its class.
+ unsigned DesignatedInit : 1;
+
+ /// Whether this is a required initializer.
+ unsigned Required : 1;
+
+ ObjCMethodInfo()
+ : FunctionInfo(),
+ DesignatedInit(false),
+ Required(false) { }
+
+ friend bool operator==(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) {
+ return static_cast<const FunctionInfo &>(lhs) == rhs &&
+ lhs.DesignatedInit == rhs.DesignatedInit &&
+ lhs.Required == rhs.Required;
+ }
+
+ friend bool operator!=(const ObjCMethodInfo &lhs, const ObjCMethodInfo &rhs) {
+ return !(lhs == rhs);
+ }
+
+ void mergePropInfoIntoSetter(const ObjCPropertyInfo &pInfo);
+
+ void mergePropInfoIntoGetter(const ObjCPropertyInfo &pInfo);
+
+ /// Merge class-wide information into the given method.
+ friend ObjCMethodInfo &operator|=(ObjCMethodInfo &lhs,
+ const ObjCContextInfo &rhs) {
+ // Merge nullability.
+ if (!lhs.NullabilityAudited) {
+ if (auto nullable = rhs.getDefaultNullability()) {
+ lhs.NullabilityAudited = true;
+ lhs.addTypeInfo(0, *nullable);
+ }
+ }
+
+ return lhs;
+ }
+
+ void dump(llvm::raw_ostream &os);
+};
+
+/// Describes API notes data for a global variable.
+class GlobalVariableInfo : public VariableInfo {
+public:
+ GlobalVariableInfo() : VariableInfo() { }
+};
+
+/// Describes API notes data for a global function.
+class GlobalFunctionInfo : public FunctionInfo {
+public:
+ GlobalFunctionInfo() : FunctionInfo() { }
+};
+
+/// Describes API notes data for an enumerator.
+class EnumConstantInfo : public CommonEntityInfo {
+public:
+ EnumConstantInfo() : CommonEntityInfo() { }
+};
+
+/// The payload for an enum_extensibility attribute. This is a tri-state rather
+/// than just a boolean because the presence of the attribute indicates
+/// auditing.
+enum class EnumExtensibilityKind {
+ None,
+ Open,
+ Closed,
+};
+
+/// Describes API notes data for a tag.
+class TagInfo : public CommonTypeInfo {
+ unsigned HasFlagEnum : 1;
+ unsigned IsFlagEnum : 1;
+public:
+ Optional<EnumExtensibilityKind> EnumExtensibility;
+
+ Optional<bool> isFlagEnum() const {
+ if (HasFlagEnum)
+ return IsFlagEnum;
+ return None;
+ }
+ void setFlagEnum(Optional<bool> Value) {
+ if (Value.hasValue()) {
+ HasFlagEnum = true;
+ IsFlagEnum = Value.getValue();
+ } else {
+ HasFlagEnum = false;
+ }
+ }
+
+ TagInfo() : CommonTypeInfo(), HasFlagEnum(0), IsFlagEnum(0) { }
+
+ friend TagInfo &operator|=(TagInfo &lhs, const TagInfo &rhs) {
+ lhs |= static_cast<const CommonTypeInfo &>(rhs);
+ if (!lhs.HasFlagEnum && rhs.HasFlagEnum) {
+ lhs.HasFlagEnum = true;
+ lhs.IsFlagEnum = rhs.IsFlagEnum;
+ }
+ if (!lhs.EnumExtensibility.hasValue() && rhs.EnumExtensibility.hasValue())
+ lhs.EnumExtensibility = rhs.EnumExtensibility;
+ return lhs;
+ }
+
+ friend bool operator==(const TagInfo &lhs, const TagInfo &rhs) {
+ return static_cast<const CommonTypeInfo &>(lhs) == rhs &&
+ lhs.isFlagEnum() == rhs.isFlagEnum() &&
+ lhs.EnumExtensibility == rhs.EnumExtensibility;
+ }
+};
+
+/// The kind of a swift_wrapper/swift_newtype.
+enum class SwiftWrapperKind {
+ None,
+ Struct,
+ Enum
+};
+
+/// Describes API notes data for a typedef.
+class TypedefInfo : public CommonTypeInfo {
+public:
+ Optional<SwiftWrapperKind> SwiftWrapper;
+
+ TypedefInfo() : CommonTypeInfo() { }
+
+ friend TypedefInfo &operator|=(TypedefInfo &lhs, const TypedefInfo &rhs) {
+ lhs |= static_cast<const CommonTypeInfo &>(rhs);
+ if (!lhs.SwiftWrapper.hasValue() && rhs.SwiftWrapper.hasValue())
+ lhs.SwiftWrapper = rhs.SwiftWrapper;
+ return lhs;
+ }
+
+ friend bool operator==(const TypedefInfo &lhs, const TypedefInfo &rhs) {
+ return static_cast<const CommonTypeInfo &>(lhs) == rhs &&
+ lhs.SwiftWrapper == rhs.SwiftWrapper;
+ }
+};
+
+/// Descripts a series of options for a module
+struct ModuleOptions {
+ bool SwiftInferImportAsMember = false;
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+#endif // LLVM_CLANG_API_NOTES_TYPES_H
diff --git a/include/clang/AST/Attr.h b/include/clang/AST/Attr.h
index bbe320c..58566db 100644
--- a/include/clang/AST/Attr.h
+++ b/include/clang/AST/Attr.h
@@ -24,6 +24,7 @@
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
diff --git a/include/clang/AST/AttrIterator.h b/include/clang/AST/AttrIterator.h
index fb9b049..5ef250c 100644
--- a/include/clang/AST/AttrIterator.h
+++ b/include/clang/AST/AttrIterator.h
@@ -108,6 +108,8 @@
specific_attr_iterator Right) {
return !(Left == Right);
}
+
+ Iterator getCurrent() const { return Current; }
};
template <typename SpecificAttr, typename Container>
diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h
index 2f735c5..515cb77 100644
--- a/include/clang/AST/DeclCXX.h
+++ b/include/clang/AST/DeclCXX.h
@@ -375,7 +375,6 @@
/// \brief These flags are \c true if a defaulted corresponding special
/// member can't be fully analyzed without performing overload resolution.
/// @{
- unsigned NeedOverloadResolutionForCopyConstructor : 1;
unsigned NeedOverloadResolutionForMoveConstructor : 1;
unsigned NeedOverloadResolutionForMoveAssignment : 1;
unsigned NeedOverloadResolutionForDestructor : 1;
@@ -384,7 +383,6 @@
/// \brief These flags are \c true if an implicit defaulted corresponding
/// special member would be defined as deleted.
/// @{
- unsigned DefaultedCopyConstructorIsDeleted : 1;
unsigned DefaultedMoveConstructorIsDeleted : 1;
unsigned DefaultedMoveAssignmentIsDeleted : 1;
unsigned DefaultedDestructorIsDeleted : 1;
@@ -417,12 +415,6 @@
/// constructor.
unsigned HasDefaultedDefaultConstructor : 1;
- /// \brief True if this class can be passed in a non-address-preserving
- /// fashion (such as in registers) according to the C++ language rules.
- /// This does not imply anything about how the ABI in use will actually
- /// pass an object of this class.
- unsigned CanPassInRegisters : 1;
-
/// \brief True if a defaulted default constructor for this class would
/// be constexpr.
unsigned DefaultedDefaultConstructorIsConstexpr : 1;
@@ -819,50 +811,18 @@
return data().FirstFriend.isValid();
}
- /// \brief \c true if a defaulted copy constructor for this class would be
- /// deleted.
- bool defaultedCopyConstructorIsDeleted() const {
- assert((!needsOverloadResolutionForCopyConstructor() ||
- (data().DeclaredSpecialMembers & SMF_CopyConstructor)) &&
- "this property has not yet been computed by Sema");
- return data().DefaultedCopyConstructorIsDeleted;
- }
-
- /// \brief \c true if a defaulted move constructor for this class would be
- /// deleted.
- bool defaultedMoveConstructorIsDeleted() const {
- assert((!needsOverloadResolutionForMoveConstructor() ||
- (data().DeclaredSpecialMembers & SMF_MoveConstructor)) &&
- "this property has not yet been computed by Sema");
- return data().DefaultedMoveConstructorIsDeleted;
- }
-
- /// \brief \c true if a defaulted destructor for this class would be deleted.
- bool defaultedDestructorIsDeleted() const {
- return !data().DefaultedDestructorIsDeleted;
- }
-
- /// \brief \c true if we know for sure that this class has a single,
- /// accessible, unambiguous copy constructor that is not deleted.
- bool hasSimpleCopyConstructor() const {
- return !hasUserDeclaredCopyConstructor() &&
- !data().DefaultedCopyConstructorIsDeleted;
- }
-
/// \brief \c true if we know for sure that this class has a single,
/// accessible, unambiguous move constructor that is not deleted.
bool hasSimpleMoveConstructor() const {
return !hasUserDeclaredMoveConstructor() && hasMoveConstructor() &&
!data().DefaultedMoveConstructorIsDeleted;
}
-
/// \brief \c true if we know for sure that this class has a single,
/// accessible, unambiguous move assignment operator that is not deleted.
bool hasSimpleMoveAssignment() const {
return !hasUserDeclaredMoveAssignment() && hasMoveAssignment() &&
!data().DefaultedMoveAssignmentIsDeleted;
}
-
/// \brief \c true if we know for sure that this class has an accessible
/// destructor that is not deleted.
bool hasSimpleDestructor() const {
@@ -918,16 +878,7 @@
/// \brief Determine whether we need to eagerly declare a defaulted copy
/// constructor for this class.
bool needsOverloadResolutionForCopyConstructor() const {
- // C++17 [class.copy.ctor]p6:
- // If the class definition declares a move constructor or move assignment
- // operator, the implicitly declared copy constructor is defined as
- // deleted.
- // In MSVC mode, sometimes a declared move assignment does not delete an
- // implicit copy constructor, so defer this choice to Sema.
- if (data().UserDeclaredSpecialMembers &
- (SMF_MoveConstructor | SMF_MoveAssignment))
- return true;
- return data().NeedOverloadResolutionForCopyConstructor;
+ return data().HasMutableFields;
}
/// \brief Determine whether an implicit copy constructor for this type
@@ -968,16 +919,7 @@
needsImplicitMoveConstructor();
}
- /// \brief Set that we attempted to declare an implicit copy
- /// constructor, but overload resolution failed so we deleted it.
- void setImplicitCopyConstructorIsDeleted() {
- assert((data().DefaultedCopyConstructorIsDeleted ||
- needsOverloadResolutionForCopyConstructor()) &&
- "Copy constructor should not be deleted");
- data().DefaultedCopyConstructorIsDeleted = true;
- }
-
- /// \brief Set that we attempted to declare an implicit move
+ /// \brief Set that we attempted to declare an implicitly move
/// constructor, but overload resolution failed so we deleted it.
void setImplicitMoveConstructorIsDeleted() {
assert((data().DefaultedMoveConstructorIsDeleted ||
@@ -1374,18 +1316,6 @@
return data().HasIrrelevantDestructor;
}
- /// \brief Determine whether this class has at least one trivial, non-deleted
- /// copy or move constructor.
- bool canPassInRegisters() const {
- return data().CanPassInRegisters;
- }
-
- /// \brief Set that we can pass this RecordDecl in registers.
- // FIXME: This should be set as part of completeDefinition.
- void setCanPassInRegisters(bool CanPass) {
- data().CanPassInRegisters = CanPass;
- }
-
/// \brief Determine whether this class has a non-literal or/ volatile type
/// non-static data member or base class.
bool hasNonLiteralTypeFieldsOrBases() const {
diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h
index 1cd6e00..4c04fb0 100644
--- a/include/clang/AST/DeclObjC.h
+++ b/include/clang/AST/DeclObjC.h
@@ -858,6 +858,11 @@
return Assign;
}
+ /// Return true if this property has an explicitly specified getter name.
+ bool hasExplicitGetterName() const {
+ return (PropertyAttributes & OBJC_PR_getter);
+ }
+
Selector getGetterName() const { return GetterName; }
SourceLocation getGetterNameLoc() const { return GetterNameLoc; }
void setGetterName(Selector Sel, SourceLocation Loc = SourceLocation()) {
@@ -865,6 +870,11 @@
GetterNameLoc = Loc;
}
+ /// Return true if this property has an explicitly specified setter name.
+ bool hasExplicitSetterName() const {
+ return (PropertyAttributes & OBJC_PR_setter);
+ }
+
Selector getSetterName() const { return SetterName; }
SourceLocation getSetterNameLoc() const { return SetterNameLoc; }
void setSetterName(Selector Sel, SourceLocation Loc = SourceLocation()) {
@@ -2617,14 +2627,23 @@
void anchor() override;
/// Class that this is an alias of.
ObjCInterfaceDecl *AliasedClass;
+ /// The location of the name of the referenced class.
+ SourceLocation AliasedClassLoc;
+ /// The location of the '@'.
+ SourceLocation AtLoc;
- ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
- ObjCInterfaceDecl* aliasedClass)
- : NamedDecl(ObjCCompatibleAlias, DC, L, Id), AliasedClass(aliasedClass) {}
+ ObjCCompatibleAliasDecl(DeclContext *DC, SourceLocation NameLoc,
+ IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass,
+ SourceLocation AliasedClassLoc, SourceLocation AtLoc)
+ : NamedDecl(ObjCCompatibleAlias, DC, NameLoc, Id),
+ AliasedClass(AliasedClass), AliasedClassLoc(AliasedClassLoc),
+ AtLoc(AtLoc) {}
+
public:
- static ObjCCompatibleAliasDecl *Create(ASTContext &C, DeclContext *DC,
- SourceLocation L, IdentifierInfo *Id,
- ObjCInterfaceDecl* aliasedClass);
+ static ObjCCompatibleAliasDecl *
+ Create(ASTContext &C, DeclContext *DC, SourceLocation NameLoc,
+ IdentifierInfo *Id, ObjCInterfaceDecl *AliasedClass,
+ SourceLocation AliasedClassLoc, SourceLocation AtLoc);
static ObjCCompatibleAliasDecl *CreateDeserialized(ASTContext &C,
unsigned ID);
@@ -2633,6 +2652,17 @@
ObjCInterfaceDecl *getClassInterface() { return AliasedClass; }
void setClassInterface(ObjCInterfaceDecl *D) { AliasedClass = D; }
+ SourceLocation getClassInterfaceLoc() const { return AliasedClassLoc; }
+
+ void setClassInterfaceLoc(SourceLocation Loc) { AliasedClassLoc = Loc; }
+
+ SourceLocation getAtLoc() const { return AtLoc; }
+ void setAtLoc(SourceLocation Loc) { AtLoc = Loc; }
+
+ SourceRange getSourceRange() const override LLVM_READONLY {
+ return SourceRange(AtLoc, AtLoc);
+ }
+
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == ObjCCompatibleAlias; }
diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h
index 2879452..1d0a203 100644
--- a/include/clang/AST/DeclTemplate.h
+++ b/include/clang/AST/DeclTemplate.h
@@ -157,6 +157,9 @@
return SourceRange(TemplateLoc, RAngleLoc);
}
+ void print(llvm::raw_ostream &Out, const PrintingPolicy &Policy,
+ const ASTContext &Context, unsigned Indentation = 0) const;
+
friend TrailingObjects;
template <size_t N, bool HasRequiresClause>
diff --git a/include/clang/AST/DependentASTVisitor.h b/include/clang/AST/DependentASTVisitor.h
new file mode 100644
index 0000000..4177344
--- /dev/null
+++ b/include/clang/AST/DependentASTVisitor.h
@@ -0,0 +1,91 @@
+//===--- DependentASTVisitor.h - Helper for dependent nodes -----*- 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 DependentASTVisitor RecursiveASTVisitor layer, which
+// is responsible for visiting unresolved symbol references.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H
+#define LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/Type.h"
+
+namespace clang {
+
+// TODO: Use in the indexer.
+template <typename Derived>
+class DependentASTVisitor : public RecursiveASTVisitor<Derived> {
+private:
+ bool visitDependentReference(
+ const Type *T, const DeclarationName &Name, SourceLocation Loc,
+ llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
+ if (!T)
+ return true;
+ const TemplateSpecializationType *TST =
+ T->getAs<TemplateSpecializationType>();
+ if (!TST)
+ return true;
+ TemplateName TN = TST->getTemplateName();
+ const ClassTemplateDecl *TD =
+ dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
+ if (!TD)
+ return true;
+ CXXRecordDecl *RD = TD->getTemplatedDecl();
+ if (!RD->hasDefinition())
+ return true;
+ RD = RD->getDefinition();
+ std::vector<const NamedDecl *> Symbols =
+ RD->lookupDependentName(Name, Filter);
+ // FIXME: Improve overload handling.
+ if (Symbols.size() != 1)
+ return true;
+ if (Loc.isInvalid())
+ return true;
+ return RecursiveASTVisitor<Derived>::getDerived()
+ .VisitDependentSymbolReference(Symbols[0], Loc);
+ }
+
+public:
+ bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
+ const DeclarationNameInfo &Info = E->getMemberNameInfo();
+ return visitDependentReference(
+ E->getBaseType().getTypePtrOrNull(), Info.getName(), Info.getLoc(),
+ [](const NamedDecl *D) { return D->isCXXInstanceMember(); });
+ }
+
+ bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
+ const DeclarationNameInfo &Info = E->getNameInfo();
+ const NestedNameSpecifier *NNS = E->getQualifier();
+ return visitDependentReference(
+ NNS->getAsType(), Info.getName(), Info.getLoc(),
+ [](const NamedDecl *D) { return !D->isCXXInstanceMember(); });
+ }
+
+ bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) {
+ const DependentNameType *DNT = TL.getTypePtr();
+ const NestedNameSpecifier *NNS = DNT->getQualifier();
+ DeclarationName Name(DNT->getIdentifier());
+ return visitDependentReference(
+ NNS->getAsType(), Name, TL.getNameLoc(),
+ [](const NamedDecl *ND) { return isa<TypeDecl>(ND); });
+ }
+
+ bool VisitDependentSymbolReference(const NamedDecl *Symbol,
+ SourceLocation SymbolNameLoc) {
+ return true;
+ }
+};
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_AST_DEPENDENT_AST_VISITOR_H
diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index 0cdbd2a..0b72535 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -2771,6 +2771,16 @@
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;
diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h
index b1ff9bd..3a6a3c3 100644
--- a/include/clang/AST/NestedNameSpecifier.h
+++ b/include/clang/AST/NestedNameSpecifier.h
@@ -219,6 +219,23 @@
/// in debugging.
void dump(const LangOptions &LO) const;
void dump() const;
+
+ /// \brief Compute the qualification required to get from the current context
+ /// (\p CurContext) to the target context (\p TargetContext).
+ ///
+ /// \param Context the AST context in which the qualification will be used.
+ ///
+ /// \param CurContext the context where an entity is being named, which is
+ /// typically based on the current scope.
+ ///
+ /// \param TargetContext the context in which the named entity actually
+ /// resides.
+ ///
+ /// \returns a nested name specifier that refers into the target context, or
+ /// NULL if no qualification is needed.
+ static NestedNameSpecifier *
+ getRequiredQualification(ASTContext &Context, const DeclContext *CurContext,
+ const DeclContext *TargetContext);
};
/// \brief A C++ nested-name-specifier augmented with source location
diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h
index 274df22..4128155 100644
--- a/include/clang/AST/PrettyPrinter.h
+++ b/include/clang/AST/PrettyPrinter.h
@@ -39,7 +39,7 @@
/// \brief Create a default printing policy for the specified language.
PrintingPolicy(const LangOptions &LO)
: Indentation(2), SuppressSpecifiers(false),
- SuppressTagKeyword(LO.CPlusPlus),
+ SupressStorageClassSpecifiers(false), SuppressTagKeyword(LO.CPlusPlus),
IncludeTagDefinition(false), SuppressScope(false),
SuppressUnwrittenScope(false), SuppressInitializers(false),
ConstantArraySizeAsWritten(false), AnonymousTagLocations(true),
@@ -50,7 +50,8 @@
UseVoidForZeroParams(!LO.CPlusPlus),
TerseOutput(false), PolishForDeclaration(false),
Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar),
- IncludeNewlines(true), MSVCFormatting(false) { }
+ IncludeNewlines(true), MSVCFormatting(false),
+ ConstantsAsWritten(false), UseStdFunctionForLambda(false) { }
/// \brief Adjust this printing policy for cases where it's known that
/// we're printing C++ code (for instance, if AST dumping reaches a
@@ -81,6 +82,10 @@
/// "const int" type specifier and instead only print the "*y".
bool SuppressSpecifiers : 1;
+ /// \brief Whether we should supress the printing of the actual storage class
+ /// specifiers for the given declaration.
+ bool SupressStorageClassSpecifiers : 1;
+
/// \brief Whether type printing should skip printing the tag keyword.
///
/// This is used when printing the inner type of elaborated types,
@@ -200,6 +205,27 @@
/// prints anonymous namespaces as `anonymous namespace' and does not insert
/// spaces after template arguments.
bool MSVCFormatting : 1;
+
+ /// \brief Whether we should print the constant expressions as written in the
+ /// sources.
+ ///
+ /// This flag determines whether constants expressions like
+ ///
+ /// \code
+ /// 0x10
+ /// 2.5e3
+ /// \endcode
+ ///
+ /// will be printed as written or as follows:
+ ///
+ /// \code
+ /// 0x10
+ /// 2.5e3
+ /// \endcode
+ bool ConstantsAsWritten;
+
+ /// \brief Whether we should use std::function<...> for lambda record types.
+ bool UseStdFunctionForLambda : 1;
};
} // end namespace clang
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index c210bd1..795f4d6 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -389,8 +389,8 @@
/// back to its original source language syntax.
void dumpPretty(const ASTContext &Context) const;
void printPretty(raw_ostream &OS, PrinterHelper *Helper,
- const PrintingPolicy &Policy,
- unsigned Indentation = 0) const;
+ const PrintingPolicy &Policy, unsigned Indentation = 0,
+ const ASTContext *Context = nullptr) const;
/// viewAST - Visualize an AST rooted at this Stmt* using GraphViz. Only
/// works on systems with GraphViz (Mac OS X) or dot+gv installed.
diff --git a/include/clang/AST/StmtVisitor.h b/include/clang/AST/StmtVisitor.h
index df4a2d8..470788e 100644
--- a/include/clang/AST/StmtVisitor.h
+++ b/include/clang/AST/StmtVisitor.h
@@ -29,15 +29,17 @@
/// StmtVisitorBase - This class implements a simple visitor for Stmt
/// subclasses. Since Expr derives from Stmt, this also includes support for
/// visiting Exprs.
-template<template <typename> class Ptr, typename ImplClass, typename RetTy=void>
+template<template <typename> class Ptr, typename ImplClass, typename RetTy=void,
+ class... ParamTys>
class StmtVisitorBase {
public:
#define PTR(CLASS) typename Ptr<CLASS>::type
#define DISPATCH(NAME, CLASS) \
- return static_cast<ImplClass*>(this)->Visit ## NAME(static_cast<PTR(CLASS)>(S))
+ return static_cast<ImplClass*>(this)->Visit ## NAME( \
+ static_cast<PTR(CLASS)>(S), std::forward<ParamTys>(P)...)
- RetTy Visit(PTR(Stmt) S) {
+ RetTy Visit(PTR(Stmt) S, ParamTys... P) {
// If we have a binary expr, dispatch to the subcode of the binop. A smart
// optimizer (e.g. LLVM) will fold this comparison into the switch stmt
@@ -111,13 +113,13 @@
// If the implementation chooses not to implement a certain visit method, fall
// back on VisitExpr or whatever else is the superclass.
#define STMT(CLASS, PARENT) \
- RetTy Visit ## CLASS(PTR(CLASS) S) { DISPATCH(PARENT, PARENT); }
+ RetTy Visit ## CLASS(PTR(CLASS) S, ParamTys... P) { DISPATCH(PARENT, PARENT); }
#include "clang/AST/StmtNodes.inc"
// If the implementation doesn't implement binary operator methods, fall back
// on VisitBinaryOperator.
#define BINOP_FALLBACK(NAME) \
- RetTy VisitBin ## NAME(PTR(BinaryOperator) S) { \
+ RetTy VisitBin ## NAME(PTR(BinaryOperator) S, ParamTys... P) { \
DISPATCH(BinaryOperator, BinaryOperator); \
}
BINOP_FALLBACK(PtrMemD) BINOP_FALLBACK(PtrMemI)
@@ -137,7 +139,7 @@
// If the implementation doesn't implement compound assignment operator
// methods, fall back on VisitCompoundAssignOperator.
#define CAO_FALLBACK(NAME) \
- RetTy VisitBin ## NAME(PTR(CompoundAssignOperator) S) { \
+ RetTy VisitBin ## NAME(PTR(CompoundAssignOperator) S, ParamTys... P) { \
DISPATCH(CompoundAssignOperator, CompoundAssignOperator); \
}
CAO_FALLBACK(MulAssign) CAO_FALLBACK(DivAssign) CAO_FALLBACK(RemAssign)
@@ -149,7 +151,7 @@
// If the implementation doesn't implement unary operator methods, fall back
// on VisitUnaryOperator.
#define UNARYOP_FALLBACK(NAME) \
- RetTy VisitUnary ## NAME(PTR(UnaryOperator) S) { \
+ RetTy VisitUnary ## NAME(PTR(UnaryOperator) S, ParamTys... P) { \
DISPATCH(UnaryOperator, UnaryOperator); \
}
UNARYOP_FALLBACK(PostInc) UNARYOP_FALLBACK(PostDec)
@@ -163,7 +165,7 @@
#undef UNARYOP_FALLBACK
// Base case, ignore it. :)
- RetTy VisitStmt(PTR(Stmt) Node) { return RetTy(); }
+ RetTy VisitStmt(PTR(Stmt) Node, ParamTys... P) { return RetTy(); }
#undef PTR
#undef DISPATCH
@@ -174,18 +176,18 @@
///
/// This class does not preserve constness of Stmt pointers (see also
/// ConstStmtVisitor).
-template<typename ImplClass, typename RetTy=void>
+template<typename ImplClass, typename RetTy=void, typename... ParamTys>
class StmtVisitor
- : public StmtVisitorBase<make_ptr, ImplClass, RetTy> {};
+ : public StmtVisitorBase<make_ptr, ImplClass, RetTy, ParamTys...> {};
/// ConstStmtVisitor - This class implements a simple visitor for Stmt
/// subclasses. Since Expr derives from Stmt, this also includes support for
/// visiting Exprs.
///
/// This class preserves constness of Stmt pointers (see also StmtVisitor).
-template<typename ImplClass, typename RetTy=void>
+template<typename ImplClass, typename RetTy=void, typename... ParamTys>
class ConstStmtVisitor
- : public StmtVisitorBase<make_const_ptr, ImplClass, RetTy> {};
+ : public StmtVisitorBase<make_const_ptr, ImplClass, RetTy, ParamTys...> {};
/// \brief This class implements a simple visitor for OMPClause
/// subclasses.
diff --git a/include/clang/Basic/AlignedAllocation.h b/include/clang/Basic/AlignedAllocation.h
new file mode 100644
index 0000000..b349694
--- /dev/null
+++ b/include/clang/Basic/AlignedAllocation.h
@@ -0,0 +1,44 @@
+//===--- AlignedAllocation.h - Aligned Allocation ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Defines a function that returns the minimum OS versions supporting
+/// C++17's aligned allocation functions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_BASIC_ALIGNED_ALLOCATION_H
+#define LLVM_CLANG_BASIC_ALIGNED_ALLOCATION_H
+
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace clang {
+
+inline VersionTuple alignedAllocMinVersion(llvm::Triple::OSType OS) {
+ switch (OS) {
+ default:
+ break;
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX: // Earliest supporting version is 10.13.
+ return VersionTuple(10U, 13U);
+ case llvm::Triple::IOS:
+ case llvm::Triple::TvOS: // Earliest supporting version is 11.0.0.
+ return VersionTuple(11U);
+ case llvm::Triple::WatchOS: // Earliest supporting version is 4.0.0.
+ return VersionTuple(4U);
+ }
+
+ llvm_unreachable("Unexpected OS");
+}
+
+} // end namespace clang
+
+#endif // LLVM_CLANG_BASIC_ALIGNED_ALLOCATION_H
diff --git a/include/clang/Basic/AllDiagnostics.h b/include/clang/Basic/AllDiagnostics.h
index fc861a1..4af321f 100644
--- a/include/clang/Basic/AllDiagnostics.h
+++ b/include/clang/Basic/AllDiagnostics.h
@@ -24,6 +24,7 @@
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Serialization/SerializationDiagnostic.h"
+#include "clang/Tooling/Core/RefactoringDiagnostic.h"
namespace clang {
template <size_t SizeOfStr, typename FieldType>
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index f13e13b..9b4dd62 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -87,6 +87,9 @@
def NonBitField : SubsetSubject<Field,
[{!S->isBitField()}]>;
+def ObjCClassMethod : SubsetSubject<ObjCMethod,
+ [{!S->isInstanceMethod()}]>;
+
def ObjCInstanceMethod : SubsetSubject<ObjCMethod,
[{S->isInstanceMethod()}]>;
@@ -195,6 +198,9 @@
list<string> Enums = enums;
}
+// Represents an attribute wrapped by another attribute.
+class AttrArgument<string name, bit opt = 0> : Argument<name, opt>;
+
// This handles one spelling of an attribute.
class Spelling<string name, string variety> {
string Name = name;
@@ -654,6 +660,7 @@
.Case("macos_app_extension", "macOS (App Extension)")
.Case("tvos_app_extension", "tvOS (App Extension)")
.Case("watchos_app_extension", "watchOS (App Extension)")
+ .Case("swift", "Swift")
.Default(llvm::StringRef());
}
static llvm::StringRef getPlatformNameSourceSpelling(llvm::StringRef Platform) {
@@ -1383,6 +1390,12 @@
let Documentation = [Undocumented];
}
+def NoEscape : InheritableAttr {
+ let Spellings = [GCC<"noescape">];
+ let Subjects = SubjectList<[ParmVar], WarnDiag, "ExpectedParameter">;
+ let Documentation = [Undocumented];
+}
+
def AssumeAligned : InheritableAttr {
let Spellings = [GCC<"assume_aligned">];
let Subjects = SubjectList<[ObjCMethod, Function]>;
@@ -1453,6 +1466,12 @@
let Documentation = [Undocumented];
}
+def NSErrorDomain : Attr {
+ let Spellings = [GNU<"ns_error_domain">];
+ let Args = [IdentifierArgument<"ErrorDomain">];
+ let Documentation = [NSErrorDomainDocs];
+}
+
def NSReturnsRetained : InheritableAttr {
let Spellings = [GNU<"ns_returns_retained">];
// let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>;
@@ -1539,6 +1558,12 @@
let Documentation = [ObjCSubclassingRestrictedDocs];
}
+def ObjCCompleteDefinition : InheritableAttr {
+ let Spellings = [GNU<"objc_complete_definition">];
+ let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
+ let Documentation = [Undocumented];
+}
+
def ObjCExplicitProtocolImpl : InheritableAttr {
let Spellings = [GNU<"objc_protocol_requires_explicit_implementation">];
let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>;
@@ -1641,6 +1666,94 @@
let Documentation = [RegparmDocs];
}
+def SwiftBridge : Attr {
+ let Spellings = [GNU<"swift_bridge">];
+ let Subjects = SubjectList<[Tag, TypedefName, ObjCInterface, ObjCProtocol],
+ ErrorDiag, "ExpectedType">;
+ let Args = [StringArgument<"SwiftType">];
+ let Documentation = [SwiftBridgeDocs];
+}
+
+def SwiftObjCMembers : Attr {
+ let Spellings = [GNU<"swift_objc_members">];
+ let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
+ let Documentation = [SwiftObjCMembersDocs];
+}
+
+def SwiftError : InheritableAttr {
+ let Spellings = [GCC<"swift_error">];
+ let Args = [EnumArgument<"Convention", "ConventionKind",
+ ["none", "nonnull_error", "null_result", "zero_result", "nonzero_result"],
+ ["None", "NonNullError", "NullResult", "ZeroResult", "NonZeroResult"]>];
+ let Subjects = SubjectList<[ObjCMethod, Function], ErrorDiag>;
+ let Documentation = [SwiftErrorDocs];
+}
+
+def SwiftName : InheritableAttr {
+ let Spellings = [GCC<"swift_name">];
+ let Args = [StringArgument<"Name">];
+ // Proper subject list disabled because of the custom error needed.
+ // Let's avoid merge conflicts for now.
+// let Subjects = SubjectList<[EnumConstant, ObjCProtocol, ObjCClassMethod],
+// ErrorDiag, "ExpectedSwiftNameSubjects">;
+ let Documentation = [Undocumented];
+}
+
+def SwiftNewtype : InheritableAttr {
+ let Spellings = [GNU<"swift_newtype">, GNU<"swift_wrapper">];
+ let Subjects = SubjectList<[TypedefName], ErrorDiag, "ExpectedType">;
+ let Args = [EnumArgument<"NewtypeKind", "NewtypeKind",
+ ["struct", "enum"],
+ ["NK_Struct", "NK_Enum"]>];
+ let Documentation = [SwiftNewtypeDocs];
+}
+
+def SwiftPrivate : InheritableAttr {
+ let Spellings = [GCC<"swift_private">];
+ let Documentation = [Undocumented];
+}
+
+def SwiftImportAsNonGeneric : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+}
+
+def SwiftImportPropertyAsAccessors : InheritableAttr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+}
+
+def SwiftVersioned : Attr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let Args = [VersionArgument<"Version">, AttrArgument<"AttrToAdd">,
+ BoolArgument<"IsReplacedByActive">];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+}
+
+def SwiftVersionedRemoval : Attr {
+ // This attribute has no spellings as it is only ever created implicitly
+ // from API notes.
+ let Spellings = [];
+ let Args = [VersionArgument<"Version">, UnsignedArgument<"RawKind">,
+ BoolArgument<"IsReplacedByActive">];
+ let SemaHandler = 0;
+ let Documentation = [Undocumented];
+ let AdditionalMembers = [{
+ attr::Kind getAttrKindToRemove() const {
+ return static_cast<attr::Kind>(getRawKind());
+ }
+ }];
+}
+
def ReqdWorkGroupSize : InheritableAttr {
let Spellings = [GNU<"reqd_work_group_size">];
let Args = [UnsignedArgument<"XDim">, UnsignedArgument<"YDim">,
@@ -2673,6 +2786,14 @@
let Documentation = [Undocumented];
}
+def OMPCaptureKind : Attr {
+ // This attribute has no spellings as it is only ever created implicitly.
+ let Spellings = [];
+ let SemaHandler = 0;
+ let Args = [UnsignedArgument<"CaptureKind">];
+ let Documentation = [Undocumented];
+}
+
def OMPDeclareSimdDecl : Attr {
let Spellings = [Pragma<"omp", "declare simd">];
let Subjects = SubjectList<[Function]>;
diff --git a/include/clang/Basic/AttrDocs.td b/include/clang/Basic/AttrDocs.td
index 33ef3ea..f18540a 100644
--- a/include/clang/Basic/AttrDocs.td
+++ b/include/clang/Basic/AttrDocs.td
@@ -2554,6 +2554,65 @@
}];
}
+def SwiftDocs : DocumentationCategory<"Controlling Swift Import"> {
+ let Content = [{
+Clang supports additional attributes for controlling how APIs are imported into Swift.
+ }];
+}
+
+def NSErrorDomainDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+The ``ns_error_domain`` attribute indicates a global constant representing the error domain.
+ }];
+}
+
+def SwiftBridgeDocs : Documentation {
+ let Category = SwiftDocs;
+ let Content = [{
+The ``swift_bridge`` attribute indicates that the type to which the attribute appertains is bridged to the named Swift type.
+ }];
+}
+
+def SwiftObjCMembersDocs : Documentation {
+ let Category = SwiftDocs;
+ let Content = [{
+The ``swift_objc_members`` attribute maps to the Swift ``@objcMembers`` attribute, which indicates that Swift members of this class, its subclasses, and all of the extensions thereof, will implicitly be exposed back to Objective-C.
+ }];
+}
+
+def SwiftErrorDocs : Documentation {
+ let Category = SwiftDocs;
+ let Heading = "swift_error";
+ let Content = [{
+The ``swift_error`` attribute controls whether a particular function (or Objective-C method) is imported into Swift as a throwing function, and if so, the dynamic convention it uses.
+
+All of these conventions except ``none`` require the function to have an error parameter. Currently, the error parameter is always the last parameter of type ``NSError**`` or ``CFErrorRef*``. Swift will remove the error parameter from the imported API, and dynamically will always pass a valid address initialized to a null pointer.
+
+* ``swift_error(none)`` means that the function should not be imported as throwing. The error parameter and result type will be left alone.
+
+* ``swift_error(null_result)`` means that calls to the function should be considered to have thrown if they return a null value. The return type must be a pointer type, and it will be imported into Swift with a non-optional type. This is the default error convention for Objective-C methods that return pointers.
+
+* ``swift_error(zero_result)`` means that calls to the function should be considered to have thrown if they return a zero result. The return type must be an integral type. If the return type would have been imported as ``Bool``, it is instead imported as ``Void``. This is the default error convention for Objective-C methods that return a type that would be imported as ``Bool``.
+
+* ``swift_error(nonzero_result)`` means that calls to the function should be considered to have thrown if they return a non-zero result. The return type must be an integral type. If the return type would have been imported as ``Bool``, it is instead imported as ``Void``.
+
+* ``swift_error(nonnull_error)`` means that calls to the function should be considered to have thrown if they leave a non-null error in the error parameter. The return type is left unmodified.
+
+}];
+}
+
+def SwiftNewtypeDocs : Documentation {
+ let Category = SwiftDocs;
+ let Heading = "swift_newtype";
+ let Content = [{
+The ``swift_newtype`` attribute indicates that the typedef to which the attribute appertains is imported as a new Swift type of the typedef's name.
+* ``swift_newtype(struct)`` means that a Swift struct will be created for this typedef.
+* ``swift_newtype(enum)`` means that a Swift enum will be created for this typedef.
+ }];
+}
+
+
def OMPDeclareTargetDocs : Documentation {
let Category = DocCatFunction;
let Heading = "#pragma omp declare target";
diff --git a/include/clang/Basic/CMakeLists.txt b/include/clang/Basic/CMakeLists.txt
index 3e0fb87..ced4d63 100644
--- a/include/clang/Basic/CMakeLists.txt
+++ b/include/clang/Basic/CMakeLists.txt
@@ -15,6 +15,7 @@
clang_diag_gen(Parse)
clang_diag_gen(Sema)
clang_diag_gen(Serialization)
+clang_diag_gen(Refactoring)
clang_tablegen(DiagnosticGroups.inc -gen-clang-diag-groups
SOURCE Diagnostic.td
TARGET ClangDiagnosticGroups)
diff --git a/include/clang/Basic/Diagnostic.td b/include/clang/Basic/Diagnostic.td
index f25068e..cb87d87 100644
--- a/include/clang/Basic/Diagnostic.td
+++ b/include/clang/Basic/Diagnostic.td
@@ -139,4 +139,4 @@
include "DiagnosticParseKinds.td"
include "DiagnosticSemaKinds.td"
include "DiagnosticSerializationKinds.td"
-
+include "DiagnosticRefactoringKinds.td"
diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td
index 98fd3c4..66cf135 100644
--- a/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/include/clang/Basic/DiagnosticCommonKinds.td
@@ -94,6 +94,12 @@
"could not acquire lock file for module '%0': %1">, InGroup<ModuleBuild>;
def remark_module_lock_timeout : Remark<
"timed out waiting to acquire lock file for module '%0'">, InGroup<ModuleBuild>;
+def err_module_shadowed : Error<
+ "import of shadowed module '%0'">;
+def err_module_lock_failure : Error<
+ "could not acquire lock file for module '%0': %1">, DefaultFatal;
+def err_module_lock_timeout : Error<
+ "timed out waiting to acquire lock file for module '%0'">, DefaultFatal;
def err_module_cycle : Error<"cyclic dependency in module '%0': %1">,
DefaultFatal;
def err_module_prebuilt : Error<
@@ -224,6 +230,11 @@
def warn_arcmt_nsalloc_realloc : Warning<"[rewriter] call returns pointer to GC managed memory; it will become unmanaged in ARC">;
def err_arcmt_nsinvocation_ownership : Error<"NSInvocation's %0 is not safe to be used with an object with ownership other than __unsafe_unretained">;
+// API notes
+def err_apinotes_message : Error<"%0">;
+def warn_apinotes_message : Warning<"%0">, InGroup<DiagGroup<"apinotes">>;
+def note_apinotes_message : Note<"%0">;
+
// OpenMP
def err_omp_more_one_clause : Error<
"directive '#pragma omp %0' cannot contain more than one '%1' clause%select{| with '%3' name modifier| with 'source' dependence}2">;
diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td
index fcef881..e5611fa 100644
--- a/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/include/clang/Basic/DiagnosticDriverKinds.td
@@ -231,7 +231,7 @@
InGroup<DiagGroup<"rtti-for-exceptions">>;
def warn_drv_disabling_vptr_no_rtti_default : Warning<
"implicitly disabling vptr sanitizer because rtti wasn't enabled">,
- InGroup<DiagGroup<"auto-disable-vptr-sanitizer">>;
+ InGroup<AutoDisableVptrSanitizer>;
def warn_drv_object_size_disabled_O0 : Warning<
"the object size sanitizer has no effect at -O0, but is explicitly enabled: %0">,
InGroup<InvalidCommandLineArgument>;
diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td
index 57c24e9..38bc726 100644
--- a/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -216,6 +216,10 @@
Error<"file '%0' specified by '-fmodules-embed-file=' not found">,
DefaultFatal;
+def remark_index_producing_module_file_data : Remark<"producing index data for "
+ "module file '%0'">,
+ InGroup<IndexStore>;
+
def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "
"(%3.%4)">;
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td
index 23e4d46..9387ed2 100644
--- a/include/clang/Basic/DiagnosticGroups.td
+++ b/include/clang/Basic/DiagnosticGroups.td
@@ -27,6 +27,7 @@
def GNUAutoType : DiagGroup<"gnu-auto-type">;
def ArrayBounds : DiagGroup<"array-bounds">;
def ArrayBoundsPointerArithmetic : DiagGroup<"array-bounds-pointer-arithmetic">;
+def AutoDisableVptrSanitizer : DiagGroup<"auto-disable-vptr-sanitizer">;
def Availability : DiagGroup<"availability">;
def Section : DiagGroup<"section">;
def AutoImport : DiagGroup<"auto-import">;
@@ -299,6 +300,7 @@
def ModuleBuild : DiagGroup<"module-build">;
def ModuleConflict : DiagGroup<"module-conflict">;
def ModuleFileExtension : DiagGroup<"module-file-extension">;
+def IndexStore : DiagGroup<"index-store">;
def NewlineEOF : DiagGroup<"newline-eof">;
def Nullability : DiagGroup<"nullability">;
def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
@@ -405,6 +407,9 @@
def StringPlusInt : DiagGroup<"string-plus-int">;
def StringPlusChar : DiagGroup<"string-plus-char">;
def StrncatSize : DiagGroup<"strncat-size">;
+
+def SwiftNameAttribute : DiagGroup<"swift-name-attribute">;
+
def TautologicalOutOfRangeCompare : DiagGroup<"tautological-constant-out-of-range-compare">;
def TautologicalPointerCompare : DiagGroup<"tautological-pointer-compare">;
def TautologicalOverlapCompare : DiagGroup<"tautological-overlap-compare">;
diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h
index cdd3585..14fb24c 100644
--- a/include/clang/Basic/DiagnosticIDs.h
+++ b/include/clang/Basic/DiagnosticIDs.h
@@ -37,8 +37,9 @@
DIAG_START_AST = DIAG_START_PARSE + 500,
DIAG_START_COMMENT = DIAG_START_AST + 110,
DIAG_START_SEMA = DIAG_START_COMMENT + 100,
- DIAG_START_ANALYSIS = DIAG_START_SEMA + 3500,
- DIAG_UPPER_LIMIT = DIAG_START_ANALYSIS + 100
+ DIAG_START_ANALYSIS = DIAG_START_SEMA + 4000,
+ DIAG_START_REFACTORING = DIAG_START_ANALYSIS + 100,
+ DIAG_UPPER_LIMIT = DIAG_START_REFACTORING + 100
};
class CustomDiagInfo;
diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td
index 706881b..2f52005 100644
--- a/include/clang/Basic/DiagnosticLexKinds.td
+++ b/include/clang/Basic/DiagnosticLexKinds.td
@@ -674,6 +674,13 @@
"expected integer literal as value for header attribute '%0'">;
def err_mmap_expected_header_attribute : Error<
"expected a header attribute name ('size' or 'mtime')">;
+def err_mmap_conflicting_export_as : Error<
+ "conflicting re-export of module '%0' as '%1' or '%2'">;
+def warn_mmap_redundant_export_as : Warning<
+ "module '%0' already re-exported as '%1'">,
+ InGroup<PrivateModule>;
+def err_mmap_submodule_export_as : Error<
+ "only top-level modules can be re-exported as public">;
def warn_auto_module_import : Warning<
"treating #%select{include|import|include_next|__include_macros}0 as an "
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index 5170c07..193a31a 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -1106,6 +1106,9 @@
def err_pragma_invalid_keyword : Error<
"invalid argument; expected 'enable'%select{|, 'full'}0%select{|, 'assume_safety'}1 or 'disable'">;
+// API notes.
+def err_type_unparsed : Error<"unparsed tokens following type">;
+
// Pragma unroll support.
def warn_pragma_unroll_cuda_value_in_parens : Warning<
"argument to '#pragma unroll' should not be in parentheses in CUDA C/C++">,
diff --git a/include/clang/Basic/DiagnosticRefactoringKinds.td b/include/clang/Basic/DiagnosticRefactoringKinds.td
new file mode 100644
index 0000000..41113c2
--- /dev/null
+++ b/include/clang/Basic/DiagnosticRefactoringKinds.td
@@ -0,0 +1,39 @@
+//==--- DiagnosticRefactoringKinds.td - refactoring diagnostics -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Refactoring Diagnostics
+//===----------------------------------------------------------------------===//
+
+let Component = "Refactoring" in {
+
+let CategoryName = "Rename Issue" in {
+def err_rename_builtin_function : Error<"%0 is a builtin function that "
+ "cannot be renamed">;
+def err_rename_sys_header : Error<"%0 cannot be renamed because it is "
+ "declared in a system header">;
+def err_method_rename_override_sys_framework : Error<"method %0 cannot be "
+ "renamed because it overrides a method declared in a system framework">;
+def err_rename_external_source_symbol : Error<"%0 is declared in a %1 file; "
+ "rename can be initiated in a %1 file only">;
+}
+
+let CategoryName = "Refactoring Continuation Issue" in {
+
+def err_ref_continuation_missing_implementation : Error<
+ "no %select{implementation file|@implementation declaration}0 for the "
+ "selected %select{declaration|@interface}0 %1; please add one and run the "
+ "refactoring action again">;
+
+def err_implement_declared_methods_all_implemented : Error<
+ "the selected methods are already implemented">;
+
+}
+
+} // end of Refactoring diagnostics
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 6456913..c33bf18 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1181,6 +1181,12 @@
InGroup<Selector>, DefaultIgnore;
def warn_unimplemented_protocol_method : Warning<
"method %0 in protocol %1 not implemented">, InGroup<Protocol>;
+def warn_class_does_not_conform_protocol : Warning<
+ "%select{class|category}0 %1 does not conform to protocol"
+ "%plural{1: %3|2:s %3 and %4|3:s %3, %4 and %5|:s %3, %4, %5, ...}2">,
+ InGroup<Protocol>;
+def note_add_missing_protocol_stubs : Note<
+ "add stubs for missing protocol requirements">;
def warn_multiple_selectors: Warning<
"several methods with selector %0 of mismatched types are found "
"for the @selector expression">,
@@ -1313,6 +1319,8 @@
"%0 cannot be defined in a type alias template">;
def err_type_defined_in_condition : Error<
"%0 cannot be defined in a condition">;
+def err_type_defined_in_enum : Error<
+ "%0 cannot be defined in an enumeration">;
def note_pure_virtual_function : Note<
"unimplemented pure virtual method %0 in %1">;
@@ -2870,6 +2878,9 @@
InGroup<Availability>;
def note_overridden_method : Note<
"overridden method is here">;
+def warn_availability_swift_unavailable_deprecated_only : Warning<
+ "only 'unavailable' and 'deprecated' are supported for Swift availability">,
+ InGroup<Availability>;
def note_protocol_method : Note<
"protocol method is here">;
@@ -3253,6 +3264,9 @@
def warn_attribute_nonnull_parm_no_args : Warning<
"'nonnull' attribute when used on parameters takes no arguments">,
InGroup<IgnoredAttributes>;
+def warn_attribute_noescape_non_pointer : Warning<
+ "'noescape' attribute ignored on parameter of non-pointer type %0">,
+ InGroup<IgnoredAttributes>;
def note_declared_nonnull : Note<
"declared %select{'returns_nonnull'|'nonnull'}0 here">;
def warn_attribute_sentinel_named_arguments : Warning<
@@ -3378,6 +3392,68 @@
def err_objc_attr_protocol_requires_definition : Error<
"attribute %0 can only be applied to @protocol definitions, not forward declarations">;
+// Swift attributes
+def warn_attr_swift_name_decl_kind : Warning<
+ "%0 attribute cannot be applied to this declaration">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_function : Warning<
+ "parameter of %0 attribute must be a Swift function name string">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_function_no_prototype : Warning<
+ "%0 attribute can only be applied to function declarations with prototypes">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_context_name_invalid_identifier : Warning<
+ "%0 attribute has invalid identifier for context name">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_basename_invalid_identifier : Warning<
+ "%0 attribute has invalid identifier for base name">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_parameter_invalid_identifier : Warning<
+ "%0 attribute has invalid identifier for parameter name">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_missing_parameters : Warning<
+ "%0 attribute is missing parameter label clause">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_not_accessor : Warning<
+ "%0 attribute for 'subscript' must be a getter or setter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_no_parameter : Warning<
+ "%0 attribute for 'subscript' must take at least one parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_getter_newValue : Warning<
+ "%0 attribute for 'subscript' getter cannot take a 'newValue:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_setter_no_newValue : Warning<
+ "%0 attribute for 'subscript' setter must take a 'newValue:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_subscript_setter_multiple_newValues : Warning<
+ "%0 attribute for 'subscript' setter cannot take multiple 'newValue:' parameters">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_getter_parameters : Warning<
+ "%0 attribute for getter must not take any parameters besides 'self:'">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_setter_parameters : Warning<
+ "%0 attribute for setter must take one parameter for new value">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_multiple_selfs : Warning<
+ "%0 attribute cannot specify more than one 'self:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_static_subscript : Warning<
+ "%0 attribute for 'subscript' must take a 'self:' parameter">,
+ InGroup<SwiftNameAttribute>;
+def warn_attr_swift_name_num_params : Warning<
+ "too %select{few|many}0 parameters in %1 attribute (expected %2; got %3)">,
+ InGroup<SwiftNameAttribute>;
+def err_attr_swift_error_no_error_parameter : Error<
+ "%0 attribute can only be applied to a %select{function|method}1 "
+ "with an error parameter">;
+def err_attr_swift_error_return_type : Error<
+ "%0 attribute with '%1' convention can only be applied to a "
+ "%select{function|method}2 returning %select{an integral type|a pointer}3">;
+def warn_swift_newtype_attribute_non_typedef : Warning<
+ "'swift_newtype' attribute may be put on a typedef only; "
+ "attribute is ignored">, InGroup<DiagGroup<"swift-newtype-attribute">>;
+
// Function Parameter Semantic Analysis.
def err_param_with_void_type : Error<"argument may not have 'void' type">;
def err_void_only_param : Error<
@@ -6420,8 +6496,8 @@
"guarantees %2 bytes">,
InGroup<OveralignedType>, DefaultIgnore;
def warn_aligned_allocation_unavailable :Warning<
- "aligned %select{allocation|deallocation}0 function of type '%1' possibly "
- "unavailable on %2">, InGroup<AlignedAllocationUnavailable>, DefaultError;
+ "aligned %select{allocation|deallocation}0 function of type '%1' is only "
+ "available on %2 %3 or newer">, InGroup<AlignedAllocationUnavailable>, DefaultError;
def note_silence_unligned_allocation_unavailable : Note<
"if you supply your own aligned allocation functions, use "
"-Wno-aligned-allocation-unavailable to silence this diagnostic">;
@@ -7925,6 +8001,7 @@
"3:enumeration values %1, %2, and %3 not handled in switch|"
":%0 enumeration values not handled in switch: %1, %2, %3...}0">,
InGroup<Switch>;
+def note_fill_in_missing_cases : Note<"add missing switch cases">;
def warn_unannotated_fallthrough : Warning<
"unannotated fall-through between switch labels">,
@@ -8201,6 +8278,14 @@
def err_nsreturns_retained_attribute_mismatch : Error<
"overriding method has mismatched ns_returns_%select{not_retained|retained}0"
" attributes">;
+
+def err_nserrordomain_not_tagdecl : Error<
+ "ns_error_domain attribute only valid on "
+ "%select{enums, structs, and unions|enums, structs, unions, and classes}0">;
+def err_nserrordomain_invalid_decl : Error<
+ "domain argument %0 does not refer to global constant">;
+def err_nserrordomain_requires_identifier : Error<
+ "domain argument must be an identifier">;
def note_getter_unavailable : Note<
"or because setter is declared here, but no getter method %0 is found">;
@@ -8485,6 +8570,13 @@
InGroup<OpenCLUnsupportedRGBA>;
} // end of sema category
+let CategoryName = "API Notes Issue" in {
+
+def err_incompatible_replacement_type : Error<
+ "API notes replacement type %0 has a different size from original type %1">;
+
+}
+
let CategoryName = "OpenMP Issue" in {
// OpenMP support.
def err_omp_expected_var_arg : Error<
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index c9230e0..2826c10 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -154,6 +154,7 @@
BENIGN_LANGOPT(ModulesErrorRecovery, 1, 1, "automatically importing modules as needed when performing error recovery")
BENIGN_LANGOPT(ImplicitModules, 1, 1, "building modules that are not specified via -fmodule-file")
COMPATIBLE_LANGOPT(ModulesLocalVisibility, 1, 0, "local submodule visibility")
+COMPATIBLE_LANGOPT(ModulesHashErrorDiags, 1, 0, "hash out diagnostic errors as part of the module hash")
COMPATIBLE_LANGOPT(Optimize , 1, 0, "__OPTIMIZE__ predefined macro")
COMPATIBLE_LANGOPT(OptimizeSize , 1, 0, "__OPTIMIZE_SIZE__ predefined macro")
COMPATIBLE_LANGOPT(Static , 1, 0, "__STATIC__ predefined macro (as opposed to __DYNAMIC__)")
@@ -261,6 +262,8 @@
LANGOPT(ApplePragmaPack, 1, 0, "Apple gcc-compatible #pragma pack handling")
LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
+LANGOPT(APINotes, 1, 0, "use external API notes")
+LANGOPT(APINotesModules, 1, 0, "use external API notes")
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
"field padding (0: none, 1:least "
diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h
index 177175e..453b062 100644
--- a/include/clang/Basic/Module.h
+++ b/include/clang/Basic/Module.h
@@ -95,6 +95,10 @@
/// \brief The name of the umbrella entry, as written in the module map.
std::string UmbrellaAsWritten;
+
+ /// \brief The module through which entities defined in this module will
+ /// eventually be exposed, for use in "private" modules.
+ std::string ExportAsModule;
private:
/// \brief The submodules of this module, indexed by name.
@@ -181,6 +185,9 @@
/// will be false to indicate that this (sub)module is not available.
SmallVector<Requirement, 2> Requirements;
+ /// \brief A module with the same name that shadows this module.
+ Module *ShadowingModule = nullptr;
+
/// \brief Whether this module is missing a feature from \c Requirements.
unsigned IsMissingRequirement : 1;
@@ -214,6 +221,9 @@
/// \brief Whether this is an inferred submodule (module * { ... }).
unsigned IsInferred : 1;
+ /// \brief Whether this is a module who has its swift_names inferred.
+ unsigned IsSwiftInferImportAsMember : 1;
+
/// \brief Whether we should infer submodules for this module based on
/// the headers.
///
@@ -359,13 +369,20 @@
///
/// \param Target The target options used for the current translation unit.
///
- /// \param Req If this module is unavailable, this parameter
- /// will be set to one of the requirements that is not met for use of
- /// this module.
+ /// \param Req If this module is unavailable because of a missing requirement,
+ /// this parameter will be set to one of the requirements that is not met for
+ /// use of this module.
+ ///
+ /// \param MissingHeader If this module is unavailable because of a missing
+ /// header, this parameter will be set to one of the missing headers.
+ ///
+ /// \param ShadowingModule If this module is unavailable because it is
+ /// shadowed, this parameter will be set to the shadowing module.
bool isAvailable(const LangOptions &LangOpts,
const TargetInfo &Target,
Requirement &Req,
- UnresolvedHeaderDirective &MissingHeader) const;
+ UnresolvedHeaderDirective &MissingHeader,
+ Module *&ShadowingModule) const;
/// \brief Determine whether this module is a submodule.
bool isSubModule() const { return Parent != nullptr; }
diff --git a/include/clang/Basic/Sanitizers.def b/include/clang/Basic/Sanitizers.def
index 71b1197..d6df617 100644
--- a/include/clang/Basic/Sanitizers.def
+++ b/include/clang/Basic/Sanitizers.def
@@ -50,6 +50,9 @@
// libFuzzer
SANITIZER("fuzzer", Fuzzer)
+// libFuzzer-required instrumentation, no linking.
+SANITIZER("fuzzer-no-link", FuzzerNoLink)
+
// ThreadSanitizer
SANITIZER("thread", Thread)
@@ -60,6 +63,7 @@
SANITIZER("alignment", Alignment)
SANITIZER("array-bounds", ArrayBounds)
SANITIZER("bool", Bool)
+SANITIZER("builtin", Builtin)
SANITIZER("enum", Enum)
SANITIZER("float-cast-overflow", FloatCastOverflow)
SANITIZER("float-divide-by-zero", FloatDivideByZero)
@@ -107,11 +111,12 @@
// -fsanitize=undefined includes all the sanitizers which have low overhead, no
// ABI or address space layout implications, and only catch undefined behavior.
SANITIZER_GROUP("undefined", Undefined,
- Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
- FloatDivideByZero | IntegerDivideByZero | NonnullAttribute |
- Null | ObjectSize | PointerOverflow | Return |
- ReturnsNonnullAttribute | Shift | SignedIntegerOverflow |
- Unreachable | VLABound | Function | Vptr)
+ Alignment | Bool | Builtin | ArrayBounds | Enum |
+ FloatCastOverflow | FloatDivideByZero |
+ IntegerDivideByZero | NonnullAttribute | Null | ObjectSize |
+ PointerOverflow | Return | ReturnsNonnullAttribute | Shift |
+ SignedIntegerOverflow | Unreachable | VLABound | Function |
+ Vptr)
// -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
diff --git a/include/clang/Basic/SourceMgrAdapter.h b/include/clang/Basic/SourceMgrAdapter.h
new file mode 100644
index 0000000..dd7b83f
--- /dev/null
+++ b/include/clang/Basic/SourceMgrAdapter.h
@@ -0,0 +1,85 @@
+//=== SourceMgrAdapter.h - SourceMgr to SourceManager Adapter ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides an adapter that maps diagnostics from llvm::SourceMgr
+// to Clang's SourceManager.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SOURCEMGRADAPTER_H
+#define LLVM_CLANG_SOURCEMGRADAPTER_H
+
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/SourceMgr.h"
+#include <string>
+#include <utility>
+
+namespace clang {
+
+class DiagnosticsEngine;
+class FileEntry;
+
+/// An adapter that can be used to translate diagnostics from one or more
+/// llvm::SourceMgr instances to a ,
+class SourceMgrAdapter {
+ /// Clang source manager.
+ SourceManager &SrcMgr;
+
+ /// Clang diagnostics engine.
+ DiagnosticsEngine &Diag;
+
+ /// Diagnostic IDs for errors, warnings, and notes.
+ unsigned ErrorDiagID, WarningDiagID, NoteDiagID;
+
+ /// The default file to use when mapping buffers.
+ const FileEntry *DefaultFile;
+
+ /// A mapping from (LLVM source manager, buffer ID) pairs to the
+ /// corresponding file ID within the Clang source manager.
+ llvm::DenseMap<std::pair<const llvm::SourceMgr *, unsigned>, FileID>
+ FileIDMapping;
+
+ /// Diagnostic handler.
+ static void handleDiag(const llvm::SMDiagnostic &diag, void *context);
+
+public:
+ /// Create a new \c SourceMgr adaptor that maps to the given source
+ /// manager and diagnostics engine.
+ SourceMgrAdapter(SourceManager &srcMgr, DiagnosticsEngine &diag,
+ unsigned errorDiagID, unsigned warningDiagID,
+ unsigned noteDiagID, const FileEntry *defaultFile = nullptr);
+
+ ~SourceMgrAdapter();
+
+ /// Map a source location in the given LLVM source manager to its
+ /// corresponding location in the Clang source manager.
+ SourceLocation mapLocation(const llvm::SourceMgr &llvmSrcMgr,llvm::SMLoc loc);
+
+ /// Map a source range in the given LLVM source manager to its corresponding
+ /// range in the Clang source manager.
+ SourceRange mapRange(const llvm::SourceMgr &llvmSrcMgr, llvm::SMRange range);
+
+ /// Handle the given diagnostic from an LLVM source manager.
+ void handleDiag(const llvm::SMDiagnostic &diag);
+
+ /// Retrieve the diagnostic handler to use with the underlying SourceMgr.
+ llvm::SourceMgr::DiagHandlerTy getDiagHandler() {
+ return &SourceMgrAdapter::handleDiag;
+ }
+
+ /// Retrieve the context to use with the diagnostic handler produced by
+ /// \c getDiagHandler().
+ void *getDiagContext() { return this; }
+};
+
+
+} // end namespace clang
+
+#endif
diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h
index d1a9ea8..beb1a7b 100644
--- a/include/clang/Basic/TargetInfo.h
+++ b/include/clang/Basic/TargetInfo.h
@@ -447,6 +447,9 @@
/// \brief Return the maximum width lock-free atomic operation which can be
/// inlined given the supported features of the given target.
unsigned getMaxAtomicInlineWidth() const { return MaxAtomicInlineWidth; }
+ /// \brief Set the maximum inline or promote width lock-free atomic operation
+ /// for the given target.
+ virtual void setMaxAtomicWidth() {}
/// \brief Returns true if the given target supports lock-free atomic
/// operations at the specified width and alignment.
virtual bool hasBuiltinAtomic(uint64_t AtomicSizeInBits,
diff --git a/include/clang/Basic/VersionTuple.h b/include/clang/Basic/VersionTuple.h
index da3b019..07315f0 100644
--- a/include/clang/Basic/VersionTuple.h
+++ b/include/clang/Basic/VersionTuple.h
@@ -17,6 +17,7 @@
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/DenseMapInfo.h"
#include <string>
#include <tuple>
@@ -70,6 +71,9 @@
return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0;
}
+ /// Whether this is a non-empty version tuple.
+ explicit operator bool () const { return !empty(); }
+
/// \brief Retrieve the major version number.
unsigned getMajor() const { return Major; }
@@ -165,4 +169,35 @@
raw_ostream& operator<<(raw_ostream &Out, const VersionTuple &V);
} // end namespace clang
+
+namespace llvm {
+ // Provide DenseMapInfo for version tuples.
+ template<>
+ struct DenseMapInfo<clang::VersionTuple> {
+ static inline clang::VersionTuple getEmptyKey() {
+ return clang::VersionTuple(0x7FFFFFFF);
+ }
+ static inline clang::VersionTuple getTombstoneKey() {
+ return clang::VersionTuple(0x7FFFFFFE);
+ }
+ static unsigned getHashValue(const clang::VersionTuple& value) {
+ unsigned result = value.getMajor();
+ if (auto minor = value.getMinor())
+ result = combineHashValue(result, *minor);
+ if (auto subminor = value.getSubminor())
+ result = combineHashValue(result, *subminor);
+ if (auto build = value.getBuild())
+ result = combineHashValue(result, *build);
+
+ return result;
+ }
+
+ static bool isEqual(const clang::VersionTuple &lhs,
+ const clang::VersionTuple &rhs) {
+ return lhs == rhs;
+ }
+ };
+
+} // end namespace llvm
+
#endif // LLVM_CLANG_BASIC_VERSIONTUPLE_H
diff --git a/include/clang/DirectoryWatcher/DirectoryWatcher.h b/include/clang/DirectoryWatcher/DirectoryWatcher.h
new file mode 100644
index 0000000..09d17a9
--- /dev/null
+++ b/include/clang/DirectoryWatcher/DirectoryWatcher.h
@@ -0,0 +1,47 @@
+//===- DirectoryWatcher.h - Listens for directory file changes --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// \brief Utility class for listening for file system changes in a directory.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H
+#define LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Index/IndexDataStore.h"
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace clang {
+
+/// Provides notifications for file system changes in a directory.
+///
+/// Guarantees that the first time the directory is processed, the receiver will
+/// be invoked even if the directory is empty.
+class DirectoryWatcher : public index::AbstractDirectoryWatcher {
+ struct Implementation;
+ Implementation &Impl;
+
+ DirectoryWatcher();
+
+ DirectoryWatcher(const DirectoryWatcher&) = delete;
+ DirectoryWatcher &operator =(const DirectoryWatcher&) = delete;
+
+public:
+ ~DirectoryWatcher();
+
+ static std::unique_ptr<DirectoryWatcher>
+ create(StringRef Path, EventReceiver Receiver, bool waitInitialSync,
+ std::string &Error);
+};
+
+} // namespace clang
+
+#endif
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index 205f36b..fd70845 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -461,6 +461,8 @@
def fmodule_format_EQ : Joined<["-"], "fmodule-format=">,
HelpText<"Select the container format for clang modules and PCH. "
"Supported options are 'raw' and 'obj'.">;
+def fmodules_hash_error_diagnostics : Flag<["-"], "fmodules-hash-error-diagnostics">,
+ HelpText<"Use a separate module cache for modules compiled with conflicting -Werror options">;
def ftest_module_file_extension_EQ :
Joined<["-"], "ftest-module-file-extension=">,
HelpText<"introduce a module file extension for testing purposes. "
diff --git a/include/clang/Driver/Job.h b/include/clang/Driver/Job.h
index ff88256..09f7ab5 100644
--- a/include/clang/Driver/Job.h
+++ b/include/clang/Driver/Job.h
@@ -34,9 +34,11 @@
struct CrashReportInfo {
StringRef Filename;
StringRef VFSPath;
+ StringRef IndexStorePath;
- CrashReportInfo(StringRef Filename, StringRef VFSPath)
- : Filename(Filename), VFSPath(VFSPath) {}
+ CrashReportInfo(StringRef Filename, StringRef VFSPath,
+ StringRef IndexStorePath)
+ : Filename(Filename), VFSPath(VFSPath), IndexStorePath(IndexStorePath) {}
};
/// Command - An executable path/name and argument vector to
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index 05dc9d7..d4d8ed4 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -320,6 +320,13 @@
def : Joined<["-"], "objcmt-white-list-dir-path=">, Flags<[CC1Option]>,
Alias<objcmt_whitelist_dir_path>;
+def index_store_path : Separate<["-"], "index-store-path">, Flags<[CC1Option]>,
+ HelpText<"Enable indexing with the specified data store path">;
+def index_ignore_system_symbols : Flag<["-"], "index-ignore-system-symbols">, Flags<[CC1Option]>,
+ HelpText<"Ignore symbols from system headers">;
+def index_record_codegen_name : Flag<["-"], "index-record-codegen-name">, Flags<[CC1Option]>,
+ HelpText<"Record the codegen name for symbols">;
+
// Make sure all other -ccc- options are rejected.
def ccc_ : Joined<["-"], "ccc-">, Group<internal_Group>, Flags<[Unsupported]>;
@@ -685,6 +692,21 @@
def fno_profile_use : Flag<["-"], "fno-profile-use">,
Alias<fno_profile_instr_use>;
+def fapinotes : Flag<["-"], "fapinotes">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Enable external API notes support">;
+def fapinotes_modules : Flag<["-"], "fapinotes-modules">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Enable module-based external API notes support">;
+def fno_apinotes : Flag<["-"], "fno-apinotes">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Disable external API notes support">;
+def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">;
+def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">,
+ Group<i_Group>, Flags<[DriverOption]>, MetaVarName<"<directory>">,
+ HelpText<"Does nothing; API notes are no longer cached separately from modules">;
+def fapinotes_swift_version : Joined<["-"], "fapinotes-swift-version=">,
+ Group<f_clang_Group>, Flags<[CC1Option]>, MetaVarName<"<version>">,
+ HelpText<"Specify the Swift version to use when filtering API notes">;
+
def fblocks : Flag<["-"], "fblocks">, Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Enable the 'blocks' language feature">;
def fbootclasspath_EQ : Joined<["-"], "fbootclasspath=">, Group<f_Group>;
@@ -694,9 +716,6 @@
def fbuiltin_module_map : Flag <["-"], "fbuiltin-module-map">, Group<f_Group>,
Flags<[DriverOption]>, HelpText<"Load the clang builtins module map file.">;
def fcaret_diagnostics : Flag<["-"], "fcaret-diagnostics">, Group<f_Group>;
-def fclang_abi_compat_EQ : Joined<["-"], "fclang-abi-compat=">, Group<f_clang_Group>,
- Flags<[CC1Option]>, MetaVarName<"<version>">, Values<"<major>.<minor>,latest">,
- HelpText<"Attempt to match the ABI of Clang <version>">;
def fclasspath_EQ : Joined<["-"], "fclasspath=">, Group<f_Group>;
def fcolor_diagnostics : Flag<["-"], "fcolor-diagnostics">, Group<f_Group>,
Flags<[CoreOption, CC1Option]>, HelpText<"Use colors in diagnostics">;
@@ -868,6 +887,10 @@
Group<f_clang_Group>;
def fno_sanitize_undefined_trap_on_error : Flag<["-"], "fno-sanitize-undefined-trap-on-error">,
Group<f_clang_Group>;
+def fsanitize_minimal_runtime : Flag<["-"], "fsanitize-minimal-runtime">,
+ Group<f_clang_Group>;
+def fno_sanitize_minimal_runtime : Flag<["-"], "fno-sanitize-minimal-runtime">,
+ Group<f_clang_Group>;
def fsanitize_link_cxx_runtime : Flag<["-"], "fsanitize-link-c++-runtime">,
Group<f_clang_Group>;
def fsanitize_cfi_cross_dso : Flag<["-"], "fsanitize-cfi-cross-dso">,
@@ -1585,6 +1608,8 @@
HelpText<"Display available options">;
def index_header_map : Flag<["-"], "index-header-map">, Flags<[CC1Option]>,
HelpText<"Make the next included directory (-I or -F) an indexer header map">;
+def iapinotes_modules : JoinedOrSeparate<["-"], "iapinotes-modules">, Group<clang_i_Group>, Flags<[CC1Option]>,
+ HelpText<"Add directory to the API notes search path referenced by module name">, MetaVarName<"<directory>">;
def idirafter : JoinedOrSeparate<["-"], "idirafter">, Group<clang_i_Group>, Flags<[CC1Option]>,
HelpText<"Add directory to AFTER include search path">;
def iframework : JoinedOrSeparate<["-"], "iframework">, Group<clang_i_Group>, Flags<[CC1Option]>,
@@ -2131,6 +2156,7 @@
def nostdincxx : Flag<["-"], "nostdinc++">, Flags<[CC1Option]>,
HelpText<"Disable standard #include directories for the C++ standard library">;
def nostdlib : Flag<["-"], "nostdlib">;
+def nostdlibxx : Flag<["-"], "nostdlib++">;
def object : Flag<["-"], "object">;
def o : JoinedOrSeparate<["-"], "o">, Flags<[DriverOption, RenderAsInput, CC1Option, CC1AsOption]>,
HelpText<"Write output to <file>">, MetaVarName<"<file>">;
diff --git a/include/clang/Driver/SanitizerArgs.h b/include/clang/Driver/SanitizerArgs.h
index a9645d4..3021d47 100644
--- a/include/clang/Driver/SanitizerArgs.h
+++ b/include/clang/Driver/SanitizerArgs.h
@@ -42,6 +42,7 @@
bool TsanMemoryAccess = true;
bool TsanFuncEntryExit = true;
bool TsanAtomics = true;
+ bool MinimalRuntime = false;
public:
/// Parses the sanitizer arguments from an argument list.
@@ -57,6 +58,7 @@
!Sanitizers.has(SanitizerKind::Address);
}
bool needsUbsanRt() const;
+ bool requiresMinimalRuntime() const { return MinimalRuntime; }
bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); }
bool needsSafeStackRt() const {
return Sanitizers.has(SanitizerKind::SafeStack);
diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h
index 6651491..a62a759 100644
--- a/include/clang/Driver/ToolChain.h
+++ b/include/clang/Driver/ToolChain.h
@@ -432,6 +432,10 @@
AddClangCXXStdlibIncludeArgs(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args) const;
+ /// Returns if the C++ standard library should be linked in.
+ /// Note that e.g. -lm should still be linked even if this returns false.
+ bool ShouldLinkCXXStdlib(const llvm::opt::ArgList &Args) const;
+
/// AddCXXStdlibLibArgs - Add the system specific linker arguments to use
/// for the given C++ standard library type.
virtual void AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args,
diff --git a/include/clang/Edit/RefactoringFixits.h b/include/clang/Edit/RefactoringFixits.h
new file mode 100644
index 0000000..bf8adb5
--- /dev/null
+++ b/include/clang/Edit/RefactoringFixits.h
@@ -0,0 +1,66 @@
+//===--- RefactoringFixits.h - Fixit producers for refactorings -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_EDIT_REFACTORING_FIXITS_H
+#define LLVM_CLANG_EDIT_REFACTORING_FIXITS_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+
+class ASTContext;
+class SwitchStmt;
+class EnumDecl;
+class ObjCContainerDecl;
+
+namespace edit {
+
+/**
+ * Generates the fix-its that perform the "add missing switch cases" refactoring
+ * operation.
+ */
+void fillInMissingSwitchEnumCases(
+ ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum,
+ const DeclContext *SwitchContext,
+ llvm::function_ref<void(const FixItHint &)> Consumer);
+
+/// Responsible for the fix-its that perform the
+/// "add missing protocol requirements" refactoring operation.
+namespace fillInMissingProtocolStubs {
+
+class FillInMissingProtocolStubsImpl;
+class FillInMissingProtocolStubs {
+ std::unique_ptr<FillInMissingProtocolStubsImpl> Impl;
+
+public:
+ FillInMissingProtocolStubs();
+ ~FillInMissingProtocolStubs();
+ FillInMissingProtocolStubs(FillInMissingProtocolStubs &&);
+ FillInMissingProtocolStubs &operator=(FillInMissingProtocolStubs &&);
+
+ /// Initiate the FillInMissingProtocolStubs edit.
+ ///
+ /// \returns true on Error.
+ bool initiate(ASTContext &Context, const ObjCContainerDecl *Container);
+ bool hasMissingRequiredMethodStubs();
+ void perform(ASTContext &Context,
+ llvm::function_ref<void(const FixItHint &)> Consumer);
+};
+
+void addMissingProtocolStubs(
+ ASTContext &Context, const ObjCContainerDecl *Container,
+ llvm::function_ref<void(const FixItHint &)> Consumer);
+
+} // end namespace fillInMissingProtocolStubs
+
+} // end namespace edit
+} // end namespace clang
+
+#endif // LLVM_CLANG_EDIT_REFACTORING_FIXITS_H
diff --git a/include/clang/Frontend/CodeGenOptions.def b/include/clang/Frontend/CodeGenOptions.def
index 4002415..8bfa46e 100644
--- a/include/clang/Frontend/CodeGenOptions.def
+++ b/include/clang/Frontend/CodeGenOptions.def
@@ -120,10 +120,6 @@
ENUM_CODEGENOPT(ObjCDispatchMethod, ObjCDispatchMethodKind, 2, Legacy)
CODEGENOPT(OmitLeafFramePointer , 1, 0) ///< Set when -momit-leaf-frame-pointer is
///< enabled.
-
-/// A version of Clang that we should attempt to be ABI-compatible with.
-ENUM_CODEGENOPT(ClangABICompat, ClangABI, 4, ClangABI::Latest)
-
VALUE_CODEGENOPT(OptimizationLevel, 2, 0) ///< The -O[0-3] option specified.
VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is specified.
@@ -152,6 +148,8 @@
CODEGENOPT(SanitizeMemoryUseAfterDtor, 1, 0) ///< Enable use-after-delete detection
///< in MemorySanitizer
CODEGENOPT(SanitizeCfiCrossDso, 1, 0) ///< Enable cross-dso support in CFI.
+CODEGENOPT(SanitizeMinimalRuntime, 1, 0) ///< Use "_minimal" sanitizer runtime for
+ ///< diagnostics.
CODEGENOPT(SanitizeCoverageType, 2, 0) ///< Type of sanitizer coverage
///< instrumentation.
CODEGENOPT(SanitizeCoverageIndirectCalls, 1, 0) ///< Enable sanitizer coverage
diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h
index 71730a2..22d5d3d 100644
--- a/include/clang/Frontend/CodeGenOptions.h
+++ b/include/clang/Frontend/CodeGenOptions.h
@@ -69,23 +69,6 @@
LocalExecTLSModel
};
- /// Clang versions with different platform ABI conformance.
- enum class ClangABI {
- /// Attempt to be ABI-compatible with code generated by Clang 3.8.x
- /// (SVN r257626). This causes <1 x long long> to be passed in an
- /// integer register instead of an SSE register on x64_64.
- Ver3_8,
-
- /// Attempt to be ABI-compatible with code generated by Clang 4.0.x
- /// (SVN r291814). This causes move operations to be ignored when
- /// determining whether a class type can be passed or returned directly.
- Ver4,
-
- /// Conform to the underlying platform's C and C++ ABIs as closely
- /// as we can.
- Latest
- };
-
enum StructReturnConventionKind {
SRCK_Default, // No special option was passed.
SRCK_OnStack, // Small structs on the stack (-fpcc-struct-return).
diff --git a/include/clang/Frontend/CompilerInstance.h b/include/clang/Frontend/CompilerInstance.h
index 5b5c752..46e10ad 100644
--- a/include/clang/Frontend/CompilerInstance.h
+++ b/include/clang/Frontend/CompilerInstance.h
@@ -183,6 +183,12 @@
/// The list of active output files.
std::list<OutputFile> OutputFiles;
+ /// \brief An optional callback function used to wrap all FrontendActions
+ /// produced to generate imported modules before they are executed.
+ std::function<std::unique_ptr<FrontendAction>
+ (const FrontendOptions &opts, std::unique_ptr<FrontendAction> action)>
+ GenModuleActionWrapper;
+
CompilerInstance(const CompilerInstance &) = delete;
void operator=(const CompilerInstance &) = delete;
public:
@@ -303,6 +309,13 @@
return Invocation->getHeaderSearchOptsPtr();
}
+ APINotesOptions &getAPINotesOpts() {
+ return Invocation->getAPINotesOpts();
+ }
+ const APINotesOptions &getAPINotesOpts() const {
+ return Invocation->getAPINotesOpts();
+ }
+
LangOptions &getLangOpts() {
return *Invocation->getLangOpts();
}
@@ -794,6 +807,15 @@
bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override;
+ void setGenModuleActionWrapper(std::function<std::unique_ptr<FrontendAction>
+ (const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)> Wrapper) {
+ GenModuleActionWrapper = Wrapper;
+ };
+
+ std::function<std::unique_ptr<FrontendAction>
+ (const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)>
+ getGenModuleActionWrapper() const { return GenModuleActionWrapper; }
+
void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) {
DependencyCollectors.push_back(std::move(Listener));
}
diff --git a/include/clang/Frontend/CompilerInvocation.h b/include/clang/Frontend/CompilerInvocation.h
index 8c4c932..4d3449f 100644
--- a/include/clang/Frontend/CompilerInvocation.h
+++ b/include/clang/Frontend/CompilerInvocation.h
@@ -10,6 +10,7 @@
#ifndef LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_
#define LLVM_CLANG_FRONTEND_COMPILERINVOCATION_H_
+#include "clang/APINotes/APINotesOptions.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileSystemOptions.h"
#include "clang/Basic/LangOptions.h"
@@ -113,6 +114,9 @@
MigratorOptions MigratorOpts;
+ /// Options controlling API notes.
+ APINotesOptions APINotesOpts;
+
/// Options controlling IRgen and the backend.
CodeGenOptions CodeGenOpts;
@@ -170,7 +174,7 @@
/// \brief Retrieve a module hash string that is suitable for uniquely
/// identifying the conditions under which the module was built.
- std::string getModuleHash() const;
+ std::string getModuleHash(DiagnosticsEngine &Diags) const;
/// @}
/// @name Option Subgroups
@@ -184,6 +188,11 @@
const MigratorOptions &getMigratorOpts() const {
return MigratorOpts;
}
+
+ APINotesOptions &getAPINotesOpts() { return APINotesOpts; }
+ const APINotesOptions &getAPINotesOpts() const {
+ return APINotesOpts;
+ }
CodeGenOptions &getCodeGenOpts() { return CodeGenOpts; }
const CodeGenOptions &getCodeGenOpts() const {
diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h
index e757a7e..b784a6f 100644
--- a/include/clang/Frontend/FrontendOptions.h
+++ b/include/clang/Frontend/FrontendOptions.h
@@ -256,6 +256,10 @@
std::string MTMigrateDir;
std::string ARCMTMigrateReportOut;
+ std::string IndexStorePath;
+ unsigned IndexIgnoreSystemSymbols : 1;
+ unsigned IndexRecordCodegenName : 1;
+
/// The input files and their types.
std::vector<FrontendInputFile> Inputs;
@@ -333,8 +337,9 @@
SkipFunctionBodies(false), UseGlobalModuleIndex(true),
GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false),
BuildingImplicitModule(false), ModulesEmbedAllFiles(false),
- IncludeTimestamps(true), ARCMTAction(ARCMT_None),
- ObjCMTAction(ObjCMT_None), ProgramAction(frontend::ParseSyntaxOnly)
+ IncludeTimestamps(true), ARCMTAction(ARCMT_None), ObjCMTAction(ObjCMT_None),
+ IndexIgnoreSystemSymbols(false), IndexRecordCodegenName(false),
+ ProgramAction(frontend::ParseSyntaxOnly)
{}
/// getInputKindForExtension - Return the appropriate input kind for a file
diff --git a/include/clang/Index/IndexDataStore.h b/include/clang/Index/IndexDataStore.h
new file mode 100644
index 0000000..714ccdd
--- /dev/null
+++ b/include/clang/Index/IndexDataStore.h
@@ -0,0 +1,102 @@
+//===--- IndexDataStore.h - Index data store info -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXDATASTORE_H
+#define LLVM_CLANG_INDEX_INDEXDATASTORE_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace index {
+
+class AbstractDirectoryWatcher {
+public:
+ enum class EventKind {
+ /// A file was added.
+ Added,
+ /// A file was removed.
+ Removed,
+ /// A file was modified.
+ Modified,
+ /// The watched directory got deleted. No more events will follow.
+ DirectoryDeleted,
+ };
+
+ struct Event {
+ EventKind Kind;
+ std::string Filename;
+ timespec ModTime;
+ };
+
+ typedef std::function<void(ArrayRef<Event> Events, bool isInitial)> EventReceiver;
+ typedef std::unique_ptr<AbstractDirectoryWatcher>(CreateFnTy)
+ (StringRef Path, EventReceiver Receiver, bool waitInitialSync, std::string &Error);
+
+ virtual ~AbstractDirectoryWatcher() {}
+};
+
+class IndexDataStore {
+public:
+ ~IndexDataStore();
+
+ static std::unique_ptr<IndexDataStore>
+ create(StringRef IndexStorePath, std::string &Error);
+
+ StringRef getFilePath() const;
+ bool foreachUnitName(bool sorted,
+ llvm::function_ref<bool(StringRef unitName)> receiver);
+
+ static unsigned getFormatVersion();
+
+ enum class UnitEventKind {
+ Added,
+ Removed,
+ Modified,
+ /// The directory got deleted. No more events will follow.
+ DirectoryDeleted,
+ };
+ struct UnitEvent {
+ UnitEventKind Kind;
+ StringRef UnitName;
+ timespec ModTime;
+ };
+ struct UnitEventNotification {
+ bool IsInitial;
+ ArrayRef<UnitEvent> Events;
+ };
+ typedef std::function<void(UnitEventNotification)> UnitEventHandler;
+
+ void setUnitEventHandler(UnitEventHandler Handler);
+ /// \returns true if an error occurred.
+ bool startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn,
+ bool waitInitialSync, std::string &Error);
+ void stopEventListening();
+
+ void discardUnit(StringRef UnitName);
+ void discardRecord(StringRef RecordName);
+
+ void purgeStaleData();
+
+private:
+ IndexDataStore(void *Impl) : Impl(Impl) {}
+
+ void *Impl; // An IndexDataStoreImpl.
+};
+
+} // namespace index
+} // namespace clang
+
+#endif
diff --git a/include/clang/Index/IndexDataStoreSymbolUtils.h b/include/clang/Index/IndexDataStoreSymbolUtils.h
new file mode 100644
index 0000000..e1d982d
--- /dev/null
+++ b/include/clang/Index/IndexDataStoreSymbolUtils.h
@@ -0,0 +1,53 @@
+//===--- IndexDataStoreSymbolUtils.h - Utilities for indexstore symbols ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H
+#define LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H
+
+#include "indexstore/indexstore.h"
+#include "clang/Index/IndexSymbol.h"
+
+namespace clang {
+namespace index {
+
+/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values.
+SymbolKind getSymbolKind(indexstore_symbol_kind_t K);
+
+SymbolSubKind getSymbolSubKind(indexstore_symbol_subkind_t K);
+
+/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown
+/// values.
+SymbolLanguage getSymbolLanguage(indexstore_symbol_language_t L);
+
+/// Map an indexstore representation to a SymbolPropertySet, handling
+/// unknown values.
+SymbolPropertySet getSymbolProperties(uint64_t Props);
+
+/// Map an indexstore representation to a SymbolRoleSet, handling unknown
+/// values.
+SymbolRoleSet getSymbolRoles(uint64_t Roles);
+
+/// Map a SymbolLanguage to a indexstore_symbol_language_t.
+indexstore_symbol_kind_t getIndexStoreKind(SymbolKind K);
+
+indexstore_symbol_subkind_t getIndexStoreSubKind(SymbolSubKind K);
+
+/// Map a SymbolLanguage to a indexstore_symbol_language_t.
+indexstore_symbol_language_t getIndexStoreLang(SymbolLanguage L);
+
+/// Map a SymbolPropertySet to its indexstore representation.
+uint64_t getIndexStoreProperties(SymbolPropertySet Props);
+
+/// Map a SymbolRoleSet to its indexstore representation.
+uint64_t getIndexStoreRoles(SymbolRoleSet Roles);
+
+} // end namespace index
+} // end namespace clang
+
+#endif // LLVM_CLANG_INDEX_INDEXDATASTORESYMBOLUTILS_H
diff --git a/include/clang/Index/IndexRecordReader.h b/include/clang/Index/IndexRecordReader.h
new file mode 100644
index 0000000..ef8edff
--- /dev/null
+++ b/include/clang/Index/IndexRecordReader.h
@@ -0,0 +1,109 @@
+//===--- IndexRecordReader.h - Index record deserialization ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXRECORDREADER_H
+#define LLVM_CLANG_INDEX_INDEXRECORDREADER_H
+
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <memory>
+
+namespace llvm {
+ class MemoryBuffer;
+}
+
+namespace clang {
+namespace index {
+
+struct IndexRecordDecl {
+ unsigned DeclID;
+ SymbolInfo SymInfo;
+ SymbolRoleSet Roles;
+ SymbolRoleSet RelatedRoles;
+ StringRef Name;
+ StringRef USR;
+ StringRef CodeGenName;
+};
+
+struct IndexRecordRelation {
+ SymbolRoleSet Roles;
+ const IndexRecordDecl *Dcl = nullptr;
+
+ IndexRecordRelation() = default;
+ IndexRecordRelation(SymbolRoleSet Roles, const IndexRecordDecl *Dcl)
+ : Roles(Roles), Dcl(Dcl) {}
+};
+
+struct IndexRecordOccurrence {
+ const IndexRecordDecl *Dcl;
+ SmallVector<IndexRecordRelation, 4> Relations;
+ SymbolRoleSet Roles;
+ unsigned Line;
+ unsigned Column;
+};
+
+class IndexRecordReader {
+ IndexRecordReader();
+
+public:
+ static std::unique_ptr<IndexRecordReader>
+ createWithRecordFilename(StringRef RecordFilename, StringRef StorePath,
+ std::string &Error);
+ static std::unique_ptr<IndexRecordReader>
+ createWithFilePath(StringRef FilePath, std::string &Error);
+ static std::unique_ptr<IndexRecordReader>
+ createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer,
+ std::string &Error);
+
+ ~IndexRecordReader();
+
+ struct DeclSearchReturn {
+ bool AcceptDecl;
+ bool ContinueSearch;
+ };
+ typedef DeclSearchReturn(DeclSearchCheck)(const IndexRecordDecl &);
+
+ /// Goes through and passes record decls, after filtering using a \c Checker
+ /// function.
+ ///
+ /// Resulting decls can be used as filter for \c foreachOccurrence. This
+ /// allows allocating memory only for the record decls that the caller is
+ /// interested in.
+ bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker,
+ llvm::function_ref<void(const IndexRecordDecl *)> Receiver);
+
+ /// \param NoCache if true, avoids allocating memory for the decls.
+ /// Useful when the caller does not intend to keep \c IndexRecordReader
+ /// for more queries.
+ bool foreachDecl(bool NoCache,
+ llvm::function_ref<bool(const IndexRecordDecl *)> Receiver);
+
+ /// \param DeclsFilter if non-empty indicates the list of decls that we want
+ /// to get occurrences for. An empty array indicates that we want occurrences
+ /// for all decls.
+ /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls.
+ bool foreachOccurrence(ArrayRef<const IndexRecordDecl *> DeclsFilter,
+ ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
+ llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver);
+ bool foreachOccurrence(
+ llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver);
+
+ bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount,
+ llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver);
+
+ struct Implementation;
+private:
+ Implementation &Impl;
+};
+
+} // namespace index
+} // namespace clang
+
+#endif
diff --git a/include/clang/Index/IndexRecordWriter.h b/include/clang/Index/IndexRecordWriter.h
new file mode 100644
index 0000000..8a9720a
--- /dev/null
+++ b/include/clang/Index/IndexRecordWriter.h
@@ -0,0 +1,102 @@
+//===--- IndexRecordWriter.h - Index record serialization -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXRECORDWRITER_H
+#define LLVM_CLANG_INDEX_INDEXRECORDWRITER_H
+
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/SmallString.h"
+
+namespace clang {
+namespace index {
+
+namespace writer {
+/// An opaque pointer to a declaration or other symbol used by the
+/// IndexRecordWriter to identify when two occurrences refer to the same symbol,
+/// and as a token for getting information about a symbol from the caller.
+typedef const void *OpaqueDecl;
+
+/// An indexer symbol suitable for serialization.
+///
+/// This includes all the information about the symbol that will be serialized
+/// except for roles, which are synthesized by looking at all the occurrences.
+///
+/// \seealso IndexRecordDecl
+/// \note this struct is generally accompanied by a buffer that owns the string
+/// storage. It should not be stored permanently.
+struct Symbol {
+ SymbolInfo SymInfo;
+ StringRef Name;
+ StringRef USR;
+ StringRef CodeGenName;
+};
+
+/// An relation to an opaque symbol.
+/// \seealso IndexRecordRelation
+struct SymbolRelation {
+ OpaqueDecl RelatedSymbol;
+ SymbolRoleSet Roles;
+};
+
+typedef llvm::function_ref<Symbol(OpaqueDecl, SmallVectorImpl<char> &Scratch)>
+ SymbolWriterCallback;
+} // end namespace writer
+
+/// A language-independent utility for serializing index record files.
+///
+/// Internally, this class is a small state machine. Users should first call
+/// beginRecord, and if the file does not already exist, then proceed to add
+/// all symbol occurrences (addOccurrence) and finally finish with endRecord.
+class IndexRecordWriter {
+ SmallString<64> RecordsPath; ///< The records directory path.
+ void *Record = nullptr; ///< The state of the current record.
+public:
+ IndexRecordWriter(StringRef IndexPath);
+
+ enum class Result {
+ Success,
+ Failure,
+ AlreadyExists,
+ };
+
+ /// Begin writing a record for the file \p Filename with contents uniquely
+ /// identified by \p RecordHash.
+ ///
+ /// \param Filename the name of the file this is a record for.
+ /// \param RecordHash the unique hash of the record contents.
+ /// \param Error on failure, set to the error message.
+ /// \param RecordFile if non-null, this is set to the name of the record file.
+ ///
+ /// \returns Success if we should continue writing this record, AlreadyExists
+ /// if the record file has already been written, or Failure if there was an
+ /// error, in which case \p Error will be set.
+ Result beginRecord(StringRef Filename, llvm::hash_code RecordHash,
+ std::string &Error, std::string *RecordFile = nullptr);
+
+ /// Finish writing the record file.
+ ///
+ /// \param Error on failure, set to the error message.
+ /// \param GetSymbolForDecl a callback mapping an writer::OpaqueDecl to its
+ /// writer::Symbol. This is how the language-specific symbol information is
+ /// provided to the IndexRecordWriter. The scratch parameter can be used for
+ /// any necessary storage.
+ ///
+ /// \return Success, or Failure and sets \p Error.
+ Result endRecord(std::string &Error,
+ writer::SymbolWriterCallback GetSymbolForDecl);
+
+ /// Add an occurrence of the symbol \p D with the given \p Roles and location.
+ void addOccurrence(writer::OpaqueDecl D, SymbolRoleSet Roles, unsigned Line,
+ unsigned Column, ArrayRef<writer::SymbolRelation> Related);
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif // LLVM_CLANG_INDEX_INDEXRECORDWRITER_H
diff --git a/include/clang/Index/IndexSymbol.h b/include/clang/Index/IndexSymbol.h
index abb132f..2aa9b6b 100644
--- a/include/clang/Index/IndexSymbol.h
+++ b/include/clang/Index/IndexSymbol.h
@@ -53,6 +53,9 @@
ConversionFunction,
Parameter,
+ Using,
+
+ CommentTag,
};
enum class SymbolLanguage {
@@ -69,6 +72,28 @@
CXXMoveConstructor,
AccessorGetter,
AccessorSetter,
+ UsingTypename,
+ UsingValue,
+
+ // Swift sub-kinds
+
+ SwiftAccessorWillSet,
+ SwiftAccessorDidSet,
+ SwiftAccessorAddressor,
+ SwiftAccessorMutableAddressor,
+
+ SwiftExtensionOfStruct,
+ SwiftExtensionOfClass,
+ SwiftExtensionOfEnum,
+ SwiftExtensionOfProtocol,
+
+ SwiftPrefixOperator,
+ SwiftPostfixOperator,
+ SwiftInfixOperator,
+
+ SwiftSubscript,
+ SwiftAssociatedType,
+ SwiftGenericTypeParam,
};
/// Set of properties that provide additional info about a symbol.
diff --git a/include/clang/Index/IndexUnitReader.h b/include/clang/Index/IndexUnitReader.h
new file mode 100644
index 0000000..ccd2dce
--- /dev/null
+++ b/include/clang/Index/IndexUnitReader.h
@@ -0,0 +1,85 @@
+//===--- IndexUnitReader.h - Index unit deserialization -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXUNITREADER_H
+#define LLVM_CLANG_INDEX_INDEXUNITREADER_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Chrono.h"
+
+namespace clang {
+namespace index {
+
+class IndexUnitReader {
+public:
+ enum class DependencyKind {
+ Unit,
+ Record,
+ File,
+ };
+
+ ~IndexUnitReader();
+
+ static std::unique_ptr<IndexUnitReader>
+ createWithUnitFilename(StringRef UnitFilename, StringRef StorePath,
+ std::string &Error);
+ static std::unique_ptr<IndexUnitReader>
+ createWithFilePath(StringRef FilePath, std::string &Error);
+
+ static Optional<llvm::sys::TimePoint<>>
+ getModificationTimeForUnit(StringRef UnitFilename, StringRef StorePath,
+ std::string &Error);
+
+ StringRef getProviderIdentifier() const;
+ StringRef getProviderVersion() const;
+
+ llvm::sys::TimePoint<> getModificationTime() const;
+ StringRef getWorkingDirectory() const;
+ StringRef getOutputFile() const;
+ StringRef getSysrootPath() const;
+ StringRef getMainFilePath() const;
+ StringRef getModuleName() const;
+ StringRef getTarget() const;
+ bool hasMainFile() const;
+ bool isSystemUnit() const;
+ bool isModuleUnit() const;
+ bool isDebugCompilation() const;
+
+ struct DependencyInfo {
+ DependencyKind Kind;
+ bool IsSystem;
+ StringRef UnitOrRecordName;
+ StringRef FilePath;
+ StringRef ModuleName;
+ size_t FileSize;
+ time_t ModTime;
+ };
+ struct IncludeInfo {
+ StringRef SourcePath;
+ unsigned SourceLine;
+ StringRef TargetPath;
+ };
+ /// Unit dependencies are provided ahead of record ones, record ones
+ /// ahead of the file ones.
+ bool foreachDependency(llvm::function_ref<bool(const DependencyInfo &Info)> Receiver);
+
+ bool foreachInclude(llvm::function_ref<bool(const IncludeInfo &Info)> Receiver);
+
+private:
+ IndexUnitReader(void *Impl) : Impl(Impl) {}
+
+ void *Impl; // An IndexUnitReaderImpl.
+};
+
+} // namespace index
+} // namespace clang
+
+#endif
diff --git a/include/clang/Index/IndexUnitWriter.h b/include/clang/Index/IndexUnitWriter.h
new file mode 100644
index 0000000..40d2c11
--- /dev/null
+++ b/include/clang/Index/IndexUnitWriter.h
@@ -0,0 +1,140 @@
+//===--- IndexUnitWriter.h - Index unit serialization ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXUNITWRITER_H
+#define LLVM_CLANG_INDEX_INDEXUNITWRITER_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallString.h"
+#include <string>
+#include <vector>
+
+namespace llvm {
+ class BitstreamWriter;
+}
+
+namespace clang {
+ class FileEntry;
+ class FileManager;
+
+namespace index {
+
+namespace writer {
+/// An opaque pointer to a module used by the IndexUnitWriter to associate
+/// record and file dependencies with a module, and as a token for getting
+/// information about the module from the caller.
+typedef const void *OpaqueModule;
+
+/// Module info suitable for serialization.
+///
+/// This is used for top-level modules and sub-modules.
+struct ModuleInfo {
+ /// Full, dot-separate, module name.
+ StringRef Name;
+};
+
+typedef llvm::function_ref<ModuleInfo(OpaqueModule, SmallVectorImpl<char> &Scratch)>
+ ModuleInfoWriterCallback;
+} // end namespace writer
+
+class IndexUnitWriter {
+ FileManager &FileMgr;
+ SmallString<64> UnitsPath;
+ std::string ProviderIdentifier;
+ std::string ProviderVersion;
+ std::string OutputFile;
+ std::string ModuleName;
+ const FileEntry *MainFile;
+ bool IsSystemUnit;
+ bool IsModuleUnit;
+ bool IsDebugCompilation;
+ std::string TargetTriple;
+ std::string WorkDir;
+ std::string SysrootPath;
+ std::function<writer::ModuleInfo(writer::OpaqueModule,
+ SmallVectorImpl<char> &Scratch)> GetInfoForModuleFn;
+ struct FileInclude {
+ int Index;
+ unsigned Line;
+ };
+ struct FileEntryData {
+ const FileEntry *File;
+ bool IsSystem;
+ int ModuleIndex;
+ std::vector<FileInclude> Includes;
+ };
+ std::vector<FileEntryData> Files;
+ std::vector<writer::OpaqueModule> Modules;
+ llvm::DenseMap<const FileEntry *, int> IndexByFile;
+ llvm::DenseMap<writer::OpaqueModule, int> IndexByModule;
+ llvm::DenseSet<const FileEntry *> SeenASTFiles;
+ struct RecordOrUnitData {
+ std::string Name;
+ int FileIndex;
+ int ModuleIndex;
+ bool IsSystem;
+ };
+ std::vector<RecordOrUnitData> Records;
+ std::vector<RecordOrUnitData> ASTFileUnits;
+
+public:
+ /// \param MainFile the main file for a compiled source file. This should be
+ /// null for PCH and module units.
+ /// \param IsSystem true for system module units, false otherwise.
+ IndexUnitWriter(FileManager &FileMgr,
+ StringRef StorePath,
+ StringRef ProviderIdentifier, StringRef ProviderVersion,
+ StringRef OutputFile,
+ StringRef ModuleName,
+ const FileEntry *MainFile,
+ bool IsSystem,
+ bool IsModuleUnit,
+ bool IsDebugCompilation,
+ StringRef TargetTriple,
+ StringRef SysrootPath,
+ writer::ModuleInfoWriterCallback GetInfoForModule);
+ ~IndexUnitWriter();
+
+ int addFileDependency(const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod);
+ void addRecordFile(StringRef RecordFile, const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod);
+ void addASTFileDependency(const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod, bool withoutUnitName = false);
+ void addUnitDependency(StringRef UnitFile, const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod);
+ bool addInclude(const FileEntry *Source, unsigned Line, const FileEntry *Target);
+
+ bool write(std::string &Error);
+
+ void getUnitNameForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str);
+ void getUnitPathForOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str);
+ /// If the unit file exists and \p timeCompareFilePath is provided, it will
+ /// return true if \p timeCompareFilePath is older than the unit file.
+ Optional<bool> isUnitUpToDateForOutputFile(StringRef FilePath,
+ Optional<StringRef> TimeCompareFilePath,
+ std::string &Error);
+ static void getUnitNameForAbsoluteOutputFile(StringRef FilePath, SmallVectorImpl<char> &Str);
+ static bool initIndexDirectory(StringRef StorePath, std::string &Error);
+
+private:
+ class PathStorage;
+ int addModule(writer::OpaqueModule Mod);
+ void writeUnitInfo(llvm::BitstreamWriter &Stream, PathStorage &PathStore);
+ void writeDependencies(llvm::BitstreamWriter &Stream, PathStorage &PathStore);
+ void writeIncludes(llvm::BitstreamWriter &Stream, PathStorage &PathStore);
+ void writePaths(llvm::BitstreamWriter &Stream, PathStorage &PathStore);
+ void writeModules(llvm::BitstreamWriter &Stream);
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/include/clang/Index/IndexingAction.h b/include/clang/Index/IndexingAction.h
index fb703be..6095e67 100644
--- a/include/clang/Index/IndexingAction.h
+++ b/include/clang/Index/IndexingAction.h
@@ -13,13 +13,17 @@
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include <memory>
+#include <string>
namespace clang {
class ASTContext;
class ASTReader;
class ASTUnit;
+ class CompilerInstance;
class Decl;
class FrontendAction;
+ class FrontendOptions;
+ class Module;
namespace serialization {
class ModuleFile;
@@ -27,6 +31,7 @@
namespace index {
class IndexDataConsumer;
+ class IndexUnitWriter;
struct IndexingOptions {
enum class SystemSymbolFilterKind {
@@ -40,6 +45,19 @@
bool IndexFunctionLocals = false;
};
+struct RecordingOptions {
+ enum class IncludesRecordingKind {
+ None,
+ UserOnly, // only record includes inside non-system files.
+ All,
+ };
+
+ std::string DataDirPath;
+ bool RecordSymbolCodeGenName = false;
+ bool RecordSystemDependencies = true;
+ IncludesRecordingKind RecordIncludes = IncludesRecordingKind::UserOnly;
+};
+
/// \param WrappedAction another frontend action to wrap over or null.
std::unique_ptr<FrontendAction>
createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer,
@@ -58,6 +76,18 @@
std::shared_ptr<IndexDataConsumer> DataConsumer,
IndexingOptions Opts);
+/// \param WrappedAction another frontend action to wrap over or null.
+std::unique_ptr<FrontendAction>
+createIndexDataRecordingAction(const FrontendOptions &FEOpts,
+ std::unique_ptr<FrontendAction> WrappedAction);
+
+/// Checks if the unit file exists for the module file, if it doesn't it
+/// generates index data for it.
+///
+/// \returns true if the index data were generated, false otherwise.
+bool emitIndexDataForModuleFile(const Module *Mod, const CompilerInstance &CI,
+ IndexUnitWriter &ParentUnitWriter);
+
} // namespace index
} // namespace clang
diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h
index 3be7331..2a229f6 100644
--- a/include/clang/Lex/Lexer.h
+++ b/include/clang/Lex/Lexer.h
@@ -133,15 +133,17 @@
/// from. Currently this is only used by _Pragma handling.
SourceLocation getFileLoc() const { return FileLoc; }
-private:
/// Lex - Return the next token in the file. If this is the end of file, it
/// return the tok::eof token. This implicitly involves the preprocessor.
bool Lex(Token &Result);
-public:
/// isPragmaLexer - Returns true if this Lexer is being used to lex a pragma.
bool isPragmaLexer() const { return Is_PragmaLexer; }
+ /// Note that this Lexer is being used to lex a pragma, or something like it
+ /// that has simple end-of-file behavior.
+ void setIsPragmaLexer(bool value) { Is_PragmaLexer = value; }
+
private:
/// IndirectLex - An indirect call to 'Lex' that can be invoked via
/// the PreprocessorLexer interface.
@@ -460,6 +462,14 @@
const LangOptions &LangOpts,
bool SkipTrailingWhitespaceAndNewLine);
+ /// \brief Returns the source location of the token that comes after the
+ /// token located at the given location \p Loc (excluding any comments and
+ /// whitespace). The returned source location will be invalid if the location
+ /// is inside a macro.
+ static SourceLocation
+ findNextTokenLocationAfterTokenAt(SourceLocation Loc, const SourceManager &SM,
+ const LangOptions &LangOpts);
+
/// \brief Returns true if the given character could appear in an identifier.
static bool isIdentifierBodyChar(char c, const LangOptions &LangOpts);
diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h
index d25431b..47f10d6 100644
--- a/include/clang/Lex/MacroInfo.h
+++ b/include/clang/Lex/MacroInfo.h
@@ -510,6 +510,9 @@
ID.AddPointer(II);
}
+ /// Get the name of the macro.
+ IdentifierInfo *getName() const { return II; }
+
/// Get the ID of the module that exports this macro.
Module *getOwningModule() const { return OwningModule; }
diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h
index 1150693..eb224a6 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -93,7 +93,7 @@
// named LangOpts::CurrentModule, if we've loaded it).
Module *SourceModule;
- /// \brief The top-level modules that are known.
+ /// \brief The unshadowed top-level modules that are known.
llvm::StringMap<Module *> Modules;
/// \brief The number of modules we have created in total.
@@ -186,6 +186,15 @@
/// header.
llvm::DenseMap<const DirectoryEntry *, Module *> UmbrellaDirs;
+ /// \brief A generation counter that is used to test whether modules of the
+ /// same name may shadow or are illegal redefintions.
+ ///
+ /// Modules from earlier scopes may shadow modules from later ones.
+ /// Modules from the same scope may not have the same name.
+ unsigned CurrentModuleScopeID = 0;
+
+ llvm::DenseMap<Module *, unsigned> ModuleScopeIDs;
+
/// \brief The set of attributes that can be attached to a module.
struct Attributes {
Attributes()
@@ -200,6 +209,9 @@
/// \brief Whether this is an exhaustive set of configuration macros.
unsigned IsExhaustive : 1;
+ /// \brief Whether this is a module who has its swift_names inferred.
+ unsigned IsSwiftInferImportAsMember : 1;
+
/// \brief Whether files in this module can only include non-modular headers
/// and headers from used modules.
unsigned NoUndeclaredIncludes : 1;
@@ -340,6 +352,8 @@
const LangOptions &LangOpts, const TargetInfo *Target,
HeaderSearch &HeaderInfo);
+ const LangOptions &getLangOpts() const { return LangOpts; }
+
/// \brief Destroy the module map.
///
~ModuleMap();
@@ -486,6 +500,24 @@
Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,
bool IsSystem, Module *Parent);
+ /// \brief Create a new top-level module that is shadowed by
+ /// \p ShadowingModule.
+ Module *createShadowedModule(StringRef Name, bool IsFramework,
+ Module *ShadowingModule);
+
+ /// \brief Creates a new declaration scope for module names, allowing
+ /// previously defined modules to shadow definitions from the new scope.
+ ///
+ /// \note Module names from earlier scopes will shadow names from the new
+ /// scope, which is the opposite of how shadowing works for variables.
+ void finishModuleDeclarationScope() { CurrentModuleScopeID += 1; }
+
+ bool mayShadowNewModule(Module *ExistingModule) {
+ assert(!ExistingModule->Parent && "expected top-level module");
+ assert(ModuleScopeIDs.count(ExistingModule) && "unknown module");
+ return ModuleScopeIDs[ExistingModule] < CurrentModuleScopeID;
+ }
+
/// \brief Retrieve the module map file containing the definition of the given
/// module.
///
diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h
index 81c3bd7..512a32e 100644
--- a/include/clang/Lex/PPCallbacks.h
+++ b/include/clang/Lex/PPCallbacks.h
@@ -266,7 +266,10 @@
/// \brief Hook called when a source range is skipped.
/// \param Range The SourceRange that was skipped. The range begins at the
/// \#if/\#else directive and ends after the \#endif/\#else directive.
- virtual void SourceRangeSkipped(SourceRange Range) {
+ /// \param EndifLoc The end location of the 'endif' token, which may precede
+ /// the range skipped by the directive (e.g excluding comments after an
+ /// 'endif').
+ virtual void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) {
}
enum ConditionValueKind {
@@ -462,9 +465,9 @@
Second->Defined(MacroNameTok, MD, Range);
}
- void SourceRangeSkipped(SourceRange Range) override {
- First->SourceRangeSkipped(Range);
- Second->SourceRangeSkipped(Range);
+ void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override {
+ First->SourceRangeSkipped(Range, EndifLoc);
+ Second->SourceRangeSkipped(Range, EndifLoc);
}
/// \brief Hook called whenever an \#if is seen.
diff --git a/include/clang/Lex/PreprocessingRecord.h b/include/clang/Lex/PreprocessingRecord.h
index fc2c507..882b8ae 100644
--- a/include/clang/Lex/PreprocessingRecord.h
+++ b/include/clang/Lex/PreprocessingRecord.h
@@ -504,7 +504,8 @@
void Defined(const Token &MacroNameTok, const MacroDefinition &MD,
SourceRange Range) override;
- void SourceRangeSkipped(SourceRange Range) override;
+ void SourceRangeSkipped(SourceRange Range,
+ SourceLocation EndifLoc) override;
void addMacroExpansion(const Token &Id, const MacroInfo *MI,
SourceRange Range);
diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h
index dba4b80..7107cc9 100644
--- a/include/clang/Lex/Preprocessor.h
+++ b/include/clang/Lex/Preprocessor.h
@@ -1836,7 +1836,8 @@
/// \p FoundElse is false, then \#else directives are ok, if not, then we have
/// already seen one so a \#else directive is a duplicate. When this returns,
/// the caller can lex the first valid token.
- void SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
+ void SkipExcludedConditionalBlock(const Token &HashToken,
+ SourceLocation IfTokenLoc,
bool FoundNonSkipPortion, bool FoundElse,
SourceLocation ElseLoc = SourceLocation());
@@ -2030,12 +2031,13 @@
void HandleUndefDirective();
// Conditional Inclusion.
- void HandleIfdefDirective(Token &Tok, bool isIfndef,
- bool ReadAnyTokensBeforeDirective);
- void HandleIfDirective(Token &Tok, bool ReadAnyTokensBeforeDirective);
+ void HandleIfdefDirective(Token &Tok, const Token &HashToken,
+ bool isIfndef, bool ReadAnyTokensBeforeDirective);
+ void HandleIfDirective(Token &Tok, const Token &HashToken,
+ bool ReadAnyTokensBeforeDirective);
void HandleEndifDirective(Token &Tok);
- void HandleElseDirective(Token &Tok);
- void HandleElifDirective(Token &Tok);
+ void HandleElseDirective(Token &Tok, const Token &HashToken);
+ void HandleElifDirective(Token &Tok, const Token &HashToken);
// Pragmas.
void HandlePragmaDirective(SourceLocation IntroducerLoc,
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 21d699e..91f7028 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -2362,6 +2362,14 @@
SourceLocation ScopeLoc,
AttributeList::Syntax Syntax);
+ void ParseSwiftNewtypeAttribute(IdentifierInfo &SwiftNewtype,
+ SourceLocation SwiftNewtypeLoc,
+ ParsedAttributes &attrs,
+ SourceLocation *endLoc,
+ IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc,
+ AttributeList::Syntax Syntax);
+
void ParseAttributeWithTypeArg(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
@@ -2784,7 +2792,19 @@
//===--------------------------------------------------------------------===//
// C++11/G++: Type Traits [Type-Traits.html in the GCC manual]
ExprResult ParseTypeTrait();
-
+
+ /// Parse the given string as a type.
+ ///
+ /// This is a dangerous utility function currently employed only by API notes.
+ /// It is not a general entry-point for safely parsing types from strings.
+ ///
+ /// \param typeStr The string to be parsed as a type.
+ /// \param context The name of the context in which this string is being
+ /// parsed, which will be used in diagnostics.
+ /// \param includeLoc The location at which this parse was triggered.
+ TypeResult parseTypeFromString(StringRef typeStr, StringRef context,
+ SourceLocation includeLoc);
+
//===--------------------------------------------------------------------===//
// Embarcadero: Arary and Expression Traits
ExprResult ParseArrayTypeTrait();
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 5a70854..239017d 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -28,6 +28,7 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/APINotes/APINotesManager.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/ExpressionTraits.h"
#include "clang/Basic/LangOptions.h"
@@ -55,6 +56,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/TinyPtrVector.h"
#include <deque>
+#include <functional>
#include <memory>
#include <string>
#include <vector>
@@ -306,6 +308,7 @@
ASTConsumer &Consumer;
DiagnosticsEngine &Diags;
SourceManager &SourceMgr;
+ api_notes::APINotesManager APINotes;
/// \brief Flag indicating whether or not to collect detailed statistics.
bool CollectStats;
@@ -618,6 +621,10 @@
OpaqueParser = P;
}
+ /// \brief Callback to the parser to parse a type expressed as a string.
+ std::function<TypeResult(StringRef, StringRef, SourceLocation)>
+ ParseTypeFromStringCallback;
+
class DelayedDiagnostics;
class DelayedDiagnosticsState {
@@ -1499,6 +1506,24 @@
}
};
+ /// Do a check to make sure \p Name looks like a legal swift_name
+ /// attribute for the decl \p D. Raise a diagnostic if the name is invalid
+ /// for the given declaration.
+ ///
+ /// For a function, this will validate a compound Swift name,
+ /// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>,
+ /// and the function will output the number of parameter names, and whether
+ /// this is a single-arg initializer.
+ ///
+ /// For a type, enum constant, property, or variable declaration, this will
+ /// validate either a simple identifier, or a qualified
+ /// <code>context.identifier</code> name.
+ ///
+ /// \returns true if the name is a valid swift name for \p D, false otherwise.
+ bool DiagnoseSwiftName(Decl *D, StringRef Name,
+ SourceLocation ArgLoc,
+ IdentifierInfo *AttrName);
+
private:
bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T,
TypeDiagnoser *Diagnoser);
@@ -1920,6 +1945,8 @@
ParmVarDecl *BuildParmVarDeclForTypedef(DeclContext *DC,
SourceLocation Loc,
QualType T);
+ QualType adjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation Loc);
ParmVarDecl *CheckParameter(DeclContext *DC, SourceLocation StartLoc,
SourceLocation NameLoc, IdentifierInfo *Name,
QualType T, TypeSourceInfo *TSInfo,
@@ -2411,6 +2438,9 @@
unsigned AttrSpellingListIndex);
OptimizeNoneAttr *mergeOptimizeNoneAttr(Decl *D, SourceRange Range,
unsigned AttrSpellingListIndex);
+ SwiftNameAttr *mergeSwiftNameAttr(Decl *D, SourceRange Range,
+ StringRef Name, bool Override,
+ unsigned AttrSpellingListIndex);
InternalLinkageAttr *mergeInternalLinkageAttr(Decl *D, SourceRange Range,
IdentifierInfo *Ident,
unsigned AttrSpellingListIndex);
@@ -3256,6 +3286,12 @@
void checkUnusedDeclAttributes(Declarator &D);
+ /// Map any API notes provided for this declaration to attributes on the
+ /// declaration.
+ ///
+ /// Triggered by declaration-attribute processing.
+ void ProcessAPINotes(Decl *D);
+
/// Determine if type T is a valid subject for a nonnull and similar
/// attributes. By default, we look through references (the behavior used by
/// nonnull), but if the second parameter is true, then we treat a reference
@@ -3311,11 +3347,16 @@
/// \param allowArrayTypes Whether to accept nullability specifiers on an
/// array type (e.g., because it will decay to a pointer).
///
+ /// \param overrideExisting Whether to override an existing, locally-specified
+ /// nullability specifier rather than complaining about the conflict.
+ ///
/// \returns true if nullability cannot be applied, false otherwise.
bool checkNullabilityTypeSpecifier(QualType &type, NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive,
- bool allowArrayTypes);
+ bool allowArrayTypes,
+ bool implicit,
+ bool overrideExisting = false);
/// \brief Stmt attributes - this routine is the top level dispatcher.
StmtResult ProcessStmtAttributes(Stmt *Stmt, AttributeList *Attrs,
@@ -8156,6 +8197,12 @@
RTC_Unknown
};
+ /// Check whether the declared result type of the given Objective-C
+ /// method declaration is compatible with the method's class.
+ ResultTypeCompatibilityKind
+ checkRelatedResultTypeCompatibility(const ObjCMethodDecl *Method,
+ const ObjCInterfaceDecl *CurrentClass);
+
void CheckObjCMethodOverrides(ObjCMethodDecl *ObjCMethod,
ObjCInterfaceDecl *CurrentClass,
ResultTypeCompatibilityKind RTC);
@@ -8507,6 +8554,11 @@
/// is performed.
bool isOpenMPPrivateDecl(ValueDecl *D, unsigned Level);
+ /// Sets OpenMP capture kind (OMPC_private, OMPC_firstprivate, OMPC_map etc.)
+ /// for \p FD based on DSA for the provided corresponding captured declaration
+ /// \p D.
+ void setOpenMPCaptureKind(FieldDecl *FD, ValueDecl *D, unsigned Level);
+
/// \brief Check if the specified variable is captured by 'target' directive.
/// \param Level Relative level of nested OpenMP construct for that the check
/// is performed.
@@ -10396,6 +10448,7 @@
/// The struct behind the CFErrorRef pointer.
RecordDecl *CFError = nullptr;
+ bool isCFError(RecordDecl *D);
/// Retrieve the identifier "NSError".
IdentifierInfo *getNSErrorIdent();
@@ -10446,6 +10499,36 @@
SmallVector<CXXRecordDecl*, 4> DelayedDllExportClasses;
private:
+ class SavePendingParsedClassStateRAII {
+ public:
+ SavePendingParsedClassStateRAII(Sema &S) : S(S) { swapSavedState(); }
+
+ ~SavePendingParsedClassStateRAII() {
+ assert(S.DelayedExceptionSpecChecks.empty() &&
+ "there shouldn't be any pending delayed exception spec checks");
+ assert(S.DelayedDefaultedMemberExceptionSpecs.empty() &&
+ "there shouldn't be any pending delayed defaulted member "
+ "exception specs");
+ assert(S.DelayedDllExportClasses.empty() &&
+ "there shouldn't be any pending delayed DLL export classes");
+ swapSavedState();
+ }
+
+ private:
+ Sema &S;
+ decltype(DelayedExceptionSpecChecks) SavedExceptionSpecChecks;
+ decltype(DelayedDefaultedMemberExceptionSpecs)
+ SavedDefaultedMemberExceptionSpecs;
+ decltype(DelayedDllExportClasses) SavedDllExportClasses;
+
+ void swapSavedState() {
+ SavedExceptionSpecChecks.swap(S.DelayedExceptionSpecChecks);
+ SavedDefaultedMemberExceptionSpecs.swap(
+ S.DelayedDefaultedMemberExceptionSpecs);
+ SavedDllExportClasses.swap(S.DelayedDllExportClasses);
+ }
+ };
+
/// \brief Helper class that collects misaligned member designations and
/// their location info for delayed diagnostics.
struct MisalignedMember {
diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h
index 9227b33..f0f17c9 100644
--- a/include/clang/Serialization/ASTBitCodes.h
+++ b/include/clang/Serialization/ASTBitCodes.h
@@ -716,6 +716,9 @@
/// \brief Specifies some declarations with initializers that must be
/// emitted to initialize the module.
SUBMODULE_INITIALIZERS = 16,
+ /// \brief Specifies the name of the module that will eventually
+ /// re-export the entities in this module.
+ SUBMODULE_EXPORT_AS = 17,
};
/// \brief Record types used within a comments block.
diff --git a/include/clang/Tooling/Core/RefactoringDiagnostic.h b/include/clang/Tooling/Core/RefactoringDiagnostic.h
new file mode 100644
index 0000000..a179260
--- /dev/null
+++ b/include/clang/Tooling/Core/RefactoringDiagnostic.h
@@ -0,0 +1,29 @@
+//===--- RefactoringDiagnostic.h - ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_CORE_REFACTORINGSTARTDIAGNOSTIC_H
+#define LLVM_CLANG_TOOLING_CORE_REFACTORINGSTARTDIAGNOSTIC_H
+
+#include "clang/Basic/Diagnostic.h"
+
+namespace clang {
+namespace diag {
+enum {
+#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR, \
+ SHOWINSYSHEADER, CATEGORY) \
+ ENUM,
+#define REFACTORINGSTART
+#include "clang/Basic/DiagnosticRefactoringKinds.inc"
+#undef DIAG
+ NUM_BUILTIN_REFACTORING_DIAGNOSTICS
+};
+} // end namespace diag
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_CORE_REFACTORINGSTARTDIAGNOSTIC_H
diff --git a/include/clang/Tooling/Refactor/IndexerQuery.h b/include/clang/Tooling/Refactor/IndexerQuery.h
new file mode 100644
index 0000000..4872893
--- /dev/null
+++ b/include/clang/Tooling/Refactor/IndexerQuery.h
@@ -0,0 +1,310 @@
+//===--- IndexerQuery.h - A set of indexer query interfaces ---------------===//
+//
+// 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 base indexer queries that can be used with
+// refactoring continuations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H
+#define LLVM_CLANG_TOOLING_REFACTOR_INDEXER_QUERY_H
+
+#include "clang/Tooling/Refactor/RefactoringOperationState.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include <vector>
+
+namespace clang {
+namespace tooling {
+namespace indexer {
+
+/// Represents an abstract indexer query.
+class IndexerQuery {
+public:
+ const char *BaseUID;
+ const char *NameUID;
+
+ IndexerQuery(const char *BaseUID, const char *NameUID)
+ : BaseUID(BaseUID), NameUID(NameUID) {}
+ virtual ~IndexerQuery() {}
+
+ virtual void invalidateTUSpecificState() = 0;
+
+ /// Checks if this query was satisfied. Returns true if it wasn't and reports
+ /// appropriate errors.
+ virtual bool verify(ASTContext &) { return false; }
+
+ // Mainly used for testing.
+ static llvm::Error loadResultsFromYAML(StringRef Source,
+ ArrayRef<IndexerQuery *> Queries);
+
+ static bool classof(const IndexerQuery *) { return true; }
+};
+
+/// An abstract AST query that can produce an AST unit in which the refactoring
+/// continuation will run.
+class ASTProducerQuery : public IndexerQuery {
+ static const char *BaseUIDString;
+
+public:
+ /// Deriving AST producer queries can redefine this type to generate custom
+ /// results that are then passed into the refactoring continuations.
+ using ResultTy = void;
+
+ ASTProducerQuery(const char *NameUID)
+ : IndexerQuery(BaseUIDString, NameUID) {}
+
+ static bool classof(const IndexerQuery *Q) {
+ return Q->BaseUID == BaseUIDString;
+ }
+};
+
+/// A query that finds a file that contains/should contain the implementation of
+/// some declaration.
+class ASTUnitForImplementationOfDeclarationQuery final
+ : public ASTProducerQuery {
+ static const char *NameUIDString;
+
+ const Decl *D;
+ PersistentFileID Result;
+
+public:
+ ASTUnitForImplementationOfDeclarationQuery(const Decl *D)
+ : ASTProducerQuery(NameUIDString), D(D), Result("") {}
+
+ using ResultTy = FileID;
+
+ const Decl *getDecl() const { return D; }
+
+ void invalidateTUSpecificState() override { D = nullptr; }
+
+ void setResult(PersistentFileID File) { Result = std::move(File); }
+
+ bool verify(ASTContext &Context) override;
+
+ const PersistentFileID &getResult() const { return Result; }
+
+ static bool classof(const IndexerQuery *D) {
+ return D->NameUID == NameUIDString;
+ }
+};
+
+/// Returns an indexer query that will allow a refactoring continuation to run
+/// in an AST unit that contains a file that should contain the implementation
+/// of the given declaration \p D.
+///
+/// The continuation function will receive \c FileID that corresponds to the
+/// implementation file. The indexer can decide which file should be used as an
+/// implementation of a declaration based on a number of different heuristics.
+/// It does not guarantee that the file will actually have any declarations that
+/// correspond to the implementation of \p D yet, as the indexer may decide to
+/// point to a file that it thinks will have the implementation declarations in
+/// the future.
+std::unique_ptr<ASTUnitForImplementationOfDeclarationQuery>
+fileThatShouldContainImplementationOf(const Decl *D);
+
+/// A declaration predicate operates.
+struct DeclPredicate {
+ const char *Name;
+
+ DeclPredicate(const char *Name) : Name(Name) {}
+
+ bool operator==(const DeclPredicate &P) const {
+ return StringRef(Name) == P.Name;
+ }
+ bool operator!=(const DeclPredicate &P) const {
+ return StringRef(Name) != P.Name;
+ }
+};
+
+/// Represents a declaration predicate that will evaluate to either 'true' or
+/// 'false' in an indexer query.
+struct BoolDeclPredicate {
+ DeclPredicate Predicate;
+ bool IsInverted;
+
+ BoolDeclPredicate(DeclPredicate Predicate, bool IsInverted = false)
+ : Predicate(Predicate), IsInverted(IsInverted) {}
+
+ BoolDeclPredicate operator!() const {
+ return BoolDeclPredicate(Predicate, /*IsInverted=*/!IsInverted);
+ }
+};
+
+namespace detail {
+
+/// AST-like representation for decl predicates.
+class DeclPredicateNode {
+public:
+ const char *NameUID;
+ DeclPredicateNode(const char *NameUID) : NameUID(NameUID) {}
+
+ static std::unique_ptr<DeclPredicateNode>
+ create(const DeclPredicate &Predicate);
+ static std::unique_ptr<DeclPredicateNode>
+ create(const BoolDeclPredicate &Predicate);
+
+ static bool classof(const DeclPredicateNode *) { return true; }
+};
+
+class DeclPredicateNodePredicate : public DeclPredicateNode {
+ static const char *NameUIDString;
+
+ DeclPredicate Predicate;
+
+public:
+ DeclPredicateNodePredicate(const DeclPredicate &Predicate)
+ : DeclPredicateNode(NameUIDString), Predicate(Predicate) {}
+
+ const DeclPredicate &getPredicate() const { return Predicate; }
+
+ static bool classof(const DeclPredicateNode *P) {
+ return P->NameUID == NameUIDString;
+ }
+};
+
+class DeclPredicateNotPredicate : public DeclPredicateNode {
+ static const char *NameUIDString;
+
+ std::unique_ptr<DeclPredicateNode> Child;
+
+public:
+ DeclPredicateNotPredicate(std::unique_ptr<DeclPredicateNode> Child)
+ : DeclPredicateNode(NameUIDString), Child(std::move(Child)) {}
+
+ const DeclPredicateNode &getChild() const { return *Child; }
+
+ static bool classof(const DeclPredicateNode *P) {
+ return P->NameUID == NameUIDString;
+ }
+};
+
+} // end namespace detail
+
+enum class QueryBoolResult {
+ Unknown,
+ Yes,
+ No,
+};
+
+// FIXME: Check that 'T' is either a PersistentDeclRef<> or a Decl *.
+template <typename T> struct Indexed {
+ T Decl;
+ // FIXME: Generalize better in the new refactoring engine.
+ QueryBoolResult IsNotDefined;
+
+ Indexed(T Decl, QueryBoolResult IsNotDefined = QueryBoolResult::Unknown)
+ : Decl(Decl), IsNotDefined(IsNotDefined) {}
+
+ Indexed(Indexed<T> &&Other) = default;
+ Indexed &operator=(Indexed<T> &&Other) = default;
+ Indexed(const Indexed<T> &Other) = default;
+ Indexed &operator=(const Indexed<T> &Other) = default;
+
+ /// True iff the declaration is not defined in the entire project.
+ bool isNotDefined() const {
+ // FIXME: This is hack. Need a better system in the new engine.
+ return IsNotDefined == QueryBoolResult::Yes;
+ }
+};
+
+/// Transforms one set of declarations into another using some predicate.
+class DeclarationsQuery : public IndexerQuery {
+ static const char *BaseUIDString;
+
+ std::vector<const Decl *> Input;
+ std::unique_ptr<detail::DeclPredicateNode> Predicate;
+
+protected:
+ std::vector<Indexed<PersistentDeclRef<Decl>>> Output;
+
+public:
+ DeclarationsQuery(std::vector<const Decl *> Input,
+ std::unique_ptr<detail::DeclPredicateNode> Predicate)
+ : IndexerQuery(BaseUIDString, nullptr), Input(std::move(Input)),
+ Predicate(std::move(Predicate)) {
+ assert(!this->Input.empty() && "empty declarations list!");
+ }
+
+ ArrayRef<const Decl *> getInputs() const { return Input; }
+
+ void invalidateTUSpecificState() override { Input.clear(); }
+
+ bool verify(ASTContext &Context) override;
+
+ void setOutput(std::vector<Indexed<PersistentDeclRef<Decl>>> Output) {
+ this->Output = Output;
+ }
+
+ const detail::DeclPredicateNode &getPredicateNode() const {
+ return *Predicate;
+ }
+
+ static bool classof(const IndexerQuery *Q) {
+ return Q->BaseUID == BaseUIDString;
+ }
+};
+
+/// The \c DeclEntity class acts as a proxy for the entity that represents a
+/// declaration in the indexer. It defines a set of declaration predicates that
+/// can be used in indexer queries.
+struct DeclEntity {
+ /// The indexer will evaluate this predicate to 'true' when a certain
+ /// declaration has a corresponding definition.
+ BoolDeclPredicate isDefined() const {
+ return BoolDeclPredicate("decl.isDefined");
+ }
+};
+
+template <typename T>
+class ManyToManyDeclarationsQuery final
+ : public std::enable_if<std::is_base_of<Decl, T>::value,
+ DeclarationsQuery>::type {
+public:
+ ManyToManyDeclarationsQuery(
+ ArrayRef<const T *> Input,
+ std::unique_ptr<detail::DeclPredicateNode> Predicate)
+ : DeclarationsQuery(std::vector<const Decl *>(Input.begin(), Input.end()),
+ std::move(Predicate)) {}
+
+ std::vector<Indexed<PersistentDeclRef<T>>> getOutput() const {
+ std::vector<Indexed<PersistentDeclRef<T>>> Results;
+ for (const auto &Ref : DeclarationsQuery::Output)
+ Results.push_back(Indexed<PersistentDeclRef<T>>(
+ PersistentDeclRef<T>(Ref.Decl.USR), Ref.IsNotDefined));
+ return Results;
+ }
+};
+
+/// Returns an indexer query that will pass a filtered list of declarations to
+/// a refactoring continuation.
+///
+/// The filtering is done based on predicates that are available on the \c
+/// DeclEntity types. For example, you can use the following invocation to
+/// find a set of declarations that are defined in the entire project:
+///
+/// \code
+/// filter({ MyDeclA, MyDeclB }, [] (const DeclEntity &D) { return D.isDefined()
+/// })
+/// \endcode
+template <typename T>
+std::unique_ptr<ManyToManyDeclarationsQuery<T>>
+filter(ArrayRef<const T *> Declarations,
+ BoolDeclPredicate (*Fn)(const DeclEntity &),
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ return llvm::make_unique<ManyToManyDeclarationsQuery<T>>(
+ Declarations, detail::DeclPredicateNode::create(Fn(DeclEntity())));
+}
+
+} // end namespace indexer
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_INDEXER_QUERY_H
diff --git a/include/clang/Tooling/Refactor/RefactoringActionFinder.h b/include/clang/Tooling/Refactor/RefactoringActionFinder.h
new file mode 100644
index 0000000..395d78c
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringActionFinder.h
@@ -0,0 +1,60 @@
+//===--- RefactoringActionFinder.h - Clang refactoring library ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides methods to find the refactoring actions that can be
+/// performed at specific locations / source ranges in a translation unit.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactor/RefactoringActions.h"
+#include "llvm/ADT/StringSet.h"
+#include <vector>
+
+namespace clang {
+
+class NamedDecl;
+class ASTContext;
+
+namespace tooling {
+
+/// Contains a set of a refactoring actions.
+struct RefactoringActionSet {
+ /// A set of refactoring actions that can be performed at some specific
+ /// location in a source file.
+ ///
+ /// The actions in the action set are ordered by their priority: most
+ /// important actions are placed before the less important ones.
+ std::vector<RefactoringActionType> Actions;
+
+ RefactoringActionSet() {}
+
+ RefactoringActionSet(RefactoringActionSet &&) = default;
+ RefactoringActionSet &operator=(RefactoringActionSet &&) = default;
+};
+
+/// \brief Returns a \c RefactoringActionSet that contains the set of actions
+/// that can be performed at the given location.
+RefactoringActionSet findActionSetAt(SourceLocation Loc,
+ SourceRange SelectionRange,
+ ASTContext &Context);
+
+/// \brief Returns a set of USRs that correspond to the given declaration.
+llvm::StringSet<> findSymbolsUSRSet(const NamedDecl *FoundDecl,
+ ASTContext &Context);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTION_FINDER_H
diff --git a/include/clang/Tooling/Refactor/RefactoringActions.def b/include/clang/Tooling/Refactor/RefactoringActions.def
new file mode 100644
index 0000000..f5c2f66
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringActions.def
@@ -0,0 +1,62 @@
+//===--- RefactoringActions.def - The list of refactoring actions --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef REFACTORING_ACTION
+#define REFACTORING_ACTION(Name, Spelling)
+#endif
+
+#ifndef REFACTORING_SUB_ACTION
+#define REFACTORING_SUB_ACTION(Name, Parent, Spelling) \
+ REFACTORING_ACTION(Parent##_##Name, Spelling)
+#endif
+
+#ifndef REFACTORING_OPERATION_ACTION
+#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command)\
+ REFACTORING_ACTION(Name, Spelling)
+#endif
+
+#ifndef REFACTORING_OPERATION_SUB_ACTION
+#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command)\
+ REFACTORING_SUB_ACTION(Name, Parent, Spelling)
+#endif
+
+REFACTORING_ACTION(Rename, "Rename")
+REFACTORING_SUB_ACTION(Local, Rename, "Rename")
+
+REFACTORING_OPERATION_ACTION(Extract, "Extract Function", "extract")
+REFACTORING_OPERATION_SUB_ACTION(Method, Extract, "Extract Method",
+ "extract-method")
+REFACTORING_OPERATION_SUB_ACTION(Expression, Extract, "Extract Expression",
+ "extract-expression")
+
+REFACTORING_OPERATION_ACTION(IfSwitchConversion, "Convert to Switch",
+ "if-switch-conversion")
+REFACTORING_OPERATION_ACTION(FillInEnumSwitchCases, "Add Missing Switch Cases",
+ "fill-in-enum-switch-cases")
+REFACTORING_OPERATION_ACTION(FillInMissingProtocolStubs,
+ "Add Missing Protocol Requirements",
+ "fill-in-missing-protocol-stubs")
+REFACTORING_OPERATION_ACTION(LocalizeObjCStringLiteral,
+ "Wrap in NSLocalizedString",
+ "localize-objc-string-literal")
+REFACTORING_OPERATION_ACTION(ExtractRepeatedExpressionIntoVariable,
+ "Extract Repeated Expression",
+ "extract-repeated-expr-into-var")
+REFACTORING_OPERATION_ACTION(FillInMissingMethodStubsFromAbstractClasses,
+ "Add Missing Abstract Class Overrides",
+ "fill-in-missing-abstract-methods")
+ // FIXME: For ObjC this should say 'Methods':
+REFACTORING_OPERATION_ACTION(ImplementDeclaredMethods,
+ "Generate Missing Function Definitions",
+ "implement-declared-methods")
+
+#undef REFACTORING_OPERATION_SUB_ACTION
+#undef REFACTORING_OPERATION_ACTION
+#undef REFACTORING_SUB_ACTION
+#undef REFACTORING_ACTION
diff --git a/include/clang/Tooling/Refactor/RefactoringActions.h b/include/clang/Tooling/Refactor/RefactoringActions.h
new file mode 100644
index 0000000..f9d9c6c
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringActions.h
@@ -0,0 +1,34 @@
+//===--- RefactoringActions.h - Clang refactoring library -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Contains a list of all the supported refactoring actions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace tooling {
+
+enum class RefactoringActionType {
+#define REFACTORING_ACTION(Name, Spelling) Name,
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+};
+
+StringRef getRefactoringActionTypeName(RefactoringActionType Action);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_ACTIONS_H
diff --git a/include/clang/Tooling/Refactor/RefactoringOperation.h b/include/clang/Tooling/Refactor/RefactoringOperation.h
new file mode 100644
index 0000000..3332178
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringOperation.h
@@ -0,0 +1,160 @@
+//===--- RefactoringOperations.h - Defines a refactoring operation --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactor/RefactoringActions.h"
+#include "clang/Tooling/Refactor/RefactoringOptionSet.h"
+#include "clang/Tooling/Refactor/RefactoringReplacement.h"
+#include "clang/Tooling/Refactor/SymbolOperation.h"
+#include "llvm/ADT/None.h"
+#include "llvm/Support/Error.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+
+class ASTContext;
+class Decl;
+class Preprocessor;
+class Stmt;
+
+namespace tooling {
+
+class RefactoringContinuation;
+
+/// A refactoring result contains the source replacements produced by the
+/// refactoring operation and the optional refactoring continuation.
+struct RefactoringResult {
+ std::vector<RefactoringReplacement> Replacements;
+ std::vector<std::unique_ptr<RefactoringResultAssociatedSymbol>>
+ AssociatedSymbols;
+ std::unique_ptr<RefactoringContinuation> Continuation;
+
+ RefactoringResult(
+ std::vector<RefactoringReplacement> Replacements,
+ std::unique_ptr<RefactoringContinuation> Continuation = nullptr)
+ : Replacements(std::move(Replacements)),
+ Continuation(std::move(Continuation)) {}
+
+ RefactoringResult(std::unique_ptr<RefactoringContinuation> Continuation)
+ : Replacements(), Continuation(std::move(Continuation)) {}
+
+ RefactoringResult(RefactoringResult &&) = default;
+ RefactoringResult &operator=(RefactoringResult &&) = default;
+};
+
+namespace indexer {
+
+class IndexerQuery;
+class ASTProducerQuery;
+
+} // end namespace indexer
+
+/// Refactoring continuations allow refactoring operations to run in external
+/// AST units with some results that were obtained after querying the indexer.
+///
+/// The state of the refactoring operation is automatically managed by the
+/// refactoring engine:
+/// - Declaration references are converted to declaration references in
+/// an external translation unit.
+class RefactoringContinuation {
+public:
+ virtual ~RefactoringContinuation() {}
+
+ virtual indexer::ASTProducerQuery *getASTUnitIndexerQuery() = 0;
+
+ virtual std::vector<indexer::IndexerQuery *>
+ getAdditionalIndexerQueries() = 0;
+
+ /// Converts the TU-specific state in the continuation to a TU-independent
+ /// state.
+ ///
+ /// This function is called before the initiation AST unit is freed.
+ virtual void persistTUSpecificState() = 0;
+
+ /// Invokes the continuation with the indexer query results and the state
+ /// values in the context of another AST unit.
+ virtual llvm::Expected<RefactoringResult>
+ runInExternalASTUnit(ASTContext &Context) = 0;
+};
+
+// TODO: Remove in favour of diagnostics.
+class RefactoringOperationError
+ : public llvm::ErrorInfo<RefactoringOperationError> {
+public:
+ static char ID;
+ StringRef FailureReason;
+
+ RefactoringOperationError(StringRef FailureReason)
+ : FailureReason(FailureReason) {}
+
+ void log(raw_ostream &OS) const override;
+
+ std::error_code convertToErrorCode() const override;
+};
+
+/// Represents an abstract refactoring operation.
+class RefactoringOperation {
+public:
+ virtual ~RefactoringOperation() {}
+
+ virtual const Stmt *getTransformedStmt() const { return nullptr; }
+
+ virtual const Stmt *getLastTransformedStmt() const { return nullptr; }
+
+ virtual const Decl *getTransformedDecl() const { return nullptr; }
+
+ virtual const Decl *getLastTransformedDecl() const { return nullptr; }
+
+ virtual std::vector<std::string> getRefactoringCandidates() { return {}; }
+
+ virtual std::vector<RefactoringActionType> getAvailableSubActions() {
+ return {};
+ }
+
+ virtual llvm::Expected<RefactoringResult>
+ perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex = 0) = 0;
+};
+
+/// A wrapper around a unique pointer to a \c RefactoringOperation or \c
+/// SymbolOperation that determines if the operation was successfully initiated
+/// or not, even if the operation itself wasn't created.
+struct RefactoringOperationResult {
+ std::unique_ptr<RefactoringOperation> RefactoringOp;
+ std::unique_ptr<SymbolOperation> SymbolOp;
+ bool Initiated;
+ StringRef FailureReason;
+
+ RefactoringOperationResult() : Initiated(false) {}
+ RefactoringOperationResult(llvm::NoneType) : Initiated(false) {}
+ explicit RefactoringOperationResult(StringRef FailureReason)
+ : Initiated(false), FailureReason(FailureReason) {}
+};
+
+/// Initiate a specific refactoring operation.
+RefactoringOperationResult initiateRefactoringOperationAt(
+ SourceLocation Location, SourceRange SelectionRange, ASTContext &Context,
+ RefactoringActionType ActionType, bool CreateOperation = true);
+
+/// Initiate a specific refactoring operation on a declaration that corresponds
+/// to the given \p DeclUSR.
+RefactoringOperationResult
+initiateRefactoringOperationOnDecl(StringRef DeclUSR, ASTContext &Context,
+ RefactoringActionType ActionType);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_H
diff --git a/include/clang/Tooling/Refactor/RefactoringOperationState.h b/include/clang/Tooling/Refactor/RefactoringOperationState.h
new file mode 100644
index 0000000..76ee7d4
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringOperationState.h
@@ -0,0 +1,66 @@
+//===--- RefactoringOperationState.h - Serializable operation state -------===//
+//
+// 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 refactoring operation state types that represent the
+// TU-independent state that is used for refactoring continuations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H
+
+#include "clang/AST/Decl.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactor/USRFinder.h"
+#include <string>
+#include <type_traits>
+
+namespace clang {
+namespace tooling {
+
+namespace detail {
+
+struct PersistentDeclRefBase {};
+
+} // end namespace detail
+
+/// Declaration references are persisted across translation units by using
+/// USRs.
+template <typename T>
+struct PersistentDeclRef : std::enable_if<std::is_base_of<Decl, T>::value,
+ detail::PersistentDeclRefBase>::type {
+ std::string USR;
+ // FIXME: We can improve the efficiency of conversion to Decl * by storing the
+ // decl kind.
+
+ PersistentDeclRef(std::string USR) : USR(std::move(USR)) {}
+ PersistentDeclRef(PersistentDeclRef &&Other) = default;
+ PersistentDeclRef &operator=(PersistentDeclRef &&Other) = default;
+ PersistentDeclRef(const PersistentDeclRef &Other) = default;
+ PersistentDeclRef &operator=(const PersistentDeclRef &Other) = default;
+
+ static PersistentDeclRef<T> create(const Decl *D) {
+ // FIXME: Move the getUSRForDecl method somewhere else.
+ return PersistentDeclRef<T>(rename::getUSRForDecl(D));
+ }
+};
+
+/// FileIDs are persisted across translation units by using filenames.
+struct PersistentFileID {
+ std::string Filename;
+
+ PersistentFileID(std::string Filename) : Filename(std::move(Filename)) {}
+ PersistentFileID(PersistentFileID &&Other) = default;
+ PersistentFileID &operator=(PersistentFileID &&Other) = default;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPERATION_STATE_H
diff --git a/include/clang/Tooling/Refactor/RefactoringOptionSet.h b/include/clang/Tooling/Refactor/RefactoringOptionSet.h
new file mode 100644
index 0000000..c3f05ac
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringOptionSet.h
@@ -0,0 +1,80 @@
+//===--- RefactoringOptionSet.h - A container for the refactoring options -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace yaml {
+class IO;
+} // end namespace yaml
+} // end namespace llvm
+
+namespace clang {
+namespace tooling {
+
+struct RefactoringOption {
+ virtual ~RefactoringOption() = default;
+
+ struct SerializationContext {
+ llvm::yaml::IO &IO;
+
+ SerializationContext(llvm::yaml::IO &IO) : IO(IO) {}
+ };
+
+ virtual void serialize(const SerializationContext &Context);
+};
+
+/// \brief A set of refactoring options that can be given to a refactoring
+/// operation.
+class RefactoringOptionSet final {
+ llvm::StringMap<std::unique_ptr<RefactoringOption>> Options;
+
+public:
+ RefactoringOptionSet() {}
+ template <typename T> RefactoringOptionSet(const T &Option) { add(Option); }
+
+ RefactoringOptionSet(RefactoringOptionSet &&) = default;
+ RefactoringOptionSet &operator=(RefactoringOptionSet &&) = default;
+
+ RefactoringOptionSet(const RefactoringOptionSet &) = delete;
+ RefactoringOptionSet &operator=(const RefactoringOptionSet &) = delete;
+
+ template <typename T> void add(const T &Option) {
+ auto It = Options.try_emplace(StringRef(T::Name), nullptr);
+ if (It.second)
+ It.first->getValue().reset(new T(Option));
+ }
+
+ template <typename T> const T *get() const {
+ auto It = Options.find(StringRef(T::Name));
+ if (It == Options.end())
+ return nullptr;
+ return static_cast<const T *>(It->getValue().get());
+ }
+
+ template <typename T> const T &get(const T &Default) const {
+ const auto *Ptr = get<T>();
+ return Ptr ? *Ptr : Default;
+ }
+
+ void print(llvm::raw_ostream &OS) const;
+
+ static llvm::Expected<RefactoringOptionSet> parse(StringRef Source);
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTION_SET_H
diff --git a/include/clang/Tooling/Refactor/RefactoringOptions.h b/include/clang/Tooling/Refactor/RefactoringOptions.h
new file mode 100644
index 0000000..f0ce4ba
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringOptions.h
@@ -0,0 +1,59 @@
+//===--- RefactoringOptions.h - A set of all the refactoring options ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a set of all possible refactoring options that can be
+// given to the refactoring operations.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H
+
+#include "clang/AST/DeclBase.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactor/RefactoringOptionSet.h"
+
+namespace clang {
+namespace tooling {
+namespace option {
+
+namespace detail {
+
+struct BoolOptionBase : RefactoringOption {
+protected:
+ bool Value = false;
+ void serializeImpl(const SerializationContext &Context, const char *Name);
+
+public:
+ operator bool() const { return Value; }
+};
+
+template <typename Option> struct BoolOption : BoolOptionBase {
+ void serialize(const SerializationContext &Context) override {
+ serializeImpl(Context, Option::Name);
+ }
+
+ static Option getTrue() {
+ Option Result;
+ Result.Value = true;
+ return Result;
+ }
+};
+
+} // end namespace detail
+
+struct AvoidTextualMatches final : detail::BoolOption<AvoidTextualMatches> {
+ static constexpr const char *Name = "rename.avoid.textual.matches";
+};
+
+} // end namespace option
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_OPTIONS_H
diff --git a/include/clang/Tooling/Refactor/RefactoringReplacement.h b/include/clang/Tooling/Refactor/RefactoringReplacement.h
new file mode 100644
index 0000000..4aed8ab
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RefactoringReplacement.h
@@ -0,0 +1,85 @@
+//===--- RefactoringReplacement.h - ------------------------*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H
+#define LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactor/SymbolName.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
+#include <string>
+
+namespace clang {
+namespace tooling {
+
+/// \brief Represent a symbol that can be used for an additional refactoring
+/// action that associated.
+class RefactoringResultAssociatedSymbol {
+ SymbolName Name;
+
+public:
+ RefactoringResultAssociatedSymbol(SymbolName Name) : Name(std::move(Name)) {}
+
+ const SymbolName &getName() const { return Name; }
+};
+
+/// \brief A replacement range.
+class RefactoringReplacement {
+public:
+ SourceRange Range;
+ std::string ReplacementString;
+
+ /// \brief Represents a symbol that is contained in the replacement string
+ /// of this replacement.
+ struct AssociatedSymbolLocation {
+ /// These offsets point into the ReplacementString.
+ llvm::SmallVector<unsigned, 4> Offsets;
+ bool IsDeclaration;
+
+ AssociatedSymbolLocation(ArrayRef<unsigned> Offsets,
+ bool IsDeclaration = false)
+ : Offsets(Offsets.begin(), Offsets.end()),
+ IsDeclaration(IsDeclaration) {}
+ };
+ llvm::SmallDenseMap<const RefactoringResultAssociatedSymbol *,
+ AssociatedSymbolLocation>
+ SymbolLocations;
+
+ RefactoringReplacement(SourceRange Range) : Range(Range) {}
+
+ RefactoringReplacement(SourceRange Range, StringRef ReplacementString)
+ : Range(Range), ReplacementString(ReplacementString.str()) {}
+ RefactoringReplacement(SourceRange Range, std::string ReplacementString)
+ : Range(Range), ReplacementString(std::move(ReplacementString)) {}
+
+ RefactoringReplacement(SourceRange Range, StringRef ReplacementString,
+ const RefactoringResultAssociatedSymbol *Symbol,
+ const AssociatedSymbolLocation &Loc)
+ : Range(Range), ReplacementString(ReplacementString.str()) {
+ SymbolLocations.insert(std::make_pair(Symbol, Loc));
+ }
+
+ RefactoringReplacement(const FixItHint &Hint) {
+ Range = Hint.RemoveRange.getAsRange();
+ ReplacementString = Hint.CodeToInsert;
+ }
+
+ RefactoringReplacement(RefactoringReplacement &&) = default;
+ RefactoringReplacement &operator=(RefactoringReplacement &&) = default;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_REFACTORING_REPLACEMENT_H
diff --git a/include/clang/Tooling/Refactor/RenameIndexedFile.h b/include/clang/Tooling/Refactor/RenameIndexedFile.h
new file mode 100644
index 0000000..d598259
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RenameIndexedFile.h
@@ -0,0 +1,83 @@
+//===--- RenameIndexedFile.h - -----------------------------*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Tooling/Refactor/RenamedSymbol.h"
+#include "clang/Tooling/Refactor/SymbolName.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+namespace tooling {
+
+class RefactoringOptionSet;
+
+namespace rename {
+
+/// An already known occurrence of the symbol that's being renamed.
+struct IndexedOccurrence {
+ /// The location of this occurrence in the indexed file.
+ unsigned Line, Column;
+ enum OccurrenceKind {
+ IndexedSymbol,
+ IndexedObjCMessageSend,
+ InclusionDirective
+ };
+ OccurrenceKind Kind;
+};
+
+struct IndexedSymbol {
+ SymbolName Name;
+ std::vector<IndexedOccurrence> IndexedOccurrences;
+ /// Whether this symbol is an Objective-C selector.
+ bool IsObjCSelector;
+
+ IndexedSymbol(SymbolName Name,
+ std::vector<IndexedOccurrence> IndexedOccurrences,
+ bool IsObjCSelector)
+ : Name(std::move(Name)),
+ IndexedOccurrences(std::move(IndexedOccurrences)),
+ IsObjCSelector(IsObjCSelector) {}
+ IndexedSymbol(IndexedSymbol &&Other) = default;
+ IndexedSymbol &operator=(IndexedSymbol &&Other) = default;
+};
+
+/// Consumes the \c SymbolOccurrences found by \c IndexedFileOccurrenceProducer.
+class IndexedFileOccurrenceConsumer {
+public:
+ virtual ~IndexedFileOccurrenceConsumer() {}
+ virtual void handleOccurrence(const SymbolOccurrence &Occurrence,
+ SourceManager &SM,
+ const LangOptions &LangOpts) = 0;
+};
+
+/// Finds the renamed \c SymbolOccurrences in an already indexed files.
+class IndexedFileOccurrenceProducer final : public PreprocessorFrontendAction {
+ bool IsMultiPiece;
+ ArrayRef<IndexedSymbol> Symbols;
+ IndexedFileOccurrenceConsumer &Consumer;
+ const RefactoringOptionSet *Options;
+
+public:
+ IndexedFileOccurrenceProducer(ArrayRef<IndexedSymbol> Symbols,
+ IndexedFileOccurrenceConsumer &Consumer,
+ const RefactoringOptionSet *Options = nullptr);
+
+private:
+ void ExecuteAction() override;
+};
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAME_INDEXED_FILE_H
diff --git a/include/clang/Tooling/Refactor/RenamedSymbol.h b/include/clang/Tooling/Refactor/RenamedSymbol.h
new file mode 100644
index 0000000..d54c497
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RenamedSymbol.h
@@ -0,0 +1,143 @@
+//===--- RenamedSymbol.h - ---------------------------------*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H
+
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactor/SymbolName.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
+
+namespace clang {
+
+class NamedDecl;
+
+namespace tooling {
+namespace rename {
+
+/// \brief A symbol that has to be renamed.
+class Symbol {
+public:
+ SymbolName Name;
+ /// The index of this symbol in a \c SymbolOperation.
+ unsigned SymbolIndex;
+ /// The declaration that was used to initiate a refactoring operation for this
+ /// symbol. May not be the most canonical declaration.
+ const NamedDecl *FoundDecl;
+ /// An optional Objective-C selector.
+ llvm::Optional<Selector> ObjCSelector;
+
+ Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex,
+ const LangOptions &LangOpts);
+
+ Symbol(Symbol &&) = default;
+ Symbol &operator=(Symbol &&) = default;
+};
+
+/// \brief An occurrence of a renamed symbol.
+///
+/// Provides information about an occurrence of symbol that helps renaming tools
+/// determine if they can rename this symbol automatically and which source
+/// ranges they have to replace.
+///
+/// A single occurrence of a symbol can span more than one source range to
+/// account for things like Objective-C selectors.
+// TODO: Rename
+class SymbolOccurrence {
+ /// The source locations that correspond to the occurence of the symbol.
+ SmallVector<SourceLocation, 4> Locations;
+
+public:
+ enum OccurrenceKind {
+ /// \brief This occurrence is an exact match and can be renamed
+ /// automatically.
+ MatchingSymbol,
+
+ /// \brief This is an occurrence of a matching selector. It can't be renamed
+ /// automatically unless the indexer proves that this selector refers only
+ /// to the declarations that correspond to the renamed symbol.
+ MatchingSelector,
+
+ /// \brief This is an occurrence of an implicit property that uses the
+ /// renamed method.
+ MatchingImplicitProperty,
+
+ /// \brief This is a textual occurrence of a symbol in a comment.
+ MatchingComment,
+
+ /// \brief This is a textual occurrence of a symbol in a doc comment.
+ MatchingDocComment,
+
+ /// \brief This is an occurrence of a symbol in an inclusion directive.
+ MatchingFilename
+ };
+
+ OccurrenceKind Kind;
+ /// Whether or not this occurrence is inside a macro. When this is true, the
+ /// locations of the occurrence contain just one location that points to
+ /// the location of the macro expansion.
+ bool IsMacroExpansion;
+ /// The index of the symbol stored in a \c SymbolOperation which matches this
+ /// occurrence.
+ unsigned SymbolIndex;
+
+ SymbolOccurrence()
+ : Kind(MatchingSymbol), IsMacroExpansion(false), SymbolIndex(0) {}
+
+ SymbolOccurrence(OccurrenceKind Kind, bool IsMacroExpansion,
+ unsigned SymbolIndex, ArrayRef<SourceLocation> Locations)
+ : Locations(Locations.begin(), Locations.end()), Kind(Kind),
+ IsMacroExpansion(IsMacroExpansion), SymbolIndex(SymbolIndex) {
+ assert(!Locations.empty() && "Renamed occurence without locations!");
+ }
+
+ SymbolOccurrence(SymbolOccurrence &&) = default;
+ SymbolOccurrence &operator=(SymbolOccurrence &&) = default;
+
+ ArrayRef<SourceLocation> locations() const {
+ if (Kind == MatchingImplicitProperty && Locations.size() == 2)
+ return llvm::makeArrayRef(Locations).drop_back();
+ return Locations;
+ }
+
+ /// Return the source range that corresponds to an individual source location
+ /// in this occurrence.
+ SourceRange getLocationRange(SourceLocation Loc, size_t OldNameSize) const {
+ SourceLocation EndLoc;
+ // Implicit property references might store the end as the second location
+ // to take into account the match for 'prop' when the old name is 'setProp'.
+ if (Kind == MatchingImplicitProperty && Locations.size() == 2) {
+ assert(Loc == Locations[0] && "invalid loc");
+ EndLoc = Locations[1];
+ } else
+ EndLoc = IsMacroExpansion ? Loc : Loc.getLocWithOffset(OldNameSize);
+ return SourceRange(Loc, EndLoc);
+ }
+
+ friend bool operator<(const SymbolOccurrence &LHS,
+ const SymbolOccurrence &RHS);
+ friend bool operator==(const SymbolOccurrence &LHS,
+ const SymbolOccurrence &RHS);
+};
+
+/// \brief Less-than operator between the two renamed symbol occurrences.
+bool operator<(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS);
+
+/// \brief Equal-to operator between the two renamed symbol occurrences.
+bool operator==(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS);
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAMED_SYMBOL_H
diff --git a/include/clang/Tooling/Refactor/RenamingOperation.h b/include/clang/Tooling/Refactor/RenamingOperation.h
new file mode 100644
index 0000000..bb360a0
--- /dev/null
+++ b/include/clang/Tooling/Refactor/RenamingOperation.h
@@ -0,0 +1,43 @@
+//===--- RenamingOperation.h - -----------------------------*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactor/SymbolName.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+class IdentifierTable;
+
+namespace tooling {
+
+class SymbolOperation;
+
+namespace rename {
+
+/// Return true if the new name is a valid language identifier.
+bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector,
+ IdentifierTable &IDs, const LangOptions &LangOpts);
+bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation,
+ IdentifierTable &IDs, const LangOptions &LangOpts);
+
+/// \brief Finds the set of new names that apply to the symbols in the given
+/// \c SymbolOperation.
+void determineNewNames(SymbolName NewName, const SymbolOperation &Operation,
+ SmallVectorImpl<SymbolName> &NewNames,
+ const LangOptions &LangOpts);
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_RENAMING_OPERATION_H
diff --git a/include/clang/Tooling/Refactor/SymbolName.h b/include/clang/Tooling/Refactor/SymbolName.h
new file mode 100644
index 0000000..f348a02
--- /dev/null
+++ b/include/clang/Tooling/Refactor/SymbolName.h
@@ -0,0 +1,74 @@
+//===--- SymbolName.h - Clang refactoring library ----------*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_NAME_H
+#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_NAME_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+
+class LangOptions;
+
+namespace tooling {
+
+/// \brief A name of a declaration that's used in the refactoring process.
+///
+/// Names can be composed of multiple string, to account for things like
+/// Objective-C selectors.
+class SymbolName {
+public:
+ SymbolName() {}
+
+ /// \brief Creates a \c SymbolName by decomposing the given \p Name using
+ /// language specific logic.
+ SymbolName(StringRef Name, const LangOptions &LangOpts);
+ SymbolName(StringRef Name, bool IsObjectiveCSelector);
+ explicit SymbolName(ArrayRef<StringRef> Name);
+
+ SymbolName(SymbolName &&) = default;
+ SymbolName &operator=(SymbolName &&) = default;
+
+ SymbolName(const SymbolName &) = default;
+ SymbolName &operator=(const SymbolName &) = default;
+
+ bool empty() const { return Strings.empty(); }
+
+ /// \brief Returns the number of the strings that make up the given name.
+ size_t size() const { return Strings.size(); }
+
+ /// \brief Returns the string at the given index.
+ StringRef operator[](size_t I) const { return Strings[I]; }
+
+ ArrayRef<std::string> strings() const { return Strings; }
+
+ bool containsEmptyPiece() const {
+ for (const auto &String : Strings) {
+ if (String.empty())
+ return true;
+ }
+ return false;
+ }
+
+ void print(raw_ostream &OS) const;
+
+private:
+ std::vector<std::string> Strings;
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const SymbolName &N);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_NAME_H
diff --git a/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h b/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h
new file mode 100644
index 0000000..6ecc23b
--- /dev/null
+++ b/include/clang/Tooling/Refactor/SymbolOccurrenceFinder.h
@@ -0,0 +1,37 @@
+//===--- SymbolOccurrenceFinder.h - Clang refactoring library -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides functionality for finding all occurrences of a USR in a
+/// given AST.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "clang/Tooling/Refactor/SymbolOperation.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tooling {
+namespace rename {
+
+// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
+std::vector<SymbolOccurrence>
+findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl);
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OCCURRENCE_FINDER_H
diff --git a/include/clang/Tooling/Refactor/SymbolOperation.h b/include/clang/Tooling/Refactor/SymbolOperation.h
new file mode 100644
index 0000000..7616aa3
--- /dev/null
+++ b/include/clang/Tooling/Refactor/SymbolOperation.h
@@ -0,0 +1,91 @@
+//===--- SymbolOperation.h - -------------------------------*- C++ -*------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Tooling/Refactor/RenamedSymbol.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+
+class ASTContext;
+class NamedDecl;
+
+namespace tooling {
+
+/// \brief A refactoring operation that deals with occurrences of symbols.
+class SymbolOperation {
+ /// Contains the symbols that are required for this operation.
+ SmallVector<rename::Symbol, 4> Symbols;
+
+ /// Maps from a USR to an index in the \c Symbol array.
+ /// Contains all of the USRs that correspond to the declarations which use
+ /// the symbols in this operation.
+ llvm::StringMap<unsigned> USRToSymbol;
+
+ /// True if all the symbols in this operation occur only in the translation
+ /// unit that defines them.
+ bool IsLocal;
+
+ /// The declaration whose implementation is needed for the correct initiation
+ /// of a symbol operation.
+ const NamedDecl *DeclThatRequiresImplementationTU;
+
+public:
+ SymbolOperation(const NamedDecl *FoundDecl, ASTContext &Context);
+
+ SymbolOperation(SymbolOperation &&) = default;
+ SymbolOperation &operator=(SymbolOperation &&) = default;
+
+ /// Return the symbol that corresponds to the given USR, or null if this USR
+ /// isn't interesting from the perspective of this operation.
+ const rename::Symbol *getSymbolForUSR(StringRef USR) const {
+ auto It = USRToSymbol.find(USR);
+ if (It != USRToSymbol.end())
+ return &Symbols[It->getValue()];
+ return nullptr;
+ }
+
+ /// The symbols that this operation is working on.
+ ///
+ /// Symbol operations, like rename, usually just work on just one symbol.
+ /// However, there are certain language constructs that require more than
+ /// one symbol in order for them to be renamed correctly. Property
+ /// declarations in Objective-C are the perfect example: in addition to the
+ /// actual property, renaming has to rename the corresponding getters and
+ /// setters, as well as the backing ivar.
+ ArrayRef<rename::Symbol> symbols() const { return Symbols; }
+
+ /// True if all the symbols in this operation occur only in the translation
+ /// unit that defines them.
+ bool isLocal() const { return IsLocal; }
+
+ /// True if the declaration that was found in the initial TU needs to be
+ /// examined in the TU that implemented it.
+ bool requiresImplementationTU() const {
+ return DeclThatRequiresImplementationTU;
+ }
+
+ /// Returns the declaration whose implementation is needed for the correct
+ /// initiation of a symbol operation.
+ const NamedDecl *declThatRequiresImplementationTU() const {
+ return DeclThatRequiresImplementationTU;
+ }
+};
+
+/// Return true if the given declaration corresponds to a local symbol.
+bool isLocalSymbol(const NamedDecl *D, const LangOptions &LangOpts);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_SYMBOL_OPERATION_H
diff --git a/include/clang/Tooling/Refactor/USRFinder.h b/include/clang/Tooling/Refactor/USRFinder.h
new file mode 100644
index 0000000..0a83f30
--- /dev/null
+++ b/include/clang/Tooling/Refactor/USRFinder.h
@@ -0,0 +1,85 @@
+//===--- USRFinder.h - Clang refactoring library --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for determining the USR of a symbol at a location in source
+/// code.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+
+class ASTContext;
+class Decl;
+class SourceLocation;
+class NamedDecl;
+
+namespace tooling {
+namespace rename {
+
+using llvm::StringRef;
+using namespace clang::ast_matchers;
+
+// Given an AST context and a point, returns a NamedDecl identifying the symbol
+// at the point. Returns null if nothing is found at the point.
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+ SourceLocation Point);
+
+/// Returns a \c NamedDecl that corresponds to the given \p USR in the given
+/// AST context. Returns null if there's no declaration that matches the given
+/// \p USR.
+const NamedDecl *getNamedDeclWithUSR(const ASTContext &Context, StringRef USR);
+
+// Converts a Decl into a USR.
+std::string getUSRForDecl(const Decl *Decl);
+
+// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
+class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
+public:
+ explicit NestedNameSpecifierLocFinder(ASTContext &Context)
+ : Context(Context) {}
+
+ ArrayRef<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
+ addMatchers();
+ Finder.matchAST(Context);
+ return Locations;
+ }
+
+private:
+ void addMatchers() {
+ const auto NestedNameSpecifierLocMatcher =
+ nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
+ Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
+ }
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
+ "nestedNameSpecifierLoc");
+ Locations.push_back(*NNS);
+ }
+
+ ASTContext &Context;
+ std::vector<NestedNameSpecifierLoc> Locations;
+ MatchFinder Finder;
+};
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H
diff --git a/include/clang/module.modulemap b/include/clang/module.modulemap
index d850bd5..abb8b10 100644
--- a/include/clang/module.modulemap
+++ b/include/clang/module.modulemap
@@ -138,4 +138,7 @@
// importing the AST matchers library gives a link dependency on the AST
// matchers (and thus the AST), which clang-format should not have.
exclude header "Tooling/RefactoringCallbacks.h"
+ exclude header "Tooling/Refactor/USRFinder.h"
+
+ textual header "Tooling/Refactor/RefactoringActions.def"
}
diff --git a/include/indexstore/IndexStoreCXX.h b/include/indexstore/IndexStoreCXX.h
new file mode 100644
index 0000000..addaa86
--- /dev/null
+++ b/include/indexstore/IndexStoreCXX.h
@@ -0,0 +1,502 @@
+//===--- IndexStoreCXX.h - C++ wrapper for the Index Store C API. ---------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header-only C++ wrapper for the Index Store C API.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H
+#define LLVM_CLANG_INDEXSTORE_INDEXSTORECXX_H
+
+#include "indexstore/indexstore.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include <ctime>
+
+namespace indexstore {
+ using llvm::ArrayRef;
+ using llvm::Optional;
+ using llvm::StringRef;
+
+static inline StringRef stringFromIndexStoreStringRef(indexstore_string_ref_t str) {
+ return StringRef(str.data, str.length);
+}
+
+class IndexRecordSymbol {
+ indexstore_symbol_t obj;
+ friend class IndexRecordReader;
+
+public:
+ IndexRecordSymbol(indexstore_symbol_t obj) : obj(obj) {}
+
+ indexstore_symbol_language_t getLanguage() {
+ return indexstore_symbol_get_language(obj);
+ }
+ indexstore_symbol_kind_t getKind() { return indexstore_symbol_get_kind(obj); }
+ indexstore_symbol_subkind_t getSubKind() { return indexstore_symbol_get_subkind(obj); }
+ uint64_t getProperties() {
+ return indexstore_symbol_get_properties(obj);
+ }
+ uint64_t getRoles() { return indexstore_symbol_get_roles(obj); }
+ uint64_t getRelatedRoles() { return indexstore_symbol_get_related_roles(obj); }
+ StringRef getName() { return stringFromIndexStoreStringRef(indexstore_symbol_get_name(obj)); }
+ StringRef getUSR() { return stringFromIndexStoreStringRef(indexstore_symbol_get_usr(obj)); }
+ StringRef getCodegenName() { return stringFromIndexStoreStringRef(indexstore_symbol_get_codegen_name(obj)); }
+};
+
+class IndexSymbolRelation {
+ indexstore_symbol_relation_t obj;
+
+public:
+ IndexSymbolRelation(indexstore_symbol_relation_t obj) : obj(obj) {}
+
+ uint64_t getRoles() { return indexstore_symbol_relation_get_roles(obj); }
+ IndexRecordSymbol getSymbol() { return indexstore_symbol_relation_get_symbol(obj); }
+};
+
+class IndexRecordOccurrence {
+ indexstore_occurrence_t obj;
+
+public:
+ IndexRecordOccurrence(indexstore_occurrence_t obj) : obj(obj) {}
+
+ IndexRecordSymbol getSymbol() { return indexstore_occurrence_get_symbol(obj); }
+ uint64_t getRoles() { return indexstore_occurrence_get_roles(obj); }
+
+ bool foreachRelation(llvm::function_ref<bool(IndexSymbolRelation)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_occurrence_relations_apply(obj, ^bool(indexstore_symbol_relation_t sym_rel) {
+ return receiver(sym_rel);
+ });
+#else
+ return false;
+#endif
+ }
+
+ std::pair<unsigned, unsigned> getLineCol() {
+ unsigned line, col;
+ indexstore_occurrence_get_line_col(obj, &line, &col);
+ return std::make_pair(line, col);
+ }
+};
+
+class IndexStore;
+typedef std::shared_ptr<IndexStore> IndexStoreRef;
+
+class IndexStore {
+ indexstore_t obj;
+ friend class IndexRecordReader;
+ friend class IndexUnitReader;
+
+public:
+ IndexStore(StringRef path, std::string &error) {
+ llvm::SmallString<64> buf = path;
+ indexstore_error_t c_err = nullptr;
+ obj = indexstore_store_create(buf.c_str(), &c_err);
+ if (c_err) {
+ error = indexstore_error_get_description(c_err);
+ indexstore_error_dispose(c_err);
+ }
+ }
+
+ IndexStore(IndexStore &&other) : obj(other.obj) {
+ other.obj = nullptr;
+ }
+
+ ~IndexStore() {
+ indexstore_store_dispose(obj);
+ }
+
+ static IndexStoreRef create(StringRef path, std::string &error) {
+ auto storeRef = std::make_shared<IndexStore>(path, error);
+ if (storeRef->isInvalid())
+ return nullptr;
+ return storeRef;
+ }
+
+ static unsigned formatVersion() {
+ return indexstore_format_version();
+ }
+
+ bool isValid() const { return obj; }
+ bool isInvalid() const { return !isValid(); }
+ explicit operator bool() const { return isValid(); }
+
+ bool foreachUnit(bool sorted, llvm::function_ref<bool(StringRef unitName)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_store_units_apply(obj, sorted, ^bool(indexstore_string_ref_t unit_name) {
+ return receiver(stringFromIndexStoreStringRef(unit_name));
+ });
+#else
+ return false;
+#endif
+ }
+
+ class UnitEvent {
+ indexstore_unit_event_t obj;
+ public:
+ UnitEvent(indexstore_unit_event_t obj) : obj(obj) {}
+
+ enum class Kind {
+ Added,
+ Removed,
+ Modified,
+ DirectoryDeleted,
+ };
+ Kind getKind() const {
+ indexstore_unit_event_kind_t c_k = indexstore_unit_event_get_kind(obj);
+ Kind K;
+ switch (c_k) {
+ case INDEXSTORE_UNIT_EVENT_ADDED: K = Kind::Added; break;
+ case INDEXSTORE_UNIT_EVENT_REMOVED: K = Kind::Removed; break;
+ case INDEXSTORE_UNIT_EVENT_MODIFIED: K = Kind::Modified; break;
+ case INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED: K = Kind::DirectoryDeleted; break;
+ }
+ return K;
+ }
+
+ StringRef getUnitName() const {
+ return stringFromIndexStoreStringRef(indexstore_unit_event_get_unit_name(obj));
+ }
+
+ timespec getModificationTime() const { return indexstore_unit_event_get_modification_time(obj); }
+ };
+
+ class UnitEventNotification {
+ indexstore_unit_event_notification_t obj;
+ public:
+ UnitEventNotification(indexstore_unit_event_notification_t obj) : obj(obj) {}
+
+ bool isInitial() const { return indexstore_unit_event_notification_is_initial(obj); }
+ size_t getEventsCount() const { return indexstore_unit_event_notification_get_events_count(obj); }
+ UnitEvent getEvent(size_t index) const { return indexstore_unit_event_notification_get_event(obj, index); }
+ };
+
+ typedef std::function<void(UnitEventNotification)> UnitEventHandler;
+
+ void setUnitEventHandler(UnitEventHandler handler) {
+#if INDEXSTORE_HAS_BLOCKS
+ if (!handler) {
+ indexstore_store_set_unit_event_handler(obj, nullptr);
+ return;
+ }
+
+ indexstore_store_set_unit_event_handler(obj, ^(indexstore_unit_event_notification_t evt_note) {
+ handler(UnitEventNotification(evt_note));
+ });
+#endif
+ }
+
+ bool startEventListening(bool waitInitialSync, std::string &error) {
+ indexstore_unit_event_listen_options_t opts;
+ opts.wait_initial_sync = waitInitialSync;
+ indexstore_error_t c_err = nullptr;
+ bool ret = indexstore_store_start_unit_event_listening(obj, &opts, sizeof(opts), &c_err);
+ if (c_err) {
+ error = indexstore_error_get_description(c_err);
+ indexstore_error_dispose(c_err);
+ }
+ return ret;
+ }
+
+ void stopEventListening() {
+ return indexstore_store_stop_unit_event_listening(obj);
+ }
+
+ void discardUnit(StringRef UnitName) {
+ llvm::SmallString<64> buf = UnitName;
+ indexstore_store_discard_unit(obj, buf.c_str());
+ }
+
+ void discardRecord(StringRef RecordName) {
+ llvm::SmallString<64> buf = RecordName;
+ indexstore_store_discard_record(obj, buf.c_str());
+ }
+
+ void getUnitNameFromOutputPath(StringRef outputPath, llvm::SmallVectorImpl<char> &nameBuf) {
+ llvm::SmallString<256> buf = outputPath;
+ size_t nameLen = indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), nameBuf.data(), nameBuf.size());
+ if (nameLen+1 > nameBuf.size()) {
+ nameBuf.resize(nameLen+1);
+ indexstore_store_get_unit_name_from_output_path(obj, buf.c_str(), nameBuf.data(), nameBuf.size());
+ }
+ }
+
+ llvm::Optional<timespec>
+ getUnitModificationTime(StringRef unitName, std::string &error) {
+ llvm::SmallString<64> buf = unitName;
+ int64_t seconds, nanoseconds;
+ indexstore_error_t c_err = nullptr;
+ bool err = indexstore_store_get_unit_modification_time(obj, buf.c_str(),
+ &seconds, &nanoseconds, &c_err);
+ if (err && c_err) {
+ error = indexstore_error_get_description(c_err);
+ indexstore_error_dispose(c_err);
+ return llvm::None;
+ }
+ timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nanoseconds;
+ return ts;
+ }
+
+ void purgeStaleData() {
+ indexstore_store_purge_stale_data(obj);
+ }
+};
+
+class IndexRecordReader {
+ indexstore_record_reader_t obj;
+
+public:
+ IndexRecordReader(IndexStore &store, StringRef recordName, std::string &error) {
+ llvm::SmallString<64> buf = recordName;
+ indexstore_error_t c_err = nullptr;
+ obj = indexstore_record_reader_create(store.obj, buf.c_str(), &c_err);
+ if (c_err) {
+ error = indexstore_error_get_description(c_err);
+ indexstore_error_dispose(c_err);
+ }
+ }
+
+ IndexRecordReader(IndexRecordReader &&other) : obj(other.obj) {
+ other.obj = nullptr;
+ }
+
+ ~IndexRecordReader() {
+ indexstore_record_reader_dispose(obj);
+ }
+
+ bool isValid() const { return obj; }
+ bool isInvalid() const { return !isValid(); }
+ explicit operator bool() const { return isValid(); }
+
+ /// Goes through and passes record decls, after filtering using a \c Checker
+ /// function.
+ ///
+ /// Resulting decls can be used as filter for \c foreachOccurrence. This
+ /// allows allocating memory only for the record decls that the caller is
+ /// interested in.
+ bool searchSymbols(llvm::function_ref<bool(IndexRecordSymbol, bool &stop)> filter,
+ llvm::function_ref<void(IndexRecordSymbol)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_record_reader_search_symbols(obj, ^bool(indexstore_symbol_t symbol, bool *stop) {
+ return filter(symbol, *stop);
+ }, ^(indexstore_symbol_t symbol) {
+ receiver(symbol);
+ });
+#else
+ return false;
+#endif
+ }
+
+ bool foreachSymbol(bool noCache, llvm::function_ref<bool(IndexRecordSymbol)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_record_reader_symbols_apply(obj, noCache, ^bool(indexstore_symbol_t sym) {
+ return receiver(sym);
+ });
+#else
+ return false;
+#endif
+ }
+
+ /// \param DeclsFilter if non-empty indicates the list of decls that we want
+ /// to get occurrences for. An empty array indicates that we want occurrences
+ /// for all decls.
+ /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls.
+ bool foreachOccurrence(ArrayRef<IndexRecordSymbol> symbolsFilter,
+ ArrayRef<IndexRecordSymbol> relatedSymbolsFilter,
+ llvm::function_ref<bool(IndexRecordOccurrence)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ llvm::SmallVector<indexstore_symbol_t, 16> c_symbolsFilter;
+ c_symbolsFilter.reserve(symbolsFilter.size());
+ for (IndexRecordSymbol sym : symbolsFilter) {
+ c_symbolsFilter.push_back(sym.obj);
+ }
+ llvm::SmallVector<indexstore_symbol_t, 16> c_relatedSymbolsFilter;
+ c_relatedSymbolsFilter.reserve(relatedSymbolsFilter.size());
+ for (IndexRecordSymbol sym : relatedSymbolsFilter) {
+ c_relatedSymbolsFilter.push_back(sym.obj);
+ }
+ return indexstore_record_reader_occurrences_of_symbols_apply(obj,
+ c_symbolsFilter.data(), c_symbolsFilter.size(),
+ c_relatedSymbolsFilter.data(),
+ c_relatedSymbolsFilter.size(),
+ ^bool(indexstore_occurrence_t occur) {
+ return receiver(occur);
+ });
+#else
+ return false;
+#endif
+ }
+
+ bool foreachOccurrence(
+ llvm::function_ref<bool(IndexRecordOccurrence)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_record_reader_occurrences_apply(obj, ^bool(indexstore_occurrence_t occur) {
+ return receiver(occur);
+ });
+#else
+ return false;
+#endif
+ }
+
+ bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineEnd,
+ llvm::function_ref<bool(IndexRecordOccurrence)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_record_reader_occurrences_in_line_range_apply(obj,
+ lineStart,
+ lineEnd,
+ ^bool(indexstore_occurrence_t occur) {
+ return receiver(occur);
+ });
+#else
+ return false;
+#endif
+ }
+};
+
+class IndexUnitDependency {
+ indexstore_unit_dependency_t obj;
+ friend class IndexUnitReader;
+
+public:
+ IndexUnitDependency(indexstore_unit_dependency_t obj) : obj(obj) {}
+
+ enum class DependencyKind {
+ Unit,
+ Record,
+ File,
+ };
+ DependencyKind getKind() {
+ switch (indexstore_unit_dependency_get_kind(obj)) {
+ case INDEXSTORE_UNIT_DEPENDENCY_UNIT: return DependencyKind::Unit;
+ case INDEXSTORE_UNIT_DEPENDENCY_RECORD: return DependencyKind::Record;
+ case INDEXSTORE_UNIT_DEPENDENCY_FILE: return DependencyKind::File;
+ }
+ }
+ bool isSystem() { return indexstore_unit_dependency_is_system(obj); }
+ StringRef getName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_name(obj)); }
+ StringRef getFilePath() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_filepath(obj)); }
+ StringRef getModuleName() { return stringFromIndexStoreStringRef(indexstore_unit_dependency_get_modulename(obj)); }
+ time_t getModificationTime() { return indexstore_unit_dependency_get_modification_time(obj); }
+ size_t getFileSize() { return indexstore_unit_dependency_get_file_size(obj); }
+
+};
+
+class IndexUnitInclude {
+ indexstore_unit_include_t obj;
+ friend class IndexUnitReader;
+
+public:
+ IndexUnitInclude(indexstore_unit_include_t obj) : obj(obj) {}
+
+ StringRef getSourcePath() {
+ return stringFromIndexStoreStringRef(indexstore_unit_include_get_source_path(obj));
+ }
+ StringRef getTargetPath() {
+ return stringFromIndexStoreStringRef(indexstore_unit_include_get_target_path(obj));
+ }
+ unsigned getSourceLine() {
+ return indexstore_unit_include_get_source_line(obj);
+ }
+};
+
+class IndexUnitReader {
+ indexstore_unit_reader_t obj;
+
+public:
+ IndexUnitReader(IndexStore &store, StringRef unitName, std::string &error) {
+ llvm::SmallString<64> buf = unitName;
+ indexstore_error_t c_err = nullptr;
+ obj = indexstore_unit_reader_create(store.obj, buf.c_str(), &c_err);
+ if (c_err) {
+ error = indexstore_error_get_description(c_err);
+ indexstore_error_dispose(c_err);
+ }
+ }
+
+ IndexUnitReader(IndexUnitReader &&other) : obj(other.obj) {
+ other.obj = nullptr;
+ }
+
+ ~IndexUnitReader() {
+ indexstore_unit_reader_dispose(obj);
+ }
+
+ bool isValid() const { return obj; }
+ bool isInvalid() const { return !isValid(); }
+ explicit operator bool() const { return isValid(); }
+
+ StringRef getProviderIdentifier() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_provider_identifier(obj));
+ }
+ StringRef getProviderVersion() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_provider_version(obj));
+ }
+
+ timespec getModificationTime() {
+ int64_t seconds, nanoseconds;
+ indexstore_unit_reader_get_modification_time(obj, &seconds, &nanoseconds);
+ timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nanoseconds;
+ return ts;
+ }
+
+ bool isSystemUnit() { return indexstore_unit_reader_is_system_unit(obj); }
+ bool isModuleUnit() { return indexstore_unit_reader_is_module_unit(obj); }
+ bool isDebugCompilation() { return indexstore_unit_reader_is_debug_compilation(obj); }
+ bool hasMainFile() { return indexstore_unit_reader_has_main_file(obj); }
+
+ StringRef getMainFilePath() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_main_file(obj));
+ }
+ StringRef getModuleName() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_module_name(obj));
+ }
+ StringRef getWorkingDirectory() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_working_dir(obj));
+ }
+ StringRef getOutputFile() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_output_file(obj));
+ }
+ StringRef getSysrootPath() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_sysroot_path(obj));
+ }
+ StringRef getTarget() {
+ return stringFromIndexStoreStringRef(indexstore_unit_reader_get_target(obj));
+ }
+
+ bool foreachDependency(llvm::function_ref<bool(IndexUnitDependency)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_unit_reader_dependencies_apply(obj, ^bool(indexstore_unit_dependency_t dep) {
+ return receiver(dep);
+ });
+#else
+ return false;
+#endif
+ }
+
+ bool foreachInclude(llvm::function_ref<bool(IndexUnitInclude)> receiver) {
+#if INDEXSTORE_HAS_BLOCKS
+ return indexstore_unit_reader_includes_apply(obj, ^bool(indexstore_unit_include_t inc) {
+ return receiver(inc);
+ });
+#else
+ return false;
+#endif
+ }
+};
+
+} // namespace indexstore
+
+#endif
diff --git a/include/indexstore/indexstore.h b/include/indexstore/indexstore.h
new file mode 100644
index 0000000..f4f41db
--- /dev/null
+++ b/include/indexstore/indexstore.h
@@ -0,0 +1,490 @@
+/*===-- indexstore/indexstore.h - Index Store C API ----------------- C -*-===*\
+|* *|
+|* The LLVM Compiler Infrastructure *|
+|* *|
+|* This file is distributed under the University of Illinois Open Source *|
+|* License. See LICENSE.TXT for details. *|
+|* *|
+|*===----------------------------------------------------------------------===*|
+|* *|
+|* This header provides a C API for the index store. *|
+|* *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H
+#define LLVM_CLANG_C_INDEXSTORE_INDEXSTORE_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <ctime>
+
+/**
+ * \brief The version constants for the Index Store C API.
+ * INDEXSTORE_VERSION_MINOR should increase when there are API additions.
+ * INDEXSTORE_VERSION_MAJOR is intended for "major" source/ABI breaking changes.
+ */
+#define INDEXSTORE_VERSION_MAJOR 0
+#define INDEXSTORE_VERSION_MINOR 9
+
+#define INDEXSTORE_VERSION_ENCODE(major, minor) ( \
+ ((major) * 10000) \
+ + ((minor) * 1))
+
+#define INDEXSTORE_VERSION INDEXSTORE_VERSION_ENCODE( \
+ INDEXSTORE_VERSION_MAJOR, \
+ INDEXSTORE_VERSION_MINOR )
+
+#define INDEXSTORE_VERSION_STRINGIZE_(major, minor) \
+ #major"."#minor
+#define INDEXSTORE_VERSION_STRINGIZE(major, minor) \
+ INDEXSTORE_VERSION_STRINGIZE_(major, minor)
+
+#define INDEXSTORE_VERSION_STRING INDEXSTORE_VERSION_STRINGIZE( \
+ INDEXSTORE_VERSION_MAJOR, \
+ INDEXSTORE_VERSION_MINOR)
+
+#ifdef __cplusplus
+# define INDEXSTORE_BEGIN_DECLS extern "C" {
+# define INDEXSTORE_END_DECLS }
+#else
+# define INDEXSTORE_BEGIN_DECLS
+# define INDEXSTORE_END_DECLS
+#endif
+
+#ifndef INDEXSTORE_PUBLIC
+# if defined (_MSC_VER)
+# define INDEXSTORE_PUBLIC __declspec(dllimport)
+# else
+# define INDEXSTORE_PUBLIC
+# endif
+#endif
+
+#ifndef __has_feature
+# define __has_feature(x) 0
+#endif
+
+#if __has_feature(blocks)
+# define INDEXSTORE_HAS_BLOCKS 1
+#else
+# define INDEXSTORE_HAS_BLOCKS 0
+#endif
+
+INDEXSTORE_BEGIN_DECLS
+
+typedef void *indexstore_error_t;
+
+INDEXSTORE_PUBLIC const char *
+indexstore_error_get_description(indexstore_error_t);
+
+INDEXSTORE_PUBLIC void
+indexstore_error_dispose(indexstore_error_t);
+
+typedef struct {
+ const char *data;
+ size_t length;
+} indexstore_string_ref_t;
+
+INDEXSTORE_PUBLIC unsigned
+indexstore_format_version(void);
+
+typedef void *indexstore_t;
+
+INDEXSTORE_PUBLIC indexstore_t
+indexstore_store_create(const char *store_path, indexstore_error_t *error);
+
+INDEXSTORE_PUBLIC void
+indexstore_store_dispose(indexstore_t);
+
+#if INDEXSTORE_HAS_BLOCKS
+INDEXSTORE_PUBLIC bool
+indexstore_store_units_apply(indexstore_t, unsigned sorted,
+ bool(^applier)(indexstore_string_ref_t unit_name));
+#endif
+
+typedef void *indexstore_unit_event_notification_t;
+typedef void *indexstore_unit_event_t;
+
+INDEXSTORE_PUBLIC size_t
+indexstore_unit_event_notification_get_events_count(indexstore_unit_event_notification_t);
+
+INDEXSTORE_PUBLIC indexstore_unit_event_t
+indexstore_unit_event_notification_get_event(indexstore_unit_event_notification_t, size_t index);
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_event_notification_is_initial(indexstore_unit_event_notification_t);
+
+typedef enum {
+ INDEXSTORE_UNIT_EVENT_ADDED = 1,
+ INDEXSTORE_UNIT_EVENT_REMOVED = 2,
+ INDEXSTORE_UNIT_EVENT_MODIFIED = 3,
+ INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED = 4,
+} indexstore_unit_event_kind_t;
+
+INDEXSTORE_PUBLIC indexstore_unit_event_kind_t
+indexstore_unit_event_get_kind(indexstore_unit_event_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_event_get_unit_name(indexstore_unit_event_t);
+
+INDEXSTORE_PUBLIC timespec
+indexstore_unit_event_get_modification_time(indexstore_unit_event_t);
+
+#if INDEXSTORE_HAS_BLOCKS
+typedef void (^indexstore_unit_event_handler_t)(indexstore_unit_event_notification_t);
+
+INDEXSTORE_PUBLIC void
+indexstore_store_set_unit_event_handler(indexstore_t,
+ indexstore_unit_event_handler_t handler);
+#endif
+
+typedef struct {
+ /// If true, \c indexstore_store_start_unit_event_listening will block until
+ /// the initial set of units is passed to the unit event handler, otherwise
+ /// the function will return and the initial set will be passed asynchronously.
+ bool wait_initial_sync;
+} indexstore_unit_event_listen_options_t;
+
+INDEXSTORE_PUBLIC bool
+indexstore_store_start_unit_event_listening(indexstore_t,
+ indexstore_unit_event_listen_options_t *,
+ size_t listen_options_struct_size,
+ indexstore_error_t *error);
+
+INDEXSTORE_PUBLIC void
+indexstore_store_stop_unit_event_listening(indexstore_t);
+
+INDEXSTORE_PUBLIC void
+indexstore_store_discard_unit(indexstore_t, const char *unit_name);
+
+INDEXSTORE_PUBLIC void
+indexstore_store_discard_record(indexstore_t, const char *record_name);
+
+INDEXSTORE_PUBLIC void
+indexstore_store_purge_stale_data(indexstore_t);
+
+/// Determines the unit name from the \c output_path and writes it out in the
+/// \c name_buf buffer. It doesn't write more than \c buf_size.
+/// \returns the length of the name. If this is larger than \c buf_size, the
+/// caller should call the function again with a buffer of the appropriate size.
+INDEXSTORE_PUBLIC size_t
+indexstore_store_get_unit_name_from_output_path(indexstore_t store,
+ const char *output_path,
+ char *name_buf,
+ size_t buf_size);
+
+/// \returns true if an error occurred, false otherwise.
+INDEXSTORE_PUBLIC bool
+indexstore_store_get_unit_modification_time(indexstore_t store,
+ const char *unit_name,
+ int64_t *seconds,
+ int64_t *nanoseconds,
+ indexstore_error_t *error);
+
+typedef void *indexstore_symbol_t;
+
+typedef enum {
+ INDEXSTORE_SYMBOL_KIND_UNKNOWN = 0,
+ INDEXSTORE_SYMBOL_KIND_MODULE = 1,
+ INDEXSTORE_SYMBOL_KIND_NAMESPACE = 2,
+ INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS = 3,
+ INDEXSTORE_SYMBOL_KIND_MACRO = 4,
+ INDEXSTORE_SYMBOL_KIND_ENUM = 5,
+ INDEXSTORE_SYMBOL_KIND_STRUCT = 6,
+ INDEXSTORE_SYMBOL_KIND_CLASS = 7,
+ INDEXSTORE_SYMBOL_KIND_PROTOCOL = 8,
+ INDEXSTORE_SYMBOL_KIND_EXTENSION = 9,
+ INDEXSTORE_SYMBOL_KIND_UNION = 10,
+ INDEXSTORE_SYMBOL_KIND_TYPEALIAS = 11,
+ INDEXSTORE_SYMBOL_KIND_FUNCTION = 12,
+ INDEXSTORE_SYMBOL_KIND_VARIABLE = 13,
+ INDEXSTORE_SYMBOL_KIND_FIELD = 14,
+ INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT = 15,
+ INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD = 16,
+ INDEXSTORE_SYMBOL_KIND_CLASSMETHOD = 17,
+ INDEXSTORE_SYMBOL_KIND_STATICMETHOD = 18,
+ INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY = 19,
+ INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY = 20,
+ INDEXSTORE_SYMBOL_KIND_STATICPROPERTY = 21,
+ INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR = 22,
+ INDEXSTORE_SYMBOL_KIND_DESTRUCTOR = 23,
+ INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION = 24,
+ INDEXSTORE_SYMBOL_KIND_PARAMETER = 25,
+ INDEXSTORE_SYMBOL_KIND_USING = 26,
+
+ INDEXSTORE_SYMBOL_KIND_COMMENTTAG = 1000,
+} indexstore_symbol_kind_t;
+
+typedef enum {
+ INDEXSTORE_SYMBOL_SUBKIND_NONE = 0,
+ INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR = 1,
+ INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR = 2,
+ INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER = 3,
+ INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER = 4,
+ INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME = 5,
+ INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE = 6,
+
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET = 1000,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET = 1001,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR = 1002,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR = 1003,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT = 1004,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS = 1005,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM = 1006,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL = 1007,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR = 1008,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR = 1009,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR = 1010,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT = 1011,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE = 1012,
+ INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM = 1013,
+} indexstore_symbol_subkind_t;
+
+typedef enum {
+ INDEXSTORE_SYMBOL_PROPERTY_GENERIC = 1 << 0,
+ INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION = 1 << 1,
+ INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION = 1 << 2,
+ INDEXSTORE_SYMBOL_PROPERTY_UNITTEST = 1 << 3,
+ INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED = 1 << 4,
+ INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION = 1 << 5,
+ INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE = 1 << 6,
+ INDEXSTORE_SYMBOL_PROPERTY_LOCAL = 1 << 7,
+} indexstore_symbol_property_t;
+
+typedef enum {
+ INDEXSTORE_SYMBOL_LANG_C = 0,
+ INDEXSTORE_SYMBOL_LANG_OBJC = 1,
+ INDEXSTORE_SYMBOL_LANG_CXX = 2,
+
+ INDEXSTORE_SYMBOL_LANG_SWIFT = 100,
+} indexstore_symbol_language_t;
+
+typedef enum {
+ INDEXSTORE_SYMBOL_ROLE_DECLARATION = 1 << 0,
+ INDEXSTORE_SYMBOL_ROLE_DEFINITION = 1 << 1,
+ INDEXSTORE_SYMBOL_ROLE_REFERENCE = 1 << 2,
+ INDEXSTORE_SYMBOL_ROLE_READ = 1 << 3,
+ INDEXSTORE_SYMBOL_ROLE_WRITE = 1 << 4,
+ INDEXSTORE_SYMBOL_ROLE_CALL = 1 << 5,
+ INDEXSTORE_SYMBOL_ROLE_DYNAMIC = 1 << 6,
+ INDEXSTORE_SYMBOL_ROLE_ADDRESSOF = 1 << 7,
+ INDEXSTORE_SYMBOL_ROLE_IMPLICIT = 1 << 8,
+
+ // Relation roles.
+ INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF = 1 << 9,
+ INDEXSTORE_SYMBOL_ROLE_REL_BASEOF = 1 << 10,
+ INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF = 1 << 11,
+ INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY = 1 << 12,
+ INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY = 1 << 13,
+ INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY = 1 << 14,
+ INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF = 1 << 15,
+ INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY = 1 << 16,
+ INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF = 1 << 17,
+ INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF = 1 << 18,
+} indexstore_symbol_role_t;
+
+INDEXSTORE_PUBLIC indexstore_symbol_language_t
+indexstore_symbol_get_language(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC indexstore_symbol_kind_t
+indexstore_symbol_get_kind(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC indexstore_symbol_subkind_t
+indexstore_symbol_get_subkind(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC uint64_t
+indexstore_symbol_get_properties(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC uint64_t
+indexstore_symbol_get_roles(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC uint64_t
+indexstore_symbol_get_related_roles(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_symbol_get_name(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_symbol_get_usr(indexstore_symbol_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_symbol_get_codegen_name(indexstore_symbol_t);
+
+typedef void *indexstore_symbol_relation_t;
+
+INDEXSTORE_PUBLIC uint64_t
+indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t);
+
+INDEXSTORE_PUBLIC indexstore_symbol_t
+indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t);
+
+typedef void *indexstore_occurrence_t;
+
+INDEXSTORE_PUBLIC indexstore_symbol_t
+indexstore_occurrence_get_symbol(indexstore_occurrence_t);
+
+#if INDEXSTORE_HAS_BLOCKS
+INDEXSTORE_PUBLIC bool
+indexstore_occurrence_relations_apply(indexstore_occurrence_t,
+ bool(^applier)(indexstore_symbol_relation_t symbol_rel));
+#endif
+
+INDEXSTORE_PUBLIC uint64_t
+indexstore_occurrence_get_roles(indexstore_occurrence_t);
+
+INDEXSTORE_PUBLIC void
+indexstore_occurrence_get_line_col(indexstore_occurrence_t,
+ unsigned *line, unsigned *column);
+
+typedef void *indexstore_record_reader_t;
+
+INDEXSTORE_PUBLIC indexstore_record_reader_t
+indexstore_record_reader_create(indexstore_t store, const char *record_name,
+ indexstore_error_t *error);
+
+INDEXSTORE_PUBLIC void
+indexstore_record_reader_dispose(indexstore_record_reader_t);
+
+#if INDEXSTORE_HAS_BLOCKS
+/// Goes through the symbol data and passes symbols to \c receiver, for the
+/// symbol data that \c filter returns true on.
+///
+/// This allows allocating memory only for the record symbols that the caller is
+/// interested in.
+INDEXSTORE_PUBLIC bool
+indexstore_record_reader_search_symbols(indexstore_record_reader_t,
+ bool(^filter)(indexstore_symbol_t symbol, bool *stop),
+ void(^receiver)(indexstore_symbol_t symbol));
+
+/// \param nocache if true, avoids allocating memory for the symbols.
+/// Useful when the caller does not intend to keep \c indexstore_record_reader_t
+/// for more queries.
+INDEXSTORE_PUBLIC bool
+indexstore_record_reader_symbols_apply(indexstore_record_reader_t,
+ bool nocache,
+ bool(^applier)(indexstore_symbol_t symbol));
+
+INDEXSTORE_PUBLIC bool
+indexstore_record_reader_occurrences_apply(indexstore_record_reader_t,
+ bool(^applier)(indexstore_occurrence_t occur));
+
+INDEXSTORE_PUBLIC bool
+indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t,
+ unsigned line_start,
+ unsigned line_count,
+ bool(^applier)(indexstore_occurrence_t occur));
+
+/// \param symbols if non-zero \c symbols_count, indicates the list of symbols
+/// that we want to get occurrences for. An empty array indicates that we want
+/// occurrences for all symbols.
+/// \param related_symbols Same as \c symbols but for related symbols.
+INDEXSTORE_PUBLIC bool
+indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t,
+ indexstore_symbol_t *symbols, size_t symbols_count,
+ indexstore_symbol_t *related_symbols, size_t related_symbols_count,
+ bool(^applier)(indexstore_occurrence_t occur));
+#endif
+
+
+typedef void *indexstore_unit_reader_t;
+
+INDEXSTORE_PUBLIC indexstore_unit_reader_t
+indexstore_unit_reader_create(indexstore_t store, const char *unit_name,
+ indexstore_error_t *error);
+
+INDEXSTORE_PUBLIC void
+indexstore_unit_reader_dispose(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC void
+indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t,
+ int64_t *seconds,
+ int64_t *nanoseconds);
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_reader_has_main_file(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_main_file(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_module_name(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_output_file(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_reader_get_target(indexstore_unit_reader_t);
+
+typedef void *indexstore_unit_dependency_t;
+typedef void *indexstore_unit_include_t;
+
+typedef enum {
+ INDEXSTORE_UNIT_DEPENDENCY_UNIT = 1,
+ INDEXSTORE_UNIT_DEPENDENCY_RECORD = 2,
+ INDEXSTORE_UNIT_DEPENDENCY_FILE = 3,
+} indexstore_unit_dependency_kind_t;
+
+INDEXSTORE_PUBLIC indexstore_unit_dependency_kind_t
+indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_dependency_is_system(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_dependency_get_name(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC time_t
+indexstore_unit_dependency_get_modification_time(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC size_t
+indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_include_get_source_path(indexstore_unit_include_t);
+
+INDEXSTORE_PUBLIC indexstore_string_ref_t
+indexstore_unit_include_get_target_path(indexstore_unit_include_t);
+
+INDEXSTORE_PUBLIC unsigned
+indexstore_unit_include_get_source_line(indexstore_unit_include_t);
+
+#if INDEXSTORE_HAS_BLOCKS
+INDEXSTORE_PUBLIC bool
+indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t,
+ bool(^applier)(indexstore_unit_dependency_t));
+
+INDEXSTORE_PUBLIC bool
+indexstore_unit_reader_includes_apply(indexstore_unit_reader_t,
+ bool(^applier)(indexstore_unit_include_t));
+
+#endif
+
+INDEXSTORE_END_DECLS
+
+#endif
diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h
new file mode 100644
index 0000000..3e1b2d5
--- /dev/null
+++ b/lib/APINotes/APINotesFormat.h
@@ -0,0 +1,308 @@
+//===--- APINotesFormat.h - The internals of API notes files ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Contains various constants and helper types to deal with API notes
+/// files.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_API_NOTES_FORMAT_H
+#define LLVM_CLANG_API_NOTES_FORMAT_H
+
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/PointerEmbeddedInt.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Bitcode/RecordLayout.h"
+
+namespace clang {
+namespace api_notes {
+
+using namespace llvm;
+
+/// Magic number for API notes files.
+const unsigned char API_NOTES_SIGNATURE[] = { 0xE2, 0x9C, 0xA8, 0x01 };
+
+/// API notes file major version number.
+///
+const uint16_t VERSION_MAJOR = 0;
+
+/// API notes file minor version number.
+///
+/// When the format changes IN ANY WAY, this number should be incremented.
+const uint16_t VERSION_MINOR = 24; // EnumExtensibility+FlagEnum
+
+using IdentifierID = PointerEmbeddedInt<unsigned, 31>;
+using IdentifierIDField = BCVBR<16>;
+
+using SelectorID = PointerEmbeddedInt<unsigned, 31>;
+using SelectorIDField = BCVBR<16>;
+
+using StoredContextID = PointerEmbeddedInt<unsigned, 31>;
+
+/// The various types of blocks that can occur within a API notes file.
+///
+/// These IDs must \em not be renumbered or reordered without incrementing
+/// VERSION_MAJOR.
+enum BlockID {
+ /// The control block, which contains all of the information that needs to
+ /// be validated prior to committing to loading the API notes file.
+ ///
+ /// \sa control_block
+ CONTROL_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
+
+ /// The identifier data block, which maps identifier strings to IDs.
+ IDENTIFIER_BLOCK_ID,
+
+ /// The Objective-C context data block, which contains information about
+ /// Objective-C classes and protocols.
+ OBJC_CONTEXT_BLOCK_ID,
+
+ /// The Objective-C property data block, which maps Objective-C
+ /// (class name, property name) pairs to information about the
+ /// property.
+ OBJC_PROPERTY_BLOCK_ID,
+
+ /// The Objective-C property data block, which maps Objective-C
+ /// (class name, selector, is_instance_method) tuples to information
+ /// about the method.
+ OBJC_METHOD_BLOCK_ID,
+
+ /// The Objective-C selector data block, which maps Objective-C
+ /// selector names (# of pieces, identifier IDs) to the selector ID
+ /// used in other tables.
+ OBJC_SELECTOR_BLOCK_ID,
+
+ /// The global variables data block, which maps global variable names to
+ /// information about the global variable.
+ GLOBAL_VARIABLE_BLOCK_ID,
+
+ /// The (global) functions data block, which maps global function names to
+ /// information about the global function.
+ GLOBAL_FUNCTION_BLOCK_ID,
+
+ /// The tag data block, which maps tag names to information about
+ /// the tags.
+ TAG_BLOCK_ID,
+
+ /// The typedef data block, which maps typedef names to information about
+ /// the typedefs.
+ TYPEDEF_BLOCK_ID,
+
+ /// The enum constant data block, which maps enumerator names to
+ /// information about the enumerators.
+ ENUM_CONSTANT_BLOCK_ID,
+};
+
+namespace control_block {
+ // These IDs must \em not be renumbered or reordered without incrementing
+ // VERSION_MAJOR.
+ enum {
+ METADATA = 1,
+ MODULE_NAME = 2,
+ MODULE_OPTIONS = 3,
+ SOURCE_FILE = 4,
+ };
+
+ using MetadataLayout = BCRecordLayout<
+ METADATA, // ID
+ BCFixed<16>, // Module format major version
+ BCFixed<16> // Module format minor version
+ >;
+
+ using ModuleNameLayout = BCRecordLayout<
+ MODULE_NAME,
+ BCBlob // Module name
+ >;
+
+ using ModuleOptionsLayout = BCRecordLayout<
+ MODULE_OPTIONS,
+ BCFixed<1> // SwiftInferImportAsMember
+ >;
+
+ using SourceFileLayout = BCRecordLayout<
+ SOURCE_FILE,
+ BCVBR<16>, // file size
+ BCVBR<16> // creation time
+ >;
+}
+
+namespace identifier_block {
+ enum {
+ IDENTIFIER_DATA = 1,
+ };
+
+ using IdentifierDataLayout = BCRecordLayout<
+ IDENTIFIER_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from identifier strings to decl kinds / decl IDs
+ >;
+}
+
+namespace objc_context_block {
+ enum {
+ OBJC_CONTEXT_ID_DATA = 1,
+ OBJC_CONTEXT_INFO_DATA = 2,
+ };
+
+ using ObjCContextIDLayout = BCRecordLayout<
+ OBJC_CONTEXT_ID_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC class names/protocol (as IDs) to context IDs
+ >;
+
+ using ObjCContextInfoLayout = BCRecordLayout<
+ OBJC_CONTEXT_INFO_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC context IDs to context information.
+ >;
+}
+
+namespace objc_property_block {
+ enum {
+ OBJC_PROPERTY_DATA = 1,
+ };
+
+ using ObjCPropertyDataLayout = BCRecordLayout<
+ OBJC_PROPERTY_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC (class name, property name) pairs to ObjC
+ // property information
+ >;
+}
+
+namespace objc_method_block {
+ enum {
+ OBJC_METHOD_DATA = 1,
+ };
+
+ using ObjCMethodDataLayout = BCRecordLayout<
+ OBJC_METHOD_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from ObjC (class names, selector,
+ // is-instance-method) tuples to ObjC method information
+ >;
+}
+
+namespace objc_selector_block {
+ enum {
+ OBJC_SELECTOR_DATA = 1,
+ };
+
+ using ObjCSelectorDataLayout = BCRecordLayout<
+ OBJC_SELECTOR_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from (# pieces, identifier IDs) to Objective-C selector ID.
+ >;
+}
+
+namespace global_variable_block {
+ enum {
+ GLOBAL_VARIABLE_DATA = 1
+ };
+
+ using GlobalVariableDataLayout = BCRecordLayout<
+ GLOBAL_VARIABLE_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to global variable information
+ >;
+}
+
+namespace global_function_block {
+ enum {
+ GLOBAL_FUNCTION_DATA = 1
+ };
+
+ using GlobalFunctionDataLayout = BCRecordLayout<
+ GLOBAL_FUNCTION_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to global function information
+ >;
+}
+
+namespace tag_block {
+ enum {
+ TAG_DATA = 1
+ };
+
+ using TagDataLayout = BCRecordLayout<
+ TAG_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to tag information
+ >;
+};
+
+namespace typedef_block {
+ enum {
+ TYPEDEF_DATA = 1
+ };
+
+ using TypedefDataLayout = BCRecordLayout<
+ TYPEDEF_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to typedef information
+ >;
+};
+
+namespace enum_constant_block {
+ enum {
+ ENUM_CONSTANT_DATA = 1
+ };
+
+ using EnumConstantDataLayout = BCRecordLayout<
+ ENUM_CONSTANT_DATA, // record ID
+ BCVBR<16>, // table offset within the blob (see below)
+ BCBlob // map from name to enumerator information
+ >;
+}
+
+/// A stored Objective-C selector.
+struct StoredObjCSelector {
+ unsigned NumPieces;
+ llvm::SmallVector<IdentifierID, 2> Identifiers;
+};
+
+} // end namespace api_notes
+} // end namespace clang
+
+namespace llvm {
+ template<>
+ struct DenseMapInfo<clang::api_notes::StoredObjCSelector> {
+ typedef DenseMapInfo<unsigned> UnsignedInfo;
+
+ static inline clang::api_notes::StoredObjCSelector getEmptyKey() {
+ return clang::api_notes::StoredObjCSelector{
+ UnsignedInfo::getEmptyKey(), { } };
+ }
+
+ static inline clang::api_notes::StoredObjCSelector getTombstoneKey() {
+ return clang::api_notes::StoredObjCSelector{
+ UnsignedInfo::getTombstoneKey(), { } };
+ }
+
+ static unsigned getHashValue(
+ const clang::api_notes::StoredObjCSelector& value) {
+ auto hash = llvm::hash_value(value.NumPieces);
+ hash = hash_combine(hash, value.Identifiers.size());
+ for (auto piece : value.Identifiers)
+ hash = hash_combine(hash, static_cast<unsigned>(piece));
+ // FIXME: Mix upper/lower 32-bit values together to produce
+ // unsigned rather than truncating.
+ return hash;
+ }
+
+ static bool isEqual(const clang::api_notes::StoredObjCSelector &lhs,
+ const clang::api_notes::StoredObjCSelector &rhs) {
+ return lhs.NumPieces == rhs.NumPieces &&
+ lhs.Identifiers == rhs.Identifiers;
+ }
+ };
+}
+
+#endif // LLVM_CLANG_API_NOTES_FORMAT_H
diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp
new file mode 100644
index 0000000..6fc0fda
--- /dev/null
+++ b/lib/APINotes/APINotesManager.cpp
@@ -0,0 +1,426 @@
+//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the APINotesManager class.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/APINotes/APINotesManager.h"
+#include "clang/APINotes/APINotesOptions.h"
+#include "clang/APINotes/APINotesReader.h"
+#include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/SourceMgrAdapter.h"
+#include "clang/Basic/Version.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include <sys/stat.h>
+
+using namespace clang;
+using namespace api_notes;
+
+#define DEBUG_TYPE "API Notes"
+STATISTIC(NumHeaderAPINotes,
+ "non-framework API notes files loaded");
+STATISTIC(NumPublicFrameworkAPINotes,
+ "framework public API notes loaded");
+STATISTIC(NumPrivateFrameworkAPINotes,
+ "framework private API notes loaded");
+STATISTIC(NumFrameworksSearched,
+ "frameworks searched");
+STATISTIC(NumDirectoriesSearched,
+ "header directories searched");
+STATISTIC(NumDirectoryCacheHits,
+ "directory cache hits");
+
+namespace {
+ /// Prints two successive strings, which much be kept alive as long as the
+ /// PrettyStackTrace entry.
+ class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
+ StringRef First, Second;
+ public:
+ PrettyStackTraceDoubleString(StringRef first, StringRef second)
+ : First(first), Second(second) {}
+ void print(raw_ostream &OS) const override {
+ OS << First << Second;
+ }
+ };
+}
+
+APINotesManager::APINotesManager(SourceManager &sourceMgr,
+ const LangOptions &langOpts)
+ : SourceMgr(sourceMgr), ImplicitAPINotes(langOpts.APINotes) { }
+
+APINotesManager::~APINotesManager() {
+ // Free the API notes readers.
+ for (const auto &entry : Readers) {
+ if (auto reader = entry.second.dyn_cast<APINotesReader *>()) {
+ delete reader;
+ }
+ }
+
+ delete CurrentModuleReaders[0];
+ delete CurrentModuleReaders[1];
+}
+
+std::unique_ptr<APINotesReader>
+APINotesManager::loadAPINotes(const FileEntry *apiNotesFile) {
+ PrettyStackTraceDoubleString trace("Loading API notes from ",
+ apiNotesFile->getName());
+
+ // If the API notes file is already in the binary form, load it directly.
+ StringRef apiNotesFileName = apiNotesFile->getName();
+ StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName);
+ if (!apiNotesFileExt.empty() &&
+ apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) {
+ auto compiledFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
+
+ // Load the file.
+ auto buffer = SourceMgr.getBuffer(compiledFileID, SourceLocation());
+ if (!buffer) return nullptr;
+
+ // Load the binary form.
+ return APINotesReader::getUnmanaged(buffer, SwiftVersion);
+ }
+
+ // Open the source file.
+ auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
+ auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation());
+ if (!sourceBuffer) return nullptr;
+
+ // Compile the API notes source into a buffer.
+ // FIXME: Either propagate OSType through or, better yet, improve the binary
+ // APINotes format to maintain complete availability information.
+ // FIXME: We don't even really need to go through the binary format at all;
+ // we're just going to immediately deserialize it again.
+ llvm::SmallVector<char, 1024> apiNotesBuffer;
+ std::unique_ptr<llvm::MemoryBuffer> compiledBuffer;
+ {
+ SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(),
+ diag::err_apinotes_message,
+ diag::warn_apinotes_message,
+ diag::note_apinotes_message,
+ apiNotesFile);
+ llvm::raw_svector_ostream OS(apiNotesBuffer);
+ if (api_notes::compileAPINotes(sourceBuffer->getBuffer(),
+ SourceMgr.getFileEntryForID(sourceFileID),
+ OS,
+ api_notes::OSType::Absent,
+ srcMgrAdapter.getDiagHandler(),
+ srcMgrAdapter.getDiagContext()))
+ return nullptr;
+
+ // Make a copy of the compiled form into the buffer.
+ compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
+ StringRef(apiNotesBuffer.data(), apiNotesBuffer.size()));
+ }
+
+ // Load the binary form we just compiled.
+ auto reader = APINotesReader::get(std::move(compiledBuffer), SwiftVersion);
+ assert(reader && "Could not load the API notes we just generated?");
+ return reader;
+}
+
+bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
+ const FileEntry *APINotesFile) {
+ assert(Readers.find(HeaderDir) == Readers.end());
+ if (auto reader = loadAPINotes(APINotesFile)) {
+ Readers[HeaderDir] = reader.release();
+ return false;
+ }
+
+ Readers[HeaderDir] = nullptr;
+ return true;
+}
+
+const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory,
+ StringRef basename,
+ bool wantPublic) {
+ FileManager &fileMgr = SourceMgr.getFileManager();
+
+ llvm::SmallString<128> path;
+ path += directory->getName();
+
+ unsigned pathLen = path.size();
+
+ StringRef basenameSuffix = "";
+ if (!wantPublic) basenameSuffix = "_private";
+
+ // Look for a binary API notes file.
+ llvm::sys::path::append(path,
+ llvm::Twine(basename) + basenameSuffix + "." + BINARY_APINOTES_EXTENSION);
+ if (const FileEntry *binaryFile = fileMgr.getFile(path))
+ return binaryFile;
+
+ // Go back to the original path.
+ path.resize(pathLen);
+
+ // Look for the source API notes file.
+ llvm::sys::path::append(path,
+ llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION);
+ return fileMgr.getFile(path);
+}
+
+const DirectoryEntry *APINotesManager::loadFrameworkAPINotes(
+ llvm::StringRef FrameworkPath,
+ llvm::StringRef FrameworkName,
+ bool Public) {
+ FileManager &FileMgr = SourceMgr.getFileManager();
+
+ llvm::SmallString<128> Path;
+ Path += FrameworkPath;
+ unsigned FrameworkNameLength = Path.size();
+
+ // Form the path to the APINotes file.
+ llvm::sys::path::append(Path, "APINotes");
+ if (Public)
+ llvm::sys::path::append(Path,
+ (llvm::Twine(FrameworkName) + "."
+ + SOURCE_APINOTES_EXTENSION));
+ else
+ llvm::sys::path::append(Path,
+ (llvm::Twine(FrameworkName) + "_private."
+ + SOURCE_APINOTES_EXTENSION));
+
+ // Try to open the APINotes file.
+ const FileEntry *APINotesFile = FileMgr.getFile(Path);
+ if (!APINotesFile)
+ return nullptr;
+
+ // Form the path to the corresponding header directory.
+ Path.resize(FrameworkNameLength);
+ if (Public)
+ llvm::sys::path::append(Path, "Headers");
+ else
+ llvm::sys::path::append(Path, "PrivateHeaders");
+
+ // Try to access the header directory.
+ const DirectoryEntry *HeaderDir = FileMgr.getDirectory(Path);
+ if (!HeaderDir)
+ return nullptr;
+
+ // Try to load the API notes.
+ if (loadAPINotes(HeaderDir, APINotesFile))
+ return nullptr;
+
+ // Success: return the header directory.
+ if (Public)
+ ++NumPublicFrameworkAPINotes;
+ else
+ ++NumPrivateFrameworkAPINotes;
+ return HeaderDir;
+}
+
+bool APINotesManager::loadCurrentModuleAPINotes(
+ const Module *module,
+ bool lookInModule,
+ ArrayRef<std::string> searchPaths) {
+ assert(!CurrentModuleReaders[0] &&
+ "Already loaded API notes for the current module?");
+
+ FileManager &fileMgr = SourceMgr.getFileManager();
+ auto moduleName = module->getTopLevelModuleName();
+
+ // First, look relative to the module itself.
+ if (lookInModule) {
+ bool foundAny = false;
+ unsigned numReaders = 0;
+
+ // Local function to try loading an API notes file in the given directory.
+ auto tryAPINotes = [&](const DirectoryEntry *dir, bool wantPublic) {
+ if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) {
+ foundAny = true;
+
+ // Try to load the API notes file.
+ CurrentModuleReaders[numReaders] = loadAPINotes(file).release();
+ if (CurrentModuleReaders[numReaders])
+ ++numReaders;
+ }
+ };
+
+ if (module->IsFramework) {
+ // For frameworks, we search in the "Headers" or "PrivateHeaders"
+ // subdirectory.
+ llvm::SmallString<128> path;
+ path += module->Directory->getName();
+ unsigned pathLen = path.size();
+
+ llvm::sys::path::append(path, "Headers");
+ if (auto apinotesDir = fileMgr.getDirectory(path))
+ tryAPINotes(apinotesDir, /*wantPublic=*/true);
+
+ path.resize(pathLen);
+ llvm::sys::path::append(path, "PrivateHeaders");
+ if (auto privateAPINotesDir = fileMgr.getDirectory(path))
+ tryAPINotes(privateAPINotesDir, /*wantPublic=*/false);
+ } else {
+ tryAPINotes(module->Directory, /*wantPublic=*/true);
+ tryAPINotes(module->Directory, /*wantPublic=*/false);
+ }
+
+ if (foundAny)
+ return numReaders > 0;
+ }
+
+ // Second, look for API notes for this module in the module API
+ // notes search paths.
+ for (const auto &searchPath : searchPaths) {
+ if (auto searchDir = fileMgr.getDirectory(searchPath)) {
+ if (auto file = findAPINotesFile(searchDir, moduleName)) {
+ CurrentModuleReaders[0] = loadAPINotes(file).release();
+ return !getCurrentModuleReaders().empty();
+ }
+ }
+ }
+
+ // Didn't find any API notes.
+ return false;
+}
+
+llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocation Loc) {
+ llvm::SmallVector<APINotesReader *, 2> Results;
+
+ // If there are readers for the current module, return them.
+ if (!getCurrentModuleReaders().empty()) {
+ Results.append(getCurrentModuleReaders().begin(), getCurrentModuleReaders().end());
+ return Results;
+ }
+
+ // If we're not allowed to implicitly load API notes files, we're done.
+ if (!ImplicitAPINotes) return Results;
+
+ // If we don't have source location information, we're done.
+ if (Loc.isInvalid()) return Results;
+
+ // API notes are associated with the expansion location. Retrieve the
+ // file for this location.
+ SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc);
+ FileID ID = SourceMgr.getFileID(ExpansionLoc);
+ if (ID.isInvalid()) return Results;
+ const FileEntry *File = SourceMgr.getFileEntryForID(ID);
+ if (!File) return Results;
+
+ // Look for API notes in the directory corresponding to this file, or one of
+ // its its parent directories.
+ const DirectoryEntry *Dir = File->getDir();
+ FileManager &FileMgr = SourceMgr.getFileManager();
+ llvm::SetVector<const DirectoryEntry *,
+ SmallVector<const DirectoryEntry *, 4>,
+ llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited;
+ do {
+ // Look for an API notes reader for this header search directory.
+ auto Known = Readers.find(Dir);
+
+ // If we already know the answer, chase it.
+ if (Known != Readers.end()) {
+ ++NumDirectoryCacheHits;
+
+ // We've been redirected to another directory for answers. Follow it.
+ if (auto OtherDir = Known->second.dyn_cast<const DirectoryEntry *>()) {
+ DirsVisited.insert(Dir);
+ Dir = OtherDir;
+ continue;
+ }
+
+ // We have the answer.
+ if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
+ break;
+ }
+
+ // Look for API notes corresponding to this directory.
+ StringRef Path = Dir->getName();
+ if (llvm::sys::path::extension(Path) == ".framework") {
+ // If this is a framework directory, check whether there are API notes
+ // in the APINotes subdirectory.
+ auto FrameworkName = llvm::sys::path::stem(Path);
+ ++NumFrameworksSearched;
+
+ // Look for API notes for both the public and private headers.
+ const DirectoryEntry *PublicDir
+ = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
+ const DirectoryEntry *PrivateDir
+ = loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);
+
+ if (PublicDir || PrivateDir) {
+ // We found API notes: don't ever look past the framework directory.
+ Readers[Dir] = nullptr;
+
+ // Pretend we found the result in the public or private directory,
+ // as appropriate. All headers should be in one of those two places,
+ // but be defensive here.
+ if (!DirsVisited.empty()) {
+ if (DirsVisited.back() == PublicDir) {
+ DirsVisited.pop_back();
+ Dir = PublicDir;
+ } else if (DirsVisited.back() == PrivateDir) {
+ DirsVisited.pop_back();
+ Dir = PrivateDir;
+ }
+ }
+
+ // Grab the result.
+ if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
+ break;
+ }
+ } else {
+ // Look for an APINotes file in this directory.
+ llvm::SmallString<128> APINotesPath;
+ APINotesPath += Dir->getName();
+ llvm::sys::path::append(APINotesPath,
+ (llvm::Twine("APINotes.")
+ + SOURCE_APINOTES_EXTENSION));
+
+ // If there is an API notes file here, try to load it.
+ ++NumDirectoriesSearched;
+ if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) {
+ if (!loadAPINotes(Dir, APINotesFile)) {
+ ++NumHeaderAPINotes;
+ if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
+ break;
+ }
+ }
+ }
+
+ // We didn't find anything. Look at the parent directory.
+ if (!DirsVisited.insert(Dir)) {
+ Dir = 0;
+ break;
+ }
+
+ StringRef ParentPath = llvm::sys::path::parent_path(Path);
+ while (llvm::sys::path::stem(ParentPath) == "..") {
+ ParentPath = llvm::sys::path::parent_path(ParentPath);
+ }
+ if (ParentPath.empty()) {
+ Dir = nullptr;
+ } else {
+ Dir = FileMgr.getDirectory(ParentPath);
+ }
+ } while (Dir);
+
+ // Path compression for all of the directories we visited, redirecting
+ // them to the directory we ended on. If no API notes were found, the
+ // resulting directory will be NULL, indicating no API notes.
+ for (const auto Visited : DirsVisited) {
+ Readers[Visited] = Dir;
+ }
+
+ return Results;
+}
diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp
new file mode 100644
index 0000000..a5dd24d
--- /dev/null
+++ b/lib/APINotes/APINotesReader.cpp
@@ -0,0 +1,1879 @@
+//===--- APINotesReader.cpp - Side Car Reader --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the \c APINotesReader class that reads source
+// API notes data providing additional information about source code as
+// a separate input, such as the non-nil/nilable annotations for
+// method parameters.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesReader.h"
+#include "APINotesFormat.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/OnDiskHashTable.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace clang;
+using namespace api_notes;
+using namespace llvm::support;
+using namespace llvm;
+
+namespace {
+ /// Deserialize a version tuple.
+ VersionTuple readVersionTuple(const uint8_t *&data) {
+ uint8_t numVersions = (*data++) & 0x03;
+
+ unsigned major = endian::readNext<uint32_t, little, unaligned>(data);
+ if (numVersions == 0)
+ return VersionTuple(major);
+
+ unsigned minor = endian::readNext<uint32_t, little, unaligned>(data);
+ if (numVersions == 1)
+ return VersionTuple(major, minor);
+
+ unsigned subminor = endian::readNext<uint32_t, little, unaligned>(data);
+ if (numVersions == 2)
+ return VersionTuple(major, minor, subminor);
+
+ unsigned build = endian::readNext<uint32_t, little, unaligned>(data);
+ return VersionTuple(major, minor, subminor, build);
+ }
+
+ /// An on-disk hash table whose data is versioned based on the Swift version.
+ template<typename Derived, typename KeyType, typename UnversionedDataType>
+ class VersionedTableInfo {
+ public:
+ using internal_key_type = KeyType;
+ using external_key_type = KeyType;
+ using data_type = SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return static_cast<size_t>(llvm::hash_value(key));
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return lhs == rhs;
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ unsigned numElements = endian::readNext<uint16_t, little, unaligned>(data);
+ data_type result;
+ result.reserve(numElements);
+ for (unsigned i = 0; i != numElements; ++i) {
+ auto version = readVersionTuple(data);
+ auto dataBefore = data; (void)dataBefore;
+ auto unversionedData = Derived::readUnversioned(key, data);
+ assert(data != dataBefore
+ && "Unversioned data reader didn't move pointer");
+ result.push_back({version, unversionedData});
+ }
+ return result;
+ }
+ };
+
+
+ /// Read serialized CommonEntityInfo.
+ void readCommonEntityInfo(const uint8_t *&data, CommonEntityInfo &info) {
+ uint8_t unavailableBits = *data++;
+ info.Unavailable = (unavailableBits >> 1) & 0x01;
+ info.UnavailableInSwift = unavailableBits & 0x01;
+ if ((unavailableBits >> 2) & 0x01)
+ info.setSwiftPrivate(static_cast<bool>((unavailableBits >> 3) & 0x01));
+
+ unsigned msgLength = endian::readNext<uint16_t, little, unaligned>(data);
+ info.UnavailableMsg
+ = std::string(reinterpret_cast<const char *>(data),
+ reinterpret_cast<const char *>(data) + msgLength);
+ data += msgLength;
+
+ unsigned swiftNameLength
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.SwiftName
+ = std::string(reinterpret_cast<const char *>(data),
+ reinterpret_cast<const char *>(data) + swiftNameLength);
+ data += swiftNameLength;
+ }
+
+ /// Read serialized CommonTypeInfo.
+ void readCommonTypeInfo(const uint8_t *&data, CommonTypeInfo &info) {
+ readCommonEntityInfo(data, info);
+
+ unsigned swiftBridgeLength =
+ endian::readNext<uint16_t, little, unaligned>(data);
+ if (swiftBridgeLength > 0) {
+ info.setSwiftBridge(
+ std::string(reinterpret_cast<const char *>(data), swiftBridgeLength-1));
+ data += swiftBridgeLength-1;
+ }
+
+ unsigned errorDomainLength =
+ endian::readNext<uint16_t, little, unaligned>(data);
+ if (errorDomainLength > 0) {
+ info.setNSErrorDomain(
+ std::string(reinterpret_cast<const char *>(data), errorDomainLength-1));
+ data += errorDomainLength-1;
+ }
+ }
+
+ /// Used to deserialize the on-disk identifier table.
+ class IdentifierTableInfo {
+ public:
+ using internal_key_type = StringRef;
+ using external_key_type = StringRef;
+ using data_type = IdentifierID;
+ using hash_value_type = uint32_t;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return llvm::HashString(key);
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return lhs == rhs;
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ return StringRef(reinterpret_cast<const char *>(data), length);
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+ };
+
+ /// Used to deserialize the on-disk Objective-C class table.
+ class ObjCContextIDTableInfo {
+ public:
+ // identifier ID, is-protocol
+ using internal_key_type = std::pair<unsigned, char>;
+ using external_key_type = internal_key_type;
+ using data_type = unsigned;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return static_cast<size_t>(llvm::hash_value(key));
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return lhs == rhs;
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID
+ = endian::readNext<uint32_t, little, unaligned>(data);
+ auto isProtocol = endian::readNext<uint8_t, little, unaligned>(data);
+ return { nameID, isProtocol };
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+ };
+
+ /// Used to deserialize the on-disk Objective-C property table.
+ class ObjCContextInfoTableInfo
+ : public VersionedTableInfo<ObjCContextInfoTableInfo,
+ unsigned,
+ ObjCContextInfo>
+ {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+
+ static ObjCContextInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ ObjCContextInfo info;
+ readCommonTypeInfo(data, info);
+ uint8_t payload = *data++;
+
+ if (payload & 0x01)
+ info.setHasDesignatedInits(true);
+ payload = payload >> 1;
+
+ if (payload & 0x4)
+ info.setDefaultNullability(static_cast<NullabilityKind>(payload&0x03));
+ payload >>= 3;
+
+ if (payload & (1 << 1))
+ info.setSwiftObjCMembers(payload & 1);
+ payload >>= 2;
+
+ if (payload & (1 << 1))
+ info.setSwiftImportAsNonGeneric(payload & 1);
+
+ return info;
+ }
+ };
+
+ /// Read serialized VariableInfo.
+ void readVariableInfo(const uint8_t *&data, VariableInfo &info) {
+ readCommonEntityInfo(data, info);
+ if (*data++) {
+ info.setNullabilityAudited(static_cast<NullabilityKind>(*data));
+ }
+ ++data;
+
+ auto typeLen
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.setType(std::string(data, data + typeLen));
+ data += typeLen;
+ }
+
+ /// Used to deserialize the on-disk Objective-C property table.
+ class ObjCPropertyTableInfo
+ : public VersionedTableInfo<ObjCPropertyTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCPropertyInfo>
+ {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto classID = endian::readNext<uint32_t, little, unaligned>(data);
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ char isInstance = endian::readNext<uint8_t, little, unaligned>(data);
+ return std::make_tuple(classID, nameID, isInstance);
+ }
+
+ static ObjCPropertyInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ ObjCPropertyInfo info;
+ readVariableInfo(data, info);
+ uint8_t flags = *data++;
+ if (flags & (1 << 0))
+ info.setSwiftImportAsAccessors(flags & (1 << 1));
+ return info;
+ }
+ };
+
+ /// Read serialized ParamInfo.
+ void readParamInfo(const uint8_t *&data, ParamInfo &info) {
+ readVariableInfo(data, info);
+
+ uint8_t payload = endian::readNext<uint8_t, little, unaligned>(data);
+ if (payload & 0x01) {
+ info.setNoEscape(payload & 0x02);
+ }
+ payload >>= 2; assert(payload == 0 && "Bad API notes");
+ }
+
+ /// Read serialized FunctionInfo.
+ void readFunctionInfo(const uint8_t *&data, FunctionInfo &info) {
+ readCommonEntityInfo(data, info);
+ info.NullabilityAudited
+ = endian::readNext<uint8_t, little, unaligned>(data);
+ info.NumAdjustedNullable
+ = endian::readNext<uint8_t, little, unaligned>(data);
+ info.NullabilityPayload
+ = endian::readNext<uint64_t, little, unaligned>(data);
+
+ unsigned numParams = endian::readNext<uint16_t, little, unaligned>(data);
+ while (numParams > 0) {
+ ParamInfo pi;
+ readParamInfo(data, pi);
+ info.Params.push_back(pi);
+ --numParams;
+ }
+
+ unsigned resultTypeLen
+ = endian::readNext<uint16_t, little, unaligned>(data);
+ info.ResultType = std::string(data, data + resultTypeLen);
+ data += resultTypeLen;
+ }
+
+ /// Used to deserialize the on-disk Objective-C method table.
+ class ObjCMethodTableInfo
+ : public VersionedTableInfo<ObjCMethodTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCMethodInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto classID = endian::readNext<uint32_t, little, unaligned>(data);
+ auto selectorID = endian::readNext<uint32_t, little, unaligned>(data);
+ auto isInstance = endian::readNext<uint8_t, little, unaligned>(data);
+ return internal_key_type{ classID, selectorID, isInstance };
+ }
+
+ static ObjCMethodInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ ObjCMethodInfo info;
+ uint8_t payload = *data++;
+ info.Required = payload & 0x01;
+ payload >>= 1;
+ info.DesignatedInit = payload & 0x01;
+ payload >>= 1;
+
+ readFunctionInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk Objective-C selector table.
+ class ObjCSelectorTableInfo {
+ public:
+ using internal_key_type = StoredObjCSelector;
+ using external_key_type = internal_key_type;
+ using data_type = SelectorID;
+ using hash_value_type = unsigned;
+ using offset_type = unsigned;
+
+ internal_key_type GetInternalKey(external_key_type key) {
+ return key;
+ }
+
+ external_key_type GetExternalKey(internal_key_type key) {
+ return key;
+ }
+
+ hash_value_type ComputeHash(internal_key_type key) {
+ return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(key);
+ }
+
+ static bool EqualKey(internal_key_type lhs, internal_key_type rhs) {
+ return llvm::DenseMapInfo<StoredObjCSelector>::isEqual(lhs, rhs);
+ }
+
+ static std::pair<unsigned, unsigned>
+ ReadKeyDataLength(const uint8_t *&data) {
+ unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(data);
+ return { keyLength, dataLength };
+ }
+
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ internal_key_type key;
+ key.NumPieces = endian::readNext<uint16_t, little, unaligned>(data);
+ unsigned numIdents = (length - sizeof(uint16_t)) / sizeof(uint32_t);
+ for (unsigned i = 0; i != numIdents; ++i) {
+ key.Identifiers.push_back(
+ endian::readNext<uint32_t, little, unaligned>(data));
+ }
+ return key;
+ }
+
+ static data_type ReadData(internal_key_type key, const uint8_t *data,
+ unsigned length) {
+ return endian::readNext<uint32_t, little, unaligned>(data);
+ }
+ };
+
+ /// Used to deserialize the on-disk global variable table.
+ class GlobalVariableTableInfo
+ : public VersionedTableInfo<GlobalVariableTableInfo, unsigned,
+ GlobalVariableInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ return nameID;
+ }
+
+ static GlobalVariableInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ GlobalVariableInfo info;
+ readVariableInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk global function table.
+ class GlobalFunctionTableInfo
+ : public VersionedTableInfo<GlobalFunctionTableInfo, unsigned,
+ GlobalFunctionInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ return nameID;
+ }
+
+ static GlobalFunctionInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ GlobalFunctionInfo info;
+ readFunctionInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk enumerator table.
+ class EnumConstantTableInfo
+ : public VersionedTableInfo<EnumConstantTableInfo, unsigned,
+ EnumConstantInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<uint32_t, little, unaligned>(data);
+ return nameID;
+ }
+
+ static EnumConstantInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ EnumConstantInfo info;
+ readCommonEntityInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk tag table.
+ class TagTableInfo
+ : public VersionedTableInfo<TagTableInfo, unsigned, TagInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<IdentifierID, little, unaligned>(data);
+ return nameID;
+ }
+
+ static TagInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ TagInfo info;
+
+ uint8_t payload = *data++;
+ if (payload & 1) {
+ info.setFlagEnum(payload & 2);
+ }
+ payload >>= 2;
+ if (payload > 0) {
+ info.EnumExtensibility =
+ static_cast<EnumExtensibilityKind>((payload & 0x3) - 1);
+ }
+
+ readCommonTypeInfo(data, info);
+ return info;
+ }
+ };
+
+ /// Used to deserialize the on-disk typedef table.
+ class TypedefTableInfo
+ : public VersionedTableInfo<TypedefTableInfo, unsigned, TypedefInfo> {
+ public:
+ static internal_key_type ReadKey(const uint8_t *data, unsigned length) {
+ auto nameID = endian::readNext<IdentifierID, little, unaligned>(data);
+ return nameID;
+ }
+
+ static TypedefInfo readUnversioned(internal_key_type key,
+ const uint8_t *&data) {
+ TypedefInfo info;
+
+ uint8_t payload = *data++;
+ if (payload > 0) {
+ info.SwiftWrapper = static_cast<SwiftWrapperKind>((payload & 0x3) - 1);
+ }
+
+ readCommonTypeInfo(data, info);
+ return info;
+ }
+ };
+} // end anonymous namespace
+
+class APINotesReader::Implementation {
+public:
+ /// The input buffer for the API notes data.
+ llvm::MemoryBuffer *InputBuffer;
+
+ /// Whether we own the input buffer.
+ bool OwnsInputBuffer;
+
+ /// The Swift version to use for filtering.
+ VersionTuple SwiftVersion;
+
+ /// The name of the module that we read from the control block.
+ std::string ModuleName;
+
+ // The size and modification time of the source file from
+ // which this API notes file was created, if known.
+ Optional<std::pair<off_t, time_t>> SourceFileSizeAndModTime;
+
+ /// Various options and attributes for the module
+ ModuleOptions ModuleOpts;
+
+ using SerializedIdentifierTable =
+ llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>;
+
+ /// The identifier table.
+ std::unique_ptr<SerializedIdentifierTable> IdentifierTable;
+
+ using SerializedObjCContextIDTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCContextIDTableInfo>;
+
+ /// The Objective-C context ID table.
+ std::unique_ptr<SerializedObjCContextIDTable> ObjCContextIDTable;
+
+ using SerializedObjCContextInfoTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCContextInfoTableInfo>;
+
+ /// The Objective-C context info table.
+ std::unique_ptr<SerializedObjCContextInfoTable> ObjCContextInfoTable;
+
+ using SerializedObjCPropertyTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>;
+
+ /// The Objective-C property table.
+ std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable;
+
+ using SerializedObjCMethodTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>;
+
+ /// The Objective-C method table.
+ std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;
+
+ using SerializedObjCSelectorTable =
+ llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;
+
+ /// The Objective-C selector table.
+ std::unique_ptr<SerializedObjCSelectorTable> ObjCSelectorTable;
+
+ using SerializedGlobalVariableTable =
+ llvm::OnDiskIterableChainedHashTable<GlobalVariableTableInfo>;
+
+ /// The global variable table.
+ std::unique_ptr<SerializedGlobalVariableTable> GlobalVariableTable;
+
+ using SerializedGlobalFunctionTable =
+ llvm::OnDiskIterableChainedHashTable<GlobalFunctionTableInfo>;
+
+ /// The global function table.
+ std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable;
+
+ using SerializedEnumConstantTable =
+ llvm::OnDiskIterableChainedHashTable<EnumConstantTableInfo>;
+
+ /// The enumerator table.
+ std::unique_ptr<SerializedEnumConstantTable> EnumConstantTable;
+
+ using SerializedTagTable =
+ llvm::OnDiskIterableChainedHashTable<TagTableInfo>;
+
+ /// The tag table.
+ std::unique_ptr<SerializedTagTable> TagTable;
+
+ using SerializedTypedefTable =
+ llvm::OnDiskIterableChainedHashTable<TypedefTableInfo>;
+
+ /// The typedef table.
+ std::unique_ptr<SerializedTypedefTable> TypedefTable;
+
+ /// Retrieve the identifier ID for the given string, or an empty
+ /// optional if the string is unknown.
+ Optional<IdentifierID> getIdentifier(StringRef str);
+
+ /// Retrieve the selector ID for the given selector, or an empty
+ /// optional if the string is unknown.
+ Optional<SelectorID> getSelector(ObjCSelectorRef selector);
+
+ bool readControlBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readIdentifierBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCContextBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCPropertyBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCMethodBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readObjCSelectorBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readGlobalVariableBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readGlobalFunctionBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readEnumConstantBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readTagBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+ bool readTypedefBlock(llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch);
+};
+
+Optional<IdentifierID> APINotesReader::Implementation::getIdentifier(
+ StringRef str) {
+ if (!IdentifierTable)
+ return None;
+
+ if (str.empty())
+ return IdentifierID(0);
+
+ auto known = IdentifierTable->find(str);
+ if (known == IdentifierTable->end())
+ return None;
+
+ return *known;
+}
+
+Optional<SelectorID> APINotesReader::Implementation::getSelector(
+ ObjCSelectorRef selector) {
+ if (!ObjCSelectorTable || !IdentifierTable)
+ return None;
+
+ // Translate the identifiers.
+ StoredObjCSelector key;
+ key.NumPieces = selector.NumPieces;
+ for (auto ident : selector.Identifiers) {
+ if (auto identID = getIdentifier(ident)) {
+ key.Identifiers.push_back(*identID);
+ } else {
+ return None;
+ }
+ }
+
+ auto known = ObjCSelectorTable->find(key);
+ if (known == ObjCSelectorTable->end())
+ return None;
+
+ return *known;
+
+}
+
+bool APINotesReader::Implementation::readControlBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(CONTROL_BLOCK_ID))
+ return true;
+
+ bool sawMetadata = false;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown metadata sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case control_block::METADATA:
+ // Already saw metadata.
+ if (sawMetadata)
+ return true;
+
+ if (scratch[0] != VERSION_MAJOR || scratch[1] != VERSION_MINOR)
+ return true;
+
+ sawMetadata = true;
+ break;
+
+ case control_block::MODULE_NAME:
+ ModuleName = blobData.str();
+ break;
+
+ case control_block::MODULE_OPTIONS:
+ ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0;
+ break;
+
+ case control_block::SOURCE_FILE:
+ SourceFileSizeAndModTime = { scratch[0], scratch[1] };
+ break;
+
+ default:
+ // Unknown metadata record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return !sawMetadata;
+}
+
+bool APINotesReader::Implementation::readIdentifierBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case identifier_block::IDENTIFIER_DATA: {
+ // Already saw identifier table.
+ if (IdentifierTable)
+ return true;
+
+ uint32_t tableOffset;
+ identifier_block::IdentifierDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ IdentifierTable.reset(
+ SerializedIdentifierTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCContextBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_context_block::OBJC_CONTEXT_ID_DATA: {
+ // Already saw Objective-C context ID table.
+ if (ObjCContextIDTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_context_block::ObjCContextIDLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCContextIDTable.reset(
+ SerializedObjCContextIDTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ case objc_context_block::OBJC_CONTEXT_INFO_DATA: {
+ // Already saw Objective-C context info table.
+ if (ObjCContextInfoTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_context_block::ObjCContextInfoLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCContextInfoTable.reset(
+ SerializedObjCContextInfoTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCPropertyBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_property_block::OBJC_PROPERTY_DATA: {
+ // Already saw Objective-C property table.
+ if (ObjCPropertyTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_property_block::ObjCPropertyDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCPropertyTable.reset(
+ SerializedObjCPropertyTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCMethodBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_method_block::OBJC_METHOD_DATA: {
+ // Already saw Objective-C method table.
+ if (ObjCMethodTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_method_block::ObjCMethodDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCMethodTable.reset(
+ SerializedObjCMethodTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readObjCSelectorBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case objc_selector_block::OBJC_SELECTOR_DATA: {
+ // Already saw Objective-C selector table.
+ if (ObjCSelectorTable)
+ return true;
+
+ uint32_t tableOffset;
+ objc_selector_block::ObjCSelectorDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ ObjCSelectorTable.reset(
+ SerializedObjCSelectorTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readGlobalVariableBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case global_variable_block::GLOBAL_VARIABLE_DATA: {
+ // Already saw global variable table.
+ if (GlobalVariableTable)
+ return true;
+
+ uint32_t tableOffset;
+ global_variable_block::GlobalVariableDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ GlobalVariableTable.reset(
+ SerializedGlobalVariableTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readGlobalFunctionBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case global_function_block::GLOBAL_FUNCTION_DATA: {
+ // Already saw global function table.
+ if (GlobalFunctionTable)
+ return true;
+
+ uint32_t tableOffset;
+ global_function_block::GlobalFunctionDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ GlobalFunctionTable.reset(
+ SerializedGlobalFunctionTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readEnumConstantBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case enum_constant_block::ENUM_CONSTANT_DATA: {
+ // Already saw enumerator table.
+ if (EnumConstantTable)
+ return true;
+
+ uint32_t tableOffset;
+ enum_constant_block::EnumConstantDataLayout::readRecord(scratch,
+ tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ EnumConstantTable.reset(
+ SerializedEnumConstantTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readTagBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(TAG_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case tag_block::TAG_DATA: {
+ // Already saw tag table.
+ if (TagTable)
+ return true;
+
+ uint32_t tableOffset;
+ tag_block::TagDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ TagTable.reset(
+ SerializedTagTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+bool APINotesReader::Implementation::readTypedefBlock(
+ llvm::BitstreamCursor &cursor,
+ SmallVectorImpl<uint64_t> &scratch) {
+ if (cursor.EnterSubBlock(TYPEDEF_BLOCK_ID))
+ return true;
+
+ auto next = cursor.advance();
+ while (next.Kind != llvm::BitstreamEntry::EndBlock) {
+ if (next.Kind == llvm::BitstreamEntry::Error)
+ return true;
+
+ if (next.Kind == llvm::BitstreamEntry::SubBlock) {
+ // Unknown sub-block, possibly for use by a future version of the
+ // API notes format.
+ if (cursor.SkipBlock())
+ return true;
+
+ next = cursor.advance();
+ continue;
+ }
+
+ scratch.clear();
+ StringRef blobData;
+ unsigned kind = cursor.readRecord(next.ID, scratch, &blobData);
+ switch (kind) {
+ case typedef_block::TYPEDEF_DATA: {
+ // Already saw typedef table.
+ if (TypedefTable)
+ return true;
+
+ uint32_t tableOffset;
+ typedef_block::TypedefDataLayout::readRecord(scratch, tableOffset);
+ auto base = reinterpret_cast<const uint8_t *>(blobData.data());
+
+ TypedefTable.reset(
+ SerializedTypedefTable::Create(base + tableOffset,
+ base + sizeof(uint32_t),
+ base));
+ break;
+ }
+
+ default:
+ // Unknown record, possibly for use by a future version of the
+ // module format.
+ break;
+ }
+
+ next = cursor.advance();
+ }
+
+ return false;
+}
+
+APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer,
+ bool ownsInputBuffer,
+ VersionTuple swiftVersion,
+ bool &failed)
+ : Impl(*new Implementation)
+{
+ failed = false;
+
+ // Initialize the input buffer.
+ Impl.InputBuffer = inputBuffer;
+ Impl.OwnsInputBuffer = ownsInputBuffer;
+ Impl.SwiftVersion = swiftVersion;
+ llvm::BitstreamCursor cursor(*Impl.InputBuffer);
+
+ // Validate signature.
+ for (auto byte : API_NOTES_SIGNATURE) {
+ if (cursor.AtEndOfStream() || cursor.Read(8) != byte) {
+ failed = true;
+ return;
+ }
+ }
+
+ // Look at all of the blocks.
+ bool hasValidControlBlock = false;
+ SmallVector<uint64_t, 64> scratch;
+ while (!cursor.AtEndOfStream()) {
+ auto topLevelEntry = cursor.advance();
+ if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
+ break;
+
+ switch (topLevelEntry.ID) {
+ case llvm::bitc::BLOCKINFO_BLOCK_ID:
+ if (!cursor.ReadBlockInfoBlock()) {
+ failed = true;
+ break;
+ }
+ break;
+
+ case CONTROL_BLOCK_ID:
+ // Only allow a single control block.
+ if (hasValidControlBlock || Impl.readControlBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+
+ hasValidControlBlock = true;
+ break;
+
+ case IDENTIFIER_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readIdentifierBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case OBJC_CONTEXT_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readObjCContextBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+
+ break;
+
+ case OBJC_PROPERTY_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readObjCPropertyBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case OBJC_METHOD_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readObjCMethodBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case OBJC_SELECTOR_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readObjCSelectorBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case GLOBAL_VARIABLE_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readGlobalVariableBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case GLOBAL_FUNCTION_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readGlobalFunctionBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case ENUM_CONSTANT_BLOCK_ID:
+ if (!hasValidControlBlock ||
+ Impl.readEnumConstantBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case TAG_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readTagBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ case TYPEDEF_BLOCK_ID:
+ if (!hasValidControlBlock || Impl.readTypedefBlock(cursor, scratch)) {
+ failed = true;
+ return;
+ }
+ break;
+
+ default:
+ // Unknown top-level block, possibly for use by a future version of the
+ // module format.
+ if (cursor.SkipBlock()) {
+ failed = true;
+ return;
+ }
+ break;
+ }
+ }
+
+ if (!cursor.AtEndOfStream()) {
+ failed = true;
+ return;
+ }
+}
+
+APINotesReader::~APINotesReader() {
+ if (Impl.OwnsInputBuffer)
+ delete Impl.InputBuffer;
+
+ delete &Impl;
+}
+
+std::unique_ptr<APINotesReader>
+APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer,
+ VersionTuple swiftVersion) {
+ bool failed = false;
+ std::unique_ptr<APINotesReader>
+ reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true,
+ swiftVersion, failed));
+ if (failed)
+ return nullptr;
+
+ return reader;
+}
+
+std::unique_ptr<APINotesReader>
+APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer,
+ VersionTuple swiftVersion) {
+ bool failed = false;
+ std::unique_ptr<APINotesReader>
+ reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false,
+ swiftVersion, failed));
+ if (failed)
+ return nullptr;
+
+ return reader;
+}
+
+StringRef APINotesReader::getModuleName() const {
+ return Impl.ModuleName;
+}
+
+Optional<std::pair<off_t, time_t>>
+APINotesReader::getSourceFileSizeAndModTime() const {
+ return Impl.SourceFileSizeAndModTime;
+}
+
+ModuleOptions APINotesReader::getModuleOptions() const {
+ return Impl.ModuleOpts;
+}
+
+template<typename T>
+APINotesReader::VersionedInfo<T>::VersionedInfo(
+ VersionTuple version,
+ SmallVector<std::pair<VersionTuple, T>, 1> results)
+ : Results(std::move(results)) {
+
+ assert(!Results.empty());
+ assert(std::is_sorted(Results.begin(), Results.end(),
+ [](const std::pair<VersionTuple, T> &left,
+ const std::pair<VersionTuple, T> &right) -> bool {
+ assert(left.first != right.first && "two entries for the same version");
+ return left.first < right.first;
+ }));
+
+ Selected = Results.size();
+ for (unsigned i = 0, n = Results.size(); i != n; ++i) {
+ if (version && Results[i].first >= version) {
+ // If the current version is "4", then entries for 4 are better than
+ // entries for 5, but both are valid. Because entries are sorted, we get
+ // that behavior by picking the first match.
+ Selected = i;
+ break;
+ }
+ }
+
+ // If we didn't find a match but we have an unversioned result, use the
+ // unversioned result. This will always be the first entry because we encode
+ // it as version 0.
+ if (Selected == Results.size() && Results[0].first.empty())
+ Selected = 0;
+}
+
+auto APINotesReader::lookupObjCClassID(StringRef name) -> Optional<ContextID> {
+ if (!Impl.ObjCContextIDTable)
+ return None;
+
+ Optional<IdentifierID> classID = Impl.getIdentifier(name);
+ if (!classID)
+ return None;
+
+ auto knownID = Impl.ObjCContextIDTable->find({*classID, '\0'});
+ if (knownID == Impl.ObjCContextIDTable->end())
+ return None;
+
+ return ContextID(*knownID);
+}
+
+auto APINotesReader::lookupObjCClassInfo(StringRef name)
+ -> VersionedInfo<ObjCContextInfo> {
+ if (!Impl.ObjCContextInfoTable)
+ return None;
+
+ Optional<ContextID> contextID = lookupObjCClassID(name);
+ if (!contextID)
+ return None;
+
+ auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value);
+ if (knownInfo == Impl.ObjCContextInfoTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *knownInfo };
+}
+
+auto APINotesReader::lookupObjCProtocolID(StringRef name)
+ -> Optional<ContextID> {
+ if (!Impl.ObjCContextIDTable)
+ return None;
+
+ Optional<IdentifierID> classID = Impl.getIdentifier(name);
+ if (!classID)
+ return None;
+
+ auto knownID = Impl.ObjCContextIDTable->find({*classID, '\1'});
+ if (knownID == Impl.ObjCContextIDTable->end())
+ return None;
+
+ return ContextID(*knownID);
+}
+
+auto APINotesReader::lookupObjCProtocolInfo(StringRef name)
+ -> VersionedInfo<ObjCContextInfo> {
+ if (!Impl.ObjCContextInfoTable)
+ return None;
+
+ Optional<ContextID> contextID = lookupObjCProtocolID(name);
+ if (!contextID)
+ return None;
+
+ auto knownInfo = Impl.ObjCContextInfoTable->find(contextID->Value);
+ if (knownInfo == Impl.ObjCContextInfoTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *knownInfo };
+}
+
+
+auto APINotesReader::lookupObjCProperty(ContextID contextID,
+ StringRef name,
+ bool isInstance)
+ -> VersionedInfo<ObjCPropertyInfo> {
+ if (!Impl.ObjCPropertyTable)
+ return None;
+
+ Optional<IdentifierID> propertyID = Impl.getIdentifier(name);
+ if (!propertyID)
+ return None;
+
+ auto known = Impl.ObjCPropertyTable->find(std::make_tuple(contextID.Value,
+ *propertyID,
+ (char)isInstance));
+ if (known == Impl.ObjCPropertyTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupObjCMethod(
+ ContextID contextID,
+ ObjCSelectorRef selector,
+ bool isInstanceMethod)
+ -> VersionedInfo<ObjCMethodInfo> {
+ if (!Impl.ObjCMethodTable)
+ return None;
+
+ Optional<SelectorID> selectorID = Impl.getSelector(selector);
+ if (!selectorID)
+ return None;
+
+ auto known = Impl.ObjCMethodTable->find(
+ ObjCMethodTableInfo::internal_key_type{
+ contextID.Value, *selectorID, isInstanceMethod});
+ if (known == Impl.ObjCMethodTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupGlobalVariable(
+ StringRef name)
+ -> VersionedInfo<GlobalVariableInfo> {
+ if (!Impl.GlobalVariableTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.GlobalVariableTable->find(*nameID);
+ if (known == Impl.GlobalVariableTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupGlobalFunction(StringRef name)
+ -> VersionedInfo<GlobalFunctionInfo> {
+ if (!Impl.GlobalFunctionTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.GlobalFunctionTable->find(*nameID);
+ if (known == Impl.GlobalFunctionTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupEnumConstant(StringRef name)
+ -> VersionedInfo<EnumConstantInfo> {
+ if (!Impl.EnumConstantTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.EnumConstantTable->find(*nameID);
+ if (known == Impl.EnumConstantTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupTag(StringRef name) -> VersionedInfo<TagInfo> {
+ if (!Impl.TagTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.TagTable->find(*nameID);
+ if (known == Impl.TagTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+auto APINotesReader::lookupTypedef(StringRef name)
+ -> VersionedInfo<TypedefInfo> {
+ if (!Impl.TypedefTable)
+ return None;
+
+ Optional<IdentifierID> nameID = Impl.getIdentifier(name);
+ if (!nameID)
+ return None;
+
+ auto known = Impl.TypedefTable->find(*nameID);
+ if (known == Impl.TypedefTable->end())
+ return None;
+
+ return { Impl.SwiftVersion, *known };
+}
+
+APINotesReader::Visitor::~Visitor() { }
+
+void APINotesReader::Visitor::visitObjCClass(
+ ContextID contextID,
+ StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitObjCProtocol(
+ ContextID contextID,
+ StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitObjCMethod(
+ ContextID contextID,
+ StringRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitObjCProperty(
+ ContextID contextID,
+ StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitGlobalVariable(
+ StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitGlobalFunction(
+ StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitEnumConstant(
+ StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitTag(
+ StringRef name,
+ const TagInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::Visitor::visitTypedef(
+ StringRef name,
+ const TypedefInfo &info,
+ VersionTuple swiftVersion) { }
+
+void APINotesReader::visit(Visitor &visitor) {
+ // FIXME: All of these iterations would be significantly more efficient if we
+ // could get the keys and data together, but OnDiskIterableHashTable doesn't
+ // support that.
+
+ // Build an identifier ID -> string mapping, which we'll need when visiting
+ // any of the tables.
+ llvm::DenseMap<unsigned, StringRef> identifiers;
+ if (Impl.IdentifierTable) {
+ for (auto key : Impl.IdentifierTable->keys()) {
+ unsigned ID = *Impl.IdentifierTable->find(key);
+ assert(identifiers.count(ID) == 0);
+ identifiers[ID] = key;
+ }
+ }
+
+ // Visit classes and protocols.
+ if (Impl.ObjCContextIDTable && Impl.ObjCContextInfoTable) {
+ for (auto key : Impl.ObjCContextIDTable->keys()) {
+ auto name = identifiers[key.first];
+ auto contextID = *Impl.ObjCContextIDTable->find(key);
+
+ auto knownInfo = Impl.ObjCContextInfoTable->find(contextID);
+ if (knownInfo == Impl.ObjCContextInfoTable->end()) continue;
+
+ for (const auto &versioned : *knownInfo) {
+ if (key.second)
+ visitor.visitObjCProtocol(ContextID(contextID), name,
+ versioned.second, versioned.first);
+ else
+ visitor.visitObjCClass(ContextID(contextID), name, versioned.second,
+ versioned.first);
+ }
+ }
+ }
+
+ // Build a selector ID -> stored Objective-C selector mapping, which we need
+ // when visiting the method tables.
+ llvm::DenseMap<unsigned, std::string> selectors;
+ if (Impl.ObjCSelectorTable) {
+ for (auto key : Impl.ObjCSelectorTable->keys()) {
+ std::string selector;
+ if (key.NumPieces == 0)
+ selector = identifiers[key.Identifiers[0]];
+ else {
+ for (auto identID : key.Identifiers) {
+ selector += identifiers[identID];
+ selector += ':';
+ }
+ }
+
+ unsigned selectorID = *Impl.ObjCSelectorTable->find(key);
+ selectors[selectorID] = selector;
+ }
+ }
+
+ // Visit methods.
+ if (Impl.ObjCMethodTable) {
+ for (auto key : Impl.ObjCMethodTable->keys()) {
+ ContextID contextID(std::get<0>(key));
+ const auto &selector = selectors[std::get<1>(key)];
+ for (const auto &versioned : *Impl.ObjCMethodTable->find(key))
+ visitor.visitObjCMethod(contextID, selector, std::get<2>(key),
+ versioned.second, versioned.first);
+ }
+ }
+
+ // Visit properties.
+ if (Impl.ObjCPropertyTable) {
+ for (auto key : Impl.ObjCPropertyTable->keys()) {
+ ContextID contextID(std::get<0>(key));
+ auto name = identifiers[std::get<1>(key)];
+ char isInstance = std::get<2>(key);
+ for (const auto &versioned : *Impl.ObjCPropertyTable->find(key)) {
+ visitor.visitObjCProperty(contextID, name, isInstance, versioned.second,
+ versioned.first);
+ }
+ }
+ }
+
+ // Visit global functions.
+ if (Impl.GlobalFunctionTable) {
+ for (auto key : Impl.GlobalFunctionTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.GlobalFunctionTable->find(key))
+ visitor.visitGlobalFunction(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit global variables.
+ if (Impl.GlobalVariableTable) {
+ for (auto key : Impl.GlobalVariableTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.GlobalVariableTable->find(key))
+ visitor.visitGlobalVariable(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit global variables.
+ if (Impl.EnumConstantTable) {
+ for (auto key : Impl.EnumConstantTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.EnumConstantTable->find(key))
+ visitor.visitEnumConstant(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit tags.
+ if (Impl.TagTable) {
+ for (auto key : Impl.TagTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.TagTable->find(key))
+ visitor.visitTag(name, versioned.second, versioned.first);
+ }
+ }
+
+ // Visit typedefs.
+ if (Impl.TypedefTable) {
+ for (auto key : Impl.TypedefTable->keys()) {
+ auto name = identifiers[key];
+ for (const auto &versioned : *Impl.TypedefTable->find(key))
+ visitor.visitTypedef(name, versioned.second, versioned.first);
+ }
+ }
+}
+
diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp
new file mode 100644
index 0000000..b178e25
--- /dev/null
+++ b/lib/APINotes/APINotesWriter.cpp
@@ -0,0 +1,1328 @@
+//===--- APINotesWriter.cpp - API Notes Writer --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the \c APINotesWriter class that writes out
+// source API notes data providing additional information about source
+// code as a separate input, such as the non-nil/nilable annotations
+// for method parameters.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesWriter.h"
+#include "APINotesFormat.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/OnDiskHashTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/DataTypes.h"
+#include <tuple>
+#include <vector>
+using namespace clang;
+using namespace api_notes;
+using namespace llvm::support;
+
+namespace {
+ template<typename T> using VersionedSmallVector =
+ SmallVector<std::pair<VersionTuple, T>, 1>;
+}
+
+class APINotesWriter::Implementation {
+ /// Mapping from strings to identifier IDs.
+ llvm::StringMap<IdentifierID> IdentifierIDs;
+
+ /// Mapping from selectors to selector ID.
+ llvm::DenseMap<StoredObjCSelector, SelectorID> SelectorIDs;
+
+ /// Scratch space for bitstream writing.
+ SmallVector<uint64_t, 64> ScratchRecord;
+
+public:
+ /// The name of the module
+ std::string ModuleName;
+
+ /// The source file from which this binary representation was
+ /// created, if known.
+ const FileEntry *SourceFile;
+
+ bool SwiftInferImportAsMember = false;
+
+ /// Information about Objective-C contexts (classes or protocols).
+ ///
+ /// Indexed by the identifier ID and a bit indication whether we're looking
+ /// for a class (0) or protocol (1) and provides both the context ID and
+ /// information describing the context within that module.
+ llvm::DenseMap<std::pair<unsigned, char>,
+ std::pair<unsigned, VersionedSmallVector<ObjCContextInfo>>>
+ ObjCContexts;
+
+ /// Mapping from context IDs to the identifier ID holding the name.
+ llvm::DenseMap<unsigned, unsigned> ObjCContextNames;
+
+ /// Information about Objective-C properties.
+ ///
+ /// Indexed by the context ID, property name, and whether this is an
+ /// instance property.
+ llvm::DenseMap<std::tuple<unsigned, unsigned, char>,
+ llvm::SmallVector<std::pair<VersionTuple, ObjCPropertyInfo>,
+ 1>>
+ ObjCProperties;
+
+ /// Information about Objective-C methods.
+ ///
+ /// Indexed by the context ID, selector ID, and Boolean (stored as a
+ /// char) indicating whether this is a class or instance method.
+ llvm::DenseMap<std::tuple<unsigned, unsigned, char>,
+ llvm::SmallVector<std::pair<VersionTuple, ObjCMethodInfo>, 1>>
+ ObjCMethods;
+
+ /// Information about global variables.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, GlobalVariableInfo>,
+ 1>>
+ GlobalVariables;
+
+ /// Information about global functions.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, GlobalFunctionInfo>,
+ 1>>
+ GlobalFunctions;
+
+ /// Information about enumerators.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, EnumConstantInfo>,
+ 1>>
+ EnumConstants;
+
+ /// Information about tags.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, TagInfo>, 1>>
+ Tags;
+
+ /// Information about typedefs.
+ ///
+ /// Indexed by the identifier ID.
+ llvm::DenseMap<unsigned,
+ llvm::SmallVector<std::pair<VersionTuple, TypedefInfo>, 1>>
+ Typedefs;
+
+ /// Retrieve the ID for the given identifier.
+ IdentifierID getIdentifier(StringRef identifier) {
+ if (identifier.empty())
+ return 0;
+
+ auto known = IdentifierIDs.find(identifier);
+ if (known != IdentifierIDs.end())
+ return known->second;
+
+ // Add to the identifier table.
+ known = IdentifierIDs.insert({identifier, IdentifierIDs.size() + 1}).first;
+ return known->second;
+ }
+
+ /// Retrieve the ID for the given selector.
+ SelectorID getSelector(ObjCSelectorRef selectorRef) {
+ // Translate the selector reference into a stored selector.
+ StoredObjCSelector selector;
+ selector.NumPieces = selectorRef.NumPieces;
+ selector.Identifiers.reserve(selectorRef.Identifiers.size());
+ for (auto piece : selectorRef.Identifiers) {
+ selector.Identifiers.push_back(getIdentifier(piece));
+ }
+
+ // Look for the stored selector.
+ auto known = SelectorIDs.find(selector);
+ if (known != SelectorIDs.end())
+ return known->second;
+
+ // Add to the selector table.
+ known = SelectorIDs.insert({selector, SelectorIDs.size()}).first;
+ return known->second;
+ }
+
+ void writeToStream(llvm::raw_ostream &os);
+
+private:
+ void writeBlockInfoBlock(llvm::BitstreamWriter &writer);
+ void writeControlBlock(llvm::BitstreamWriter &writer);
+ void writeIdentifierBlock(llvm::BitstreamWriter &writer);
+ void writeObjCContextBlock(llvm::BitstreamWriter &writer);
+ void writeObjCPropertyBlock(llvm::BitstreamWriter &writer);
+ void writeObjCMethodBlock(llvm::BitstreamWriter &writer);
+ void writeObjCSelectorBlock(llvm::BitstreamWriter &writer);
+ void writeGlobalVariableBlock(llvm::BitstreamWriter &writer);
+ void writeGlobalFunctionBlock(llvm::BitstreamWriter &writer);
+ void writeEnumConstantBlock(llvm::BitstreamWriter &writer);
+ void writeTagBlock(llvm::BitstreamWriter &writer);
+ void writeTypedefBlock(llvm::BitstreamWriter &writer);
+};
+
+/// Record the name of a block.
+static void emitBlockID(llvm::BitstreamWriter &out, unsigned ID,
+ StringRef name,
+ SmallVectorImpl<unsigned char> &nameBuffer) {
+ SmallVector<unsigned, 1> idBuffer;
+ idBuffer.push_back(ID);
+ out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, idBuffer);
+
+ // Emit the block name if present.
+ if (name.empty())
+ return;
+ nameBuffer.resize(name.size());
+ memcpy(nameBuffer.data(), name.data(), name.size());
+ out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer);
+}
+
+/// Record the name of a record within a block.
+static void emitRecordID(llvm::BitstreamWriter &out, unsigned ID,
+ StringRef name,
+ SmallVectorImpl<unsigned char> &nameBuffer) {
+ assert(ID < 256 && "can't fit record ID in next to name");
+ nameBuffer.resize(name.size()+1);
+ nameBuffer[0] = ID;
+ memcpy(nameBuffer.data()+1, name.data(), name.size());
+ out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer);
+}
+
+void APINotesWriter::Implementation::writeBlockInfoBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, llvm::bitc::BLOCKINFO_BLOCK_ID, 2);
+
+ SmallVector<unsigned char, 64> nameBuffer;
+#define BLOCK(X) emitBlockID(writer, X ## _ID, #X, nameBuffer)
+#define BLOCK_RECORD(K, X) emitRecordID(writer, K::X, #X, nameBuffer)
+
+ BLOCK(CONTROL_BLOCK);
+ BLOCK_RECORD(control_block, METADATA);
+ BLOCK_RECORD(control_block, MODULE_NAME);
+
+ BLOCK(IDENTIFIER_BLOCK);
+ BLOCK_RECORD(identifier_block, IDENTIFIER_DATA);
+
+ BLOCK(OBJC_CONTEXT_BLOCK);
+ BLOCK_RECORD(objc_context_block, OBJC_CONTEXT_ID_DATA);
+
+ BLOCK(OBJC_PROPERTY_BLOCK);
+ BLOCK_RECORD(objc_property_block, OBJC_PROPERTY_DATA);
+
+ BLOCK(OBJC_METHOD_BLOCK);
+ BLOCK_RECORD(objc_method_block, OBJC_METHOD_DATA);
+
+ BLOCK(OBJC_SELECTOR_BLOCK);
+ BLOCK_RECORD(objc_selector_block, OBJC_SELECTOR_DATA);
+
+ BLOCK(GLOBAL_VARIABLE_BLOCK);
+ BLOCK_RECORD(global_variable_block, GLOBAL_VARIABLE_DATA);
+
+ BLOCK(GLOBAL_FUNCTION_BLOCK);
+ BLOCK_RECORD(global_function_block, GLOBAL_FUNCTION_DATA);
+#undef BLOCK
+#undef BLOCK_RECORD
+}
+
+void APINotesWriter::Implementation::writeControlBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, CONTROL_BLOCK_ID, 3);
+ control_block::MetadataLayout metadata(writer);
+ metadata.emit(ScratchRecord, VERSION_MAJOR, VERSION_MINOR);
+
+ control_block::ModuleNameLayout moduleName(writer);
+ moduleName.emit(ScratchRecord, ModuleName);
+
+ if (SwiftInferImportAsMember) {
+ control_block::ModuleOptionsLayout moduleOptions(writer);
+ moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember);
+ }
+
+ if (SourceFile) {
+ control_block::SourceFileLayout sourceFile(writer);
+ sourceFile.emit(ScratchRecord, SourceFile->getSize(),
+ SourceFile->getModificationTime());
+ }
+}
+
+namespace {
+ /// Used to serialize the on-disk identifier table.
+ class IdentifierTableInfo {
+ public:
+ using key_type = StringRef;
+ using key_type_ref = key_type;
+ using data_type = IdentifierID;
+ using data_type_ref = const data_type &;
+ using hash_value_type = uint32_t;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return llvm::HashString(key);
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = key.size();
+ uint32_t dataLength = sizeof(uint32_t);
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ out << key;
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(data);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeIdentifierBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, IDENTIFIER_BLOCK_ID, 3);
+
+ if (IdentifierIDs.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<IdentifierTableInfo> generator;
+ for (auto &entry : IdentifierIDs)
+ generator.insert(entry.first(), entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ identifier_block::IdentifierDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Retrieve the serialized size of the given CommonEntityInfo, for use in
+ /// on-disk hash tables.
+ static unsigned getCommonEntityInfoSize(const CommonEntityInfo &info) {
+ return 5 + info.UnavailableMsg.size() + info.SwiftName.size();
+ }
+
+ /// Emit a serialized representation of the common entity information.
+ static void emitCommonEntityInfo(raw_ostream &out,
+ const CommonEntityInfo &info) {
+ endian::Writer<little> writer(out);
+ uint8_t payload = 0;
+ if (auto swiftPrivate = info.isSwiftPrivate()) {
+ payload |= 0x01;
+ if (*swiftPrivate) payload |= 0x02;
+ }
+ payload <<= 1;
+ payload |= info.Unavailable;
+ payload <<= 1;
+ payload |= info.UnavailableInSwift;
+
+ writer.write<uint8_t>(payload);
+
+ writer.write<uint16_t>(info.UnavailableMsg.size());
+ out.write(info.UnavailableMsg.c_str(), info.UnavailableMsg.size());
+ writer.write<uint16_t>(info.SwiftName.size());
+ out.write(info.SwiftName.c_str(), info.SwiftName.size());
+ }
+
+ // Retrieve the serialized size of the given CommonTypeInfo, for use
+ // in on-disk hash tables.
+ static unsigned getCommonTypeInfoSize(const CommonTypeInfo &info) {
+ return 2 + (info.getSwiftBridge() ? info.getSwiftBridge()->size() : 0) +
+ 2 + (info.getNSErrorDomain() ? info.getNSErrorDomain()->size() : 0) +
+ getCommonEntityInfoSize(info);
+ }
+
+ /// Emit a serialized representation of the common type information.
+ static void emitCommonTypeInfo(raw_ostream &out, const CommonTypeInfo &info) {
+ emitCommonEntityInfo(out, info);
+ endian::Writer<little> writer(out);
+ if (auto swiftBridge = info.getSwiftBridge()) {
+ writer.write<uint16_t>(swiftBridge->size() + 1);
+ out.write(swiftBridge->c_str(), swiftBridge->size());
+ } else {
+ writer.write<uint16_t>(0);
+ }
+ if (auto nsErrorDomain = info.getNSErrorDomain()) {
+ writer.write<uint16_t>(nsErrorDomain->size() + 1);
+ out.write(nsErrorDomain->c_str(), info.getNSErrorDomain()->size());
+ } else {
+ writer.write<uint16_t>(0);
+ }
+ }
+
+ /// Used to serialize the on-disk Objective-C context table.
+ class ObjCContextIDTableInfo {
+ public:
+ using key_type = std::pair<unsigned, char>; // identifier ID, is-protocol
+ using key_type_ref = key_type;
+ using data_type = unsigned;
+ using data_type_ref = const data_type &;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return static_cast<size_t>(llvm::hash_value(key));
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = sizeof(uint32_t) + 1;
+ uint32_t dataLength = sizeof(uint32_t);
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key.first);
+ writer.write<uint8_t>(key.second);
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(data);
+ }
+ };
+} // end anonymous namespace
+
+namespace {
+ /// Retrieve the serialized size of the given VersionTuple, for use in
+ /// on-disk hash tables.
+ unsigned getVersionTupleSize(const VersionTuple &version) {
+ unsigned size = sizeof(uint8_t) + /*major*/sizeof(uint32_t);
+ if (version.getMinor()) size += sizeof(uint32_t);
+ if (version.getSubminor()) size += sizeof(uint32_t);
+ if (version.getBuild()) size += sizeof(uint32_t);
+ return size;
+ }
+
+ /// Emit a serialized representation of a version tuple.
+ void emitVersionTuple(raw_ostream &out, const VersionTuple &version) {
+ endian::Writer<little> writer(out);
+
+ // First byte contains the number of components beyond the 'major'
+ // component.
+ uint8_t descriptor;
+ if (version.getBuild()) descriptor = 3;
+ else if (version.getSubminor()) descriptor = 2;
+ else if (version.getMinor()) descriptor = 1;
+ else descriptor = 0;
+ assert(!version.usesUnderscores() && "Not a serializable version");
+ writer.write<uint8_t>(descriptor);
+
+ // Write the components.
+ writer.write<uint32_t>(version.getMajor());
+ if (auto minor = version.getMinor())
+ writer.write<uint32_t>(*minor);
+ if (auto subminor = version.getSubminor())
+ writer.write<uint32_t>(*subminor);
+ if (auto build = version.getBuild())
+ writer.write<uint32_t>(*build);
+ }
+
+ /// Localized helper to make a type dependent, thwarting template argument
+ /// deduction.
+ template<typename T>
+ struct MakeDependent {
+ typedef T Type;
+ };
+
+ /// Determine the size of an array of versioned information,
+ template<typename T>
+ unsigned getVersionedInfoSize(
+ const SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray,
+ llvm::function_ref<unsigned(const typename MakeDependent<T>::Type&)>
+ getInfoSize) {
+ unsigned result = sizeof(uint16_t); // # of elements
+ for (const auto &element : infoArray) {
+ result += getVersionTupleSize(element.first);
+ result += getInfoSize(element.second);
+ }
+
+ return result;
+ }
+
+ /// Emit versioned information.
+ template<typename T>
+ void emitVersionedInfo(
+ raw_ostream &out,
+ SmallVectorImpl<std::pair<VersionTuple, T>> &infoArray,
+ llvm::function_ref<void(raw_ostream &out,
+ const typename MakeDependent<T>::Type& info)>
+ emitInfo) {
+ std::sort(infoArray.begin(), infoArray.end(),
+ [](const std::pair<VersionTuple, T> &left,
+ const std::pair<VersionTuple, T> &right) -> bool {
+ assert(left.first != right.first && "two entries for the same version");
+ return left.first < right.first;
+ });
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(infoArray.size());
+ for (const auto &element : infoArray) {
+ emitVersionTuple(out, element.first);
+ emitInfo(out, element.second);
+ }
+ }
+
+ /// Retrieve the serialized size of the given VariableInfo, for use in
+ /// on-disk hash tables.
+ unsigned getVariableInfoSize(const VariableInfo &info) {
+ return 2 + getCommonEntityInfoSize(info) + 2 + info.getType().size();
+ }
+
+ /// Emit a serialized representation of the variable information.
+ void emitVariableInfo(raw_ostream &out, const VariableInfo &info) {
+ emitCommonEntityInfo(out, info);
+
+ uint8_t bytes[2] = { 0, 0 };
+ if (auto nullable = info.getNullability()) {
+ bytes[0] = 1;
+ bytes[1] = static_cast<uint8_t>(*nullable);
+ } else {
+ // Nothing to do.
+ }
+
+ out.write(reinterpret_cast<const char *>(bytes), 2);
+
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(info.getType().size());
+ out.write(info.getType().data(), info.getType().size());
+ }
+
+ /// On-dish hash table info key base for handling versioned data.
+ template<typename Derived, typename KeyType, typename UnversionedDataType>
+ class VersionedTableInfo {
+ Derived &asDerived() {
+ return *static_cast<Derived *>(this);
+ }
+
+ const Derived &asDerived() const {
+ return *static_cast<const Derived *>(this);
+ }
+
+ public:
+ using key_type = KeyType;
+ using key_type_ref = key_type;
+ using data_type =
+ SmallVector<std::pair<VersionTuple, UnversionedDataType>, 1>;
+ using data_type_ref = data_type &;
+ using hash_value_type = size_t;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return llvm::hash_value(key);
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = asDerived().getKeyLength(key);
+ uint32_t dataLength = getVersionedInfoSize(data,
+ [this](const UnversionedDataType &unversionedInfo) {
+ return asDerived().getUnversionedInfoSize(unversionedInfo);
+ });
+
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ emitVersionedInfo(out, data,
+ [this](llvm::raw_ostream &out,
+ const UnversionedDataType &unversionedInfo) {
+ asDerived().emitUnversionedInfo(out, unversionedInfo);
+ });
+ }
+ };
+
+ /// Used to serialize the on-disk Objective-C property table.
+ class ObjCContextInfoTableInfo
+ : public VersionedTableInfo<ObjCContextInfoTableInfo,
+ unsigned,
+ ObjCContextInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const ObjCContextInfo &info) {
+ return getCommonTypeInfoSize(info) + 1;
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const ObjCContextInfo &info) {
+ emitCommonTypeInfo(out, info);
+
+ uint8_t payload = 0;
+ if (auto swiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric()) {
+ payload |= (0x01 << 1) | swiftImportAsNonGeneric.getValue();
+ }
+ payload <<= 2;
+ if (auto swiftObjCMembers = info.getSwiftObjCMembers()) {
+ payload |= (0x01 << 1) | swiftObjCMembers.getValue();
+ }
+ payload <<= 3;
+ if (auto nullable = info.getDefaultNullability()) {
+ payload |= (0x01 << 2) | static_cast<uint8_t>(*nullable);
+ }
+ payload = (payload << 1) | (info.hasDesignatedInits() ? 1 : 0);
+ out << payload;
+ }
+ };
+
+ /// Used to serialize the on-disk Objective-C property table.
+ class ObjCPropertyTableInfo
+ : public VersionedTableInfo<ObjCPropertyTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCPropertyInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(std::get<0>(key));
+ writer.write<uint32_t>(std::get<1>(key));
+ writer.write<uint8_t>(std::get<2>(key));
+ }
+
+ unsigned getUnversionedInfoSize(const ObjCPropertyInfo &info) {
+ return getVariableInfoSize(info) + 1;
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const ObjCPropertyInfo &info) {
+ emitVariableInfo(out, info);
+ uint8_t flags = 0;
+ if (Optional<bool> value = info.getSwiftImportAsAccessors()) {
+ flags |= 1 << 0;
+ flags |= value.getValue() << 1;
+ }
+ out << flags;
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeObjCContextBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_CONTEXT_BLOCK_ID, 3);
+
+ if (ObjCContexts.empty())
+ return;
+
+ {
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCContextIDTableInfo> generator;
+ for (auto &entry : ObjCContexts)
+ generator.insert(entry.first, entry.second.first);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_context_block::ObjCContextIDLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+ }
+
+ {
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCContextInfoTableInfo>
+ generator;
+ for (auto &entry : ObjCContexts)
+ generator.insert(entry.second.first, entry.second.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_context_block::ObjCContextInfoLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+ }
+}
+
+void APINotesWriter::Implementation::writeObjCPropertyBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_PROPERTY_BLOCK_ID, 3);
+
+ if (ObjCProperties.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCPropertyTableInfo> generator;
+ for (auto &entry : ObjCProperties)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_property_block::ObjCPropertyDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ static unsigned getParamInfoSize(const ParamInfo &info) {
+ return getVariableInfoSize(info) + 1;
+ }
+
+ static void emitParamInfo(raw_ostream &out, const ParamInfo &info) {
+ emitVariableInfo(out, info);
+
+ endian::Writer<little> writer(out);
+
+ uint8_t payload = 0;
+ if (auto noescape = info.isNoEscape()) {
+ payload |= 0x01;
+ if (*noescape)
+ payload |= 0x02;
+ }
+ writer.write<uint8_t>(payload);
+ }
+
+ /// Retrieve the serialized size of the given FunctionInfo, for use in
+ /// on-disk hash tables.
+ static unsigned getFunctionInfoSize(const FunctionInfo &info) {
+ unsigned size = 2 + sizeof(uint64_t) + getCommonEntityInfoSize(info) + 2;
+
+ for (const auto ¶m : info.Params)
+ size += getParamInfoSize(param);
+
+ size += 2 + info.ResultType.size();
+ return size;
+ }
+
+ /// Emit a serialized representation of the function information.
+ static void emitFunctionInfo(raw_ostream &out, const FunctionInfo &info) {
+ emitCommonEntityInfo(out, info);
+
+ endian::Writer<little> writer(out);
+ writer.write<uint8_t>(info.NullabilityAudited);
+ writer.write<uint8_t>(info.NumAdjustedNullable);
+ writer.write<uint64_t>(info.NullabilityPayload);
+
+ // Parameters.
+ writer.write<uint16_t>(info.Params.size());
+ for (const auto &pi : info.Params)
+ emitParamInfo(out, pi);
+
+ // Result type.
+ writer.write<uint16_t>(info.ResultType.size());
+ out.write(info.ResultType.data(), info.ResultType.size());
+ }
+
+ /// Used to serialize the on-disk Objective-C method table.
+ class ObjCMethodTableInfo
+ : public VersionedTableInfo<ObjCMethodTableInfo,
+ std::tuple<unsigned, unsigned, char>,
+ ObjCMethodInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t) + sizeof(uint32_t) + 1;
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(std::get<0>(key));
+ writer.write<uint32_t>(std::get<1>(key));
+ writer.write<uint8_t>(std::get<2>(key));
+ }
+
+ unsigned getUnversionedInfoSize(const ObjCMethodInfo &info) {
+ return 1 + getFunctionInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const ObjCMethodInfo &info) {
+ uint8_t payload = 0;
+ payload = (payload << 1) | info.DesignatedInit;
+ payload = (payload << 1) | info.Required;
+ endian::Writer<little> writer(out);
+ writer.write<uint8_t>(payload);
+
+ emitFunctionInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeObjCMethodBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_METHOD_BLOCK_ID, 3);
+
+ if (ObjCMethods.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCMethodTableInfo> generator;
+ for (auto &entry : ObjCMethods) {
+ generator.insert(entry.first, entry.second);
+ }
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_method_block::ObjCMethodDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk Objective-C selector table.
+ class ObjCSelectorTableInfo {
+ public:
+ using key_type = StoredObjCSelector;
+ using key_type_ref = const key_type &;
+ using data_type = SelectorID;
+ using data_type_ref = data_type;
+ using hash_value_type = unsigned;
+ using offset_type = unsigned;
+
+ hash_value_type ComputeHash(key_type_ref key) {
+ return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(key);
+ }
+
+ std::pair<unsigned, unsigned> EmitKeyDataLength(raw_ostream &out,
+ key_type_ref key,
+ data_type_ref data) {
+ uint32_t keyLength = sizeof(uint16_t)
+ + sizeof(uint32_t) * key.Identifiers.size();
+ uint32_t dataLength = sizeof(uint32_t);
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(keyLength);
+ writer.write<uint16_t>(dataLength);
+ return { keyLength, dataLength };
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint16_t>(key.NumPieces);
+ for (auto piece : key.Identifiers) {
+ writer.write<uint32_t>(piece);
+ }
+ }
+
+ void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data,
+ unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(data);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeObjCSelectorBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, OBJC_SELECTOR_BLOCK_ID, 3);
+
+ if (SelectorIDs.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<ObjCSelectorTableInfo> generator;
+ for (auto &entry : SelectorIDs)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ objc_selector_block::ObjCSelectorDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk global variable table.
+ class GlobalVariableTableInfo
+ : public VersionedTableInfo<GlobalVariableTableInfo,
+ unsigned,
+ GlobalVariableInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref key) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const GlobalVariableInfo &info) {
+ return getVariableInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out,
+ const GlobalVariableInfo &info) {
+ emitVariableInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeGlobalVariableBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, GLOBAL_VARIABLE_BLOCK_ID, 3);
+
+ if (GlobalVariables.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<GlobalVariableTableInfo> generator;
+ for (auto &entry : GlobalVariables)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ global_variable_block::GlobalVariableDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk global function table.
+ class GlobalFunctionTableInfo
+ : public VersionedTableInfo<GlobalFunctionTableInfo,
+ unsigned,
+ GlobalFunctionInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const GlobalFunctionInfo &info) {
+ return getFunctionInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out,
+ const GlobalFunctionInfo &info) {
+ emitFunctionInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeGlobalFunctionBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, GLOBAL_FUNCTION_BLOCK_ID, 3);
+
+ if (GlobalFunctions.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<GlobalFunctionTableInfo> generator;
+ for (auto &entry : GlobalFunctions) {
+ generator.insert(entry.first, entry.second);
+ }
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ global_function_block::GlobalFunctionDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk global enum constant.
+ class EnumConstantTableInfo
+ : public VersionedTableInfo<EnumConstantTableInfo,
+ unsigned,
+ EnumConstantInfo> {
+ public:
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(uint32_t);
+ }
+
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<uint32_t>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const EnumConstantInfo &info) {
+ return getCommonEntityInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const EnumConstantInfo &info) {
+ emitCommonEntityInfo(out, info);
+ }
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeEnumConstantBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, ENUM_CONSTANT_BLOCK_ID, 3);
+
+ if (EnumConstants.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<EnumConstantTableInfo> generator;
+ for (auto &entry : EnumConstants)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ enum_constant_block::EnumConstantDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ template<typename Derived, typename UnversionedDataType>
+ class CommonTypeTableInfo
+ : public VersionedTableInfo<Derived, unsigned, UnversionedDataType> {
+ public:
+ using key_type_ref = typename CommonTypeTableInfo::key_type_ref;
+
+ unsigned getKeyLength(key_type_ref) {
+ return sizeof(IdentifierID);
+ }
+ void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) {
+ endian::Writer<little> writer(out);
+ writer.write<IdentifierID>(key);
+ }
+
+ unsigned getUnversionedInfoSize(const UnversionedDataType &info) {
+ return getCommonTypeInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out,
+ const UnversionedDataType &info) {
+ emitCommonTypeInfo(out, info);
+ }
+ };
+
+ /// Used to serialize the on-disk tag table.
+ class TagTableInfo : public CommonTypeTableInfo<TagTableInfo, TagInfo> {
+ public:
+ unsigned getUnversionedInfoSize(const TagInfo &info) {
+ return 1 + getCommonTypeInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const TagInfo &info) {
+ endian::Writer<little> writer(out);
+
+ uint8_t payload = 0;
+ if (auto enumExtensibility = info.EnumExtensibility) {
+ payload |= static_cast<uint8_t>(enumExtensibility.getValue()) + 1;
+ assert((payload < (1 << 2)) && "must fit in two bits");
+ }
+
+ payload <<= 2;
+ if (Optional<bool> value = info.isFlagEnum()) {
+ payload |= 1 << 0;
+ payload |= value.getValue() << 1;
+ }
+
+ writer.write<uint8_t>(payload);
+
+ emitCommonTypeInfo(out, info);
+ }
+ };
+
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeTagBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, TAG_BLOCK_ID, 3);
+
+ if (Tags.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<TagTableInfo> generator;
+ for (auto &entry : Tags)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ tag_block::TagDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+namespace {
+ /// Used to serialize the on-disk typedef table.
+ class TypedefTableInfo
+ : public CommonTypeTableInfo<TypedefTableInfo, TypedefInfo> {
+
+ public:
+ unsigned getUnversionedInfoSize(const TypedefInfo &info) {
+ return 1 + getCommonTypeInfoSize(info);
+ }
+
+ void emitUnversionedInfo(raw_ostream &out, const TypedefInfo &info) {
+ endian::Writer<little> writer(out);
+
+ uint8_t payload = 0;
+ if (auto swiftWrapper = info.SwiftWrapper) {
+ payload |= static_cast<uint8_t>(*swiftWrapper) + 1;
+ }
+
+ writer.write<uint8_t>(payload);
+
+ emitCommonTypeInfo(out, info);
+ }
+
+ };
+} // end anonymous namespace
+
+void APINotesWriter::Implementation::writeTypedefBlock(
+ llvm::BitstreamWriter &writer) {
+ BCBlockRAII restoreBlock(writer, TYPEDEF_BLOCK_ID, 3);
+
+ if (Typedefs.empty())
+ return;
+
+ llvm::SmallString<4096> hashTableBlob;
+ uint32_t tableOffset;
+ {
+ llvm::OnDiskChainedHashTableGenerator<TypedefTableInfo> generator;
+ for (auto &entry : Typedefs)
+ generator.insert(entry.first, entry.second);
+
+ llvm::raw_svector_ostream blobStream(hashTableBlob);
+ // Make sure that no bucket is at offset 0
+ endian::Writer<little>(blobStream).write<uint32_t>(0);
+ tableOffset = generator.Emit(blobStream);
+ }
+
+ typedef_block::TypedefDataLayout layout(writer);
+ layout.emit(ScratchRecord, tableOffset, hashTableBlob);
+}
+
+void APINotesWriter::Implementation::writeToStream(llvm::raw_ostream &os) {
+ // Write the API notes file into a buffer.
+ SmallVector<char, 0> buffer;
+ {
+ llvm::BitstreamWriter writer(buffer);
+
+ // Emit the signature.
+ for (unsigned char byte : API_NOTES_SIGNATURE)
+ writer.Emit(byte, 8);
+
+ // Emit the blocks.
+ writeBlockInfoBlock(writer);
+ writeControlBlock(writer);
+ writeIdentifierBlock(writer);
+ writeObjCContextBlock(writer);
+ writeObjCPropertyBlock(writer);
+ writeObjCMethodBlock(writer);
+ writeObjCSelectorBlock(writer);
+ writeGlobalVariableBlock(writer);
+ writeGlobalFunctionBlock(writer);
+ writeEnumConstantBlock(writer);
+ writeTagBlock(writer);
+ writeTypedefBlock(writer);
+ }
+
+ // Write the buffer to the stream.
+ os.write(buffer.data(), buffer.size());
+ os.flush();
+}
+
+APINotesWriter::APINotesWriter(StringRef moduleName, const FileEntry *sourceFile)
+ : Impl(*new Implementation)
+{
+ Impl.ModuleName = moduleName;
+ Impl.SourceFile = sourceFile;
+}
+
+APINotesWriter::~APINotesWriter() {
+ delete &Impl;
+}
+
+
+void APINotesWriter::writeToStream(raw_ostream &os) {
+ Impl.writeToStream(os);
+}
+
+ContextID APINotesWriter::addObjCContext(StringRef name, bool isClass,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID nameID = Impl.getIdentifier(name);
+
+ std::pair<unsigned, char> key(nameID, isClass ? 0 : 1);
+ auto known = Impl.ObjCContexts.find(key);
+ if (known == Impl.ObjCContexts.end()) {
+ unsigned nextID = Impl.ObjCContexts.size() + 1;
+
+ VersionedSmallVector<ObjCContextInfo> emptyVersionedInfo;
+ known = Impl.ObjCContexts.insert(
+ std::make_pair(key, std::make_pair(nextID, emptyVersionedInfo)))
+ .first;
+
+ Impl.ObjCContextNames[nextID] = nameID;
+ }
+
+ // Add this version information.
+ auto &versionedVec = known->second.second;
+ bool found = false;
+ for (auto &versioned : versionedVec){
+ if (versioned.first == swiftVersion) {
+ versioned.second |= info;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ versionedVec.push_back({swiftVersion, info});
+
+ return ContextID(known->second.first);
+}
+
+void APINotesWriter::addObjCProperty(ContextID contextID, StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID nameID = Impl.getIdentifier(name);
+ Impl.ObjCProperties[std::make_tuple(contextID.Value, nameID, isInstance)]
+ .push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addObjCMethod(ContextID contextID,
+ ObjCSelectorRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion) {
+ SelectorID selectorID = Impl.getSelector(selector);
+ auto key = std::tuple<unsigned, unsigned, char>{
+ contextID.Value, selectorID, isInstanceMethod};
+ Impl.ObjCMethods[key].push_back({swiftVersion, info});
+
+ // If this method is a designated initializer, update the class to note that
+ // it has designated initializers.
+ if (info.DesignatedInit) {
+ assert(Impl.ObjCContexts.count({Impl.ObjCContextNames[contextID.Value],
+ (char)0}));
+ auto &versionedVec =
+ Impl.ObjCContexts[{Impl.ObjCContextNames[contextID.Value], (char)0}]
+ .second;
+ bool found = false;
+ for (auto &versioned : versionedVec) {
+ if (versioned.first == swiftVersion) {
+ versioned.second.setHasDesignatedInits(true);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ versionedVec.push_back({swiftVersion, ObjCContextInfo()});
+ versionedVec.back().second.setHasDesignatedInits(true);
+ }
+ }
+}
+
+void APINotesWriter::addGlobalVariable(llvm::StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID variableID = Impl.getIdentifier(name);
+ Impl.GlobalVariables[variableID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addGlobalFunction(llvm::StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID nameID = Impl.getIdentifier(name);
+ Impl.GlobalFunctions[nameID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addEnumConstant(llvm::StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID enumConstantID = Impl.getIdentifier(name);
+ Impl.EnumConstants[enumConstantID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addTag(llvm::StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID tagID = Impl.getIdentifier(name);
+ Impl.Tags[tagID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addTypedef(llvm::StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion) {
+ IdentifierID typedefID = Impl.getIdentifier(name);
+ Impl.Typedefs[typedefID].push_back({swiftVersion, info});
+}
+
+void APINotesWriter::addModuleOptions(ModuleOptions opts) {
+ Impl.SwiftInferImportAsMember = opts.SwiftInferImportAsMember;
+}
+
diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp
new file mode 100644
index 0000000..f0f6cb8
--- /dev/null
+++ b/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -0,0 +1,1503 @@
+//===--- APINotesYAMLCompiler.cpp - API Notes YAML format reader *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file reads API notes specified in YAML format.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "clang/APINotes/APINotesReader.h"
+#include "clang/APINotes/Types.h"
+#include "clang/APINotes/APINotesWriter.h"
+#include "clang/Basic/VersionTuple.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <algorithm>
+
+/*
+
+ YAML Format specification.
+
+ Nullability should be expressed using one of the following values:
+ O - Optional (or Nullable)
+ N - Not Optional
+ S - Scalar
+ U - Unknown
+ Note, the API is considered 'audited' when at least the return value or a
+ parameter has a nullability value. For 'audited' APIs, we assume the default
+ nullability for any underspecified type.
+
+---
+ Name: AppKit # The name of the framework
+
+ Availability: OSX # Optional: Specifies which platform the API is
+ # available on. [OSX / iOS / none/
+ # available / nonswift]
+
+ AvailabilityMsg: "" # Optional: Custom availability message to display to
+ # the user, when API is not available.
+
+ Classes: # List of classes
+ ...
+ Protocols: # List of protocols
+ ...
+ Functions: # List of functions
+ ...
+ Globals: # List of globals
+ ...
+ Enumerators: # List of enumerators
+ ...
+ Tags: # List of tags (struct/union/enum/C++ class)
+ ...
+ Typedefs: # List of typedef-names and C++11 type aliases
+ ...
+
+ Each class and protocol is defined as following:
+
+ - Name: NSView # The name of the class
+
+ AuditedForNullability: false # Optional: Specifies if the whole class
+ # has been audited for nullability.
+ # If yes, we assume all the methods and
+ # properties of the class have default
+ # nullability unless it is overwritten by
+ # a method/property specific info below.
+ # This applies to all classes, extensions,
+ # and categories of the class defined in
+ # the current framework/module.
+ # (false/true)
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+ Methods:
+ - Selector: "setSubviews:" # Full name
+
+ MethodKind: Instance # [Class/Instance]
+
+ Nullability: [N, N, O, S] # The nullability of parameters in
+ # the signature.
+
+ NullabilityOfRet: O # The nullability of the return value.
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+ DesignatedInit: false # Optional: Specifies if this method is a
+ # designated initializer (false/true)
+
+ Required: false # Optional: Specifies if this method is a
+ # required initializer (false/true)
+
+ Properties:
+ - Name: window
+
+ Nullability: O
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+ The protocol definition format is the same as the class definition.
+
+ Each function definition is of the following form:
+
+ - Name: "myGlobalFunction" # Full name
+
+ Nullability: [N, N, O, S] # The nullability of parameters in
+ # the signature.
+
+ NullabilityOfRet: O # The nullability of the return value.
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+Each global variable definition is of the following form:
+
+ - Name: MyGlobalVar
+
+ Nullability: O
+
+ Availability: OSX
+
+ AvailabilityMsg: ""
+
+*/
+
+using llvm::StringRef;
+using namespace clang;
+namespace {
+ enum class APIAvailability {
+ Available = 0,
+ OSX,
+ IOS,
+ None,
+ NonSwift,
+ };
+
+ enum class MethodKind {
+ Class,
+ Instance,
+ };
+
+ /// Old attribute deprecated in favor of SwiftName.
+ enum class FactoryAsInitKind {
+ /// Infer based on name and type (the default).
+ Infer,
+ /// Treat as a class method.
+ AsClassMethod,
+ /// Treat as an initializer.
+ AsInitializer
+ };
+
+ /// Syntactic sugar for EnumExtensibility and FlagEnum
+ enum class EnumConvenienceAliasKind {
+ /// EnumExtensibility: none, FlagEnum: false
+ None,
+ /// EnumExtensibility: open, FlagEnum: false
+ CFEnum,
+ /// EnumExtensibility: open, FlagEnum: true
+ CFOptions,
+ /// EnumExtensibility: closed, FlagEnum: false
+ CFClosedEnum
+ };
+
+ struct AvailabilityItem {
+ APIAvailability Mode = APIAvailability::Available;
+ StringRef Msg;
+ AvailabilityItem() : Mode(APIAvailability::Available), Msg("") {}
+ };
+
+ static llvm::Optional<NullabilityKind> AbsentNullability = llvm::None;
+ static llvm::Optional<NullabilityKind> DefaultNullability =
+ NullabilityKind::NonNull;
+ typedef std::vector<clang::NullabilityKind> NullabilitySeq;
+
+ struct Param {
+ unsigned Position;
+ Optional<bool> NoEscape = false;
+ llvm::Optional<NullabilityKind> Nullability;
+ StringRef Type;
+ };
+ typedef std::vector<Param> ParamsSeq;
+
+ struct Method {
+ StringRef Selector;
+ MethodKind Kind;
+ ParamsSeq Params;
+ NullabilitySeq Nullability;
+ llvm::Optional<NullabilityKind> NullabilityOfRet;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ FactoryAsInitKind FactoryAsInit = FactoryAsInitKind::Infer;
+ bool DesignatedInit = false;
+ bool Required = false;
+ StringRef ResultType;
+ };
+ typedef std::vector<Method> MethodsSeq;
+
+ struct Property {
+ StringRef Name;
+ llvm::Optional<MethodKind> Kind;
+ llvm::Optional<NullabilityKind> Nullability;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ Optional<bool> SwiftImportAsAccessors;
+ StringRef Type;
+ };
+ typedef std::vector<Property> PropertiesSeq;
+
+ struct Class {
+ StringRef Name;
+ bool AuditedForNullability = false;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ Optional<bool> SwiftImportAsNonGeneric;
+ Optional<bool> SwiftObjCMembers;
+ MethodsSeq Methods;
+ PropertiesSeq Properties;
+ };
+ typedef std::vector<Class> ClassesSeq;
+
+ struct Function {
+ StringRef Name;
+ ParamsSeq Params;
+ NullabilitySeq Nullability;
+ llvm::Optional<NullabilityKind> NullabilityOfRet;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ StringRef Type;
+ StringRef ResultType;
+ };
+ typedef std::vector<Function> FunctionsSeq;
+
+ struct GlobalVariable {
+ StringRef Name;
+ llvm::Optional<NullabilityKind> Nullability;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ StringRef Type;
+ };
+ typedef std::vector<GlobalVariable> GlobalVariablesSeq;
+
+ struct EnumConstant {
+ StringRef Name;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ };
+ typedef std::vector<EnumConstant> EnumConstantsSeq;
+
+ struct Tag {
+ StringRef Name;
+ AvailabilityItem Availability;
+ StringRef SwiftName;
+ Optional<bool> SwiftPrivate;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ Optional<api_notes::EnumExtensibilityKind> EnumExtensibility;
+ Optional<bool> FlagEnum;
+ Optional<EnumConvenienceAliasKind> EnumConvenienceKind;
+ };
+ typedef std::vector<Tag> TagsSeq;
+
+ struct Typedef {
+ StringRef Name;
+ AvailabilityItem Availability;
+ StringRef SwiftName;
+ Optional<bool> SwiftPrivate;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ Optional<api_notes::SwiftWrapperKind> SwiftWrapper;
+ };
+ typedef std::vector<Typedef> TypedefsSeq;
+
+ struct TopLevelItems {
+ ClassesSeq Classes;
+ ClassesSeq Protocols;
+ FunctionsSeq Functions;
+ GlobalVariablesSeq Globals;
+ EnumConstantsSeq EnumConstants;
+ TagsSeq Tags;
+ TypedefsSeq Typedefs;
+ };
+
+ struct Versioned {
+ VersionTuple Version;
+ TopLevelItems Items;
+ };
+
+ typedef std::vector<Versioned> VersionedSeq;
+
+ struct Module {
+ StringRef Name;
+ AvailabilityItem Availability;
+ TopLevelItems TopLevel;
+ VersionedSeq SwiftVersions;
+
+ llvm::Optional<bool> SwiftInferImportAsMember = {llvm::None};
+
+ LLVM_ATTRIBUTE_DEPRECATED(
+ void dump() LLVM_ATTRIBUTE_USED,
+ "only for use within the debugger");
+ };
+}
+
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(clang::NullabilityKind)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Method)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Property)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Param)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Class)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Function)
+LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable)
+LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Tag)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef)
+LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned)
+
+namespace llvm {
+ namespace yaml {
+
+ template <>
+ struct ScalarEnumerationTraits<NullabilityKind > {
+ static void enumeration(IO &io, NullabilityKind &value) {
+ io.enumCase(value, "N", NullabilityKind::NonNull);
+ io.enumCase(value, "O", NullabilityKind::Nullable);
+ io.enumCase(value, "U", NullabilityKind::Unspecified);
+ // TODO: Mapping this to it's own value would allow for better cross
+ // checking. Also the default should be Unknown.
+ io.enumCase(value, "S", NullabilityKind::Unspecified);
+ }
+ };
+
+ template <>
+ struct ScalarEnumerationTraits<FactoryAsInitKind> {
+ static void enumeration(IO &io, FactoryAsInitKind &value) {
+ io.enumCase(value, "A", FactoryAsInitKind::Infer);
+ io.enumCase(value, "C", FactoryAsInitKind::AsClassMethod);
+ io.enumCase(value, "I", FactoryAsInitKind::AsInitializer);
+ }
+ };
+
+ template <>
+ struct ScalarEnumerationTraits<MethodKind> {
+ static void enumeration(IO &io, MethodKind &value) {
+ io.enumCase(value, "Class", MethodKind::Class);
+ io.enumCase(value, "Instance", MethodKind::Instance);
+ }
+ };
+
+ template <>
+ struct ScalarEnumerationTraits<APIAvailability> {
+ static void enumeration(IO &io, APIAvailability &value) {
+ io.enumCase(value, "OSX", APIAvailability::OSX);
+ io.enumCase(value, "iOS", APIAvailability::IOS);
+ io.enumCase(value, "none", APIAvailability::None);
+ io.enumCase(value, "nonswift", APIAvailability::NonSwift);
+ io.enumCase(value, "available", APIAvailability::Available);
+ }
+ };
+
+ template<>
+ struct ScalarEnumerationTraits<api_notes::SwiftWrapperKind> {
+ static void enumeration(IO &io, api_notes::SwiftWrapperKind &value) {
+ io.enumCase(value, "none", api_notes::SwiftWrapperKind::None);
+ io.enumCase(value, "struct", api_notes::SwiftWrapperKind::Struct);
+ io.enumCase(value, "enum", api_notes::SwiftWrapperKind::Enum);
+ }
+ };
+
+ template<>
+ struct ScalarEnumerationTraits<api_notes::EnumExtensibilityKind> {
+ static void enumeration(IO &io, api_notes::EnumExtensibilityKind &value) {
+ io.enumCase(value, "none", api_notes::EnumExtensibilityKind::None);
+ io.enumCase(value, "open", api_notes::EnumExtensibilityKind::Open);
+ io.enumCase(value, "closed", api_notes::EnumExtensibilityKind::Closed);
+ }
+ };
+
+ template<>
+ struct ScalarEnumerationTraits<EnumConvenienceAliasKind> {
+ static void enumeration(IO &io, EnumConvenienceAliasKind &value) {
+ io.enumCase(value, "none", EnumConvenienceAliasKind::None);
+ io.enumCase(value, "CFEnum", EnumConvenienceAliasKind::CFEnum);
+ io.enumCase(value, "NSEnum", EnumConvenienceAliasKind::CFEnum);
+ io.enumCase(value, "CFOptions", EnumConvenienceAliasKind::CFOptions);
+ io.enumCase(value, "NSOptions", EnumConvenienceAliasKind::CFOptions);
+ io.enumCase(value, "CFClosedEnum",
+ EnumConvenienceAliasKind::CFClosedEnum);
+ io.enumCase(value, "NSClosedEnum",
+ EnumConvenienceAliasKind::CFClosedEnum);
+ }
+ };
+
+ template <>
+ struct ScalarTraits<VersionTuple> {
+ static void output(const VersionTuple &value, void*,
+ llvm::raw_ostream &out) {
+ out << value;
+ }
+ static StringRef input(StringRef scalar, void*, VersionTuple &value) {
+ if (value.tryParse(scalar))
+ return "not a version number in the form XX.YY";
+
+ // Canonicalize on '.' as a separator.
+ value.UseDotAsSeparator();
+ return StringRef();
+ }
+
+ static bool mustQuote(StringRef) { return false; }
+ };
+
+ template <>
+ struct MappingTraits<Param> {
+ static void mapping(IO &io, Param& p) {
+ io.mapRequired("Position", p.Position);
+ io.mapOptional("Nullability", p.Nullability,
+ AbsentNullability);
+ io.mapOptional("NoEscape", p.NoEscape);
+ io.mapOptional("Type", p.Type, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<Property> {
+ static void mapping(IO &io, Property& p) {
+ io.mapRequired("Name", p.Name);
+ io.mapOptional("PropertyKind", p.Kind);
+ io.mapOptional("Nullability", p.Nullability,
+ AbsentNullability);
+ io.mapOptional("Availability", p.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", p.Availability.Msg);
+ io.mapOptional("SwiftPrivate", p.SwiftPrivate);
+ io.mapOptional("SwiftName", p.SwiftName);
+ io.mapOptional("SwiftImportAsAccessors", p.SwiftImportAsAccessors);
+ io.mapOptional("Type", p.Type, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<Method> {
+ static void mapping(IO &io, Method& m) {
+ io.mapRequired("Selector", m.Selector);
+ io.mapRequired("MethodKind", m.Kind);
+ io.mapOptional("Parameters", m.Params);
+ io.mapOptional("Nullability", m.Nullability);
+ io.mapOptional("NullabilityOfRet", m.NullabilityOfRet,
+ AbsentNullability);
+ io.mapOptional("Availability", m.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", m.Availability.Msg);
+ io.mapOptional("SwiftPrivate", m.SwiftPrivate);
+ io.mapOptional("SwiftName", m.SwiftName);
+ io.mapOptional("FactoryAsInit", m.FactoryAsInit,
+ FactoryAsInitKind::Infer);
+ io.mapOptional("DesignatedInit", m.DesignatedInit, false);
+ io.mapOptional("Required", m.Required, false);
+ io.mapOptional("ResultType", m.ResultType, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<Class> {
+ static void mapping(IO &io, Class& c) {
+ io.mapRequired("Name", c.Name);
+ io.mapOptional("AuditedForNullability", c.AuditedForNullability, false);
+ io.mapOptional("Availability", c.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", c.Availability.Msg);
+ io.mapOptional("SwiftPrivate", c.SwiftPrivate);
+ io.mapOptional("SwiftName", c.SwiftName);
+ io.mapOptional("SwiftBridge", c.SwiftBridge);
+ io.mapOptional("NSErrorDomain", c.NSErrorDomain);
+ io.mapOptional("SwiftImportAsNonGeneric", c.SwiftImportAsNonGeneric);
+ io.mapOptional("SwiftObjCMembers", c.SwiftObjCMembers);
+ io.mapOptional("Methods", c.Methods);
+ io.mapOptional("Properties", c.Properties);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Function> {
+ static void mapping(IO &io, Function& f) {
+ io.mapRequired("Name", f.Name);
+ io.mapOptional("Parameters", f.Params);
+ io.mapOptional("Nullability", f.Nullability);
+ io.mapOptional("NullabilityOfRet", f.NullabilityOfRet,
+ AbsentNullability);
+ io.mapOptional("Availability", f.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", f.Availability.Msg);
+ io.mapOptional("SwiftPrivate", f.SwiftPrivate);
+ io.mapOptional("SwiftName", f.SwiftName);
+ io.mapOptional("ResultType", f.ResultType, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<GlobalVariable> {
+ static void mapping(IO &io, GlobalVariable& v) {
+ io.mapRequired("Name", v.Name);
+ io.mapOptional("Nullability", v.Nullability,
+ AbsentNullability);
+ io.mapOptional("Availability", v.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", v.Availability.Msg);
+ io.mapOptional("SwiftPrivate", v.SwiftPrivate);
+ io.mapOptional("SwiftName", v.SwiftName);
+ io.mapOptional("Type", v.Type, StringRef(""));
+ }
+ };
+
+ template <>
+ struct MappingTraits<EnumConstant> {
+ static void mapping(IO &io, EnumConstant& v) {
+ io.mapRequired("Name", v.Name);
+ io.mapOptional("Availability", v.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", v.Availability.Msg);
+ io.mapOptional("SwiftPrivate", v.SwiftPrivate);
+ io.mapOptional("SwiftName", v.SwiftName);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Tag> {
+ static void mapping(IO &io, Tag& t) {
+ io.mapRequired("Name", t.Name);
+ io.mapOptional("Availability", t.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", t.Availability.Msg);
+ io.mapOptional("SwiftPrivate", t.SwiftPrivate);
+ io.mapOptional("SwiftName", t.SwiftName);
+ io.mapOptional("SwiftBridge", t.SwiftBridge);
+ io.mapOptional("NSErrorDomain", t.NSErrorDomain);
+ io.mapOptional("EnumExtensibility", t.EnumExtensibility);
+ io.mapOptional("FlagEnum", t.FlagEnum);
+ io.mapOptional("EnumKind", t.EnumConvenienceKind);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Typedef> {
+ static void mapping(IO &io, Typedef& t) {
+ io.mapRequired("Name", t.Name);
+ io.mapOptional("Availability", t.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", t.Availability.Msg);
+ io.mapOptional("SwiftPrivate", t.SwiftPrivate);
+ io.mapOptional("SwiftName", t.SwiftName);
+ io.mapOptional("SwiftBridge", t.SwiftBridge);
+ io.mapOptional("NSErrorDomain", t.NSErrorDomain);
+ io.mapOptional("SwiftWrapper", t.SwiftWrapper);
+ }
+ };
+
+ static void mapTopLevelItems(IO &io, TopLevelItems &i) {
+ io.mapOptional("Classes", i.Classes);
+ io.mapOptional("Protocols", i.Protocols);
+ io.mapOptional("Functions", i.Functions);
+ io.mapOptional("Globals", i.Globals);
+ io.mapOptional("Enumerators", i.EnumConstants);
+ io.mapOptional("Tags", i.Tags);
+ io.mapOptional("Typedefs", i.Typedefs);
+ }
+
+ template <>
+ struct MappingTraits<Versioned> {
+ static void mapping(IO &io, Versioned& v) {
+ io.mapRequired("Version", v.Version);
+ mapTopLevelItems(io, v.Items);
+ }
+ };
+
+ template <>
+ struct MappingTraits<Module> {
+ static void mapping(IO &io, Module& m) {
+ io.mapRequired("Name", m.Name);
+ io.mapOptional("Availability", m.Availability.Mode);
+ io.mapOptional("AvailabilityMsg", m.Availability.Msg);
+ io.mapOptional("SwiftInferImportAsMember", m.SwiftInferImportAsMember);
+
+ mapTopLevelItems(io, m.TopLevel);
+
+ io.mapOptional("SwiftVersions", m.SwiftVersions);
+ }
+ };
+ }
+}
+
+using llvm::yaml::Input;
+using llvm::yaml::Output;
+
+void Module::dump() {
+ Output yout(llvm::errs());
+ yout << *this;
+}
+
+static bool parseAPINotes(StringRef yamlInput, Module &module,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) {
+ Input yin(yamlInput, nullptr, diagHandler, diagHandlerCtxt);
+ yin >> module;
+
+ return static_cast<bool>(yin.error());
+}
+
+namespace {
+ using namespace api_notes;
+
+ class YAMLConverter {
+ const Module &TheModule;
+ const FileEntry *SourceFile;
+ APINotesWriter *Writer;
+ OSType TargetOS;
+ llvm::raw_ostream &OS;
+ llvm::SourceMgr::DiagHandlerTy DiagHandler;
+ void *DiagHandlerCtxt;
+ bool ErrorOccured;
+
+ /// Emit a diagnostic
+ bool emitError(llvm::Twine message) {
+ DiagHandler(llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error,
+ message.str()),
+ DiagHandlerCtxt);
+ ErrorOccured = true;
+ return true;
+ }
+
+ public:
+ YAMLConverter(const Module &module,
+ const FileEntry *sourceFile,
+ OSType targetOS,
+ llvm::raw_ostream &os,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) :
+ TheModule(module), SourceFile(sourceFile), Writer(0), TargetOS(targetOS), OS(os),
+ DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt),
+ ErrorOccured(false) {}
+
+ bool isAvailable(const AvailabilityItem &in) {
+ // Check if the API is available on the OS for which we are building.
+ if (in.Mode == APIAvailability::OSX && TargetOS != OSType::OSX)
+ return false;
+ if (in.Mode == APIAvailability::IOS && TargetOS != OSType::IOS)
+ return false;
+ return true;
+ }
+
+ bool convertAvailability(const AvailabilityItem &in,
+ CommonEntityInfo &outInfo,
+ llvm::StringRef apiName) {
+ // Populate the unavailability information.
+ outInfo.Unavailable = (in.Mode == APIAvailability::None);
+ outInfo.UnavailableInSwift = (in.Mode == APIAvailability::NonSwift);
+ if (outInfo.Unavailable || outInfo.UnavailableInSwift) {
+ outInfo.UnavailableMsg = in.Msg;
+ } else {
+ if (!in.Msg.empty()) {
+ emitError("availability message for available API '" +
+ apiName + "' will not be used");
+ }
+ }
+ return false;
+ }
+
+ void convertParams(const ParamsSeq ¶ms, FunctionInfo &outInfo) {
+ for (const auto &p : params) {
+ ParamInfo pi;
+ if (p.Nullability)
+ pi.setNullabilityAudited(*p.Nullability);
+ pi.setNoEscape(p.NoEscape);
+ pi.setType(p.Type);
+ while (outInfo.Params.size() <= p.Position) {
+ outInfo.Params.push_back(ParamInfo());
+ }
+ outInfo.Params[p.Position] |= pi;
+ }
+ }
+
+ void convertNullability(const NullabilitySeq &nullability,
+ Optional<NullabilityKind> nullabilityOfRet,
+ FunctionInfo &outInfo,
+ llvm::StringRef apiName) {
+ if (nullability.size() > FunctionInfo::getMaxNullabilityIndex()) {
+ emitError("nullability info for " + apiName + " does not fit");
+ return;
+ }
+
+ bool audited = false;
+ unsigned int idx = 1;
+ for (auto i = nullability.begin(),
+ e = nullability.end(); i != e; ++i, ++idx){
+ outInfo.addTypeInfo(idx, *i);
+ audited = true;
+ }
+ if (nullabilityOfRet) {
+ outInfo.addTypeInfo(0, *nullabilityOfRet);
+ audited = true;
+ } else if (audited) {
+ outInfo.addTypeInfo(0, *DefaultNullability);
+ }
+ if (audited) {
+ outInfo.NullabilityAudited = audited;
+ outInfo.NumAdjustedNullable = idx;
+ }
+ }
+
+ /// Convert the common parts of an entity from YAML.
+ template<typename T>
+ bool convertCommon(const T& common, CommonEntityInfo &info,
+ StringRef apiName) {
+ if (!isAvailable(common.Availability))
+ return true;
+
+ convertAvailability(common.Availability, info, apiName);
+ info.setSwiftPrivate(common.SwiftPrivate);
+ info.SwiftName = common.SwiftName;
+ return false;
+ }
+
+ /// Convert the common parts of a type entity from YAML.
+ template<typename T>
+ bool convertCommonType(const T& common, CommonTypeInfo &info,
+ StringRef apiName) {
+ if (convertCommon(common, info, apiName))
+ return true;
+
+ info.setSwiftBridge(common.SwiftBridge);
+ info.setNSErrorDomain(common.NSErrorDomain);
+ return false;
+ }
+
+ // Translate from Method into ObjCMethodInfo and write it out.
+ void convertMethod(const Method &meth,
+ ContextID classID, StringRef className,
+ VersionTuple swiftVersion) {
+ ObjCMethodInfo mInfo;
+
+ if (convertCommon(meth, mInfo, meth.Selector))
+ return;
+
+ // Check if the selector ends with ':' to determine if it takes arguments.
+ bool takesArguments = meth.Selector.endswith(":");
+
+ // Split the selector into pieces.
+ llvm::SmallVector<StringRef, 4> a;
+ meth.Selector.split(a, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+ if (!takesArguments && a.size() > 1 ) {
+ emitError("selector " + meth.Selector + "is missing a ':' at the end");
+ return;
+ }
+
+ // Construct ObjCSelectorRef.
+ api_notes::ObjCSelectorRef selectorRef;
+ selectorRef.NumPieces = !takesArguments ? 0 : a.size();
+ selectorRef.Identifiers = a;
+
+ // Translate the initializer info.
+ mInfo.DesignatedInit = meth.DesignatedInit;
+ mInfo.Required = meth.Required;
+ if (meth.FactoryAsInit != FactoryAsInitKind::Infer) {
+ emitError("'FactoryAsInit' is no longer valid; "
+ "use 'SwiftName' instead");
+ }
+ mInfo.ResultType = meth.ResultType;
+
+ // Translate parameter information.
+ convertParams(meth.Params, mInfo);
+
+ // Translate nullability info.
+ convertNullability(meth.Nullability, meth.NullabilityOfRet,
+ mInfo, meth.Selector);
+
+ // Write it.
+ Writer->addObjCMethod(classID, selectorRef,
+ meth.Kind == MethodKind::Instance,
+ mInfo, swiftVersion);
+ }
+
+ void convertContext(const Class &cl, bool isClass,
+ VersionTuple swiftVersion) {
+ // Write the class.
+ ObjCContextInfo cInfo;
+
+ if (convertCommonType(cl, cInfo, cl.Name))
+ return;
+
+ if (cl.AuditedForNullability)
+ cInfo.setDefaultNullability(*DefaultNullability);
+ if (cl.SwiftImportAsNonGeneric)
+ cInfo.setSwiftImportAsNonGeneric(*cl.SwiftImportAsNonGeneric);
+ if (cl.SwiftObjCMembers)
+ cInfo.setSwiftObjCMembers(*cl.SwiftObjCMembers);
+
+ ContextID clID = Writer->addObjCContext(cl.Name, isClass, cInfo,
+ swiftVersion);
+
+ // Write all methods.
+ llvm::StringMap<std::pair<bool, bool>> knownMethods;
+ for (const auto &method : cl.Methods) {
+ // Check for duplicate method definitions.
+ bool isInstanceMethod = method.Kind == MethodKind::Instance;
+ bool &known = isInstanceMethod ? knownMethods[method.Selector].first
+ : knownMethods[method.Selector].second;
+ if (known) {
+ emitError(llvm::Twine("duplicate definition of method '") +
+ (isInstanceMethod? "-" : "+") + "[" + cl.Name + " " +
+ method.Selector + "]'");
+ continue;
+ }
+ known = true;
+
+ convertMethod(method, clID, cl.Name, swiftVersion);
+ }
+
+ // Write all properties.
+ llvm::StringSet<> knownInstanceProperties;
+ llvm::StringSet<> knownClassProperties;
+ for (const auto &prop : cl.Properties) {
+ // Check for duplicate property definitions.
+ if ((!prop.Kind || *prop.Kind == MethodKind::Instance) &&
+ !knownInstanceProperties.insert(prop.Name).second) {
+ emitError("duplicate definition of instance property '" + cl.Name +
+ "." + prop.Name + "'");
+ continue;
+ }
+
+ if ((!prop.Kind || *prop.Kind == MethodKind::Class) &&
+ !knownClassProperties.insert(prop.Name).second) {
+ emitError("duplicate definition of class property '" + cl.Name + "." +
+ prop.Name + "'");
+ continue;
+ }
+
+ // Translate from Property into ObjCPropertyInfo.
+ ObjCPropertyInfo pInfo;
+ if (!isAvailable(prop.Availability))
+ continue;
+ convertAvailability(prop.Availability, pInfo, prop.Name);
+ pInfo.setSwiftPrivate(prop.SwiftPrivate);
+ pInfo.SwiftName = prop.SwiftName;
+ if (prop.Nullability)
+ pInfo.setNullabilityAudited(*prop.Nullability);
+ if (prop.SwiftImportAsAccessors)
+ pInfo.setSwiftImportAsAccessors(*prop.SwiftImportAsAccessors);
+ pInfo.setType(prop.Type);
+ if (prop.Kind) {
+ Writer->addObjCProperty(clID, prop.Name,
+ *prop.Kind == MethodKind::Instance, pInfo,
+ swiftVersion);
+ } else {
+ // Add both instance and class properties with this name.
+ Writer->addObjCProperty(clID, prop.Name, true, pInfo, swiftVersion);
+ Writer->addObjCProperty(clID, prop.Name, false, pInfo, swiftVersion);
+ }
+ }
+ }
+
+ void convertTopLevelItems(const TopLevelItems &items,
+ VersionTuple swiftVersion) {
+ // Write all classes.
+ llvm::StringSet<> knownClasses;
+ for (const auto &cl : items.Classes) {
+ // Check for duplicate class definitions.
+ if (!knownClasses.insert(cl.Name).second) {
+ emitError("multiple definitions of class '" + cl.Name + "'");
+ continue;
+ }
+
+ convertContext(cl, /*isClass*/ true, swiftVersion);
+ }
+
+ // Write all protocols.
+ llvm::StringSet<> knownProtocols;
+ for (const auto &pr : items.Protocols) {
+ // Check for duplicate protocol definitions.
+ if (!knownProtocols.insert(pr.Name).second) {
+ emitError("multiple definitions of protocol '" + pr.Name + "'");
+ continue;
+ }
+
+ convertContext(pr, /*isClass*/ false, swiftVersion);
+ }
+
+ // Write all global variables.
+ llvm::StringSet<> knownGlobals;
+ for (const auto &global : items.Globals) {
+ // Check for duplicate global variables.
+ if (!knownGlobals.insert(global.Name).second) {
+ emitError("multiple definitions of global variable '" +
+ global.Name + "'");
+ continue;
+ }
+
+ GlobalVariableInfo info;
+ if (!isAvailable(global.Availability))
+ continue;
+ convertAvailability(global.Availability, info, global.Name);
+ info.setSwiftPrivate(global.SwiftPrivate);
+ info.SwiftName = global.SwiftName;
+ if (global.Nullability)
+ info.setNullabilityAudited(*global.Nullability);
+ info.setType(global.Type);
+ Writer->addGlobalVariable(global.Name, info, swiftVersion);
+ }
+
+ // Write all global functions.
+ llvm::StringSet<> knownFunctions;
+ for (const auto &function : items.Functions) {
+ // Check for duplicate global functions.
+ if (!knownFunctions.insert(function.Name).second) {
+ emitError("multiple definitions of global function '" +
+ function.Name + "'");
+ continue;
+ }
+
+ GlobalFunctionInfo info;
+ if (!isAvailable(function.Availability))
+ continue;
+ convertAvailability(function.Availability, info, function.Name);
+ info.setSwiftPrivate(function.SwiftPrivate);
+ info.SwiftName = function.SwiftName;
+ convertParams(function.Params, info);
+ convertNullability(function.Nullability,
+ function.NullabilityOfRet,
+ info, function.Name);
+ info.ResultType = function.ResultType;
+ Writer->addGlobalFunction(function.Name, info, swiftVersion);
+ }
+
+ // Write all enumerators.
+ llvm::StringSet<> knownEnumConstants;
+ for (const auto &enumConstant : items.EnumConstants) {
+ // Check for duplicate enumerators
+ if (!knownEnumConstants.insert(enumConstant.Name).second) {
+ emitError("multiple definitions of enumerator '" +
+ enumConstant.Name + "'");
+ continue;
+ }
+
+ EnumConstantInfo info;
+ if (!isAvailable(enumConstant.Availability))
+ continue;
+ convertAvailability(enumConstant.Availability, info, enumConstant.Name);
+ info.setSwiftPrivate(enumConstant.SwiftPrivate);
+ info.SwiftName = enumConstant.SwiftName;
+ Writer->addEnumConstant(enumConstant.Name, info, swiftVersion);
+ }
+
+ // Write all tags.
+ llvm::StringSet<> knownTags;
+ for (const auto &t : items.Tags) {
+ // Check for duplicate tag definitions.
+ if (!knownTags.insert(t.Name).second) {
+ emitError("multiple definitions Of tag '" + t.Name + "'");
+ continue;
+ }
+
+ TagInfo tagInfo;
+ if (convertCommonType(t, tagInfo, t.Name))
+ continue;
+
+ if (t.EnumConvenienceKind) {
+ if (t.EnumExtensibility) {
+ emitError(llvm::Twine(
+ "cannot mix EnumKind and EnumExtensibility (for ") + t.Name +
+ ")");
+ continue;
+ }
+ if (t.FlagEnum) {
+ emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") +
+ t.Name + ")");
+ continue;
+ }
+ switch (t.EnumConvenienceKind.getValue()) {
+ case EnumConvenienceAliasKind::None:
+ tagInfo.EnumExtensibility = EnumExtensibilityKind::None;
+ tagInfo.setFlagEnum(false);
+ break;
+ case EnumConvenienceAliasKind::CFEnum:
+ tagInfo.EnumExtensibility = EnumExtensibilityKind::Open;
+ tagInfo.setFlagEnum(false);
+ break;
+ case EnumConvenienceAliasKind::CFOptions:
+ tagInfo.EnumExtensibility = EnumExtensibilityKind::Open;
+ tagInfo.setFlagEnum(true);
+ break;
+ case EnumConvenienceAliasKind::CFClosedEnum:
+ tagInfo.EnumExtensibility = EnumExtensibilityKind::Closed;
+ tagInfo.setFlagEnum(false);
+ break;
+ }
+ } else {
+ tagInfo.EnumExtensibility = t.EnumExtensibility;
+ tagInfo.setFlagEnum(t.FlagEnum);
+ }
+
+ Writer->addTag(t.Name, tagInfo, swiftVersion);
+ }
+
+ // Write all typedefs.
+ llvm::StringSet<> knownTypedefs;
+ for (const auto &t : items.Typedefs) {
+ // Check for duplicate typedef definitions.
+ if (!knownTypedefs.insert(t.Name).second) {
+ emitError("multiple definitions of typedef '" + t.Name + "'");
+ continue;
+ }
+
+ TypedefInfo typedefInfo;
+ if (convertCommonType(t, typedefInfo, t.Name))
+ continue;
+ typedefInfo.SwiftWrapper = t.SwiftWrapper;
+
+ Writer->addTypedef(t.Name, typedefInfo, swiftVersion);
+ }
+ }
+
+ bool convertModule() {
+ if (!isAvailable(TheModule.Availability))
+ return false;
+
+ // Set up the writer.
+ // FIXME: This is kindof ugly.
+ APINotesWriter writer(TheModule.Name, SourceFile);
+ Writer = &writer;
+
+ // Write the top-level items.
+ convertTopLevelItems(TheModule.TopLevel, VersionTuple());
+
+ if (TheModule.SwiftInferImportAsMember) {
+ ModuleOptions opts;
+ opts.SwiftInferImportAsMember = true;
+ Writer->addModuleOptions(opts);
+ }
+
+ // Convert the versioned information.
+ for (const auto &versioned : TheModule.SwiftVersions) {
+ convertTopLevelItems(versioned.Items, versioned.Version);
+ }
+
+ if (!ErrorOccured)
+ Writer->writeToStream(OS);
+
+ return ErrorOccured;
+ }
+ };
+}
+
+static bool compile(const Module &module,
+ const FileEntry *sourceFile,
+ llvm::raw_ostream &os,
+ api_notes::OSType targetOS,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt){
+ using namespace api_notes;
+
+ YAMLConverter c(module, sourceFile, targetOS, os, diagHandler, diagHandlerCtxt);
+ return c.convertModule();
+}
+
+bool api_notes::parseAndDumpAPINotes(StringRef yamlInput) {
+ Module module;
+
+ if (parseAPINotes(yamlInput, module, nullptr, nullptr))
+ return true;
+
+ Output yout(llvm::outs());
+ yout << module;
+
+ return false;
+}
+
+/// Simple diagnostic handler that prints diagnostics to standard error.
+static void printDiagnostic(const llvm::SMDiagnostic &diag, void *context) {
+ diag.print(nullptr, llvm::errs());
+}
+
+bool api_notes::compileAPINotes(StringRef yamlInput,
+ const FileEntry *sourceFile,
+ llvm::raw_ostream &os,
+ OSType targetOS,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) {
+ Module module;
+
+ if (!diagHandler) {
+ diagHandler = &printDiagnostic;
+ }
+
+ if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt))
+ return true;
+
+ return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt);
+}
+
+namespace {
+ // Deserialize the API notes file into a module.
+ class DecompileVisitor : public APINotesReader::Visitor {
+ /// Allocator used to clone those strings that need it.
+ llvm::BumpPtrAllocator Allocator;
+
+ /// The module we're building.
+ Module TheModule;
+
+ /// A known context, which tracks what we know about a context ID.
+ struct KnownContext {
+ /// Whether this is a protocol (vs. a class).
+ bool isProtocol;
+
+ /// The indices into the top-level items for this context at each
+ /// Swift version.
+ SmallVector<std::pair<VersionTuple, unsigned>, 1> indices;
+
+ Class &getContext(const VersionTuple &swiftVersion,
+ TopLevelItems &items) {
+ ClassesSeq &seq = isProtocol ? items.Protocols : items.Classes;
+
+ for (auto &index : indices) {
+ if (index.first == swiftVersion)
+ return seq[index.second];
+ }
+
+ indices.push_back({swiftVersion, seq.size()});
+ seq.push_back(Class());
+ return seq.back();
+ }
+ };
+
+ /// A mapping from context ID to a pair (index, is-protocol) that indicates
+ /// the index of that class or protocol in the global "classes" or
+ /// "protocols" list.
+ llvm::DenseMap<unsigned, KnownContext> knownContexts;
+
+ /// Copy a string into allocated memory so it does disappear on us.
+ StringRef copyString(StringRef string) {
+ if (string.empty()) return StringRef();
+
+ void *ptr = Allocator.Allocate(string.size(), 1);
+ memcpy(ptr, string.data(), string.size());
+ return StringRef(reinterpret_cast<const char *>(ptr), string.size());
+ }
+
+ /// Copy an optional string into allocated memory so it does disappear on us.
+ Optional<StringRef> maybeCopyString(Optional<StringRef> string) {
+ if (!string) return None;
+
+ return copyString(*string);
+ }
+
+ /// Copy an optional string into allocated memory so it does disappear on us.
+ Optional<StringRef> maybeCopyString(Optional<std::string> string) {
+ if (!string) return None;
+
+ return copyString(*string);
+ }
+
+ template<typename T>
+ void handleCommon(T &record, const CommonEntityInfo &info) {
+ handleAvailability(record.Availability, info);
+ record.SwiftPrivate = info.isSwiftPrivate();
+ record.SwiftName = copyString(info.SwiftName);
+ }
+
+ template<typename T>
+ void handleCommonType(T &record, const CommonTypeInfo &info) {
+ handleCommon(record, info);
+ record.SwiftBridge = maybeCopyString(info.getSwiftBridge());
+ record.NSErrorDomain = maybeCopyString(info.getNSErrorDomain());
+ }
+
+ /// Map Objective-C context info.
+ void handleObjCContext(Class &record, StringRef name,
+ const ObjCContextInfo &info) {
+ record.Name = name;
+
+ handleCommonType(record, info);
+ record.SwiftImportAsNonGeneric = info.getSwiftImportAsNonGeneric();
+ record.SwiftObjCMembers = info.getSwiftObjCMembers();
+
+ if (info.getDefaultNullability()) {
+ record.AuditedForNullability = true;
+ }
+ }
+
+ /// Map availability information, if present.
+ void handleAvailability(AvailabilityItem &availability,
+ const CommonEntityInfo &info) {
+ if (info.Unavailable) {
+ availability.Mode = APIAvailability::None;
+ availability.Msg = copyString(info.UnavailableMsg);
+ }
+
+ if (info.UnavailableInSwift) {
+ availability.Mode = APIAvailability::NonSwift;
+ availability.Msg = copyString(info.UnavailableMsg);
+ }
+ }
+
+ /// Map parameter information for a function.
+ void handleParameters(ParamsSeq ¶ms,
+ const FunctionInfo &info) {
+ unsigned position = 0;
+ for (const auto &pi: info.Params) {
+ Param p;
+ p.Position = position++;
+ p.Nullability = pi.getNullability();
+ p.NoEscape = pi.isNoEscape();
+ p.Type = copyString(pi.getType());
+ params.push_back(p);
+ }
+ }
+
+ /// Map nullability information for a function.
+ void handleNullability(NullabilitySeq &nullability,
+ llvm::Optional<NullabilityKind> &nullabilityOfRet,
+ const FunctionInfo &info,
+ unsigned numParams) {
+ if (info.NullabilityAudited) {
+ nullabilityOfRet = info.getReturnTypeInfo();
+
+ // Figure out the number of parameters from the selector.
+ for (unsigned i = 0; i != numParams; ++i)
+ nullability.push_back(info.getParamTypeInfo(i));
+ }
+ }
+
+ TopLevelItems &getTopLevelItems(VersionTuple swiftVersion) {
+ if (!swiftVersion) return TheModule.TopLevel;
+
+ for (auto &versioned : TheModule.SwiftVersions) {
+ if (versioned.Version == swiftVersion)
+ return versioned.Items;
+ }
+
+ TheModule.SwiftVersions.push_back(Versioned());
+ TheModule.SwiftVersions.back().Version = swiftVersion;
+ return TheModule.SwiftVersions.back().Items;
+ }
+
+ public:
+ virtual void visitObjCClass(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) {
+ // Record this known context.
+ auto &items = getTopLevelItems(swiftVersion);
+ auto &known = knownContexts[contextID.Value];
+ known.isProtocol = false;
+
+ handleObjCContext(known.getContext(swiftVersion, items), name, info);
+ }
+
+ virtual void visitObjCProtocol(ContextID contextID, StringRef name,
+ const ObjCContextInfo &info,
+ VersionTuple swiftVersion) {
+ // Record this known context.
+ auto &items = getTopLevelItems(swiftVersion);
+ auto &known = knownContexts[contextID.Value];
+ known.isProtocol = true;
+
+ handleObjCContext(known.getContext(swiftVersion, items), name, info);
+ }
+
+ virtual void visitObjCMethod(ContextID contextID, StringRef selector,
+ bool isInstanceMethod,
+ const ObjCMethodInfo &info,
+ VersionTuple swiftVersion) {
+ Method method;
+ method.Selector = copyString(selector);
+ method.Kind = isInstanceMethod ? MethodKind::Instance : MethodKind::Class;
+
+ handleCommon(method, info);
+ handleParameters(method.Params, info);
+ handleNullability(method.Nullability, method.NullabilityOfRet, info,
+ selector.count(':'));
+ method.DesignatedInit = info.DesignatedInit;
+ method.Required = info.Required;
+ method.ResultType = copyString(info.ResultType);
+ auto &items = getTopLevelItems(swiftVersion);
+ knownContexts[contextID.Value].getContext(swiftVersion, items)
+ .Methods.push_back(method);
+ }
+
+ virtual void visitObjCProperty(ContextID contextID, StringRef name,
+ bool isInstance,
+ const ObjCPropertyInfo &info,
+ VersionTuple swiftVersion) {
+ Property property;
+ property.Name = name;
+ property.Kind = isInstance ? MethodKind::Instance : MethodKind::Class;
+ handleCommon(property, info);
+
+ // FIXME: No way to represent "not audited for nullability".
+ if (auto nullability = info.getNullability()) {
+ property.Nullability = *nullability;
+ }
+
+ property.SwiftImportAsAccessors = info.getSwiftImportAsAccessors();
+
+ property.Type = copyString(info.getType());
+
+ auto &items = getTopLevelItems(swiftVersion);
+ knownContexts[contextID.Value].getContext(swiftVersion, items)
+ .Properties.push_back(property);
+ }
+
+ virtual void visitGlobalFunction(StringRef name,
+ const GlobalFunctionInfo &info,
+ VersionTuple swiftVersion) {
+ Function function;
+ function.Name = name;
+ handleCommon(function, info);
+ handleParameters(function.Params, info);
+ if (info.NumAdjustedNullable > 0)
+ handleNullability(function.Nullability, function.NullabilityOfRet,
+ info, info.NumAdjustedNullable-1);
+ function.ResultType = copyString(info.ResultType);
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Functions.push_back(function);
+ }
+
+ virtual void visitGlobalVariable(StringRef name,
+ const GlobalVariableInfo &info,
+ VersionTuple swiftVersion) {
+ GlobalVariable global;
+ global.Name = name;
+ handleCommon(global, info);
+
+ // FIXME: No way to represent "not audited for nullability".
+ if (auto nullability = info.getNullability()) {
+ global.Nullability = *nullability;
+ }
+ global.Type = copyString(info.getType());
+
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Globals.push_back(global);
+ }
+
+ virtual void visitEnumConstant(StringRef name,
+ const EnumConstantInfo &info,
+ VersionTuple swiftVersion) {
+ EnumConstant enumConstant;
+ enumConstant.Name = name;
+ handleCommon(enumConstant, info);
+
+ auto &items = getTopLevelItems(swiftVersion);
+ items.EnumConstants.push_back(enumConstant);
+ }
+
+ virtual void visitTag(StringRef name, const TagInfo &info,
+ VersionTuple swiftVersion) {
+ Tag tag;
+ tag.Name = name;
+ handleCommonType(tag, info);
+ tag.EnumExtensibility = info.EnumExtensibility;
+ tag.FlagEnum = info.isFlagEnum();
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Tags.push_back(tag);
+ }
+
+ virtual void visitTypedef(StringRef name, const TypedefInfo &info,
+ VersionTuple swiftVersion) {
+ Typedef td;
+ td.Name = name;
+ handleCommonType(td, info);
+ td.SwiftWrapper = info.SwiftWrapper;
+ auto &items = getTopLevelItems(swiftVersion);
+ items.Typedefs.push_back(td);
+ }
+
+ /// Retrieve the module.
+ Module &getModule() { return TheModule; }
+ };
+}
+
+/// Produce a flattened, numeric value for optional method/property kinds.
+static unsigned flattenPropertyKind(llvm::Optional<MethodKind> kind) {
+ return kind ? (*kind == MethodKind::Instance ? 2 : 1) : 0;
+}
+
+/// Sort the items in the given block of "top-level" items.
+static void sortTopLevelItems(TopLevelItems &items) {
+ // Sort classes.
+ std::sort(items.Classes.begin(), items.Classes.end(),
+ [](const Class &lhs, const Class &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort protocols.
+ std::sort(items.Protocols.begin(), items.Protocols.end(),
+ [](const Class &lhs, const Class &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort methods and properties within each class and protocol.
+ auto sortMembers = [](Class &record) {
+ // Sort properties.
+ std::sort(record.Properties.begin(), record.Properties.end(),
+ [](const Property &lhs, const Property &rhs) -> bool {
+ return lhs.Name < rhs.Name ||
+ (lhs.Name == rhs.Name &&
+ flattenPropertyKind(lhs.Kind) <
+ flattenPropertyKind(rhs.Kind));
+ });
+
+ // Sort methods.
+ std::sort(record.Methods.begin(), record.Methods.end(),
+ [](const Method &lhs, const Method &rhs) -> bool {
+ return lhs.Selector < rhs.Selector ||
+ (lhs.Selector == rhs.Selector &&
+ static_cast<unsigned>(lhs.Kind)
+ < static_cast<unsigned>(rhs.Kind));
+ });
+ };
+ std::for_each(items.Classes.begin(), items.Classes.end(), sortMembers);
+ std::for_each(items.Protocols.begin(), items.Protocols.end(), sortMembers);
+
+ // Sort functions.
+ std::sort(items.Functions.begin(), items.Functions.end(),
+ [](const Function &lhs, const Function &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort global variables.
+ std::sort(items.Globals.begin(), items.Globals.end(),
+ [](const GlobalVariable &lhs, const GlobalVariable &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort enum constants.
+ std::sort(items.EnumConstants.begin(), items.EnumConstants.end(),
+ [](const EnumConstant &lhs, const EnumConstant &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort tags.
+ std::sort(items.Tags.begin(), items.Tags.end(),
+ [](const Tag &lhs, const Tag &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+
+ // Sort typedefs.
+ std::sort(items.Typedefs.begin(), items.Typedefs.end(),
+ [](const Typedef &lhs, const Typedef &rhs) -> bool {
+ return lhs.Name < rhs.Name;
+ });
+}
+
+bool api_notes::decompileAPINotes(std::unique_ptr<llvm::MemoryBuffer> input,
+ llvm::raw_ostream &os) {
+ // Try to read the file.
+ auto reader = APINotesReader::get(std::move(input), VersionTuple());
+ if (!reader) {
+ llvm::errs() << "not a well-formed API notes binary file\n";
+ return true;
+ }
+
+ DecompileVisitor decompileVisitor;
+ reader->visit(decompileVisitor);
+
+ // Sort the data in the module, because the API notes reader doesn't preserve
+ // order.
+ auto &module = decompileVisitor.getModule();
+
+ // Set module name.
+ module.Name = reader->getModuleName();
+
+ // Set module options
+ auto opts = reader->getModuleOptions();
+ if (opts.SwiftInferImportAsMember)
+ module.SwiftInferImportAsMember = true;
+
+ // Sort the top-level items.
+ sortTopLevelItems(module.TopLevel);
+
+ // Sort the Swift versions.
+ std::sort(module.SwiftVersions.begin(), module.SwiftVersions.end(),
+ [](const Versioned &lhs, const Versioned &rhs) -> bool {
+ return lhs.Version < rhs.Version;
+ });
+
+ // Sort the top-level items within each Swift version.
+ for (auto &versioned : module.SwiftVersions)
+ sortTopLevelItems(versioned.Items);
+
+ // Output the YAML representation.
+ Output yout(os);
+ yout << module;
+
+ return false;
+}
+
diff --git a/lib/APINotes/CMakeLists.txt b/lib/APINotes/CMakeLists.txt
new file mode 100644
index 0000000..da9d0d1
--- /dev/null
+++ b/lib/APINotes/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+ BitReader
+ Support
+ )
+
+add_clang_library(clangAPINotes
+ APINotesManager.cpp
+ APINotesWriter.cpp
+ APINotesReader.cpp
+ APINotesYAMLCompiler.cpp
+ Types.cpp
+
+ LINK_LIBS
+ clangBasic
+)
diff --git a/lib/APINotes/Types.cpp b/lib/APINotes/Types.cpp
new file mode 100644
index 0000000..4bbb4a8
--- /dev/null
+++ b/lib/APINotes/Types.cpp
@@ -0,0 +1,55 @@
+//===--- Types.cpp - API Notes Data Types ----------------------*- 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 data types used in the representation of API notes data.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/Types.h"
+#include "llvm/Support/raw_ostream.h"
+
+void clang::api_notes::ObjCMethodInfo::dump(llvm::raw_ostream &os) {
+ os << DesignatedInit << " " << Unavailable << " "
+ << NullabilityAudited << " " << NumAdjustedNullable << " "
+ << NullabilityPayload << " " << UnavailableMsg << "\n";
+}
+
+void clang::api_notes::ObjCContextInfo::dump(llvm::raw_ostream &os) {
+ os << HasDefaultNullability << " " << DefaultNullability << " "
+ << HasDesignatedInits << "\n";
+}
+
+void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoSetter(
+ const ObjCPropertyInfo &pInfo) {
+ // Set the type of the first argument of the the setter or check that the
+ // value we have is consistent with the property.
+ // TODO: Can we provide proper error handling here?
+ if (auto pNullability = pInfo.getNullability()) {
+ if (!NullabilityAudited) {
+ addParamTypeInfo(0, *pNullability);
+ assert(NumAdjustedNullable == 2);
+ } else {
+ assert(getParamTypeInfo(0) == *pNullability);
+ }
+ }
+}
+
+void clang::api_notes::ObjCMethodInfo::mergePropInfoIntoGetter(
+ const ObjCPropertyInfo &pInfo) {
+ // Set the return type of the getter or check that the value we have is
+ // consistent with the property.
+ // TODO: Can we provide proper error handling here?
+ if (auto pNullability = pInfo.getNullability()) {
+ if (!NullabilityAudited) {
+ addReturnTypeInfo(*pNullability);
+ assert(NumAdjustedNullable == 1);
+ } else {
+ assert(getReturnTypeInfo() == *pNullability);
+ }
+ }
+}
diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp
index c60373c..a7d673c 100644
--- a/lib/AST/ASTContext.cpp
+++ b/lib/AST/ASTContext.cpp
@@ -4080,11 +4080,6 @@
bool allowOnPointerType) const {
hasError = false;
- if (const ObjCTypeParamType *objT =
- dyn_cast<ObjCTypeParamType>(type.getTypePtr())) {
- return getObjCTypeParamType(objT->getDecl(), protocols);
- }
-
// Apply protocol qualifiers to ObjCObjectPointerType.
if (allowOnPointerType) {
if (const ObjCObjectPointerType *objPtr =
diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp
index 92ed7da..57b9b09 100644
--- a/lib/AST/ASTDumper.cpp
+++ b/lib/AST/ASTDumper.cpp
@@ -1711,6 +1711,8 @@
void ASTDumper::VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *D) {
dumpName(D);
dumpDeclRef(D->getClassInterface());
+ OS << " ";
+ dumpLocation(D->getClassInterfaceLoc());
}
void ASTDumper::VisitObjCPropertyDecl(const ObjCPropertyDecl *D) {
diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp
index 2c0bb11..6e33b98 100644
--- a/lib/AST/ASTImporter.cpp
+++ b/lib/AST/ASTImporter.cpp
@@ -956,16 +956,12 @@
ToData.HasUninitializedFields = FromData.HasUninitializedFields;
ToData.HasInheritedConstructor = FromData.HasInheritedConstructor;
ToData.HasInheritedAssignment = FromData.HasInheritedAssignment;
- ToData.NeedOverloadResolutionForCopyConstructor
- = FromData.NeedOverloadResolutionForCopyConstructor;
ToData.NeedOverloadResolutionForMoveConstructor
= FromData.NeedOverloadResolutionForMoveConstructor;
ToData.NeedOverloadResolutionForMoveAssignment
= FromData.NeedOverloadResolutionForMoveAssignment;
ToData.NeedOverloadResolutionForDestructor
= FromData.NeedOverloadResolutionForDestructor;
- ToData.DefaultedCopyConstructorIsDeleted
- = FromData.DefaultedCopyConstructorIsDeleted;
ToData.DefaultedMoveConstructorIsDeleted
= FromData.DefaultedMoveConstructorIsDeleted;
ToData.DefaultedMoveAssignmentIsDeleted
@@ -977,7 +973,6 @@
= FromData.HasConstexprNonCopyMoveConstructor;
ToData.HasDefaultedDefaultConstructor
= FromData.HasDefaultedDefaultConstructor;
- ToData.CanPassInRegisters = FromData.CanPassInRegisters;
ToData.DefaultedDefaultConstructorIsConstexpr
= FromData.DefaultedDefaultConstructorIsConstexpr;
ToData.HasConstexprDefaultConstructor
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 5782b7b..1caceab 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -55,18 +55,15 @@
HasOnlyCMembers(true), HasInClassInitializer(false),
HasUninitializedReferenceMember(false), HasUninitializedFields(false),
HasInheritedConstructor(false), HasInheritedAssignment(false),
- NeedOverloadResolutionForCopyConstructor(false),
NeedOverloadResolutionForMoveConstructor(false),
NeedOverloadResolutionForMoveAssignment(false),
NeedOverloadResolutionForDestructor(false),
- DefaultedCopyConstructorIsDeleted(false),
DefaultedMoveConstructorIsDeleted(false),
DefaultedMoveAssignmentIsDeleted(false),
DefaultedDestructorIsDeleted(false), HasTrivialSpecialMembers(SMF_All),
DeclaredNonTrivialSpecialMembers(0), HasIrrelevantDestructor(true),
HasConstexprNonCopyMoveConstructor(false),
HasDefaultedDefaultConstructor(false),
- CanPassInRegisters(true),
DefaultedDefaultConstructorIsConstexpr(true),
HasConstexprDefaultConstructor(false),
HasNonLiteralTypeFieldsOrBases(false), ComputedVisibleConversions(false),
@@ -355,10 +352,8 @@
setHasVolatileMember(true);
// Keep track of the presence of mutable fields.
- if (BaseClassDecl->hasMutableFields()) {
+ if (BaseClassDecl->hasMutableFields())
data().HasMutableFields = true;
- data().NeedOverloadResolutionForCopyConstructor = true;
- }
if (BaseClassDecl->hasUninitializedReferenceMember())
data().HasUninitializedReferenceMember = true;
@@ -411,8 +406,6 @@
// -- a direct or virtual base class B that cannot be copied/moved [...]
// -- a non-static data member of class type M (or array thereof)
// that cannot be copied or moved [...]
- if (!Subobj->hasSimpleCopyConstructor())
- data().NeedOverloadResolutionForCopyConstructor = true;
if (!Subobj->hasSimpleMoveConstructor())
data().NeedOverloadResolutionForMoveConstructor = true;
@@ -433,7 +426,6 @@
// -- any non-static data member has a type with a destructor
// that is deleted or inaccessible from the defaulted [ctor or dtor].
if (!Subobj->hasSimpleDestructor()) {
- data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForDestructor = true;
}
@@ -719,10 +711,8 @@
data().IsStandardLayout = false;
// Keep track of the presence of mutable fields.
- if (Field->isMutable()) {
+ if (Field->isMutable())
data().HasMutableFields = true;
- data().NeedOverloadResolutionForCopyConstructor = true;
- }
// C++11 [class.union]p8, DR1460:
// If X is a union, a non-static data member of X that is not an anonymous
@@ -766,12 +756,6 @@
// A standard-layout class is a class that:
// -- has no non-static data members of type [...] reference,
data().IsStandardLayout = false;
-
- // C++1z [class.copy.ctor]p10:
- // A defaulted copy constructor for a class X is defined as deleted if X has:
- // -- a non-static data member of rvalue reference type
- if (T->isRValueReferenceType())
- data().DefaultedCopyConstructorIsDeleted = true;
}
if (!Field->hasInClassInitializer() && !Field->isMutable()) {
@@ -825,10 +809,6 @@
// We may need to perform overload resolution to determine whether a
// field can be moved if it's const or volatile qualified.
if (T.getCVRQualifiers() & (Qualifiers::Const | Qualifiers::Volatile)) {
- // We need to care about 'const' for the copy constructor because an
- // implicit copy constructor might be declared with a non-const
- // parameter.
- data().NeedOverloadResolutionForCopyConstructor = true;
data().NeedOverloadResolutionForMoveConstructor = true;
data().NeedOverloadResolutionForMoveAssignment = true;
}
@@ -839,8 +819,6 @@
// -- X is a union-like class that has a variant member with a
// non-trivial [corresponding special member]
if (isUnion()) {
- if (FieldRec->hasNonTrivialCopyConstructor())
- data().DefaultedCopyConstructorIsDeleted = true;
if (FieldRec->hasNonTrivialMoveConstructor())
data().DefaultedMoveConstructorIsDeleted = true;
if (FieldRec->hasNonTrivialMoveAssignment())
@@ -852,8 +830,6 @@
// For an anonymous union member, our overload resolution will perform
// overload resolution for its members.
if (Field->isAnonymousStructOrUnion()) {
- data().NeedOverloadResolutionForCopyConstructor |=
- FieldRec->data().NeedOverloadResolutionForCopyConstructor;
data().NeedOverloadResolutionForMoveConstructor |=
FieldRec->data().NeedOverloadResolutionForMoveConstructor;
data().NeedOverloadResolutionForMoveAssignment |=
@@ -939,10 +915,8 @@
}
// Keep track of the presence of mutable fields.
- if (FieldRec->hasMutableFields()) {
+ if (FieldRec->hasMutableFields())
data().HasMutableFields = true;
- data().NeedOverloadResolutionForCopyConstructor = true;
- }
// C++11 [class.copy]p13:
// If the implicitly-defined constructor would satisfy the
@@ -1476,7 +1450,7 @@
void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
RecordDecl::completeDefinition();
-
+
// If the class may be abstract (but hasn't been marked as such), check for
// any pure final overriders.
if (mayBeAbstract()) {
diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp
index d8bdb63..2fb95eb 100644
--- a/lib/AST/DeclObjC.cpp
+++ b/lib/AST/DeclObjC.cpp
@@ -1338,12 +1338,8 @@
IdentifierInfo *name,
SourceLocation colonLoc,
TypeSourceInfo *boundInfo) {
- auto *TPDecl =
- new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
- nameLoc, name, colonLoc, boundInfo);
- QualType TPType = ctx.getObjCTypeParamType(TPDecl, {});
- TPDecl->setTypeForDecl(TPType.getTypePtr());
- return TPDecl;
+ return new (ctx, dc) ObjCTypeParamDecl(ctx, dc, variance, varianceLoc, index,
+ nameLoc, name, colonLoc, boundInfo);
}
ObjCTypeParamDecl *ObjCTypeParamDecl::CreateDeserialized(ASTContext &ctx,
@@ -2157,18 +2153,19 @@
void ObjCCompatibleAliasDecl::anchor() { }
-ObjCCompatibleAliasDecl *
-ObjCCompatibleAliasDecl::Create(ASTContext &C, DeclContext *DC,
- SourceLocation L,
- IdentifierInfo *Id,
- ObjCInterfaceDecl* AliasedClass) {
- return new (C, DC) ObjCCompatibleAliasDecl(DC, L, Id, AliasedClass);
+ObjCCompatibleAliasDecl *ObjCCompatibleAliasDecl::Create(
+ ASTContext &C, DeclContext *DC, SourceLocation NameLoc, IdentifierInfo *Id,
+ ObjCInterfaceDecl *AliasedClass, SourceLocation AliasedClassLoc,
+ SourceLocation AtLoc) {
+ return new (C, DC) ObjCCompatibleAliasDecl(DC, NameLoc, Id, AliasedClass,
+ AliasedClassLoc, AtLoc);
}
ObjCCompatibleAliasDecl *
ObjCCompatibleAliasDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
- return new (C, ID) ObjCCompatibleAliasDecl(nullptr, SourceLocation(),
- nullptr, nullptr);
+ return new (C, ID)
+ ObjCCompatibleAliasDecl(nullptr, SourceLocation(), nullptr, nullptr,
+ SourceLocation(), SourceLocation());
}
//===----------------------------------------------------------------------===//
diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp
index 6eeba88..7b7a8b9 100644
--- a/lib/AST/DeclPrinter.cpp
+++ b/lib/AST/DeclPrinter.cpp
@@ -28,6 +28,7 @@
class DeclPrinter : public DeclVisitor<DeclPrinter> {
raw_ostream &Out;
PrintingPolicy Policy;
+ const ASTContext &Context;
unsigned Indentation;
bool PrintInstantiation;
@@ -48,9 +49,10 @@
public:
DeclPrinter(raw_ostream &Out, const PrintingPolicy &Policy,
- unsigned Indentation = 0, bool PrintInstantiation = false)
- : Out(Out), Policy(Policy), Indentation(Indentation),
- PrintInstantiation(PrintInstantiation) { }
+ const ASTContext &Context, unsigned Indentation = 0,
+ bool PrintInstantiation = false)
+ : Out(Out), Policy(Policy), Context(Context), Indentation(Indentation),
+ PrintInstantiation(PrintInstantiation) {}
void VisitDeclContext(DeclContext *DC, bool Indent = true);
@@ -115,10 +117,19 @@
void Decl::print(raw_ostream &Out, const PrintingPolicy &Policy,
unsigned Indentation, bool PrintInstantiation) const {
- DeclPrinter Printer(Out, Policy, Indentation, PrintInstantiation);
+ DeclPrinter Printer(Out, Policy, getASTContext(), Indentation,
+ PrintInstantiation);
Printer.Visit(const_cast<Decl*>(this));
}
+void TemplateParameterList::print(raw_ostream &Out,
+ const PrintingPolicy &Policy,
+ const ASTContext &Context,
+ unsigned Indentation) const {
+ DeclPrinter Printer(Out, Policy, Context, Indentation);
+ Printer.printTemplateParameters(this);
+}
+
static QualType GetBaseType(QualType T) {
// FIXME: This should be on the Type class!
QualType BaseType = T;
@@ -192,7 +203,7 @@
DC = DC->getParent();
ASTContext &Ctx = cast<TranslationUnitDecl>(DC)->getASTContext();
- DeclPrinter Printer(llvm::errs(), Ctx.getPrintingPolicy(), 0);
+ DeclPrinter Printer(llvm::errs(), Ctx.getPrintingPolicy(), Ctx, 0);
Printer.VisitDeclContext(const_cast<DeclContext *>(this), /*Indent=*/false);
}
@@ -467,7 +478,7 @@
prettyPrintAttributes(D);
if (Expr *Init = D->getInitExpr()) {
Out << " = ";
- Init->printPretty(Out, nullptr, Policy, Indentation);
+ Init->printPretty(Out, nullptr, Policy, Indentation, &Context);
}
}
@@ -488,13 +499,15 @@
CXXConversionDecl *ConversionDecl = dyn_cast<CXXConversionDecl>(D);
CXXDeductionGuideDecl *GuideDecl = dyn_cast<CXXDeductionGuideDecl>(D);
if (!Policy.SuppressSpecifiers) {
- switch (D->getStorageClass()) {
- case SC_None: break;
- case SC_Extern: Out << "extern "; break;
- case SC_Static: Out << "static "; break;
- case SC_PrivateExtern: Out << "__private_extern__ "; break;
- case SC_Auto: case SC_Register:
- llvm_unreachable("invalid for functions");
+ if (!Policy.SupressStorageClassSpecifiers) {
+ switch (D->getStorageClass()) {
+ case SC_None: break;
+ case SC_Extern: Out << "extern "; break;
+ case SC_Static: Out << "static "; break;
+ case SC_PrivateExtern: Out << "__private_extern__ "; break;
+ case SC_Auto: case SC_Register:
+ llvm_unreachable("invalid for functions");
+ }
}
if (D->isInlineSpecified()) Out << "inline ";
@@ -521,7 +534,7 @@
Proto = GuideDecl->getDeducedTemplate()->getDeclName().getAsString();
if (const TemplateArgumentList *TArgs = D->getTemplateSpecializationArgs()) {
llvm::raw_string_ostream POut(Proto);
- DeclPrinter TArgPrinter(POut, SubPolicy, Indentation);
+ DeclPrinter TArgPrinter(POut, SubPolicy, Context, Indentation);
TArgPrinter.printTemplateArguments(*TArgs);
}
@@ -539,7 +552,7 @@
Proto += "(";
if (FT) {
llvm::raw_string_ostream POut(Proto);
- DeclPrinter ParamPrinter(POut, SubPolicy, Indentation);
+ DeclPrinter ParamPrinter(POut, SubPolicy, Context, Indentation);
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
if (i) POut << ", ";
ParamPrinter.VisitParmVarDecl(D->getParamDecl(i));
@@ -695,7 +708,7 @@
// This is a K&R function definition, so we need to print the
// parameters.
Out << '\n';
- DeclPrinter ParamPrinter(Out, SubPolicy, Indentation);
+ DeclPrinter ParamPrinter(Out, SubPolicy, Context, Indentation);
Indentation += Policy.Indentation;
for (unsigned i = 0, e = D->getNumParams(); i != e; ++i) {
Indent();
@@ -1261,6 +1274,9 @@
return;
}
bool eolnOut = false;
+ prettyPrintAttributes(OID);
+ if (OID->hasAttrs()) Out << "\n";
+
Out << "@interface " << I;
if (auto TypeParams = OID->getTypeParamListAsWritten()) {
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index afc7fa8..ef47639 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1695,6 +1695,26 @@
}
}
+const FieldDecl *CastExpr::getTargetFieldForToUnionCast(QualType unionType,
+ QualType opType) {
+ auto RD = unionType->castAs<RecordType>()->getDecl();
+ return getTargetFieldForToUnionCast(RD, opType);
+}
+
+const FieldDecl *CastExpr::getTargetFieldForToUnionCast(const RecordDecl *RD,
+ QualType OpType) {
+ auto &Ctx = RD->getASTContext();
+ RecordDecl::field_iterator Field, FieldEnd;
+ for (Field = RD->field_begin(), FieldEnd = RD->field_end();
+ Field != FieldEnd; ++Field) {
+ if (Ctx.hasSameUnqualifiedType(Field->getType(), OpType) &&
+ !Field->isUnnamedBitfield()) {
+ return *Field;
+ }
+ }
+ return nullptr;
+}
+
ImplicitCastExpr *ImplicitCastExpr::Create(const ASTContext &C, QualType T,
CastKind Kind, Expr *Operand,
const CXXCastPath *BasePath,
diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp
index e2e0dbe..88ee526 100644
--- a/lib/AST/NestedNameSpecifier.cpp
+++ b/lib/AST/NestedNameSpecifier.cpp
@@ -691,3 +691,34 @@
memcpy(Mem, Buffer, BufferSize);
return NestedNameSpecifierLoc(Representation, Mem);
}
+
+NestedNameSpecifier *NestedNameSpecifier::getRequiredQualification(
+ ASTContext &Context, const DeclContext *CurContext,
+ const DeclContext *TargetContext) {
+ SmallVector<const DeclContext *, 4> TargetParents;
+
+ for (const DeclContext *CommonAncestor = TargetContext;
+ CommonAncestor && !CommonAncestor->Encloses(CurContext);
+ CommonAncestor = CommonAncestor->getLookupParent()) {
+ if (CommonAncestor->isTransparentContext() ||
+ CommonAncestor->isFunctionOrMethod())
+ continue;
+
+ TargetParents.push_back(CommonAncestor);
+ }
+
+ NestedNameSpecifier *Result = nullptr;
+ while (!TargetParents.empty()) {
+ const DeclContext *Parent = TargetParents.pop_back_val();
+
+ if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) {
+ if (!Namespace->getIdentifier())
+ continue;
+
+ Result = NestedNameSpecifier::Create(Context, Result, Namespace);
+ } else if (const TagDecl *TD = dyn_cast<TagDecl>(Parent))
+ Result = NestedNameSpecifier::Create(
+ Context, Result, false, Context.getTypeDeclType(TD).getTypePtr());
+ }
+ return Result;
+}
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 5ebaa32..89fd1b9 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -24,6 +24,7 @@
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/CharInfo.h"
+#include "clang/Lex/Lexer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Format.h"
using namespace clang;
@@ -38,12 +39,14 @@
unsigned IndentLevel;
clang::PrinterHelper* Helper;
PrintingPolicy Policy;
+ const ASTContext *Context;
public:
- StmtPrinter(raw_ostream &os, PrinterHelper* helper,
- const PrintingPolicy &Policy,
- unsigned Indentation = 0)
- : OS(os), IndentLevel(Indentation), Helper(helper), Policy(Policy) {}
+ StmtPrinter(raw_ostream &os, PrinterHelper *helper,
+ const PrintingPolicy &Policy, unsigned Indentation = 0,
+ const ASTContext *Context = nullptr)
+ : OS(os), IndentLevel(Indentation), Helper(helper), Policy(Policy),
+ Context(Context) {}
void PrintStmt(Stmt *S) {
PrintStmt(S, Policy.Indentation);
@@ -1419,7 +1422,26 @@
}
}
+/// Prints the given expression using the original source text. Returns true on
+/// success, false otherwise.
+static bool printExprAsWritten(raw_ostream &OS, Expr *E,
+ const ASTContext *Context) {
+ if (!Context)
+ return false;
+ bool Invalid = false;
+ StringRef Source = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(E->getSourceRange()),
+ Context->getSourceManager(), Context->getLangOpts(), &Invalid);
+ if (!Invalid) {
+ OS << Source;
+ return true;
+ }
+ return false;
+}
+
void StmtPrinter::VisitIntegerLiteral(IntegerLiteral *Node) {
+ if (Policy.ConstantsAsWritten && printExprAsWritten(OS, Node, Context))
+ return;
bool isSigned = Node->getType()->isSignedIntegerType();
OS << Node->getValue().toString(10, isSigned);
@@ -1463,6 +1485,8 @@
}
void StmtPrinter::VisitFloatingLiteral(FloatingLiteral *Node) {
+ if (Policy.ConstantsAsWritten && printExprAsWritten(OS, Node, Context))
+ return;
PrintFloatingLiteral(OS, Node, /*PrintSuffix=*/true);
}
@@ -2672,11 +2696,10 @@
printPretty(llvm::errs(), nullptr, PrintingPolicy(Context.getLangOpts()));
}
-void Stmt::printPretty(raw_ostream &OS,
- PrinterHelper *Helper,
- const PrintingPolicy &Policy,
- unsigned Indentation) const {
- StmtPrinter P(OS, Helper, Policy, Indentation);
+void Stmt::printPretty(raw_ostream &OS, PrinterHelper *Helper,
+ const PrintingPolicy &Policy, unsigned Indentation,
+ const ASTContext *Context) const {
+ StmtPrinter P(OS, Helper, Policy, Indentation, Context);
P.Visit(const_cast<Stmt*>(this));
}
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index d21781d..fddd7db 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -1081,24 +1081,13 @@
// Replace an Objective-C type parameter reference with the corresponding
// type argument.
- if (const auto *OTPTy = dyn_cast<ObjCTypeParamType>(splitType.Ty)) {
- if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(OTPTy->getDecl())) {
+ if (const auto *typedefTy = dyn_cast<TypedefType>(splitType.Ty)) {
+ if (auto *typeParam = dyn_cast<ObjCTypeParamDecl>(typedefTy->getDecl())) {
// If we have type arguments, use them.
if (!typeArgs.empty()) {
+ // FIXME: Introduce SubstObjCTypeParamType ?
QualType argType = typeArgs[typeParam->getIndex()];
- if (OTPTy->qual_empty())
- return ctx.getQualifiedType(argType, splitType.Quals);
-
- // Apply protocol lists if exists.
- bool hasError;
- SmallVector<ObjCProtocolDecl*, 8> protocolsVec;
- protocolsVec.append(OTPTy->qual_begin(),
- OTPTy->qual_end());
- ArrayRef<ObjCProtocolDecl *> protocolsToApply = protocolsVec;
- QualType resultTy = ctx.applyObjCProtocolQualifiers(argType,
- protocolsToApply, hasError, true/*allowOnPointerType*/);
-
- return ctx.getQualifiedType(resultTy, splitType.Quals);
+ return ctx.getQualifiedType(argType, splitType.Quals);
}
switch (context) {
diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp
index 15c63bf..269abb2 100644
--- a/lib/AST/TypePrinter.cpp
+++ b/lib/AST/TypePrinter.cpp
@@ -84,11 +84,12 @@
unsigned Indentation;
bool HasEmptyPlaceHolder;
bool InsideCCAttribute;
+ bool IgnoreFunctionProtoTypeConstQual;
public:
explicit TypePrinter(const PrintingPolicy &Policy, unsigned Indentation = 0)
- : Policy(Policy), Indentation(Indentation),
- HasEmptyPlaceHolder(false), InsideCCAttribute(false) { }
+ : Policy(Policy), Indentation(Indentation), HasEmptyPlaceHolder(false),
+ InsideCCAttribute(false), IgnoreFunctionProtoTypeConstQual(false) {}
void print(const Type *ty, Qualifiers qs, raw_ostream &OS,
StringRef PlaceHolder);
@@ -689,8 +690,12 @@
printFunctionAfter(Info, OS);
if (unsigned quals = T->getTypeQuals()) {
- OS << ' ';
- AppendTypeQualList(OS, quals, Policy.Restrict);
+ if (IgnoreFunctionProtoTypeConstQual)
+ quals &= ~unsigned(Qualifiers::Const);
+ if (quals) {
+ OS << ' ';
+ AppendTypeQualList(OS, quals, Policy.Restrict);
+ }
}
switch (T->getRefQualifier()) {
@@ -1009,6 +1014,13 @@
else if (TypedefNameDecl *Typedef = D->getTypedefNameForAnonDecl()) {
assert(Typedef->getIdentifier() && "Typedef without identifier?");
OS << Typedef->getIdentifier()->getName();
+ } else if (Policy.UseStdFunctionForLambda && isa<CXXRecordDecl>(D) &&
+ cast<CXXRecordDecl>(D)->isLambda()) {
+ OS << "std::function<";
+ QualType T = cast<CXXRecordDecl>(D)->getLambdaCallOperator()->getType();
+ SaveAndRestore<bool> NoConst(IgnoreFunctionProtoTypeConstQual, true);
+ print(T, OS, "");
+ OS << '>';
} else {
// Make an unambiguous representation for anonymous types, e.g.
// (anonymous enum at /usr/include/string.h:120:9)
diff --git a/lib/Basic/CMakeLists.txt b/lib/Basic/CMakeLists.txt
index e971b55..36198af 100644
--- a/lib/Basic/CMakeLists.txt
+++ b/lib/Basic/CMakeLists.txt
@@ -98,6 +98,7 @@
Sanitizers.cpp
SourceLocation.cpp
SourceManager.cpp
+ SourceMgrAdapter.cpp
TargetInfo.cpp
Targets.cpp
TokenKinds.cpp
diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp
index 0cdaf8e..c7e4133 100644
--- a/lib/Basic/DiagnosticIDs.cpp
+++ b/lib/Basic/DiagnosticIDs.cpp
@@ -43,7 +43,7 @@
unsigned SFINAE : 2;
unsigned WarnNoWerror : 1;
unsigned WarnShowInSystemHeader : 1;
- unsigned Category : 5;
+ unsigned Category : 6;
uint16_t OptionGroupIndex;
@@ -88,6 +88,7 @@
#include "clang/Basic/DiagnosticCommentKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
+#include "clang/Basic/DiagnosticRefactoringKinds.inc"
#undef DIAG
};
@@ -137,6 +138,7 @@
CATEGORY(COMMENT, AST)
CATEGORY(SEMA, COMMENT)
CATEGORY(ANALYSIS, SEMA)
+CATEGORY(REFACTORING, ANALYSIS)
#undef CATEGORY
// Avoid out of bounds reads.
diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp
index 1d96afd..3ac9679 100644
--- a/lib/Basic/Module.cpp
+++ b/lib/Basic/Module.cpp
@@ -32,7 +32,8 @@
IsMissingRequirement(false), HasIncompatibleModuleFile(false),
IsAvailable(true), IsFromModuleFile(false), IsFramework(IsFramework),
IsExplicit(IsExplicit), IsSystem(false), IsExternC(false),
- IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false),
+ IsInferred(false), IsSwiftInferImportAsMember(false),
+ InferSubmodules(false), InferExplicitSubmodules(false),
InferExportWildcard(false), ConfigMacrosExhaustive(false),
NoUndeclaredIncludes(false), NameVisibility(Hidden) {
if (Parent) {
@@ -85,11 +86,16 @@
bool Module::isAvailable(const LangOptions &LangOpts, const TargetInfo &Target,
Requirement &Req,
- UnresolvedHeaderDirective &MissingHeader) const {
+ UnresolvedHeaderDirective &MissingHeader,
+ Module *&ShadowingModule) const {
if (IsAvailable)
return true;
for (const Module *Current = this; Current; Current = Current->Parent) {
+ if (Current->ShadowingModule) {
+ ShadowingModule = Current->ShadowingModule;
+ return false;
+ }
for (unsigned I = 0, N = Current->Requirements.size(); I != N; ++I) {
if (hasFeature(Current->Requirements[I].first, LangOpts, Target) !=
Current->Requirements[I].second) {
@@ -361,6 +367,8 @@
OS << " [system]";
if (IsExternC)
OS << " [extern_c]";
+ if (IsSwiftInferImportAsMember)
+ OS << " [swift_infer_import_as_member]";
}
OS << " {\n";
@@ -440,6 +448,11 @@
}
}
+ if (!ExportAsModule.empty()) {
+ OS.indent(Indent + 2);
+ OS << "export_as" << ExportAsModule << "\n";
+ }
+
for (submodule_const_iterator MI = submodule_begin(), MIEnd = submodule_end();
MI != MIEnd; ++MI)
// Print inferred subframework modules so that we don't need to re-infer
diff --git a/lib/Basic/SourceMgrAdapter.cpp b/lib/Basic/SourceMgrAdapter.cpp
new file mode 100644
index 0000000..1d52a24
--- /dev/null
+++ b/lib/Basic/SourceMgrAdapter.cpp
@@ -0,0 +1,137 @@
+//=== SourceMgrAdapter.cpp - SourceMgr to SourceManager Adapter -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the adapter that maps diagnostics from llvm::SourceMgr
+// to Clang's SourceManager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/SourceMgrAdapter.h"
+#include "clang/Basic/Diagnostic.h"
+
+using namespace clang;
+
+void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag,
+ void *context) {
+ static_cast<SourceMgrAdapter *>(context)->handleDiag(diag);
+}
+
+SourceMgrAdapter::SourceMgrAdapter(SourceManager &srcMgr,
+ DiagnosticsEngine &diag,
+ unsigned errorDiagID,
+ unsigned warningDiagID,
+ unsigned noteDiagID,
+ const FileEntry *defaultFile)
+ : SrcMgr(srcMgr), Diag(diag), ErrorDiagID(errorDiagID),
+ WarningDiagID(warningDiagID), NoteDiagID(noteDiagID),
+ DefaultFile(defaultFile) { }
+
+SourceMgrAdapter::~SourceMgrAdapter() { }
+
+SourceLocation SourceMgrAdapter::mapLocation(const llvm::SourceMgr &llvmSrcMgr,
+ llvm::SMLoc loc) {
+ // Map invalid locations.
+ if (!loc.isValid())
+ return SourceLocation();
+
+ // Find the buffer containing the location.
+ unsigned bufferID = llvmSrcMgr.FindBufferContainingLoc(loc);
+ if (!bufferID)
+ return SourceLocation();
+
+
+ // If we haven't seen this buffer before, copy it over.
+ auto buffer = llvmSrcMgr.getMemoryBuffer(bufferID);
+ auto knownBuffer = FileIDMapping.find(std::make_pair(&llvmSrcMgr, bufferID));
+ if (knownBuffer == FileIDMapping.end()) {
+ FileID fileID;
+ if (DefaultFile) {
+ // Map to the default file.
+ fileID = SrcMgr.createFileID(DefaultFile, SourceLocation(),
+ SrcMgr::C_User);
+
+ // Only do this once.
+ DefaultFile = nullptr;
+ } else {
+ // Make a copy of the memory buffer.
+ StringRef bufferName = buffer->getBufferIdentifier();
+ auto bufferCopy
+ = std::unique_ptr<llvm::MemoryBuffer>(
+ llvm::MemoryBuffer::getMemBufferCopy(buffer->getBuffer(),
+ bufferName));
+
+ // Add this memory buffer to the Clang source manager.
+ fileID = SrcMgr.createFileID(std::move(bufferCopy));
+ }
+
+ // Save the mapping.
+ knownBuffer = FileIDMapping.insert(
+ std::make_pair(std::make_pair(&llvmSrcMgr, bufferID),
+ fileID)).first;
+ }
+
+ // Translate the offset into the file.
+ unsigned offset = loc.getPointer() - buffer->getBufferStart();
+ return SrcMgr.getLocForStartOfFile(knownBuffer->second)
+ .getLocWithOffset(offset);
+}
+
+SourceRange SourceMgrAdapter::mapRange(const llvm::SourceMgr &llvmSrcMgr,
+ llvm::SMRange range) {
+ if (!range.isValid())
+ return SourceRange();
+
+ SourceLocation start = mapLocation(llvmSrcMgr, range.Start);
+ SourceLocation end = mapLocation(llvmSrcMgr, range.End);
+ return SourceRange(start, end);
+}
+
+void SourceMgrAdapter::handleDiag(const llvm::SMDiagnostic &diag) {
+ // Map the location.
+ SourceLocation loc;
+ if (auto *llvmSrcMgr = diag.getSourceMgr())
+ loc = mapLocation(*llvmSrcMgr, diag.getLoc());
+
+ // Extract the message.
+ StringRef message = diag.getMessage();
+
+ // Map the diagnostic kind.
+ unsigned diagID;
+ switch (diag.getKind()) {
+ case llvm::SourceMgr::DK_Error:
+ diagID = ErrorDiagID;
+ break;
+
+ case llvm::SourceMgr::DK_Warning:
+ diagID = WarningDiagID;
+ break;
+
+ case llvm::SourceMgr::DK_Note:
+ diagID = NoteDiagID;
+ break;
+ }
+
+ // Report the diagnostic.
+ DiagnosticBuilder builder = Diag.Report(loc, diagID) << message;
+
+ if (auto *llvmSrcMgr = diag.getSourceMgr()) {
+ // Translate ranges.
+ SourceLocation startOfLine = loc.getLocWithOffset(-diag.getColumnNo());
+ for (auto range : diag.getRanges()) {
+ builder << SourceRange(startOfLine.getLocWithOffset(range.first),
+ startOfLine.getLocWithOffset(range.second));
+ }
+
+ // Translate Fix-Its.
+ for (const llvm::SMFixIt &fixIt : diag.getFixIts()) {
+ CharSourceRange range(mapRange(*llvmSrcMgr, fixIt.getRange()), false);
+ builder << FixItHint::CreateReplacement(range, fixIt.getText());
+ }
+ }
+}
diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp
index 915c161..b0dc0d2 100644
--- a/lib/Basic/Targets.cpp
+++ b/lib/Basic/Targets.cpp
@@ -4881,7 +4881,7 @@
// x86-64 has atomics up to 16 bytes.
MaxAtomicPromoteWidth = 128;
- MaxAtomicInlineWidth = 128;
+ MaxAtomicInlineWidth = 64;
}
BuiltinVaListKind getBuiltinVaListKind() const override {
return TargetInfo::X86_64ABIBuiltinVaList;
@@ -4938,6 +4938,13 @@
return llvm::makeArrayRef(BuiltinInfoX86,
X86::LastTSBuiltin - Builtin::FirstTSBuiltin);
}
+
+ void setMaxAtomicWidth() override {
+ if (hasFeature("cx16"))
+ MaxAtomicInlineWidth = 128;
+ return;
+ }
+
};
// x86-64 Windows target
@@ -10029,6 +10036,7 @@
Target->setSupportedOpenCLOpts();
Target->setOpenCLExtensionOpts();
+ Target->setMaxAtomicWidth();
if (!Target->validateTarget(Diags))
return nullptr;
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index dfd819a..5e291e5 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(Headers)
add_subdirectory(Basic)
+add_subdirectory(APINotes)
add_subdirectory(Lex)
add_subdirectory(Parse)
add_subdirectory(AST)
@@ -18,6 +19,7 @@
add_subdirectory(FrontendTool)
add_subdirectory(Tooling)
add_subdirectory(Index)
+add_subdirectory(DirectoryWatcher)
if(CLANG_ENABLE_STATIC_ANALYZER)
add_subdirectory(StaticAnalyzer)
endif()
diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h
index 575506d..e4dce2f 100644
--- a/lib/CodeGen/ABIInfo.h
+++ b/lib/CodeGen/ABIInfo.h
@@ -24,7 +24,6 @@
namespace clang {
class ASTContext;
- class CodeGenOptions;
class TargetInfo;
namespace CodeGen {
@@ -69,7 +68,6 @@
llvm::LLVMContext &getVMContext() const;
const llvm::DataLayout &getDataLayout() const;
const TargetInfo &getTarget() const;
- const CodeGenOptions &getCodeGenOpts() const;
/// Return the calling convention to use for system runtime
/// functions.
diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp
index 1810489..6cf7f71 100644
--- a/lib/CodeGen/CGBlocks.cpp
+++ b/lib/CodeGen/CGBlocks.cpp
@@ -16,6 +16,7 @@
#include "CGObjCRuntime.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "clang/CodeGen/ConstantInitBuilder.h"
#include "clang/AST/DeclObjC.h"
#include "llvm/ADT/SmallSet.h"
@@ -290,7 +291,7 @@
const Expr *init = var->getInit();
if (!init) return nullptr;
- return CGM.EmitConstantInit(*var, CGF);
+ return ConstantEmitter(CGM, CGF).tryEmitAbstractForInitializer(*var);
}
/// Get the low bit of a nonzero character count. This is the
@@ -724,12 +725,13 @@
llvm::Constant *blockFn
= CodeGenFunction(CGM, true).GenerateBlockFunction(CurGD, blockInfo,
LocalDeclMap,
- isLambdaConv);
+ isLambdaConv,
+ blockInfo.CanBeGlobal);
blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
// If there is nothing to capture, we can emit this as a global block.
if (blockInfo.CanBeGlobal)
- return buildGlobalBlock(CGM, blockInfo, blockFn);
+ return CGM.getAddrOfGlobalBlockIfEmitted(blockInfo.BlockExpression);
// Otherwise, we have to emit this as a local block.
@@ -1113,17 +1115,14 @@
computeBlockInfo(*this, nullptr, blockInfo);
// Using that metadata, generate the actual block function.
- llvm::Constant *blockFn;
{
CodeGenFunction::DeclMapTy LocalDeclMap;
- blockFn = CodeGenFunction(*this).GenerateBlockFunction(GlobalDecl(),
- blockInfo,
- LocalDeclMap,
- false);
+ CodeGenFunction(*this).GenerateBlockFunction(
+ GlobalDecl(), blockInfo, LocalDeclMap,
+ /*IsLambdaConversionToBlock*/ false, /*BuildGlobalBlock*/ true);
}
- blockFn = llvm::ConstantExpr::getBitCast(blockFn, VoidPtrTy);
- return buildGlobalBlock(*this, blockInfo, blockFn);
+ return getAddrOfGlobalBlockIfEmitted(BE);
}
static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM,
@@ -1225,7 +1224,8 @@
CodeGenFunction::GenerateBlockFunction(GlobalDecl GD,
const CGBlockInfo &blockInfo,
const DeclMapTy &ldm,
- bool IsLambdaConversionToBlock) {
+ bool IsLambdaConversionToBlock,
+ bool BuildGlobalBlock) {
const BlockDecl *blockDecl = blockInfo.getBlockDecl();
CurGD = GD;
@@ -1284,6 +1284,10 @@
fnLLVMType, llvm::GlobalValue::InternalLinkage, name, &CGM.getModule());
CGM.SetInternalFunctionAttributes(blockDecl, fn, fnInfo);
+ if (BuildGlobalBlock)
+ buildGlobalBlock(CGM, blockInfo,
+ llvm::ConstantExpr::getBitCast(fn, VoidPtrTy));
+
// Begin generating the function.
StartFunction(blockDecl, fnType->getReturnType(), fn, fnInfo, args,
blockDecl->getLocation(),
diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp
index f3527b0..c43b0e5 100644
--- a/lib/CodeGen/CGBuiltin.cpp
+++ b/lib/CodeGen/CGBuiltin.cpp
@@ -16,6 +16,7 @@
#include "CGOpenCLRuntime.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -641,6 +642,26 @@
};
}
+Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
+ BuiltinCheckKind Kind) {
+ assert(Kind == BCK_CLZPassedZero ||
+ Kind == BCK_CTZPassedZero && "Unsupported builtin check kind");
+
+ Value *ArgValue = EmitScalarExpr(E);
+ if (!SanOpts.has(SanitizerKind::Builtin) || !getTarget().isCLZForZeroUndef())
+ return ArgValue;
+
+ SanitizerScope SanScope(this);
+ Value *Cond = Builder.CreateICmpNE(
+ ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
+ EmitCheck(std::make_pair(Cond, SanitizerKind::Builtin),
+ SanitizerHandler::InvalidBuiltin,
+ {EmitCheckSourceLocation(E->getExprLoc()),
+ llvm::ConstantInt::get(Builder.getInt8Ty(), Kind)},
+ None);
+ return ArgValue;
+}
+
RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
unsigned BuiltinID, const CallExpr *E,
ReturnValueSlot ReturnValue) {
@@ -660,7 +681,7 @@
default: break; // Handle intrinsics and libm functions below.
case Builtin::BI__builtin___CFStringMakeConstantString:
case Builtin::BI__builtin___NSStringMakeConstantString:
- return RValue::get(CGM.EmitConstantExpr(E, E->getType(), nullptr));
+ return RValue::get(ConstantEmitter(*this).emitAbstract(E, E->getType()));
case Builtin::BI__builtin_stdarg_start:
case Builtin::BI__builtin_va_start:
case Builtin::BI__va_start:
@@ -792,7 +813,7 @@
case Builtin::BI__builtin_ctz:
case Builtin::BI__builtin_ctzl:
case Builtin::BI__builtin_ctzll: {
- Value *ArgValue = EmitScalarExpr(E->getArg(0));
+ Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CTZPassedZero);
llvm::Type *ArgType = ArgValue->getType();
Value *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType);
@@ -809,7 +830,7 @@
case Builtin::BI__builtin_clz:
case Builtin::BI__builtin_clzl:
case Builtin::BI__builtin_clzll: {
- Value *ArgValue = EmitScalarExpr(E->getArg(0));
+ Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);
llvm::Type *ArgType = ArgValue->getType();
Value *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
@@ -1346,8 +1367,8 @@
llvm::ConstantInt::get(Int32Ty, Offset)));
}
case Builtin::BI__builtin_return_address: {
- Value *Depth =
- CGM.EmitConstantExpr(E->getArg(0), getContext().UnsignedIntTy, this);
+ Value *Depth = ConstantEmitter(*this).emitAbstract(E->getArg(0),
+ getContext().UnsignedIntTy);
Value *F = CGM.getIntrinsic(Intrinsic::returnaddress);
return RValue::get(Builder.CreateCall(F, Depth));
}
@@ -1356,8 +1377,8 @@
return RValue::get(Builder.CreateCall(F, Builder.getInt32(0)));
}
case Builtin::BI__builtin_frame_address: {
- Value *Depth =
- CGM.EmitConstantExpr(E->getArg(0), getContext().UnsignedIntTy, this);
+ Value *Depth = ConstantEmitter(*this).emitAbstract(E->getArg(0),
+ getContext().UnsignedIntTy);
Value *F = CGM.getIntrinsic(Intrinsic::frameaddress);
return RValue::get(Builder.CreateCall(F, Depth));
}
diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp
index 0332586..e29e525 100644
--- a/lib/CodeGen/CGCXXABI.cpp
+++ b/lib/CodeGen/CGCXXABI.cpp
@@ -30,9 +30,38 @@
}
bool CGCXXABI::canCopyArgument(const CXXRecordDecl *RD) const {
+ // If RD has a non-trivial move or copy constructor, we cannot copy the
+ // argument.
+ if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialMoveConstructor())
+ return false;
+
+ // If RD has a non-trivial destructor, we cannot copy the argument.
+ if (RD->hasNonTrivialDestructor())
+ return false;
+
// We can only copy the argument if there exists at least one trivial,
// non-deleted copy or move constructor.
- return RD->canPassInRegisters();
+ // FIXME: This assumes that all lazily declared copy and move constructors are
+ // not deleted. This assumption might not be true in some corner cases.
+ bool CopyDeleted = false;
+ bool MoveDeleted = false;
+ for (const CXXConstructorDecl *CD : RD->ctors()) {
+ if (CD->isCopyConstructor() || CD->isMoveConstructor()) {
+ assert(CD->isTrivial());
+ // We had at least one undeleted trivial copy or move ctor. Return
+ // directly.
+ if (!CD->isDeleted())
+ return true;
+ if (CD->isCopyConstructor())
+ CopyDeleted = true;
+ else
+ MoveDeleted = true;
+ }
+ }
+
+ // If all trivial copy and move constructors are deleted, we cannot copy the
+ // argument.
+ return !(CopyDeleted && MoveDeleted);
}
llvm::Constant *CGCXXABI::GetBogusMemberPointer(QualType T) {
diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp
index 18b1d10..fc354e6 100644
--- a/lib/CodeGen/CGDebugInfo.cpp
+++ b/lib/CodeGen/CGDebugInfo.cpp
@@ -18,6 +18,7 @@
#include "CGRecordLayout.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
@@ -527,16 +528,15 @@
// Create new compile unit.
// FIXME - Eliminate TheCU.
+ auto &CGOpts = CGM.getCodeGenOpts();
TheCU = DBuilder.createCompileUnit(
LangTag,
DBuilder.createFile(remapDIPath(MainFileName),
remapDIPath(getCurrentDirname()), CSKind, Checksum),
- Producer, LO.Optimize, CGM.getCodeGenOpts().DwarfDebugFlags, RuntimeVers,
- CGM.getCodeGenOpts().EnableSplitDwarf
- ? ""
- : CGM.getCodeGenOpts().SplitDwarfFile,
- EmissionKind, 0 /* DWOid */, CGM.getCodeGenOpts().SplitDwarfInlining,
- CGM.getCodeGenOpts().DebugInfoForProfiling);
+ Producer, LO.Optimize || CGOpts.PrepareForLTO || CGOpts.EmitSummaryIndex,
+ CGOpts.DwarfDebugFlags, RuntimeVers,
+ CGOpts.EnableSplitDwarf ? "" : CGOpts.SplitDwarfFile, EmissionKind,
+ 0 /* DWOid */, CGOpts.SplitDwarfInlining, CGOpts.DebugInfoForProfiling);
}
llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) {
@@ -1590,7 +1590,7 @@
QualType T = E->getType();
if (E->isGLValue())
T = CGM.getContext().getLValueReferenceType(T);
- llvm::Constant *V = CGM.EmitConstantExpr(E, T);
+ llvm::Constant *V = ConstantEmitter(CGM).emitAbstract(E, T);
assert(V && "Expression in template argument isn't constant");
llvm::DIType *TTy = getOrCreateType(T, Unit);
TemplateParams.push_back(DBuilder.createTemplateValueParameter(
@@ -1766,6 +1766,29 @@
return false;
}
+/// Does a type definition exist in an imported clang module?
+static bool isDefinedInClangModule(const RecordDecl *RD) {
+ // Only definitions that where imported from an AST file come from a module.
+ if (!RD || !RD->isFromASTFile())
+ return false;
+ // Anonymous entities cannot be addressed. Treat them as not from module.
+ if (!RD->isExternallyVisible() && RD->getName().empty())
+ return false;
+ if (auto *CXXDecl = dyn_cast<CXXRecordDecl>(RD)) {
+ if (!CXXDecl->isCompleteDefinition())
+ return false;
+ auto TemplateKind = CXXDecl->getTemplateSpecializationKind();
+ if (TemplateKind != TSK_Undeclared) {
+ // This is a template, check the origin of the first member.
+ if (CXXDecl->field_begin() == CXXDecl->field_end())
+ return TemplateKind == TSK_ExplicitInstantiationDeclaration;
+ if (!CXXDecl->field_begin()->isFromASTFile())
+ return false;
+ }
+ }
+ return true;
+}
+
void CGDebugInfo::completeClassData(const RecordDecl *RD) {
if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
if (CXXRD->isDynamicClass() &&
@@ -1773,6 +1796,10 @@
llvm::GlobalValue::AvailableExternallyLinkage &&
!isClassOrMethodDLLImport(CXXRD))
return;
+
+ if (DebugTypeExtRefs && isDefinedInClangModule(RD->getDefinition()))
+ return;
+
completeClass(RD);
}
@@ -1799,29 +1826,6 @@
return false;
}
-/// Does a type definition exist in an imported clang module?
-static bool isDefinedInClangModule(const RecordDecl *RD) {
- // Only definitions that where imported from an AST file come from a module.
- if (!RD || !RD->isFromASTFile())
- return false;
- // Anonymous entities cannot be addressed. Treat them as not from module.
- if (!RD->isExternallyVisible() && RD->getName().empty())
- return false;
- if (auto *CXXDecl = dyn_cast<CXXRecordDecl>(RD)) {
- if (!CXXDecl->isCompleteDefinition())
- return false;
- auto TemplateKind = CXXDecl->getTemplateSpecializationKind();
- if (TemplateKind != TSK_Undeclared) {
- // This is a template, check the origin of the first member.
- if (CXXDecl->field_begin() == CXXDecl->field_end())
- return TemplateKind == TSK_ExplicitInstantiationDeclaration;
- if (!CXXDecl->field_begin()->isFromASTFile())
- return false;
- }
- }
- return true;
-}
-
static bool shouldOmitDefinition(codegenoptions::DebugInfoKind DebugKind,
bool DebugTypeExtRefs, const RecordDecl *RD,
const LangOptions &LangOpts) {
@@ -3795,7 +3799,7 @@
if (LocalAddr) {
// Insert an llvm.dbg.value into the current block.
DBuilder.insertDbgValueIntrinsic(
- LocalAddr, 0, debugVar, DBuilder.createExpression(),
+ LocalAddr, debugVar, DBuilder.createExpression(),
llvm::DebugLoc::get(line, column, scope, CurInlinedAt),
Builder.GetInsertBlock());
}
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 2351786..75a1350 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -19,6 +19,7 @@
#include "CGOpenMPRuntime.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/CharUnits.h"
@@ -307,7 +308,8 @@
llvm::GlobalVariable *
CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,
llvm::GlobalVariable *GV) {
- llvm::Constant *Init = CGM.EmitConstantInit(D, this);
+ ConstantEmitter emitter(*this);
+ llvm::Constant *Init = emitter.tryEmitForInitializer(D);
// If constant emission failed, then this should be a C++ static
// initializer.
@@ -355,6 +357,8 @@
GV->setConstant(CGM.isTypeConstant(D.getType(), true));
GV->setInitializer(Init);
+ emitter.finalize(GV);
+
if (hasNontrivialDestruction(D.getType()) && HaveInsertPoint()) {
// We have a constant initializer, but a nontrivial destructor. We still
// need to perform a guarded "initialization" in order to register the
@@ -1236,7 +1240,7 @@
llvm::Constant *constant = nullptr;
if (emission.IsConstantAggregate || D.isConstexpr()) {
assert(!capturedByInit && "constant init contains a capturing block?");
- constant = CGM.EmitConstantInit(D, this);
+ constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D);
}
if (!constant) {
diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp
index 40ae092..709ed00 100644
--- a/lib/CodeGen/CGException.cpp
+++ b/lib/CodeGen/CGException.cpp
@@ -15,6 +15,7 @@
#include "CGCXXABI.h"
#include "CGCleanup.h"
#include "CGObjCRuntime.h"
+#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/StmtCXX.h"
@@ -1800,7 +1801,8 @@
// "catch i8* null". We can't do this on x86 because the filter has to save
// the exception code.
llvm::Constant *C =
- CGM.EmitConstantExpr(Except->getFilterExpr(), getContext().IntTy, this);
+ ConstantEmitter(*this).tryEmitAbstract(Except->getFilterExpr(),
+ getContext().IntTy);
if (CGM.getTarget().getTriple().getArch() != llvm::Triple::x86 && C &&
C->isOneValue()) {
CatchScope->setCatchAllHandler(0, createBasicBlock("__except"));
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index bc37c9e..1a4b48a 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -20,6 +20,7 @@
#include "CGRecordLayout.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
@@ -356,7 +357,7 @@
if (CGF.CGM.getCodeGenOpts().MergeAllConstants &&
(Ty->isArrayType() || Ty->isRecordType()) &&
CGF.CGM.isTypeConstant(Ty, true))
- if (llvm::Constant *Init = CGF.CGM.EmitConstantExpr(Inner, Ty, &CGF)) {
+ if (auto Init = ConstantEmitter(CGF).tryEmitAbstract(Inner, Ty)) {
if (auto AddrSpace = CGF.getTarget().getConstantAddressSpace()) {
auto AS = AddrSpace.getValue();
auto *GV = new llvm::GlobalVariable(
@@ -604,20 +605,23 @@
auto PtrToAlloca =
dyn_cast<llvm::AllocaInst>(Ptr->stripPointerCastsNoFollowAliases());
+ llvm::Value *IsNonNull = nullptr;
+ bool IsGuaranteedNonNull =
+ SkippedChecks.has(SanitizerKind::Null) || PtrToAlloca;
bool AllowNullPointers = TCK == TCK_DowncastPointer || TCK == TCK_Upcast ||
TCK == TCK_UpcastToVirtualBase;
if ((SanOpts.has(SanitizerKind::Null) || AllowNullPointers) &&
- !SkippedChecks.has(SanitizerKind::Null) && !PtrToAlloca) {
+ !IsGuaranteedNonNull) {
// The glvalue must not be an empty glvalue.
- llvm::Value *IsNonNull = Builder.CreateIsNotNull(Ptr);
+ IsNonNull = Builder.CreateIsNotNull(Ptr);
// The IR builder can constant-fold the null check if the pointer points to
// a constant.
- bool PtrIsNonNull =
+ IsGuaranteedNonNull =
IsNonNull == llvm::ConstantInt::getTrue(getLLVMContext());
// Skip the null check if the pointer is known to be non-null.
- if (!PtrIsNonNull) {
+ if (!IsGuaranteedNonNull) {
if (AllowNullPointers) {
// When performing pointer casts, it's OK if the value is null.
// Skip the remaining checks in that case.
@@ -697,6 +701,18 @@
TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference ||
TCK == TCK_UpcastToVirtualBase) &&
RD && RD->hasDefinition() && RD->isDynamicClass()) {
+ // Ensure that the pointer is non-null before loading it. If there is no
+ // compile-time guarantee, reuse the run-time null check or emit a new one.
+ if (!IsGuaranteedNonNull) {
+ if (!IsNonNull)
+ IsNonNull = Builder.CreateIsNotNull(Ptr);
+ if (!Done)
+ Done = createBasicBlock("vptr.null");
+ llvm::BasicBlock *VptrNotNull = createBasicBlock("vptr.not.null");
+ Builder.CreateCondBr(IsNonNull, VptrNotNull, Done);
+ EmitBlock(VptrNotNull);
+ }
+
// Compute a hash of the mangled name of the type.
//
// FIXME: This is not guaranteed to be deterministic! Move to a
@@ -1303,7 +1319,8 @@
return ConstantEmission();
// Emit as a constant.
- llvm::Constant *C = CGM.EmitConstantValue(result.Val, resultType, this);
+ auto C = ConstantEmitter(*this).emitAbstract(refExpr->getLocation(),
+ result.Val, resultType);
// Make sure we emit a debug reference to the global variable.
// This should probably fire even for
@@ -1322,6 +1339,25 @@
return ConstantEmission::forValue(C);
}
+static DeclRefExpr *tryToConvertMemberExprToDeclRefExpr(CodeGenFunction &CGF,
+ const MemberExpr *ME) {
+ if (auto *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) {
+ // Try to emit static variable member expressions as DREs.
+ return DeclRefExpr::Create(
+ CGF.getContext(), NestedNameSpecifierLoc(), SourceLocation(), VD,
+ /*RefersToEnclosingVariableOrCapture=*/false, ME->getExprLoc(),
+ ME->getType(), ME->getValueKind());
+ }
+ return nullptr;
+}
+
+CodeGenFunction::ConstantEmission
+CodeGenFunction::tryEmitAsConstant(const MemberExpr *ME) {
+ if (DeclRefExpr *DRE = tryToConvertMemberExprToDeclRefExpr(*this, ME))
+ return tryEmitAsConstant(DRE);
+ return ConstantEmission();
+}
+
llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue,
SourceLocation Loc) {
return EmitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatile(),
@@ -2268,7 +2304,9 @@
!(E->refersToEnclosingVariableOrCapture() && CapturedStmtInfo &&
LocalDeclMap.count(VD))) {
llvm::Constant *Val =
- CGM.EmitConstantValue(*VD->evaluateValue(), VD->getType(), this);
+ ConstantEmitter(*this).emitAbstract(E->getLocation(),
+ *VD->evaluateValue(),
+ VD->getType());
assert(Val && "failed to emit reference constant expression");
// FIXME: Eventually we will want to emit vector element references.
@@ -2685,13 +2723,16 @@
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
bool NeedsAbortSuffix =
IsFatal && RecoverKind != CheckRecoverableKind::Unrecoverable;
+ bool MinimalRuntime = CGF.CGM.getCodeGenOpts().SanitizeMinimalRuntime;
const SanitizerHandlerInfo &CheckInfo = SanitizerHandlers[CheckHandler];
const StringRef CheckName = CheckInfo.Name;
- std::string FnName =
- ("__ubsan_handle_" + CheckName +
- (CheckInfo.Version ? "_v" + llvm::utostr(CheckInfo.Version) : "") +
- (NeedsAbortSuffix ? "_abort" : ""))
- .str();
+ std::string FnName = "__ubsan_handle_" + CheckName.str();
+ if (CheckInfo.Version && !MinimalRuntime)
+ FnName += "_v" + llvm::utostr(CheckInfo.Version);
+ if (MinimalRuntime)
+ FnName += "_minimal";
+ if (NeedsAbortSuffix)
+ FnName += "_abort";
bool MayReturn =
!IsFatal || RecoverKind == CheckRecoverableKind::AlwaysRecoverable;
@@ -2778,24 +2819,26 @@
// representing operand values.
SmallVector<llvm::Value *, 4> Args;
SmallVector<llvm::Type *, 4> ArgTypes;
- Args.reserve(DynamicArgs.size() + 1);
- ArgTypes.reserve(DynamicArgs.size() + 1);
+ if (!CGM.getCodeGenOpts().SanitizeMinimalRuntime) {
+ Args.reserve(DynamicArgs.size() + 1);
+ ArgTypes.reserve(DynamicArgs.size() + 1);
- // Emit handler arguments and create handler function type.
- if (!StaticArgs.empty()) {
- llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
- auto *InfoPtr =
- new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
- llvm::GlobalVariable::PrivateLinkage, Info);
- InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
- CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
- Args.push_back(Builder.CreateBitCast(InfoPtr, Int8PtrTy));
- ArgTypes.push_back(Int8PtrTy);
- }
+ // Emit handler arguments and create handler function type.
+ if (!StaticArgs.empty()) {
+ llvm::Constant *Info = llvm::ConstantStruct::getAnon(StaticArgs);
+ auto *InfoPtr =
+ new llvm::GlobalVariable(CGM.getModule(), Info->getType(), false,
+ llvm::GlobalVariable::PrivateLinkage, Info);
+ InfoPtr->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);
+ CGM.getSanitizerMetadata()->disableSanitizerForGlobal(InfoPtr);
+ Args.push_back(Builder.CreateBitCast(InfoPtr, Int8PtrTy));
+ ArgTypes.push_back(Int8PtrTy);
+ }
- for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) {
- Args.push_back(EmitCheckValue(DynamicArgs[i]));
- ArgTypes.push_back(IntPtrTy);
+ for (size_t i = 0, n = DynamicArgs.size(); i != n; ++i) {
+ Args.push_back(EmitCheckValue(DynamicArgs[i]));
+ ArgTypes.push_back(IntPtrTy);
+ }
}
llvm::FunctionType *FnType =
@@ -3520,6 +3563,11 @@
}
LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) {
+ if (DeclRefExpr *DRE = tryToConvertMemberExprToDeclRefExpr(*this, E)) {
+ EmitIgnoredExpr(E->getBase());
+ return EmitDeclRefLValue(DRE);
+ }
+
Expr *BaseExpr = E->getBase();
// If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar.
LValue BaseLV;
@@ -3546,9 +3594,6 @@
return LV;
}
- if (auto *VD = dyn_cast<VarDecl>(ND))
- return EmitGlobalVarDeclLValue(*this, E, VD);
-
if (const auto *FD = dyn_cast<FunctionDecl>(ND))
return EmitFunctionDeclLValue(*this, E, FD);
@@ -4363,10 +4408,7 @@
SanitizerScope SanScope(this);
llvm::Constant *FTRTTIConst =
CGM.GetAddrOfRTTIDescriptor(QualType(FnType, 0), /*ForEH=*/true);
- llvm::Type *PrefixStructTyElems[] = {
- PrefixSig->getType(),
- FTRTTIConst->getType()
- };
+ llvm::Type *PrefixStructTyElems[] = {PrefixSig->getType(), Int32Ty};
llvm::StructType *PrefixStructTy = llvm::StructType::get(
CGM.getLLVMContext(), PrefixStructTyElems, /*isPacked=*/true);
@@ -4387,8 +4429,10 @@
EmitBlock(TypeCheck);
llvm::Value *CalleeRTTIPtr =
Builder.CreateConstGEP2_32(PrefixStructTy, CalleePrefixStruct, 0, 1);
- llvm::Value *CalleeRTTI =
+ llvm::Value *CalleeRTTIEncoded =
Builder.CreateAlignedLoad(CalleeRTTIPtr, getPointerAlign());
+ llvm::Value *CalleeRTTI =
+ DecodeAddrUsedInPrologue(CalleePtr, CalleeRTTIEncoded);
llvm::Value *CalleeRTTIMatch =
Builder.CreateICmpEQ(CalleeRTTI, FTRTTIConst);
llvm::Constant *StaticData[] = {
diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp
index a05a088..1ab8433 100644
--- a/lib/CodeGen/CGExprAgg.cpp
+++ b/lib/CodeGen/CGExprAgg.cpp
@@ -124,24 +124,7 @@
}
// l-values.
- void VisitDeclRefExpr(DeclRefExpr *E) {
- // For aggregates, we should always be able to emit the variable
- // as an l-value unless it's a reference. This is due to the fact
- // that we can't actually ever see a normal l2r conversion on an
- // aggregate in C++, and in C there's no language standard
- // actively preventing us from listing variables in the captures
- // list of a block.
- if (E->getDecl()->getType()->isReferenceType()) {
- if (CodeGenFunction::ConstantEmission result
- = CGF.tryEmitAsConstant(E)) {
- EmitFinalDestCopy(E->getType(), result.getReferenceLValue(CGF, E));
- return;
- }
- }
-
- EmitAggLoadOfLValue(E);
- }
-
+ void VisitDeclRefExpr(DeclRefExpr *E) { EmitAggLoadOfLValue(E); }
void VisitMemberExpr(MemberExpr *ME) { EmitAggLoadOfLValue(ME); }
void VisitUnaryDeref(UnaryOperator *E) { EmitAggLoadOfLValue(E); }
void VisitStringLiteral(StringLiteral *E) { EmitAggLoadOfLValue(E); }
diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp
index ab17024..8ee236d 100644
--- a/lib/CodeGen/CGExprCXX.cpp
+++ b/lib/CodeGen/CGExprCXX.cpp
@@ -16,6 +16,7 @@
#include "CGCXXABI.h"
#include "CGDebugInfo.h"
#include "CGObjCRuntime.h"
+#include "ConstantEmitter.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/Frontend/CodeGenOptions.h"
#include "llvm/IR/CallSite.h"
@@ -681,8 +682,8 @@
// Emit the array size expression.
// We multiply the size of all dimensions for NumElements.
// e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6.
- numElements = CGF.CGM.EmitConstantExpr(e->getArraySize(),
- CGF.getContext().getSizeType(), &CGF);
+ numElements =
+ ConstantEmitter(CGF).tryEmitAbstract(e->getArraySize(), e->getType());
if (!numElements)
numElements = CGF.EmitScalarExpr(e->getArraySize());
assert(isa<llvm::IntegerType>(numElements->getType()));
diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp
index 9809723..f019e35 100644
--- a/lib/CodeGen/CGExprComplex.cpp
+++ b/lib/CodeGen/CGExprComplex.cpp
@@ -120,18 +120,22 @@
return Visit(E->getSubExpr());
}
+ ComplexPairTy emitConstant(const CodeGenFunction::ConstantEmission &Constant,
+ Expr *E) {
+ assert(Constant && "not a constant");
+ if (Constant.isReference())
+ return EmitLoadOfLValue(Constant.getReferenceLValue(CGF, E),
+ E->getExprLoc());
+
+ llvm::Constant *pair = Constant.getValue();
+ return ComplexPairTy(pair->getAggregateElement(0U),
+ pair->getAggregateElement(1U));
+ }
// l-values.
ComplexPairTy VisitDeclRefExpr(DeclRefExpr *E) {
- if (CodeGenFunction::ConstantEmission result = CGF.tryEmitAsConstant(E)) {
- if (result.isReference())
- return EmitLoadOfLValue(result.getReferenceLValue(CGF, E),
- E->getExprLoc());
-
- llvm::Constant *pair = result.getValue();
- return ComplexPairTy(pair->getAggregateElement(0U),
- pair->getAggregateElement(1U));
- }
+ if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E))
+ return emitConstant(Constant, E);
return EmitLoadOfLValue(E);
}
ComplexPairTy VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) {
@@ -141,7 +145,14 @@
return CGF.EmitObjCMessageExpr(E).getComplexVal();
}
ComplexPairTy VisitArraySubscriptExpr(Expr *E) { return EmitLoadOfLValue(E); }
- ComplexPairTy VisitMemberExpr(const Expr *E) { return EmitLoadOfLValue(E); }
+ ComplexPairTy VisitMemberExpr(MemberExpr *ME) {
+ if (CodeGenFunction::ConstantEmission Constant =
+ CGF.tryEmitAsConstant(ME)) {
+ CGF.EmitIgnoredExpr(ME->getBase());
+ return emitConstant(Constant, ME);
+ }
+ return EmitLoadOfLValue(ME);
+ }
ComplexPairTy VisitOpaqueValueExpr(OpaqueValueExpr *E) {
if (E->isGLValue())
return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E), E->getExprLoc());
diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp
index 6b72774..7262ba6 100644
--- a/lib/CodeGen/CGExprConstant.cpp
+++ b/lib/CodeGen/CGExprConstant.cpp
@@ -16,6 +16,7 @@
#include "CGObjCRuntime.h"
#include "CGRecordLayout.h"
#include "CodeGenModule.h"
+#include "ConstantEmitter.h"
#include "TargetInfo.h"
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
@@ -37,25 +38,26 @@
class ConstExprEmitter;
class ConstStructBuilder {
CodeGenModule &CGM;
- CodeGenFunction *CGF;
+ ConstantEmitter &Emitter;
bool Packed;
CharUnits NextFieldOffsetInChars;
CharUnits LLVMStructAlignment;
SmallVector<llvm::Constant *, 32> Elements;
public:
- static llvm::Constant *BuildStruct(CodeGenModule &CGM, CodeGenFunction *CFG,
- ConstExprEmitter *Emitter,
+ static llvm::Constant *BuildStruct(ConstantEmitter &Emitter,
+ ConstExprEmitter *ExprEmitter,
llvm::ConstantStruct *Base,
- InitListExpr *Updater);
- static llvm::Constant *BuildStruct(CodeGenModule &CGM, CodeGenFunction *CGF,
- InitListExpr *ILE);
- static llvm::Constant *BuildStruct(CodeGenModule &CGM, CodeGenFunction *CGF,
+ InitListExpr *Updater,
+ QualType ValTy);
+ static llvm::Constant *BuildStruct(ConstantEmitter &Emitter,
+ InitListExpr *ILE, QualType StructTy);
+ static llvm::Constant *BuildStruct(ConstantEmitter &Emitter,
const APValue &Value, QualType ValTy);
private:
- ConstStructBuilder(CodeGenModule &CGM, CodeGenFunction *CGF)
- : CGM(CGM), CGF(CGF), Packed(false),
+ ConstStructBuilder(ConstantEmitter &emitter)
+ : CGM(emitter.CGM), Emitter(emitter), Packed(false),
NextFieldOffsetInChars(CharUnits::Zero()),
LLVMStructAlignment(CharUnits::One()) { }
@@ -76,7 +78,7 @@
bool Build(InitListExpr *ILE);
bool Build(ConstExprEmitter *Emitter, llvm::ConstantStruct *Base,
InitListExpr *Updater);
- void Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase,
+ bool Build(const APValue &Val, const RecordDecl *RD, bool IsPrimaryBase,
const CXXRecordDecl *VTableClass, CharUnits BaseOffset);
llvm::Constant *Finalize(QualType Ty);
@@ -391,10 +393,10 @@
// we just use explicit null values for them.
llvm::Constant *EltInit;
if (ElementNo < ILE->getNumInits())
- EltInit = CGM.EmitConstantExpr(ILE->getInit(ElementNo++),
- Field->getType(), CGF);
+ EltInit = Emitter.tryEmitPrivateForMemory(ILE->getInit(ElementNo++),
+ Field->getType());
else
- EltInit = CGM.EmitNullConstant(Field->getType());
+ EltInit = Emitter.emitNullForMemory(Field->getType());
if (!EltInit)
return false;
@@ -431,7 +433,7 @@
};
}
-void ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
+bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
bool IsPrimaryBase,
const CXXRecordDecl *VTableClass,
CharUnits Offset) {
@@ -486,8 +488,9 @@
const APValue &FieldValue =
RD->isUnion() ? Val.getUnionValue() : Val.getStructField(FieldNo);
llvm::Constant *EltInit =
- CGM.EmitConstantValueForMemory(FieldValue, Field->getType(), CGF);
- assert(EltInit && "EmitConstantValue can't fail");
+ Emitter.tryEmitPrivateForMemory(FieldValue, Field->getType());
+ if (!EltInit)
+ return false;
if (!Field->isBitField()) {
// Handle non-bitfield members.
@@ -498,6 +501,8 @@
cast<llvm::ConstantInt>(EltInit));
}
}
+
+ return true;
}
llvm::Constant *ConstStructBuilder::Finalize(QualType Ty) {
@@ -559,37 +564,37 @@
return Result;
}
-llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM,
- CodeGenFunction *CGF,
- ConstExprEmitter *Emitter,
+llvm::Constant *ConstStructBuilder::BuildStruct(ConstantEmitter &Emitter,
+ ConstExprEmitter *ExprEmitter,
llvm::ConstantStruct *Base,
- InitListExpr *Updater) {
- ConstStructBuilder Builder(CGM, CGF);
- if (!Builder.Build(Emitter, Base, Updater))
+ InitListExpr *Updater,
+ QualType ValTy) {
+ ConstStructBuilder Builder(Emitter);
+ if (!Builder.Build(ExprEmitter, Base, Updater))
return nullptr;
- return Builder.Finalize(Updater->getType());
+ return Builder.Finalize(ValTy);
}
-llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM,
- CodeGenFunction *CGF,
- InitListExpr *ILE) {
- ConstStructBuilder Builder(CGM, CGF);
+llvm::Constant *ConstStructBuilder::BuildStruct(ConstantEmitter &Emitter,
+ InitListExpr *ILE,
+ QualType ValTy) {
+ ConstStructBuilder Builder(Emitter);
if (!Builder.Build(ILE))
return nullptr;
- return Builder.Finalize(ILE->getType());
+ return Builder.Finalize(ValTy);
}
-llvm::Constant *ConstStructBuilder::BuildStruct(CodeGenModule &CGM,
- CodeGenFunction *CGF,
+llvm::Constant *ConstStructBuilder::BuildStruct(ConstantEmitter &Emitter,
const APValue &Val,
QualType ValTy) {
- ConstStructBuilder Builder(CGM, CGF);
+ ConstStructBuilder Builder(Emitter);
const RecordDecl *RD = ValTy->castAs<RecordType>()->getDecl();
const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD);
- Builder.Build(Val, RD, false, CD, CharUnits::Zero());
+ if (!Builder.Build(Val, RD, false, CD, CharUnits::Zero()))
+ return nullptr;
return Builder.Finalize(ValTy);
}
@@ -599,57 +604,86 @@
// ConstExprEmitter
//===----------------------------------------------------------------------===//
+static ConstantAddress tryEmitGlobalCompoundLiteral(CodeGenModule &CGM,
+ CodeGenFunction *CGF,
+ const CompoundLiteralExpr *E) {
+ CharUnits Align = CGM.getContext().getTypeAlignInChars(E->getType());
+ if (llvm::GlobalVariable *Addr =
+ CGM.getAddrOfConstantCompoundLiteralIfEmitted(E))
+ return ConstantAddress(Addr, Align);
+
+ unsigned addressSpace = E->getType().getAddressSpace();
+
+ ConstantEmitter emitter(CGM, CGF);
+ llvm::Constant *C = emitter.tryEmitForInitializer(E->getInitializer(),
+ addressSpace, E->getType());
+ if (!C) {
+ assert(!E->isFileScope() &&
+ "file-scope compound literal did not have constant initializer!");
+ return ConstantAddress::invalid();
+ }
+
+ auto GV = new llvm::GlobalVariable(CGM.getModule(), C->getType(),
+ CGM.isTypeConstant(E->getType(), true),
+ llvm::GlobalValue::InternalLinkage,
+ C, ".compoundliteral", nullptr,
+ llvm::GlobalVariable::NotThreadLocal,
+ CGM.getContext().getTargetAddressSpace(addressSpace));
+ emitter.finalize(GV);
+ GV->setAlignment(Align.getQuantity());
+ CGM.setAddrOfConstantCompoundLiteral(E, GV);
+ return ConstantAddress(GV, Align);
+}
+
/// This class only needs to handle two cases:
/// 1) Literals (this is used by APValue emission to emit literals).
/// 2) Arrays, structs and unions (outside C++11 mode, we don't currently
/// constant fold these types).
class ConstExprEmitter :
- public StmtVisitor<ConstExprEmitter, llvm::Constant*> {
+ public StmtVisitor<ConstExprEmitter, llvm::Constant*, QualType> {
CodeGenModule &CGM;
- CodeGenFunction *CGF;
+ ConstantEmitter &Emitter;
llvm::LLVMContext &VMContext;
public:
- ConstExprEmitter(CodeGenModule &cgm, CodeGenFunction *cgf)
- : CGM(cgm), CGF(cgf), VMContext(cgm.getLLVMContext()) {
+ ConstExprEmitter(ConstantEmitter &emitter)
+ : CGM(emitter.CGM), Emitter(emitter), VMContext(CGM.getLLVMContext()) {
}
//===--------------------------------------------------------------------===//
// Visitor Methods
//===--------------------------------------------------------------------===//
- llvm::Constant *VisitStmt(Stmt *S) {
+ llvm::Constant *VisitStmt(Stmt *S, QualType T) {
return nullptr;
}
- llvm::Constant *VisitParenExpr(ParenExpr *PE) {
- return Visit(PE->getSubExpr());
+ llvm::Constant *VisitParenExpr(ParenExpr *PE, QualType T) {
+ return Visit(PE->getSubExpr(), T);
}
llvm::Constant *
- VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *PE) {
- return Visit(PE->getReplacement());
+ VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *PE,
+ QualType T) {
+ return Visit(PE->getReplacement(), T);
}
- llvm::Constant *VisitGenericSelectionExpr(GenericSelectionExpr *GE) {
- return Visit(GE->getResultExpr());
+ llvm::Constant *VisitGenericSelectionExpr(GenericSelectionExpr *GE,
+ QualType T) {
+ return Visit(GE->getResultExpr(), T);
}
- llvm::Constant *VisitChooseExpr(ChooseExpr *CE) {
- return Visit(CE->getChosenSubExpr());
+ llvm::Constant *VisitChooseExpr(ChooseExpr *CE, QualType T) {
+ return Visit(CE->getChosenSubExpr(), T);
}
- llvm::Constant *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
- return Visit(E->getInitializer());
+ llvm::Constant *VisitCompoundLiteralExpr(CompoundLiteralExpr *E, QualType T) {
+ return Visit(E->getInitializer(), T);
}
- llvm::Constant *VisitCastExpr(CastExpr* E) {
+ llvm::Constant *VisitCastExpr(CastExpr *E, QualType destType) {
if (const auto *ECE = dyn_cast<ExplicitCastExpr>(E))
- CGM.EmitExplicitCastExprType(ECE, CGF);
+ CGM.EmitExplicitCastExprType(ECE, Emitter.CGF);
Expr *subExpr = E->getSubExpr();
- llvm::Constant *C = CGM.EmitConstantExpr(subExpr, subExpr->getType(), CGF);
- if (!C) return nullptr;
-
- llvm::Type *destType = ConvertType(E->getType());
switch (E->getCastKind()) {
case CK_ToUnion: {
@@ -657,14 +691,22 @@
assert(E->getType()->isUnionType() &&
"Destination type is not union type!");
+ auto field = E->getTargetUnionField();
+
+ auto C = Emitter.tryEmitPrivateForMemory(subExpr, field->getType());
+ if (!C) return nullptr;
+
+ auto destTy = ConvertType(destType);
+ if (C->getType() == destTy) return C;
+
// Build a struct with the union sub-element as the first member,
- // and padded to the appropriate size
+ // and padded to the appropriate size.
SmallVector<llvm::Constant*, 2> Elts;
SmallVector<llvm::Type*, 2> Types;
Elts.push_back(C);
Types.push_back(C->getType());
unsigned CurSize = CGM.getDataLayout().getTypeAllocSize(C->getType());
- unsigned TotalSize = CGM.getDataLayout().getTypeAllocSize(destType);
+ unsigned TotalSize = CGM.getDataLayout().getTypeAllocSize(destTy);
assert(CurSize <= TotalSize && "Union size mismatch!");
if (unsigned NumPadBytes = TotalSize - CurSize) {
@@ -676,20 +718,26 @@
Types.push_back(Ty);
}
- llvm::StructType* STy =
- llvm::StructType::get(C->getType()->getContext(), Types, false);
+ llvm::StructType *STy = llvm::StructType::get(VMContext, Types, false);
return llvm::ConstantStruct::get(STy, Elts);
}
- case CK_AddressSpaceConversion:
- return llvm::ConstantExpr::getAddrSpaceCast(C, destType);
+ case CK_AddressSpaceConversion: {
+ auto C = Emitter.tryEmitPrivate(subExpr, subExpr->getType());
+ if (!C) return nullptr;
+ unsigned destAS = E->getType()->getPointeeType().getAddressSpace();
+ unsigned srcAS = subExpr->getType()->getPointeeType().getAddressSpace();
+ llvm::Type *destTy = ConvertType(E->getType());
+ return CGM.getTargetCodeGenInfo().performAddrSpaceCast(CGM, C, srcAS,
+ destAS, destTy);
+ }
case CK_LValueToRValue:
case CK_AtomicToNonAtomic:
case CK_NonAtomicToAtomic:
case CK_NoOp:
case CK_ConstructorConversion:
- return C;
+ return Visit(subExpr, destType);
case CK_IntToOCLSampler:
llvm_unreachable("global sampler variables are not generated");
@@ -701,8 +749,11 @@
case CK_ReinterpretMemberPointer:
case CK_DerivedToBaseMemberPointer:
- case CK_BaseToDerivedMemberPointer:
+ case CK_BaseToDerivedMemberPointer: {
+ auto C = Emitter.tryEmitPrivate(subExpr, subExpr->getType());
+ if (!C) return nullptr;
return CGM.getCXXABI().EmitMemberPointerConversion(E, C);
+ }
// These will never be supported.
case CK_ObjCObjectLValueCast:
@@ -759,27 +810,28 @@
llvm_unreachable("Invalid CastKind");
}
- llvm::Constant *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE) {
- return Visit(DAE->getExpr());
+ llvm::Constant *VisitCXXDefaultArgExpr(CXXDefaultArgExpr *DAE, QualType T) {
+ return Visit(DAE->getExpr(), T);
}
- llvm::Constant *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE) {
+ llvm::Constant *VisitCXXDefaultInitExpr(CXXDefaultInitExpr *DIE, QualType T) {
// No need for a DefaultInitExprScope: we don't handle 'this' in a
// constant expression.
- return Visit(DIE->getExpr());
+ return Visit(DIE->getExpr(), T);
}
- llvm::Constant *VisitExprWithCleanups(ExprWithCleanups *E) {
+ llvm::Constant *VisitExprWithCleanups(ExprWithCleanups *E, QualType T) {
if (!E->cleanupsHaveSideEffects())
- return Visit(E->getSubExpr());
+ return Visit(E->getSubExpr(), T);
return nullptr;
}
- llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
- return Visit(E->GetTemporaryExpr());
+ llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E,
+ QualType T) {
+ return Visit(E->GetTemporaryExpr(), T);
}
- llvm::Constant *EmitArrayInitialization(InitListExpr *ILE) {
+ llvm::Constant *EmitArrayInitialization(InitListExpr *ILE, QualType T) {
llvm::ArrayType *AType =
cast<llvm::ArrayType>(ConvertType(ILE->getType()));
llvm::Type *ElemTy = AType->getElementType();
@@ -790,13 +842,14 @@
// initialise any elements that have not been initialised explicitly
unsigned NumInitableElts = std::min(NumInitElements, NumElements);
+ QualType EltType = CGM.getContext().getAsArrayType(T)->getElementType();
+
// Initialize remaining array elements.
- // FIXME: This doesn't handle member pointers correctly!
llvm::Constant *fillC;
if (Expr *filler = ILE->getArrayFiller())
- fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF);
+ fillC = Emitter.tryEmitAbstractForMemory(filler, EltType);
else
- fillC = llvm::Constant::getNullValue(ElemTy);
+ fillC = Emitter.emitNullForMemory(EltType);
if (!fillC)
return nullptr;
@@ -805,13 +858,13 @@
return llvm::ConstantAggregateZero::get(AType);
// Copy initializer elements.
- std::vector<llvm::Constant*> Elts;
+ SmallVector<llvm::Constant*, 16> Elts;
Elts.reserve(NumInitableElts + NumElements);
bool RewriteType = false;
for (unsigned i = 0; i < NumInitableElts; ++i) {
Expr *Init = ILE->getInit(i);
- llvm::Constant *C = CGM.EmitConstantExpr(Init, Init->getType(), CGF);
+ llvm::Constant *C = Emitter.tryEmitPrivateForMemory(Init, EltType);
if (!C)
return nullptr;
RewriteType |= (C->getType() != ElemTy);
@@ -835,33 +888,33 @@
return llvm::ConstantArray::get(AType, Elts);
}
- llvm::Constant *EmitRecordInitialization(InitListExpr *ILE) {
- return ConstStructBuilder::BuildStruct(CGM, CGF, ILE);
+ llvm::Constant *EmitRecordInitialization(InitListExpr *ILE, QualType T) {
+ return ConstStructBuilder::BuildStruct(Emitter, ILE, T);
}
- llvm::Constant *VisitImplicitValueInitExpr(ImplicitValueInitExpr* E) {
- return CGM.EmitNullConstant(E->getType());
+ llvm::Constant *VisitImplicitValueInitExpr(ImplicitValueInitExpr* E,
+ QualType T) {
+ return CGM.EmitNullConstant(T);
}
- llvm::Constant *VisitInitListExpr(InitListExpr *ILE) {
+ llvm::Constant *VisitInitListExpr(InitListExpr *ILE, QualType T) {
if (ILE->isTransparent())
- return Visit(ILE->getInit(0));
+ return Visit(ILE->getInit(0), T);
if (ILE->getType()->isArrayType())
- return EmitArrayInitialization(ILE);
+ return EmitArrayInitialization(ILE, T);
if (ILE->getType()->isRecordType())
- return EmitRecordInitialization(ILE);
+ return EmitRecordInitialization(ILE, T);
return nullptr;
}
llvm::Constant *EmitDesignatedInitUpdater(llvm::Constant *Base,
- InitListExpr *Updater) {
- QualType ExprType = Updater->getType();
-
- if (ExprType->isArrayType()) {
- llvm::ArrayType *AType = cast<llvm::ArrayType>(ConvertType(ExprType));
+ InitListExpr *Updater,
+ QualType destType) {
+ if (auto destAT = CGM.getContext().getAsArrayType(destType)) {
+ llvm::ArrayType *AType = cast<llvm::ArrayType>(ConvertType(destType));
llvm::Type *ElemType = AType->getElementType();
unsigned NumInitElements = Updater->getNumInits();
@@ -870,12 +923,12 @@
std::vector<llvm::Constant *> Elts;
Elts.reserve(NumElements);
- if (llvm::ConstantDataArray *DataArray =
- dyn_cast<llvm::ConstantDataArray>(Base))
+ QualType destElemType = destAT->getElementType();
+
+ if (auto DataArray = dyn_cast<llvm::ConstantDataArray>(Base))
for (unsigned i = 0; i != NumElements; ++i)
Elts.push_back(DataArray->getElementAsConstant(i));
- else if (llvm::ConstantArray *Array =
- dyn_cast<llvm::ConstantArray>(Base))
+ else if (auto Array = dyn_cast<llvm::ConstantArray>(Base))
for (unsigned i = 0; i != NumElements; ++i)
Elts.push_back(Array->getOperand(i));
else
@@ -884,7 +937,7 @@
llvm::Constant *fillC = nullptr;
if (Expr *filler = Updater->getArrayFiller())
if (!isa<NoInitExpr>(filler))
- fillC = CGM.EmitConstantExpr(filler, filler->getType(), CGF);
+ fillC = Emitter.tryEmitAbstractForMemory(filler, destElemType);
bool RewriteType = (fillC && fillC->getType() != ElemType);
for (unsigned i = 0; i != NumElements; ++i) {
@@ -897,9 +950,9 @@
else if (!Init || isa<NoInitExpr>(Init))
; // Do nothing.
else if (InitListExpr *ChildILE = dyn_cast<InitListExpr>(Init))
- Elts[i] = EmitDesignatedInitUpdater(Elts[i], ChildILE);
+ Elts[i] = EmitDesignatedInitUpdater(Elts[i], ChildILE, destElemType);
else
- Elts[i] = CGM.EmitConstantExpr(Init, Init->getType(), CGF);
+ Elts[i] = Emitter.tryEmitPrivateForMemory(Init, destElemType);
if (!Elts[i])
return nullptr;
@@ -919,25 +972,24 @@
return llvm::ConstantArray::get(AType, Elts);
}
- if (ExprType->isRecordType())
- return ConstStructBuilder::BuildStruct(CGM, CGF, this,
- dyn_cast<llvm::ConstantStruct>(Base), Updater);
+ if (destType->isRecordType())
+ return ConstStructBuilder::BuildStruct(Emitter, this,
+ dyn_cast<llvm::ConstantStruct>(Base), Updater, destType);
return nullptr;
}
- llvm::Constant *VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E) {
- return EmitDesignatedInitUpdater(
- CGM.EmitConstantExpr(E->getBase(), E->getType(), CGF),
- E->getUpdater());
+ llvm::Constant *VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *E,
+ QualType destType) {
+ auto C = Visit(E->getBase(), destType);
+ if (!C) return nullptr;
+ return EmitDesignatedInitUpdater(C, E->getUpdater(), destType);
}
- llvm::Constant *VisitCXXConstructExpr(CXXConstructExpr *E) {
+ llvm::Constant *VisitCXXConstructExpr(CXXConstructExpr *E, QualType Ty) {
if (!E->getConstructor()->isTrivial())
return nullptr;
- QualType Ty = E->getType();
-
// FIXME: We should not have to call getBaseElementType here.
const RecordType *RT =
CGM.getContext().getBaseElementType(Ty)->getAs<RecordType>();
@@ -960,26 +1012,23 @@
assert(CGM.getContext().hasSameUnqualifiedType(Ty, Arg->getType()) &&
"argument to copy ctor is of wrong type");
- return Visit(Arg);
+ return Visit(Arg, Ty);
}
return CGM.EmitNullConstant(Ty);
}
- llvm::Constant *VisitStringLiteral(StringLiteral *E) {
+ llvm::Constant *VisitStringLiteral(StringLiteral *E, QualType T) {
return CGM.GetConstantArrayFromStringLiteral(E);
}
- llvm::Constant *VisitObjCEncodeExpr(ObjCEncodeExpr *E) {
+ llvm::Constant *VisitObjCEncodeExpr(ObjCEncodeExpr *E, QualType T) {
// This must be an @encode initializing an array in a static initializer.
// Don't emit it as the address of the string, emit the string data itself
// as an inline array.
std::string Str;
CGM.getContext().getObjCEncodingForType(E->getEncodedType(), Str);
- QualType T = E->getType();
- if (T->getTypeClass() == Type::TypeOfExpr)
- T = cast<TypeOfExprType>(T)->getUnderlyingExpr()->getType();
- const ConstantArrayType *CAT = cast<ConstantArrayType>(T);
+ const ConstantArrayType *CAT = CGM.getContext().getAsConstantArrayType(T);
// Resize the string to the right size, adding zeros at the end, or
// truncating as needed.
@@ -987,151 +1036,19 @@
return llvm::ConstantDataArray::getString(VMContext, Str, false);
}
- llvm::Constant *VisitUnaryExtension(const UnaryOperator *E) {
- return Visit(E->getSubExpr());
+ llvm::Constant *VisitUnaryExtension(const UnaryOperator *E, QualType T) {
+ return Visit(E->getSubExpr(), T);
}
// Utility methods
llvm::Type *ConvertType(QualType T) {
return CGM.getTypes().ConvertType(T);
}
-
-public:
- ConstantAddress EmitLValue(APValue::LValueBase LVBase) {
- if (const ValueDecl *Decl = LVBase.dyn_cast<const ValueDecl*>()) {
- if (Decl->hasAttr<WeakRefAttr>())
- return CGM.GetWeakRefReference(Decl);
- if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Decl))
- return ConstantAddress(CGM.GetAddrOfFunction(FD), CharUnits::One());
- if (const VarDecl* VD = dyn_cast<VarDecl>(Decl)) {
- // We can never refer to a variable with local storage.
- if (!VD->hasLocalStorage()) {
- CharUnits Align = CGM.getContext().getDeclAlign(VD);
- if (VD->isFileVarDecl() || VD->hasExternalStorage())
- return ConstantAddress(CGM.GetAddrOfGlobalVar(VD), Align);
- else if (VD->isLocalVarDecl()) {
- auto Ptr = CGM.getOrCreateStaticVarDecl(
- *VD, CGM.getLLVMLinkageVarDefinition(VD, /*isConstant=*/false));
- return ConstantAddress(Ptr, Align);
- }
- }
- }
- return ConstantAddress::invalid();
- }
-
- Expr *E = const_cast<Expr*>(LVBase.get<const Expr*>());
- switch (E->getStmtClass()) {
- default: break;
- case Expr::CompoundLiteralExprClass: {
- CompoundLiteralExpr *CLE = cast<CompoundLiteralExpr>(E);
- CharUnits Align = CGM.getContext().getTypeAlignInChars(E->getType());
- if (llvm::GlobalVariable *Addr =
- CGM.getAddrOfConstantCompoundLiteralIfEmitted(CLE))
- return ConstantAddress(Addr, Align);
-
- llvm::Constant* C = CGM.EmitConstantExpr(CLE->getInitializer(),
- CLE->getType(), CGF);
- // FIXME: "Leaked" on failure.
- if (!C) return ConstantAddress::invalid();
-
- auto GV = new llvm::GlobalVariable(CGM.getModule(), C->getType(),
- E->getType().isConstant(CGM.getContext()),
- llvm::GlobalValue::InternalLinkage,
- C, ".compoundliteral", nullptr,
- llvm::GlobalVariable::NotThreadLocal,
- CGM.getContext().getTargetAddressSpace(E->getType()));
- GV->setAlignment(Align.getQuantity());
- CGM.setAddrOfConstantCompoundLiteral(CLE, GV);
- return ConstantAddress(GV, Align);
- }
- case Expr::StringLiteralClass:
- return CGM.GetAddrOfConstantStringFromLiteral(cast<StringLiteral>(E));
- case Expr::ObjCEncodeExprClass:
- return CGM.GetAddrOfConstantStringFromObjCEncode(cast<ObjCEncodeExpr>(E));
- case Expr::ObjCStringLiteralClass: {
- ObjCStringLiteral* SL = cast<ObjCStringLiteral>(E);
- ConstantAddress C =
- CGM.getObjCRuntime().GenerateConstantString(SL->getString());
- return C.getElementBitCast(ConvertType(E->getType()));
- }
- case Expr::PredefinedExprClass: {
- unsigned Type = cast<PredefinedExpr>(E)->getIdentType();
- if (CGF) {
- LValue Res = CGF->EmitPredefinedLValue(cast<PredefinedExpr>(E));
- return cast<ConstantAddress>(Res.getAddress());
- } else if (Type == PredefinedExpr::PrettyFunction) {
- return CGM.GetAddrOfConstantCString("top level", ".tmp");
- }
-
- return CGM.GetAddrOfConstantCString("", ".tmp");
- }
- case Expr::AddrLabelExprClass: {
- assert(CGF && "Invalid address of label expression outside function.");
- llvm::Constant *Ptr =
- CGF->GetAddrOfLabel(cast<AddrLabelExpr>(E)->getLabel());
- Ptr = llvm::ConstantExpr::getBitCast(Ptr, ConvertType(E->getType()));
- return ConstantAddress(Ptr, CharUnits::One());
- }
- case Expr::CallExprClass: {
- CallExpr* CE = cast<CallExpr>(E);
- unsigned builtin = CE->getBuiltinCallee();
- if (builtin !=
- Builtin::BI__builtin___CFStringMakeConstantString &&
- builtin !=
- Builtin::BI__builtin___NSStringMakeConstantString)
- break;
- const Expr *Arg = CE->getArg(0)->IgnoreParenCasts();
- const StringLiteral *Literal = cast<StringLiteral>(Arg);
- if (builtin ==
- Builtin::BI__builtin___NSStringMakeConstantString) {
- return CGM.getObjCRuntime().GenerateConstantString(Literal);
- }
- // FIXME: need to deal with UCN conversion issues.
- return CGM.GetAddrOfConstantCFString(Literal);
- }
- case Expr::BlockExprClass: {
- StringRef FunctionName;
- if (CGF)
- FunctionName = CGF->CurFn->getName();
- else
- FunctionName = "global";
-
- // This is not really an l-value.
- llvm::Constant *Ptr =
- CGM.GetAddrOfGlobalBlock(cast<BlockExpr>(E), FunctionName);
- return ConstantAddress(Ptr, CGM.getPointerAlign());
- }
- case Expr::CXXTypeidExprClass: {
- CXXTypeidExpr *Typeid = cast<CXXTypeidExpr>(E);
- QualType T;
- if (Typeid->isTypeOperand())
- T = Typeid->getTypeOperand(CGM.getContext());
- else
- T = Typeid->getExprOperand()->getType();
- return ConstantAddress(CGM.GetAddrOfRTTIDescriptor(T),
- CGM.getPointerAlign());
- }
- case Expr::CXXUuidofExprClass: {
- return CGM.GetAddrOfUuidDescriptor(cast<CXXUuidofExpr>(E));
- }
- case Expr::MaterializeTemporaryExprClass: {
- MaterializeTemporaryExpr *MTE = cast<MaterializeTemporaryExpr>(E);
- assert(MTE->getStorageDuration() == SD_Static);
- SmallVector<const Expr *, 2> CommaLHSs;
- SmallVector<SubobjectAdjustment, 2> Adjustments;
- const Expr *Inner = MTE->GetTemporaryExpr()
- ->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
- return CGM.GetAddrOfGlobalTemporary(MTE, Inner);
- }
- }
-
- return ConstantAddress::invalid();
- }
};
} // end anonymous namespace.
-bool ConstStructBuilder::Build(ConstExprEmitter *Emitter,
+bool ConstStructBuilder::Build(ConstExprEmitter *ExprEmitter,
llvm::ConstantStruct *Base,
InitListExpr *Updater) {
assert(Base && "base expression should not be empty");
@@ -1179,9 +1096,10 @@
if (!Init || isa<NoInitExpr>(Init))
; // Do nothing.
else if (InitListExpr *ChildILE = dyn_cast<InitListExpr>(Init))
- EltInit = Emitter->EmitDesignatedInitUpdater(EltInit, ChildILE);
+ EltInit = ExprEmitter->EmitDesignatedInitUpdater(EltInit, ChildILE,
+ Field->getType());
else
- EltInit = CGM.EmitConstantExpr(Init, Field->getType(), CGF);
+ EltInit = Emitter.tryEmitPrivateForMemory(Init, Field->getType());
++ElementNo;
@@ -1200,26 +1118,294 @@
return true;
}
-llvm::Constant *CodeGenModule::EmitConstantInit(const VarDecl &D,
- CodeGenFunction *CGF) {
+llvm::Constant *ConstantEmitter::validateAndPopAbstract(llvm::Constant *C,
+ AbstractState saved) {
+ Abstract = saved.OldValue;
+
+ assert(saved.OldPlaceholdersSize == PlaceholderAddresses.size() &&
+ "created a placeholder while doing an abstract emission?");
+
+ // No validation necessary for now.
+ // No cleanup to do for now.
+ return C;
+}
+
+llvm::Constant *
+ConstantEmitter::tryEmitAbstractForInitializer(const VarDecl &D) {
+ auto state = pushAbstract();
+ auto C = tryEmitPrivateForVarInit(D);
+ return validateAndPopAbstract(C, state);
+}
+
+llvm::Constant *
+ConstantEmitter::tryEmitAbstract(const Expr *E, QualType destType) {
+ auto state = pushAbstract();
+ auto C = tryEmitPrivate(E, destType);
+ return validateAndPopAbstract(C, state);
+}
+
+llvm::Constant *
+ConstantEmitter::tryEmitAbstract(const APValue &value, QualType destType) {
+ auto state = pushAbstract();
+ auto C = tryEmitPrivate(value, destType);
+ return validateAndPopAbstract(C, state);
+}
+
+llvm::Constant *
+ConstantEmitter::emitAbstract(const Expr *E, QualType destType) {
+ auto state = pushAbstract();
+ auto C = tryEmitPrivate(E, destType);
+ C = validateAndPopAbstract(C, state);
+ if (!C) {
+ CGM.Error(E->getExprLoc(),
+ "internal error: could not emit constant value \"abstractly\"");
+ C = CGM.EmitNullConstant(destType);
+ }
+ return C;
+}
+
+llvm::Constant *
+ConstantEmitter::emitAbstract(SourceLocation loc, const APValue &value,
+ QualType destType) {
+ auto state = pushAbstract();
+ auto C = tryEmitPrivate(value, destType);
+ C = validateAndPopAbstract(C, state);
+ if (!C) {
+ CGM.Error(loc,
+ "internal error: could not emit constant value \"abstractly\"");
+ C = CGM.EmitNullConstant(destType);
+ }
+ return C;
+}
+
+llvm::Constant *ConstantEmitter::tryEmitForInitializer(const VarDecl &D) {
+ initializeNonAbstract(D.getType().getAddressSpace());
+ return markIfFailed(tryEmitPrivateForVarInit(D));
+}
+
+llvm::Constant *ConstantEmitter::tryEmitForInitializer(const Expr *E,
+ unsigned destAddrSpace,
+ QualType destType) {
+ initializeNonAbstract(destAddrSpace);
+ return markIfFailed(tryEmitPrivateForMemory(E, destType));
+}
+
+llvm::Constant *ConstantEmitter::emitForInitializer(const APValue &value,
+ unsigned destAddrSpace,
+ QualType destType) {
+ initializeNonAbstract(destAddrSpace);
+ auto C = tryEmitPrivateForMemory(value, destType);
+ assert(C && "couldn't emit constant value non-abstractly?");
+ return C;
+}
+
+llvm::GlobalValue *ConstantEmitter::getCurrentAddrPrivate() {
+ assert(!Abstract && "cannot get current address for abstract constant");
+
+
+
+ // Make an obviously ill-formed global that should blow up compilation
+ // if it survives.
+ auto global = new llvm::GlobalVariable(CGM.getModule(), CGM.Int8Ty, true,
+ llvm::GlobalValue::PrivateLinkage,
+ /*init*/ nullptr,
+ /*name*/ "",
+ /*before*/ nullptr,
+ llvm::GlobalVariable::NotThreadLocal,
+ CGM.getContext().getTargetAddressSpace(DestAddressSpace));
+
+ PlaceholderAddresses.push_back(std::make_pair(nullptr, global));
+
+ return global;
+}
+
+void ConstantEmitter::registerCurrentAddrPrivate(llvm::Constant *signal,
+ llvm::GlobalValue *placeholder) {
+ assert(!PlaceholderAddresses.empty());
+ assert(PlaceholderAddresses.back().first == nullptr);
+ assert(PlaceholderAddresses.back().second == placeholder);
+ PlaceholderAddresses.back().first = signal;
+}
+
+namespace {
+ struct ReplacePlaceholders {
+ CodeGenModule &CGM;
+
+ /// The base address of the global.
+ llvm::Constant *Base;
+ llvm::Type *BaseValueTy = nullptr;
+
+ /// The placeholder addresses that were registered during emission.
+ llvm::DenseMap<llvm::Constant*, llvm::GlobalVariable*> PlaceholderAddresses;
+
+ /// The locations of the placeholder signals.
+ llvm::DenseMap<llvm::GlobalVariable*, llvm::Constant*> Locations;
+
+ /// The current index stack. We use a simple unsigned stack because
+ /// we assume that placeholders will be relatively sparse in the
+ /// initializer, but we cache the index values we find just in case.
+ llvm::SmallVector<unsigned, 8> Indices;
+ llvm::SmallVector<llvm::Constant*, 8> IndexValues;
+
+ ReplacePlaceholders(CodeGenModule &CGM, llvm::Constant *base,
+ ArrayRef<std::pair<llvm::Constant*,
+ llvm::GlobalVariable*>> addresses)
+ : CGM(CGM), Base(base),
+ PlaceholderAddresses(addresses.begin(), addresses.end()) {
+ }
+
+ void replaceInInitializer(llvm::Constant *init) {
+ // Remember the type of the top-most initializer.
+ BaseValueTy = init->getType();
+
+ // Initialize the stack.
+ Indices.push_back(0);
+ IndexValues.push_back(nullptr);
+
+ // Recurse into the initializer.
+ findLocations(init);
+
+ // Check invariants.
+ assert(IndexValues.size() == Indices.size() && "mismatch");
+ assert(Indices.size() == 1 && "didn't pop all indices");
+
+ // Do the replacement; this basically invalidates 'init'.
+ assert(Locations.size() == PlaceholderAddresses.size() &&
+ "missed a placeholder?");
+
+ // We're iterating over a hashtable, so this would be a source of
+ // non-determinism in compiler output *except* that we're just
+ // messing around with llvm::Constant structures, which never itself
+ // does anything that should be visible in compiler output.
+ for (auto &entry : Locations) {
+ assert(entry.first->getParent() == nullptr && "not a placeholder!");
+ entry.first->replaceAllUsesWith(entry.second);
+ entry.first->eraseFromParent();
+ }
+ }
+
+ private:
+ void findLocations(llvm::Constant *init) {
+ // Recurse into aggregates.
+ if (auto agg = dyn_cast<llvm::ConstantAggregate>(init)) {
+ for (unsigned i = 0, e = agg->getNumOperands(); i != e; ++i) {
+ Indices.push_back(i);
+ IndexValues.push_back(nullptr);
+
+ findLocations(agg->getOperand(i));
+
+ IndexValues.pop_back();
+ Indices.pop_back();
+ }
+ return;
+ }
+
+ // Otherwise, check for registered constants.
+ while (true) {
+ auto it = PlaceholderAddresses.find(init);
+ if (it != PlaceholderAddresses.end()) {
+ setLocation(it->second);
+ break;
+ }
+
+ // Look through bitcasts or other expressions.
+ if (auto expr = dyn_cast<llvm::ConstantExpr>(init)) {
+ init = expr->getOperand(0);
+ } else {
+ break;
+ }
+ }
+ }
+
+ void setLocation(llvm::GlobalVariable *placeholder) {
+ assert(Locations.find(placeholder) == Locations.end() &&
+ "already found location for placeholder!");
+
+ // Lazily fill in IndexValues with the values from Indices.
+ // We do this in reverse because we should always have a strict
+ // prefix of indices from the start.
+ assert(Indices.size() == IndexValues.size());
+ for (size_t i = Indices.size() - 1; i != size_t(-1); --i) {
+ if (IndexValues[i]) {
+#ifndef NDEBUG
+ for (size_t j = 0; j != i + 1; ++j) {
+ assert(IndexValues[j] &&
+ isa<llvm::ConstantInt>(IndexValues[j]) &&
+ cast<llvm::ConstantInt>(IndexValues[j])->getZExtValue()
+ == Indices[j]);
+ }
+#endif
+ break;
+ }
+
+ IndexValues[i] = llvm::ConstantInt::get(CGM.Int32Ty, Indices[i]);
+ }
+
+ // Form a GEP and then bitcast to the placeholder type so that the
+ // replacement will succeed.
+ llvm::Constant *location =
+ llvm::ConstantExpr::getInBoundsGetElementPtr(BaseValueTy,
+ Base, IndexValues);
+ location = llvm::ConstantExpr::getBitCast(location,
+ placeholder->getType());
+
+ Locations.insert({placeholder, location});
+ }
+ };
+}
+
+void ConstantEmitter::finalize(llvm::GlobalVariable *global) {
+ assert(InitializedNonAbstract &&
+ "finalizing emitter that was used for abstract emission?");
+ assert(!Finalized && "finalizing emitter multiple times");
+ assert(global->getInitializer());
+
+ // Note that we might also be Failed.
+ Finalized = true;
+
+ if (!PlaceholderAddresses.empty()) {
+ ReplacePlaceholders(CGM, global, PlaceholderAddresses)
+ .replaceInInitializer(global->getInitializer());
+ PlaceholderAddresses.clear(); // satisfy
+ }
+}
+
+ConstantEmitter::~ConstantEmitter() {
+ assert((!InitializedNonAbstract || Finalized || Failed) &&
+ "not finalized after being initialized for non-abstract emission");
+ assert(PlaceholderAddresses.empty() && "unhandled placeholders");
+}
+
+static QualType getNonMemoryType(CodeGenModule &CGM, QualType type) {
+ if (auto AT = type->getAs<AtomicType>()) {
+ return CGM.getContext().getQualifiedType(AT->getValueType(),
+ type.getQualifiers());
+ }
+ return type;
+}
+
+llvm::Constant *ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &D) {
// Make a quick check if variable can be default NULL initialized
// and avoid going through rest of code which may do, for c++11,
// initialization of memory to all NULLs.
if (!D.hasLocalStorage()) {
- QualType Ty = D.getType();
- if (Ty->isArrayType())
- Ty = Context.getBaseElementType(Ty);
+ QualType Ty = CGM.getContext().getBaseElementType(D.getType());
if (Ty->isRecordType())
if (const CXXConstructExpr *E =
dyn_cast_or_null<CXXConstructExpr>(D.getInit())) {
const CXXConstructorDecl *CD = E->getConstructor();
if (CD->isTrivial() && CD->isDefaultConstructor())
- return EmitNullConstant(D.getType());
+ return CGM.EmitNullConstant(D.getType());
}
}
-
- if (const APValue *Value = D.evaluateValue())
- return EmitConstantValueForMemory(*Value, D.getType(), CGF);
+
+ QualType destType = D.getType();
+
+ // Try to emit the initializer. Note that this can allow some things that
+ // are not allowed by tryEmitPrivateForMemory alone.
+ if (auto value = D.evaluateValue()) {
+ return tryEmitPrivateForMemory(*value, destType);
+ }
// FIXME: Implement C++11 [basic.start.init]p2: if the initializer of a
// reference is a constant expression, and the reference binds to a temporary,
@@ -1227,42 +1413,95 @@
// incorrectly emit a prvalue constant in this case, and the calling code
// interprets that as the (pointer) value of the reference, rather than the
// desired value of the referee.
- if (D.getType()->isReferenceType())
+ if (destType->isReferenceType())
return nullptr;
const Expr *E = D.getInit();
assert(E && "No initializer to emit");
- llvm::Constant* C = ConstExprEmitter(*this, CGF).Visit(const_cast<Expr*>(E));
- if (C && C->getType()->isIntegerTy(1)) {
- llvm::Type *BoolTy = getTypes().ConvertTypeForMem(E->getType());
- C = llvm::ConstantExpr::getZExt(C, BoolTy);
+ auto nonMemoryDestType = getNonMemoryType(CGM, destType);
+ auto C =
+ ConstExprEmitter(*this).Visit(const_cast<Expr*>(E), nonMemoryDestType);
+ return (C ? emitForMemory(C, destType) : nullptr);
+}
+
+llvm::Constant *
+ConstantEmitter::tryEmitAbstractForMemory(const Expr *E, QualType destType) {
+ auto nonMemoryDestType = getNonMemoryType(CGM, destType);
+ auto C = tryEmitAbstract(E, nonMemoryDestType);
+ return (C ? emitForMemory(C, destType) : nullptr);
+}
+
+llvm::Constant *
+ConstantEmitter::tryEmitAbstractForMemory(const APValue &value,
+ QualType destType) {
+ auto nonMemoryDestType = getNonMemoryType(CGM, destType);
+ auto C = tryEmitAbstract(value, nonMemoryDestType);
+ return (C ? emitForMemory(C, destType) : nullptr);
+}
+
+llvm::Constant *ConstantEmitter::tryEmitPrivateForMemory(const Expr *E,
+ QualType destType) {
+ auto nonMemoryDestType = getNonMemoryType(CGM, destType);
+ llvm::Constant *C = tryEmitPrivate(E, nonMemoryDestType);
+ return (C ? emitForMemory(C, destType) : nullptr);
+}
+
+llvm::Constant *ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
+ QualType destType) {
+ auto nonMemoryDestType = getNonMemoryType(CGM, destType);
+ auto C = tryEmitPrivate(value, nonMemoryDestType);
+ return (C ? emitForMemory(C, destType) : nullptr);
+}
+
+llvm::Constant *ConstantEmitter::emitForMemory(CodeGenModule &CGM,
+ llvm::Constant *C,
+ QualType destType) {
+ // For an _Atomic-qualified constant, we may need to add tail padding.
+ if (auto AT = destType->getAs<AtomicType>()) {
+ QualType destValueType = AT->getValueType();
+ C = emitForMemory(CGM, C, destValueType);
+
+ uint64_t innerSize = CGM.getContext().getTypeSize(destValueType);
+ uint64_t outerSize = CGM.getContext().getTypeSize(destType);
+ if (innerSize == outerSize)
+ return C;
+
+ assert(innerSize < outerSize && "emitted over-large constant for atomic");
+ llvm::Constant *elts[] = {
+ C,
+ llvm::ConstantAggregateZero::get(
+ llvm::ArrayType::get(CGM.Int8Ty, (outerSize - innerSize) / 8))
+ };
+ return llvm::ConstantStruct::getAnon(elts);
}
+
+ // Zero-extend bool.
+ if (C->getType()->isIntegerTy(1)) {
+ llvm::Type *boolTy = CGM.getTypes().ConvertTypeForMem(destType);
+ return llvm::ConstantExpr::getZExt(C, boolTy);
+ }
+
return C;
}
-llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E,
- QualType DestType,
- CodeGenFunction *CGF) {
+llvm::Constant *ConstantEmitter::tryEmitPrivate(const Expr *E,
+ QualType destType) {
Expr::EvalResult Result;
bool Success = false;
- if (DestType->isReferenceType())
- Success = E->EvaluateAsLValue(Result, Context);
+ if (destType->isReferenceType())
+ Success = E->EvaluateAsLValue(Result, CGM.getContext());
else
- Success = E->EvaluateAsRValue(Result, Context);
+ Success = E->EvaluateAsRValue(Result, CGM.getContext());
- llvm::Constant *C = nullptr;
+ llvm::Constant *C;
if (Success && !Result.HasSideEffects)
- C = EmitConstantValue(Result.Val, DestType, CGF);
+ C = tryEmitPrivate(Result.Val, destType);
else
- C = ConstExprEmitter(*this, CGF).Visit(const_cast<Expr*>(E));
+ C = ConstExprEmitter(*this).Visit(const_cast<Expr*>(E), destType);
- if (C && C->getType()->isIntegerTy(1)) {
- llvm::Type *BoolTy = getTypes().ConvertTypeForMem(E->getType());
- C = llvm::ConstantExpr::getZExt(C, BoolTy);
- }
return C;
}
@@ -1270,94 +1509,311 @@
return getTargetCodeGenInfo().getNullPointer(*this, T, QT);
}
-llvm::Constant *CodeGenModule::EmitConstantValue(const APValue &Value,
- QualType DestType,
- CodeGenFunction *CGF) {
- // For an _Atomic-qualified constant, we may need to add tail padding.
- if (auto *AT = DestType->getAs<AtomicType>()) {
- QualType InnerType = AT->getValueType();
- auto *Inner = EmitConstantValue(Value, InnerType, CGF);
+namespace {
+/// A struct which can be used to peephole certain kinds of finalization
+/// that normally happen during l-value emission.
+struct ConstantLValue {
+ llvm::Constant *Value;
+ bool HasOffsetApplied;
- uint64_t InnerSize = Context.getTypeSize(InnerType);
- uint64_t OuterSize = Context.getTypeSize(DestType);
- if (InnerSize == OuterSize)
- return Inner;
+ /*implicit*/ ConstantLValue(llvm::Constant *value,
+ bool hasOffsetApplied = false)
+ : Value(value), HasOffsetApplied(false) {}
- assert(InnerSize < OuterSize && "emitted over-large constant for atomic");
- llvm::Constant *Elts[] = {
- Inner,
- llvm::ConstantAggregateZero::get(
- llvm::ArrayType::get(Int8Ty, (OuterSize - InnerSize) / 8))
- };
- return llvm::ConstantStruct::getAnon(Elts);
+ /*implicit*/ ConstantLValue(ConstantAddress address)
+ : ConstantLValue(address.getPointer()) {}
+};
+
+/// A helper class for emitting constant l-values.
+class ConstantLValueEmitter : public ConstStmtVisitor<ConstantLValueEmitter,
+ ConstantLValue> {
+ CodeGenModule &CGM;
+ ConstantEmitter &Emitter;
+ const APValue &Value;
+ QualType DestType;
+
+ // Befriend StmtVisitorBase so that we don't have to expose Visit*.
+ friend StmtVisitorBase;
+
+public:
+ ConstantLValueEmitter(ConstantEmitter &emitter, const APValue &value,
+ QualType destType)
+ : CGM(emitter.CGM), Emitter(emitter), Value(value), DestType(destType) {}
+
+ llvm::Constant *tryEmit();
+
+private:
+ llvm::Constant *tryEmitAbsolute(llvm::Type *destTy);
+ ConstantLValue tryEmitBase(const APValue::LValueBase &base);
+
+ ConstantLValue VisitStmt(const Stmt *S) { return nullptr; }
+ ConstantLValue VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
+ ConstantLValue VisitStringLiteral(const StringLiteral *E);
+ ConstantLValue VisitObjCEncodeExpr(const ObjCEncodeExpr *E);
+ ConstantLValue VisitObjCStringLiteral(const ObjCStringLiteral *E);
+ ConstantLValue VisitPredefinedExpr(const PredefinedExpr *E);
+ ConstantLValue VisitAddrLabelExpr(const AddrLabelExpr *E);
+ ConstantLValue VisitCallExpr(const CallExpr *E);
+ ConstantLValue VisitBlockExpr(const BlockExpr *E);
+ ConstantLValue VisitCXXTypeidExpr(const CXXTypeidExpr *E);
+ ConstantLValue VisitCXXUuidofExpr(const CXXUuidofExpr *E);
+ ConstantLValue VisitMaterializeTemporaryExpr(
+ const MaterializeTemporaryExpr *E);
+
+ bool hasNonZeroOffset() const {
+ return !Value.getLValueOffset().isZero();
}
+ /// Return the value offset.
+ llvm::Constant *getOffset() {
+ return llvm::ConstantInt::get(CGM.Int64Ty,
+ Value.getLValueOffset().getQuantity());
+ }
+
+ /// Apply the value offset to the given constant.
+ llvm::Constant *applyOffset(llvm::Constant *C) {
+ if (!hasNonZeroOffset())
+ return C;
+
+ llvm::Type *origPtrTy = C->getType();
+ unsigned AS = origPtrTy->getPointerAddressSpace();
+ llvm::Type *charPtrTy = CGM.Int8Ty->getPointerTo(AS);
+ C = llvm::ConstantExpr::getBitCast(C, charPtrTy);
+ C = llvm::ConstantExpr::getGetElementPtr(CGM.Int8Ty, C, getOffset());
+ C = llvm::ConstantExpr::getPointerCast(C, origPtrTy);
+ return C;
+ }
+};
+
+}
+
+llvm::Constant *ConstantLValueEmitter::tryEmit() {
+ const APValue::LValueBase &base = Value.getLValueBase();
+
+ // Certain special array initializers are represented in APValue
+ // as l-values referring to the base expression which generates the
+ // array. This happens with e.g. string literals. These should
+ // probably just get their own representation kind in APValue.
+ if (DestType->isArrayType()) {
+ assert(!hasNonZeroOffset() && "offset on array initializer");
+ auto expr = const_cast<Expr*>(base.get<const Expr*>());
+ return ConstExprEmitter(Emitter).Visit(expr, DestType);
+ }
+
+ // Otherwise, the destination type should be a pointer or reference
+ // type, but it might also be a cast thereof.
+ //
+ // FIXME: the chain of casts required should be reflected in the APValue.
+ // We need this in order to correctly handle things like a ptrtoint of a
+ // non-zero null pointer and addrspace casts that aren't trivially
+ // represented in LLVM IR.
+ auto destTy = CGM.getTypes().ConvertTypeForMem(DestType);
+ assert(isa<llvm::IntegerType>(destTy) || isa<llvm::PointerType>(destTy));
+
+ // If there's no base at all, this is a null or absolute pointer,
+ // possibly cast back to an integer type.
+ if (!base) {
+ return tryEmitAbsolute(destTy);
+ }
+
+ // Otherwise, try to emit the base.
+ ConstantLValue result = tryEmitBase(base);
+
+ // If that failed, we're done.
+ llvm::Constant *value = result.Value;
+ if (!value) return nullptr;
+
+ // Apply the offset if necessary and not already done.
+ if (!result.HasOffsetApplied) {
+ value = applyOffset(value);
+ }
+
+ // Convert to the appropriate type; this could be an lvalue for
+ // an integer. FIXME: performAddrSpaceCast
+ if (isa<llvm::PointerType>(destTy))
+ return llvm::ConstantExpr::getPointerCast(value, destTy);
+
+ return llvm::ConstantExpr::getPtrToInt(value, destTy);
+}
+
+/// Try to emit an absolute l-value, such as a null pointer or an integer
+/// bitcast to pointer type.
+llvm::Constant *
+ConstantLValueEmitter::tryEmitAbsolute(llvm::Type *destTy) {
+ auto offset = getOffset();
+
+ // If we're producing a pointer, this is easy.
+ if (auto destPtrTy = cast<llvm::PointerType>(destTy)) {
+ if (Value.isNullPointer()) {
+ // FIXME: integer offsets from non-zero null pointers.
+ return CGM.getNullPointer(destPtrTy, DestType);
+ }
+
+ // Convert the integer to a pointer-sized integer before converting it
+ // to a pointer.
+ // FIXME: signedness depends on the original integer type.
+ auto intptrTy = CGM.getDataLayout().getIntPtrType(destPtrTy);
+ llvm::Constant *C = offset;
+ C = llvm::ConstantExpr::getIntegerCast(getOffset(), intptrTy,
+ /*isSigned*/ false);
+ C = llvm::ConstantExpr::getIntToPtr(C, destPtrTy);
+ return C;
+ }
+
+ // Otherwise, we're basically returning an integer constant.
+
+ // FIXME: this does the wrong thing with ptrtoint of a null pointer,
+ // but since we don't know the original pointer type, there's not much
+ // we can do about it.
+
+ auto C = getOffset();
+ C = llvm::ConstantExpr::getIntegerCast(C, destTy, /*isSigned*/ false);
+ return C;
+}
+
+ConstantLValue
+ConstantLValueEmitter::tryEmitBase(const APValue::LValueBase &base) {
+ // Handle values.
+ if (const ValueDecl *D = base.dyn_cast<const ValueDecl*>()) {
+ if (D->hasAttr<WeakRefAttr>())
+ return CGM.GetWeakRefReference(D).getPointer();
+
+ if (auto FD = dyn_cast<FunctionDecl>(D))
+ return CGM.GetAddrOfFunction(FD);
+
+ if (auto VD = dyn_cast<VarDecl>(D)) {
+ // We can never refer to a variable with local storage.
+ if (!VD->hasLocalStorage()) {
+ if (VD->isFileVarDecl() || VD->hasExternalStorage())
+ return CGM.GetAddrOfGlobalVar(VD);
+
+ if (VD->isLocalVarDecl()) {
+ return CGM.getOrCreateStaticVarDecl(
+ *VD, CGM.getLLVMLinkageVarDefinition(VD, /*isConstant=*/false));
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ // Otherwise, it must be an expression.
+ return Visit(base.get<const Expr*>());
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
+ return tryEmitGlobalCompoundLiteral(CGM, Emitter.CGF, E);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitStringLiteral(const StringLiteral *E) {
+ return CGM.GetAddrOfConstantStringFromLiteral(E);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitObjCEncodeExpr(const ObjCEncodeExpr *E) {
+ return CGM.GetAddrOfConstantStringFromObjCEncode(E);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitObjCStringLiteral(const ObjCStringLiteral *E) {
+ auto C = CGM.getObjCRuntime().GenerateConstantString(E->getString());
+ return C.getElementBitCast(CGM.getTypes().ConvertTypeForMem(E->getType()));
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitPredefinedExpr(const PredefinedExpr *E) {
+ if (auto CGF = Emitter.CGF) {
+ LValue Res = CGF->EmitPredefinedLValue(E);
+ return cast<ConstantAddress>(Res.getAddress());
+ }
+
+ auto kind = E->getIdentType();
+ if (kind == PredefinedExpr::PrettyFunction) {
+ return CGM.GetAddrOfConstantCString("top level", ".tmp");
+ }
+
+ return CGM.GetAddrOfConstantCString("", ".tmp");
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitAddrLabelExpr(const AddrLabelExpr *E) {
+ assert(Emitter.CGF && "Invalid address of label expression outside function");
+ llvm::Constant *Ptr = Emitter.CGF->GetAddrOfLabel(E->getLabel());
+ Ptr = llvm::ConstantExpr::getBitCast(Ptr,
+ CGM.getTypes().ConvertType(E->getType()));
+ return Ptr;
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitCallExpr(const CallExpr *E) {
+ unsigned builtin = E->getBuiltinCallee();
+ if (builtin != Builtin::BI__builtin___CFStringMakeConstantString &&
+ builtin != Builtin::BI__builtin___NSStringMakeConstantString)
+ return nullptr;
+
+ auto literal = cast<StringLiteral>(E->getArg(0)->IgnoreParenCasts());
+ if (builtin == Builtin::BI__builtin___NSStringMakeConstantString) {
+ return CGM.getObjCRuntime().GenerateConstantString(literal);
+ } else {
+ // FIXME: need to deal with UCN conversion issues.
+ return CGM.GetAddrOfConstantCFString(literal);
+ }
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitBlockExpr(const BlockExpr *E) {
+ StringRef functionName;
+ if (auto CGF = Emitter.CGF)
+ functionName = CGF->CurFn->getName();
+ else
+ functionName = "global";
+
+ return CGM.GetAddrOfGlobalBlock(E, functionName);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
+ QualType T;
+ if (E->isTypeOperand())
+ T = E->getTypeOperand(CGM.getContext());
+ else
+ T = E->getExprOperand()->getType();
+ return CGM.GetAddrOfRTTIDescriptor(T);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitCXXUuidofExpr(const CXXUuidofExpr *E) {
+ return CGM.GetAddrOfUuidDescriptor(E);
+}
+
+ConstantLValue
+ConstantLValueEmitter::VisitMaterializeTemporaryExpr(
+ const MaterializeTemporaryExpr *E) {
+ assert(E->getStorageDuration() == SD_Static);
+ SmallVector<const Expr *, 2> CommaLHSs;
+ SmallVector<SubobjectAdjustment, 2> Adjustments;
+ const Expr *Inner = E->GetTemporaryExpr()
+ ->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
+ return CGM.GetAddrOfGlobalTemporary(E, Inner);
+}
+
+llvm::Constant *ConstantEmitter::tryEmitPrivate(const APValue &Value,
+ QualType DestType) {
switch (Value.getKind()) {
case APValue::Uninitialized:
llvm_unreachable("Constant expressions should be initialized.");
- case APValue::LValue: {
- llvm::Type *DestTy = getTypes().ConvertTypeForMem(DestType);
- llvm::Constant *Offset =
- llvm::ConstantInt::get(Int64Ty, Value.getLValueOffset().getQuantity());
-
- llvm::Constant *C = nullptr;
-
- if (APValue::LValueBase LVBase = Value.getLValueBase()) {
- // An array can be represented as an lvalue referring to the base.
- if (isa<llvm::ArrayType>(DestTy)) {
- assert(Offset->isNullValue() && "offset on array initializer");
- return ConstExprEmitter(*this, CGF).Visit(
- const_cast<Expr*>(LVBase.get<const Expr*>()));
- }
-
- C = ConstExprEmitter(*this, CGF).EmitLValue(LVBase).getPointer();
-
- // Apply offset if necessary.
- if (!Offset->isNullValue()) {
- unsigned AS = C->getType()->getPointerAddressSpace();
- llvm::Type *CharPtrTy = Int8Ty->getPointerTo(AS);
- llvm::Constant *Casted = llvm::ConstantExpr::getBitCast(C, CharPtrTy);
- Casted = llvm::ConstantExpr::getGetElementPtr(Int8Ty, Casted, Offset);
- C = llvm::ConstantExpr::getPointerCast(Casted, C->getType());
- }
-
- // Convert to the appropriate type; this could be an lvalue for
- // an integer.
- if (isa<llvm::PointerType>(DestTy))
- return llvm::ConstantExpr::getPointerCast(C, DestTy);
-
- return llvm::ConstantExpr::getPtrToInt(C, DestTy);
- } else {
- C = Offset;
-
- // Convert to the appropriate type; this could be an lvalue for
- // an integer.
- if (auto PT = dyn_cast<llvm::PointerType>(DestTy)) {
- if (Value.isNullPointer())
- return getNullPointer(PT, DestType);
- // Convert the integer to a pointer-sized integer before converting it
- // to a pointer.
- C = llvm::ConstantExpr::getIntegerCast(
- C, getDataLayout().getIntPtrType(DestTy),
- /*isSigned=*/false);
- return llvm::ConstantExpr::getIntToPtr(C, DestTy);
- }
-
- // If the types don't match this should only be a truncate.
- if (C->getType() != DestTy)
- return llvm::ConstantExpr::getTrunc(C, DestTy);
-
- return C;
- }
- }
+ case APValue::LValue:
+ return ConstantLValueEmitter(*this, Value, DestType).tryEmit();
case APValue::Int:
- return llvm::ConstantInt::get(VMContext, Value.getInt());
+ return llvm::ConstantInt::get(CGM.getLLVMContext(), Value.getInt());
case APValue::ComplexInt: {
llvm::Constant *Complex[2];
- Complex[0] = llvm::ConstantInt::get(VMContext,
+ Complex[0] = llvm::ConstantInt::get(CGM.getLLVMContext(),
Value.getComplexIntReal());
- Complex[1] = llvm::ConstantInt::get(VMContext,
+ Complex[1] = llvm::ConstantInt::get(CGM.getLLVMContext(),
Value.getComplexIntImag());
// FIXME: the target may want to specify that this is packed.
@@ -1368,18 +1824,19 @@
case APValue::Float: {
const llvm::APFloat &Init = Value.getFloat();
if (&Init.getSemantics() == &llvm::APFloat::IEEEhalf() &&
- !Context.getLangOpts().NativeHalfType &&
- !Context.getLangOpts().HalfArgsAndReturns)
- return llvm::ConstantInt::get(VMContext, Init.bitcastToAPInt());
+ !CGM.getContext().getLangOpts().NativeHalfType &&
+ !CGM.getContext().getLangOpts().HalfArgsAndReturns)
+ return llvm::ConstantInt::get(CGM.getLLVMContext(),
+ Init.bitcastToAPInt());
else
- return llvm::ConstantFP::get(VMContext, Init);
+ return llvm::ConstantFP::get(CGM.getLLVMContext(), Init);
}
case APValue::ComplexFloat: {
llvm::Constant *Complex[2];
- Complex[0] = llvm::ConstantFP::get(VMContext,
+ Complex[0] = llvm::ConstantFP::get(CGM.getLLVMContext(),
Value.getComplexFloatReal());
- Complex[1] = llvm::ConstantFP::get(VMContext,
+ Complex[1] = llvm::ConstantFP::get(CGM.getLLVMContext(),
Value.getComplexFloatImag());
// FIXME: the target may want to specify that this is packed.
@@ -1394,9 +1851,9 @@
for (unsigned I = 0; I != NumElts; ++I) {
const APValue &Elt = Value.getVectorElt(I);
if (Elt.isInt())
- Inits[I] = llvm::ConstantInt::get(VMContext, Elt.getInt());
+ Inits[I] = llvm::ConstantInt::get(CGM.getLLVMContext(), Elt.getInt());
else if (Elt.isFloat())
- Inits[I] = llvm::ConstantFP::get(VMContext, Elt.getFloat());
+ Inits[I] = llvm::ConstantFP::get(CGM.getLLVMContext(), Elt.getFloat());
else
llvm_unreachable("unsupported vector element type");
}
@@ -1405,13 +1862,14 @@
case APValue::AddrLabelDiff: {
const AddrLabelExpr *LHSExpr = Value.getAddrLabelDiffLHS();
const AddrLabelExpr *RHSExpr = Value.getAddrLabelDiffRHS();
- llvm::Constant *LHS = EmitConstantExpr(LHSExpr, LHSExpr->getType(), CGF);
- llvm::Constant *RHS = EmitConstantExpr(RHSExpr, RHSExpr->getType(), CGF);
+ llvm::Constant *LHS = tryEmitPrivate(LHSExpr, LHSExpr->getType());
+ llvm::Constant *RHS = tryEmitPrivate(RHSExpr, RHSExpr->getType());
+ if (!LHS || !RHS) return nullptr;
// Compute difference
- llvm::Type *ResultType = getTypes().ConvertType(DestType);
- LHS = llvm::ConstantExpr::getPtrToInt(LHS, IntPtrTy);
- RHS = llvm::ConstantExpr::getPtrToInt(RHS, IntPtrTy);
+ llvm::Type *ResultType = CGM.getTypes().ConvertType(DestType);
+ LHS = llvm::ConstantExpr::getPtrToInt(LHS, CGM.IntPtrTy);
+ RHS = llvm::ConstantExpr::getPtrToInt(RHS, CGM.IntPtrTy);
llvm::Constant *AddrLabelDiff = llvm::ConstantExpr::getSub(LHS, RHS);
// LLVM is a bit sensitive about the exact format of the
@@ -1421,21 +1879,21 @@
}
case APValue::Struct:
case APValue::Union:
- return ConstStructBuilder::BuildStruct(*this, CGF, Value, DestType);
+ return ConstStructBuilder::BuildStruct(*this, Value, DestType);
case APValue::Array: {
- const ArrayType *CAT = Context.getAsArrayType(DestType);
+ const ArrayType *CAT = CGM.getContext().getAsArrayType(DestType);
unsigned NumElements = Value.getArraySize();
unsigned NumInitElts = Value.getArrayInitializedElts();
// Emit array filler, if there is one.
llvm::Constant *Filler = nullptr;
if (Value.hasArrayFiller())
- Filler = EmitConstantValueForMemory(Value.getArrayFiller(),
- CAT->getElementType(), CGF);
+ Filler = tryEmitAbstractForMemory(Value.getArrayFiller(),
+ CAT->getElementType());
// Emit initializer elements.
llvm::Type *CommonElementType =
- getTypes().ConvertType(CAT->getElementType());
+ CGM.getTypes().ConvertType(CAT->getElementType());
// Try to use a ConstantAggregateZero if we can.
if (Filler && Filler->isNullValue() && !NumInitElts) {
@@ -1444,15 +1902,21 @@
return llvm::ConstantAggregateZero::get(AType);
}
- std::vector<llvm::Constant*> Elts;
+ SmallVector<llvm::Constant*, 16> Elts;
Elts.reserve(NumElements);
for (unsigned I = 0; I < NumElements; ++I) {
llvm::Constant *C = Filler;
- if (I < NumInitElts)
- C = EmitConstantValueForMemory(Value.getArrayInitializedElt(I),
- CAT->getElementType(), CGF);
- else
- assert(Filler && "Missing filler for implicit elements of initializer");
+ if (I < NumInitElts) {
+ C = tryEmitPrivateForMemory(Value.getArrayInitializedElt(I),
+ CAT->getElementType());
+ } else if (!Filler) {
+ assert(Value.hasArrayFiller() &&
+ "Missing filler for implicit elements of initializer");
+ C = tryEmitPrivateForMemory(Value.getArrayFiller(),
+ CAT->getElementType());
+ }
+ if (!C) return nullptr;
+
if (I == 0)
CommonElementType = C->getType();
else if (C->getType() != CommonElementType)
@@ -1466,7 +1930,8 @@
Types.reserve(NumElements);
for (unsigned i = 0, e = Elts.size(); i < e; ++i)
Types.push_back(Elts[i]->getType());
- llvm::StructType *SType = llvm::StructType::get(VMContext, Types, true);
+ llvm::StructType *SType =
+ llvm::StructType::get(CGM.getLLVMContext(), Types, true);
return llvm::ConstantStruct::get(SType, Elts);
}
@@ -1475,23 +1940,11 @@
return llvm::ConstantArray::get(AType, Elts);
}
case APValue::MemberPointer:
- return getCXXABI().EmitMemberPointer(Value, DestType);
+ return CGM.getCXXABI().EmitMemberPointer(Value, DestType);
}
llvm_unreachable("Unknown APValue kind");
}
-llvm::Constant *
-CodeGenModule::EmitConstantValueForMemory(const APValue &Value,
- QualType DestType,
- CodeGenFunction *CGF) {
- llvm::Constant *C = EmitConstantValue(Value, DestType, CGF);
- if (C->getType()->isIntegerTy(1)) {
- llvm::Type *BoolTy = getTypes().ConvertTypeForMem(DestType);
- C = llvm::ConstantExpr::getZExt(C, BoolTy);
- }
- return C;
-}
-
llvm::GlobalVariable *CodeGenModule::getAddrOfConstantCompoundLiteralIfEmitted(
const CompoundLiteralExpr *E) {
return EmittedCompoundLiterals.lookup(E);
@@ -1507,7 +1960,7 @@
ConstantAddress
CodeGenModule::GetAddrOfConstantCompoundLiteral(const CompoundLiteralExpr *E) {
assert(E->isFileScope() && "not a file-scope compound literal expr");
- return ConstExprEmitter(*this, nullptr).EmitLValue(E);
+ return tryEmitGlobalCompoundLiteral(*this, nullptr, E);
}
llvm::Constant *
@@ -1629,6 +2082,11 @@
return EmitNullConstant(CGM, base, /*asCompleteObject=*/false);
}
+llvm::Constant *ConstantEmitter::emitNullForMemory(CodeGenModule &CGM,
+ QualType T) {
+ return emitForMemory(CGM, CGM.EmitNullConstant(T), T);
+}
+
llvm::Constant *CodeGenModule::EmitNullConstant(QualType T) {
if (T->getAs<PointerType>())
return getNullPointer(
@@ -1643,7 +2101,8 @@
QualType ElementTy = CAT->getElementType();
- llvm::Constant *Element = EmitNullConstant(ElementTy);
+ llvm::Constant *Element =
+ ConstantEmitter::emitNullForMemory(*this, ElementTy);
unsigned NumElements = CAT->getSize().getZExtValue();
SmallVector<llvm::Constant *, 8> Array(NumElements, Element);
return llvm::ConstantArray::get(ATy, Array);
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index 1170b01..65f9007 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -428,14 +428,19 @@
return CGF.getOpaqueRValueMapping(E).getScalarVal();
}
+ Value *emitConstant(const CodeGenFunction::ConstantEmission &Constant,
+ Expr *E) {
+ assert(Constant && "not a constant");
+ if (Constant.isReference())
+ return EmitLoadOfLValue(Constant.getReferenceLValue(CGF, E),
+ E->getExprLoc());
+ return Constant.getValue();
+ }
+
// l-values.
Value *VisitDeclRefExpr(DeclRefExpr *E) {
- if (CodeGenFunction::ConstantEmission result = CGF.tryEmitAsConstant(E)) {
- if (result.isReference())
- return EmitLoadOfLValue(result.getReferenceLValue(CGF, E),
- E->getExprLoc());
- return result.getValue();
- }
+ if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E))
+ return emitConstant(Constant, E);
return EmitLoadOfLValue(E);
}
@@ -1009,10 +1014,41 @@
return Builder.CreateVectorSplat(NumElements, Src, "splat");
}
- // Allow bitcast from vector to integer/fp of the same size.
- if (isa<llvm::VectorType>(SrcTy) ||
- isa<llvm::VectorType>(DstTy))
- return Builder.CreateBitCast(Src, DstTy, "conv");
+ if (isa<llvm::VectorType>(SrcTy) || isa<llvm::VectorType>(DstTy)) {
+ // Allow bitcast from vector to integer/fp of the same size.
+ unsigned SrcSize = SrcTy->getPrimitiveSizeInBits();
+ unsigned DstSize = DstTy->getPrimitiveSizeInBits();
+ if (SrcSize == DstSize)
+ return Builder.CreateBitCast(Src, DstTy, "conv");
+
+ // Conversions between vectors of different sizes are not allowed except
+ // when vectors of half are involved. Operations on storage-only half
+ // vectors require promoting half vector operands to float vectors and
+ // truncating the result, which is either an int or float vector, to a
+ // short or half vector.
+
+ // Source and destination are both expected to be vectors.
+ llvm::Type *SrcElementTy = SrcTy->getVectorElementType();
+ llvm::Type *DstElementTy = DstTy->getVectorElementType();
+
+ assert(((SrcElementTy->isIntegerTy() &&
+ DstElementTy->isIntegerTy()) ||
+ (SrcElementTy->isFloatingPointTy() &&
+ DstElementTy->isFloatingPointTy())) &&
+ "unexpected conversion between a floating-point vector and an "
+ "integer vector");
+
+ // Truncate an i32 vector to an i16 vector.
+ if (SrcElementTy->isIntegerTy())
+ return Builder.CreateIntCast(Src, DstTy, false, "conv");
+
+ // Truncate a float vector to a half vector.
+ if (SrcSize > DstSize)
+ return Builder.CreateFPTrunc(Src, DstTy, "conv");
+
+ // Promote a half vector to a float vector.
+ return Builder.CreateFPExt(Src, DstTy, "conv");
+ }
// Finally, we have the arithmetic types: real int/float.
Value *Res = nullptr;
@@ -1299,13 +1335,15 @@
}
Value *ScalarExprEmitter::VisitMemberExpr(MemberExpr *E) {
- llvm::APSInt Value;
- if (E->EvaluateAsInt(Value, CGF.getContext(), Expr::SE_AllowSideEffects)) {
- if (E->isArrow())
- CGF.EmitScalarExpr(E->getBase());
- else
- EmitLValue(E->getBase());
- return Builder.getInt(Value);
+ if (CodeGenFunction::ConstantEmission Constant = CGF.tryEmitAsConstant(E)) {
+ CGF.EmitIgnoredExpr(E->getBase());
+ return emitConstant(Constant, E);
+ } else {
+ llvm::APSInt Value;
+ if (E->EvaluateAsInt(Value, CGF.getContext(), Expr::SE_AllowSideEffects)) {
+ CGF.EmitIgnoredExpr(E->getBase());
+ return Builder.getInt(Value);
+ }
}
return EmitLoadOfLValue(E);
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index 90fcad2..93577a7 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -1816,22 +1816,6 @@
}
-static bool IsForwarding(StringRef Name) {
- return llvm::StringSwitch<bool>(Name)
- .Cases("objc_autoreleaseReturnValue", // ARCInstKind::AutoreleaseRV
- "objc_autorelease", // ARCInstKind::Autorelease
- "objc_retainAutoreleaseReturnValue", // ARCInstKind::FusedRetainAutoreleaseRV
- "objc_retainAutoreleasedReturnValue", // ARCInstKind::RetainRV
- "objc_retainAutorelease", // ARCInstKind::FusedRetainAutorelease
- "objc_retainedObject", // ARCInstKind::NoopCast
- "objc_retain", // ARCInstKind::Retain
- "objc_unretainedObject", // ARCInstKind::NoopCast
- "objc_unretainedPointer", // ARCInstKind::NoopCast
- "objc_unsafeClaimAutoreleasedReturnValue", // ARCInstKind::ClaimRV
- true)
- .Default(false);
-}
-
static llvm::Constant *createARCRuntimeFunction(CodeGenModule &CGM,
llvm::FunctionType *FTy,
StringRef Name) {
@@ -1849,9 +1833,6 @@
// performance.
F->addFnAttr(llvm::Attribute::NonLazyBind);
}
-
- if (IsForwarding(Name))
- F->arg_begin()->addAttr(llvm::Attribute::Returned);
}
return RTF;
diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp
index 98435fe..32a7d6a 100644
--- a/lib/CodeGen/CGObjCMac.cpp
+++ b/lib/CodeGen/CGObjCMac.cpp
@@ -7142,7 +7142,12 @@
}
assert(GV->getLinkage() == L);
- return GV;
+
+ if (IsForDefinition ||
+ GV->getValueType() == ObjCTypes.ClassnfABITy)
+ return GV;
+
+ return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.ClassnfABIPtrTy);
}
llvm::Value *
diff --git a/lib/CodeGen/CGOpenMPRuntime.cpp b/lib/CodeGen/CGOpenMPRuntime.cpp
index d488bd4..afbeb18 100644
--- a/lib/CodeGen/CGOpenMPRuntime.cpp
+++ b/lib/CodeGen/CGOpenMPRuntime.cpp
@@ -2447,7 +2447,7 @@
OutlinedFnArgs.push_back(ThreadIDAddr.getPointer());
OutlinedFnArgs.push_back(ZeroAddr.getPointer());
OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
- CGF.EmitCallOrInvoke(OutlinedFn, OutlinedFnArgs);
+ RT.emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);
// __kmpc_end_serialized_parallel(&Loc, GTid);
llvm::Value *EndArgs[] = {RT.emitUpdateLocation(CGF, Loc), ThreadID};
@@ -3348,14 +3348,14 @@
auto *UnRegFn = createOffloadingBinaryDescriptorFunction(
CGM, ".omp_offloading.descriptor_unreg",
[&](CodeGenFunction &CGF, PrePostActionTy &) {
- CGF.EmitCallOrInvoke(createRuntimeFunction(OMPRTL__tgt_unregister_lib),
- Desc);
+ CGF.EmitRuntimeCall(createRuntimeFunction(OMPRTL__tgt_unregister_lib),
+ Desc);
});
auto *RegFn = createOffloadingBinaryDescriptorFunction(
CGM, ".omp_offloading.descriptor_reg",
[&](CodeGenFunction &CGF, PrePostActionTy &) {
- CGF.EmitCallOrInvoke(createRuntimeFunction(OMPRTL__tgt_register_lib),
- Desc);
+ CGF.EmitRuntimeCall(createRuntimeFunction(OMPRTL__tgt_register_lib),
+ Desc);
CGM.getCXXABI().registerGlobalDtor(CGF, RegUnregVar, UnRegFn, Desc);
});
if (CGM.supportsCOMDAT()) {
@@ -3859,7 +3859,8 @@
}
CallArgs.push_back(SharedsParam);
- CGF.EmitCallOrInvoke(TaskFunction, CallArgs);
+ CGM.getOpenMPRuntime().emitOutlinedFunctionCall(CGF, Loc, TaskFunction,
+ CallArgs);
CGF.EmitStoreThroughLValue(
RValue::get(CGF.Builder.getInt32(/*C=*/0)),
CGF.MakeAddrLValue(CGF.ReturnValue, KmpInt32Ty));
@@ -4534,8 +4535,8 @@
DepWaitTaskArgs[5] = llvm::ConstantPointerNull::get(CGF.VoidPtrTy);
}
auto &&ElseCodeGen = [&TaskArgs, ThreadID, NewTaskNewTaskTTy, TaskEntry,
- NumDependencies, &DepWaitTaskArgs](CodeGenFunction &CGF,
- PrePostActionTy &) {
+ NumDependencies, &DepWaitTaskArgs,
+ Loc](CodeGenFunction &CGF, PrePostActionTy &) {
auto &RT = CGF.CGM.getOpenMPRuntime();
CodeGenFunction::RunCleanupsScope LocalScope(CGF);
// Build void __kmpc_omp_wait_deps(ident_t *, kmp_int32 gtid,
@@ -4546,11 +4547,12 @@
CGF.EmitRuntimeCall(RT.createRuntimeFunction(OMPRTL__kmpc_omp_wait_deps),
DepWaitTaskArgs);
// Call proxy_task_entry(gtid, new_task);
- auto &&CodeGen = [TaskEntry, ThreadID, NewTaskNewTaskTTy](
- CodeGenFunction &CGF, PrePostActionTy &Action) {
+ auto &&CodeGen = [TaskEntry, ThreadID, NewTaskNewTaskTTy,
+ Loc](CodeGenFunction &CGF, PrePostActionTy &Action) {
Action.Enter(CGF);
llvm::Value *OutlinedFnArgs[] = {ThreadID, NewTaskNewTaskTTy};
- CGF.EmitCallOrInvoke(TaskEntry, OutlinedFnArgs);
+ CGF.CGM.getOpenMPRuntime().emitOutlinedFunctionCall(CGF, Loc, TaskEntry,
+ OutlinedFnArgs);
};
// Build void __kmpc_omp_task_begin_if0(ident_t *, kmp_int32 gtid,
@@ -7034,7 +7036,7 @@
CGF.Builder.CreateCondBr(Failed, OffloadFailedBlock, OffloadContBlock);
CGF.EmitBlock(OffloadFailedBlock);
- CGF.Builder.CreateCall(OutlinedFn, KernelArgs);
+ emitOutlinedFunctionCall(CGF, D.getLocStart(), OutlinedFn, KernelArgs);
CGF.EmitBranch(OffloadContBlock);
CGF.EmitBlock(OffloadContBlock, /*IsFinished=*/true);
@@ -7754,3 +7756,29 @@
CGF.EmitRuntimeCall(RTLFn, Args);
}
+void CGOpenMPRuntime::emitCall(CodeGenFunction &CGF, llvm::Value *Callee,
+ ArrayRef<llvm::Value *> Args,
+ SourceLocation Loc) const {
+ auto DL = ApplyDebugLocation::CreateDefaultArtificial(CGF, Loc);
+
+ if (auto *Fn = dyn_cast<llvm::Function>(Callee)) {
+ if (Fn->doesNotThrow()) {
+ CGF.EmitNounwindRuntimeCall(Fn, Args);
+ return;
+ }
+ }
+ CGF.EmitRuntimeCall(Callee, Args);
+}
+
+void CGOpenMPRuntime::emitOutlinedFunctionCall(
+ CodeGenFunction &CGF, SourceLocation Loc, llvm::Value *OutlinedFn,
+ ArrayRef<llvm::Value *> Args) const {
+ assert(Loc.isValid() && "Outlined function call location must be valid.");
+ emitCall(CGF, OutlinedFn, Args, Loc);
+}
+
+Address CGOpenMPRuntime::getParameterAddress(CodeGenFunction &CGF,
+ const VarDecl *NativeParam,
+ const VarDecl *TargetParam) const {
+ return CGF.GetAddrOfLocalVar(NativeParam);
+}
diff --git a/lib/CodeGen/CGOpenMPRuntime.h b/lib/CodeGen/CGOpenMPRuntime.h
index 5dcf999..30e37a8 100644
--- a/lib/CodeGen/CGOpenMPRuntime.h
+++ b/lib/CodeGen/CGOpenMPRuntime.h
@@ -250,6 +250,11 @@
//
virtual StringRef getOutlinedHelperName() const { return ".omp_outlined."; }
+ /// Emits \p Callee function call with arguments \p Args with location \p Loc.
+ void emitCall(CodeGenFunction &CGF, llvm::Value *Callee,
+ ArrayRef<llvm::Value *> Args = llvm::None,
+ SourceLocation Loc = SourceLocation()) const;
+
private:
/// \brief Default const ident_t object used for initialization of all other
/// ident_t objects.
@@ -1324,6 +1329,30 @@
/// \param C 'depend' clause with 'sink|source' dependency kind.
virtual void emitDoacrossOrdered(CodeGenFunction &CGF,
const OMPDependClause *C);
+
+ /// Translates the native parameter of outlined function if this is required
+ /// for target.
+ /// \param FD Field decl from captured record for the paramater.
+ /// \param NativeParam Parameter itself.
+ virtual const VarDecl *translateParameter(const FieldDecl *FD,
+ const VarDecl *NativeParam) const {
+ return NativeParam;
+ }
+
+ /// Gets the address of the native argument basing on the address of the
+ /// target-specific parameter.
+ /// \param NativeParam Parameter itself.
+ /// \param TargetParam Corresponding target-specific parameter.
+ virtual Address getParameterAddress(CodeGenFunction &CGF,
+ const VarDecl *NativeParam,
+ const VarDecl *TargetParam) const;
+
+ /// Emits call of the outlined function with the provided arguments,
+ /// translating these arguments to correct target-specific arguments.
+ virtual void
+ emitOutlinedFunctionCall(CodeGenFunction &CGF, SourceLocation Loc,
+ llvm::Value *OutlinedFn,
+ ArrayRef<llvm::Value *> Args = llvm::None) const;
};
} // namespace CodeGen
diff --git a/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp b/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp
index 3ced05d..fca0a50 100644
--- a/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp
+++ b/lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp
@@ -150,20 +150,18 @@
/// Get the GPU warp size.
static llvm::Value *getNVPTXWarpSize(CodeGenFunction &CGF) {
- CGBuilderTy &Bld = CGF.Builder;
- return Bld.CreateCall(
+ return CGF.EmitRuntimeCall(
llvm::Intrinsic::getDeclaration(
&CGF.CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_warpsize),
- llvm::None, "nvptx_warp_size");
+ "nvptx_warp_size");
}
/// Get the id of the current thread on the GPU.
static llvm::Value *getNVPTXThreadID(CodeGenFunction &CGF) {
- CGBuilderTy &Bld = CGF.Builder;
- return Bld.CreateCall(
+ return CGF.EmitRuntimeCall(
llvm::Intrinsic::getDeclaration(
&CGF.CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_tid_x),
- llvm::None, "nvptx_tid");
+ "nvptx_tid");
}
/// Get the id of the warp in the block.
@@ -185,17 +183,15 @@
/// Get the maximum number of threads in a block of the GPU.
static llvm::Value *getNVPTXNumThreads(CodeGenFunction &CGF) {
- CGBuilderTy &Bld = CGF.Builder;
- return Bld.CreateCall(
+ return CGF.EmitRuntimeCall(
llvm::Intrinsic::getDeclaration(
&CGF.CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_ntid_x),
- llvm::None, "nvptx_num_threads");
+ "nvptx_num_threads");
}
/// Get barrier to synchronize all threads in a block.
static void getNVPTXCTABarrier(CodeGenFunction &CGF) {
- CGBuilderTy &Bld = CGF.Builder;
- Bld.CreateCall(llvm::Intrinsic::getDeclaration(
+ CGF.EmitRuntimeCall(llvm::Intrinsic::getDeclaration(
&CGF.CGM.getModule(), llvm::Intrinsic::nvvm_barrier0));
}
@@ -205,9 +201,9 @@
llvm::Value *NumThreads) {
CGBuilderTy &Bld = CGF.Builder;
llvm::Value *Args[] = {Bld.getInt32(ID), NumThreads};
- Bld.CreateCall(llvm::Intrinsic::getDeclaration(&CGF.CGM.getModule(),
- llvm::Intrinsic::nvvm_barrier),
- Args);
+ CGF.EmitRuntimeCall(llvm::Intrinsic::getDeclaration(
+ &CGF.CGM.getModule(), llvm::Intrinsic::nvvm_barrier),
+ Args);
}
/// Synchronize all GPU threads in a block.
@@ -345,7 +341,7 @@
Bld.CreateCondBr(IsWorker, WorkerBB, MasterCheckBB);
CGF.EmitBlock(WorkerBB);
- CGF.EmitCallOrInvoke(WST.WorkerFn, llvm::None);
+ emitCall(CGF, WST.WorkerFn);
CGF.EmitBranch(EST.ExitBB);
CGF.EmitBlock(MasterCheckBB);
@@ -555,7 +551,7 @@
CGF.CreateDefaultAlignTempAlloca(CGF.Int32Ty, /*Name=*/".zero.addr");
CGF.InitTempAlloca(ZeroAddr, CGF.Builder.getInt32(/*C=*/0));
llvm::Value *FnArgs[] = {ZeroAddr.getPointer(), ZeroAddr.getPointer()};
- CGF.EmitCallOrInvoke(Fn, FnArgs);
+ emitCall(CGF, Fn, FnArgs);
// Go to end of parallel region.
CGF.EmitBranch(TerminateBB);
@@ -883,7 +879,7 @@
OutlinedFnArgs.push_back(ZeroAddr.getPointer());
OutlinedFnArgs.push_back(ZeroAddr.getPointer());
OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
- CGF.EmitCallOrInvoke(OutlinedFn, OutlinedFnArgs);
+ emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);
}
void CGOpenMPRuntimeNVPTX::emitParallelCall(
@@ -932,10 +928,10 @@
auto *ThreadID = getThreadID(CGF, Loc);
llvm::Value *Args[] = {RTLoc, ThreadID};
- auto &&SeqGen = [this, Fn, &CapturedVars, &Args](CodeGenFunction &CGF,
- PrePostActionTy &) {
- auto &&CodeGen = [this, Fn, &CapturedVars](CodeGenFunction &CGF,
- PrePostActionTy &Action) {
+ auto &&SeqGen = [this, Fn, &CapturedVars, &Args, Loc](CodeGenFunction &CGF,
+ PrePostActionTy &) {
+ auto &&CodeGen = [this, Fn, &CapturedVars, Loc](CodeGenFunction &CGF,
+ PrePostActionTy &Action) {
Action.Enter(CGF);
llvm::SmallVector<llvm::Value *, 16> OutlinedFnArgs;
@@ -944,7 +940,7 @@
OutlinedFnArgs.push_back(
llvm::ConstantPointerNull::get(CGM.Int32Ty->getPointerTo()));
OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
- CGF.EmitCallOrInvoke(Fn, OutlinedFnArgs);
+ emitOutlinedFunctionCall(CGF, Loc, Fn, OutlinedFnArgs);
};
RegionCodeGenTy RCG(CodeGen);
@@ -980,7 +976,7 @@
OutlinedFnArgs.push_back(
llvm::ConstantPointerNull::get(CGM.Int32Ty->getPointerTo()));
OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
- CGF.EmitCallOrInvoke(OutlinedFn, OutlinedFnArgs);
+ emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, OutlinedFnArgs);
}
/// This function creates calls to one of two shuffle functions to copy
@@ -2238,3 +2234,81 @@
CGF.EmitBranch(DefaultBB);
CGF.EmitBlock(DefaultBB, /*IsFinished=*/true);
}
+
+const VarDecl *
+CGOpenMPRuntimeNVPTX::translateParameter(const FieldDecl *FD,
+ const VarDecl *NativeParam) const {
+ if (!NativeParam->getType()->isReferenceType())
+ return NativeParam;
+ QualType ArgType = NativeParam->getType();
+ QualifierCollector QC;
+ const Type *NonQualTy = QC.strip(ArgType);
+ QualType PointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
+ if (const auto *Attr = FD->getAttr<OMPCaptureKindAttr>()) {
+ if (Attr->getCaptureKind() == OMPC_map) {
+ PointeeTy = CGM.getContext().getAddrSpaceQualType(PointeeTy,
+ LangAS::opencl_global);
+ }
+ }
+ ArgType = CGM.getContext().getPointerType(PointeeTy);
+ QC.addRestrict();
+ enum { NVPTX_local_addr = 5 };
+ QC.addAddressSpace(NVPTX_local_addr);
+ ArgType = QC.apply(CGM.getContext(), ArgType);
+ return ImplicitParamDecl::Create(
+ CGM.getContext(), /*DC=*/nullptr, NativeParam->getLocation(),
+ NativeParam->getIdentifier(), ArgType, ImplicitParamDecl::Other);
+}
+
+Address
+CGOpenMPRuntimeNVPTX::getParameterAddress(CodeGenFunction &CGF,
+ const VarDecl *NativeParam,
+ const VarDecl *TargetParam) const {
+ assert(NativeParam != TargetParam &&
+ NativeParam->getType()->isReferenceType() &&
+ "Native arg must not be the same as target arg.");
+ Address LocalAddr = CGF.GetAddrOfLocalVar(TargetParam);
+ QualType NativeParamType = NativeParam->getType();
+ QualifierCollector QC;
+ const Type *NonQualTy = QC.strip(NativeParamType);
+ QualType NativePointeeTy = cast<ReferenceType>(NonQualTy)->getPointeeType();
+ unsigned NativePointeeAddrSpace =
+ NativePointeeTy.getQualifiers().getAddressSpace();
+ QualType TargetPointeeTy = TargetParam->getType()->getPointeeType();
+ llvm::Value *TargetAddr = CGF.EmitLoadOfScalar(
+ LocalAddr, /*Volatile=*/false, TargetPointeeTy, SourceLocation());
+ // First cast to generic.
+ TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
+ TargetAddr, TargetAddr->getType()->getPointerElementType()->getPointerTo(
+ /*AddrSpace=*/0));
+ // Cast from generic to native address space.
+ TargetAddr = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
+ TargetAddr, TargetAddr->getType()->getPointerElementType()->getPointerTo(
+ NativePointeeAddrSpace));
+ Address NativeParamAddr = CGF.CreateMemTemp(NativeParamType);
+ CGF.EmitStoreOfScalar(TargetAddr, NativeParamAddr, /*Volatile=*/false,
+ NativeParam->getType());
+ return NativeParamAddr;
+}
+
+void CGOpenMPRuntimeNVPTX::emitOutlinedFunctionCall(
+ CodeGenFunction &CGF, SourceLocation Loc, llvm::Value *OutlinedFn,
+ ArrayRef<llvm::Value *> Args) const {
+ SmallVector<llvm::Value *, 4> TargetArgs;
+ auto *FnType =
+ cast<llvm::FunctionType>(OutlinedFn->getType()->getPointerElementType());
+ for (unsigned I = 0, E = Args.size(); I < E; ++I) {
+ llvm::Type *TargetType = FnType->getParamType(I);
+ llvm::Value *NativeArg = Args[I];
+ if (!TargetType->isPointerTy()) {
+ TargetArgs.emplace_back(NativeArg);
+ continue;
+ }
+ llvm::Value *TargetArg = CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(
+ NativeArg, NativeArg->getType()->getPointerElementType()->getPointerTo(
+ /*AddrSpace=*/0));
+ TargetArgs.emplace_back(
+ CGF.Builder.CreatePointerBitCastOrAddrSpaceCast(TargetArg, TargetType));
+ }
+ CGOpenMPRuntime::emitOutlinedFunctionCall(CGF, Loc, OutlinedFn, TargetArgs);
+}
diff --git a/lib/CodeGen/CGOpenMPRuntimeNVPTX.h b/lib/CodeGen/CGOpenMPRuntimeNVPTX.h
index ae25e94..a9d6386 100644
--- a/lib/CodeGen/CGOpenMPRuntimeNVPTX.h
+++ b/lib/CodeGen/CGOpenMPRuntimeNVPTX.h
@@ -268,6 +268,26 @@
/// \return Specified function.
llvm::Constant *createNVPTXRuntimeFunction(unsigned Function);
+ /// Translates the native parameter of outlined function if this is required
+ /// for target.
+ /// \param FD Field decl from captured record for the paramater.
+ /// \param NativeParam Parameter itself.
+ const VarDecl *translateParameter(const FieldDecl *FD,
+ const VarDecl *NativeParam) const override;
+
+ /// Gets the address of the native argument basing on the address of the
+ /// target-specific parameter.
+ /// \param NativeParam Parameter itself.
+ /// \param TargetParam Corresponding target-specific parameter.
+ Address getParameterAddress(CodeGenFunction &CGF, const VarDecl *NativeParam,
+ const VarDecl *TargetParam) const override;
+
+ /// Emits call of the outlined function with the provided arguments,
+ /// translating these arguments to correct target-specific arguments.
+ void emitOutlinedFunctionCall(
+ CodeGenFunction &CGF, SourceLocation Loc, llvm::Value *OutlinedFn,
+ ArrayRef<llvm::Value *> Args = llvm::None) const override;
+
/// Target codegen is specialized based on two programming models: the
/// 'generic' fork-join model of OpenMP, and a more GPU efficient 'spmd'
/// model for constructs like 'target parallel' that support it.
diff --git a/lib/CodeGen/CGStmtOpenMP.cpp b/lib/CodeGen/CGStmtOpenMP.cpp
index 6135cf3..36b6c87 100644
--- a/lib/CodeGen/CGStmtOpenMP.cpp
+++ b/lib/CodeGen/CGStmtOpenMP.cpp
@@ -246,12 +246,12 @@
const CapturedStmt *S = nullptr;
/// true if cast to/from UIntPtr is required for variables captured by
/// value.
- bool UIntPtrCastRequired = true;
- /// true if only casted argumefnts must be registered as local args or VLA
+ const bool UIntPtrCastRequired = true;
+ /// true if only casted arguments must be registered as local args or VLA
/// sizes.
- bool RegisterCastedArgsOnly = false;
+ const bool RegisterCastedArgsOnly = false;
/// Name of the generated function.
- StringRef FunctionName;
+ const StringRef FunctionName;
explicit FunctionOptions(const CapturedStmt *S, bool UIntPtrCastRequired,
bool RegisterCastedArgsOnly,
StringRef FunctionName)
@@ -261,9 +261,9 @@
};
}
-static std::pair<llvm::Function *, bool> emitOutlinedFunctionPrologue(
+static llvm::Function *emitOutlinedFunctionPrologue(
CodeGenFunction &CGF, FunctionArgList &Args,
- llvm::DenseMap<const Decl *, std::pair<const VarDecl *, Address>>
+ llvm::MapVector<const Decl *, std::pair<const VarDecl *, Address>>
&LocalAddrs,
llvm::DenseMap<const Decl *, std::pair<const Expr *, llvm::Value *>>
&VLASizes,
@@ -276,9 +276,12 @@
// Build the argument list.
CodeGenModule &CGM = CGF.CGM;
ASTContext &Ctx = CGM.getContext();
- bool HasUIntPtrArgs = false;
+ FunctionArgList TargetArgs;
Args.append(CD->param_begin(),
std::next(CD->param_begin(), CD->getContextParamPosition()));
+ TargetArgs.append(
+ CD->param_begin(),
+ std::next(CD->param_begin(), CD->getContextParamPosition()));
auto I = FO.S->captures().begin();
for (auto *FD : RD->fields()) {
QualType ArgType = FD->getType();
@@ -292,7 +295,6 @@
// outlined function.
if ((I->capturesVariableByCopy() && !ArgType->isAnyPointerType()) ||
I->capturesVariableArrayType()) {
- HasUIntPtrArgs = true;
if (FO.UIntPtrCastRequired)
ArgType = Ctx.getUIntPtrType();
}
@@ -308,19 +310,28 @@
}
if (ArgType->isVariablyModifiedType())
ArgType = getCanonicalParamType(Ctx, ArgType.getNonReferenceType());
- Args.push_back(ImplicitParamDecl::Create(Ctx, /*DC=*/nullptr,
- FD->getLocation(), II, ArgType,
- ImplicitParamDecl::Other));
+ auto *Arg =
+ ImplicitParamDecl::Create(Ctx, /*DC=*/nullptr, FD->getLocation(), II,
+ ArgType, ImplicitParamDecl::Other);
+ Args.emplace_back(Arg);
+ // Do not cast arguments if we emit function with non-original types.
+ TargetArgs.emplace_back(
+ FO.UIntPtrCastRequired
+ ? Arg
+ : CGM.getOpenMPRuntime().translateParameter(FD, Arg));
++I;
}
Args.append(
std::next(CD->param_begin(), CD->getContextParamPosition() + 1),
CD->param_end());
+ TargetArgs.append(
+ std::next(CD->param_begin(), CD->getContextParamPosition() + 1),
+ CD->param_end());
// Create the function declaration.
FunctionType::ExtInfo ExtInfo;
const CGFunctionInfo &FuncInfo =
- CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args);
+ CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, TargetArgs);
llvm::FunctionType *FuncLLVMTy = CGM.getTypes().GetFunctionType(FuncInfo);
llvm::Function *F =
@@ -328,19 +339,26 @@
FO.FunctionName, &CGM.getModule());
CGM.SetInternalFunctionAttributes(CD, F, FuncInfo);
if (CD->isNothrow())
- F->addFnAttr(llvm::Attribute::NoUnwind);
+ F->setDoesNotThrow();
// Generate the function.
- CGF.StartFunction(CD, Ctx.VoidTy, F, FuncInfo, Args, CD->getLocation(),
+ CGF.StartFunction(CD, Ctx.VoidTy, F, FuncInfo, TargetArgs, CD->getLocation(),
CD->getBody()->getLocStart());
unsigned Cnt = CD->getContextParamPosition();
I = FO.S->captures().begin();
for (auto *FD : RD->fields()) {
+ // Do not map arguments if we emit function with non-original types.
+ Address LocalAddr(Address::invalid());
+ if (!FO.UIntPtrCastRequired && Args[Cnt] != TargetArgs[Cnt]) {
+ LocalAddr = CGM.getOpenMPRuntime().getParameterAddress(CGF, Args[Cnt],
+ TargetArgs[Cnt]);
+ } else {
+ LocalAddr = CGF.GetAddrOfLocalVar(Args[Cnt]);
+ }
// If we are capturing a pointer by copy we don't need to do anything, just
// use the value that we get from the arguments.
if (I->capturesVariableByCopy() && FD->getType()->isAnyPointerType()) {
const VarDecl *CurVD = I->getCapturedVar();
- Address LocalAddr = CGF.GetAddrOfLocalVar(Args[Cnt]);
// If the variable is a reference we need to materialize it here.
if (CurVD->getType()->isReferenceType()) {
Address RefAddr = CGF.CreateMemTemp(
@@ -357,8 +375,8 @@
}
LValueBaseInfo BaseInfo(AlignmentSource::Decl, false);
- LValue ArgLVal = CGF.MakeAddrLValue(CGF.GetAddrOfLocalVar(Args[Cnt]),
- Args[Cnt]->getType(), BaseInfo);
+ LValue ArgLVal =
+ CGF.MakeAddrLValue(LocalAddr, Args[Cnt]->getType(), BaseInfo);
if (FD->hasCapturedVLAType()) {
if (FO.UIntPtrCastRequired) {
ArgLVal = CGF.MakeAddrLValue(castValueFromUintptr(CGF, FD->getType(),
@@ -412,7 +430,7 @@
++I;
}
- return {F, HasUIntPtrArgs};
+ return F;
}
llvm::Function *
@@ -426,14 +444,17 @@
getDebugInfo() &&
CGM.getCodeGenOpts().getDebugInfo() >= codegenoptions::LimitedDebugInfo;
FunctionArgList Args;
- llvm::DenseMap<const Decl *, std::pair<const VarDecl *, Address>> LocalAddrs;
+ llvm::MapVector<const Decl *, std::pair<const VarDecl *, Address>> LocalAddrs;
llvm::DenseMap<const Decl *, std::pair<const Expr *, llvm::Value *>> VLASizes;
+ SmallString<256> Buffer;
+ llvm::raw_svector_ostream Out(Buffer);
+ Out << CapturedStmtInfo->getHelperName();
+ if (NeedWrapperFunction)
+ Out << "_debug__";
FunctionOptions FO(&S, !NeedWrapperFunction, /*RegisterCastedArgsOnly=*/false,
- CapturedStmtInfo->getHelperName());
- llvm::Function *F;
- bool HasUIntPtrArgs;
- std::tie(F, HasUIntPtrArgs) = emitOutlinedFunctionPrologue(
- *this, Args, LocalAddrs, VLASizes, CXXThisValue, FO);
+ Out.str());
+ llvm::Function *F = emitOutlinedFunctionPrologue(*this, Args, LocalAddrs,
+ VLASizes, CXXThisValue, FO);
for (const auto &LocalAddrPair : LocalAddrs) {
if (LocalAddrPair.second.first) {
setAddrOfLocalVar(LocalAddrPair.second.first,
@@ -445,20 +466,19 @@
PGO.assignRegionCounters(GlobalDecl(CD), F);
CapturedStmtInfo->EmitBody(*this, CD->getBody());
FinishFunction(CD->getBodyRBrace());
- if (!NeedWrapperFunction || !HasUIntPtrArgs)
+ if (!NeedWrapperFunction)
return F;
FunctionOptions WrapperFO(&S, /*UIntPtrCastRequired=*/true,
/*RegisterCastedArgsOnly=*/true,
- ".nondebug_wrapper.");
+ CapturedStmtInfo->getHelperName());
CodeGenFunction WrapperCGF(CGM, /*suppressNewContext=*/true);
- WrapperCGF.disableDebugInfo();
Args.clear();
LocalAddrs.clear();
VLASizes.clear();
llvm::Function *WrapperF =
emitOutlinedFunctionPrologue(WrapperCGF, Args, LocalAddrs, VLASizes,
- WrapperCGF.CXXThisValue, WrapperFO).first;
+ WrapperCGF.CXXThisValue, WrapperFO);
LValueBaseInfo BaseInfo(AlignmentSource::Decl, false);
llvm::SmallVector<llvm::Value *, 4> CallArgs;
for (const auto *Arg : Args) {
@@ -480,7 +500,8 @@
}
CallArgs.emplace_back(CallArg);
}
- WrapperCGF.Builder.CreateCall(F, CallArgs);
+ CGM.getOpenMPRuntime().emitOutlinedFunctionCall(WrapperCGF, S.getLocStart(),
+ F, CallArgs);
WrapperCGF.FinishFunction();
return WrapperF;
}
@@ -2728,6 +2749,7 @@
OMPPrivateScope Scope(CGF);
if (!Data.PrivateVars.empty() || !Data.FirstprivateVars.empty() ||
!Data.LastprivateVars.empty()) {
+ enum { PrivatesParam = 2, CopyFnParam = 3 };
auto *CopyFn = CGF.Builder.CreateLoad(
CGF.GetAddrOfLocalVar(CS->getCapturedDecl()->getParam(3)));
auto *PrivatesPtr = CGF.Builder.CreateLoad(
@@ -2759,7 +2781,8 @@
PrivatePtrs.push_back(std::make_pair(VD, PrivatePtr));
CallArgs.push_back(PrivatePtr.getPointer());
}
- CGF.EmitRuntimeCall(CopyFn, CallArgs);
+ CGF.CGM.getOpenMPRuntime().emitOutlinedFunctionCall(CGF, S.getLocStart(),
+ CopyFn, CallArgs);
for (auto &&Pair : LastprivateDstsOrigs) {
auto *OrigVD = cast<VarDecl>(Pair.second->getDecl());
DeclRefExpr DRE(
@@ -3070,7 +3093,8 @@
llvm::SmallVector<llvm::Value *, 16> CapturedVars;
CGF.GenerateOpenMPCapturedVars(*CS, CapturedVars);
auto *OutlinedFn = emitOutlinedOrderedFunction(CGM, CS);
- CGF.EmitNounwindRuntimeCall(OutlinedFn, CapturedVars);
+ CGM.getOpenMPRuntime().emitOutlinedFunctionCall(CGF, S.getLocStart(),
+ OutlinedFn, CapturedVars);
} else {
Action.Enter(CGF);
CGF.EmitStmt(
diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp
index c23b25e..2cc0afe 100644
--- a/lib/CodeGen/CodeGenFunction.cpp
+++ b/lib/CodeGen/CodeGenFunction.cpp
@@ -429,6 +429,43 @@
return CGM.getCodeGenOpts().XRayInstrumentFunctions;
}
+llvm::Constant *
+CodeGenFunction::EncodeAddrForUseInPrologue(llvm::Function *F,
+ llvm::Constant *Addr) {
+ // Addresses stored in prologue data can't require run-time fixups and must
+ // be PC-relative. Run-time fixups are undesirable because they necessitate
+ // writable text segments, which are unsafe. And absolute addresses are
+ // undesirable because they break PIE mode.
+
+ // Add a layer of indirection through a private global. Taking its address
+ // won't result in a run-time fixup, even if Addr has linkonce_odr linkage.
+ auto *GV = new llvm::GlobalVariable(CGM.getModule(), Addr->getType(),
+ /*isConstant=*/true,
+ llvm::GlobalValue::PrivateLinkage, Addr);
+
+ // Create a PC-relative address.
+ auto *GOTAsInt = llvm::ConstantExpr::getPtrToInt(GV, IntPtrTy);
+ auto *FuncAsInt = llvm::ConstantExpr::getPtrToInt(F, IntPtrTy);
+ auto *PCRelAsInt = llvm::ConstantExpr::getSub(GOTAsInt, FuncAsInt);
+ return (IntPtrTy == Int32Ty)
+ ? PCRelAsInt
+ : llvm::ConstantExpr::getTrunc(PCRelAsInt, Int32Ty);
+}
+
+llvm::Value *
+CodeGenFunction::DecodeAddrUsedInPrologue(llvm::Value *F,
+ llvm::Value *EncodedAddr) {
+ // Reconstruct the address of the global.
+ auto *PCRelAsInt = Builder.CreateSExt(EncodedAddr, IntPtrTy);
+ auto *FuncAsInt = Builder.CreatePtrToInt(F, IntPtrTy, "func_addr.int");
+ auto *GOTAsInt = Builder.CreateAdd(PCRelAsInt, FuncAsInt, "global_addr.int");
+ auto *GOTAddr = Builder.CreateIntToPtr(GOTAsInt, Int8PtrPtrTy, "global_addr");
+
+ // Load the original pointer through the global.
+ return Builder.CreateLoad(Address(GOTAddr, getPointerAlign()),
+ "decoded_addr");
+}
+
/// EmitFunctionInstrumentation - Emit LLVM code to call the specified
/// instrumentation function with the current function and the call site, if
/// function instrumentation is enabled.
@@ -821,7 +858,10 @@
CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) {
llvm::Constant *FTRTTIConst =
CGM.GetAddrOfRTTIDescriptor(FD->getType(), /*ForEH=*/true);
- llvm::Constant *PrologueStructElems[] = { PrologueSig, FTRTTIConst };
+ llvm::Constant *FTRTTIConstEncoded =
+ EncodeAddrForUseInPrologue(Fn, FTRTTIConst);
+ llvm::Constant *PrologueStructElems[] = {PrologueSig,
+ FTRTTIConstEncoded};
llvm::Constant *PrologueStructConst =
llvm::ConstantStruct::getAnon(PrologueStructElems, /*Packed=*/true);
Fn->setPrologueData(PrologueStructConst);
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 753dd92..6febda6 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -111,6 +111,7 @@
SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \
SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \
SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \
+ SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \
SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \
SANITIZER_CHECK(MissingReturn, missing_return, 0) \
SANITIZER_CHECK(MulOverflow, mul_overflow, 0) \
@@ -1583,7 +1584,8 @@
llvm::Function *GenerateBlockFunction(GlobalDecl GD,
const CGBlockInfo &Info,
const DeclMapTy &ldm,
- bool IsLambdaConversionToBlock);
+ bool IsLambdaConversionToBlock,
+ bool BuildGlobalBlock);
llvm::Constant *GenerateCopyHelperFunction(const CGBlockInfo &blockInfo);
llvm::Constant *GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo);
@@ -1774,6 +1776,15 @@
/// EmitMCountInstrumentation - Emit call to .mcount.
void EmitMCountInstrumentation();
+ /// Encode an address into a form suitable for use in a function prologue.
+ llvm::Constant *EncodeAddrForUseInPrologue(llvm::Function *F,
+ llvm::Constant *Addr);
+
+ /// Decode an address used in a function prologue, encoded by \c
+ /// EncodeAddrForUseInPrologue.
+ llvm::Value *DecodeAddrUsedInPrologue(llvm::Value *F,
+ llvm::Value *EncodedAddr);
+
/// EmitFunctionProlog - Emit the target specific LLVM code to load the
/// arguments for the given function. This is also responsible for naming the
/// LLVM function arguments.
@@ -3157,6 +3168,7 @@
};
ConstantEmission tryEmitAsConstant(DeclRefExpr *refExpr);
+ ConstantEmission tryEmitAsConstant(const MemberExpr *ME);
RValue EmitPseudoObjectRValue(const PseudoObjectExpr *e,
AggValueSlot slot = AggValueSlot::ignored());
@@ -3605,6 +3617,17 @@
SourceLocation Loc,
const Twine &Name = "");
+ /// Specifies which type of sanitizer check to apply when handling a
+ /// particular builtin.
+ enum BuiltinCheckKind {
+ BCK_CTZPassedZero,
+ BCK_CLZPassedZero,
+ };
+
+ /// Emits an argument for a call to a builtin. If the builtin sanitizer is
+ /// enabled, a runtime check specified by \p Kind is also emitted.
+ llvm::Value *EmitCheckedArgForBuiltin(const Expr *E, BuiltinCheckKind Kind);
+
/// \brief Emit a description of a type in a format suitable for passing to
/// a runtime sanitizer handler.
llvm::Constant *EmitCheckTypeDescriptor(QualType T);
diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp
index 5561d45..1780572 100644
--- a/lib/CodeGen/CodeGenModule.cpp
+++ b/lib/CodeGen/CodeGenModule.cpp
@@ -24,6 +24,7 @@
#include "CodeGenFunction.h"
#include "CodeGenPGO.h"
#include "CodeGenTBAA.h"
+#include "ConstantEmitter.h"
#include "CoverageMappingGen.h"
#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
@@ -2678,6 +2679,8 @@
const VarDecl *InitDecl;
const Expr *InitExpr = D->getAnyInitializer(InitDecl);
+ Optional<ConstantEmitter> emitter;
+
// CUDA E.2.4.1 "__shared__ variables cannot have an initialization
// as part of their declaration." Sema has already checked for
// error cases, so we just need to set Init to UndefValue.
@@ -2698,7 +2701,8 @@
Init = EmitNullConstant(D->getType());
} else {
initializedGlobalDecl = GlobalDecl(D);
- Init = EmitConstantInit(*InitDecl);
+ emitter.emplace(*this);
+ Init = emitter->tryEmitForInitializer(*InitDecl);
if (!Init) {
QualType T = InitExpr->getType();
@@ -2811,7 +2815,9 @@
Linkage = llvm::GlobalValue::InternalLinkage;
}
}
+
GV->setInitializer(Init);
+ if (emitter) emitter->finalize(GV);
// If it is safe to mark the global 'constant', do so now.
GV->setConstant(!NeedsGlobalCtor && !NeedsGlobalDtor &&
@@ -3727,12 +3733,18 @@
!EvalResult.hasSideEffects())
Value = &EvalResult.Val;
+ unsigned AddrSpace =
+ VD ? GetGlobalVarAddressSpace(VD) : MaterializedType.getAddressSpace();
+
+ Optional<ConstantEmitter> emitter;
llvm::Constant *InitialValue = nullptr;
bool Constant = false;
llvm::Type *Type;
if (Value) {
// The temporary has a constant initializer, use it.
- InitialValue = EmitConstantValue(*Value, MaterializedType, nullptr);
+ emitter.emplace(*this);
+ InitialValue = emitter->emitForInitializer(*Value, AddrSpace,
+ MaterializedType);
Constant = isTypeConstant(MaterializedType, /*ExcludeCtor*/Value);
Type = InitialValue->getType();
} else {
@@ -3757,12 +3769,11 @@
Linkage = llvm::GlobalVariable::InternalLinkage;
}
}
- unsigned AddrSpace =
- VD ? GetGlobalVarAddressSpace(VD) : MaterializedType.getAddressSpace();
auto TargetAS = getContext().getTargetAddressSpace(AddrSpace);
auto *GV = new llvm::GlobalVariable(
getModule(), Type, Constant, Linkage, InitialValue, Name.c_str(),
/*InsertBefore=*/nullptr, llvm::GlobalVariable::NotThreadLocal, TargetAS);
+ if (emitter) emitter->finalize(GV);
setGlobalVisibility(GV, VD);
GV->setAlignment(Align.getQuantity());
if (supportsCOMDAT() && GV->isWeakForLinker())
@@ -4527,7 +4538,7 @@
llvm::Value *
CodeGenModule::createOpenCLIntToSamplerConversion(const Expr *E,
CodeGenFunction &CGF) {
- llvm::Constant *C = EmitConstantExpr(E, E->getType(), &CGF);
+ llvm::Constant *C = ConstantEmitter(CGF).emitAbstract(E, E->getType());
auto SamplerT = getOpenCLRuntime().getSamplerType();
auto FTy = llvm::FunctionType::get(SamplerT, {C->getType()}, false);
return CGF.Builder.CreateCall(CreateRuntimeFunction(FTy,
diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h
index b162e72..47a6dfa 100644
--- a/lib/CodeGen/CodeGenModule.h
+++ b/lib/CodeGen/CodeGenModule.h
@@ -942,27 +942,6 @@
llvm::Constant *getMemberPointerConstant(const UnaryOperator *e);
- /// Try to emit the initializer for the given declaration as a constant;
- /// returns 0 if the expression cannot be emitted as a constant.
- llvm::Constant *EmitConstantInit(const VarDecl &D,
- CodeGenFunction *CGF = nullptr);
-
- /// Try to emit the given expression as a constant; returns 0 if the
- /// expression cannot be emitted as a constant.
- llvm::Constant *EmitConstantExpr(const Expr *E, QualType DestType,
- CodeGenFunction *CGF = nullptr);
-
- /// Emit the given constant value as a constant, in the type's scalar
- /// representation.
- llvm::Constant *EmitConstantValue(const APValue &Value, QualType DestType,
- CodeGenFunction *CGF = nullptr);
-
- /// Emit the given constant value as a constant, in the type's memory
- /// representation.
- llvm::Constant *EmitConstantValueForMemory(const APValue &Value,
- QualType DestType,
- CodeGenFunction *CGF = nullptr);
-
/// \brief Emit type info if type of an expression is a variably modified
/// type. Also emit proper debug info for cast types.
void EmitExplicitCastExprType(const ExplicitCastExpr *E,
@@ -1355,6 +1334,7 @@
bool AttrOnCallSite,
llvm::AttrBuilder &FuncAttrs);
};
+
} // end namespace CodeGen
} // end namespace clang
diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp
index 9306c4f..5ed9291 100644
--- a/lib/CodeGen/CodeGenTypes.cpp
+++ b/lib/CodeGen/CodeGenTypes.cpp
@@ -44,10 +44,6 @@
delete &*I++;
}
-const CodeGenOptions &CodeGenTypes::getCodeGenOpts() const {
- return CGM.getCodeGenOpts();
-}
-
void CodeGenTypes::addRecordTypeName(const RecordDecl *RD,
llvm::StructType *Ty,
StringRef suffix) {
diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h
index 9d0e3de..f0b97eb 100644
--- a/lib/CodeGen/CodeGenTypes.h
+++ b/lib/CodeGen/CodeGenTypes.h
@@ -178,7 +178,6 @@
const TargetInfo &getTarget() const { return Target; }
CGCXXABI &getCXXABI() const { return TheCXXABI; }
llvm::LLVMContext &getLLVMContext() { return TheModule.getContext(); }
- const CodeGenOptions &getCodeGenOpts() const;
/// ConvertType - Convert type T into a llvm::Type.
llvm::Type *ConvertType(QualType T);
diff --git a/lib/CodeGen/ConstantEmitter.h b/lib/CodeGen/ConstantEmitter.h
new file mode 100644
index 0000000..991ef35
--- /dev/null
+++ b/lib/CodeGen/ConstantEmitter.h
@@ -0,0 +1,180 @@
+//===--- ConstantEmitter.h - IR constant emission ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A helper class for emitting expressions and values as llvm::Constants
+// and as initializers for global variables.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_CODEGEN_CONSTANTEMITTER_H
+#define LLVM_CLANG_LIB_CODEGEN_CONSTANTEMITTER_H
+
+#include "CodeGenFunction.h"
+#include "CodeGenModule.h"
+
+namespace clang {
+namespace CodeGen {
+
+class ConstantEmitter {
+public:
+ CodeGenModule &CGM;
+ CodeGenFunction *CGF;
+
+private:
+ bool Abstract = false;
+
+ /// Whether non-abstract components of the emitter have been initialized.
+ bool InitializedNonAbstract = false;
+
+ /// Whether the emitter has been finalized.
+ bool Finalized = false;
+
+ /// Whether the constant-emission failed.
+ bool Failed = false;
+
+ /// The AST address space where this (non-abstract) initializer is going.
+ /// Used for generating appropriate placeholders.
+ unsigned DestAddressSpace;
+
+ llvm::SmallVector<std::pair<llvm::Constant *, llvm::GlobalVariable*>, 4>
+ PlaceholderAddresses;
+
+public:
+ ConstantEmitter(CodeGenModule &CGM, CodeGenFunction *CGF = nullptr)
+ : CGM(CGM), CGF(CGF) {}
+
+ /// Initialize this emission in the context of the given function.
+ /// Use this if the expression might contain contextaul references like
+ /// block addresses or PredefinedExprs.
+ ConstantEmitter(CodeGenFunction &CGF)
+ : CGM(CGF.CGM), CGF(&CGF) {}
+
+ ConstantEmitter(const ConstantEmitter &other) = delete;
+ ConstantEmitter &operator=(const ConstantEmitter &other) = delete;
+
+ ~ConstantEmitter();
+
+ /// Is the current emission context abstract?
+ bool isAbstract() const {
+ return Abstract;
+ }
+
+ /// Try to emit the initiaizer of the given declaration as an abstract
+ /// constant. If this succeeds, the emission must be finalized.
+ llvm::Constant *tryEmitForInitializer(const VarDecl &D);
+ llvm::Constant *tryEmitForInitializer(const Expr *E,
+ unsigned destAddrSpace,
+ QualType destType);
+ llvm::Constant *emitForInitializer(const APValue &value,
+ unsigned destAddrSpace,
+ QualType destType);
+
+ void finalize(llvm::GlobalVariable *global);
+
+ // All of the "abstract" emission methods below permit the emission to
+ // be immediately discarded without finalizing anything. Therefore, they
+ // must also promise not to do anything that will, in the future, require
+ // finalization:
+ //
+ // - using the CGF (if present) for anything other than establishing
+ // semantic context; for example, an expression with ignored
+ // side-effects must not be emitted as an abstract expression
+ //
+ // - doing anything that would not be safe to duplicate within an
+ // initializer or to propagate to another context; for example,
+ // side effects, or emitting an initialization that requires a
+ // reference to its current location.
+
+ /// Try to emit the initializer of the given declaration as an abstract
+ /// constant.
+ llvm::Constant *tryEmitAbstractForInitializer(const VarDecl &D);
+
+ /// Emit the result of the given expression as an abstract constant,
+ /// asserting that it succeeded. This is only safe to do when the
+ /// expression is known to be a constant expression with either a fairly
+ /// simple type or a known simple form.
+ llvm::Constant *emitAbstract(const Expr *E, QualType T);
+ llvm::Constant *emitAbstract(SourceLocation loc, const APValue &value,
+ QualType T);
+
+ /// Try to emit the result of the given expression as an abstract constant.
+ llvm::Constant *tryEmitAbstract(const Expr *E, QualType T);
+ llvm::Constant *tryEmitAbstractForMemory(const Expr *E, QualType T);
+
+ llvm::Constant *tryEmitAbstract(const APValue &value, QualType T);
+ llvm::Constant *tryEmitAbstractForMemory(const APValue &value, QualType T);
+
+ llvm::Constant *emitNullForMemory(QualType T) {
+ return emitNullForMemory(CGM, T);
+ }
+ llvm::Constant *emitForMemory(llvm::Constant *C, QualType T) {
+ return emitForMemory(CGM, C, T);
+ }
+
+ static llvm::Constant *emitNullForMemory(CodeGenModule &CGM, QualType T);
+ static llvm::Constant *emitForMemory(CodeGenModule &CGM, llvm::Constant *C,
+ QualType T);
+
+ // These are private helper routines of the constant emitter that
+ // can't actually be private because things are split out into helper
+ // functions and classes.
+
+ llvm::Constant *tryEmitPrivateForVarInit(const VarDecl &D);
+
+ llvm::Constant *tryEmitPrivate(const Expr *E, QualType T);
+ llvm::Constant *tryEmitPrivateForMemory(const Expr *E, QualType T);
+
+ llvm::Constant *tryEmitPrivate(const APValue &value, QualType T);
+ llvm::Constant *tryEmitPrivateForMemory(const APValue &value, QualType T);
+
+ /// Get the address of the current location. This is a constant
+ /// that will resolve, after finalization, to the address of the
+ /// 'signal' value that is registered with the emitter later.
+ llvm::GlobalValue *getCurrentAddrPrivate();
+
+ /// Register a 'signal' value with the emitter to inform it where to
+ /// resolve a placeholder. The signal value must be unique in the
+ /// initializer; it might, for example, be the address of a global that
+ /// refers to the current-address value in its own initializer.
+ ///
+ /// Uses of the placeholder must be properly anchored before finalizing
+ /// the emitter, e.g. by being installed as the initializer of a global
+ /// variable. That is, it must be possible to replaceAllUsesWith
+ /// the placeholder with the proper address of the signal.
+ void registerCurrentAddrPrivate(llvm::Constant *signal,
+ llvm::GlobalValue *placeholder);
+
+private:
+ void initializeNonAbstract(unsigned destAS) {
+ assert(!InitializedNonAbstract);
+ InitializedNonAbstract = true;
+ DestAddressSpace = destAS;
+ }
+ llvm::Constant *markIfFailed(llvm::Constant *init) {
+ if (!init)
+ Failed = true;
+ return init;
+ }
+
+ struct AbstractState {
+ bool OldValue;
+ size_t OldPlaceholdersSize;
+ };
+ AbstractState pushAbstract() {
+ AbstractState saved = { Abstract, PlaceholderAddresses.size() };
+ Abstract = true;
+ return saved;
+ }
+ llvm::Constant *validateAndPopAbstract(llvm::Constant *C, AbstractState save);
+};
+
+}
+}
+
+#endif
diff --git a/lib/CodeGen/CoverageMappingGen.cpp b/lib/CodeGen/CoverageMappingGen.cpp
index a102347..8f6f3ed 100644
--- a/lib/CodeGen/CoverageMappingGen.cpp
+++ b/lib/CodeGen/CoverageMappingGen.cpp
@@ -29,7 +29,7 @@
using namespace CodeGen;
using namespace llvm::coverage;
-void CoverageSourceInfo::SourceRangeSkipped(SourceRange Range) {
+void CoverageSourceInfo::SourceRangeSkipped(SourceRange Range, SourceLocation) {
SkippedRanges.push_back(Range);
}
@@ -45,10 +45,19 @@
/// \brief The region's ending location.
Optional<SourceLocation> LocEnd;
+ /// Whether this region should be emitted after its parent is emitted.
+ bool DeferRegion;
+
+ /// Whether this region is a gap region. The count from a gap region is set
+ /// as the line execution count if there are no other regions on the line.
+ bool GapRegion;
+
public:
SourceMappingRegion(Counter Count, Optional<SourceLocation> LocStart,
- Optional<SourceLocation> LocEnd)
- : Count(Count), LocStart(LocStart), LocEnd(LocEnd) {}
+ Optional<SourceLocation> LocEnd, bool DeferRegion = false,
+ bool GapRegion = false)
+ : Count(Count), LocStart(LocStart), LocEnd(LocEnd),
+ DeferRegion(DeferRegion), GapRegion(GapRegion) {}
const Counter &getCounter() const { return Count; }
@@ -71,6 +80,44 @@
assert(LocEnd && "Region has no end location");
return *LocEnd;
}
+
+ bool isDeferred() const { return DeferRegion; }
+
+ void setDeferred(bool Deferred) { DeferRegion = Deferred; }
+
+ bool isGap() const { return GapRegion; }
+
+ void setGap(bool Gap) { GapRegion = Gap; }
+};
+
+/// Spelling locations for the start and end of a source region.
+struct SpellingRegion {
+ /// The line where the region starts.
+ unsigned LineStart;
+
+ /// The column where the region starts.
+ unsigned ColumnStart;
+
+ /// The line where the region ends.
+ unsigned LineEnd;
+
+ /// The column where the region ends.
+ unsigned ColumnEnd;
+
+ SpellingRegion(SourceManager &SM, SourceLocation LocStart,
+ SourceLocation LocEnd) {
+ LineStart = SM.getSpellingLineNumber(LocStart);
+ ColumnStart = SM.getSpellingColumnNumber(LocStart);
+ LineEnd = SM.getSpellingLineNumber(LocEnd);
+ ColumnEnd = SM.getSpellingColumnNumber(LocEnd);
+ }
+
+ /// Check if the start and end locations appear in source order, i.e
+ /// top->bottom, left->right.
+ bool isInSourceOrder() const {
+ return (LineStart < LineEnd) ||
+ (LineStart == LineEnd && ColumnStart <= ColumnEnd);
+ }
};
/// \brief Provides the common functionality for the different
@@ -241,12 +288,9 @@
auto CovFileID = getCoverageFileID(LocStart);
if (!CovFileID)
continue;
- unsigned LineStart = SM.getSpellingLineNumber(LocStart);
- unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart);
- unsigned LineEnd = SM.getSpellingLineNumber(LocEnd);
- unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd);
+ SpellingRegion SR{SM, LocStart, LocEnd};
auto Region = CounterMappingRegion::makeSkipped(
- *CovFileID, LineStart, ColumnStart, LineEnd, ColumnEnd);
+ *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd);
// Make sure that we only collect the regions that are inside
// the souce code of this function.
if (Region.LineStart >= FileLineRanges[*CovFileID].first &&
@@ -284,16 +328,19 @@
if (Filter.count(std::make_pair(LocStart, LocEnd)))
continue;
- // Find the spilling locations for the mapping region.
- unsigned LineStart = SM.getSpellingLineNumber(LocStart);
- unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart);
- unsigned LineEnd = SM.getSpellingLineNumber(LocEnd);
- unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd);
+ // Find the spelling locations for the mapping region.
+ SpellingRegion SR{SM, LocStart, LocEnd};
+ assert(SR.isInSourceOrder() && "region start and end out of order");
- assert(LineStart <= LineEnd && "region start and end out of order");
- MappingRegions.push_back(CounterMappingRegion::makeRegion(
- Region.getCounter(), *CovFileID, LineStart, ColumnStart, LineEnd,
- ColumnEnd));
+ if (Region.isGap()) {
+ MappingRegions.push_back(CounterMappingRegion::makeGapRegion(
+ Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
+ SR.LineEnd, SR.ColumnEnd));
+ } else {
+ MappingRegions.push_back(CounterMappingRegion::makeRegion(
+ Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
+ SR.LineEnd, SR.ColumnEnd));
+ }
}
}
@@ -317,14 +364,11 @@
"region spans multiple files");
Filter.insert(std::make_pair(ParentLoc, LocEnd));
- unsigned LineStart = SM.getSpellingLineNumber(ParentLoc);
- unsigned ColumnStart = SM.getSpellingColumnNumber(ParentLoc);
- unsigned LineEnd = SM.getSpellingLineNumber(LocEnd);
- unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd);
-
+ SpellingRegion SR{SM, ParentLoc, LocEnd};
+ assert(SR.isInSourceOrder() && "region start and end out of order");
MappingRegions.push_back(CounterMappingRegion::makeExpansion(
- *ParentFileID, *ExpandedFileID, LineStart, ColumnStart, LineEnd,
- ColumnEnd));
+ *ParentFileID, *ExpandedFileID, SR.LineStart, SR.ColumnStart,
+ SR.LineEnd, SR.ColumnEnd));
}
return Filter;
}
@@ -389,6 +433,10 @@
/// \brief A stack of currently live regions.
std::vector<SourceMappingRegion> RegionStack;
+ /// The currently deferred region: its end location and count can be set once
+ /// its parent has been popped from the region stack.
+ Optional<SourceMappingRegion> DeferredRegion;
+
CounterExpressionBuilder Builder;
/// \brief A location in the most recently visited file or macro.
@@ -424,19 +472,61 @@
/// used with popRegions to exit a "scope", ending the region that was pushed.
size_t pushRegion(Counter Count, Optional<SourceLocation> StartLoc = None,
Optional<SourceLocation> EndLoc = None) {
- if (StartLoc)
+ if (StartLoc) {
MostRecentLocation = *StartLoc;
+ completeDeferred(Count, MostRecentLocation);
+ }
RegionStack.emplace_back(Count, StartLoc, EndLoc);
return RegionStack.size() - 1;
}
+ /// Complete any pending deferred region by setting its end location and
+ /// count, and then pushing it onto the region stack.
+ size_t completeDeferred(Counter Count, SourceLocation DeferredEndLoc) {
+ size_t Index = RegionStack.size();
+ if (!DeferredRegion)
+ return Index;
+
+ // Consume the pending region.
+ SourceMappingRegion DR = DeferredRegion.getValue();
+ DeferredRegion = None;
+
+ // If the region ends in an expansion, find the expansion site.
+ if (SM.getFileID(DeferredEndLoc) != SM.getMainFileID()) {
+ FileID StartFile = SM.getFileID(DR.getStartLoc());
+ if (isNestedIn(DeferredEndLoc, StartFile)) {
+ do {
+ DeferredEndLoc = getIncludeOrExpansionLoc(DeferredEndLoc);
+ } while (StartFile != SM.getFileID(DeferredEndLoc));
+ }
+ }
+
+ // The parent of this deferred region ends where the containing decl ends,
+ // so the region isn't useful.
+ if (DR.getStartLoc() == DeferredEndLoc)
+ return Index;
+
+ // If we're visiting statements in non-source order (e.g switch cases or
+ // a loop condition) we can't construct a sensible deferred region.
+ if (!SpellingRegion(SM, DR.getStartLoc(), DeferredEndLoc).isInSourceOrder())
+ return Index;
+
+ DR.setGap(true);
+ DR.setCounter(Count);
+ DR.setEndLoc(DeferredEndLoc);
+ handleFileExit(DeferredEndLoc);
+ RegionStack.push_back(DR);
+ return Index;
+ }
+
/// \brief Pop regions from the stack into the function's list of regions.
///
/// Adds all regions from \c ParentIndex to the top of the stack to the
/// function's \c SourceRegions.
void popRegions(size_t ParentIndex) {
assert(RegionStack.size() >= ParentIndex && "parent not in stack");
+ bool ParentOfDeferredRegion = false;
while (RegionStack.size() > ParentIndex) {
SourceMappingRegion &Region = RegionStack.back();
if (Region.hasStartLoc()) {
@@ -468,9 +558,26 @@
assert(SM.isWrittenInSameFile(Region.getStartLoc(), EndLoc));
SourceRegions.push_back(Region);
+
+ if (ParentOfDeferredRegion) {
+ ParentOfDeferredRegion = false;
+
+ // If there's an existing deferred region, keep the old one, because
+ // it means there are two consecutive returns (or a similar pattern).
+ if (!DeferredRegion.hasValue() &&
+ // File IDs aren't gathered within macro expansions, so it isn't
+ // useful to try and create a deferred region inside of one.
+ (SM.getFileID(EndLoc) == SM.getMainFileID()))
+ DeferredRegion =
+ SourceMappingRegion(Counter::getZero(), EndLoc, None);
+ }
+ } else if (Region.isDeferred()) {
+ assert(!ParentOfDeferredRegion && "Consecutive deferred regions");
+ ParentOfDeferredRegion = true;
}
RegionStack.pop_back();
}
+ assert(!ParentOfDeferredRegion && "Deferred region with no parent");
}
/// \brief Return the currently active region.
@@ -481,15 +588,17 @@
/// \brief Propagate counts through the children of \c S.
Counter propagateCounts(Counter TopCount, const Stmt *S) {
- size_t Index = pushRegion(TopCount, getStart(S), getEnd(S));
+ SourceLocation StartLoc = getStart(S);
+ SourceLocation EndLoc = getEnd(S);
+ size_t Index = pushRegion(TopCount, StartLoc, EndLoc);
Visit(S);
Counter ExitCount = getRegion().getCounter();
popRegions(Index);
// The statement may be spanned by an expansion. Make sure we handle a file
// exit out of this expansion before moving to the next statement.
- if (SM.isBeforeInTranslationUnit(getStart(S), S->getLocStart()))
- MostRecentLocation = getEnd(S);
+ if (SM.isBeforeInTranslationUnit(StartLoc, S->getLocStart()))
+ MostRecentLocation = EndLoc;
return ExitCount;
}
@@ -595,6 +704,8 @@
handleFileExit(StartLoc);
if (!Region.hasStartLoc())
Region.setStartLoc(StartLoc);
+
+ completeDeferred(Region.getCounter(), StartLoc);
}
/// \brief Mark \c S as a terminator, starting a zero region.
@@ -604,6 +715,7 @@
if (!Region.hasEndLoc())
Region.setEndLoc(getEnd(S));
pushRegion(Counter::getZero());
+ getRegion().setDeferred(true);
}
/// \brief Keep counts of breaks and continues inside loops.
@@ -617,13 +729,15 @@
CoverageMappingModuleGen &CVM,
llvm::DenseMap<const Stmt *, unsigned> &CounterMap, SourceManager &SM,
const LangOptions &LangOpts)
- : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap) {}
+ : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
+ DeferredRegion(None) {}
/// \brief Write the mapping data to the output stream
void write(llvm::raw_ostream &OS) {
llvm::SmallVector<unsigned, 8> VirtualFileMapping;
gatherFileIDs(VirtualFileMapping);
SourceRegionFilter Filter = emitExpansionRegions();
+ assert(!DeferredRegion && "Deferred region never completed");
emitSourceRegions(Filter);
gatherSkippedRegions();
@@ -645,13 +759,25 @@
}
void VisitDecl(const Decl *D) {
+ assert(!DeferredRegion && "Deferred region never completed");
+
Stmt *Body = D->getBody();
// Do not propagate region counts into system headers.
if (Body && SM.isInSystemHeader(SM.getSpellingLoc(getStart(Body))))
return;
- propagateCounts(getRegionCounter(Body), Body);
+ Counter ExitCount = propagateCounts(getRegionCounter(Body), Body);
+ assert(RegionStack.empty() && "Regions entered but never exited");
+
+ // Special case: if the last statement is a return, throw away the
+ // deferred region. This allows the closing brace to have a count.
+ if (auto *CS = dyn_cast_or_null<CompoundStmt>(Body))
+ if (dyn_cast_or_null<ReturnStmt>(CS->body_back()))
+ DeferredRegion = None;
+
+ // Complete any deferred regions introduced by the last statement.
+ popRegions(completeDeferred(ExitCount, getEnd(Body)));
}
void VisitReturnStmt(const ReturnStmt *S) {
@@ -682,6 +808,8 @@
assert(!BreakContinueStack.empty() && "break not in a loop or switch!");
BreakContinueStack.back().BreakCount = addCounters(
BreakContinueStack.back().BreakCount, getRegion().getCounter());
+ // FIXME: a break in a switch should terminate regions for all preceding
+ // case statements, not just the most recent one.
terminateRegion(S);
}
@@ -692,6 +820,16 @@
terminateRegion(S);
}
+ void VisitCallExpr(const CallExpr *E) {
+ VisitStmt(E);
+
+ // Terminate the region when we hit a noreturn function.
+ // (This is helpful dealing with switch statements.)
+ QualType CalleeType = E->getCallee()->getType();
+ if (getFunctionExtInfo(*CalleeType).getNoReturn())
+ terminateRegion(E);
+ }
+
void VisitWhileStmt(const WhileStmt *S) {
extendRegion(S);
@@ -823,15 +961,20 @@
extendRegion(Body);
if (const auto *CS = dyn_cast<CompoundStmt>(Body)) {
if (!CS->body_empty()) {
- // The body of the switch needs a zero region so that fallthrough counts
- // behave correctly, but it would be misleading to include the braces of
- // the compound statement in the zeroed area, so we need to handle this
- // specially.
+ // Make a region for the body of the switch. If the body starts with
+ // a case, that case will reuse this region; otherwise, this covers
+ // the unreachable code at the beginning of the switch body.
size_t Index =
- pushRegion(Counter::getZero(), getStart(CS->body_front()),
- getEnd(CS->body_back()));
+ pushRegion(Counter::getZero(), getStart(CS->body_front()));
for (const auto *Child : CS->children())
Visit(Child);
+
+ // Set the end for the body of the switch, if it isn't already set.
+ for (size_t i = RegionStack.size(); i != Index; --i) {
+ if (!RegionStack[i - 1].hasEndLoc())
+ RegionStack[i - 1].setEndLoc(getEnd(CS->body_back()));
+ }
+
popRegions(Index);
}
} else
@@ -992,6 +1135,9 @@
case CounterMappingRegion::SkippedRegion:
OS << "Skipped,";
break;
+ case CounterMappingRegion::GapRegion:
+ OS << "Gap,";
+ break;
}
OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart
diff --git a/lib/CodeGen/CoverageMappingGen.h b/lib/CodeGen/CoverageMappingGen.h
index b6789c2..d07ed5e 100644
--- a/lib/CodeGen/CoverageMappingGen.h
+++ b/lib/CodeGen/CoverageMappingGen.h
@@ -39,7 +39,7 @@
public:
ArrayRef<SourceRange> getSkippedRanges() const { return SkippedRanges; }
- void SourceRangeSkipped(SourceRange Range) override;
+ void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override;
};
namespace CodeGen {
diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp
index bd4cb9a..c82b967 100644
--- a/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/lib/CodeGen/ItaniumCXXABI.cpp
@@ -62,20 +62,12 @@
bool classifyReturnType(CGFunctionInfo &FI) const override;
- bool passClassIndirect(const CXXRecordDecl *RD) const {
- // Clang <= 4 used the pre-C++11 rule, which ignores move operations.
- // The PS4 platform ABI follows the behavior of Clang 3.2.
- if (CGM.getCodeGenOpts().getClangABICompat() <=
- CodeGenOptions::ClangABI::Ver4 ||
- CGM.getTriple().getOS() == llvm::Triple::PS4)
- return RD->hasNonTrivialDestructor() ||
- RD->hasNonTrivialCopyConstructor();
- return !canCopyArgument(RD);
- }
-
RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override {
- // If C++ prohibits us from making a copy, pass by address.
- if (passClassIndirect(RD))
+ // Structures with either a non-trivial destructor or a non-trivial
+ // copy constructor are always indirect.
+ // FIXME: Use canCopyArgument() when it is fixed to handle lazily declared
+ // special members.
+ if (RD->hasNonTrivialDestructor() || RD->hasNonTrivialCopyConstructor())
return RAA_Indirect;
return RAA_Default;
}
@@ -1006,8 +998,10 @@
if (!RD)
return false;
- // If C++ prohibits us from making a copy, return by address.
- if (passClassIndirect(RD)) {
+ // Return indirectly if we have a non-trivial copy ctor or non-trivial dtor.
+ // FIXME: Use canCopyArgument() when it is fixed to handle lazily declared
+ // special members.
+ if (RD->hasNonTrivialDestructor() || RD->hasNonTrivialCopyConstructor()) {
auto Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType());
FI.getReturnInfo() = ABIArgInfo::getIndirect(Align, /*ByVal=*/false);
return true;
diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp
index 1bd2937..78b510b 100644
--- a/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -819,44 +819,46 @@
return RAA_Default;
case llvm::Triple::x86_64:
- // If a class has a destructor, we'd really like to pass it indirectly
+ // Win64 passes objects with non-trivial copy ctors indirectly.
+ if (RD->hasNonTrivialCopyConstructor())
+ return RAA_Indirect;
+
+ // If an object has a destructor, we'd really like to pass it indirectly
// because it allows us to elide copies. Unfortunately, MSVC makes that
// impossible for small types, which it will pass in a single register or
// stack slot. Most objects with dtors are large-ish, so handle that early.
// We can't call out all large objects as being indirect because there are
// multiple x64 calling conventions and the C++ ABI code shouldn't dictate
// how we pass large POD types.
- //
- // Note: This permits small classes with nontrivial destructors to be
- // passed in registers, which is non-conforming.
if (RD->hasNonTrivialDestructor() &&
getContext().getTypeSize(RD->getTypeForDecl()) > 64)
return RAA_Indirect;
- // If a class has at least one non-deleted, trivial copy constructor, it
- // is passed according to the C ABI. Otherwise, it is passed indirectly.
- //
- // Note: This permits classes with non-trivial copy or move ctors to be
- // passed in registers, so long as they *also* have a trivial copy ctor,
- // which is non-conforming.
- if (RD->needsImplicitCopyConstructor()) {
- // If the copy ctor has not yet been declared, we can read its triviality
- // off the AST.
- if (!RD->defaultedCopyConstructorIsDeleted() &&
- RD->hasTrivialCopyConstructor())
- return RAA_Default;
- } else {
- // Otherwise, we need to find the copy constructor(s) and ask.
- for (const CXXConstructorDecl *CD : RD->ctors()) {
- if (CD->isCopyConstructor()) {
- // We had at least one nondeleted trivial copy ctor. Return directly.
- if (!CD->isDeleted() && CD->isTrivial())
- return RAA_Default;
- }
+ // If this is true, the implicit copy constructor that Sema would have
+ // created would not be deleted. FIXME: We should provide a more direct way
+ // for CodeGen to ask whether the constructor was deleted.
+ if (!RD->hasUserDeclaredCopyConstructor() &&
+ !RD->hasUserDeclaredMoveConstructor() &&
+ !RD->needsOverloadResolutionForMoveConstructor() &&
+ !RD->hasUserDeclaredMoveAssignment() &&
+ !RD->needsOverloadResolutionForMoveAssignment())
+ return RAA_Default;
+
+ // Otherwise, Sema should have created an implicit copy constructor if
+ // needed.
+ assert(!RD->needsImplicitCopyConstructor());
+
+ // We have to make sure the trivial copy constructor isn't deleted.
+ for (const CXXConstructorDecl *CD : RD->ctors()) {
+ if (CD->isCopyConstructor()) {
+ assert(CD->isTrivial());
+ // We had at least one undeleted trivial copy ctor. Return directly.
+ if (!CD->isDeleted())
+ return RAA_Default;
}
}
- // We have no trivial, non-deleted copy constructor.
+ // The trivial copy constructor was deleted. Return indirectly.
return RAA_Indirect;
}
diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp
index ece3a40..546439c 100644
--- a/lib/CodeGen/TargetInfo.cpp
+++ b/lib/CodeGen/TargetInfo.cpp
@@ -183,11 +183,7 @@
return CGT.getTarget();
}
-const CodeGenOptions &ABIInfo::getCodeGenOpts() const {
- return CGT.getCodeGenOpts();
-}
-
-bool ABIInfo::isAndroid() const { return getTarget().getTriple().isAndroid(); }
+bool ABIInfo:: isAndroid() const { return getTarget().getTriple().isAndroid(); }
bool ABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const {
return false;
@@ -1070,8 +1066,8 @@
getUBSanFunctionSignature(CodeGen::CodeGenModule &CGM) const override {
unsigned Sig = (0xeb << 0) | // jmp rel8
(0x06 << 8) | // .+0x08
- ('F' << 16) |
- ('T' << 24);
+ ('v' << 16) |
+ ('2' << 24);
return llvm::ConstantInt::get(CGM.Int32Ty, Sig);
}
@@ -2099,14 +2095,9 @@
return !getTarget().getTriple().isOSDarwin();
}
- /// GCC classifies <1 x long long> as SSE but some platform ABIs choose to
- /// classify it as INTEGER (for compatibility with older clang compilers).
+ /// GCC classifies <1 x long long> as SSE but compatibility with older clang
+ // compilers require us to classify it as INTEGER.
bool classifyIntegerMMXAsSSE() const {
- // Clang <= 3.8 did not do this.
- if (getCodeGenOpts().getClangABICompat() <=
- CodeGenOptions::ClangABI::Ver3_8)
- return false;
-
const llvm::Triple &Triple = getTarget().getTriple();
if (Triple.isOSDarwin() || Triple.getOS() == llvm::Triple::PS4)
return false;
@@ -2260,17 +2251,10 @@
llvm::Constant *
getUBSanFunctionSignature(CodeGen::CodeGenModule &CGM) const override {
- unsigned Sig;
- if (getABIInfo().has64BitPointers())
- Sig = (0xeb << 0) | // jmp rel8
- (0x0a << 8) | // .+0x0c
- ('F' << 16) |
- ('T' << 24);
- else
- Sig = (0xeb << 0) | // jmp rel8
- (0x06 << 8) | // .+0x08
- ('F' << 16) |
- ('T' << 24);
+ unsigned Sig = (0xeb << 0) | // jmp rel8
+ (0x06 << 8) | // .+0x08
+ ('v' << 16) |
+ ('2' << 24);
return llvm::ConstantInt::get(CGM.Int32Ty, Sig);
}
diff --git a/lib/DirectoryWatcher/CMakeLists.txt b/lib/DirectoryWatcher/CMakeLists.txt
new file mode 100644
index 0000000..425a40f
--- /dev/null
+++ b/lib/DirectoryWatcher/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangDirectoryWatcher
+ DirectoryWatcher.cpp
+
+ LINK_LIBS
+ clangBasic
+ )
diff --git a/lib/DirectoryWatcher/DirectoryWatcher.cpp b/lib/DirectoryWatcher/DirectoryWatcher.cpp
new file mode 100644
index 0000000..3a90526
--- /dev/null
+++ b/lib/DirectoryWatcher/DirectoryWatcher.cpp
@@ -0,0 +1,275 @@
+//===- DirectoryWatcher.cpp - Listens for directory file changes ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// \brief Utility class for listening for file system changes in a directory.
+//===----------------------------------------------------------------------===//
+
+#include "clang/DirectoryWatcher/DirectoryWatcher.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define HAVE_CORESERVICES 0
+
+#if defined(__has_include)
+#if __has_include(<CoreServices/CoreServices.h>)
+
+#include <CoreServices/CoreServices.h>
+#undef HAVE_CORESERVICES
+#define HAVE_CORESERVICES 1
+
+#endif
+#endif
+
+using namespace clang;
+using namespace llvm;
+
+static timespec toTimeSpec(sys::TimePoint<> tp) {
+ std::chrono::seconds sec = std::chrono::time_point_cast<std::chrono::seconds>(
+ tp).time_since_epoch();
+ std::chrono::nanoseconds nsec =
+ std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec)
+ .time_since_epoch();
+ timespec ts;
+ ts.tv_sec = sec.count();
+ ts.tv_nsec = nsec.count();
+ return ts;
+}
+
+static Optional<timespec> getModTime(StringRef path) {
+ sys::fs::file_status Status;
+ std::error_code EC = status(path, Status);
+ if (EC)
+ return None;
+ return toTimeSpec(Status.getLastModificationTime());
+}
+
+struct DirectoryWatcher::Implementation {
+#if HAVE_CORESERVICES
+ FSEventStreamRef EventStream = nullptr;
+
+ bool setupFSEventStream(StringRef path, EventReceiver receiver,
+ dispatch_queue_t queue);
+ void stopFSEventStream();
+
+ ~Implementation() {
+ stopFSEventStream();
+ };
+#endif
+};
+
+#if HAVE_CORESERVICES
+namespace {
+struct EventStreamContextData {
+ std::string WatchedPath;
+ DirectoryWatcher::EventReceiver Receiver;
+
+ EventStreamContextData(std::string watchedPath, DirectoryWatcher::EventReceiver receiver)
+ : WatchedPath(std::move(watchedPath)), Receiver(std::move(receiver)) {
+ }
+
+ static void dispose(const void *ctx) {
+ delete static_cast<const EventStreamContextData*>(ctx);
+ }
+};
+}
+
+static void eventStreamCallback(
+ ConstFSEventStreamRef stream,
+ void *clientCallBackInfo,
+ size_t numEvents,
+ void *eventPaths,
+ const FSEventStreamEventFlags eventFlags[],
+ const FSEventStreamEventId eventIds[]) {
+ auto *ctx = static_cast<EventStreamContextData*>(clientCallBackInfo);
+
+ std::vector<DirectoryWatcher::Event> Events;
+ for (size_t i = 0; i < numEvents; ++i) {
+ StringRef path = ((const char **)eventPaths)[i];
+ const FSEventStreamEventFlags flags = eventFlags[i];
+ if (!(flags & kFSEventStreamEventFlagItemIsFile)) {
+ if ((flags & kFSEventStreamEventFlagItemRemoved) && path == ctx->WatchedPath) {
+ DirectoryWatcher::Event Evt{DirectoryWatcher::EventKind::DirectoryDeleted, path, timespec{}};
+ Events.push_back(Evt);
+ break;
+ }
+ continue;
+ }
+ DirectoryWatcher::EventKind K = DirectoryWatcher::EventKind::Modified;
+ if ((flags & kFSEventStreamEventFlagItemCreated) ||
+ (flags & kFSEventStreamEventFlagItemRenamed))
+ K = DirectoryWatcher::EventKind::Added;
+ if (flags & kFSEventStreamEventFlagItemRemoved)
+ K = DirectoryWatcher::EventKind::Removed;
+ timespec modTime{};
+ if (K != DirectoryWatcher::EventKind::Removed) {
+ auto modTimeOpt = getModTime(path);
+ if (!modTimeOpt.hasValue())
+ continue;
+ modTime = modTimeOpt.getValue();
+ }
+ DirectoryWatcher::Event Evt{K, path, modTime};
+ Events.push_back(Evt);
+ }
+
+ ctx->Receiver(Events, /*isInitial=*/false);
+}
+
+bool DirectoryWatcher::Implementation::setupFSEventStream(StringRef path,
+ EventReceiver receiver,
+ dispatch_queue_t queue) {
+ if (path.empty())
+ return true;
+
+ CFMutableArrayRef pathsToWatch = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
+ CFStringRef cfPathStr = CFStringCreateWithBytes(nullptr, (const UInt8 *)path.data(), path.size(), kCFStringEncodingUTF8, false);
+ CFArrayAppendValue(pathsToWatch, cfPathStr);
+ CFRelease(cfPathStr);
+ CFAbsoluteTime latency = 0.2; // Latency in seconds.
+
+ std::string realPath;
+ {
+ SmallString<128> Storage;
+ StringRef P = llvm::Twine(path).toNullTerminatedStringRef(Storage);
+ char Buffer[PATH_MAX];
+ // Use ::realpath to get the real path name
+ if (::realpath(P.begin(), Buffer) != nullptr)
+ realPath = Buffer;
+ else
+ realPath = path;
+ }
+
+ EventStreamContextData *ctxData = new EventStreamContextData(std::move(realPath), std::move(receiver));
+ FSEventStreamContext context;
+ context.version = 0;
+ context.info = ctxData;
+ context.retain = nullptr;
+ context.release = EventStreamContextData::dispose;
+ context.copyDescription = nullptr;
+
+ EventStream = FSEventStreamCreate(nullptr,
+ eventStreamCallback,
+ &context,
+ pathsToWatch,
+ kFSEventStreamEventIdSinceNow,
+ latency,
+ kFSEventStreamCreateFlagFileEvents |
+ kFSEventStreamCreateFlagNoDefer);
+ CFRelease(pathsToWatch);
+ if (!EventStream) {
+ return true;
+ }
+ FSEventStreamSetDispatchQueue(EventStream, queue);
+ FSEventStreamStart(EventStream);
+ return false;
+}
+
+void DirectoryWatcher::Implementation::stopFSEventStream() {
+ if (!EventStream)
+ return;
+ FSEventStreamStop(EventStream);
+ FSEventStreamInvalidate(EventStream);
+ FSEventStreamRelease(EventStream);
+ EventStream = nullptr;
+}
+#endif
+
+DirectoryWatcher::DirectoryWatcher()
+ : Impl(*new Implementation()) {}
+
+DirectoryWatcher::~DirectoryWatcher() {
+ delete &Impl;
+}
+
+#if HAVE_CORESERVICES
+static std::vector<DirectoryWatcher::Event> scanDirectory(StringRef Path) {
+ using namespace llvm::sys;
+
+ std::vector<DirectoryWatcher::Event> Events;
+ std::error_code EC;
+ for (auto It = fs::directory_iterator(Path, EC), End = fs::directory_iterator();
+ !EC && It != End; It.increment(EC)) {
+ auto modTime = getModTime(It->path());
+ if (!modTime.hasValue())
+ continue;
+ DirectoryWatcher::Event Event{DirectoryWatcher::EventKind::Added, It->path(), modTime.getValue()};
+ Events.push_back(std::move(Event));
+ }
+ return Events;
+}
+#endif
+
+std::unique_ptr<DirectoryWatcher> DirectoryWatcher::create(StringRef Path,
+ EventReceiver Receiver, bool waitInitialSync, std::string &Error) {
+#if HAVE_CORESERVICES
+
+ using namespace llvm::sys;
+
+ if (!fs::exists(Path)) {
+ std::error_code EC = fs::create_directories(Path);
+ if (EC) {
+ Error = EC.message();
+ return nullptr;
+ }
+ }
+
+ bool IsDir;
+ std::error_code EC = fs::is_directory(Path, IsDir);
+ if (EC) {
+ Error = EC.message();
+ return nullptr;
+ }
+ if (!IsDir) {
+ Error = "path is not a directory: ";
+ Error += Path;
+ return nullptr;
+ }
+
+ std::unique_ptr<DirectoryWatcher> DirWatch;
+ DirWatch.reset(new DirectoryWatcher());
+ auto &Impl = DirWatch->Impl;
+
+ dispatch_queue_t queue = dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL);
+ dispatch_semaphore_t initScanSema = dispatch_semaphore_create(0);
+ dispatch_semaphore_t setupFSEventsSema = dispatch_semaphore_create(0);
+
+ std::string copiedPath = Path;
+ dispatch_retain(initScanSema);
+ dispatch_retain(setupFSEventsSema);
+ dispatch_async(queue, ^{
+ // Wait for the event stream to be setup before doing the initial scan,
+ // to make sure we won't miss any events.
+ dispatch_semaphore_wait(setupFSEventsSema, DISPATCH_TIME_FOREVER);
+ auto events = scanDirectory(copiedPath);
+ Receiver(events, /*isInitial=*/true);
+ dispatch_semaphore_signal(initScanSema);
+ dispatch_release(setupFSEventsSema);
+ dispatch_release(initScanSema);
+ });
+ bool fsErr = Impl.setupFSEventStream(Path, Receiver, queue);
+ dispatch_semaphore_signal(setupFSEventsSema);
+
+ if (waitInitialSync) {
+ dispatch_semaphore_wait(initScanSema, DISPATCH_TIME_FOREVER);
+ }
+ dispatch_release(setupFSEventsSema);
+ dispatch_release(initScanSema);
+ dispatch_release(queue);
+
+ if (fsErr) {
+ raw_string_ostream(Error) << "failed to setup FSEvents stream for path: " << Path;
+ return nullptr;
+ }
+
+ return DirWatch;
+#else
+ return nullptr;
+#endif
+}
diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp
index ba4d0e8..dd95e7e 100644
--- a/lib/Driver/Driver.cpp
+++ b/lib/Driver/Driver.cpp
@@ -995,7 +995,9 @@
}
// Assume associated files are based off of the first temporary file.
- CrashReportInfo CrashInfo(TempFiles[0], VFS);
+ CrashReportInfo CrashInfo(
+ TempFiles[0], VFS,
+ C.getArgs().getLastArgValue(options::OPT_index_store_path));
std::string Script = CrashInfo.Filename.rsplit('.').first.str() + ".sh";
std::error_code EC;
diff --git a/lib/Driver/Job.cpp b/lib/Driver/Job.cpp
index 8b85680..355b9e0 100644
--- a/lib/Driver/Job.cpp
+++ b/lib/Driver/Job.cpp
@@ -67,6 +67,8 @@
.Default(false);
if (IsInclude)
return HaveCrashVFS ? false : true;
+ if (StringRef(Flag).startswith("-index-store-path"))
+ return true;
// The remaining flags are treated as a single argument.
@@ -88,6 +90,8 @@
return HaveCrashVFS ? false : true;
if (FlagRef.startswith("-fmodules-cache-path="))
return true;
+ if (FlagRef.startswith("-fapinotes-cache-path="))
+ return true;
SkipNum = 0;
return false;
@@ -219,6 +223,7 @@
}
bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty();
+ bool HaveIndexStorePath = CrashInfo && !CrashInfo->IndexStorePath.empty();
for (size_t i = 0, e = Args.size(); i < e; ++i) {
const char *const Arg = Args[i];
@@ -282,6 +287,24 @@
printArg(OS, ModCachePath, Quote);
}
+ if (CrashInfo && HaveIndexStorePath) {
+ SmallString<128> IndexStoreDir;
+
+ if (HaveCrashVFS) {
+ IndexStoreDir = llvm::sys::path::parent_path(
+ llvm::sys::path::parent_path(CrashInfo->VFSPath));
+ llvm::sys::path::append(IndexStoreDir, "index-store");
+ } else {
+ IndexStoreDir = "index-store";
+ }
+
+ OS << ' ';
+ printArg(OS, "-index-store-path", Quote);
+ OS << ' ';
+ printArg(OS, IndexStoreDir.c_str(), Quote);
+ }
+
+
if (ResponseFile != nullptr) {
OS << "\n Arguments passed via response file:\n";
writeResponseFile(OS);
diff --git a/lib/Driver/SanitizerArgs.cpp b/lib/Driver/SanitizerArgs.cpp
index 7a442c8..65ebe7d 100644
--- a/lib/Driver/SanitizerArgs.cpp
+++ b/lib/Driver/SanitizerArgs.cpp
@@ -29,10 +29,11 @@
NeedsUbsanRt = Undefined | Integer | Nullability | CFI,
NeedsUbsanCxxRt = Vptr | CFI,
NotAllowedWithTrap = Vptr,
+ NotAllowedWithMinimalRuntime = Vptr,
RequiresPIE = DataFlow,
NeedsUnwindTables = Address | Thread | Memory | DataFlow,
SupportsCoverage = Address | KernelAddress | Memory | Leak | Undefined |
- Integer | Nullability | DataFlow | Fuzzer,
+ Integer | Nullability | DataFlow | Fuzzer | FuzzerNoLink,
RecoverableByDefault = Undefined | Integer | Nullability,
Unrecoverable = Unreachable | Return,
LegacyFsanitizeRecoverMask = Undefined | Integer,
@@ -41,6 +42,7 @@
Nullability | LocalBounds | CFI,
TrappingDefault = CFI,
CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast,
+ CompatibleWithMinimalRuntime = TrappingSupported,
};
enum CoverageFeature {
@@ -100,6 +102,8 @@
BlacklistFile = "dfsan_abilist.txt";
else if (Kinds & CFI)
BlacklistFile = "cfi_blacklist.txt";
+ else if (Kinds & (Undefined | Integer | Nullability))
+ BlacklistFile = "ubsan_blacklist.txt";
if (BlacklistFile) {
clang::SmallString<64> Path(D.ResourceDir);
@@ -208,6 +212,10 @@
SanitizerMask TrappingKinds = parseSanitizeTrapArgs(D, Args);
SanitizerMask InvalidTrappingKinds = TrappingKinds & NotAllowedWithTrap;
+ MinimalRuntime =
+ Args.hasFlag(options::OPT_fsanitize_minimal_runtime,
+ options::OPT_fno_sanitize_minimal_runtime, MinimalRuntime);
+
// The object size sanitizer should not be enabled at -O0.
Arg *OptLevel = Args.getLastArg(options::OPT_O_Group);
bool RemoveObjectSizeAtO0 =
@@ -245,6 +253,18 @@
DiagnosedKinds |= KindsToDiagnose;
}
Add &= ~InvalidTrappingKinds;
+
+ if (MinimalRuntime) {
+ if (SanitizerMask KindsToDiagnose =
+ Add & NotAllowedWithMinimalRuntime & ~DiagnosedKinds) {
+ std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
+ D.Diag(diag::err_drv_argument_not_allowed_with)
+ << Desc << "-fsanitize-minimal-runtime";
+ DiagnosedKinds |= KindsToDiagnose;
+ }
+ Add &= ~NotAllowedWithMinimalRuntime;
+ }
+
if (SanitizerMask KindsToDiagnose = Add & ~Supported & ~DiagnosedKinds) {
std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
D.Diag(diag::err_drv_unsupported_opt_for_target)
@@ -281,11 +301,18 @@
// Silently discard any unsupported sanitizers implicitly enabled through
// group expansion.
Add &= ~InvalidTrappingKinds;
+ if (MinimalRuntime) {
+ Add &= ~NotAllowedWithMinimalRuntime;
+ }
Add &= Supported;
- // Enable coverage if the fuzzing flag is set.
if (Add & Fuzzer)
- CoverageFeatures |= CoverageTracePCGuard | CoverageIndirCall | CoverageTraceCmp;
+ Add |= FuzzerNoLink;
+
+ // Enable coverage if the fuzzing flag is set.
+ if (Add & FuzzerNoLink)
+ CoverageFeatures |= CoverageTracePCGuard | CoverageIndirCall |
+ CoverageTraceCmp;
Kinds |= Add;
} else if (Arg->getOption().matches(options::OPT_fno_sanitize_EQ)) {
@@ -484,6 +511,21 @@
Stats = Args.hasFlag(options::OPT_fsanitize_stats,
options::OPT_fno_sanitize_stats, false);
+ if (MinimalRuntime) {
+ SanitizerMask IncompatibleMask =
+ Kinds & ~setGroupBits(CompatibleWithMinimalRuntime);
+ if (IncompatibleMask)
+ D.Diag(clang::diag::err_drv_argument_not_allowed_with)
+ << "-fsanitize-minimal-runtime"
+ << lastArgumentForMask(D, Args, IncompatibleMask);
+
+ SanitizerMask NonTrappingCfi = Kinds & CFI & ~TrappingKinds;
+ if (NonTrappingCfi)
+ D.Diag(clang::diag::err_drv_argument_only_allowed_with)
+ << "fsanitize-minimal-runtime"
+ << "fsanitize-trap=cfi";
+ }
+
// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
// enabled sanitizers.
for (const auto *Arg : Args) {
@@ -736,6 +778,9 @@
if (Stats)
CmdArgs.push_back("-fsanitize-stats");
+ if (MinimalRuntime)
+ CmdArgs.push_back("-fsanitize-minimal-runtime");
+
if (AsanFieldPadding)
CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
llvm::utostr(AsanFieldPadding)));
diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp
index 9a858df..60d53f5 100644
--- a/lib/Driver/ToolChain.cpp
+++ b/lib/Driver/ToolChain.cpp
@@ -648,8 +648,16 @@
DriverArgs.AddAllArgs(CC1Args, options::OPT_stdlib_EQ);
}
+bool ToolChain::ShouldLinkCXXStdlib(const llvm::opt::ArgList &Args) const {
+ return getDriver().CCCIsCXX() &&
+ !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs,
+ options::OPT_nostdlibxx);
+}
+
void ToolChain::AddCXXStdlibLibArgs(const ArgList &Args,
ArgStringList &CmdArgs) const {
+ assert(!Args.hasArg(options::OPT_nostdlibxx) &&
+ "should not have called this");
CXXStdlibType Type = GetCXXStdlibType(Args);
switch (Type) {
diff --git a/lib/Driver/ToolChains/Ananas.cpp b/lib/Driver/ToolChains/Ananas.cpp
index a67e1d2..ee072cc 100644
--- a/lib/Driver/ToolChains/Ananas.cpp
+++ b/lib/Driver/ToolChains/Ananas.cpp
@@ -91,11 +91,10 @@
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
- if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- if (D.CCCIsCXX())
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (ToolChain.ShouldLinkCXXStdlib(Args))
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs))
CmdArgs.push_back("-lc");
- }
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nostartfiles)) {
CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath("crtend.o")));
diff --git a/lib/Driver/ToolChains/BareMetal.cpp b/lib/Driver/ToolChains/BareMetal.cpp
index 28e4f5b..57a6686 100644
--- a/lib/Driver/ToolChains/BareMetal.cpp
+++ b/lib/Driver/ToolChains/BareMetal.cpp
@@ -184,10 +184,9 @@
options::OPT_e, options::OPT_s, options::OPT_t,
options::OPT_Z_Flag, options::OPT_r});
+ if (TC.ShouldLinkCXXStdlib(Args))
+ TC.AddCXXStdlibLibArgs(Args, CmdArgs);
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- if (C.getDriver().CCCIsCXX())
- TC.AddCXXStdlibLibArgs(Args, CmdArgs);
-
CmdArgs.push_back("-lc");
CmdArgs.push_back("-lm");
diff --git a/lib/Driver/ToolChains/Clang.cpp b/lib/Driver/ToolChains/Clang.cpp
index 6a6b90f..def01ae 100644
--- a/lib/Driver/ToolChains/Clang.cpp
+++ b/lib/Driver/ToolChains/Clang.cpp
@@ -2532,6 +2532,15 @@
CmdArgs.push_back("-mpie-copy-relocations");
}
+ // -fhosted is default.
+ // TODO: Audit uses of KernelOrKext and see where it'd be more appropriate to
+ // use Freestanding.
+ bool Freestanding =
+ Args.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, false) ||
+ KernelOrKext;
+ if (Freestanding)
+ CmdArgs.push_back("-ffreestanding");
+
// This is a coarse approximation of what llvm-gcc actually does, both
// -fasynchronous-unwind-tables and -fnon-call-exceptions interact in more
// complicated ways.
@@ -2540,7 +2549,7 @@
options::OPT_fno_asynchronous_unwind_tables,
(getToolChain().IsUnwindTablesDefault(Args) ||
getToolChain().getSanitizerArgs().needsUnwindTables()) &&
- !KernelOrKext);
+ !Freestanding);
if (Args.hasFlag(options::OPT_funwind_tables, options::OPT_fno_unwind_tables,
AsynchronousUnwindTables))
CmdArgs.push_back("-munwind-tables");
@@ -2855,9 +2864,6 @@
addPGOAndCoverageFlags(C, D, Output, Args, CmdArgs);
- if (auto *ABICompatArg = Args.getLastArg(options::OPT_fclang_abi_compat_EQ))
- ABICompatArg->render(Args, CmdArgs);
-
// Add runtime flag for PS4 when PGO or Coverage are enabled.
if (getToolChain().getTriple().isPS4CPU())
PS4cpu::addProfileRTArgs(getToolChain(), Args, CmdArgs);
@@ -2949,6 +2955,26 @@
Args.AddLastArg(CmdArgs, options::OPT_objcmt_whitelist_dir_path);
}
+ if (Args.hasArg(options::OPT_index_store_path)) {
+ Args.AddLastArg(CmdArgs, options::OPT_index_store_path);
+ Args.AddLastArg(CmdArgs, options::OPT_index_ignore_system_symbols);
+ Args.AddLastArg(CmdArgs, options::OPT_index_record_codegen_name);
+
+ // If '-o' is passed along with '-fsyntax-only' pass it along the cc1
+ // invocation so that the index action knows what the out file is.
+ if (isa<CompileJobAction>(JA) && JA.getType() == types::TY_Nothing) {
+ Args.AddLastArg(CmdArgs, options::OPT_o);
+ }
+ }
+
+ if (const char *IdxStorePath = ::getenv("CLANG_PROJECT_INDEX_PATH")) {
+ CmdArgs.push_back("-index-store-path");
+ CmdArgs.push_back(IdxStorePath);
+ CmdArgs.push_back("-index-ignore-system-symbols");
+ CmdArgs.push_back("-index-record-codegen-name");
+ }
+
+
// Add preprocessing options like -I, -D, etc. if we are using the
// preprocessor.
//
@@ -3192,14 +3218,6 @@
Args.AddLastArg(CmdArgs, options::OPT_ftlsmodel_EQ);
- // -fhosted is default.
- bool IsHosted = true;
- if (Args.hasFlag(options::OPT_ffreestanding, options::OPT_fhosted, false) ||
- KernelOrKext) {
- CmdArgs.push_back("-ffreestanding");
- IsHosted = false;
- }
-
// Forward -f (flag) options which we can pass directly.
Args.AddLastArg(CmdArgs, options::OPT_femit_all_decls);
Args.AddLastArg(CmdArgs, options::OPT_fheinous_gnu_extensions);
@@ -3325,10 +3343,6 @@
} else {
StackProtectorLevel =
getToolChain().GetDefaultStackProtectorLevel(KernelOrKext);
- // Only use a default stack protector on Darwin in case -ffreestanding
- // is not specified.
- if (Triple.isOSDarwin() && !IsHosted)
- StackProtectorLevel = 0;
}
}
if (StackProtectorLevel) {
@@ -3484,6 +3498,19 @@
options::OPT_fno_assume_sane_operator_new))
CmdArgs.push_back("-fno-assume-sane-operator-new");
+ if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false) ||
+ Args.hasFlag(options::OPT_fapinotes_modules,
+ options::OPT_fno_apinotes_modules, false) ||
+ Args.hasArg(options::OPT_iapinotes_modules)) {
+ if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false))
+ CmdArgs.push_back("-fapinotes");
+ if (Args.hasFlag(options::OPT_fapinotes_modules,
+ options::OPT_fno_apinotes_modules, false))
+ CmdArgs.push_back("-fapinotes-modules");
+
+ Args.AddLastArg(CmdArgs, options::OPT_fapinotes_swift_version);
+ }
+
// -fblocks=0 is default.
if (Args.hasFlag(options::OPT_fblocks, options::OPT_fno_blocks,
getToolChain().IsBlocksDefault()) ||
diff --git a/lib/Driver/ToolChains/CloudABI.cpp b/lib/Driver/ToolChains/CloudABI.cpp
index 0f6c712..cdf807f 100644
--- a/lib/Driver/ToolChains/CloudABI.cpp
+++ b/lib/Driver/ToolChains/CloudABI.cpp
@@ -80,9 +80,9 @@
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
+ if (ToolChain.ShouldLinkCXXStdlib(Args))
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- if (D.CCCIsCXX())
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
CmdArgs.push_back("-lc");
CmdArgs.push_back("-lcompiler_rt");
}
diff --git a/lib/Driver/ToolChains/CommonArgs.cpp b/lib/Driver/ToolChains/CommonArgs.cpp
index 00bd60b..08c1bee 100644
--- a/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/lib/Driver/ToolChains/CommonArgs.cpp
@@ -541,6 +541,7 @@
if (SanArgs.needsAsanRt() && SanArgs.needsSharedAsanRt()) {
SharedRuntimes.push_back("asan");
}
+
// The stats_client library is also statically linked into DSOs.
if (SanArgs.needsStatsRt())
StaticRuntimes.push_back("stats_client");
@@ -574,9 +575,13 @@
StaticRuntimes.push_back("tsan_cxx");
}
if (SanArgs.needsUbsanRt()) {
- StaticRuntimes.push_back("ubsan_standalone");
- if (SanArgs.linkCXXRuntimes())
- StaticRuntimes.push_back("ubsan_standalone_cxx");
+ if (SanArgs.requiresMinimalRuntime()) {
+ StaticRuntimes.push_back("ubsan_minimal");
+ } else {
+ StaticRuntimes.push_back("ubsan_standalone");
+ if (SanArgs.linkCXXRuntimes())
+ StaticRuntimes.push_back("ubsan_standalone_cxx");
+ }
}
if (SanArgs.needsSafeStackRt()) {
NonWholeStaticRuntimes.push_back("safestack");
@@ -597,17 +602,6 @@
StaticRuntimes.push_back("esan");
}
-static void addLibFuzzerRuntime(const ToolChain &TC,
- const ArgList &Args,
- ArgStringList &CmdArgs) {
- StringRef ParentDir = llvm::sys::path::parent_path(TC.getDriver().InstalledDir);
- SmallString<128> P(ParentDir);
- llvm::sys::path::append(P, "lib", "libLLVMFuzzer.a");
- CmdArgs.push_back(Args.MakeArgString(P));
- TC.AddCXXStdlibLibArgs(Args, CmdArgs);
-}
-
-
// Should be called before we add system libraries (C++ ABI, libstdc++/libc++,
// C runtime, etc). Returns true if sanitizer system deps need to be linked in.
bool tools::addSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
@@ -617,10 +611,14 @@
collectSanitizerRuntimes(TC, Args, SharedRuntimes, StaticRuntimes,
NonWholeStaticRuntimes, HelperStaticRuntimes,
RequiredSymbols);
+
// Inject libfuzzer dependencies.
if (TC.getSanitizerArgs().needsFuzzer()
&& !Args.hasArg(options::OPT_shared)) {
- addLibFuzzerRuntime(TC, Args, CmdArgs);
+
+ addSanitizerRuntime(TC, Args, CmdArgs, "fuzzer", false, true);
+ if (!Args.hasArg(clang::driver::options::OPT_nostdlibxx))
+ TC.AddCXXStdlibLibArgs(Args, CmdArgs);
}
for (auto RT : SharedRuntimes)
diff --git a/lib/Driver/ToolChains/CrossWindows.cpp b/lib/Driver/ToolChains/CrossWindows.cpp
index 04b71c4..67ce08f 100644
--- a/lib/Driver/ToolChains/CrossWindows.cpp
+++ b/lib/Driver/ToolChains/CrossWindows.cpp
@@ -160,8 +160,7 @@
TC.AddFilePathLibArgs(Args, CmdArgs);
AddLinkerInputs(TC, Inputs, Args, CmdArgs, JA);
- if (D.CCCIsCXX() && !Args.hasArg(options::OPT_nostdlib) &&
- !Args.hasArg(options::OPT_nodefaultlibs)) {
+ if (TC.ShouldLinkCXXStdlib(Args)) {
bool StaticCXX = Args.hasArg(options::OPT_static_libstdcxx) &&
!Args.hasArg(options::OPT_static);
if (StaticCXX)
diff --git a/lib/Driver/ToolChains/Darwin.cpp b/lib/Driver/ToolChains/Darwin.cpp
index 32103a6..5f8e76e 100644
--- a/lib/Driver/ToolChains/Darwin.cpp
+++ b/lib/Driver/ToolChains/Darwin.cpp
@@ -10,6 +10,7 @@
#include "Darwin.h"
#include "Arch/ARM.h"
#include "CommonArgs.h"
+#include "clang/Basic/AlignedAllocation.h"
#include "clang/Basic/ObjCRuntime.h"
#include "clang/Basic/VirtualFileSystem.h"
#include "clang/Driver/Compilation.h"
@@ -435,6 +436,10 @@
// more information.
ArgStringList CmdArgs;
+ Args.ClaimAllArgs(options::OPT_index_store_path);
+ Args.ClaimAllArgs(options::OPT_index_ignore_system_symbols);
+ Args.ClaimAllArgs(options::OPT_index_record_codegen_name);
+
/// Hack(tm) to ignore linking errors when we are doing ARC migration.
if (Args.hasArg(options::OPT_ccc_arcmt_check,
options::OPT_ccc_arcmt_migrate)) {
@@ -493,7 +498,7 @@
if (getToolChain().getSanitizerArgs().needsSafeStackRt()) {
getMachOToolChain().AddLinkRuntimeLib(Args, CmdArgs,
"libclang_rt.safestack_osx.a",
- /*AlwaysLink=*/true);
+ toolchains::Darwin::RLO_AlwaysLink);
}
Args.AddAllArgs(CmdArgs, options::OPT_L);
@@ -548,10 +553,9 @@
Args.MakeArgString(Twine("-threads=") + llvm::to_string(Parallelism)));
}
+ if (getToolChain().ShouldLinkCXXStdlib(Args))
+ getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- if (getToolChain().getDriver().CCCIsCXX())
- getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
-
// link_ssp spec is empty.
// Let the tool chain choose which runtime library to link.
@@ -897,10 +901,11 @@
}
void MachO::AddLinkRuntimeLib(const ArgList &Args, ArgStringList &CmdArgs,
- StringRef DarwinLibName, bool AlwaysLink,
- bool IsEmbedded, bool AddRPath) const {
+ StringRef DarwinLibName,
+ RuntimeLinkOptions Opts) const {
SmallString<128> Dir(getDriver().ResourceDir);
- llvm::sys::path::append(Dir, "lib", IsEmbedded ? "macho_embedded" : "darwin");
+ llvm::sys::path::append(
+ Dir, "lib", (Opts & RLO_IsEmbedded) ? "macho_embedded" : "darwin");
SmallString<128> P(Dir);
llvm::sys::path::append(P, DarwinLibName);
@@ -908,14 +913,19 @@
// For now, allow missing resource libraries to support developers who may
// not have compiler-rt checked out or integrated into their build (unless
// we explicitly force linking with this library).
- if (AlwaysLink || getVFS().exists(P))
- CmdArgs.push_back(Args.MakeArgString(P));
+ if ((Opts & RLO_AlwaysLink) || getVFS().exists(P)) {
+ const char *LibArg = Args.MakeArgString(P);
+ if (Opts & RLO_FirstLink)
+ CmdArgs.insert(CmdArgs.begin(), LibArg);
+ else
+ CmdArgs.push_back(LibArg);
+ }
// Adding the rpaths might negatively interact when other rpaths are involved,
// so we should make sure we add the rpaths last, after all user-specified
// rpaths. This is currently true from this place, but we need to be
// careful if this function is ever called before user's rpaths are emitted.
- if (AddRPath) {
+ if (Opts & RLO_AddRPath) {
assert(DarwinLibName.endswith(".dylib") && "must be a dynamic library");
// Add @executable_path to rpath to support having the dylib copied with
@@ -930,18 +940,6 @@
}
}
-void MachO::AddFuzzerLinkArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
-
- // Go up one directory from Clang to find the libfuzzer archive file.
- StringRef ParentDir = llvm::sys::path::parent_path(getDriver().InstalledDir);
- SmallString<128> P(ParentDir);
- llvm::sys::path::append(P, "lib", "libLLVMFuzzer.a");
- CmdArgs.push_back(Args.MakeArgString(P));
-
- // Libfuzzer is written in C++ and requires libcxx.
- AddCXXStdlibLibArgs(Args, CmdArgs);
-}
-
StringRef Darwin::getPlatformFamily() const {
switch (TargetPlatform) {
case DarwinPlatformKind::MacOS:
@@ -996,20 +994,23 @@
ArgStringList &CmdArgs) const {
if (!needsProfileRT(Args)) return;
- AddLinkRuntimeLib(Args, CmdArgs, (Twine("libclang_rt.profile_") +
- getOSLibraryNameSuffix() + ".a").str(),
- /*AlwaysLink*/ true);
+ AddLinkRuntimeLib(
+ Args, CmdArgs,
+ (Twine("libclang_rt.profile_") + getOSLibraryNameSuffix() + ".a").str(),
+ RuntimeLinkOptions(RLO_AlwaysLink | RLO_FirstLink));
}
void DarwinClang::AddLinkSanitizerLibArgs(const ArgList &Args,
ArgStringList &CmdArgs,
- StringRef Sanitizer) const {
- AddLinkRuntimeLib(
- Args, CmdArgs,
- (Twine("libclang_rt.") + Sanitizer + "_" +
- getOSLibraryNameSuffix() + "_dynamic.dylib").str(),
- /*AlwaysLink*/ true, /*IsEmbedded*/ false,
- /*AddRPath*/ true);
+ StringRef Sanitizer,
+ bool Shared) const {
+ auto RLO = RuntimeLinkOptions(RLO_AlwaysLink | (Shared ? RLO_AddRPath : 0U));
+ AddLinkRuntimeLib(Args, CmdArgs,
+ (Twine("libclang_rt.") + Sanitizer + "_" +
+ getOSLibraryNameSuffix() +
+ (Shared ? "_dynamic.dylib" : ".a"))
+ .str(),
+ RLO);
}
ToolChain::RuntimeLibType DarwinClang::GetRuntimeLibType(
@@ -1050,16 +1051,22 @@
if (Sanitize.needsLsanRt())
AddLinkSanitizerLibArgs(Args, CmdArgs, "lsan");
if (Sanitize.needsUbsanRt())
- AddLinkSanitizerLibArgs(Args, CmdArgs, "ubsan");
+ AddLinkSanitizerLibArgs(Args, CmdArgs,
+ Sanitize.requiresMinimalRuntime() ? "ubsan_minimal"
+ : "ubsan");
if (Sanitize.needsTsanRt())
AddLinkSanitizerLibArgs(Args, CmdArgs, "tsan");
- if (Sanitize.needsFuzzer() && !Args.hasArg(options::OPT_dynamiclib))
- AddFuzzerLinkArgs(Args, CmdArgs);
+ if (Sanitize.needsFuzzer() && !Args.hasArg(options::OPT_dynamiclib)) {
+ AddLinkSanitizerLibArgs(Args, CmdArgs, "fuzzer", /*shared=*/false);
+
+ // Libfuzzer is written in C++ and requires libcxx.
+ AddCXXStdlibLibArgs(Args, CmdArgs);
+ }
if (Sanitize.needsStatsRt()) {
StringRef OS = isTargetMacOS() ? "osx" : "iossim";
AddLinkRuntimeLib(Args, CmdArgs,
(Twine("libclang_rt.stats_client_") + OS + ".a").str(),
- /*AlwaysLink=*/true);
+ RLO_AlwaysLink);
AddLinkSanitizerLibArgs(Args, CmdArgs, "stats");
}
if (Sanitize.needsEsanRt())
@@ -1741,23 +1748,31 @@
: "soft";
CompilerRT += Args.hasArg(options::OPT_fPIC) ? "_pic.a" : "_static.a";
- AddLinkRuntimeLib(Args, CmdArgs, CompilerRT, false, true);
+ AddLinkRuntimeLib(Args, CmdArgs, CompilerRT, RLO_IsEmbedded);
}
bool Darwin::isAlignedAllocationUnavailable() const {
+ llvm::Triple::OSType OS;
+
switch (TargetPlatform) {
case MacOS: // Earlier than 10.13.
- return TargetVersion < VersionTuple(10U, 13U, 0U);
+ OS = llvm::Triple::MacOSX;
+ break;
case IPhoneOS:
case IPhoneOSSimulator:
+ OS = llvm::Triple::IOS;
+ break;
case TvOS:
case TvOSSimulator: // Earlier than 11.0.
- return TargetVersion < VersionTuple(11U, 0U, 0U);
+ OS = llvm::Triple::TvOS;
+ break;
case WatchOS:
case WatchOSSimulator: // Earlier than 4.0.
- return TargetVersion < VersionTuple(4U, 0U, 0U);
+ OS = llvm::Triple::WatchOS;
+ break;
}
- llvm_unreachable("Unsupported platform");
+
+ return TargetVersion < alignedAllocMinVersion(OS);
}
void Darwin::addClangTargetOptions(const llvm::opt::ArgList &DriverArgs,
@@ -2015,6 +2030,8 @@
Res |= SanitizerKind::Address;
Res |= SanitizerKind::Leak;
Res |= SanitizerKind::Fuzzer;
+ Res |= SanitizerKind::FuzzerNoLink;
+ Res |= SanitizerKind::Function;
if (isTargetMacOS()) {
if (!isMacosxVersionLT(10, 9))
Res |= SanitizerKind::Vptr;
diff --git a/lib/Driver/ToolChains/Darwin.h b/lib/Driver/ToolChains/Darwin.h
index 77c569e..3c6c740 100644
--- a/lib/Driver/ToolChains/Darwin.h
+++ b/lib/Driver/ToolChains/Darwin.h
@@ -152,10 +152,11 @@
llvm::opt::ArgStringList &CmdArgs) const {}
/// Add the linker arguments to link the compiler runtime library.
+ ///
+ /// FIXME: This API is intended for use with embedded libraries only, and is
+ /// misleadingly named.
virtual void AddLinkRuntimeLibArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const;
- virtual void AddFuzzerLinkArgs(const llvm::opt::ArgList &Args,
- llvm::opt::ArgStringList &CmdArgs) const;
virtual void addStartObjectFileArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs) const {
@@ -171,10 +172,26 @@
/// Is the target either iOS or an iOS simulator?
bool isTargetIOSBased() const { return false; }
+ /// Options to control how a runtime library is linked.
+ enum RuntimeLinkOptions : unsigned {
+ // Link the library in even if it can't be found in the VFS.
+ RLO_AlwaysLink = 1 << 0,
+
+ // Use the embedded runtime from the macho_embedded directory.
+ RLO_IsEmbedded = 1 << 1,
+
+ // Emit rpaths for @executable_path as well as the resource directory.
+ RLO_AddRPath = 1 << 2,
+
+ //< Link the library in before any others.
+ RLO_FirstLink = 1 << 3,
+ };
+
+ /// Add a runtime library to the list of items to link.
void AddLinkRuntimeLib(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
- StringRef DarwinLibName, bool AlwaysLink = false,
- bool IsEmbedded = false, bool AddRPath = false) const;
+ StringRef DarwinLibName,
+ RuntimeLinkOptions Opts = RuntimeLinkOptions()) const;
/// Add any profiling runtime libraries that are needed. This is essentially a
/// MachO specific version of addProfileRT in Tools.cpp.
@@ -489,7 +506,8 @@
private:
void AddLinkSanitizerLibArgs(const llvm::opt::ArgList &Args,
llvm::opt::ArgStringList &CmdArgs,
- StringRef Sanitizer) const;
+ StringRef Sanitizer,
+ bool shared = true) const;
};
} // end namespace toolchains
diff --git a/lib/Driver/ToolChains/DragonFly.cpp b/lib/Driver/ToolChains/DragonFly.cpp
index bd2c7fc..648469e 100644
--- a/lib/Driver/ToolChains/DragonFly.cpp
+++ b/lib/Driver/ToolChains/DragonFly.cpp
@@ -127,7 +127,8 @@
}
if (D.CCCIsCXX()) {
- getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (getToolChain().ShouldLinkCXXStdlib(Args))
+ getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
CmdArgs.push_back("-lm");
}
diff --git a/lib/Driver/ToolChains/FreeBSD.cpp b/lib/Driver/ToolChains/FreeBSD.cpp
index c6626e9..2f066cf 100644
--- a/lib/Driver/ToolChains/FreeBSD.cpp
+++ b/lib/Driver/ToolChains/FreeBSD.cpp
@@ -240,7 +240,8 @@
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
addOpenMPRuntime(CmdArgs, ToolChain, Args);
if (D.CCCIsCXX()) {
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (ToolChain.ShouldLinkCXXStdlib(Args))
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
if (Args.hasArg(options::OPT_pg))
CmdArgs.push_back("-lm_p");
else
diff --git a/lib/Driver/ToolChains/Fuchsia.cpp b/lib/Driver/ToolChains/Fuchsia.cpp
index 78053aa..b87d9f0 100644
--- a/lib/Driver/ToolChains/Fuchsia.cpp
+++ b/lib/Driver/ToolChains/Fuchsia.cpp
@@ -107,13 +107,15 @@
CmdArgs.push_back("-Bdynamic");
if (D.CCCIsCXX()) {
- bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
- !Args.hasArg(options::OPT_static);
- if (OnlyLibstdcxxStatic)
- CmdArgs.push_back("-Bstatic");
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
- if (OnlyLibstdcxxStatic)
- CmdArgs.push_back("-Bdynamic");
+ if (ToolChain.ShouldLinkCXXStdlib(Args)) {
+ bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
+ !Args.hasArg(options::OPT_static);
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bstatic");
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bdynamic");
+ }
CmdArgs.push_back("-lm");
}
diff --git a/lib/Driver/ToolChains/Gnu.cpp b/lib/Driver/ToolChains/Gnu.cpp
index 72a9f85..5155ce3 100644
--- a/lib/Driver/ToolChains/Gnu.cpp
+++ b/lib/Driver/ToolChains/Gnu.cpp
@@ -560,13 +560,15 @@
if (D.CCCIsCXX() &&
!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
- !Args.hasArg(options::OPT_static);
- if (OnlyLibstdcxxStatic)
- CmdArgs.push_back("-Bstatic");
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
- if (OnlyLibstdcxxStatic)
- CmdArgs.push_back("-Bdynamic");
+ if (ToolChain.ShouldLinkCXXStdlib(Args)) {
+ bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
+ !Args.hasArg(options::OPT_static);
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bstatic");
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bdynamic");
+ }
CmdArgs.push_back("-lm");
}
// Silence warnings when linking C code with a C++ '-stdlib' argument.
diff --git a/lib/Driver/ToolChains/Hexagon.cpp b/lib/Driver/ToolChains/Hexagon.cpp
index 9bf1590..4f62b1f 100644
--- a/lib/Driver/ToolChains/Hexagon.cpp
+++ b/lib/Driver/ToolChains/Hexagon.cpp
@@ -248,7 +248,8 @@
//----------------------------------------------------------------------------
if (IncStdLib && IncDefLibs) {
if (D.CCCIsCXX()) {
- HTC.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (HTC.ShouldLinkCXXStdlib(Args))
+ HTC.AddCXXStdlibLibArgs(Args, CmdArgs);
CmdArgs.push_back("-lm");
}
diff --git a/lib/Driver/ToolChains/Linux.cpp b/lib/Driver/ToolChains/Linux.cpp
index 08a27fa..1adf9f7 100644
--- a/lib/Driver/ToolChains/Linux.cpp
+++ b/lib/Driver/ToolChains/Linux.cpp
@@ -828,6 +828,7 @@
SanitizerMask Res = ToolChain::getSupportedSanitizers();
Res |= SanitizerKind::Address;
Res |= SanitizerKind::Fuzzer;
+ Res |= SanitizerKind::FuzzerNoLink;
Res |= SanitizerKind::KernelAddress;
Res |= SanitizerKind::Vptr;
Res |= SanitizerKind::SafeStack;
diff --git a/lib/Driver/ToolChains/MinGW.cpp b/lib/Driver/ToolChains/MinGW.cpp
index 632b76d..b84c6ee 100644
--- a/lib/Driver/ToolChains/MinGW.cpp
+++ b/lib/Driver/ToolChains/MinGW.cpp
@@ -185,8 +185,7 @@
// TODO: Add profile stuff here
- if (D.CCCIsCXX() &&
- !Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
+ if (TC.ShouldLinkCXXStdlib(Args)) {
bool OnlyLibstdcxxStatic = Args.hasArg(options::OPT_static_libstdcxx) &&
!Args.hasArg(options::OPT_static);
if (OnlyLibstdcxxStatic)
diff --git a/lib/Driver/ToolChains/Minix.cpp b/lib/Driver/ToolChains/Minix.cpp
index 2e8939c..39e6f90 100644
--- a/lib/Driver/ToolChains/Minix.cpp
+++ b/lib/Driver/ToolChains/Minix.cpp
@@ -72,7 +72,8 @@
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
if (D.CCCIsCXX()) {
- getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (getToolChain().ShouldLinkCXXStdlib(Args))
+ getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
CmdArgs.push_back("-lm");
}
}
diff --git a/lib/Driver/ToolChains/NaCl.cpp b/lib/Driver/ToolChains/NaCl.cpp
index 5eb5c74..128478d 100644
--- a/lib/Driver/ToolChains/NaCl.cpp
+++ b/lib/Driver/ToolChains/NaCl.cpp
@@ -133,13 +133,15 @@
if (D.CCCIsCXX() &&
!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- bool OnlyLibstdcxxStatic =
- Args.hasArg(options::OPT_static_libstdcxx) && !IsStatic;
- if (OnlyLibstdcxxStatic)
- CmdArgs.push_back("-Bstatic");
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
- if (OnlyLibstdcxxStatic)
- CmdArgs.push_back("-Bdynamic");
+ if (ToolChain.ShouldLinkCXXStdlib(Args)) {
+ bool OnlyLibstdcxxStatic =
+ Args.hasArg(options::OPT_static_libstdcxx) && !IsStatic;
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bstatic");
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (OnlyLibstdcxxStatic)
+ CmdArgs.push_back("-Bdynamic");
+ }
CmdArgs.push_back("-lm");
}
diff --git a/lib/Driver/ToolChains/NetBSD.cpp b/lib/Driver/ToolChains/NetBSD.cpp
index a1a3108..9cb0468 100644
--- a/lib/Driver/ToolChains/NetBSD.cpp
+++ b/lib/Driver/ToolChains/NetBSD.cpp
@@ -278,7 +278,8 @@
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
addOpenMPRuntime(CmdArgs, getToolChain(), Args);
if (D.CCCIsCXX()) {
- getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (getToolChain().ShouldLinkCXXStdlib(Args))
+ getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
CmdArgs.push_back("-lm");
}
if (NeedsSanitizerDeps)
diff --git a/lib/Driver/ToolChains/OpenBSD.cpp b/lib/Driver/ToolChains/OpenBSD.cpp
index 1d54a1e..fbb84a6 100644
--- a/lib/Driver/ToolChains/OpenBSD.cpp
+++ b/lib/Driver/ToolChains/OpenBSD.cpp
@@ -179,7 +179,8 @@
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
if (D.CCCIsCXX()) {
- getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (getToolChain().ShouldLinkCXXStdlib(Args))
+ getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
if (Args.hasArg(options::OPT_pg))
CmdArgs.push_back("-lm_p");
else
diff --git a/lib/Driver/ToolChains/PS4CPU.cpp b/lib/Driver/ToolChains/PS4CPU.cpp
index c1b8c3d..b37fe7d 100644
--- a/lib/Driver/ToolChains/PS4CPU.cpp
+++ b/lib/Driver/ToolChains/PS4CPU.cpp
@@ -227,7 +227,8 @@
// libraries for both C and C++ compilations.
CmdArgs.push_back("-lkernel");
if (D.CCCIsCXX()) {
- ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
+ if (ToolChain.ShouldLinkCXXStdlib(Args))
+ ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
if (Args.hasArg(options::OPT_pg))
CmdArgs.push_back("-lm_p");
else
diff --git a/lib/Driver/ToolChains/Solaris.cpp b/lib/Driver/ToolChains/Solaris.cpp
index de98d11..9fe6e9d 100644
--- a/lib/Driver/ToolChains/Solaris.cpp
+++ b/lib/Driver/ToolChains/Solaris.cpp
@@ -100,7 +100,7 @@
AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- if (getToolChain().getDriver().CCCIsCXX())
+ if (getToolChain().ShouldLinkCXXStdlib(Args))
getToolChain().AddCXXStdlibLibArgs(Args, CmdArgs);
CmdArgs.push_back("-lgcc_s");
CmdArgs.push_back("-lc");
diff --git a/lib/Driver/ToolChains/WebAssembly.cpp b/lib/Driver/ToolChains/WebAssembly.cpp
index 058bc42..88a3f1b 100644
--- a/lib/Driver/ToolChains/WebAssembly.cpp
+++ b/lib/Driver/ToolChains/WebAssembly.cpp
@@ -38,7 +38,6 @@
const char *LinkingOutput) const {
const ToolChain &ToolChain = getToolChain();
- const Driver &D = ToolChain.getDriver();
const char *Linker = Args.MakeArgString(ToolChain.GetLinkerPath());
ArgStringList CmdArgs;
CmdArgs.push_back("-flavor");
@@ -77,7 +76,7 @@
AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs, JA);
if (!Args.hasArg(options::OPT_nostdlib, options::OPT_nodefaultlibs)) {
- if (D.CCCIsCXX())
+ if (ToolChain.ShouldLinkCXXStdlib(Args))
ToolChain.AddCXXStdlibLibArgs(Args, CmdArgs);
if (Args.hasArg(options::OPT_pthread))
diff --git a/lib/Edit/CMakeLists.txt b/lib/Edit/CMakeLists.txt
index a7fa9c2..99aff3c 100644
--- a/lib/Edit/CMakeLists.txt
+++ b/lib/Edit/CMakeLists.txt
@@ -5,6 +5,8 @@
add_clang_library(clangEdit
Commit.cpp
EditedSource.cpp
+ FillInMissingProtocolStubs.cpp
+ FillInMissingSwitchEnumCases.cpp
RewriteObjCFoundationAPI.cpp
LINK_LIBS
diff --git a/lib/Edit/FillInMissingProtocolStubs.cpp b/lib/Edit/FillInMissingProtocolStubs.cpp
new file mode 100644
index 0000000..64843e5
--- /dev/null
+++ b/lib/Edit/FillInMissingProtocolStubs.cpp
@@ -0,0 +1,466 @@
+//===--- FillInMissingProtocolStubs.cpp - --------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Add methods from protocol(s)" refactoring operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/NSAPI.h"
+#include "clang/Edit/RefactoringFixits.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/DenseSet.h"
+#include <algorithm>
+
+using namespace clang;
+using namespace edit;
+using namespace fillInMissingProtocolStubs;
+
+// FIXME: This is duplicated with the refactoring lib.
+static bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,
+ const SourceManager &SM) {
+ return !Loc1.isMacroID() && !Loc2.isMacroID() &&
+ SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2);
+}
+
+static bool isSemicolonAtLocation(SourceLocation TokenLoc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ return Lexer::getSourceText(
+ CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM,
+ LangOpts) == ";";
+}
+
+static SourceLocation getLocationOfPrecedingToken(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation Result = Loc;
+ if (Result.isMacroID())
+ Result = SM.getExpansionLoc(Result);
+ FileID FID = SM.getFileID(Result);
+ SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+ if (Loc == StartOfFile)
+ return SourceLocation();
+ return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts);
+}
+
+static SourceLocation
+getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ assert(!SpellingLoc.isMacroID() && "Expecting a spelling location");
+ SourceLocation NextTokenLoc =
+ Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts);
+ if (NextTokenLoc.isValid()) {
+ bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM);
+ if (IsSameLine) {
+ // Could be a ';' on the same line, so try looking after the ';'
+ if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts))
+ return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM,
+ LangOpts);
+ } else {
+ SourceLocation LastLoc = SM.translateLineCol(
+ SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc),
+ std::numeric_limits<unsigned>::max());
+ if (LastLoc.isValid())
+ return LastLoc;
+ }
+ }
+ return Lexer::getLocForEndOfToken(SpellingLoc, 0, SM, LangOpts);
+}
+
+namespace {
+
+struct ProtocolInfo {
+ /// The lower the priority, the more important this protocol is considered to
+ /// be. Typically protocols from the class have lower priority than protocols
+ /// from superclasses.
+ int Priority;
+};
+
+using ProtocolMapTy = llvm::DenseMap<const ObjCProtocolDecl *, ProtocolInfo>;
+
+/// Contains the set of methods from all the protocols that the class conforms
+/// to.
+class MethodSet {
+public:
+ struct MethodInfo {
+ const ObjCMethodDecl *M;
+ const ObjCProtocolDecl *P;
+ int ProtocolPriority;
+ enum MethodPresenceKind { IsDeclared = 0x1, IsImplemented = 0x2 };
+ unsigned PresenceKind = 0;
+ const ObjCMethodDecl *DeclaredOrImplementedMethod = nullptr;
+
+ MethodInfo(const ObjCMethodDecl *M, const ObjCProtocolDecl *P,
+ int ProtocolPriority)
+ : M(M), P(P), ProtocolPriority(ProtocolPriority) {}
+
+ bool isRequired() const {
+ return M->getImplementationControl() == ObjCMethodDecl::Required;
+ }
+ void markAs(MethodPresenceKind Kind) { PresenceKind |= Kind; }
+ bool is(MethodPresenceKind Kind) const {
+ return (PresenceKind & Kind) == Kind;
+ }
+ };
+
+private:
+ llvm::DenseMap<Selector, MethodInfo> InstanceMethods;
+ llvm::DenseMap<Selector, MethodInfo> ClassMethods;
+
+ void markMethodsFrom(const ObjCContainerDecl *Container,
+ MethodInfo::MethodPresenceKind Kind) {
+ for (const ObjCMethodDecl *M : Container->methods()) {
+ auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods;
+ auto It = Map.find(M->getSelector());
+ if (It != Map.end()) {
+ It->second.markAs(Kind);
+ if (!It->second.DeclaredOrImplementedMethod)
+ It->second.DeclaredOrImplementedMethod = M;
+ }
+ }
+ }
+
+public:
+ MethodSet() {}
+ MethodSet(MethodSet &&Other) = default;
+ MethodSet &operator=(MethodSet &&Other) = default;
+
+ void gatherMethodsFrom(const ObjCProtocolDecl *P, int Priority) {
+ for (const ObjCMethodDecl *M : P->methods()) {
+ if (M->isImplicit())
+ continue;
+ AvailabilityResult Availability = M->getAvailability();
+ // Methods that are unavailable or not yet introduced are not considered
+ // to be required.
+ if (Availability == AR_NotYetIntroduced || Availability == AR_Unavailable)
+ continue;
+ auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods;
+ Map.insert(std::make_pair(M->getSelector(), MethodInfo(M, P, Priority)));
+ }
+ }
+
+ void markImplementedMethods(const ObjCContainerDecl *Container) {
+ assert(isa<ObjCImplDecl>(Container) && "Not an implementation container");
+ markMethodsFrom(Container, MethodInfo::IsImplemented);
+ if (const auto *ID = dyn_cast<ObjCImplementationDecl>(Container)) {
+ const auto *I = ID->getClassInterface();
+ // Mark declarations from super-classes as implemented to prevent
+ // redundant implementations.
+ while ((I = I->getSuperClass()))
+ markMethodsFrom(I, MethodInfo::IsImplemented);
+ }
+ }
+
+ void markDeclaredMethods(const ObjCContainerDecl *Container) {
+ assert(!isa<ObjCImplDecl>(Container) && "Not an interface container");
+ markMethodsFrom(Container, MethodInfo::IsDeclared);
+ // Mark declarations from super-classes as declared to prevent redundant
+ // declarations.
+ if (const auto *I = dyn_cast<ObjCInterfaceDecl>(Container)) {
+ while ((I = I->getSuperClass()))
+ markMethodsFrom(I, MethodInfo::IsDeclared);
+ }
+ }
+
+ /// Returns true if the given container has missing @required method stubs.
+ ///
+ /// For @interfaces, this method returns true when the interface is missing
+ /// a declaration for any @required method in all of the protocols.
+ /// For @implementations, this method returns true when the implementation is
+ /// missing an implementation of any @required method in all of the protocols.
+ bool hasMissingRequiredMethodStubs(const ObjCContainerDecl *Container) {
+ MethodInfo::MethodPresenceKind Kind = isa<ObjCImplDecl>(Container)
+ ? MethodInfo::IsImplemented
+ : MethodInfo::IsDeclared;
+ for (const auto &I : InstanceMethods) {
+ if (!I.second.isRequired())
+ continue;
+ if (!I.second.is(Kind))
+ return true;
+ }
+ for (const auto &I : ClassMethods) {
+ if (!I.second.isRequired())
+ continue;
+ if (!I.second.is(Kind))
+ return true;
+ }
+ return false;
+ }
+
+ std::vector<MethodInfo>
+ getMissingRequiredMethods(const ObjCContainerDecl *Container) {
+ MethodInfo::MethodPresenceKind Kind = isa<ObjCImplDecl>(Container)
+ ? MethodInfo::IsImplemented
+ : MethodInfo::IsDeclared;
+ std::vector<MethodInfo> Results;
+ for (const auto &I : InstanceMethods) {
+ if (!I.second.isRequired())
+ continue;
+ if (!I.second.is(Kind))
+ Results.push_back(I.second);
+ }
+ for (const auto &I : ClassMethods) {
+ if (!I.second.isRequired())
+ continue;
+ if (!I.second.is(Kind))
+ Results.push_back(I.second);
+ }
+ return Results;
+ }
+
+ SourceLocation findLocationForInsertionForMethodsFromProtocol(
+ const ObjCProtocolDecl *P, const ObjCContainerDecl *Container,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ MethodInfo::MethodPresenceKind Kind = isa<ObjCImplDecl>(Container)
+ ? MethodInfo::IsImplemented
+ : MethodInfo::IsDeclared;
+ llvm::SmallVector<const ObjCMethodDecl *, 4> MethodsFromProtocolInContainer;
+ for (const ObjCMethodDecl *M : P->methods()) {
+ if (M->isImplicit())
+ continue;
+ const auto &Map = M->isInstanceMethod() ? InstanceMethods : ClassMethods;
+ auto It = Map.find(M->getSelector());
+ if (It == Map.end())
+ continue;
+ if (!It->second.is(Kind))
+ continue;
+ const ObjCMethodDecl *ContainerMethod =
+ It->second.DeclaredOrImplementedMethod;
+ // Ignore method declarations from superclasses.
+ if (ContainerMethod->getLexicalDeclContext() != Container)
+ continue;
+ // This is a method from the given protocol that either declared or
+ // implemented in the container.
+ MethodsFromProtocolInContainer.push_back(ContainerMethod);
+ }
+ // Find the appropriate source locations by looking
+ if (MethodsFromProtocolInContainer.empty())
+ return SourceLocation();
+ SourceLocation Loc = MethodsFromProtocolInContainer[0]->getLocEnd();
+ if (Loc.isMacroID())
+ Loc = SM.getExpansionRange(Loc).second;
+ for (const ObjCMethodDecl *M :
+ makeArrayRef(MethodsFromProtocolInContainer).drop_front()) {
+ SourceLocation EndLoc = M->getLocEnd();
+ if (EndLoc.isMacroID())
+ EndLoc = SM.getExpansionRange(EndLoc).second;
+ if (SM.isBeforeInTranslationUnit(Loc, EndLoc))
+ Loc = EndLoc;
+ }
+ return getLastLineLocationUnlessItHasOtherTokens(Loc, SM, LangOpts);
+ }
+};
+
+} // end anonymous namespace
+
+namespace clang {
+namespace edit {
+namespace fillInMissingProtocolStubs {
+
+class FillInMissingProtocolStubsImpl {
+public:
+ const ObjCContainerDecl *Container;
+ MethodSet Methods;
+};
+
+} // end namespace fillInMissingProtocolStubsImpl
+} // end namespace edit
+} // end namespace clang
+
+static void gatherProtocols(
+ llvm::iterator_range<ObjCList<ObjCProtocolDecl>::iterator> Protocols,
+ NSAPI &API, ProtocolMapTy &Result, int &Priority) {
+ for (const ObjCProtocolDecl *P : Protocols) {
+ // Ignore the 'NSObject' protocol.
+ if (API.getNSClassId(NSAPI::ClassId_NSObject) == P->getIdentifier())
+ continue;
+ gatherProtocols(P->protocols(), API, Result, Priority);
+ Result.insert(std::make_pair(P, ProtocolInfo{Priority++}));
+ }
+}
+
+static ProtocolMapTy
+gatherSuitableClassProtocols(const ObjCInterfaceDecl *I,
+ const ObjCContainerDecl *Container, NSAPI &API) {
+ ProtocolMapTy Result;
+ // The class of interest should use the protocols from extensions when the
+ // operation is initiated from the @implementation / extension.
+ auto ClassProtocols =
+ Container == I ? I->protocols() : I->all_referenced_protocols();
+ int Priority = 0;
+ gatherProtocols(ClassProtocols, API, Result, Priority);
+ while ((I = I->getSuperClass()))
+ gatherProtocols(I->protocols(), API, Result, Priority);
+ return Result;
+}
+
+static const ObjCContainerDecl *
+getInterfaceOrCategory(const ObjCContainerDecl *Container) {
+ if (const auto *Impl = dyn_cast<ObjCImplementationDecl>(Container))
+ return Impl->getClassInterface();
+ if (const auto *CategoryImpl = dyn_cast<ObjCCategoryImplDecl>(Container))
+ return CategoryImpl->getCategoryDecl();
+ return Container;
+}
+
+static bool initiate(FillInMissingProtocolStubsImpl &Dest, ASTContext &Context,
+ const ObjCContainerDecl *Container) {
+ const ObjCContainerDecl *ContainerProtocolSource =
+ getInterfaceOrCategory(Container);
+ if (!ContainerProtocolSource)
+ return false;
+
+ // The protocols that are specified in the @interface and/or in the
+ // superclasses.
+ ProtocolMapTy Protocols;
+ NSAPI API(Context);
+ if (const auto *I = dyn_cast<ObjCInterfaceDecl>(ContainerProtocolSource)) {
+ if (!I->hasDefinition())
+ return false;
+ Protocols = gatherSuitableClassProtocols(I, Container, API);
+ if (Protocols.empty())
+ return false;
+ } else if (const auto *I =
+ dyn_cast<ObjCCategoryDecl>(ContainerProtocolSource)) {
+ int Priority = 0;
+ gatherProtocols(I->protocols(), API, Protocols, Priority);
+ if (Protocols.empty())
+ return false;
+ }
+
+ // Check if there are missing @required methods.
+ for (const auto &P : Protocols)
+ Dest.Methods.gatherMethodsFrom(P.first, P.second.Priority);
+ if (isa<ObjCImplDecl>(Container))
+ Dest.Methods.markImplementedMethods(Container);
+ else
+ Dest.Methods.markDeclaredMethods(Container);
+
+ Dest.Container = Container;
+ return true;
+}
+
+FillInMissingProtocolStubs::FillInMissingProtocolStubs() {}
+FillInMissingProtocolStubs::~FillInMissingProtocolStubs() {}
+FillInMissingProtocolStubs::FillInMissingProtocolStubs(
+ FillInMissingProtocolStubs &&Other)
+ : Impl(std::move(Other.Impl)) {}
+FillInMissingProtocolStubs &FillInMissingProtocolStubs::
+operator=(FillInMissingProtocolStubs &&Other) {
+ Impl = std::move(Other.Impl);
+ return *this;
+}
+
+bool FillInMissingProtocolStubs::initiate(ASTContext &Context,
+ const ObjCContainerDecl *Container) {
+ Impl = llvm::make_unique<FillInMissingProtocolStubsImpl>();
+ if (!::initiate(*Impl, Context, Container))
+ return true;
+ return false;
+}
+
+bool FillInMissingProtocolStubs::hasMissingRequiredMethodStubs() {
+ return Impl->Methods.hasMissingRequiredMethodStubs(Impl->Container);
+}
+
+static void perform(MethodSet &Methods, const ObjCContainerDecl *Container,
+ ASTContext &Context,
+ llvm::function_ref<void(const FixItHint &)> Consumer) {
+ auto MissingMethods = Methods.getMissingRequiredMethods(Container);
+ // Sort the methods by grouping them into protocol clusters and then sorting
+ // them alphabetically within the same protocol.
+ std::sort(MissingMethods.begin(), MissingMethods.end(),
+ [](const MethodSet::MethodInfo &A, const MethodSet::MethodInfo &B) {
+ if (A.ProtocolPriority == B.ProtocolPriority)
+ return A.M->getSelector().getAsString() <
+ B.M->getSelector().getAsString();
+ assert(A.P != B.P && "Same protocols should have same priority");
+ return A.ProtocolPriority < B.ProtocolPriority;
+ });
+
+ SourceLocation InsertionLoc =
+ isa<ObjCImplDecl>(Container)
+ ? Container->getLocEnd()
+ : getLocationOfPrecedingToken(Container->getLocEnd(),
+ Context.getSourceManager(),
+ Context.getLangOpts());
+ if (InsertionLoc.isInvalid())
+ InsertionLoc = Container->getLocEnd();
+
+ PrintingPolicy PP = Context.getPrintingPolicy();
+ PP.PolishForDeclaration = true;
+ PP.SuppressStrongLifetime = true;
+ PP.SuppressLifetimeQualifiers = true;
+ PP.SuppressUnwrittenScope = true;
+
+ std::string EndInsertionOSStr;
+ llvm::raw_string_ostream EndInsertionOS(EndInsertionOSStr);
+
+ std::string InsertionGroupStr;
+ llvm::raw_string_ostream InsertionGroupOS(InsertionGroupStr);
+
+ const ObjCProtocolDecl *CurrentProtocol = nullptr;
+ SourceLocation CurrentProtocolInsertionLoc;
+ bool IsImplementation = isa<ObjCImplDecl>(Container);
+ for (const auto &Method : MissingMethods) {
+ const ObjCProtocolDecl *P = Method.P;
+ if (CurrentProtocol != P) {
+ if (!InsertionGroupOS.str().empty()) {
+ assert(CurrentProtocolInsertionLoc.isValid());
+ Consumer(FixItHint::CreateInsertion(CurrentProtocolInsertionLoc,
+ InsertionGroupOS.str()));
+ }
+ InsertionGroupStr.clear();
+ CurrentProtocol = P;
+ CurrentProtocolInsertionLoc =
+ Methods.findLocationForInsertionForMethodsFromProtocol(
+ P, Container, Context.getSourceManager(), Context.getLangOpts());
+ }
+ bool IsInsertingAfterRelatedMethods = CurrentProtocolInsertionLoc.isValid();
+ raw_ostream &OS =
+ IsInsertingAfterRelatedMethods ? InsertionGroupOS : EndInsertionOS;
+
+ std::string MethodDeclStr;
+ llvm::raw_string_ostream MethodOS(MethodDeclStr);
+ Method.M->print(MethodOS, PP);
+ if (IsInsertingAfterRelatedMethods)
+ OS << "\n\n";
+ OS << StringRef(MethodOS.str()).drop_back(); // Drop the ';'
+ if (IsImplementation)
+ OS << " { \n <#code#>\n}\n";
+ else
+ OS << ";\n";
+ if (!IsInsertingAfterRelatedMethods)
+ OS << "\n";
+ }
+ if (!InsertionGroupOS.str().empty()) {
+ assert(CurrentProtocolInsertionLoc.isValid());
+ Consumer(FixItHint::CreateInsertion(CurrentProtocolInsertionLoc,
+ InsertionGroupOS.str()));
+ }
+ if (!EndInsertionOS.str().empty())
+ Consumer(FixItHint::CreateInsertion(InsertionLoc, EndInsertionOS.str()));
+}
+
+void FillInMissingProtocolStubs::perform(
+ ASTContext &Context, llvm::function_ref<void(const FixItHint &)> Consumer) {
+ ::perform(Impl->Methods, Impl->Container, Context, Consumer);
+}
+
+void fillInMissingProtocolStubs::addMissingProtocolStubs(
+ ASTContext &Context, const ObjCContainerDecl *Container,
+ llvm::function_ref<void(const FixItHint &)> Consumer) {
+ FillInMissingProtocolStubsImpl Impl;
+ if (initiate(Impl, Context, Container))
+ perform(Impl.Methods, Impl.Container, Context, Consumer);
+}
diff --git a/lib/Edit/FillInMissingSwitchEnumCases.cpp b/lib/Edit/FillInMissingSwitchEnumCases.cpp
new file mode 100644
index 0000000..c6d5934
--- /dev/null
+++ b/lib/Edit/FillInMissingSwitchEnumCases.cpp
@@ -0,0 +1,192 @@
+//===--- FillInMissingSwitchEnumCases.cpp - ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/NestedNameSpecifier.h"
+#include "clang/Edit/RefactoringFixits.h"
+#include <unordered_map>
+
+using namespace clang;
+
+namespace {
+
+struct CaseInfo {
+ const SwitchCase *Case, *NextCase;
+ unsigned Index;
+};
+typedef std::unordered_map<int64_t, CaseInfo> CoveredEnumCasesInfoType;
+
+/// Return true if the ordering of the covered enum cases is similar to the
+/// order of the enum case constants that are defined in the enum.
+bool useCaseBasedOrdering(const ArrayRef<int64_t> &CoveredEnumCaseValues,
+ const CoveredEnumCasesInfoType &CoveredEnumCases) {
+ if (CoveredEnumCaseValues.empty())
+ return false;
+ for (const auto &I : llvm::enumerate(CoveredEnumCaseValues)) {
+ auto It = CoveredEnumCases.find(I.value());
+ if (It == CoveredEnumCases.end())
+ return false;
+ const CaseInfo &Case = It->second;
+ if (Case.Index != I.index())
+ return false;
+ }
+ return true;
+}
+
+/// Determine if the inserted cases should be wrapped in braces using a simple
+/// heuristic:
+/// Wrap only if at least 90% of existing cases use braces.
+bool useBraces(const SwitchStmt *S) {
+ unsigned CaseCount = 0, CompoundCasesCount = 0;
+ for (const SwitchCase *Case = S->getSwitchCaseList(); Case;
+ Case = Case->getNextSwitchCase(), ++CaseCount) {
+ if (!Case->getSubStmt())
+ continue;
+ if (isa<CompoundStmt>(Case->getSubStmt()))
+ ++CompoundCasesCount;
+ }
+ return CaseCount && float(CompoundCasesCount) / float(CaseCount) >= 0.9;
+}
+
+} // end anonymous namespace
+
+void edit::fillInMissingSwitchEnumCases(
+ ASTContext &Context, const SwitchStmt *Switch, const EnumDecl *Enum,
+ const DeclContext *SwitchContext,
+ llvm::function_ref<void(const FixItHint &)> Consumer) {
+ // Compute the number of cases in the switch.
+ unsigned CaseCount = 0;
+ for (const SwitchCase *Case = Switch->getSwitchCaseList(); Case;
+ Case = Case->getNextSwitchCase())
+ ++CaseCount;
+
+ // Compute the set of enum values that are covered by the switch.
+ CoveredEnumCasesInfoType CoveredEnumCases;
+ const SwitchCase *DefaultCase = nullptr;
+ const SwitchCase *FirstCoveredEnumCase = nullptr;
+ const SwitchCase *NextCase = nullptr;
+ unsigned CaseIndex = CaseCount;
+ for (const SwitchCase *Case = Switch->getSwitchCaseList(); Case;
+ NextCase = Case, Case = Case->getNextSwitchCase()) {
+ // The cases in the switch are ordered back to front, so the index has
+ // to be reversed.
+ --CaseIndex;
+ if (isa<DefaultStmt>(Case)) {
+ DefaultCase = Case;
+ continue;
+ }
+ const auto *CS = cast<CaseStmt>(Case);
+ if (const auto *LHS = CS->getLHS()) {
+ llvm::APSInt Value;
+ if (!LHS->EvaluateAsInt(Value, Context))
+ continue;
+ // Only allow constant that fix into 64 bits.
+ if (Value.getMinSignedBits() > 64)
+ continue;
+ CoveredEnumCases[Value.getSExtValue()] =
+ CaseInfo{Case, NextCase, CaseIndex};
+ // The cases in the switch are ordered back to front, so the last
+ // case is actually the first enum case in the switch.
+ FirstCoveredEnumCase = Case;
+ }
+ }
+
+ // Wrap the inserted cases in braces using a simple heuristic:
+ // Wrap only if at least 90% of existing cases use braces.
+ bool WrapInBraces = useBraces(Switch);
+ auto CreateReplacementForMissingCaseGroup =
+ [&](ArrayRef<const EnumConstantDecl *> UncoveredEnumCases,
+ SourceLocation InsertionLoc = SourceLocation()) {
+ if (UncoveredEnumCases.empty())
+ return;
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ for (const auto *EnumCase : UncoveredEnumCases) {
+ OS << "case ";
+ if (SwitchContext) {
+ const auto *NS = NestedNameSpecifier::getRequiredQualification(
+ Context, SwitchContext, Enum->getLexicalDeclContext());
+ if (NS)
+ NS->print(OS, Context.getPrintingPolicy());
+ }
+ if (Enum->isScoped())
+ OS << Enum->getName() << "::";
+ OS << EnumCase->getName() << ":";
+ if (WrapInBraces)
+ OS << " {";
+ OS << "\n<#code#>\nbreak;\n";
+ if (WrapInBraces)
+ OS << "}\n";
+ }
+
+ if (InsertionLoc.isInvalid()) {
+ // Insert the cases before the 'default' if it's the last case in the
+ // switch.
+ // Note: Switch cases are ordered back to front, so the last default
+ // case would be the first case in the switch statement.
+ if (DefaultCase && DefaultCase == Switch->getSwitchCaseList())
+ InsertionLoc = DefaultCase->getLocStart();
+ else
+ InsertionLoc = Switch->getBody()->getLocEnd();
+ }
+ Consumer(FixItHint::CreateInsertion(
+ Context.getSourceManager().getSpellingLoc(InsertionLoc), OS.str()));
+ };
+
+ // Determine which enum cases are uncovered.
+
+ llvm::SmallVector<std::pair<const EnumConstantDecl *, int64_t>, 8> EnumCases;
+ llvm::SmallVector<int64_t, 8> CoveredEnumCaseValues;
+ for (const auto *EnumCase : Enum->enumerators()) {
+ if (EnumCase->getInitVal().getMinSignedBits() > 64)
+ continue;
+ int64_t Value = EnumCase->getInitVal().getSExtValue();
+ EnumCases.push_back(std::make_pair(EnumCase, Value));
+ if (CoveredEnumCases.count(Value))
+ CoveredEnumCaseValues.push_back(Value);
+ }
+
+ llvm::SmallVector<const EnumConstantDecl *, 8> UncoveredEnumCases;
+ // Figure out if the ordering of the covered enum cases is similar to the
+ // order of enum case values defined in the enum.
+ if (useCaseBasedOrdering(CoveredEnumCaseValues, CoveredEnumCases)) {
+ // Start inserting before the first covered case.
+ SourceLocation InsertionLoc = FirstCoveredEnumCase->getLocStart();
+
+ for (const auto &EnumCase : EnumCases) {
+ if (!CoveredEnumCases.count(EnumCase.second)) {
+ UncoveredEnumCases.push_back(EnumCase.first);
+ continue;
+ }
+ // Create the insertion source replacement for this set of uncovered
+ // cases.
+ CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc);
+ UncoveredEnumCases.clear();
+ // Find the insertion location for the next set of uncovered cases.
+ auto It = CoveredEnumCases.find(EnumCase.second);
+ assert(It != CoveredEnumCases.end() && "Missing enum case");
+ const CaseInfo &Case = It->second;
+ InsertionLoc = Case.NextCase ? Case.NextCase->getLocStart()
+ : /*Insert before end*/ SourceLocation();
+ }
+ CreateReplacementForMissingCaseGroup(UncoveredEnumCases, InsertionLoc);
+ } else {
+ // Gather all of the uncovered enum cases.
+ for (const auto &EnumCase : EnumCases) {
+ if (!CoveredEnumCases.count(EnumCase.second))
+ UncoveredEnumCases.push_back(EnumCase.first);
+ }
+ assert(!UncoveredEnumCases.empty() &&
+ "Can't fill-in enum cases in a full switch");
+ CreateReplacementForMissingCaseGroup(UncoveredEnumCases);
+ }
+}
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index 1094e6d..a6f812a 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -542,6 +542,9 @@
// Initialize the ASTContext
Context->InitBuiltinTypes(*Target);
+ // Adjust printing policy based on language options.
+ Context->setPrintingPolicy(PrintingPolicy(LangOpt));
+
// We didn't have access to the comment options when the ASTContext was
// constructed, so register them now.
Context->getCommentCommandTraits().registerCommentOptions(
diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt
index ba3bd7d..da6a161 100644
--- a/lib/Frontend/CMakeLists.txt
+++ b/lib/Frontend/CMakeLists.txt
@@ -53,6 +53,7 @@
${optional_deps}
LINK_LIBS
+ clangAPINotes
clangAST
clangBasic
clangDriver
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index bb6a665..3cfb9d7 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/APINotes/APINotesReader.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -28,6 +29,7 @@
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/Utils.h"
#include "clang/Frontend/VerifyDiagnosticConsumer.h"
+#include "clang/Index/IndexingAction.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/PTHManager.h"
#include "clang/Lex/Preprocessor.h"
@@ -472,7 +474,7 @@
SmallString<256> SpecificModuleCache(getHeaderSearchOpts().ModuleCachePath);
if (!SpecificModuleCache.empty() && !getHeaderSearchOpts().DisableModuleHash)
llvm::sys::path::append(SpecificModuleCache,
- getInvocation().getModuleHash());
+ getInvocation().getModuleHash(getDiagnostics()));
return SpecificModuleCache.str();
}
@@ -632,6 +634,27 @@
CodeCompleteConsumer *CompletionConsumer) {
TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
TUKind, CompletionConsumer));
+
+ // Set up API notes.
+ TheSema->APINotes.setSwiftVersion(getAPINotesOpts().SwiftVersion);
+
+ // If we're building a module and are supposed to load API notes,
+ // notify the API notes manager.
+ if (auto currentModule = getPreprocessor().getCurrentModule()) {
+ (void)TheSema->APINotes.loadCurrentModuleAPINotes(
+ currentModule,
+ getLangOpts().APINotesModules,
+ getAPINotesOpts().ModuleSearchPaths);
+ // Check for any attributes we should add to the module
+ for (auto reader : TheSema->APINotes.getCurrentModuleReaders()) {
+ // swift_infer_import_as_member
+ if (reader->getModuleOptions().SwiftInferImportAsMember) {
+ currentModule->IsSwiftInferImportAsMember = true;
+ break;
+ }
+ }
+ }
+
// Attach the external sema source if there is any.
if (ExternalSemaSrc) {
TheSema->addExternalSource(ExternalSemaSrc.get());
@@ -1100,9 +1123,11 @@
PPOpts.RetainRemappedFileBuffers = true;
Invocation->getDiagnosticOpts().VerifyDiagnostics = 0;
- assert(ImportingInstance.getInvocation().getModuleHash() ==
- Invocation->getModuleHash() && "Module hash mismatch!");
-
+ assert(ImportingInstance.getInvocation().getModuleHash(
+ ImportingInstance.getDiagnostics()) ==
+ Invocation->getModuleHash(ImportingInstance.getDiagnostics()) &&
+ "Module hash mismatch!");
+
// Construct a compiler instance that will be used to actually create the
// module. Since we're sharing a PCMCache,
// CompilerInstance::CompilerInstance is responsible for finalizing the
@@ -1128,6 +1153,10 @@
SourceMgr.pushModuleBuildStack(ModuleName,
FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager()));
+ // Pass along the GenModuleActionWrapper callback
+ auto wrapGenModuleAction = ImportingInstance.getGenModuleActionWrapper();
+ Instance.setGenModuleActionWrapper(wrapGenModuleAction);
+
// If we're collecting module dependencies, we need to share a collector
// between all of the module CompilerInstances. Other than that, we don't
// want to produce any dependency output from the module build.
@@ -1146,8 +1175,14 @@
llvm::CrashRecoveryContext CRC;
CRC.RunSafelyOnThread(
[&]() {
- GenerateModuleFromModuleMapAction Action;
- Instance.ExecuteAction(Action);
+ // FIXME: I have no idea what the best way to do this is, but it's
+ // probably not this. Interfaces changed upstream.
+ std::unique_ptr<FrontendAction> Action(
+ new GenerateModuleFromModuleMapAction);
+ if (wrapGenModuleAction) {
+ Action = wrapGenModuleAction(FrontendOpts, std::move(Action));
+ }
+ Instance.ExecuteAction(*Action);
},
ThreadStackSize);
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 0d0869c..2bb2946 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -573,33 +573,6 @@
if (!Opts.ProfileInstrumentUsePath.empty())
setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath);
- if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) {
- Opts.setClangABICompat(CodeGenOptions::ClangABI::Latest);
-
- StringRef Ver = A->getValue();
- std::pair<StringRef, StringRef> VerParts = Ver.split('.');
- unsigned Major, Minor = 0;
-
- // Check the version number is valid: either 3.x (0 <= x <= 9) or
- // y or y.0 (4 <= y <= current version).
- if (!VerParts.first.startswith("0") &&
- !VerParts.first.getAsInteger(10, Major) &&
- 3 <= Major && Major <= CLANG_VERSION_MAJOR &&
- (Major == 3 ? VerParts.second.size() == 1 &&
- !VerParts.second.getAsInteger(10, Minor)
- : VerParts.first.size() == Ver.size() ||
- VerParts.second == "0")) {
- // Got a valid version number.
- if (Major == 3 && Minor <= 8)
- Opts.setClangABICompat(CodeGenOptions::ClangABI::Ver3_8);
- else if (Major <= 4)
- Opts.setClangABICompat(CodeGenOptions::ClangABI::Ver4);
- } else if (Ver != "latest") {
- Diags.Report(diag::err_drv_invalid_value)
- << A->getAsString(Args) << A->getValue();
- }
- }
-
Opts.CoverageMapping =
Args.hasFlag(OPT_fcoverage_mapping, OPT_fno_coverage_mapping, false);
Opts.DumpCoverageMapping = Args.hasArg(OPT_dump_coverage_mapping);
@@ -825,6 +798,7 @@
getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags);
Opts.SanitizeMemoryUseAfterDtor =
Args.hasArg(OPT_fsanitize_memory_use_after_dtor);
+ Opts.SanitizeMinimalRuntime = Args.hasArg(OPT_fsanitize_minimal_runtime);
Opts.SanitizeCfiCrossDso = Args.hasArg(OPT_fsanitize_cfi_cross_dso);
Opts.SanitizeStats = Args.hasArg(OPT_fsanitize_stats);
if (Arg *A = Args.getLastArg(OPT_fsanitize_address_use_after_scope,
@@ -1417,6 +1391,10 @@
<< "ARC migration" << "ObjC migration";
}
+ Opts.IndexStorePath = Args.getLastArgValue(OPT_index_store_path);
+ Opts.IndexIgnoreSystemSymbols = Args.hasArg(OPT_index_ignore_system_symbols);
+ Opts.IndexRecordCodegenName = Args.hasArg(OPT_index_record_codegen_name);
+
InputKind DashX(InputKind::Unknown);
if (const Arg *A = Args.getLastArg(OPT_x)) {
StringRef XValue = A->getValue();
@@ -1648,6 +1626,18 @@
Opts.AddVFSOverlayFile(A->getValue());
}
+static void ParseAPINotesArgs(APINotesOptions &Opts, ArgList &Args,
+ DiagnosticsEngine &diags) {
+ using namespace options;
+ if (const Arg *A = Args.getLastArg(OPT_fapinotes_swift_version)) {
+ if (Opts.SwiftVersion.tryParse(A->getValue()))
+ diags.Report(diag::err_drv_invalid_value)
+ << A->getAsString(Args) << A->getValue();
+ }
+ for (const Arg *A : Args.filtered(OPT_iapinotes_modules))
+ Opts.ModuleSearchPaths.push_back(A->getValue());
+}
+
void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
const llvm::Triple &T,
PreprocessorOptions &PPOpts,
@@ -2123,6 +2113,7 @@
Args.hasArg(OPT_fmodules_local_submodule_visibility) || Opts.ModulesTS;
Opts.ModulesCodegen = Args.hasArg(OPT_fmodules_codegen);
Opts.ModulesDebugInfo = Args.hasArg(OPT_fmodules_debuginfo);
+ Opts.ModulesHashErrorDiags = Args.hasArg(OPT_fmodules_hash_error_diagnostics);
Opts.ModulesSearchAll = Opts.Modules &&
!Args.hasArg(OPT_fno_modules_search_all) &&
Args.hasArg(OPT_fmodules_search_all);
@@ -2209,6 +2200,8 @@
// is enabled.
Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns)
| Opts.NativeHalfArgsAndReturns;
+ Opts.APINotes = Args.hasArg(OPT_fapinotes);
+ Opts.APINotesModules = Args.hasArg(OPT_fapinotes_modules);
Opts.GNUAsm = !Args.hasArg(OPT_fno_gnu_inline_asm);
// __declspec is enabled by default for the PS4 by the driver, and also
@@ -2654,6 +2647,8 @@
Res.getTargetOpts());
ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args,
Res.getFileSystemOpts().WorkingDir);
+ ParseAPINotesArgs(Res.getAPINotesOpts(), Args, Diags);
+
if (DashX.getFormat() == InputKind::Precompiled ||
DashX.getLanguage() == InputKind::LLVM_IR) {
// ObjCAAutoRefCount and Sanitize LangOpts are used to setup the
@@ -2713,7 +2708,16 @@
return Success;
}
-std::string CompilerInvocation::getModuleHash() const {
+// Some extension diagnostics aren't explicitly mapped and require custom
+// logic in the dianognostic engine to be used, track -pedantic-errors
+static bool isExtHandlingFromDiagsError(DiagnosticsEngine &Diags) {
+ diag::Severity Ext = Diags.getExtensionHandlingBehavior();
+ if (Ext == diag::Severity::Warning && Diags.getWarningsAsErrors())
+ return true;
+ return Ext >= diag::Severity::Error;
+}
+
+std::string CompilerInvocation::getModuleHash(DiagnosticsEngine &Diags) const {
// Note: For QoI reasons, the things we use as a hash here should all be
// dumped via the -module-info flag.
using llvm::hash_code;
@@ -2782,7 +2786,19 @@
// Extend the signature with the module file extensions.
const FrontendOptions &frontendOpts = getFrontendOpts();
for (const auto &ext : frontendOpts.ModuleFileExtensions) {
- code = ext->hashExtension(code);
+ code = hash_combine(code, ext->hashExtension(code));
+ }
+
+ // Extend the signature with the SWift version for API notes.
+ const APINotesOptions &apiNotesOpts = getAPINotesOpts();
+ if (apiNotesOpts.SwiftVersion) {
+ code = hash_combine(code, apiNotesOpts.SwiftVersion.getMajor());
+ if (auto minor = apiNotesOpts.SwiftVersion.getMinor())
+ code = hash_combine(code, *minor);
+ if (auto subminor = apiNotesOpts.SwiftVersion.getSubminor())
+ code = hash_combine(code, *subminor);
+ if (auto build = apiNotesOpts.SwiftVersion.getBuild())
+ code = hash_combine(code, *build);
}
// Extend the signature with the enabled sanitizers, if at least one is
@@ -2792,6 +2808,24 @@
if (!SanHash.empty())
code = hash_combine(code, SanHash.Mask);
+ // Check for a couple things (see checkDiagnosticMappings in ASTReader.cpp):
+ // -Werror: consider all warnings into the hash
+ // -Werror=something: consider only the specified into the hash
+ // -pedantic-error
+ if (getLangOpts()->ModulesHashErrorDiags) {
+ bool ConsiderAllWarningsAsErrors = Diags.getWarningsAsErrors();
+ code = hash_combine(code, isExtHandlingFromDiagsError(Diags));
+ for (auto DiagIDMappingPair : Diags.getDiagnosticMappings()) {
+ diag::kind DiagID = DiagIDMappingPair.first;
+ auto CurLevel = Diags.getDiagnosticLevel(DiagID, SourceLocation());
+ if (CurLevel < DiagnosticsEngine::Error && !ConsiderAllWarningsAsErrors)
+ continue; // not significant
+ code = hash_combine(
+ code,
+ Diags.getDiagnosticIDs()->getWarningOptionForDiag(DiagID).str());
+ }
+ }
+
return llvm::APInt(64, code).toString(36, /*Signed=*/false);
}
diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp
index 704d515..9f69394 100644
--- a/lib/Frontend/FrontendAction.cpp
+++ b/lib/Frontend/FrontendAction.cpp
@@ -863,6 +863,13 @@
CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename;
}
+ // Add a module declaration scope so that modules from -fmodule-map-file
+ // arguments may shadow modules found implicitly in search paths.
+ CI.getPreprocessor()
+ .getHeaderSearchInfo()
+ .getModuleMap()
+ .finishModuleDeclarationScope();
+
// If we were asked to load any module files, do so now.
for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles)
if (!CI.loadModuleFile(ModuleFile))
diff --git a/lib/Frontend/InitHeaderSearch.cpp b/lib/Frontend/InitHeaderSearch.cpp
index 1d7c8a0..4bde9d0 100644
--- a/lib/Frontend/InitHeaderSearch.cpp
+++ b/lib/Frontend/InitHeaderSearch.cpp
@@ -485,6 +485,7 @@
if (triple.isOSDarwin()) {
AddPath("/System/Library/Frameworks", System, true);
AddPath("/Library/Frameworks", System, true);
+ AddPath("/System/Library/PrivateFrameworks", System, true);
}
}
}
diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt
index 7e11be0..4a713e5 100644
--- a/lib/FrontendTool/CMakeLists.txt
+++ b/lib/FrontendTool/CMakeLists.txt
@@ -8,6 +8,7 @@
clangCodeGen
clangDriver
clangFrontend
+ clangIndex
clangRewriteFrontend
)
diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
index 1666315..4f88087 100644
--- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -23,6 +23,7 @@
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Frontend/Utils.h"
+#include "clang/Index/IndexingAction.h"
#include "clang/Rewrite/Frontend/FrontendActions.h"
#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
#include "llvm/Option/OptTable.h"
@@ -164,6 +165,11 @@
}
#endif
+ if (!FEOpts.IndexStorePath.empty()) {
+ Act = index::createIndexDataRecordingAction(FEOpts, std::move(Act));
+ CI.setGenModuleActionWrapper(&index::createIndexDataRecordingAction);
+ }
+
// If there are any AST files to merge, create a frontend action
// adaptor to perform the merge.
if (!FEOpts.ASTMergeFiles.empty())
diff --git a/lib/Index/BitstreamVisitor.h b/lib/Index/BitstreamVisitor.h
new file mode 100644
index 0000000..d324f1a
--- /dev/null
+++ b/lib/Index/BitstreamVisitor.h
@@ -0,0 +1,163 @@
+//===--- BitstreamVisitor.h - Helper for reading a bitstream --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H
+#define LLVM_CLANG_LIB_INDEX_BITSTREAMVISITOR_H
+
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Serialization/ASTReader.h"
+#include <string>
+
+namespace clang {
+namespace index {
+namespace store {
+
+/// Helper class that saves the current stream position and
+/// then restores it when destroyed.
+struct SavedStreamPosition {
+ explicit SavedStreamPosition(llvm::BitstreamCursor &Cursor)
+ : Cursor(Cursor), Offset(Cursor.GetCurrentBitNo()) { }
+
+ ~SavedStreamPosition() {
+ Cursor.JumpToBit(Offset);
+ }
+
+private:
+ llvm::BitstreamCursor &Cursor;
+ uint64_t Offset;
+};
+
+enum class StreamVisit {
+ Continue,
+ Skip,
+ Abort
+};
+
+template <typename ImplClass>
+class BitstreamVisitor {
+ SmallVector<unsigned, 4> BlockStack;
+
+protected:
+ llvm::BitstreamCursor &Stream;
+ Optional<llvm::BitstreamBlockInfo> BlockInfo;
+ std::string *Error;
+
+public:
+ BitstreamVisitor(llvm::BitstreamCursor &Stream)
+ : Stream(Stream) {}
+
+ StreamVisit visitBlock(unsigned ID) {
+ return StreamVisit::Continue;
+ }
+
+ bool visit(std::string &Error) {
+ this->Error = &Error;
+
+ ASTReader::RecordData Record;
+ while (1) {
+ llvm::BitstreamEntry Entry = Stream.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd);
+
+ switch (Entry.Kind) {
+ case llvm::BitstreamEntry::Error:
+ Error = "malformed serialization";
+ return false;
+
+ case llvm::BitstreamEntry::EndBlock:
+ if (BlockStack.empty())
+ return true;
+ BlockStack.pop_back();
+ if (Stream.ReadBlockEnd()) {
+ Error = "malformed serialization";
+ return false;
+ }
+ if (Stream.AtEndOfStream())
+ return true;
+ break;
+
+ case llvm::BitstreamEntry::SubBlock: {
+ if (Entry.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) {
+ BlockInfo = Stream.ReadBlockInfoBlock();
+ if (!BlockInfo) {
+ Error = "malformed BlockInfoBlock";
+ return false;
+ }
+ Stream.setBlockInfo(&*BlockInfo);
+ break;
+ }
+
+ StreamVisit Ret = static_cast<ImplClass*>(this)->visitBlock(Entry.ID);
+ switch (Ret) {
+ case StreamVisit::Continue:
+ if (Stream.EnterSubBlock(Entry.ID)) {
+ Error = "malformed block record";
+ return false;
+ }
+ readBlockAbbrevs(Stream);
+ BlockStack.push_back(Entry.ID);
+ break;
+
+ case StreamVisit::Skip:
+ if (Stream.SkipBlock()) {
+ Error = "malformed serialization";
+ return false;
+ }
+ if (Stream.AtEndOfStream())
+ return true;
+ break;
+
+ case StreamVisit::Abort:
+ return false;
+ }
+ break;
+ }
+
+ case llvm::BitstreamEntry::Record: {
+ Record.clear();
+ StringRef Blob;
+ unsigned RecID = Stream.readRecord(Entry.ID, Record, &Blob);
+ unsigned BlockID = BlockStack.empty() ? 0 : BlockStack.back();
+ StreamVisit Ret = static_cast<ImplClass*>(this)->visitRecord(BlockID, RecID, Record, Blob);
+ switch (Ret) {
+ case StreamVisit::Continue:
+ break;
+
+ case StreamVisit::Skip:
+ Stream.skipRecord(Entry.ID);
+ break;
+
+ case StreamVisit::Abort:
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ static void readBlockAbbrevs(llvm::BitstreamCursor &Cursor) {
+ while (true) {
+ uint64_t Offset = Cursor.GetCurrentBitNo();
+ unsigned Code = Cursor.ReadCode();
+
+ // We expect all abbrevs to be at the start of the block.
+ if (Code != llvm::bitc::DEFINE_ABBREV) {
+ Cursor.JumpToBit(Offset);
+ return;
+ }
+ Cursor.ReadAbbrevRecord();
+ }
+ }
+};
+
+} // end namespace store
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/lib/Index/CMakeLists.txt b/lib/Index/CMakeLists.txt
index c9fbfaf..bb17d15 100644
--- a/lib/Index/CMakeLists.txt
+++ b/lib/Index/CMakeLists.txt
@@ -1,17 +1,27 @@
set(LLVM_LINK_COMPONENTS
+ BitReader
Core
Support
)
add_clang_library(clangIndex
+ ClangIndexRecordWriter.cpp
CodegenNameGenerator.cpp
CommentToXML.cpp
+ FileIndexRecord.cpp
IndexBody.cpp
+ IndexDataStore.cpp
+ IndexDataStoreUtils.cpp
IndexDecl.cpp
IndexingAction.cpp
IndexingContext.cpp
+ IndexRecordHasher.cpp
+ IndexRecordReader.cpp
+ IndexRecordWriter.cpp
IndexSymbol.cpp
IndexTypeSourceInfo.cpp
+ IndexUnitReader.cpp
+ IndexUnitWriter.cpp
USRGeneration.cpp
ADDITIONAL_HEADERS
@@ -23,6 +33,7 @@
clangBasic
clangFormat
clangFrontend
+ clangLex
clangRewrite
clangSerialization
clangToolingCore
diff --git a/lib/Index/ClangIndexRecordWriter.cpp b/lib/Index/ClangIndexRecordWriter.cpp
new file mode 100644
index 0000000..612ef1b
--- /dev/null
+++ b/lib/Index/ClangIndexRecordWriter.cpp
@@ -0,0 +1,128 @@
+//===--- ClangIndexRecordWriter.cpp - Index record serialization ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangIndexRecordWriter.h"
+#include "FileIndexRecord.h"
+#include "clang/Index/IndexSymbol.h"
+#include "clang/Index/IndexRecordReader.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+
+using namespace clang;
+using namespace clang::index;
+
+StringRef ClangIndexRecordWriter::getUSR(const Decl *D) {
+ assert(D->isCanonicalDecl());
+ auto Insert = USRByDecl.insert(std::make_pair(D, StringRef()));
+ if (Insert.second) {
+ Insert.first->second = getUSRNonCached(D);
+ }
+ return Insert.first->second;
+}
+
+StringRef ClangIndexRecordWriter::getUSRNonCached(const Decl *D) {
+ SmallString<256> Buf;
+ bool Ignore = generateUSRForDecl(D, Buf);
+ if (Ignore)
+ return StringRef();
+ StringRef USR = Buf.str();
+ char *Ptr = Allocator.Allocate<char>(USR.size());
+ std::copy(USR.begin(), USR.end(), Ptr);
+ return StringRef(Ptr, USR.size());
+}
+
+ClangIndexRecordWriter::ClangIndexRecordWriter(ASTContext &Ctx,
+ RecordingOptions Opts)
+ : Impl(Opts.DataDirPath), Ctx(Ctx), RecordOpts(std::move(Opts)),
+ Hasher(Ctx) {
+ if (Opts.RecordSymbolCodeGenName)
+ CGNameGen.reset(new CodegenNameGenerator(Ctx));
+}
+
+ClangIndexRecordWriter::~ClangIndexRecordWriter() {}
+
+bool ClangIndexRecordWriter::writeRecord(StringRef Filename,
+ const FileIndexRecord &IdxRecord,
+ std::string &Error,
+ std::string *OutRecordFile) {
+
+ auto RecordHash = Hasher.hashRecord(IdxRecord);
+
+ switch (Impl.beginRecord(Filename, RecordHash, Error, OutRecordFile)) {
+ case IndexRecordWriter::Result::Success:
+ break; // Continue writing.
+ case IndexRecordWriter::Result::Failure:
+ return true;
+ case IndexRecordWriter::Result::AlreadyExists:
+ return false;
+ }
+
+ ASTContext &Ctx = getASTContext();
+ SourceManager &SM = Ctx.getSourceManager();
+ FileID FID = IdxRecord.getFileID();
+ auto getLineCol = [&](unsigned Offset) -> std::pair<unsigned, unsigned> {
+ unsigned LineNo = SM.getLineNumber(FID, Offset);
+ unsigned ColNo = SM.getColumnNumber(FID, Offset);
+ return std::make_pair(LineNo, ColNo);
+ };
+
+ for (auto &Occur : IdxRecord.getDeclOccurrences()) {
+ unsigned Line, Col;
+ std::tie(Line, Col) = getLineCol(Occur.Offset);
+ SmallVector<writer::SymbolRelation, 3> Related;
+ Related.reserve(Occur.Relations.size());
+ for (auto &Rel : Occur.Relations)
+ Related.push_back(writer::SymbolRelation{Rel.RelatedSymbol, Rel.Roles});
+
+ Impl.addOccurrence(Occur.Dcl, Occur.Roles, Line, Col, Related);
+ }
+
+ PrintingPolicy Policy(Ctx.getLangOpts());
+ Policy.SuppressTemplateArgsInCXXConstructors = true;
+
+ auto Result = Impl.endRecord(Error,
+ [&](writer::OpaqueDecl OD, SmallVectorImpl<char> &Scratch) {
+ const Decl *D = static_cast<const Decl *>(OD);
+ auto Info = getSymbolInfo(D);
+
+ writer::Symbol Sym;
+ Sym.SymInfo = Info;
+
+ auto *ND = dyn_cast<NamedDecl>(D);
+ if (ND) {
+ llvm::raw_svector_ostream OS(Scratch);
+ DeclarationName DeclName = ND->getDeclName();
+ if (!DeclName.isEmpty())
+ DeclName.print(OS, Policy);
+ }
+ unsigned NameLen = Scratch.size();
+ Sym.Name = StringRef(Scratch.data(), NameLen);
+
+ Sym.USR = getUSR(D);
+ assert(!Sym.USR.empty() && "Recorded decl without USR!");
+
+ if (CGNameGen && ND) {
+ llvm::raw_svector_ostream OS(Scratch);
+ CGNameGen->writeName(ND, OS);
+ }
+ unsigned CGNameLen = Scratch.size() - NameLen;
+ Sym.CodeGenName = StringRef(Scratch.data() + NameLen, CGNameLen);
+ return Sym;
+ });
+
+ switch (Result) {
+ case IndexRecordWriter::Result::Success:
+ case IndexRecordWriter::Result::AlreadyExists:
+ return false;
+ case IndexRecordWriter::Result::Failure:
+ return true;
+ }
+}
diff --git a/lib/Index/ClangIndexRecordWriter.h b/lib/Index/ClangIndexRecordWriter.h
new file mode 100644
index 0000000..b68f987
--- /dev/null
+++ b/lib/Index/ClangIndexRecordWriter.h
@@ -0,0 +1,55 @@
+//===--- ClangIndexRecordWriter.h - Index record serialization ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H
+#define LLVM_CLANG_LIB_INDEX_CLANGINDEXRECORDWRITER_H
+
+#include "IndexRecordHasher.h"
+#include "clang/Index/IndexRecordWriter.h"
+#include "clang/Index/IndexingAction.h"
+#include "clang/Index/CodegenNameGenerator.h"
+#include "llvm/ADT/SmallString.h"
+
+namespace clang {
+ class ASTContext;
+ class Decl;
+
+namespace index {
+ class FileIndexRecord;
+
+class ClangIndexRecordWriter {
+ IndexRecordWriter Impl;
+
+ ASTContext &Ctx;
+ RecordingOptions RecordOpts;
+
+ std::unique_ptr<CodegenNameGenerator> CGNameGen;
+ llvm::BumpPtrAllocator Allocator;
+ llvm::DenseMap<const Decl *, StringRef> USRByDecl;
+ IndexRecordHasher Hasher;
+
+public:
+ ClangIndexRecordWriter(ASTContext &Ctx, RecordingOptions Opts);
+ ~ClangIndexRecordWriter();
+
+ ASTContext &getASTContext() { return Ctx; }
+ CodegenNameGenerator *getCGNameGen() { return CGNameGen.get(); }
+
+ bool writeRecord(StringRef Filename, const FileIndexRecord &Record,
+ std::string &Error, std::string *RecordFile = nullptr);
+ StringRef getUSR(const Decl *D);
+
+private:
+ StringRef getUSRNonCached(const Decl *D);
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/lib/Index/CommentToXML.cpp b/lib/Index/CommentToXML.cpp
index e568c83..918068a 100644
--- a/lib/Index/CommentToXML.cpp
+++ b/lib/Index/CommentToXML.cpp
@@ -579,6 +579,7 @@
PrintingPolicy PPolicy(LangOpts);
PPolicy.PolishForDeclaration = true;
PPolicy.TerseOutput = true;
+ PPolicy.ConstantsAsWritten = true;
ThisDecl->CurrentDecl->print(OS, PPolicy,
/*Indentation*/0, /*PrintInstantiation*/false);
}
diff --git a/lib/Index/FileIndexRecord.cpp b/lib/Index/FileIndexRecord.cpp
new file mode 100644
index 0000000..98d66b6
--- /dev/null
+++ b/lib/Index/FileIndexRecord.cpp
@@ -0,0 +1,59 @@
+//===--- FileIndexRecord.cpp - Index data per file ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FileIndexRecord.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang;
+using namespace clang::index;
+
+void FileIndexRecord::addDeclOccurence(SymbolRoleSet Roles,
+ unsigned Offset,
+ const Decl *D,
+ ArrayRef<SymbolRelation> Relations) {
+ assert(D->isCanonicalDecl());
+
+ auto IsNextOccurence = [&]()->bool {
+ if (Decls.empty())
+ return true;
+ auto &Last = Decls.back();
+ return Last.Offset < Offset;
+ };
+
+ if (IsNextOccurence()) {
+ Decls.emplace_back(Roles, Offset, D, Relations);
+ return;
+ }
+
+ DeclOccurrence NewInfo(Roles, Offset, D, Relations);
+ auto It = std::upper_bound(Decls.begin(), Decls.end(), NewInfo);
+ Decls.insert(It, std::move(NewInfo));
+}
+
+void FileIndexRecord::print(llvm::raw_ostream &OS) {
+ OS << "DECLS BEGIN ---\n";
+ for (auto &DclInfo : Decls) {
+ auto D = DclInfo.Dcl;
+ SourceManager &SM = D->getASTContext().getSourceManager();
+ SourceLocation Loc = SM.getFileLoc(D->getLocation());
+ PresumedLoc PLoc = SM.getPresumedLoc(Loc);
+ OS << llvm::sys::path::filename(PLoc.getFilename()) << ':' << PLoc.getLine()
+ << ':' << PLoc.getColumn();
+
+ if (auto ND = dyn_cast<NamedDecl>(D)) {
+ OS << ' ' << ND->getNameAsString();
+ }
+
+ OS << '\n';
+ }
+ OS << "DECLS END ---\n";
+}
diff --git a/lib/Index/FileIndexRecord.h b/lib/Index/FileIndexRecord.h
new file mode 100644
index 0000000..a663173
--- /dev/null
+++ b/lib/Index/FileIndexRecord.h
@@ -0,0 +1,69 @@
+//===--- FileIndexRecord.h - Index data per file --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H
+#define LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H
+
+#include "clang/Index/IndexSymbol.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include <vector>
+
+namespace clang {
+ class IdentifierInfo;
+
+namespace index {
+
+class FileIndexRecord {
+public:
+ struct DeclOccurrence {
+ SymbolRoleSet Roles;
+ unsigned Offset;
+ const Decl *Dcl;
+ SmallVector<SymbolRelation, 3> Relations;
+
+ DeclOccurrence(SymbolRoleSet R,
+ unsigned Offset,
+ const Decl *D,
+ ArrayRef<SymbolRelation> Relations)
+ : Roles(R),
+ Offset(Offset),
+ Dcl(D),
+ Relations(Relations.begin(), Relations.end()) {}
+
+ friend bool operator <(const DeclOccurrence &LHS, const DeclOccurrence &RHS) {
+ return LHS.Offset < RHS.Offset;
+ }
+ };
+
+private:
+ FileID FID;
+ bool IsSystem;
+ std::vector<DeclOccurrence> Decls;
+
+public:
+ FileIndexRecord(FileID FID, bool isSystem) : FID(FID), IsSystem(isSystem) {}
+
+ ArrayRef<DeclOccurrence> getDeclOccurrences() const { return Decls; }
+
+ FileID getFileID() const { return FID; }
+ bool isSystem() const { return IsSystem; }
+
+ void addDeclOccurence(SymbolRoleSet Roles,
+ unsigned Offset,
+ const Decl *D,
+ ArrayRef<SymbolRelation> Relations);
+ void print(llvm::raw_ostream &OS);
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/lib/Index/IndexDataStore.cpp b/lib/Index/IndexDataStore.cpp
new file mode 100644
index 0000000..4140392
--- /dev/null
+++ b/lib/Index/IndexDataStore.cpp
@@ -0,0 +1,259 @@
+//===--- IndexDataStore.cpp - Index data store info -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/IndexDataStore.h"
+#include "IndexDataStoreUtils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Mutex.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::index;
+using namespace clang::index::store;
+using namespace llvm;
+
+static void appendSubDir(StringRef subdir, SmallVectorImpl<char> &StorePathBuf) {
+ SmallString<10> VersionPath;
+ raw_svector_ostream(VersionPath) << 'v' << STORE_FORMAT_VERSION;
+
+ sys::path::append(StorePathBuf, VersionPath);
+ sys::path::append(StorePathBuf, subdir);
+}
+
+void store::appendInteriorUnitPath(StringRef UnitName,
+ SmallVectorImpl<char> &PathBuf) {
+ sys::path::append(PathBuf, UnitName);
+}
+
+void store::appendUnitSubDir(SmallVectorImpl<char> &StorePathBuf) {
+ return appendSubDir("units", StorePathBuf);
+}
+
+void store::appendRecordSubDir(SmallVectorImpl<char> &StorePathBuf) {
+ return appendSubDir("records", StorePathBuf);
+}
+
+void store::appendInteriorRecordPath(StringRef RecordName,
+ SmallVectorImpl<char> &PathBuf) {
+ // To avoid putting a huge number of files into the records directory, create
+ // subdirectories based on the last 2 characters from the hash.
+ StringRef hash2chars = RecordName.substr(RecordName.size()-2);
+ sys::path::append(PathBuf, hash2chars);
+ sys::path::append(PathBuf, RecordName);
+}
+
+//===----------------------------------------------------------------------===//
+// IndexDataStore
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class UnitEventHandlerData {
+ mutable sys::Mutex Mtx;
+ IndexDataStore::UnitEventHandler Handler;
+
+public:
+ void setHandler(IndexDataStore::UnitEventHandler handler) {
+ sys::ScopedLock L(Mtx);
+ Handler = std::move(handler);
+ }
+ IndexDataStore::UnitEventHandler getHandler() const {
+ sys::ScopedLock L(Mtx);
+ return Handler;
+ }
+};
+
+class IndexDataStoreImpl {
+ std::string FilePath;
+ std::shared_ptr<UnitEventHandlerData> TheUnitEventHandlerData;
+ std::unique_ptr<AbstractDirectoryWatcher> DirWatcher;
+
+public:
+ explicit IndexDataStoreImpl(StringRef indexStorePath)
+ : FilePath(indexStorePath) {
+ TheUnitEventHandlerData = std::make_shared<UnitEventHandlerData>();
+ }
+
+ StringRef getFilePath() const { return FilePath; }
+ bool foreachUnitName(bool sorted,
+ llvm::function_ref<bool(StringRef unitName)> receiver);
+ void setUnitEventHandler(IndexDataStore::UnitEventHandler Handler);
+ bool startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn,
+ bool waitInitialSync, std::string &Error);
+ void stopEventListening();
+ void discardUnit(StringRef UnitName);
+ void discardRecord(StringRef RecordName);
+ void purgeStaleData();
+};
+
+} // anonymous namespace
+
+bool IndexDataStoreImpl::foreachUnitName(bool sorted,
+ llvm::function_ref<bool(StringRef unitName)> receiver) {
+ SmallString<128> UnitPath;
+ UnitPath = FilePath;
+ appendUnitSubDir(UnitPath);
+
+ std::vector<std::string> filenames;
+
+ std::error_code EC;
+ for (auto It = sys::fs::directory_iterator(UnitPath, EC),
+ End = sys::fs::directory_iterator();
+ !EC && It != End; It.increment(EC)) {
+ StringRef unitName = sys::path::filename(It->path());
+ if (!sorted) {
+ if (!receiver(unitName))
+ return false;
+ } else {
+ filenames.push_back(unitName);
+ }
+ }
+
+ if (sorted) {
+ llvm::array_pod_sort(filenames.begin(), filenames.end());
+ for (auto &fname : filenames)
+ if (!receiver(fname))
+ return false;
+ }
+ return true;
+}
+
+void IndexDataStoreImpl::setUnitEventHandler(IndexDataStore::UnitEventHandler handler) {
+ TheUnitEventHandlerData->setHandler(std::move(handler));
+}
+
+bool IndexDataStoreImpl::startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn,
+ bool waitInitialSync, std::string &Error) {
+ if (DirWatcher) {
+ Error = "event listener already active";
+ return true;
+ }
+
+ SmallString<128> UnitPath;
+ UnitPath = FilePath;
+ appendUnitSubDir(UnitPath);
+
+ auto localUnitEventHandlerData = TheUnitEventHandlerData;
+ auto OnUnitsChange = [localUnitEventHandlerData](ArrayRef<AbstractDirectoryWatcher::Event> Events, bool isInitial) {
+ SmallVector<IndexDataStore::UnitEvent, 16> UnitEvents;
+ UnitEvents.reserve(Events.size());
+ for (const AbstractDirectoryWatcher::Event &evt : Events) {
+ IndexDataStore::UnitEventKind K;
+ StringRef UnitName = sys::path::filename(evt.Filename);
+ switch (evt.Kind) {
+ case AbstractDirectoryWatcher::EventKind::Added:
+ K = IndexDataStore::UnitEventKind::Added; break;
+ case AbstractDirectoryWatcher::EventKind::Removed:
+ K = IndexDataStore::UnitEventKind::Removed; break;
+ case AbstractDirectoryWatcher::EventKind::Modified:
+ K = IndexDataStore::UnitEventKind::Modified; break;
+ case AbstractDirectoryWatcher::EventKind::DirectoryDeleted:
+ K = IndexDataStore::UnitEventKind::DirectoryDeleted;
+ UnitName = StringRef();
+ break;
+ }
+ UnitEvents.push_back(IndexDataStore::UnitEvent{K, UnitName, evt.ModTime});
+ }
+
+ if (auto handler = localUnitEventHandlerData->getHandler()) {
+ IndexDataStore::UnitEventNotification EventNote{isInitial, UnitEvents};
+ handler(EventNote);
+ }
+ };
+
+ DirWatcher = createFn(UnitPath.str(), OnUnitsChange, waitInitialSync, Error);
+ if (!DirWatcher)
+ return true;
+
+ return false;
+}
+
+void IndexDataStoreImpl::stopEventListening() {
+ DirWatcher.reset();
+}
+
+void IndexDataStoreImpl::discardUnit(StringRef UnitName) {
+ SmallString<128> UnitPath;
+ UnitPath = FilePath;
+ appendUnitSubDir(UnitPath);
+ appendInteriorUnitPath(UnitName, UnitPath);
+ sys::fs::remove(UnitPath);
+}
+
+void IndexDataStoreImpl::discardRecord(StringRef RecordName) {
+ SmallString<128> RecordPath;
+ RecordPath = FilePath;
+ appendRecordSubDir(RecordPath);
+ appendInteriorRecordPath(RecordName, RecordPath);
+ sys::fs::remove(RecordPath);
+}
+
+void IndexDataStoreImpl::purgeStaleData() {
+ // FIXME: Implement.
+}
+
+
+std::unique_ptr<IndexDataStore>
+IndexDataStore::create(StringRef IndexStorePath, std::string &Error) {
+ if (!sys::fs::exists(IndexStorePath)) {
+ raw_string_ostream OS(Error);
+ OS << "index store path does not exist: " << IndexStorePath;
+ return nullptr;
+ }
+
+ return std::unique_ptr<IndexDataStore>(
+ new IndexDataStore(new IndexDataStoreImpl(IndexStorePath)));
+}
+
+#define IMPL static_cast<IndexDataStoreImpl*>(Impl)
+
+IndexDataStore::~IndexDataStore() {
+ delete IMPL;
+}
+
+StringRef IndexDataStore::getFilePath() const {
+ return IMPL->getFilePath();
+}
+
+bool IndexDataStore::foreachUnitName(bool sorted,
+ llvm::function_ref<bool(StringRef unitName)> receiver) {
+ return IMPL->foreachUnitName(sorted, std::move(receiver));
+}
+
+unsigned IndexDataStore::getFormatVersion() {
+ return STORE_FORMAT_VERSION;
+}
+
+void IndexDataStore::setUnitEventHandler(UnitEventHandler Handler) {
+ return IMPL->setUnitEventHandler(std::move(Handler));
+}
+
+bool IndexDataStore::startEventListening(llvm::function_ref<AbstractDirectoryWatcher::CreateFnTy> createFn,
+ bool waitInitialSync, std::string &Error) {
+ return IMPL->startEventListening(std::move(createFn), waitInitialSync, Error);
+}
+
+void IndexDataStore::stopEventListening() {
+ return IMPL->stopEventListening();
+}
+
+void IndexDataStore::discardUnit(StringRef UnitName) {
+ IMPL->discardUnit(UnitName);
+}
+
+void IndexDataStore::discardRecord(StringRef RecordName) {
+ IMPL->discardRecord(RecordName);
+}
+
+void IndexDataStore::purgeStaleData() {
+ IMPL->purgeStaleData();
+}
diff --git a/lib/Index/IndexDataStoreUtils.cpp b/lib/Index/IndexDataStoreUtils.cpp
new file mode 100644
index 0000000..3efaec9
--- /dev/null
+++ b/lib/Index/IndexDataStoreUtils.cpp
@@ -0,0 +1,410 @@
+//===--- IndexDataStoreUtils.cpp - Functions/constants for the data store -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/IndexDataStoreSymbolUtils.h"
+#include "IndexDataStoreUtils.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+
+using namespace clang;
+using namespace clang::index;
+using namespace clang::index::store;
+using namespace llvm;
+
+void store::emitBlockID(unsigned ID, const char *Name,
+ BitstreamWriter &Stream, RecordDataImpl &Record) {
+ Record.clear();
+ Record.push_back(ID);
+ Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, Record);
+
+ // Emit the block name if present.
+ if (!Name || Name[0] == 0)
+ return;
+ Record.clear();
+ while (*Name)
+ Record.push_back(*Name++);
+ Stream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, Record);
+}
+
+void store::emitRecordID(unsigned ID, const char *Name,
+ BitstreamWriter &Stream,
+ RecordDataImpl &Record) {
+ Record.clear();
+ Record.push_back(ID);
+ while (*Name)
+ Record.push_back(*Name++);
+ Stream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, Record);
+}
+
+/// Map an indexstore_symbol_kind_t to a SymbolKind, handling unknown values.
+SymbolKind index::getSymbolKind(indexstore_symbol_kind_t K) {
+ switch ((uint64_t)K) {
+ default:
+ case INDEXSTORE_SYMBOL_KIND_UNKNOWN:
+ return SymbolKind::Unknown;
+ case INDEXSTORE_SYMBOL_KIND_MODULE:
+ return SymbolKind::Module;
+ case INDEXSTORE_SYMBOL_KIND_NAMESPACE:
+ return SymbolKind::Namespace;
+ case INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS:
+ return SymbolKind::NamespaceAlias;
+ case INDEXSTORE_SYMBOL_KIND_MACRO:
+ return SymbolKind::Macro;
+ case INDEXSTORE_SYMBOL_KIND_ENUM:
+ return SymbolKind::Enum;
+ case INDEXSTORE_SYMBOL_KIND_STRUCT:
+ return SymbolKind::Struct;
+ case INDEXSTORE_SYMBOL_KIND_CLASS:
+ return SymbolKind::Class;
+ case INDEXSTORE_SYMBOL_KIND_PROTOCOL:
+ return SymbolKind::Protocol;
+ case INDEXSTORE_SYMBOL_KIND_EXTENSION:
+ return SymbolKind::Extension;
+ case INDEXSTORE_SYMBOL_KIND_UNION:
+ return SymbolKind::Union;
+ case INDEXSTORE_SYMBOL_KIND_TYPEALIAS:
+ return SymbolKind::TypeAlias;
+ case INDEXSTORE_SYMBOL_KIND_FUNCTION:
+ return SymbolKind::Function;
+ case INDEXSTORE_SYMBOL_KIND_VARIABLE:
+ return SymbolKind::Variable;
+ case INDEXSTORE_SYMBOL_KIND_FIELD:
+ return SymbolKind::Field;
+ case INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT:
+ return SymbolKind::EnumConstant;
+ case INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD:
+ return SymbolKind::InstanceMethod;
+ case INDEXSTORE_SYMBOL_KIND_CLASSMETHOD:
+ return SymbolKind::ClassMethod;
+ case INDEXSTORE_SYMBOL_KIND_STATICMETHOD:
+ return SymbolKind::StaticMethod;
+ case INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY:
+ return SymbolKind::InstanceProperty;
+ case INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY:
+ return SymbolKind::ClassProperty;
+ case INDEXSTORE_SYMBOL_KIND_STATICPROPERTY:
+ return SymbolKind::StaticProperty;
+ case INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR:
+ return SymbolKind::Constructor;
+ case INDEXSTORE_SYMBOL_KIND_DESTRUCTOR:
+ return SymbolKind::Destructor;
+ case INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION:
+ return SymbolKind::ConversionFunction;
+ case INDEXSTORE_SYMBOL_KIND_PARAMETER:
+ return SymbolKind::Parameter;
+ case INDEXSTORE_SYMBOL_KIND_USING:
+ return SymbolKind::Using;
+ case INDEXSTORE_SYMBOL_KIND_COMMENTTAG:
+ return SymbolKind::CommentTag;
+ }
+}
+
+SymbolSubKind index::getSymbolSubKind(indexstore_symbol_subkind_t K) {
+ switch ((uint64_t)K) {
+ default:
+ case INDEXSTORE_SYMBOL_SUBKIND_NONE:
+ return SymbolSubKind::None;
+ case INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR:
+ return SymbolSubKind::CXXCopyConstructor;
+ case INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR:
+ return SymbolSubKind::CXXMoveConstructor;
+ case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER:
+ return SymbolSubKind::AccessorGetter;
+ case INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER:
+ return SymbolSubKind::AccessorSetter;
+ case INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME:
+ return SymbolSubKind::UsingTypename;
+ case INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE:
+ return SymbolSubKind::UsingValue;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET:
+ return SymbolSubKind::SwiftAccessorWillSet;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET:
+ return SymbolSubKind::SwiftAccessorDidSet;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR:
+ return SymbolSubKind::SwiftAccessorAddressor;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR:
+ return SymbolSubKind::SwiftAccessorMutableAddressor;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT:
+ return SymbolSubKind::SwiftExtensionOfStruct;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS:
+ return SymbolSubKind::SwiftExtensionOfClass;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM:
+ return SymbolSubKind::SwiftExtensionOfEnum;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL:
+ return SymbolSubKind::SwiftExtensionOfProtocol;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR:
+ return SymbolSubKind::SwiftPrefixOperator;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR:
+ return SymbolSubKind::SwiftPostfixOperator;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR:
+ return SymbolSubKind::SwiftInfixOperator;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT:
+ return SymbolSubKind::SwiftSubscript;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE:
+ return SymbolSubKind::SwiftAssociatedType;
+ case INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM:
+ return SymbolSubKind::SwiftGenericTypeParam;
+ }
+}
+
+/// Map an indexstore_symbol_language_t to a SymbolLanguage, handling unknown
+/// values.
+SymbolLanguage index::getSymbolLanguage(indexstore_symbol_language_t L) {
+ switch ((uint64_t)L) {
+ default: // FIXME: add an unknown language?
+ case INDEXSTORE_SYMBOL_LANG_C:
+ return SymbolLanguage::C;
+ case INDEXSTORE_SYMBOL_LANG_OBJC:
+ return SymbolLanguage::ObjC;
+ case INDEXSTORE_SYMBOL_LANG_CXX:
+ return SymbolLanguage::CXX;
+ case INDEXSTORE_SYMBOL_LANG_SWIFT:
+ return SymbolLanguage::Swift;
+ }
+}
+
+/// Map an indexstore representation to a SymbolPropertySet, handling
+/// unknown values.
+SymbolPropertySet index::getSymbolProperties(uint64_t Props) {
+ // FIXME: currently these enums must be kept in sync.
+ return (uint64_t)Props;
+}
+
+/// Map an indexstore representation to a SymbolRoleSet, handling unknown
+/// values.
+SymbolRoleSet index::getSymbolRoles(uint64_t Roles) {
+ // FIXME: currently these enums must be kept in sync.
+ return (uint64_t)Roles;
+}
+
+/// Map a SymbolLanguage to a indexstore_symbol_language_t.
+indexstore_symbol_kind_t index::getIndexStoreKind(SymbolKind K) {
+ switch (K) {
+ case SymbolKind::Unknown:
+ return INDEXSTORE_SYMBOL_KIND_UNKNOWN;
+ case SymbolKind::Module:
+ return INDEXSTORE_SYMBOL_KIND_MODULE;
+ case SymbolKind::Namespace:
+ return INDEXSTORE_SYMBOL_KIND_NAMESPACE;
+ case SymbolKind::NamespaceAlias:
+ return INDEXSTORE_SYMBOL_KIND_NAMESPACEALIAS;
+ case SymbolKind::Macro:
+ return INDEXSTORE_SYMBOL_KIND_MACRO;
+ case SymbolKind::Enum:
+ return INDEXSTORE_SYMBOL_KIND_ENUM;
+ case SymbolKind::Struct:
+ return INDEXSTORE_SYMBOL_KIND_STRUCT;
+ case SymbolKind::Class:
+ return INDEXSTORE_SYMBOL_KIND_CLASS;
+ case SymbolKind::Protocol:
+ return INDEXSTORE_SYMBOL_KIND_PROTOCOL;
+ case SymbolKind::Extension:
+ return INDEXSTORE_SYMBOL_KIND_EXTENSION;
+ case SymbolKind::Union:
+ return INDEXSTORE_SYMBOL_KIND_UNION;
+ case SymbolKind::TypeAlias:
+ return INDEXSTORE_SYMBOL_KIND_TYPEALIAS;
+ case SymbolKind::Function:
+ return INDEXSTORE_SYMBOL_KIND_FUNCTION;
+ case SymbolKind::Variable:
+ return INDEXSTORE_SYMBOL_KIND_VARIABLE;
+ case SymbolKind::Field:
+ return INDEXSTORE_SYMBOL_KIND_FIELD;
+ case SymbolKind::EnumConstant:
+ return INDEXSTORE_SYMBOL_KIND_ENUMCONSTANT;
+ case SymbolKind::InstanceMethod:
+ return INDEXSTORE_SYMBOL_KIND_INSTANCEMETHOD;
+ case SymbolKind::ClassMethod:
+ return INDEXSTORE_SYMBOL_KIND_CLASSMETHOD;
+ case SymbolKind::StaticMethod:
+ return INDEXSTORE_SYMBOL_KIND_STATICMETHOD;
+ case SymbolKind::InstanceProperty:
+ return INDEXSTORE_SYMBOL_KIND_INSTANCEPROPERTY;
+ case SymbolKind::ClassProperty:
+ return INDEXSTORE_SYMBOL_KIND_CLASSPROPERTY;
+ case SymbolKind::StaticProperty:
+ return INDEXSTORE_SYMBOL_KIND_STATICPROPERTY;
+ case SymbolKind::Constructor:
+ return INDEXSTORE_SYMBOL_KIND_CONSTRUCTOR;
+ case SymbolKind::Destructor:
+ return INDEXSTORE_SYMBOL_KIND_DESTRUCTOR;
+ case SymbolKind::ConversionFunction:
+ return INDEXSTORE_SYMBOL_KIND_CONVERSIONFUNCTION;
+ case SymbolKind::Parameter:
+ return INDEXSTORE_SYMBOL_KIND_PARAMETER;
+ case SymbolKind::Using:
+ return INDEXSTORE_SYMBOL_KIND_USING;
+ case SymbolKind::CommentTag:
+ return INDEXSTORE_SYMBOL_KIND_COMMENTTAG;
+ }
+ llvm_unreachable("unexpected symbol kind");
+}
+
+indexstore_symbol_subkind_t index::getIndexStoreSubKind(SymbolSubKind K) {
+ switch (K) {
+ case SymbolSubKind::None:
+ return INDEXSTORE_SYMBOL_SUBKIND_NONE;
+ case SymbolSubKind::CXXCopyConstructor:
+ return INDEXSTORE_SYMBOL_SUBKIND_CXXCOPYCONSTRUCTOR;
+ case SymbolSubKind::CXXMoveConstructor:
+ return INDEXSTORE_SYMBOL_SUBKIND_CXXMOVECONSTRUCTOR;
+ case SymbolSubKind::AccessorGetter:
+ return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORGETTER;
+ case SymbolSubKind::AccessorSetter:
+ return INDEXSTORE_SYMBOL_SUBKIND_ACCESSORSETTER;
+ case SymbolSubKind::UsingTypename:
+ return INDEXSTORE_SYMBOL_SUBKIND_USINGTYPENAME;
+ case SymbolSubKind::UsingValue:
+ return INDEXSTORE_SYMBOL_SUBKIND_USINGVALUE;
+ case SymbolSubKind::SwiftAccessorWillSet:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORWILLSET;
+ case SymbolSubKind::SwiftAccessorDidSet:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORDIDSET;
+ case SymbolSubKind::SwiftAccessorAddressor:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORADDRESSOR;
+ case SymbolSubKind::SwiftAccessorMutableAddressor:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTACCESSORMUTABLEADDRESSOR;
+ case SymbolSubKind::SwiftExtensionOfStruct:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFSTRUCT;
+ case SymbolSubKind::SwiftExtensionOfClass:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFCLASS;
+ case SymbolSubKind::SwiftExtensionOfEnum:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFENUM;
+ case SymbolSubKind::SwiftExtensionOfProtocol:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTEXTENSIONOFPROTOCOL;
+ case SymbolSubKind::SwiftPrefixOperator:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTPREFIXOPERATOR;
+ case SymbolSubKind::SwiftPostfixOperator:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTPOSTFIXOPERATOR;
+ case SymbolSubKind::SwiftInfixOperator:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTINFIXOPERATOR;
+ case SymbolSubKind::SwiftSubscript:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTSUBSCRIPT;
+ case SymbolSubKind::SwiftAssociatedType:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTASSOCIATEDTYPE;
+ case SymbolSubKind::SwiftGenericTypeParam:
+ return INDEXSTORE_SYMBOL_SUBKIND_SWIFTGENERICTYPEPARAM;
+ }
+ llvm_unreachable("unexpected symbol subkind");
+}
+
+/// Map a SymbolLanguage to a indexstore_symbol_language_t.
+indexstore_symbol_language_t index::getIndexStoreLang(SymbolLanguage L) {
+ switch (L) {
+ case SymbolLanguage::C:
+ return INDEXSTORE_SYMBOL_LANG_C;
+ case SymbolLanguage::ObjC:
+ return INDEXSTORE_SYMBOL_LANG_OBJC;
+ case SymbolLanguage::CXX:
+ return INDEXSTORE_SYMBOL_LANG_CXX;
+ case SymbolLanguage::Swift:
+ return INDEXSTORE_SYMBOL_LANG_SWIFT;
+ }
+ llvm_unreachable("unexpected symbol language");
+}
+
+/// Map a SymbolPropertySet to its indexstore representation.
+uint64_t index::getIndexStoreProperties(SymbolPropertySet Props) {
+ uint64_t storeProp = 0;
+ applyForEachSymbolProperty(Props, [&](SymbolProperty prop) {
+ switch (prop) {
+ case SymbolProperty::Generic:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GENERIC;
+ break;
+ case SymbolProperty::TemplatePartialSpecialization:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_PARTIAL_SPECIALIZATION;
+ break;
+ case SymbolProperty::TemplateSpecialization:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_TEMPLATE_SPECIALIZATION;
+ break;
+ case SymbolProperty::UnitTest:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_UNITTEST;
+ break;
+ case SymbolProperty::IBAnnotated:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBANNOTATED;
+ break;
+ case SymbolProperty::IBOutletCollection:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_IBOUTLETCOLLECTION;
+ break;
+ case SymbolProperty::GKInspectable:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_GKINSPECTABLE;
+ break;
+ case SymbolProperty::Local:
+ storeProp |= INDEXSTORE_SYMBOL_PROPERTY_LOCAL;
+ break;
+ }
+ });
+ return storeProp;
+}
+
+/// Map a SymbolRoleSet to its indexstore representation.
+uint64_t index::getIndexStoreRoles(SymbolRoleSet Roles) {
+ uint64_t storeRoles = 0;
+ applyForEachSymbolRole(Roles, [&](SymbolRole role) {
+ switch (role) {
+ case SymbolRole::Declaration:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_DECLARATION;
+ break;
+ case SymbolRole::Definition:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_DEFINITION;
+ break;
+ case SymbolRole::Reference:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REFERENCE;
+ break;
+ case SymbolRole::Read:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_READ;
+ break;
+ case SymbolRole::Write:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_WRITE;
+ break;
+ case SymbolRole::Call:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_CALL;
+ break;
+ case SymbolRole::Dynamic:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_DYNAMIC;
+ break;
+ case SymbolRole::AddressOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_ADDRESSOF;
+ break;
+ case SymbolRole::Implicit:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_IMPLICIT;
+ break;
+ case SymbolRole::RelationChildOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CHILDOF;
+ break;
+ case SymbolRole::RelationBaseOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_BASEOF;
+ break;
+ case SymbolRole::RelationOverrideOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_OVERRIDEOF;
+ break;
+ case SymbolRole::RelationReceivedBy:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_RECEIVEDBY;
+ break;
+ case SymbolRole::RelationCalledBy:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CALLEDBY;
+ break;
+ case SymbolRole::RelationExtendedBy:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_EXTENDEDBY;
+ break;
+ case SymbolRole::RelationAccessorOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_ACCESSOROF;
+ break;
+ case SymbolRole::RelationContainedBy:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_CONTAINEDBY;
+ break;
+ case SymbolRole::RelationIBTypeOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_IBTYPEOF;
+ break;
+ case SymbolRole::RelationSpecializationOf:
+ storeRoles |= INDEXSTORE_SYMBOL_ROLE_REL_SPECIALIZATIONOF;
+ break;
+ }
+ });
+ return storeRoles;
+}
diff --git a/lib/Index/IndexDataStoreUtils.h b/lib/Index/IndexDataStoreUtils.h
new file mode 100644
index 0000000..ad310e1
--- /dev/null
+++ b/lib/Index/IndexDataStoreUtils.h
@@ -0,0 +1,116 @@
+//===--- IndexDataStoreUtils.h - Functions/constants for the data store ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H
+#define LLVM_CLANG_LIB_INDEX_INDEXDATASTOREUTILS_H
+
+#include "llvm/Bitcode/BitCodes.h"
+#include "clang/Basic/LLVM.h"
+
+namespace llvm {
+ class BitstreamWriter;
+}
+
+namespace clang {
+namespace index {
+namespace store {
+
+static const unsigned STORE_FORMAT_VERSION = 5;
+
+void appendUnitSubDir(SmallVectorImpl<char> &StorePathBuf);
+void appendInteriorUnitPath(StringRef UnitName,
+ SmallVectorImpl<char> &PathBuf);
+void appendRecordSubDir(SmallVectorImpl<char> &StorePathBuf);
+void appendInteriorRecordPath(StringRef RecordName,
+ SmallVectorImpl<char> &PathBuf);
+
+enum RecordBitRecord {
+ REC_VERSION = 0,
+ REC_DECLINFO = 1,
+ REC_DECLOFFSETS = 2,
+ REC_DECLOCCURRENCE = 3,
+};
+
+enum RecordBitBlock {
+ REC_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
+ REC_DECLS_BLOCK_ID,
+ REC_DECLOFFSETS_BLOCK_ID,
+ REC_DECLOCCURRENCES_BLOCK_ID,
+};
+
+enum UnitBitRecord {
+ UNIT_VERSION = 0,
+ UNIT_INFO = 1,
+ UNIT_DEPENDENCY = 2,
+ UNIT_INCLUDE = 3,
+ UNIT_PATH = 4,
+ UNIT_PATH_BUFFER = 5,
+ UNIT_MODULE = 6,
+ UNIT_MODULE_BUFFER = 7,
+};
+
+enum UnitBitBlock {
+ UNIT_VERSION_BLOCK_ID = llvm::bitc::FIRST_APPLICATION_BLOCKID,
+ UNIT_INFO_BLOCK_ID,
+ UNIT_DEPENDENCIES_BLOCK_ID,
+ UNIT_INCLUDES_BLOCK_ID,
+ UNIT_PATHS_BLOCK_ID,
+ UNIT_MODULES_BLOCK_ID,
+};
+
+enum UnitDependencyKind {
+ UNIT_DEPEND_KIND_FILE = 0,
+ UNIT_DEPEND_KIND_RECORD = 1,
+ UNIT_DEPEND_KIND_UNIT = 2,
+};
+static const unsigned UnitDependencyKindBitNum = 2;
+
+enum UnitFilePathPrefixKind {
+ UNIT_PATH_PREFIX_NONE = 0,
+ UNIT_PATH_PREFIX_WORKDIR = 1,
+ UNIT_PATH_PREFIX_SYSROOT = 2,
+};
+static const unsigned UnitFilePathPrefixKindBitNum = 2;
+
+typedef SmallVector<uint64_t, 64> RecordData;
+typedef SmallVectorImpl<uint64_t> RecordDataImpl;
+
+struct BitPathComponent {
+ size_t Offset = 0;
+ size_t Size = 0;
+ BitPathComponent(size_t Offset, size_t Size) : Offset(Offset), Size(Size) {}
+ BitPathComponent() = default;
+};
+
+struct DirBitPath {
+ UnitFilePathPrefixKind PrefixKind = UNIT_PATH_PREFIX_NONE;
+ BitPathComponent Dir;
+ DirBitPath(UnitFilePathPrefixKind Kind,
+ BitPathComponent Dir) : PrefixKind(Kind), Dir(Dir) {}
+ DirBitPath() = default;
+};
+
+struct FileBitPath : DirBitPath {
+ BitPathComponent Filename;
+ FileBitPath(UnitFilePathPrefixKind Kind, BitPathComponent Dir,
+ BitPathComponent Filename) : DirBitPath(Kind, Dir), Filename(Filename) {}
+ FileBitPath() = default;
+};
+
+void emitBlockID(unsigned ID, const char *Name,
+ llvm::BitstreamWriter &Stream, RecordDataImpl &Record);
+
+void emitRecordID(unsigned ID, const char *Name,
+ llvm::BitstreamWriter &Stream, RecordDataImpl &Record);
+
+} // end namespace store
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/lib/Index/IndexDecl.cpp b/lib/Index/IndexDecl.cpp
index c5230c0..ee28ef7 100644
--- a/lib/Index/IndexDecl.cpp
+++ b/lib/Index/IndexDecl.cpp
@@ -267,6 +267,10 @@
TypeNameInfo->getTypeLoc().getLocStart(),
Dtor->getParent(), Dtor->getDeclContext());
}
+ } else if (const auto *Guide = dyn_cast<CXXDeductionGuideDecl>(D)) {
+ IndexCtx.handleReference(Guide->getDeducedTemplate()->getTemplatedDecl(),
+ Guide->getLocation(), Guide,
+ Guide->getDeclContext());
}
// Template specialization arguments.
if (const ASTTemplateArgumentListInfo *TemplateArgInfo =
@@ -607,6 +611,24 @@
SymbolRoleSet());
}
+ bool VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) {
+ TRY_DECL(D, IndexCtx.handleDecl(D));
+ const DeclContext *DC = D->getDeclContext()->getRedeclContext();
+ const NamedDecl *Parent = dyn_cast<NamedDecl>(DC);
+ IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent,
+ D->getLexicalDeclContext());
+ return true;
+ }
+
+ bool VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) {
+ TRY_DECL(D, IndexCtx.handleDecl(D));
+ const DeclContext *DC = D->getDeclContext()->getRedeclContext();
+ const NamedDecl *Parent = dyn_cast<NamedDecl>(DC);
+ IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent,
+ D->getLexicalDeclContext());
+ return true;
+ }
+
bool VisitClassTemplateSpecializationDecl(const
ClassTemplateSpecializationDecl *D) {
// FIXME: Notify subsequent callbacks if info comes from implicit
diff --git a/lib/Index/IndexRecordHasher.cpp b/lib/Index/IndexRecordHasher.cpp
new file mode 100644
index 0000000..1edf4c1
--- /dev/null
+++ b/lib/Index/IndexRecordHasher.cpp
@@ -0,0 +1,480 @@
+//===--- IndexRecordHasher.cpp - Index record hashing ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IndexRecordHasher.h"
+#include "FileIndexRecord.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclVisitor.h"
+#include "llvm/Support/Path.h"
+
+#define INITIAL_HASH 5381
+#define COMBINE_HASH(...) (Hash = hash_combine(Hash, __VA_ARGS__))
+
+using namespace clang;
+using namespace clang::index;
+using namespace llvm;
+
+static hash_code computeHash(const TemplateArgument &Arg,
+ IndexRecordHasher &Hasher);
+
+namespace {
+class DeclHashVisitor : public ConstDeclVisitor<DeclHashVisitor, hash_code> {
+ IndexRecordHasher &Hasher;
+
+public:
+ DeclHashVisitor(IndexRecordHasher &Hasher) : Hasher(Hasher) {}
+
+ hash_code VisitDecl(const Decl *D) {
+ return VisitDeclContext(D->getDeclContext());
+ }
+
+ hash_code VisitNamedDecl(const NamedDecl *D) {
+ hash_code Hash = VisitDecl(D);
+ if (auto *attr = D->getExternalSourceSymbolAttr()) {
+ COMBINE_HASH(hash_value(attr->getDefinedIn()));
+ }
+ return COMBINE_HASH(Hasher.hash(D->getDeclName()));
+ }
+
+ hash_code VisitTagDecl(const TagDecl *D) {
+ if (D->getDeclName().isEmpty()) {
+ if (const TypedefNameDecl *TD = D->getTypedefNameForAnonDecl())
+ return Visit(TD);
+
+ hash_code Hash = VisitDeclContext(D->getDeclContext());
+ if (D->isEmbeddedInDeclarator() && !D->isFreeStanding()) {
+ COMBINE_HASH(hashLoc(D->getLocation(), /*IncludeOffset=*/true));
+ } else
+ COMBINE_HASH('a');
+ return Hash;
+ }
+
+ hash_code Hash = VisitTypeDecl(D);
+ return COMBINE_HASH('T');
+ }
+
+ hash_code VisitClassTemplateSpecializationDecl(const ClassTemplateSpecializationDecl *D) {
+ hash_code Hash = VisitCXXRecordDecl(D);
+ const TemplateArgumentList &Args = D->getTemplateArgs();
+ COMBINE_HASH('>');
+ for (unsigned I = 0, N = Args.size(); I != N; ++I) {
+ COMBINE_HASH(computeHash(Args.get(I), Hasher));
+ }
+ return Hash;
+ }
+
+ hash_code VisitObjCContainerDecl(const ObjCContainerDecl *D) {
+ hash_code Hash = VisitNamedDecl(D);
+ return COMBINE_HASH('I');
+ }
+
+ hash_code VisitObjCImplDecl(const ObjCImplDecl *D) {
+ if (auto *ID = D->getClassInterface())
+ return VisitObjCInterfaceDecl(ID);
+ else
+ return 0;
+ }
+
+ hash_code VisitObjCCategoryDecl(const ObjCCategoryDecl *D) {
+ // FIXME: Differentiate between category and the interface ?
+ if (auto *ID = D->getClassInterface())
+ return VisitObjCInterfaceDecl(ID);
+ else
+ return 0;
+ }
+
+ hash_code VisitFunctionDecl(const FunctionDecl *D) {
+ hash_code Hash = VisitNamedDecl(D);
+ ASTContext &Ctx = Hasher.getASTContext();
+ if ((!Ctx.getLangOpts().CPlusPlus && !D->hasAttr<OverloadableAttr>())
+ || D->isExternC())
+ return Hash;
+
+ for (auto param : D->parameters()) {
+ COMBINE_HASH(Hasher.hash(param->getType()));
+ }
+ return Hash;
+ }
+
+ hash_code VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) {
+ hash_code Hash = VisitNamedDecl(D);
+ COMBINE_HASH(Hasher.hash(D->getQualifier()));
+ return Hash;
+ }
+
+ hash_code VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) {
+ hash_code Hash = VisitNamedDecl(D);
+ COMBINE_HASH(Hasher.hash(D->getQualifier()));
+ return Hash;
+ }
+
+ hash_code VisitDeclContext(const DeclContext *DC) {
+ // FIXME: Add location if this is anonymous namespace ?
+ DC = DC->getRedeclContext();
+ const Decl *D = cast<Decl>(DC)->getCanonicalDecl();
+ if (auto *ND = dyn_cast<NamedDecl>(D))
+ return Hasher.hash(ND);
+ else
+ return 0;
+ }
+
+ hash_code hashLoc(SourceLocation Loc, bool IncludeOffset) {
+ if (Loc.isInvalid()) {
+ return 0;
+ }
+ hash_code Hash = INITIAL_HASH;
+ const SourceManager &SM = Hasher.getASTContext().getSourceManager();
+ Loc = SM.getFileLoc(Loc);
+ const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(Loc);
+ const FileEntry *FE = SM.getFileEntryForID(Decomposed.first);
+ if (FE) {
+ COMBINE_HASH(llvm::sys::path::filename(FE->getName()));
+ } else {
+ // This case really isn't interesting.
+ return 0;
+ }
+ if (IncludeOffset) {
+ // Use the offest into the FileID to represent the location. Using
+ // a line/column can cause us to look back at the original source file,
+ // which is expensive.
+ COMBINE_HASH(Decomposed.second);
+ }
+ return Hash;
+ }
+};
+}
+
+hash_code IndexRecordHasher::hashRecord(const FileIndexRecord &Record) {
+ hash_code Hash = INITIAL_HASH;
+ for (auto &Info : Record.getDeclOccurrences()) {
+ COMBINE_HASH(Info.Roles, Info.Offset, hash(Info.Dcl));
+ for (auto &Rel : Info.Relations) {
+ COMBINE_HASH(hash(Rel.RelatedSymbol));
+ }
+ }
+ return Hash;
+}
+
+hash_code IndexRecordHasher::hash(const Decl *D) {
+ assert(D->isCanonicalDecl());
+
+ if (isa<TagDecl>(D) || isa<ObjCContainerDecl>(D)) {
+ return tryCache(D, D);
+ } else if (auto *NS = dyn_cast<NamespaceDecl>(D)) {
+ if (NS->isAnonymousNamespace())
+ return hash_value(StringRef("@aN"));
+ return tryCache(D, D);
+ } else {
+ // There's a balance between caching results and not growing the cache too
+ // much. Measurements showed that avoiding caching all decls is beneficial
+ // particularly when including all of Cocoa.
+ return hashImpl(D);
+ }
+}
+
+hash_code IndexRecordHasher::hash(QualType NonCanTy) {
+ CanQualType CanTy = Ctx.getCanonicalType(NonCanTy);
+ return hash(CanTy);
+}
+
+hash_code IndexRecordHasher::hash(CanQualType CT) {
+ // Do some hashing without going to the cache, for example we can avoid
+ // storing the hash for both the type and its const-qualified version.
+ hash_code Hash = INITIAL_HASH;
+
+ auto asCanon = [](QualType Ty) -> CanQualType {
+ return CanQualType::CreateUnsafe(Ty);
+ };
+
+ while (true) {
+ Qualifiers Q = CT.getQualifiers();
+ CT = CT.getUnqualifiedType();
+ const Type *T = CT.getTypePtr();
+ unsigned qVal = 0;
+ if (Q.hasConst())
+ qVal |= 0x1;
+ if (Q.hasVolatile())
+ qVal |= 0x2;
+ if (Q.hasRestrict())
+ qVal |= 0x4;
+ if(qVal)
+ COMBINE_HASH(qVal);
+
+ // Hash in ObjC GC qualifiers?
+
+ if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) {
+ return COMBINE_HASH(BT->getKind());
+ }
+ if (const PointerType *PT = dyn_cast<PointerType>(T)) {
+ COMBINE_HASH('*');
+ CT = asCanon(PT->getPointeeType());
+ continue;
+ }
+ if (const ReferenceType *RT = dyn_cast<ReferenceType>(T)) {
+ COMBINE_HASH('&');
+ CT = asCanon(RT->getPointeeType());
+ continue;
+ }
+ if (const BlockPointerType *BT = dyn_cast<BlockPointerType>(T)) {
+ COMBINE_HASH('B');
+ CT = asCanon(BT->getPointeeType());
+ continue;
+ }
+ if (const ObjCObjectPointerType *OPT = dyn_cast<ObjCObjectPointerType>(T)) {
+ COMBINE_HASH('*');
+ CT = asCanon(OPT->getPointeeType());
+ continue;
+ }
+ if (const TagType *TT = dyn_cast<TagType>(T)) {
+ return COMBINE_HASH('$', hash(TT->getDecl()->getCanonicalDecl()));
+ }
+ if (const ObjCInterfaceType *OIT = dyn_cast<ObjCInterfaceType>(T)) {
+ return COMBINE_HASH('$', hash(OIT->getDecl()->getCanonicalDecl()));
+ }
+ if (const ObjCObjectType *OIT = dyn_cast<ObjCObjectType>(T)) {
+ for (auto *Prot : OIT->getProtocols())
+ COMBINE_HASH(hash(Prot));
+ CT = asCanon(OIT->getBaseType());
+ continue;
+ }
+ if (const TemplateTypeParmType *TTP = dyn_cast<TemplateTypeParmType>(T)) {
+ return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex());
+ }
+ if (const InjectedClassNameType *InjT = dyn_cast<InjectedClassNameType>(T)) {
+ CT = asCanon(InjT->getInjectedSpecializationType().getCanonicalType());
+ continue;
+ }
+
+ break;
+ }
+
+ return COMBINE_HASH(tryCache(CT.getAsOpaquePtr(), CT));
+}
+
+hash_code IndexRecordHasher::hash(DeclarationName Name) {
+ assert(!Name.isEmpty());
+ // Measurements for using cache or not here, showed significant slowdown when
+ // using the cache for all DeclarationNames when parsing Cocoa, and minor
+ // improvement or no difference for a couple of C++ single translation unit
+ // files. So we avoid caching DeclarationNames.
+ return hashImpl(Name);
+}
+
+hash_code IndexRecordHasher::hash(const NestedNameSpecifier *NNS) {
+ assert(NNS);
+ // Measurements for the C++ single translation unit files did not show much
+ // difference here; choosing to cache them currently.
+ return tryCache(NNS, NNS);
+}
+
+template <typename T>
+hash_code IndexRecordHasher::tryCache(const void *Ptr, T Obj) {
+ auto It = HashByPtr.find(Ptr);
+ if (It != HashByPtr.end())
+ return It->second;
+
+ hash_code Hash = hashImpl(Obj);
+ // hashImpl() may call into tryCache recursively and mutate
+ // HashByPtr, so we use find() earlier and insert the hash with another
+ // lookup here instead of calling insert() earlier and utilizing the iterator
+ // that insert() returns.
+ HashByPtr[Ptr] = Hash;
+ return Hash;
+}
+
+hash_code IndexRecordHasher::hashImpl(const Decl *D) {
+ return DeclHashVisitor(*this).Visit(D);
+}
+
+static hash_code computeHash(const IdentifierInfo *II) {
+ return hash_value(II->getName());
+}
+
+static hash_code computeHash(Selector Sel) {
+ unsigned N = Sel.getNumArgs();
+ if (N == 0)
+ ++N;
+ hash_code Hash = INITIAL_HASH;
+ for (unsigned I = 0; I != N; ++I)
+ if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I))
+ COMBINE_HASH(computeHash(II));
+ return Hash;
+}
+
+static hash_code computeHash(TemplateName Name, IndexRecordHasher &Hasher) {
+ hash_code Hash = INITIAL_HASH;
+ if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
+ if (TemplateTemplateParmDecl *TTP
+ = dyn_cast<TemplateTemplateParmDecl>(Template)) {
+ return COMBINE_HASH('t', TTP->getDepth(), TTP->getIndex());
+ }
+
+ return COMBINE_HASH(Hasher.hash(Template->getCanonicalDecl()));
+ }
+
+ // FIXME: Hash dependent template names.
+ return Hash;
+}
+
+static hash_code computeHash(const TemplateArgument &Arg,
+ IndexRecordHasher &Hasher) {
+ hash_code Hash = INITIAL_HASH;
+
+ switch (Arg.getKind()) {
+ case TemplateArgument::Null:
+ break;
+
+ case TemplateArgument::Declaration:
+ COMBINE_HASH(Hasher.hash(Arg.getAsDecl()));
+ break;
+
+ case TemplateArgument::NullPtr:
+ break;
+
+ case TemplateArgument::TemplateExpansion:
+ COMBINE_HASH('P'); // pack expansion of...
+ // Fall through
+ case TemplateArgument::Template:
+ COMBINE_HASH(computeHash(Arg.getAsTemplateOrTemplatePattern(), Hasher));
+ break;
+
+ case TemplateArgument::Expression:
+ // FIXME: Hash expressions.
+ break;
+
+ case TemplateArgument::Pack:
+ COMBINE_HASH('p');
+ for (const auto &P : Arg.pack_elements())
+ COMBINE_HASH(computeHash(P, Hasher));
+ break;
+
+ case TemplateArgument::Type:
+ COMBINE_HASH(Hasher.hash(Arg.getAsType()));
+ break;
+
+ case TemplateArgument::Integral:
+ COMBINE_HASH('V', Hasher.hash(Arg.getIntegralType()), Arg.getAsIntegral());
+ break;
+ }
+
+ return Hash;
+}
+
+hash_code IndexRecordHasher::hashImpl(CanQualType CQT) {
+ hash_code Hash = INITIAL_HASH;
+
+ auto asCanon = [](QualType Ty) -> CanQualType {
+ return CanQualType::CreateUnsafe(Ty);
+ };
+
+ const Type *T = CQT.getTypePtr();
+
+ if (const PackExpansionType *Expansion = dyn_cast<PackExpansionType>(T)) {
+ return COMBINE_HASH('P', hash(asCanon(Expansion->getPattern())));
+ }
+ if (const RValueReferenceType *RT = dyn_cast<RValueReferenceType>(T)) {
+ return COMBINE_HASH('%', hash(asCanon(RT->getPointeeType())));
+ }
+ if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(T)) {
+ COMBINE_HASH('F', hash(asCanon(FT->getReturnType())));
+ for (const auto &I : FT->param_types())
+ COMBINE_HASH(hash(asCanon(I)));
+ return COMBINE_HASH(FT->isVariadic());
+ }
+ if (const ComplexType *CT = dyn_cast<ComplexType>(T)) {
+ return COMBINE_HASH('<', hash(asCanon(CT->getElementType())));
+ }
+ if (const TemplateSpecializationType *Spec
+ = dyn_cast<TemplateSpecializationType>(T)) {
+ COMBINE_HASH('>', computeHash(Spec->getTemplateName(), *this));
+ for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I)
+ COMBINE_HASH(computeHash(Spec->getArg(I), *this));
+ return Hash;
+ }
+ if (const DependentNameType *DNT = dyn_cast<DependentNameType>(T)) {
+ COMBINE_HASH('^');
+ if (const NestedNameSpecifier *NNS = DNT->getQualifier())
+ COMBINE_HASH(hash(NNS));
+ return COMBINE_HASH(computeHash(DNT->getIdentifier()));
+ }
+
+ // Unhandled type.
+ return Hash;
+}
+
+hash_code IndexRecordHasher::hashImpl(DeclarationName Name) {
+ hash_code Hash = INITIAL_HASH;
+ COMBINE_HASH(Name.getNameKind());
+
+ switch (Name.getNameKind()) {
+ case DeclarationName::Identifier:
+ COMBINE_HASH(computeHash(Name.getAsIdentifierInfo()));
+ break;
+ case DeclarationName::ObjCZeroArgSelector:
+ case DeclarationName::ObjCOneArgSelector:
+ case DeclarationName::ObjCMultiArgSelector:
+ COMBINE_HASH(computeHash(Name.getObjCSelector()));
+ break;
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::CXXDestructorName:
+ case DeclarationName::CXXConversionFunctionName:
+ break;
+ case DeclarationName::CXXOperatorName:
+ COMBINE_HASH(Name.getCXXOverloadedOperator());
+ break;
+ case DeclarationName::CXXLiteralOperatorName:
+ COMBINE_HASH(computeHash(Name.getCXXLiteralIdentifier()));
+ case DeclarationName::CXXUsingDirective:
+ break;
+ case DeclarationName::CXXDeductionGuideName:
+ COMBINE_HASH(computeHash(Name.getCXXDeductionGuideTemplate()
+ ->getDeclName().getAsIdentifierInfo()));
+ break;
+ }
+
+ return Hash;
+}
+
+hash_code IndexRecordHasher::hashImpl(const NestedNameSpecifier *NNS) {
+ hash_code Hash = INITIAL_HASH;
+ if (auto *Pre = NNS->getPrefix())
+ COMBINE_HASH(hash(Pre));
+
+ COMBINE_HASH(NNS->getKind());
+
+ switch (NNS->getKind()) {
+ case NestedNameSpecifier::Identifier:
+ COMBINE_HASH(computeHash(NNS->getAsIdentifier()));
+ break;
+
+ case NestedNameSpecifier::Namespace:
+ COMBINE_HASH(hash(NNS->getAsNamespace()->getCanonicalDecl()));
+ break;
+
+ case NestedNameSpecifier::NamespaceAlias:
+ COMBINE_HASH(hash(NNS->getAsNamespaceAlias()->getCanonicalDecl()));
+ break;
+
+ case NestedNameSpecifier::Global:
+ break;
+
+ case NestedNameSpecifier::Super:
+ break;
+
+ case NestedNameSpecifier::TypeSpecWithTemplate:
+ // Fall through to hash the type.
+
+ case NestedNameSpecifier::TypeSpec:
+ COMBINE_HASH(hash(QualType(NNS->getAsType(), 0)));
+ break;
+ }
+
+ return Hash;
+}
diff --git a/lib/Index/IndexRecordHasher.h b/lib/Index/IndexRecordHasher.h
new file mode 100644
index 0000000..af3accc
--- /dev/null
+++ b/lib/Index/IndexRecordHasher.h
@@ -0,0 +1,58 @@
+//===--- IndexRecordHasher.h - Index record hashing -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H
+#define LLVM_CLANG_LIB_INDEX_INDEXRECORDHASHER_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+
+namespace clang {
+ class ASTContext;
+ class Decl;
+ class DeclarationName;
+ class NestedNameSpecifier;
+ class QualType;
+ class Type;
+ template <typename> class CanQual;
+ typedef CanQual<Type> CanQualType;
+
+namespace index {
+ class FileIndexRecord;
+
+class IndexRecordHasher {
+ ASTContext &Ctx;
+ llvm::DenseMap<const void *, llvm::hash_code> HashByPtr;
+
+public:
+ explicit IndexRecordHasher(ASTContext &Ctx) : Ctx(Ctx) {}
+ ASTContext &getASTContext() { return Ctx; }
+
+ llvm::hash_code hashRecord(const FileIndexRecord &Record);
+ llvm::hash_code hash(const Decl *D);
+ llvm::hash_code hash(QualType Ty);
+ llvm::hash_code hash(CanQualType Ty);
+ llvm::hash_code hash(DeclarationName Name);
+ llvm::hash_code hash(const NestedNameSpecifier *NNS);
+
+private:
+ template <typename T>
+ llvm::hash_code tryCache(const void *Ptr, T Obj);
+
+ llvm::hash_code hashImpl(const Decl *D);
+ llvm::hash_code hashImpl(CanQualType Ty);
+ llvm::hash_code hashImpl(DeclarationName Name);
+ llvm::hash_code hashImpl(const NestedNameSpecifier *NNS);
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/lib/Index/IndexRecordReader.cpp b/lib/Index/IndexRecordReader.cpp
new file mode 100644
index 0000000..bd2ec1f
--- /dev/null
+++ b/lib/Index/IndexRecordReader.cpp
@@ -0,0 +1,407 @@
+//===--- IndexRecordReader.cpp - Index record deserialization -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/IndexRecordReader.h"
+#include "IndexDataStoreUtils.h"
+#include "BitstreamVisitor.h"
+#include "clang/Index/IndexDataStoreSymbolUtils.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::index;
+using namespace clang::index::store;
+using namespace llvm;
+
+struct IndexRecordReader::Implementation {
+ BumpPtrAllocator Allocator;
+ std::unique_ptr<MemoryBuffer> Buffer;
+ llvm::BitstreamCursor DeclCursor;
+ llvm::BitstreamCursor OccurCursor;
+ ArrayRef<uint32_t> DeclOffsets;
+ const IndexRecordDecl **Decls;
+
+ void setDeclOffsets(ArrayRef<uint32_t> Offs) {
+ DeclOffsets = Offs;
+ Decls = Allocator.Allocate<const IndexRecordDecl*>(Offs.size());
+ memset(Decls, 0, sizeof(IndexRecordDecl*)*Offs.size());
+ }
+
+ unsigned getNumDecls() const { return DeclOffsets.size(); }
+
+ const IndexRecordDecl *getDeclByID(unsigned DeclID) {
+ if (DeclID == 0)
+ return nullptr;
+ return getDecl(DeclID-1);
+ }
+
+ const IndexRecordDecl *getDecl(unsigned Index) {
+ assert(Index < getNumDecls());
+ if (const IndexRecordDecl *D = Decls[Index])
+ return D;
+
+ IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>();
+ readDecl(Index, *D);
+ Decls[Index] = D;
+ return D;
+ }
+
+ /// Goes through the decls and populates a vector of record decls, based on
+ /// what the given function returns.
+ ///
+ /// The advantage of this function is to allocate memory only for the record
+ /// decls that the caller is interested in.
+ bool searchDecls(llvm::function_ref<DeclSearchCheck> Checker,
+ llvm::function_ref<void(const IndexRecordDecl *)> Receiver) {
+ for (unsigned I = 0, E = getNumDecls(); I != E; ++I) {
+ if (const IndexRecordDecl *D = Decls[I]) {
+ DeclSearchReturn Ret = Checker(*D);
+ if (Ret.AcceptDecl)
+ Receiver(D);
+ if (!Ret.ContinueSearch)
+ return false;
+ continue;
+ }
+
+ IndexRecordDecl LocalD;
+ readDecl(I, LocalD);
+ DeclSearchReturn Ret = Checker(LocalD);
+ if (Ret.AcceptDecl) {
+ IndexRecordDecl *D = Allocator.Allocate<IndexRecordDecl>();
+ *D = LocalD;
+ Decls[I] = D;
+ Receiver(D);
+ }
+ if (!Ret.ContinueSearch)
+ return false;
+ }
+ return true;
+ }
+
+ void readDecl(unsigned Index, IndexRecordDecl &RecD) {
+ RecordData Record;
+ StringRef Blob;
+ DeclCursor.JumpToBit(DeclOffsets[Index]);
+ unsigned Code = DeclCursor.ReadCode();
+ unsigned RecID = DeclCursor.readRecord(Code, Record, &Blob);
+ assert(RecID == REC_DECLINFO);
+ (void)RecID;
+
+ unsigned I = 0;
+ RecD.DeclID = Index+1;
+ RecD.SymInfo.Kind = getSymbolKind((indexstore_symbol_kind_t)read(Record, I));
+ RecD.SymInfo.SubKind = getSymbolSubKind((indexstore_symbol_subkind_t)read(Record, I));
+ RecD.SymInfo.Lang =
+ getSymbolLanguage((indexstore_symbol_language_t)read(Record, I));
+ RecD.SymInfo.Properties = getSymbolProperties(read(Record, I));
+ RecD.Roles = getSymbolRoles(read(Record, I));
+ RecD.RelatedRoles = getSymbolRoles(read(Record, I));
+ size_t NameLen = read(Record, I);
+ size_t USRLen = read(Record, I);
+ RecD.Name = Blob.substr(0, NameLen);
+ RecD.USR = Blob.substr(NameLen, USRLen);
+ RecD.CodeGenName = Blob.substr(NameLen+USRLen);
+ }
+
+ /// Reads occurrence data.
+ /// \param DeclsFilter if non-empty indicates the list of decls that we want
+ /// to get occurrences for. If empty then indicates that we want occurrences
+ /// for all decls.
+ /// \param RelatedDeclsFilter Same as \c DeclsFilter but for related decls.
+ /// \returns true if the occurrence info was filled out, false if occurrence
+ /// was ignored.
+ bool readOccurrence(RecordDataImpl &Record, StringRef Blob,
+ ArrayRef<const IndexRecordDecl *> DeclsFilter,
+ ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
+ IndexRecordOccurrence &RecOccur) {
+
+ auto isDeclIDContained = [](unsigned DeclID,
+ ArrayRef<const IndexRecordDecl *> Ds) -> bool {
+ if (Ds.empty())
+ return true; // empty means accept all.
+ auto pred = [DeclID](const IndexRecordDecl *D) { return D->DeclID == DeclID; };
+ return std::find_if(Ds.begin(), Ds.end(), pred) != Ds.end();
+ };
+
+ unsigned I = 0;
+ unsigned DeclID = read(Record, I);
+ if (!isDeclIDContained(DeclID, DeclsFilter))
+ return false;
+
+ if (!RelatedDeclsFilter.empty()) {
+ unsigned RelI = I+3;
+ unsigned NumRelated = read(Record, RelI);
+ bool FoundRelated = false;
+ while (NumRelated--) {
+ ++RelI; // roles;
+ unsigned RelDID = read(Record, RelI);
+ if (isDeclIDContained(RelDID, RelatedDeclsFilter)) {
+ FoundRelated = true;
+ break;
+ }
+ }
+ if (!FoundRelated)
+ return false;
+ }
+
+ RecOccur.Dcl = getDeclByID(DeclID);
+ RecOccur.Roles = getSymbolRoles(read(Record, I));
+ RecOccur.Line = read(Record, I);
+ RecOccur.Column = read(Record, I);
+
+ unsigned NumRelated = read(Record, I);
+ while (NumRelated--) {
+ SymbolRoleSet RelRoles = getSymbolRoles(read(Record, I));
+ const IndexRecordDecl *RelD = getDeclByID(read(Record, I));
+ RecOccur.Relations.emplace_back(RelRoles, RelD);
+ }
+
+ return true;
+ }
+
+ bool foreachDecl(bool NoCache,
+ function_ref<bool(const IndexRecordDecl *)> Receiver) {
+ for (unsigned I = 0, E = getNumDecls(); I != E; ++I) {
+ if (const IndexRecordDecl *D = Decls[I]) {
+ if (!Receiver(D))
+ return false;
+ continue;
+ }
+
+ if (NoCache) {
+ IndexRecordDecl LocalD;
+ readDecl(I, LocalD);
+ if (!Receiver(&LocalD))
+ return false;
+ } else {
+ if (!Receiver(getDecl(I)))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool foreachOccurrence(ArrayRef<const IndexRecordDecl *> DeclsFilter,
+ ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
+ function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
+ class OccurBitVisitor : public BitstreamVisitor<OccurBitVisitor> {
+ IndexRecordReader::Implementation &Reader;
+ ArrayRef<const IndexRecordDecl *> DeclsFilter;
+ ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter;
+ function_ref<bool(const IndexRecordOccurrence &)> Receiver;
+
+ public:
+ OccurBitVisitor(llvm::BitstreamCursor &Stream,
+ IndexRecordReader::Implementation &Reader,
+ ArrayRef<const IndexRecordDecl *> DeclsFilter,
+ ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
+ function_ref<bool(const IndexRecordOccurrence &)> Receiver)
+ : BitstreamVisitor(Stream),
+ Reader(Reader),
+ DeclsFilter(DeclsFilter),
+ RelatedDeclsFilter(RelatedDeclsFilter),
+ Receiver(std::move(Receiver)) {}
+
+ StreamVisit visitRecord(unsigned BlockID, unsigned RecID,
+ RecordDataImpl &Record, StringRef Blob) {
+ assert(RecID == REC_DECLOCCURRENCE);
+ IndexRecordOccurrence RecOccur;
+ if (Reader.readOccurrence(Record, Blob, DeclsFilter, RelatedDeclsFilter,
+ RecOccur))
+ if (!Receiver(RecOccur))
+ return StreamVisit::Abort;
+ return StreamVisit::Continue;
+ }
+ };
+
+ SavedStreamPosition SavedPosition(OccurCursor);
+ OccurBitVisitor Visitor(OccurCursor, *this, DeclsFilter, RelatedDeclsFilter,
+ Receiver);
+ std::string Error;
+ return Visitor.visit(Error);
+ }
+
+ bool foreachOccurrenceInLineRange(unsigned lineStart, unsigned lineCount,
+ llvm::function_ref<bool(const IndexRecordOccurrence &)> receiver) {
+ // FIXME: Use binary search and make this more efficient.
+ unsigned lineEnd = lineStart+lineCount;
+ return foreachOccurrence(None, None, [&](const IndexRecordOccurrence &occur) -> bool {
+ if (occur.Line > lineEnd)
+ return false; // we're done.
+ if (occur.Line >= lineStart) {
+ if (!receiver(occur))
+ return false;
+ }
+ return true;
+ });
+ }
+
+ static uint64_t read(RecordDataImpl &Record, unsigned &I) {
+ return Record[I++];
+ }
+};
+
+namespace {
+
+class IndexBitstreamVisitor : public BitstreamVisitor<IndexBitstreamVisitor> {
+ IndexRecordReader::Implementation &Reader;
+
+public:
+ IndexBitstreamVisitor(llvm::BitstreamCursor &Stream,
+ IndexRecordReader::Implementation &Reader)
+ : BitstreamVisitor(Stream), Reader(Reader) {}
+
+ StreamVisit visitBlock(unsigned ID) {
+ switch ((RecordBitBlock)ID) {
+ case REC_VERSION_BLOCK_ID:
+ case REC_DECLOFFSETS_BLOCK_ID:
+ return StreamVisit::Continue;
+
+ case REC_DECLS_BLOCK_ID:
+ Reader.DeclCursor = Stream;
+ if (Reader.DeclCursor.EnterSubBlock(ID)) {
+ *Error = "malformed block record";
+ return StreamVisit::Abort;
+ }
+ readBlockAbbrevs(Reader.DeclCursor);
+ return StreamVisit::Skip;
+
+ case REC_DECLOCCURRENCES_BLOCK_ID:
+ Reader.OccurCursor = Stream;
+ if (Reader.OccurCursor.EnterSubBlock(ID)) {
+ *Error = "malformed block record";
+ return StreamVisit::Abort;
+ }
+ readBlockAbbrevs(Reader.OccurCursor);
+ return StreamVisit::Skip;
+ }
+
+ // Some newly introduced block in a minor version update that we cannot
+ // handle.
+ return StreamVisit::Skip;
+ }
+
+ StreamVisit visitRecord(unsigned BlockID, unsigned RecID,
+ RecordDataImpl &Record, StringRef Blob) {
+ switch (BlockID) {
+ case REC_VERSION_BLOCK_ID: {
+ unsigned StoreFormatVersion = Record[0];
+ if (StoreFormatVersion != STORE_FORMAT_VERSION) {
+ llvm::raw_string_ostream OS(*Error);
+ OS << "Store format version mismatch: " << StoreFormatVersion;
+ OS << " , expected: " << STORE_FORMAT_VERSION;
+ return StreamVisit::Abort;
+ }
+ break;
+ }
+ case REC_DECLOFFSETS_BLOCK_ID:
+ assert(RecID == REC_DECLOFFSETS);
+ Reader.setDeclOffsets(makeArrayRef((uint32_t*)Blob.data(), Record[0]));
+ break;
+
+ case REC_DECLS_BLOCK_ID:
+ case REC_DECLOCCURRENCES_BLOCK_ID:
+ llvm_unreachable("shouldn't visit this block'");
+ }
+ return StreamVisit::Continue;
+ }
+};
+
+} // anonymous namespace
+
+std::unique_ptr<IndexRecordReader>
+IndexRecordReader::createWithRecordFilename(StringRef RecordFilename,
+ StringRef StorePath,
+ std::string &Error) {
+ SmallString<128> PathBuf = StorePath;
+ appendRecordSubDir(PathBuf);
+ appendInteriorRecordPath(RecordFilename, PathBuf);
+ return createWithFilePath(PathBuf.str(), Error);
+}
+
+std::unique_ptr<IndexRecordReader>
+IndexRecordReader::createWithFilePath(StringRef FilePath, std::string &Error) {
+ auto ErrOrBuf = MemoryBuffer::getFile(FilePath, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+ if (!ErrOrBuf) {
+ raw_string_ostream(Error) << "failed opening index record '"
+ << FilePath << "': " << ErrOrBuf.getError().message();
+ return nullptr;
+ }
+ return createWithBuffer(std::move(*ErrOrBuf), Error);
+}
+
+std::unique_ptr<IndexRecordReader>
+IndexRecordReader::createWithBuffer(std::unique_ptr<llvm::MemoryBuffer> Buffer,
+ std::string &Error) {
+
+ std::unique_ptr<IndexRecordReader> Reader;
+ Reader.reset(new IndexRecordReader());
+ auto &Impl = Reader->Impl;
+ Impl.Buffer = std::move(Buffer);
+ llvm::BitstreamCursor Stream(*Impl.Buffer);
+
+ // Sniff for the signature.
+ if (Stream.Read(8) != 'I' ||
+ Stream.Read(8) != 'D' ||
+ Stream.Read(8) != 'X' ||
+ Stream.Read(8) != 'R') {
+ Error = "not a serialized index record file";
+ return nullptr;
+ }
+
+ IndexBitstreamVisitor BitVisitor(Stream, Impl);
+ if (!BitVisitor.visit(Error))
+ return nullptr;
+
+ return Reader;
+}
+
+IndexRecordReader::IndexRecordReader()
+ : Impl(*new Implementation()) {
+
+}
+
+IndexRecordReader::~IndexRecordReader() {
+ delete &Impl;
+}
+
+bool IndexRecordReader::searchDecls(
+ llvm::function_ref<DeclSearchCheck> Checker,
+ llvm::function_ref<void(const IndexRecordDecl *)> Receiver) {
+ return Impl.searchDecls(std::move(Checker), std::move(Receiver));
+}
+
+bool IndexRecordReader::foreachDecl(bool NoCache,
+ function_ref<bool(const IndexRecordDecl *)> Receiver) {
+ return Impl.foreachDecl(NoCache, std::move(Receiver));
+}
+
+bool IndexRecordReader::foreachOccurrence(
+ ArrayRef<const IndexRecordDecl *> DeclsFilter,
+ ArrayRef<const IndexRecordDecl *> RelatedDeclsFilter,
+ function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
+ return Impl.foreachOccurrence(DeclsFilter, RelatedDeclsFilter,
+ std::move(Receiver));
+}
+
+bool IndexRecordReader::foreachOccurrence(
+ llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
+ return foreachOccurrence(None, None, std::move(Receiver));
+}
+
+bool IndexRecordReader::foreachOccurrenceInLineRange(unsigned lineStart,
+ unsigned lineCount,
+ llvm::function_ref<bool(const IndexRecordOccurrence &)> Receiver) {
+ return Impl.foreachOccurrenceInLineRange(lineStart, lineCount, Receiver);
+}
diff --git a/lib/Index/IndexRecordWriter.cpp b/lib/Index/IndexRecordWriter.cpp
new file mode 100644
index 0000000..c4e6d50
--- /dev/null
+++ b/lib/Index/IndexRecordWriter.cpp
@@ -0,0 +1,366 @@
+//===--- IndexRecordWriter.cpp - Index record serialization ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/IndexRecordWriter.h"
+#include "IndexDataStoreUtils.h"
+#include "indexstore/indexstore.h"
+#include "clang/Index/IndexDataStoreSymbolUtils.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::index;
+using namespace clang::index::store;
+using namespace llvm;
+
+using writer::OpaqueDecl;
+
+namespace {
+struct DeclInfo {
+ OpaqueDecl D;
+ SymbolRoleSet Roles;
+ SymbolRoleSet RelatedRoles;
+};
+
+struct OccurrenceInfo {
+ unsigned DeclID;
+ OpaqueDecl D;
+ SymbolRoleSet Roles;
+ unsigned Line;
+ unsigned Column;
+ SmallVector<std::pair<writer::SymbolRelation, unsigned>, 4> Related;
+};
+
+struct RecordState {
+ std::string RecordPath;
+ SmallString<512> Buffer;
+ BitstreamWriter Stream;
+
+ DenseMap<OpaqueDecl, unsigned> IndexForDecl;
+ std::vector<DeclInfo> Decls;
+ std::vector<OccurrenceInfo> Occurrences;
+
+ RecordState(std::string &&RecordPath)
+ : RecordPath(std::move(RecordPath)), Stream(Buffer) {}
+};
+} // end anonymous namespace
+
+static void writeBlockInfo(BitstreamWriter &Stream) {
+ RecordData Record;
+
+ Stream.EnterBlockInfoBlock();
+#define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record)
+#define RECORD(X) emitRecordID(X, #X, Stream, Record)
+
+ BLOCK(REC_VERSION_BLOCK);
+ RECORD(REC_VERSION);
+
+ BLOCK(REC_DECLS_BLOCK);
+ RECORD(REC_DECLINFO);
+
+ BLOCK(REC_DECLOFFSETS_BLOCK);
+ RECORD(REC_DECLOFFSETS);
+
+ BLOCK(REC_DECLOCCURRENCES_BLOCK);
+ RECORD(REC_DECLOCCURRENCE);
+
+#undef RECORD
+#undef BLOCK
+ Stream.ExitBlock();
+}
+
+static void writeVersionInfo(BitstreamWriter &Stream) {
+ using namespace llvm::sys;
+
+ Stream.EnterSubblock(REC_VERSION_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(REC_VERSION));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ RecordData Record;
+ Record.push_back(REC_VERSION);
+ Record.push_back(STORE_FORMAT_VERSION);
+ Stream.EmitRecordWithAbbrev(AbbrevCode, Record);
+
+ Stream.ExitBlock();
+}
+
+template <typename T, typename Allocator>
+static StringRef data(const std::vector<T, Allocator> &v) {
+ if (v.empty())
+ return StringRef();
+ return StringRef(reinterpret_cast<const char *>(&v[0]), sizeof(T) * v.size());
+}
+
+template <typename T> static StringRef data(const SmallVectorImpl<T> &v) {
+ return StringRef(reinterpret_cast<const char *>(v.data()),
+ sizeof(T) * v.size());
+}
+
+static void writeDecls(BitstreamWriter &Stream, ArrayRef<DeclInfo> Decls,
+ ArrayRef<OccurrenceInfo> Occurrences,
+ writer::SymbolWriterCallback GetSymbolForDecl) {
+ SmallVector<uint32_t, 32> DeclOffsets;
+ DeclOffsets.reserve(Decls.size());
+
+ //===--------------------------------------------------------------------===//
+ // DECLS_BLOCK_ID
+ //===--------------------------------------------------------------------===//
+
+ Stream.EnterSubblock(REC_DECLS_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(REC_DECLINFO));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Kind
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // SubKind
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Language
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolPropertyBitNum)); // Properties
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Roles
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Related Roles
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Length of name in block
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Length of USR in block
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name + USR + CodeGen symbol name
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+#ifndef NDEBUG
+ StringSet<> USRSet;
+#endif
+
+ RecordData Record;
+ llvm::SmallString<256> Blob;
+ llvm::SmallString<256> Scratch;
+ for (auto &Info : Decls) {
+ DeclOffsets.push_back(Stream.GetCurrentBitNo());
+ Blob.clear();
+ Scratch.clear();
+
+ writer::Symbol SymInfo = GetSymbolForDecl(Info.D, Scratch);
+ assert(SymInfo.SymInfo.Kind != SymbolKind::Unknown);
+ assert(!SymInfo.USR.empty() && "Recorded decl without USR!");
+
+ Blob += SymInfo.Name;
+ Blob += SymInfo.USR;
+ Blob += SymInfo.CodeGenName;
+
+#ifndef NDEBUG
+ bool IsNew = USRSet.insert(SymInfo.USR).second;
+ if (!IsNew) {
+ llvm::errs() << "Index: Duplicate USR! " << SymInfo.USR << "\n";
+ // FIXME: print more information so it's easier to find the declaration.
+ }
+#endif
+
+ Record.clear();
+ Record.push_back(REC_DECLINFO);
+ Record.push_back(getIndexStoreKind(SymInfo.SymInfo.Kind));
+ Record.push_back(getIndexStoreSubKind(SymInfo.SymInfo.SubKind));
+ Record.push_back(getIndexStoreLang(SymInfo.SymInfo.Lang));
+ Record.push_back(getIndexStoreProperties(SymInfo.SymInfo.Properties));
+ Record.push_back(getIndexStoreRoles(Info.Roles));
+ Record.push_back(getIndexStoreRoles(Info.RelatedRoles));
+ Record.push_back(SymInfo.Name.size());
+ Record.push_back(SymInfo.USR.size());
+ Stream.EmitRecordWithBlob(AbbrevCode, Record, Blob);
+ }
+
+ Stream.ExitBlock();
+
+ //===--------------------------------------------------------------------===//
+ // DECLOFFSETS_BLOCK_ID
+ //===--------------------------------------------------------------------===//
+
+ Stream.EnterSubblock(REC_DECLOFFSETS_BLOCK_ID, 3);
+
+ Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(REC_DECLOFFSETS));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Number of Decls
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Offsets array
+ AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ Record.clear();
+ Record.push_back(REC_DECLOFFSETS);
+ Record.push_back(DeclOffsets.size());
+ Stream.EmitRecordWithBlob(AbbrevCode, Record, data(DeclOffsets));
+
+ Stream.ExitBlock();
+
+ //===--------------------------------------------------------------------===//
+ // DECLOCCURRENCES_BLOCK_ID
+ //===--------------------------------------------------------------------===//
+
+ Stream.EnterSubblock(REC_DECLOCCURRENCES_BLOCK_ID, 3);
+
+ Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(REC_DECLOCCURRENCE));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Decl ID
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, SymbolRoleBitNum)); // Roles
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // Line
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Column
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // Num related
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); // Related Roles/IDs
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // Roles or ID
+ AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ for (auto &Occur : Occurrences) {
+ Record.clear();
+ Record.push_back(REC_DECLOCCURRENCE);
+ Record.push_back(Occur.DeclID);
+ Record.push_back(getIndexStoreRoles(Occur.Roles));
+ Record.push_back(Occur.Line);
+ Record.push_back(Occur.Column);
+ Record.push_back(Occur.Related.size());
+ for (auto &Rel : Occur.Related) {
+ Record.push_back(getIndexStoreRoles(Rel.first.Roles));
+ Record.push_back(Rel.second);
+ }
+ Stream.EmitRecordWithAbbrev(AbbrevCode, Record);
+ }
+ Stream.ExitBlock();
+}
+
+IndexRecordWriter::IndexRecordWriter(StringRef IndexPath)
+ : RecordsPath(IndexPath) {
+ store::appendRecordSubDir(RecordsPath);
+}
+
+IndexRecordWriter::Result
+IndexRecordWriter::beginRecord(StringRef Filename, hash_code RecordHash,
+ std::string &Error, std::string *OutRecordFile) {
+ using namespace llvm::sys;
+ assert(!Record && "called beginRecord before calling endRecord on previous");
+
+ std::string RecordName;
+ {
+ llvm::raw_string_ostream RN(RecordName);
+ RN << path::filename(Filename);
+ RN << "-" << APInt(64, RecordHash).toString(36, /*Signed=*/false);
+ }
+ SmallString<256> RecordPath = RecordsPath.str();
+ appendInteriorRecordPath(RecordName, RecordPath);
+
+ if (OutRecordFile)
+ *OutRecordFile = RecordName;
+
+ if (std::error_code EC =
+ fs::access(RecordPath.c_str(), fs::AccessMode::Exist)) {
+ if (EC != errc::no_such_file_or_directory) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "could not access record '" << RecordPath
+ << "': " << EC.message();
+ return Result::Failure;
+ }
+ } else {
+ return Result::AlreadyExists;
+ }
+
+ // Write the record header.
+ auto *State = new RecordState(RecordPath.str());
+ Record = State;
+ llvm::BitstreamWriter &Stream = State->Stream;
+ Stream.Emit('I', 8);
+ Stream.Emit('D', 8);
+ Stream.Emit('X', 8);
+ Stream.Emit('R', 8);
+
+ writeBlockInfo(Stream);
+ writeVersionInfo(Stream);
+
+ return Result::Success;
+}
+
+IndexRecordWriter::Result
+IndexRecordWriter::endRecord(std::string &Error,
+ writer::SymbolWriterCallback GetSymbolForDecl) {
+ assert(Record && "called endRecord without calling beginRecord");
+ auto &State = *static_cast<RecordState *>(Record);
+ Record = nullptr;
+ struct ScopedDelete {
+ RecordState *S;
+ ScopedDelete(RecordState *S) : S(S) {}
+ ~ScopedDelete() { delete S; }
+ } Deleter(&State);
+
+ if (!State.Decls.empty()) {
+ writeDecls(State.Stream, State.Decls, State.Occurrences, GetSymbolForDecl);
+ }
+
+ if (std::error_code EC = sys::fs::create_directory(sys::path::parent_path(State.RecordPath))) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to create directory '" << sys::path::parent_path(State.RecordPath) << "': " << EC.message();
+ return Result::Failure;
+ }
+
+ // Create a unique file to write to so that we can move the result into place
+ // atomically. If this process crashes we don't want to interfere with any
+ // other concurrent processes.
+ SmallString<128> TempPath(State.RecordPath);
+ TempPath += "-temp-%%%%%%%%";
+ int TempFD;
+ if (sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to create temporary file: " << TempPath;
+ return Result::Failure;
+ }
+
+ raw_fd_ostream OS(TempFD, /*shouldClose=*/true);
+ OS.write(State.Buffer.data(), State.Buffer.size());
+ OS.close();
+
+ // Atomically move the unique file into place.
+ if (std::error_code EC =
+ sys::fs::rename(TempPath.c_str(), State.RecordPath.c_str())) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to rename '" << TempPath << "' to '" << State.RecordPath << "': " << EC.message();
+ return Result::Failure;
+ }
+
+ return Result::Success;
+}
+
+void IndexRecordWriter::addOccurrence(
+ OpaqueDecl D, SymbolRoleSet Roles, unsigned Line, unsigned Column,
+ ArrayRef<writer::SymbolRelation> Related) {
+ assert(Record && "called addOccurrence without calling beginRecord");
+ auto &State = *static_cast<RecordState *>(Record);
+
+ auto insertDecl = [&](OpaqueDecl D, SymbolRoleSet Roles,
+ SymbolRoleSet RelatedRoles) -> unsigned {
+ auto Insert =
+ State.IndexForDecl.insert(std::make_pair(D, State.Decls.size()));
+ unsigned Index = Insert.first->second;
+
+ if (Insert.second) {
+ State.Decls.push_back(DeclInfo{D, Roles, RelatedRoles});
+ } else {
+ State.Decls[Index].Roles |= Roles;
+ State.Decls[Index].RelatedRoles |= RelatedRoles;
+ }
+ return Index + 1;
+ };
+
+ unsigned DeclID = insertDecl(D, Roles, SymbolRoleSet());
+
+ decltype(OccurrenceInfo::Related) RelatedDecls;
+ RelatedDecls.reserve(Related.size());
+ for (auto &Rel : Related) {
+ unsigned ID = insertDecl(Rel.RelatedSymbol, SymbolRoleSet(), Rel.Roles);
+ RelatedDecls.emplace_back(Rel, ID);
+ }
+
+ State.Occurrences.push_back(
+ OccurrenceInfo{DeclID, D, Roles, Line, Column, std::move(RelatedDecls)});
+}
diff --git a/lib/Index/IndexSymbol.cpp b/lib/Index/IndexSymbol.cpp
index 0dc3720..1f8410d 100644
--- a/lib/Index/IndexSymbol.cpp
+++ b/lib/Index/IndexSymbol.cpp
@@ -201,25 +201,22 @@
Info.Properties |= (unsigned)SymbolProperty::UnitTest;
break;
}
- case Decl::ObjCMethod:
- if (cast<ObjCMethodDecl>(D)->isInstanceMethod()) {
- const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D);
- Info.Kind = SymbolKind::InstanceMethod;
- if (MD->isPropertyAccessor()) {
- if (MD->param_size())
- Info.SubKind = SymbolSubKind::AccessorSetter;
- else
- Info.SubKind = SymbolSubKind::AccessorGetter;
- }
- } else {
- Info.Kind = SymbolKind::ClassMethod;
+ case Decl::ObjCMethod: {
+ const ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D);
+ Info.Kind = MD->isInstanceMethod() ? SymbolKind::InstanceMethod : SymbolKind::ClassMethod;
+ if (MD->isPropertyAccessor()) {
+ if (MD->param_size())
+ Info.SubKind = SymbolSubKind::AccessorSetter;
+ else
+ Info.SubKind = SymbolSubKind::AccessorGetter;
}
Info.Lang = SymbolLanguage::ObjC;
- if (isUnitTest(cast<ObjCMethodDecl>(D)))
+ if (isUnitTest(MD))
Info.Properties |= (unsigned)SymbolProperty::UnitTest;
if (D->hasAttr<IBActionAttr>())
Info.Properties |= (unsigned)SymbolProperty::IBAnnotated;
break;
+ }
case Decl::ObjCProperty:
Info.Kind = SymbolKind::InstanceProperty;
Info.Lang = SymbolLanguage::ObjC;
@@ -303,6 +300,18 @@
Info.Kind = SymbolKind::TypeAlias;
Info.Lang = SymbolLanguage::CXX;
break;
+ case Decl::UnresolvedUsingTypename:
+ Info.Kind = SymbolKind::Using;
+ Info.SubKind = SymbolSubKind::UsingTypename;
+ Info.Lang = SymbolLanguage::CXX;
+ Info.Properties |= (unsigned)SymbolProperty::Generic;
+ break;
+ case Decl::UnresolvedUsingValue:
+ Info.Kind = SymbolKind::Using;
+ Info.SubKind = SymbolSubKind::UsingValue;
+ Info.Lang = SymbolLanguage::CXX;
+ Info.Properties |= (unsigned)SymbolProperty::Generic;
+ break;
case Decl::Binding:
Info.Kind = SymbolKind::Variable;
Info.Lang = SymbolLanguage::CXX;
@@ -451,6 +460,8 @@
case SymbolKind::Destructor: return "destructor";
case SymbolKind::ConversionFunction: return "coversion-func";
case SymbolKind::Parameter: return "param";
+ case SymbolKind::Using: return "using";
+ case SymbolKind::CommentTag: return "comment-tag";
}
llvm_unreachable("invalid symbol kind");
}
@@ -462,6 +473,22 @@
case SymbolSubKind::CXXMoveConstructor: return "cxx-move-ctor";
case SymbolSubKind::AccessorGetter: return "acc-get";
case SymbolSubKind::AccessorSetter: return "acc-set";
+ case SymbolSubKind::UsingTypename: return "using-typename";
+ case SymbolSubKind::UsingValue: return "using-value";
+ case SymbolSubKind::SwiftAccessorWillSet: return "acc-willset";
+ case SymbolSubKind::SwiftAccessorDidSet: return "acc-didset";
+ case SymbolSubKind::SwiftAccessorAddressor: return "acc-addr";
+ case SymbolSubKind::SwiftAccessorMutableAddressor: return "acc-mutaddr";
+ case SymbolSubKind::SwiftExtensionOfStruct: return "ext-struct";
+ case SymbolSubKind::SwiftExtensionOfClass: return "ext-class";
+ case SymbolSubKind::SwiftExtensionOfEnum: return "ext-enum";
+ case SymbolSubKind::SwiftExtensionOfProtocol: return "ext-protocol";
+ case SymbolSubKind::SwiftPrefixOperator: return "prefix-operator";
+ case SymbolSubKind::SwiftPostfixOperator: return "postfix-operator";
+ case SymbolSubKind::SwiftInfixOperator: return "infix-operator";
+ case SymbolSubKind::SwiftSubscript: return "subscript";
+ case SymbolSubKind::SwiftAssociatedType: return "associated-type";
+ case SymbolSubKind::SwiftGenericTypeParam: return "generic-type-param";
}
llvm_unreachable("invalid symbol subkind");
}
diff --git a/lib/Index/IndexTypeSourceInfo.cpp b/lib/Index/IndexTypeSourceInfo.cpp
index ae27ebe..c8ff3d7 100644
--- a/lib/Index/IndexTypeSourceInfo.cpp
+++ b/lib/Index/IndexTypeSourceInfo.cpp
@@ -126,8 +126,9 @@
return true;
}
- bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
- if (const TemplateSpecializationType *T = TL.getTypePtr()) {
+ template<typename TypeLocType>
+ bool HandleTemplateSpecializationTypeLoc(TypeLocType TL) {
+ if (const auto *T = TL.getTypePtr()) {
if (IndexCtx.shouldIndexImplicitTemplateInsts()) {
if (CXXRecordDecl *RD = T->getAsCXXRecordDecl())
IndexCtx.handleReference(RD, TL.getTemplateNameLoc(),
@@ -141,6 +142,14 @@
return true;
}
+ bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
+ return HandleTemplateSpecializationTypeLoc(TL);
+ }
+
+ bool VisitDeducedTemplateSpecializationTypeLoc(DeducedTemplateSpecializationTypeLoc TL) {
+ return HandleTemplateSpecializationTypeLoc(TL);
+ }
+
bool VisitDependentNameTypeLoc(DependentNameTypeLoc TL) {
const DependentNameType *DNT = TL.getTypePtr();
const NestedNameSpecifier *NNS = DNT->getQualifier();
diff --git a/lib/Index/IndexUnitReader.cpp b/lib/Index/IndexUnitReader.cpp
new file mode 100644
index 0000000..12a9056
--- /dev/null
+++ b/lib/Index/IndexUnitReader.cpp
@@ -0,0 +1,516 @@
+//===--- IndexUnitReader.cpp - Index unit deserialization -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/IndexUnitReader.h"
+#include "IndexDataStoreUtils.h"
+#include "BitstreamVisitor.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Bitcode/BitstreamReader.h"
+#include "llvm/Support/Chrono.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <unistd.h>
+
+using namespace clang;
+using namespace clang::index;
+using namespace clang::index::store;
+using namespace llvm;
+
+namespace {
+
+typedef function_ref<bool(const IndexUnitReader::DependencyInfo &)> DependencyReceiver;
+typedef function_ref<bool(const IndexUnitReader::IncludeInfo &)> IncludeReceiver;
+
+class IndexUnitReaderImpl {
+ sys::TimePoint<> ModTime;
+ std::unique_ptr<MemoryBuffer> MemBuf;
+
+public:
+ StringRef ProviderIdentifier;
+ StringRef ProviderVersion;
+ llvm::BitstreamCursor DependCursor;
+ llvm::BitstreamCursor IncludeCursor;
+ bool IsSystemUnit;
+ bool IsModuleUnit;
+ bool IsDebugCompilation;
+ StringRef WorkingDir;
+ StringRef OutputFile;
+ StringRef SysrootPath;
+ StringRef ModuleName;
+ SmallString<128> MainFilePath;
+ StringRef Target;
+ std::vector<FileBitPath> Paths;
+ StringRef PathsBuffer;
+
+ struct ModuleInfo {
+ unsigned NameOffset;
+ unsigned NameSize;
+ };
+ std::vector<ModuleInfo> Modules;
+ StringRef ModuleNamesBuffer;
+
+ bool init(std::unique_ptr<MemoryBuffer> Buf, sys::TimePoint<> ModTime,
+ std::string &Error);
+
+ StringRef getProviderIdentifier() const { return ProviderIdentifier; }
+ StringRef getProviderVersion() const { return ProviderVersion; }
+
+ sys::TimePoint<> getModificationTime() const { return ModTime; }
+ StringRef getWorkingDirectory() const { return WorkingDir; }
+ StringRef getOutputFile() const { return OutputFile; }
+ StringRef getSysrootPath() const { return SysrootPath; }
+ StringRef getTarget() const { return Target; }
+
+ StringRef getModuleName() const { return ModuleName; }
+ StringRef getMainFilePath() const { return MainFilePath.str(); }
+ bool hasMainFile() const { return !MainFilePath.empty(); }
+ bool isSystemUnit() const { return IsSystemUnit; }
+ bool isModuleUnit() const { return IsModuleUnit; }
+ bool isDebugCompilation() const { return IsDebugCompilation; }
+
+ /// Unit dependencies are provided ahead of record ones, record ones
+ /// ahead of the file ones.
+ bool foreachDependency(DependencyReceiver Receiver);
+
+ bool foreachInclude(IncludeReceiver Receiver);
+
+ StringRef getPathFromBuffer(size_t Offset, size_t Size) {
+ return PathsBuffer.substr(Offset, Size);
+ }
+
+ void constructFilePath(SmallVectorImpl<char> &Path, int PathIndex);
+
+ StringRef getModuleName(int ModuleIndex);
+};
+
+class IndexUnitBitstreamVisitor : public BitstreamVisitor<IndexUnitBitstreamVisitor> {
+ IndexUnitReaderImpl &Reader;
+ size_t WorkDirOffset;
+ size_t WorkDirSize;
+ size_t OutputFileOffset;
+ size_t OutputFileSize;
+ size_t SysrootOffset;
+ size_t SysrootSize;
+ int MainPathIndex;
+
+public:
+ IndexUnitBitstreamVisitor(llvm::BitstreamCursor &Stream,
+ IndexUnitReaderImpl &Reader)
+ : BitstreamVisitor(Stream), Reader(Reader) {}
+
+ StreamVisit visitBlock(unsigned ID) {
+ switch ((UnitBitBlock)ID) {
+ case UNIT_VERSION_BLOCK_ID:
+ case UNIT_INFO_BLOCK_ID:
+ case UNIT_PATHS_BLOCK_ID:
+ case UNIT_MODULES_BLOCK_ID:
+ return StreamVisit::Continue;
+
+ case UNIT_DEPENDENCIES_BLOCK_ID:
+ Reader.DependCursor = Stream;
+ if (Reader.DependCursor.EnterSubBlock(ID)) {
+ *Error = "malformed unit dependencies block record";
+ return StreamVisit::Abort;
+ }
+ readBlockAbbrevs(Reader.DependCursor);
+ return StreamVisit::Skip;
+ case UNIT_INCLUDES_BLOCK_ID:
+ Reader.IncludeCursor = Stream;
+ if (Reader.IncludeCursor.EnterSubBlock(ID)) {
+ *Error = "malformed unit includes block record";
+ return StreamVisit::Abort;
+ }
+ readBlockAbbrevs(Reader.IncludeCursor);
+ return StreamVisit::Skip;
+ }
+
+ // Some newly introduced block in a minor version update that we cannot
+ // handle.
+ return StreamVisit::Skip;
+ }
+
+ StreamVisit visitRecord(unsigned BlockID, unsigned RecID,
+ RecordDataImpl &Record, StringRef Blob) {
+ switch (BlockID) {
+ case UNIT_VERSION_BLOCK_ID: {
+ unsigned StoreFormatVersion = Record[0];
+ if (StoreFormatVersion != STORE_FORMAT_VERSION) {
+ llvm::raw_string_ostream OS(*Error);
+ OS << "Store format version mismatch: " << StoreFormatVersion;
+ OS << " , expected: " << STORE_FORMAT_VERSION;
+ return StreamVisit::Abort;
+ }
+ break;
+ }
+
+ case UNIT_INFO_BLOCK_ID: {
+ assert(RecID == UNIT_INFO);
+ unsigned I = 0;
+ Reader.IsSystemUnit = Record[I++];
+
+ // Save these to lookup them up after we get the paths buffer.
+ WorkDirOffset = Record[I++];
+ WorkDirSize = Record[I++];
+ OutputFileOffset = Record[I++];
+ OutputFileSize = Record[I++];
+ SysrootOffset = Record[I++];
+ SysrootSize = Record[I++];
+ MainPathIndex = (int)Record[I++] - 1;
+ Reader.IsDebugCompilation = Record[I++];
+ Reader.IsModuleUnit = Record[I++];
+
+ size_t moduleNameSize = Record[I++];
+ size_t providerIdentifierSize = Record[I++];
+ size_t providerVersionSize = Record[I++];
+ I++; // Reserved for ProviderDataVersion.
+ Reader.ModuleName = Blob.substr(0, moduleNameSize);
+ Blob = Blob.drop_front(moduleNameSize);
+ Reader.ProviderIdentifier = Blob.substr(0, providerIdentifierSize);
+ Blob = Blob.drop_front(providerIdentifierSize);
+ Reader.ProviderVersion = Blob.substr(0, providerVersionSize);
+ Reader.Target = Blob.drop_front(providerVersionSize);
+ break;
+ }
+
+ case UNIT_PATHS_BLOCK_ID:
+ switch (RecID) {
+ case UNIT_PATH:
+ {
+ unsigned I = 0;
+ UnitFilePathPrefixKind Kind = (UnitFilePathPrefixKind)Record[I++];
+ size_t DirOffset = Record[I++];
+ size_t DirSize = Record[I++];
+ size_t FilenameOffset = Record[I++];
+ size_t FilenameSize = Record[I++];
+
+ Reader.Paths.emplace_back(Kind, BitPathComponent(DirOffset, DirSize),
+ BitPathComponent(FilenameOffset, FilenameSize));
+ }
+ break;
+ case UNIT_PATH_BUFFER:
+ Reader.PathsBuffer = Blob;
+ Reader.WorkingDir = Reader.getPathFromBuffer(WorkDirOffset, WorkDirSize);
+ Reader.OutputFile = Reader.getPathFromBuffer(OutputFileOffset, OutputFileSize);
+ Reader.SysrootPath = Reader.getPathFromBuffer(SysrootOffset, SysrootSize);
+
+ // now we can populate the main file's path
+ Reader.constructFilePath(Reader.MainFilePath, MainPathIndex);
+ break;
+ default:
+ llvm_unreachable("shouldn't visit this record");
+ }
+ break;
+
+ case UNIT_MODULES_BLOCK_ID:
+ switch (RecID) {
+ case UNIT_MODULE:
+ {
+ unsigned I = 0;
+ unsigned NameOffset = Record[I++];
+ unsigned NameSize = Record[I++];
+ Reader.Modules.push_back({NameOffset, NameSize});
+ }
+ break;
+ case UNIT_MODULE_BUFFER:
+ Reader.ModuleNamesBuffer = Blob;
+ break;
+ default:
+ llvm_unreachable("shouldn't visit this record");
+ }
+ break;
+
+ case UNIT_DEPENDENCIES_BLOCK_ID:
+ case UNIT_INCLUDES_BLOCK_ID:
+ llvm_unreachable("shouldn't visit this block'");
+ }
+ return StreamVisit::Continue;
+ }
+};
+
+typedef std::function<bool(RecordDataImpl& Record, StringRef Blob)>
+ BlockVisitorCallback;
+
+class IndexUnitBlockBitstreamVisitor : public BitstreamVisitor<IndexUnitBlockBitstreamVisitor> {
+ unsigned RecID;
+ BlockVisitorCallback Visit;
+
+public:
+ IndexUnitBlockBitstreamVisitor(unsigned RecID,
+ llvm::BitstreamCursor &BlockStream,
+ BlockVisitorCallback Visit)
+ : BitstreamVisitor(BlockStream), RecID(RecID), Visit(std::move(Visit)) {}
+
+ StreamVisit visitRecord(unsigned BlockID, unsigned RecID,
+ RecordDataImpl &Record, StringRef Blob) {
+ if (RecID != this->RecID)
+ llvm_unreachable("shouldn't be called with this RecID");
+
+ if (Visit(Record, Blob))
+ return StreamVisit::Continue;
+ return StreamVisit::Abort;
+ }
+};
+
+} // anonymous namespace
+
+bool IndexUnitReaderImpl::init(std::unique_ptr<MemoryBuffer> Buf,
+ sys::TimePoint<> ModTime, std::string &Error) {
+ this->ModTime = ModTime;
+ this->MemBuf = std::move(Buf);
+ llvm::BitstreamCursor Stream(*MemBuf);
+
+ // Sniff for the signature.
+ if (Stream.Read(8) != 'I' ||
+ Stream.Read(8) != 'D' ||
+ Stream.Read(8) != 'X' ||
+ Stream.Read(8) != 'U') {
+ Error = "not a serialized index unit file";
+ return true;
+ }
+
+ IndexUnitBitstreamVisitor BitVisitor(Stream, *this);
+ return !BitVisitor.visit(Error);
+}
+
+/// Unit dependencies are provided ahead of record ones, record ones
+/// ahead of the file ones.
+bool IndexUnitReaderImpl::foreachDependency(DependencyReceiver Receiver) {
+ store::SavedStreamPosition SavedDepPosition(DependCursor);
+ IndexUnitBlockBitstreamVisitor Visitor(UNIT_DEPENDENCY, DependCursor,
+ [&](RecordDataImpl& Record, StringRef Blob) {
+ unsigned I = 0;
+ UnitDependencyKind UnitDepKind = (UnitDependencyKind)Record[I++];
+ bool IsSystem = Record[I++];
+ int PathIndex = (int)Record[I++] - 1;
+ int ModuleIndex = (int)Record[I++] - 1;
+ time_t ModTime = (time_t)Record[I++];
+ size_t FileSize = Record[I++];
+ StringRef Name = Blob;
+
+ IndexUnitReader::DependencyKind DepKind;
+ switch (UnitDepKind) {
+ case UNIT_DEPEND_KIND_UNIT:
+ DepKind = IndexUnitReader::DependencyKind::Unit; break;
+ case UNIT_DEPEND_KIND_RECORD:
+ DepKind = IndexUnitReader::DependencyKind::Record; break;
+ case UNIT_DEPEND_KIND_FILE:
+ DepKind = IndexUnitReader::DependencyKind::File; break;
+ }
+
+ SmallString<512> PathBuf;
+ this->constructFilePath(PathBuf, PathIndex);
+ StringRef ModuleName = this->getModuleName(ModuleIndex);
+
+ return Receiver(IndexUnitReader::DependencyInfo{DepKind, IsSystem, Name,
+ PathBuf.str(), ModuleName, FileSize, ModTime});
+ });
+
+ std::string Error;
+ return Visitor.visit(Error);
+}
+
+bool IndexUnitReaderImpl::foreachInclude(IncludeReceiver Receiver) {
+ store::SavedStreamPosition SavedIncPosition(IncludeCursor);
+ IndexUnitBlockBitstreamVisitor Visitor(UNIT_INCLUDE, IncludeCursor,
+ [&](RecordDataImpl& Record, StringRef Blob) {
+ unsigned I = 0;
+ int SourcePathIndex = (int)Record[I++] - 1;
+ unsigned Line = Record[I++];
+ int TargetPathIndex = (int)Record[I++] - 1;
+
+ SmallString<512> SourceBuf, TargetBuf;
+ this->constructFilePath(SourceBuf, SourcePathIndex);
+ this->constructFilePath(TargetBuf, TargetPathIndex);
+ return Receiver(IndexUnitReader::IncludeInfo{SourceBuf.str(), Line, TargetBuf.str()});
+ });
+
+ std::string Error;
+ return Visitor.visit(Error);
+}
+
+
+void IndexUnitReaderImpl::constructFilePath(SmallVectorImpl<char> &PathBuf,
+ int PathIndex) {
+
+ if (PathIndex < 0) return;
+ FileBitPath &Path = Paths[PathIndex];
+ StringRef Prefix;
+ switch (Path.PrefixKind) {
+ case UNIT_PATH_PREFIX_NONE:
+ break;
+ case UNIT_PATH_PREFIX_WORKDIR:
+ Prefix = getWorkingDirectory();
+ break;
+ case UNIT_PATH_PREFIX_SYSROOT:
+ Prefix = getSysrootPath();
+ break;
+ }
+ PathBuf.append(Prefix.begin(), Prefix.end());
+ sys::path::append(PathBuf,
+ getPathFromBuffer(Path.Dir.Offset, Path.Dir.Size),
+ getPathFromBuffer(Path.Filename.Offset, Path.Filename.Size));
+}
+
+StringRef IndexUnitReaderImpl::getModuleName(int ModuleIndex) {
+ if (ModuleIndex < 0)
+ return StringRef();
+ auto &ModInfo = Modules[ModuleIndex];
+ return StringRef(ModuleNamesBuffer.data()+ModInfo.NameOffset, ModInfo.NameSize);
+}
+
+
+//===----------------------------------------------------------------------===//
+// IndexUnitReader
+//===----------------------------------------------------------------------===//
+
+std::unique_ptr<IndexUnitReader>
+IndexUnitReader::createWithUnitFilename(StringRef UnitFilename,
+ StringRef StorePath,
+ std::string &Error) {
+ SmallString<128> PathBuf = StorePath;
+ appendUnitSubDir(PathBuf);
+ sys::path::append(PathBuf, UnitFilename);
+ return createWithFilePath(PathBuf.str(), Error);
+}
+
+std::unique_ptr<IndexUnitReader>
+IndexUnitReader::createWithFilePath(StringRef FilePath, std::string &Error) {
+ int FD;
+ std::error_code EC = sys::fs::openFileForRead(FilePath, FD);
+ if (EC) {
+ raw_string_ostream(Error) << "Failed opening '" << FilePath << "': "
+ << EC.message();
+ return nullptr;
+ }
+
+ assert(FD != -1);
+ struct AutoFDClose {
+ int FD;
+ AutoFDClose(int FD) : FD(FD) {}
+ ~AutoFDClose() {
+ ::close(FD);
+ }
+ } AutoFDClose(FD);
+
+ sys::fs::file_status FileStat;
+ EC = sys::fs::status(FD, FileStat);
+ if (EC) {
+ Error = EC.message();
+ return nullptr;
+ }
+
+ auto ErrOrBuf = MemoryBuffer::getOpenFile(FD, FilePath, /*FileSize=*/-1,
+ /*RequiresNullTerminator=*/false);
+ if (!ErrOrBuf) {
+ raw_string_ostream(Error) << "Failed opening '" << FilePath << "': "
+ << ErrOrBuf.getError().message();
+ return nullptr;
+ }
+
+ std::unique_ptr<IndexUnitReaderImpl> Impl(new IndexUnitReaderImpl());
+ bool Err = Impl->init(std::move(*ErrOrBuf), FileStat.getLastModificationTime(),
+ Error);
+ if (Err)
+ return nullptr;
+
+ std::unique_ptr<IndexUnitReader> Reader;
+ Reader.reset(new IndexUnitReader(Impl.release()));
+ return Reader;
+}
+
+Optional<sys::TimePoint<>>
+IndexUnitReader::getModificationTimeForUnit(StringRef UnitFilename,
+ StringRef StorePath,
+ std::string &Error) {
+ SmallString<128> PathBuf = StorePath;
+ appendUnitSubDir(PathBuf);
+ sys::path::append(PathBuf, UnitFilename);
+
+ sys::fs::file_status FileStat;
+ std::error_code EC = sys::fs::status(PathBuf.str(), FileStat);
+ if (EC) {
+ Error = EC.message();
+ return None;
+ }
+ return FileStat.getLastModificationTime();
+}
+
+#define IMPL static_cast<IndexUnitReaderImpl*>(Impl)
+
+IndexUnitReader::~IndexUnitReader() {
+ delete IMPL;
+}
+
+StringRef IndexUnitReader::getProviderIdentifier() const {
+ return IMPL->getProviderIdentifier();
+}
+
+StringRef IndexUnitReader::getProviderVersion() const {
+ return IMPL->getProviderVersion();
+}
+
+llvm::sys::TimePoint<> IndexUnitReader::getModificationTime() const {
+ return IMPL->getModificationTime();
+}
+
+StringRef IndexUnitReader::getWorkingDirectory() const {
+ return IMPL->getWorkingDirectory();
+}
+
+StringRef IndexUnitReader::getOutputFile() const {
+ return IMPL->getOutputFile();
+}
+
+StringRef IndexUnitReader::getSysrootPath() const {
+ return IMPL->getSysrootPath();
+}
+
+StringRef IndexUnitReader::getMainFilePath() const {
+ return IMPL->getMainFilePath();
+}
+
+StringRef IndexUnitReader::getModuleName() const {
+ return IMPL->getModuleName();
+}
+
+StringRef IndexUnitReader::getTarget() const {
+ return IMPL->getTarget();
+}
+
+bool IndexUnitReader::hasMainFile() const {
+ return IMPL->hasMainFile();
+}
+
+bool IndexUnitReader::isSystemUnit() const {
+ return IMPL->isSystemUnit();
+}
+
+bool IndexUnitReader::isModuleUnit() const {
+ return IMPL->isModuleUnit();
+}
+
+bool IndexUnitReader::isDebugCompilation() const {
+ return IMPL->isDebugCompilation();
+}
+
+/// \c Index is the index in the \c getDependencies array.
+/// Unit dependencies are provided ahead of record ones.
+bool IndexUnitReader::foreachDependency(DependencyReceiver Receiver) {
+ return IMPL->foreachDependency(std::move(Receiver));
+}
+
+bool IndexUnitReader::foreachInclude(IncludeReceiver Receiver) {
+ return IMPL->foreachInclude(std::move(Receiver));
+}
diff --git a/lib/Index/IndexUnitWriter.cpp b/lib/Index/IndexUnitWriter.cpp
new file mode 100644
index 0000000..7c981ae
--- /dev/null
+++ b/lib/Index/IndexUnitWriter.cpp
@@ -0,0 +1,628 @@
+//===--- IndexUnitWriter.cpp - Index unit serialization -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Index/IndexUnitWriter.h"
+#include "IndexDataStoreUtils.h"
+#include "clang/Basic/FileManager.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Bitcode/BitstreamWriter.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::index;
+using namespace clang::index::store;
+using namespace llvm;
+
+
+class IndexUnitWriter::PathStorage {
+ std::string WorkDir;
+ std::string SysrootPath;
+ SmallString<512> PathsBuf;
+ StringMap<DirBitPath, BumpPtrAllocator> Dirs;
+ std::vector<FileBitPath> FileBitPaths;
+ DenseMap<const FileEntry *, size_t> FileToIndex;
+
+public:
+ PathStorage(StringRef workDir, StringRef sysrootPath) {
+ WorkDir = workDir;
+ if (sysrootPath == "/")
+ sysrootPath = StringRef();
+ SysrootPath = sysrootPath;
+ }
+
+ StringRef getPathsBuffer() const { return PathsBuf.str(); }
+
+ ArrayRef<FileBitPath> getBitPaths() const { return FileBitPaths; }
+
+ int getPathIndex(const FileEntry *FE) {
+ if (!FE)
+ return -1;
+ auto Pair = FileToIndex.insert(std::make_pair(FE, FileBitPaths.size()));
+ bool IsNew = Pair.second;
+ size_t Index = Pair.first->getSecond();
+
+ if (IsNew) {
+ StringRef Filename = sys::path::filename(FE->getName());
+ DirBitPath Dir = getDirBitPath(sys::path::parent_path(FE->getName()));
+ FileBitPaths.emplace_back(Dir.PrefixKind, Dir.Dir,
+ BitPathComponent(getPathOffset(Filename),
+ Filename.size()));
+ }
+ return Index;
+ }
+
+ size_t getPathOffset(StringRef Path) {
+ if (Path.empty())
+ return 0;
+ size_t offset = PathsBuf.size();
+ PathsBuf += Path;
+ return offset;
+ }
+
+private:
+ DirBitPath getDirBitPath(StringRef dirStr) {
+ auto pair = Dirs.insert(std::make_pair(dirStr, DirBitPath()));
+ bool isNew = pair.second;
+ auto &dirPath = pair.first->second;
+
+ if (isNew) {
+ if (isPathInDir(SysrootPath, dirStr)) {
+ dirPath.PrefixKind = UNIT_PATH_PREFIX_SYSROOT;
+ dirStr = dirStr.drop_front(SysrootPath.size());
+ while (!dirStr.empty() && dirStr[0] == '/')
+ dirStr = dirStr.drop_front();
+ } else if (isPathInDir(WorkDir, dirStr)) {
+ dirPath.PrefixKind = UNIT_PATH_PREFIX_WORKDIR;
+ dirStr = dirStr.drop_front(WorkDir.size());
+ while (!dirStr.empty() && dirStr[0] == '/')
+ dirStr = dirStr.drop_front();
+ }
+ dirPath.Dir.Offset = getPathOffset(dirStr);
+ dirPath.Dir.Size = dirStr.size();
+ }
+ return dirPath;
+ }
+
+ static bool isPathInDir(StringRef dir, StringRef path) {
+ if (dir.empty() || !path.startswith(dir))
+ return false;
+ StringRef rest = path.drop_front(dir.size());
+ return !rest.empty() && sys::path::is_separator(rest.front());
+ }
+};
+
+IndexUnitWriter::IndexUnitWriter(FileManager &FileMgr,
+ StringRef StorePath,
+ StringRef ProviderIdentifier,
+ StringRef ProviderVersion,
+ StringRef OutputFile,
+ StringRef ModuleName,
+ const FileEntry *MainFile,
+ bool IsSystem,
+ bool IsModuleUnit,
+ bool IsDebugCompilation,
+ StringRef TargetTriple,
+ StringRef SysrootPath,
+ writer::ModuleInfoWriterCallback GetInfoForModule)
+: FileMgr(FileMgr) {
+ this->UnitsPath = StorePath;
+ store::appendUnitSubDir(this->UnitsPath);
+ this->ProviderIdentifier = ProviderIdentifier;
+ this->ProviderVersion = ProviderVersion;
+ this->OutputFile = OutputFile;
+ this->ModuleName = ModuleName;
+ this->MainFile = MainFile;
+ this->IsSystemUnit = IsSystem;
+ this->IsModuleUnit = IsModuleUnit;
+ this->IsDebugCompilation = IsDebugCompilation;
+ this->TargetTriple = TargetTriple;
+ this->SysrootPath = SysrootPath;
+ this->GetInfoForModuleFn = GetInfoForModule;
+}
+
+IndexUnitWriter::~IndexUnitWriter() {}
+
+int IndexUnitWriter::addModule(writer::OpaqueModule Mod) {
+ if (!Mod)
+ return -1;
+
+ auto Pair = IndexByModule.insert(std::make_pair(Mod, Modules.size()));
+ bool WasInserted = Pair.second;
+ if (WasInserted) {
+ Modules.push_back(Mod);
+ }
+ return Pair.first->second;
+}
+
+int IndexUnitWriter::addFileDependency(const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod) {
+ assert(File);
+ auto Pair = IndexByFile.insert(std::make_pair(File, Files.size()));
+ bool WasInserted = Pair.second;
+ if (WasInserted) {
+ Files.push_back(FileEntryData{File, IsSystem, addModule(Mod), {}});
+ }
+ return Pair.first->second;
+}
+
+void IndexUnitWriter::addRecordFile(StringRef RecordFile, const FileEntry *File,
+ bool IsSystem, writer::OpaqueModule Mod) {
+ int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1;
+ Records.push_back(RecordOrUnitData{RecordFile, Dep, addModule(Mod), IsSystem});
+}
+
+void IndexUnitWriter::addASTFileDependency(const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod,
+ bool withoutUnitName) {
+ assert(File);
+ if (!SeenASTFiles.insert(File).second)
+ return;
+
+ SmallString<64> UnitName;
+ if (!withoutUnitName)
+ getUnitNameForOutputFile(File->getName(), UnitName);
+ addUnitDependency(UnitName.str(), File, IsSystem, Mod);
+}
+
+void IndexUnitWriter::addUnitDependency(StringRef UnitFile,
+ const FileEntry *File, bool IsSystem,
+ writer::OpaqueModule Mod) {
+ int Dep = File ? addFileDependency(File, IsSystem, /*module=*/nullptr) : -1;
+ ASTFileUnits.emplace_back(RecordOrUnitData{UnitFile, Dep, addModule(Mod), IsSystem});
+}
+
+bool IndexUnitWriter::addInclude(const FileEntry *Source, unsigned Line,
+ const FileEntry *Target) {
+ // FIXME: This will ignore includes of headers that resolve to module imports
+ // because the 'target' header has not been added as a file dependency earlier
+ // so it is missing from \c IndexByFile.
+
+ auto It = IndexByFile.find(Source);
+ if (It == IndexByFile.end())
+ return false;
+ int SourceIndex = It->getSecond();
+ It = IndexByFile.find(Target);
+ if (It == IndexByFile.end())
+ return false;
+ int TargetIndex = It->getSecond();
+ Files[SourceIndex].Includes.emplace_back(FileInclude{TargetIndex, Line});
+ return true;
+};
+
+void IndexUnitWriter::getUnitNameForOutputFile(StringRef FilePath,
+ SmallVectorImpl<char> &Str) {
+ SmallString<256> AbsPath(FilePath);
+ FileMgr.makeAbsolutePath(AbsPath);
+ return getUnitNameForAbsoluteOutputFile(AbsPath, Str);
+}
+
+void IndexUnitWriter::getUnitPathForOutputFile(StringRef FilePath,
+ SmallVectorImpl<char> &Str) {
+ Str.append(UnitsPath.begin(), UnitsPath.end());
+ Str.push_back('/');
+ return getUnitNameForOutputFile(FilePath, Str);
+}
+
+Optional<bool> IndexUnitWriter::isUnitUpToDateForOutputFile(StringRef FilePath,
+ Optional<StringRef> TimeCompareFilePath,
+ std::string &Error) {
+ SmallString<256> UnitPath;
+ getUnitPathForOutputFile(FilePath, UnitPath);
+
+ llvm::sys::fs::file_status UnitStat;
+ if (std::error_code EC = llvm::sys::fs::status(UnitPath.c_str(), UnitStat)) {
+ if (EC != llvm::errc::no_such_file_or_directory) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "could not access path '" << UnitPath
+ << "': " << EC.message();
+ return None;
+ }
+ return false;
+ }
+
+ if (!TimeCompareFilePath.hasValue())
+ return true;
+
+ llvm::sys::fs::file_status CompareStat;
+ if (std::error_code EC = llvm::sys::fs::status(*TimeCompareFilePath, CompareStat)) {
+ if (EC != llvm::errc::no_such_file_or_directory) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "could not access path '" << *TimeCompareFilePath
+ << "': " << EC.message();
+ return None;
+ }
+ return true;
+ }
+
+ // Return true (unit is up-to-date) if the file to compare is older than the
+ // unit file.
+ return CompareStat.getLastModificationTime() <= UnitStat.getLastModificationTime();
+}
+
+void IndexUnitWriter::getUnitNameForAbsoluteOutputFile(StringRef FilePath,
+ SmallVectorImpl<char> &Str) {
+ StringRef Fname = sys::path::filename(FilePath);
+ Str.append(Fname.begin(), Fname.end());
+ Str.push_back('-');
+ llvm::hash_code PathHashVal = llvm::hash_value(FilePath);
+ llvm::APInt(64, PathHashVal).toString(Str, 36, /*Signed=*/false);
+}
+
+static void writeBlockInfo(BitstreamWriter &Stream) {
+ RecordData Record;
+
+ Stream.EnterBlockInfoBlock();
+#define BLOCK(X) emitBlockID(X ## _ID, #X, Stream, Record)
+#define RECORD(X) emitRecordID(X, #X, Stream, Record)
+
+ BLOCK(UNIT_VERSION_BLOCK);
+ RECORD(UNIT_VERSION);
+
+ BLOCK(UNIT_INFO_BLOCK);
+ RECORD(UNIT_INFO);
+
+ BLOCK(UNIT_DEPENDENCIES_BLOCK);
+ RECORD(UNIT_DEPENDENCY);
+
+ BLOCK(UNIT_INCLUDES_BLOCK);
+ RECORD(UNIT_INCLUDE);
+
+ BLOCK(UNIT_PATHS_BLOCK);
+ RECORD(UNIT_PATH);
+ RECORD(UNIT_PATH_BUFFER);
+
+ BLOCK(UNIT_MODULES_BLOCK);
+ RECORD(UNIT_MODULE);
+ RECORD(UNIT_MODULE_BUFFER);
+
+#undef RECORD
+#undef BLOCK
+ Stream.ExitBlock();
+}
+
+static void writeVersionInfo(BitstreamWriter &Stream) {
+ using namespace llvm::sys;
+
+ Stream.EnterSubblock(UNIT_VERSION_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(UNIT_VERSION));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Store format version
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ RecordData Record;
+ Record.push_back(UNIT_VERSION);
+ Record.push_back(STORE_FORMAT_VERSION);
+ Stream.EmitRecordWithAbbrev(AbbrevCode, Record);
+
+ Stream.ExitBlock();
+}
+
+bool IndexUnitWriter::write(std::string &Error) {
+ using namespace llvm::sys;
+
+ // Determine the working directory.
+ SmallString<128> CWDPath;
+ if (!FileMgr.getFileSystemOpts().WorkingDir.empty()) {
+ CWDPath = FileMgr.getFileSystemOpts().WorkingDir;
+ if (!path::is_absolute(CWDPath)) {
+ fs::make_absolute(CWDPath);
+ }
+ } else {
+ std::error_code EC = sys::fs::current_path(CWDPath);
+ if (EC) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to determine current working directory: " << EC.message();
+ return true;
+ }
+ }
+ WorkDir = CWDPath.str();
+
+ SmallString<512> Buffer;
+ BitstreamWriter Stream(Buffer);
+ Stream.Emit('I', 8);
+ Stream.Emit('D', 8);
+ Stream.Emit('X', 8);
+ Stream.Emit('U', 8);
+
+ PathStorage PathStore(WorkDir, SysrootPath);
+
+ writeBlockInfo(Stream);
+ writeVersionInfo(Stream);
+ writeUnitInfo(Stream, PathStore);
+ writeDependencies(Stream, PathStore);
+ writeIncludes(Stream, PathStore);
+ writePaths(Stream, PathStore);
+ writeModules(Stream);
+
+ SmallString<256> UnitPath;
+ getUnitPathForOutputFile(OutputFile, UnitPath);
+
+ SmallString<128> TempPath;
+ TempPath = path::parent_path(UnitsPath);
+ TempPath += '/';
+ TempPath += path::filename(UnitPath);
+ TempPath += "-%%%%%%%%";
+ int TempFD;
+ if (llvm::sys::fs::createUniqueFile(TempPath.str(), TempFD, TempPath)) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to create temporary file: " << TempPath;
+ return true;
+ }
+
+ raw_fd_ostream OS(TempFD, /*shouldClose=*/true);
+ OS.write(Buffer.data(), Buffer.size());
+ OS.close();
+
+ std::error_code EC = fs::rename(/*from=*/TempPath.c_str(), /*to=*/UnitPath.c_str());
+ if (EC) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to rename '" << TempPath << "' to '" << UnitPath << "': " << EC.message();
+ return true;
+ }
+
+ return false;
+}
+
+void IndexUnitWriter::writeUnitInfo(llvm::BitstreamWriter &Stream,
+ PathStorage &PathStore) {
+ Stream.EnterSubblock(UNIT_INFO_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(UNIT_INFO));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystemUnit
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // WorkDir offset
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // WorkDir size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // OutputFile offset
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // OutputFile size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Sysroot offset
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Sysroot size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Main path id
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsDebugCompilation
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsModuleUnit
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // Module name size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderIdentifier size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderVersion size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 5)); // ProviderDataVersion
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module name + ProviderIdentifier + ProviderVersion + target triple
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ RecordData Record;
+ Record.push_back(UNIT_INFO);
+ Record.push_back(IsSystemUnit);
+ Record.push_back(PathStore.getPathOffset(WorkDir));
+ Record.push_back(WorkDir.size());
+ Record.push_back(PathStore.getPathOffset(OutputFile));
+ Record.push_back(OutputFile.size());
+ Record.push_back(PathStore.getPathOffset(SysrootPath));
+ Record.push_back(SysrootPath.size());
+ Record.push_back(PathStore.getPathIndex(MainFile) + 1); // Make 1-based with 0=invalid
+ Record.push_back(IsDebugCompilation);
+ Record.push_back(IsModuleUnit);
+ Record.push_back(ModuleName.size());
+ Record.push_back(ProviderIdentifier.size());
+ Record.push_back(ProviderVersion.size());
+ // ProviderDataVersion is reserved. Not sure it is a good to idea to have
+ // clients consider the specifics of a 'provider data version', but reserving
+ // to avoid store format version change in case there is a use case in the
+ // future.
+ Record.push_back(0); // ProviderDataVersion
+ SmallString<128> InfoStrings;
+ InfoStrings += ModuleName;
+ InfoStrings += ProviderIdentifier;
+ InfoStrings += ProviderVersion;
+ InfoStrings += TargetTriple;
+ Stream.EmitRecordWithBlob(AbbrevCode, Record, InfoStrings);
+
+ Stream.ExitBlock();
+}
+
+void IndexUnitWriter::writeDependencies(llvm::BitstreamWriter &Stream,
+ PathStorage &PathStore) {
+ std::vector<bool> FileUsedForRecordOrUnit;
+ FileUsedForRecordOrUnit.resize(Files.size());
+
+ Stream.EnterSubblock(UNIT_DEPENDENCIES_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(UNIT_DEPENDENCY));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitDependencyKindBitNum)); // Dependency kind
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // PathIndex (1-based, 0 = none)
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // ModuleIndex (1-based, 0 = none)
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 32)); // time_t
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 16)); // file size
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Name
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ RecordData Record;
+
+ auto addRecordOrUnitData = [&](UnitDependencyKind K, const RecordOrUnitData &Data) {
+ Record.push_back(UNIT_DEPENDENCY);
+ Record.push_back(K);
+ Record.push_back(Data.IsSystem);
+ if (Data.FileIndex != -1) {
+ Record.push_back(PathStore.getPathIndex(Files[Data.FileIndex].File) + 1);
+ FileUsedForRecordOrUnit[Data.FileIndex] = true;
+ } else {
+ Record.push_back(0);
+ }
+ if (Data.ModuleIndex != -1) {
+ Record.push_back(Data.ModuleIndex + 1);
+ } else {
+ Record.push_back(0);
+ }
+ if (Data.FileIndex != -1) {
+ Record.push_back(Files[Data.FileIndex].File->getModificationTime());
+ Record.push_back(Files[Data.FileIndex].File->getSize());
+ } else {
+ Record.push_back(0);
+ Record.push_back(0);
+ }
+ Stream.EmitRecordWithBlob(AbbrevCode, Record, Data.Name);
+ };
+
+ for (auto &ASTData : ASTFileUnits) {
+ Record.clear();
+ addRecordOrUnitData(UNIT_DEPEND_KIND_UNIT, ASTData);
+ }
+ for (auto &recordData : Records) {
+ Record.clear();
+ addRecordOrUnitData(UNIT_DEPEND_KIND_RECORD, recordData);
+ }
+ size_t FileIndex = 0;
+ for (auto &File : Files) {
+ if (FileUsedForRecordOrUnit[FileIndex++])
+ continue;
+ Record.clear();
+ Record.push_back(UNIT_DEPENDENCY);
+ Record.push_back(UNIT_DEPEND_KIND_FILE);
+ Record.push_back(File.IsSystem);
+ Record.push_back(PathStore.getPathIndex(File.File) + 1);
+ if (File.ModuleIndex != -1) {
+ Record.push_back(File.ModuleIndex + 1);
+ } else {
+ Record.push_back(0);
+ }
+ Record.push_back(File.File->getModificationTime());
+ Record.push_back(File.File->getSize());
+ Stream.EmitRecordWithBlob(AbbrevCode, Record, StringRef());
+ }
+
+ Stream.ExitBlock();
+}
+
+void IndexUnitWriter::writeIncludes(llvm::BitstreamWriter &Stream,
+ PathStorage &PathStore) {
+ Stream.EnterSubblock(UNIT_INCLUDES_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(UNIT_INCLUDE));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // source path index (1-based, 0 = no path)
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 12)); // source include line
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // target path index (1-based, 0 = no path)
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ RecordData Record;
+
+ for (auto &Including : Files) {
+ for(auto &Included: Including.Includes) {
+ Record.clear();
+ Record.push_back(UNIT_INCLUDE);
+ Record.push_back(PathStore.getPathIndex(Including.File) + 1);
+ Record.push_back(Included.Line);
+ Record.push_back(PathStore.getPathIndex(Files[Included.Index].File) + 1);
+ Stream.EmitRecordWithAbbrev(AbbrevCode, Record);
+ }
+ }
+ Stream.ExitBlock();
+}
+
+void IndexUnitWriter::writePaths(llvm::BitstreamWriter &Stream,
+ PathStorage &PathStore) {
+ Stream.EnterSubblock(UNIT_PATHS_BLOCK_ID, 3);
+
+ auto PathAbbrev = std::make_shared<BitCodeAbbrev>();
+ PathAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH));
+ PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, UnitFilePathPrefixKindBitNum)); // Path prefix kind
+ PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // DirPath offset
+ PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // DirPath size
+ PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 10)); // Filename offset
+ PathAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Filename size
+ unsigned PathAbbrevCode = Stream.EmitAbbrev(std::move(PathAbbrev));
+
+ auto PathBufferAbbrev = std::make_shared<BitCodeAbbrev>();
+ PathBufferAbbrev->Add(BitCodeAbbrevOp(UNIT_PATH_BUFFER));
+ PathBufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Paths buffer
+ unsigned PathBufferAbbrevCode = Stream.EmitAbbrev(PathBufferAbbrev);
+
+ RecordData Record;
+ for(auto &BitPath: PathStore.getBitPaths()) {
+ Record.push_back(UNIT_PATH);
+ Record.push_back(BitPath.PrefixKind);
+ Record.push_back(BitPath.Dir.Offset);
+ Record.push_back(BitPath.Dir.Size);
+ Record.push_back(BitPath.Filename.Offset);
+ Record.push_back(BitPath.Filename.Size);
+ Stream.EmitRecordWithAbbrev(PathAbbrevCode, Record);
+ Record.clear();
+ }
+
+ Record.push_back(UNIT_PATH_BUFFER);
+ Stream.EmitRecordWithBlob(PathBufferAbbrevCode, Record, PathStore.getPathsBuffer());
+
+ Stream.ExitBlock();
+}
+
+void IndexUnitWriter::writeModules(llvm::BitstreamWriter &Stream) {
+ Stream.EnterSubblock(UNIT_MODULES_BLOCK_ID, 3);
+
+ auto Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(UNIT_MODULE));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 9)); // Module name offset
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Module name size
+ unsigned AbbrevCode = Stream.EmitAbbrev(std::move(Abbrev));
+
+ auto BufferAbbrev = std::make_shared<BitCodeAbbrev>();
+ BufferAbbrev->Add(BitCodeAbbrevOp(UNIT_MODULE_BUFFER));
+ BufferAbbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Module names buffer
+ unsigned BufferAbbrevCode = Stream.EmitAbbrev(BufferAbbrev);
+
+ SmallString<512> ModuleNamesBuf;
+
+ RecordData Record;
+ for (auto &Mod : Modules) {
+ SmallString<64> ModuleName;
+ StringRef name = GetInfoForModuleFn(Mod, ModuleName).Name;
+ size_t offset = ModuleNamesBuf.size();
+ ModuleNamesBuf += name;
+
+ Record.push_back(UNIT_MODULE);
+ Record.push_back(offset);
+ Record.push_back(name.size());
+ Stream.EmitRecordWithAbbrev(AbbrevCode, Record);
+ Record.clear();
+ }
+
+ Record.push_back(UNIT_MODULE_BUFFER);
+ Stream.EmitRecordWithBlob(BufferAbbrevCode, Record, ModuleNamesBuf.str());
+
+ Stream.ExitBlock();
+}
+
+bool IndexUnitWriter::initIndexDirectory(StringRef StorePath,
+ std::string &Error) {
+ using namespace llvm::sys;
+ SmallString<128> SubPath = StorePath;
+ store::appendRecordSubDir(SubPath);
+ std::error_code EC = fs::create_directories(SubPath);
+ if (EC) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to create directory '" << SubPath << "': " << EC.message();
+ return true;
+ }
+
+ SubPath = StorePath;
+ store::appendUnitSubDir(SubPath);
+ EC = fs::create_directory(SubPath);
+ if (EC) {
+ llvm::raw_string_ostream Err(Error);
+ Err << "failed to create directory '" << SubPath << "': " << EC.message();
+ return true;
+ }
+
+ return false;
+}
diff --git a/lib/Index/IndexingAction.cpp b/lib/Index/IndexingAction.cpp
index 84d3120..14263b1 100644
--- a/lib/Index/IndexingAction.cpp
+++ b/lib/Index/IndexingAction.cpp
@@ -9,9 +9,16 @@
#include "clang/Index/IndexingAction.h"
#include "clang/Index/IndexDataConsumer.h"
+#include "FileIndexRecord.h"
#include "IndexingContext.h"
+#include "ClangIndexRecordWriter.h"
+#include "IndexDataStoreUtils.h"
+#include "clang/Index/IndexUnitWriter.h"
+#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/Utils.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Serialization/ASTReader.h"
@@ -80,7 +87,8 @@
: DataConsumer(std::move(dataConsumer)),
IndexCtx(Opts, *DataConsumer) {}
- std::unique_ptr<IndexASTConsumer> createIndexASTConsumer() {
+ std::unique_ptr<IndexASTConsumer> createIndexASTConsumer(CompilerInstance &CI) {
+ IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot);
return llvm::make_unique<IndexASTConsumer>(IndexCtx);
}
@@ -98,7 +106,7 @@
protected:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
StringRef InFile) override {
- return createIndexASTConsumer();
+ return createIndexASTConsumer(CI);
}
void EndSourceFileAction() override {
@@ -108,7 +116,7 @@
};
class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase {
- bool IndexActionFailed = false;
+ bool CreatedASTConsumer = false;
public:
WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction,
@@ -128,21 +136,20 @@
void WrappingIndexAction::EndSourceFileAction() {
// Invoke wrapped action's method.
WrapperFrontendAction::EndSourceFileAction();
- if (!IndexActionFailed)
+ if (CreatedASTConsumer)
finish();
}
std::unique_ptr<ASTConsumer>
WrappingIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
- if (!OtherConsumer) {
- IndexActionFailed = true;
+ if (!OtherConsumer)
return nullptr;
- }
+ CreatedASTConsumer = true;
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Consumers.push_back(std::move(OtherConsumer));
- Consumers.push_back(createIndexASTConsumer());
+ Consumers.push_back(createIndexASTConsumer(CI));
return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
}
@@ -203,3 +210,628 @@
}
DataConsumer->finish();
}
+
+//===----------------------------------------------------------------------===//
+// Index Data Recording
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class IndexDataRecorder : public IndexDataConsumer {
+ IndexingContext *IndexCtx = nullptr;
+ const Preprocessor *PP = nullptr;
+ typedef llvm::DenseMap<FileID, std::unique_ptr<FileIndexRecord>> RecordByFileTy;
+ RecordByFileTy RecordByFile;
+
+public:
+ void init(IndexingContext *idxCtx, const CompilerInstance &CI) {
+ IndexCtx = idxCtx;
+ PP = &CI.getPreprocessor();
+ initialize(CI.getASTContext());
+ }
+
+ RecordByFileTy::const_iterator record_begin() const { return RecordByFile.begin(); }
+ RecordByFileTy::const_iterator record_end() const { return RecordByFile.end(); }
+ bool record_empty() const { return RecordByFile.empty(); }
+
+private:
+ bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles,
+ ArrayRef<SymbolRelation> Relations,
+ FileID FID, unsigned Offset,
+ ASTNodeInfo ASTNode) override {
+ // Ignore the predefines buffer.
+ if (FID == PP->getPredefinesFileID())
+ return true;
+
+ FileIndexRecord &Rec = getFileIndexRecord(FID);
+ Rec.addDeclOccurence(Roles, Offset, D, Relations);
+ return true;
+ }
+
+ FileIndexRecord &getFileIndexRecord(FileID FID) {
+ auto &Entry = RecordByFile[FID];
+ if (!Entry) {
+ Entry.reset(new FileIndexRecord(FID, IndexCtx->isSystemFile(FID)));
+ }
+ return *Entry;
+ }
+};
+
+struct IncludeLocation {
+ const FileEntry *Source;
+ const FileEntry *Target;
+ unsigned Line;
+};
+
+class IncludePPCallbacks : public PPCallbacks {
+ IndexingContext &IndexCtx;
+ RecordingOptions RecordOpts;
+ std::vector<IncludeLocation> &Includes;
+ SourceManager &SourceMgr;
+
+public:
+ IncludePPCallbacks(IndexingContext &indexCtx, RecordingOptions recordOpts,
+ std::vector<IncludeLocation> &IncludesForFile,
+ SourceManager &SourceMgr) :
+ IndexCtx(indexCtx), RecordOpts(recordOpts),
+ Includes(IncludesForFile), SourceMgr(SourceMgr) {}
+
+private:
+ void addInclude(SourceLocation From, const FileEntry *To) {
+ assert(To);
+ if (RecordOpts.RecordIncludes == RecordingOptions::IncludesRecordingKind::None)
+ return;
+
+ std::pair<FileID, unsigned> LocInfo = SourceMgr.getDecomposedExpansionLoc(From);
+ switch (RecordOpts.RecordIncludes) {
+ case RecordingOptions::IncludesRecordingKind::None:
+ llvm_unreachable("should have already checked in the beginning");
+ case RecordingOptions::IncludesRecordingKind::UserOnly:
+ if (IndexCtx.isSystemFile(LocInfo.first))
+ return; // Ignore includes of system headers.
+ break;
+ case RecordingOptions::IncludesRecordingKind::All:
+ break;
+ }
+ auto *FE = SourceMgr.getFileEntryForID(LocInfo.first);
+ if (!FE)
+ return;
+ auto lineNo = SourceMgr.getLineNumber(LocInfo.first, LocInfo.second);
+ Includes.push_back({FE, To, lineNo});
+ }
+
+ virtual void InclusionDirective(SourceLocation HashLoc,
+ const Token &IncludeTok,
+ StringRef FileName,
+ bool IsAngled,
+ CharSourceRange FilenameRange,
+ const FileEntry *File,
+ StringRef SearchPath,
+ StringRef RelativePath,
+ const Module *Imported) override {
+ if (HashLoc.isFileID() && File && File->isValid())
+ addInclude(HashLoc, File);
+ }
+};
+
+class IndexDependencyProvider {
+public:
+ virtual ~IndexDependencyProvider() {}
+
+ virtual void visitFileDependencies(const CompilerInstance &CI,
+ llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) = 0;
+ virtual void visitIncludes(
+ llvm::function_ref<void(const FileEntry *Source, unsigned Line,
+ const FileEntry *Target)> visitor) = 0;
+ virtual void visitModuleImports(const CompilerInstance &CI,
+ llvm::function_ref<void(serialization::ModuleFile &Mod,
+ bool isSystem)> visitor) = 0;
+};
+
+class SourceFilesIndexDependencyCollector : public DependencyCollector, public IndexDependencyProvider {
+ IndexingContext &IndexCtx;
+ RecordingOptions RecordOpts;
+ llvm::SetVector<const FileEntry *> Entries;
+ llvm::BitVector IsSystemByUID;
+ std::vector<IncludeLocation> Includes;
+ SourceManager *SourceMgr = nullptr;
+ std::string SysrootPath;
+
+public:
+ SourceFilesIndexDependencyCollector(IndexingContext &indexCtx, RecordingOptions recordOpts)
+ : IndexCtx(indexCtx), RecordOpts(recordOpts) {}
+
+ virtual void attachToPreprocessor(Preprocessor &PP) override {
+ DependencyCollector::attachToPreprocessor(PP);
+ PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>(IndexCtx,
+ RecordOpts,
+ Includes,
+ PP.getSourceManager()));
+ }
+
+ void setSourceManager(SourceManager *SourceMgr) {
+ this->SourceMgr = SourceMgr;
+ }
+ void setSysrootPath(StringRef sysroot) { SysrootPath = sysroot; }
+
+ void visitFileDependencies(const CompilerInstance &CI,
+ llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override {
+ for (auto *FE : getEntries()) {
+ visitor(FE, isSystemFile(FE));
+ }
+ }
+
+ void visitIncludes(
+ llvm::function_ref<void(const FileEntry *Source, unsigned Line,
+ const FileEntry *Target)> visitor) override {
+ for (auto &Include : Includes) {
+ visitor(Include.Source, Include.Line, Include.Target);
+ }
+ }
+
+ void visitModuleImports(const CompilerInstance &CI,
+ llvm::function_ref<void(serialization::ModuleFile &Mod,
+ bool isSystem)> visitor) override {
+ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+
+ if (auto Reader = CI.getModuleManager()) {
+ Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool {
+ bool isSystemMod = false;
+ if (Mod.isModule()) {
+ if (auto *M = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false))
+ isSystemMod = M->IsSystem;
+ }
+ if (!isSystemMod || needSystemDependencies())
+ visitor(Mod, isSystemMod);
+ return true; // skip module dependencies.
+ });
+ }
+ }
+
+private:
+ bool isSystemFile(const FileEntry *FE) {
+ auto UID = FE->getUID();
+ return IsSystemByUID.size() > UID && IsSystemByUID[UID];
+ }
+
+ ArrayRef<const FileEntry *> getEntries() const {
+ return Entries.getArrayRef();
+ }
+
+ bool needSystemDependencies() override {
+ return RecordOpts.RecordSystemDependencies;
+ }
+
+ bool sawDependency(StringRef Filename, bool FromModule,
+ bool IsSystem, bool IsModuleFile, bool IsMissing) override {
+ bool sawIt = DependencyCollector::sawDependency(Filename, FromModule,
+ IsSystem, IsModuleFile,
+ IsMissing);
+ if (auto *FE = SourceMgr->getFileManager().getFile(Filename)) {
+ if (sawIt)
+ Entries.insert(FE);
+ // Record system-ness for all files that we pass through.
+ if (IsSystemByUID.size() < FE->getUID()+1)
+ IsSystemByUID.resize(FE->getUID()+1);
+ IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename);
+ }
+ return sawIt;
+ }
+
+ bool isInSysroot(StringRef Filename) {
+ return !SysrootPath.empty() && Filename.startswith(SysrootPath);
+ }
+};
+
+class IndexRecordActionBase {
+protected:
+ RecordingOptions RecordOpts;
+ IndexDataRecorder Recorder;
+ IndexingContext IndexCtx;
+ SourceFilesIndexDependencyCollector DepCollector;
+
+ IndexRecordActionBase(IndexingOptions IndexOpts, RecordingOptions recordOpts)
+ : RecordOpts(std::move(recordOpts)),
+ IndexCtx(IndexOpts, Recorder),
+ DepCollector(IndexCtx, RecordOpts) {
+ }
+
+ std::unique_ptr<IndexASTConsumer>
+ createIndexASTConsumer(CompilerInstance &CI) {
+ IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot);
+ Recorder.init(&IndexCtx, CI);
+
+ Preprocessor &PP = CI.getPreprocessor();
+ DepCollector.setSourceManager(&CI.getSourceManager());
+ DepCollector.setSysrootPath(IndexCtx.getSysrootPath());
+ DepCollector.attachToPreprocessor(PP);
+
+ return llvm::make_unique<IndexASTConsumer>(IndexCtx);
+ }
+
+ void finish(CompilerInstance &CI);
+};
+
+class IndexRecordAction : public ASTFrontendAction, IndexRecordActionBase {
+public:
+ IndexRecordAction(IndexingOptions IndexOpts, RecordingOptions RecordOpts)
+ : IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {}
+
+protected:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ return createIndexASTConsumer(CI);
+ }
+
+ void EndSourceFileAction() override {
+ FrontendAction::EndSourceFileAction();
+ finish(getCompilerInstance());
+ }
+};
+
+class WrappingIndexRecordAction : public WrapperFrontendAction, IndexRecordActionBase {
+ bool CreatedASTConsumer = false;
+
+public:
+ WrappingIndexRecordAction(std::unique_ptr<FrontendAction> WrappedAction,
+ IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts)
+ : WrapperFrontendAction(std::move(WrappedAction)),
+ IndexRecordActionBase(std::move(IndexOpts), std::move(RecordOpts)) {}
+
+protected:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) override {
+ auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+ if (!OtherConsumer)
+ return nullptr;
+
+ CreatedASTConsumer = true;
+ std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+ Consumers.push_back(std::move(OtherConsumer));
+ Consumers.push_back(createIndexASTConsumer(CI));
+ return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
+ }
+
+ void EndSourceFileAction() override {
+ // Invoke wrapped action's method.
+ WrapperFrontendAction::EndSourceFileAction();
+ if (CreatedASTConsumer)
+ finish(getCompilerInstance());
+ }
+};
+
+} // anonymous namespace
+
+static std::string getClangVersion() {
+ // Try picking the version from an Apple Clang tag.
+ std::string RepositoryPath = getClangRepositoryPath();
+ StringRef BuildNumber = StringRef(RepositoryPath);
+ size_t DashOffset = BuildNumber.find('-');
+ if (BuildNumber.startswith("clang") && DashOffset != StringRef::npos) {
+ BuildNumber = BuildNumber.substr(DashOffset + 1);
+ return BuildNumber;
+ }
+ // Fallback to the generic version.
+ return CLANG_VERSION_STRING;
+}
+
+static void writeUnitData(const CompilerInstance &CI,
+ IndexDataRecorder &Recorder,
+ IndexDependencyProvider &DepProvider,
+ IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts,
+ StringRef OutputFile,
+ const FileEntry *RootFile,
+ Module *UnitModule,
+ StringRef SysrootPath);
+
+void IndexRecordActionBase::finish(CompilerInstance &CI) {
+ // We may emit more diagnostics so do the begin/end source file invocations
+ // on the diagnostic client.
+ // FIXME: FrontendAction::EndSourceFile() should probably not call
+ // CI.getDiagnosticClient().EndSourceFile()' until after it has called
+ // 'EndSourceFileAction()', so that code executing during EndSourceFileAction()
+ // can emit diagnostics. If this is fixed, DiagClientBeginEndRAII can go away.
+ struct DiagClientBeginEndRAII {
+ CompilerInstance &CI;
+ DiagClientBeginEndRAII(CompilerInstance &CI) : CI(CI) {
+ CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts());
+ }
+ ~DiagClientBeginEndRAII() {
+ CI.getDiagnosticClient().EndSourceFile();
+ }
+ } diagClientBeginEndRAII(CI);
+
+ SourceManager &SM = CI.getSourceManager();
+ DiagnosticsEngine &Diag = CI.getDiagnostics();
+ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+ StringRef DataPath = RecordOpts.DataDirPath;
+
+ std::string Error;
+ if (IndexUnitWriter::initIndexDirectory(DataPath, Error)) {
+ unsigned DiagID = Diag.getCustomDiagID(
+ DiagnosticsEngine::Error, "failed creating index directory %0");
+ Diag.Report(DiagID) << Error;
+ return;
+ }
+
+ std::string OutputFile = CI.getFrontendOpts().OutputFile;
+ if (OutputFile.empty()) {
+ OutputFile = CI.getFrontendOpts().Inputs[0].getFile();
+ OutputFile += ".o";
+ }
+
+ const FileEntry *RootFile = nullptr;
+ Module *UnitMod = nullptr;
+ bool isModuleGeneration = CI.getLangOpts().isCompilingModule();
+ if (!isModuleGeneration &&
+ CI.getFrontendOpts().ProgramAction != frontend::GeneratePCH) {
+ RootFile = SM.getFileEntryForID(SM.getMainFileID());
+ }
+ if (isModuleGeneration) {
+ UnitMod = HS.lookupModule(CI.getLangOpts().CurrentModule,
+ /*AllowSearch=*/false);
+ }
+
+ writeUnitData(CI, Recorder, DepCollector, IndexCtx.getIndexOpts(), RecordOpts,
+ OutputFile, RootFile, UnitMod,
+ IndexCtx.getSysrootPath());
+}
+
+/// Checks if the unit file exists for module file, if it doesn't it generates
+/// index data for it.
+static bool produceIndexDataForModuleFile(
+ serialization::ModuleFile &Mod,
+ const CompilerInstance &CI,
+ IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts,
+ IndexUnitWriter &ParentUnitWriter);
+
+static void writeUnitData(const CompilerInstance &CI,
+ IndexDataRecorder &Recorder,
+ IndexDependencyProvider &DepProvider,
+ IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts,
+ StringRef OutputFile,
+ const FileEntry *RootFile,
+ Module *UnitModule,
+ StringRef SysrootPath) {
+
+ SourceManager &SM = CI.getSourceManager();
+ DiagnosticsEngine &Diag = CI.getDiagnostics();
+ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+ StringRef DataPath = RecordOpts.DataDirPath;
+ bool IsSystemUnit = UnitModule ? UnitModule->IsSystem : false;
+ bool IsModuleUnit = UnitModule != nullptr;
+ bool IsDebugCompilation = CI.getCodeGenOpts().OptimizationLevel == 0;
+ std::string ModuleName = UnitModule ? UnitModule->getFullModuleName() : std::string();
+
+ auto getModuleInfo = [](writer::OpaqueModule mod, SmallVectorImpl<char> &Scratch) -> writer::ModuleInfo {
+ assert(mod);
+ writer::ModuleInfo info;
+ std::string fullName = static_cast<const Module*>(mod)->getFullModuleName();
+ unsigned offset = Scratch.size();
+ Scratch.append(fullName.begin(), fullName.end());
+ info.Name = StringRef(Scratch.data()+offset, fullName.size());
+ return info;
+ };
+
+ auto findModuleForHeader = [&](const FileEntry *FE) -> Module * {
+ if (!UnitModule)
+ return nullptr;
+ if (auto Mod = HS.findModuleForHeader(FE).getModule())
+ if (Mod->isSubModuleOf(UnitModule))
+ return Mod;
+ return nullptr;
+ };
+
+ IndexUnitWriter UnitWriter(CI.getFileManager(),
+ DataPath,
+ "clang", getClangVersion(),
+ OutputFile,
+ ModuleName,
+ RootFile,
+ IsSystemUnit,
+ IsModuleUnit,
+ IsDebugCompilation,
+ CI.getTargetOpts().Triple,
+ SysrootPath,
+ getModuleInfo);
+
+ DepProvider.visitFileDependencies(CI, [&](const FileEntry *FE, bool isSystemFile) {
+ UnitWriter.addFileDependency(FE, isSystemFile, findModuleForHeader(FE));
+ });
+ DepProvider.visitIncludes([&](const FileEntry *Source, unsigned Line, const FileEntry *Target) {
+ UnitWriter.addInclude(Source, Line, Target);
+ });
+ DepProvider.visitModuleImports(CI, [&](serialization::ModuleFile &Mod, bool isSystemMod) {
+ Module *UnitMod = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false);
+ UnitWriter.addASTFileDependency(Mod.File, isSystemMod, UnitMod);
+ if (Mod.isModule()) {
+ produceIndexDataForModuleFile(Mod, CI, IndexOpts, RecordOpts, UnitWriter);
+ }
+ });
+
+ ClangIndexRecordWriter RecordWriter(CI.getASTContext(), RecordOpts);
+ for (auto I = Recorder.record_begin(), E = Recorder.record_end(); I != E; ++I) {
+ FileID FID = I->first;
+ const FileIndexRecord &Rec = *I->second;
+ const FileEntry *FE = SM.getFileEntryForID(FID);
+ std::string RecordFile;
+ std::string Error;
+
+ if (RecordWriter.writeRecord(FE->getName(), Rec, Error, &RecordFile)) {
+ unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error,
+ "failed writing record '%0': %1");
+ Diag.Report(DiagID) << RecordFile << Error;
+ return;
+ }
+ UnitWriter.addRecordFile(RecordFile, FE, Rec.isSystem(),
+ findModuleForHeader(FE));
+ }
+
+ std::string Error;
+ if (UnitWriter.write(Error)) {
+ unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error,
+ "failed writing unit data: %0");
+ Diag.Report(DiagID) << Error;
+ return;
+ }
+}
+
+namespace {
+class ModuleFileIndexDependencyCollector : public IndexDependencyProvider {
+ serialization::ModuleFile &ModFile;
+ RecordingOptions RecordOpts;
+
+public:
+ ModuleFileIndexDependencyCollector(serialization::ModuleFile &Mod,
+ RecordingOptions recordOpts)
+ : ModFile(Mod), RecordOpts(recordOpts) {}
+
+ void visitFileDependencies(const CompilerInstance &CI,
+ llvm::function_ref<void(const FileEntry *FE, bool isSystem)> visitor) override {
+ auto Reader = CI.getModuleManager();
+ Reader->visitInputFiles(ModFile, RecordOpts.RecordSystemDependencies,
+ /*Complain=*/false,
+ [&](const serialization::InputFile &IF, bool isSystem) {
+ auto *FE = IF.getFile();
+ if (!FE)
+ return;
+ // Ignore module map files, they are not as important to track as source
+ // files and they may be auto-generated which would create an undesirable
+ // dependency on an intermediate build byproduct.
+ if (FE->getName().endswith("module.modulemap"))
+ return;
+
+ visitor(FE, isSystem);
+ });
+ }
+
+ void visitIncludes(
+ llvm::function_ref<void(const FileEntry *Source, unsigned Line,
+ const FileEntry *Target)> visitor) override {
+ // FIXME: Module files without a preprocessing record do not have info about
+ // include locations. Serialize enough data to be able to retrieve such info.
+ }
+
+ void visitModuleImports(const CompilerInstance &CI,
+ llvm::function_ref<void(serialization::ModuleFile &Mod,
+ bool isSystem)> visitor) override {
+ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+ for (auto *Mod : ModFile.Imports) {
+ bool isSystemMod = false;
+ if (auto *M = HS.lookupModule(Mod->ModuleName, /*AllowSearch=*/false))
+ isSystemMod = M->IsSystem;
+ if (!isSystemMod || RecordOpts.RecordSystemDependencies)
+ visitor(*Mod, isSystemMod);
+ }
+ }
+};
+} // anonymous namespace.
+
+static void indexModule(serialization::ModuleFile &Mod,
+ const CompilerInstance &CI,
+ IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts) {
+ DiagnosticsEngine &Diag = CI.getDiagnostics();
+ Diag.Report(Mod.ImportLoc, diag::remark_index_producing_module_file_data)
+ << Mod.FileName;
+
+ StringRef SysrootPath = CI.getHeaderSearchOpts().Sysroot;
+ HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+ Module *UnitMod = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false);
+
+ IndexDataRecorder Recorder;
+ IndexingContext IndexCtx(IndexOpts, Recorder);
+
+ IndexCtx.setASTContext(CI.getASTContext());
+ IndexCtx.setSysrootPath(SysrootPath);
+ Recorder.init(&IndexCtx, CI);
+
+ for (const Decl *D : CI.getModuleManager()->getModuleFileLevelDecls(Mod)) {
+ IndexCtx.indexTopLevelDecl(D);
+ }
+ Recorder.finish();
+
+ ModuleFileIndexDependencyCollector DepCollector(Mod, RecordOpts);
+ writeUnitData(CI, Recorder, DepCollector, IndexOpts, RecordOpts,
+ Mod.FileName, /*RootFile=*/nullptr, UnitMod, SysrootPath);
+
+}
+
+static bool produceIndexDataForModuleFile(
+ serialization::ModuleFile &Mod,
+ const CompilerInstance &CI,
+ IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts,
+ IndexUnitWriter &ParentUnitWriter) {
+ DiagnosticsEngine &Diag = CI.getDiagnostics();
+ std::string Error;
+ // We don't do timestamp check with the PCM file, on purpose. The PCM may get
+ // touched for various reasons which would cause unnecessary work to emit
+ // index data. User modules normally will get rebuilt and their index data
+ // re-emitted, and system modules are generally stable (and they can also can
+ // get rebuilt along with their index data).
+ auto IsUptodateOpt = ParentUnitWriter.isUnitUpToDateForOutputFile(Mod.FileName, None, Error);
+ if (!IsUptodateOpt.hasValue()) {
+ unsigned DiagID = Diag.getCustomDiagID(DiagnosticsEngine::Error,
+ "failed file status check: %0");
+ Diag.Report(DiagID) << Error;
+ return false;
+ }
+ if (*IsUptodateOpt)
+ return false;
+
+ indexModule(Mod, CI, IndexOpts, RecordOpts);
+ return true;
+}
+
+static std::unique_ptr<FrontendAction>
+createIndexDataRecordingAction(IndexingOptions IndexOpts,
+ RecordingOptions RecordOpts,
+ std::unique_ptr<FrontendAction> WrappedAction) {
+ if (WrappedAction)
+ return llvm::make_unique<WrappingIndexRecordAction>(std::move(WrappedAction),
+ std::move(IndexOpts),
+ std::move(RecordOpts));
+ return llvm::make_unique<IndexRecordAction>(std::move(IndexOpts),
+ std::move(RecordOpts));
+}
+
+static std::pair<IndexingOptions, RecordingOptions>
+getIndexOptionsFromFrontendOptions(const FrontendOptions &FEOpts) {
+ index::IndexingOptions IndexOpts;
+ index::RecordingOptions RecordOpts;
+ RecordOpts.DataDirPath = FEOpts.IndexStorePath;
+ if (FEOpts.IndexIgnoreSystemSymbols) {
+ IndexOpts.SystemSymbolFilter =
+ index::IndexingOptions::SystemSymbolFilterKind::None;
+ }
+ RecordOpts.RecordSymbolCodeGenName = FEOpts.IndexRecordCodegenName;
+ return { IndexOpts, RecordOpts };
+}
+
+std::unique_ptr<FrontendAction>
+index::createIndexDataRecordingAction(const FrontendOptions &FEOpts,
+ std::unique_ptr<FrontendAction> WrappedAction) {
+ index::IndexingOptions IndexOpts;
+ index::RecordingOptions RecordOpts;
+ std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(FEOpts);
+ return ::createIndexDataRecordingAction(IndexOpts, RecordOpts,
+ std::move(WrappedAction));
+}
+
+bool index::emitIndexDataForModuleFile(const Module *Mod,
+ const CompilerInstance &CI,
+ IndexUnitWriter &ParentUnitWriter) {
+ index::IndexingOptions IndexOpts;
+ index::RecordingOptions RecordOpts;
+ std::tie(IndexOpts, RecordOpts) = getIndexOptionsFromFrontendOptions(CI.getFrontendOpts());
+
+ auto astReader = CI.getModuleManager();
+ serialization::ModuleFile *ModFile = astReader->getModuleManager().lookup(Mod->getASTFile());
+ assert(ModFile && "no module file loaded for module ?");
+ return produceIndexDataForModuleFile(*ModFile, CI, IndexOpts, RecordOpts, ParentUnitWriter);
+}
diff --git a/lib/Index/IndexingContext.cpp b/lib/Index/IndexingContext.cpp
index addee69..c1ce8bc 100644
--- a/lib/Index/IndexingContext.cpp
+++ b/lib/Index/IndexingContext.cpp
@@ -93,12 +93,7 @@
if (FID.isInvalid())
return true;
- bool Invalid = false;
- const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
- if (Invalid || !SEntry.isFile())
- return true;
-
- if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
+ if (isSystemFile(FID)) {
switch (IndexOpts.SystemSymbolFilter) {
case IndexingOptions::SystemSymbolFilterKind::None:
return true;
@@ -161,6 +156,56 @@
return true;
}
+void IndexingContext::setSysrootPath(StringRef path) {
+ // Ignore sysroot path if it points to root, otherwise every header will be
+ // treated as system one.
+ if (path == "/")
+ path = StringRef();
+ SysrootPath = path;
+}
+
+bool IndexingContext::isSystemFile(FileID FID) {
+ if (LastFileCheck.first == FID)
+ return LastFileCheck.second;
+
+ auto result = [&](bool res) -> bool {
+ LastFileCheck = { FID, res };
+ return res;
+ };
+
+ bool Invalid = false;
+ const SrcMgr::SLocEntry &SEntry =
+ Ctx->getSourceManager().getSLocEntry(FID, &Invalid);
+ if (Invalid || !SEntry.isFile())
+ return result(false);
+
+ const SrcMgr::FileInfo &FI = SEntry.getFile();
+ if (FI.getFileCharacteristic() != SrcMgr::C_User)
+ return result(true);
+
+ auto *CC = FI.getContentCache();
+ if (!CC)
+ return result(false);
+ auto *FE = CC->OrigEntry;
+ if (!FE)
+ return result(false);
+
+ if (SysrootPath.empty())
+ return result(false);
+
+ // Check if directory is in sysroot so that we can consider system headers
+ // even the headers found via a user framework search path, pointing inside
+ // sysroot.
+ auto dirEntry = FE->getDir();
+ auto pair = DirEntries.insert(std::make_pair(dirEntry, false));
+ bool &isSystemDir = pair.first->second;
+ bool wasInserted = pair.second;
+ if (wasInserted) {
+ isSystemDir = StringRef(dirEntry->getName()).startswith(SysrootPath);
+ }
+ return result(isSystemDir);
+}
+
static const CXXRecordDecl *
getDeclContextForTemplateInstationPattern(const Decl *D) {
if (const auto *CTSD =
@@ -231,8 +276,8 @@
/// Whether the given NamedDecl should be skipped because it has no name.
static bool shouldSkipNamelessDecl(const NamedDecl *ND) {
- return ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) &&
- !isa<ObjCCategoryDecl>(ND);
+ return (ND->getDeclName().isEmpty() && !isa<TagDecl>(ND) &&
+ !isa<ObjCCategoryDecl>(ND)) || isa<CXXDeductionGuideDecl>(ND);
}
static const Decl *adjustParent(const Decl *Parent) {
@@ -321,7 +366,7 @@
const Expr *OrigE,
const Decl *OrigD,
const DeclContext *ContainerDC) {
- if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
+ if (D->isImplicit() && !(isa<ObjCMethodDecl>(D) || isa<ObjCIvarDecl>(D)))
return true;
if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D)))
return true;
@@ -337,12 +382,7 @@
if (FID.isInvalid())
return true;
- bool Invalid = false;
- const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
- if (Invalid || !SEntry.isFile())
- return true;
-
- if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
+ if (isSystemFile(FID)) {
switch (IndexOpts.SystemSymbolFilter) {
case IndexingOptions::SystemSymbolFilterKind::None:
return true;
diff --git a/lib/Index/IndexingContext.h b/lib/Index/IndexingContext.h
index 566651c..70e72e1 100644
--- a/lib/Index/IndexingContext.h
+++ b/lib/Index/IndexingContext.h
@@ -11,9 +11,11 @@
#define LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H
#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
#include "clang/Index/IndexSymbol.h"
#include "clang/Index/IndexingAction.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
namespace clang {
class ASTContext;
@@ -29,7 +31,7 @@
class Stmt;
class Expr;
class TypeLoc;
- class SourceLocation;
+ class DirectoryEntry;
namespace index {
class IndexDataConsumer;
@@ -38,6 +40,13 @@
IndexingOptions IndexOpts;
IndexDataConsumer &DataConsumer;
ASTContext *Ctx = nullptr;
+ std::string SysrootPath;
+
+ // Records whether a directory entry is system or not.
+ llvm::DenseMap<const DirectoryEntry *, bool> DirEntries;
+ // Keeps track of the last check for whether a FileID is system or
+ // not. This is used to speed up isSystemFile() call.
+ std::pair<FileID, bool> LastFileCheck;
public:
IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer)
@@ -47,6 +56,10 @@
IndexDataConsumer &getDataConsumer() { return DataConsumer; }
void setASTContext(ASTContext &ctx) { Ctx = &ctx; }
+ void setSysrootPath(StringRef path);
+ StringRef getSysrootPath() const { return SysrootPath; }
+
+ bool isSystemFile(FileID FID);
bool shouldIndex(const Decl *D);
diff --git a/lib/Index/USRGeneration.cpp b/lib/Index/USRGeneration.cpp
index 21054b0..c4fe737 100644
--- a/lib/Index/USRGeneration.cpp
+++ b/lib/Index/USRGeneration.cpp
@@ -99,6 +99,8 @@
void VisitVarDecl(const VarDecl *D);
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D);
void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D);
+ void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D);
+ void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D);
void VisitLinkageSpecDecl(const LinkageSpecDecl *D) {
IgnoreResults = true;
@@ -112,14 +114,6 @@
IgnoreResults = true;
}
- void VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) {
- IgnoreResults = true;
- }
-
- void VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) {
- IgnoreResults = true;
- }
-
bool ShouldGenerateLocation(const NamedDecl *D);
bool isLocal(const NamedDecl *D) {
@@ -609,6 +603,16 @@
return IgnoreResults;
}
+static void printQualifier(llvm::raw_ostream &Out, ASTContext &Ctx, NestedNameSpecifier *NNS) {
+ // FIXME: Encode the qualifier, don't just print it.
+ PrintingPolicy PO(Ctx.getLangOpts());
+ PO.SuppressTagKeyword = true;
+ PO.SuppressUnwrittenScope = true;
+ PO.ConstantArraySizeAsWritten = false;
+ PO.AnonymousTagLocations = false;
+ NNS->print(Out, PO);
+}
+
void USRGenerator::VisitType(QualType T) {
// This method mangles in USR information for types. It can possibly
// just reuse the naming-mangling logic used by codegen, although the
@@ -797,13 +801,7 @@
}
if (const DependentNameType *DNT = T->getAs<DependentNameType>()) {
Out << '^';
- // FIXME: Encode the qualifier, don't just print it.
- PrintingPolicy PO(Ctx.getLangOpts());
- PO.SuppressTagKeyword = true;
- PO.SuppressUnwrittenScope = true;
- PO.ConstantArraySizeAsWritten = false;
- PO.AnonymousTagLocations = false;
- DNT->getQualifier()->print(Out, PO);
+ printQualifier(Out, Ctx, DNT->getQualifier());
Out << ':' << DNT->getIdentifier()->getName();
return;
}
@@ -912,6 +910,26 @@
}
}
+void USRGenerator::VisitUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) {
+ if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D)))
+ return;
+ VisitDeclContext(D->getDeclContext());
+ Out << "@UUV@";
+ printQualifier(Out, D->getASTContext(), D->getQualifier());
+ EmitDeclName(D);
+}
+
+void USRGenerator::VisitUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) {
+ if (ShouldGenerateLocation(D) && GenLoc(D, /*IncludeOffset=*/isLocal(D)))
+ return;
+ VisitDeclContext(D->getDeclContext());
+ Out << "@UUT@";
+ printQualifier(Out, D->getASTContext(), D->getQualifier());
+ Out << D->getName(); // Simple name.
+}
+
+
+
//===----------------------------------------------------------------------===//
// USR generation functions.
//===----------------------------------------------------------------------===//
diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp
index 1ebcc0a..b9fb005 100644
--- a/lib/Lex/HeaderSearch.cpp
+++ b/lib/Lex/HeaderSearch.cpp
@@ -1108,6 +1108,7 @@
// Get information about this file.
HeaderFileInfo &FileInfo = getFileInfo(File);
+ bool isCompilingModule = ModMap.getLangOpts().isCompilingModule();
// FIXME: this is a workaround for the lack of proper modules-aware support
// for #import / #pragma once
@@ -1143,7 +1144,7 @@
// headers find in the wild might rely only on #import and do not contain
// controlling macros, be conservative and only try to enter textual headers
// if such macro is present.
- if (!FileInfo.isModuleHeader &&
+ if ((isCompilingModule || !isImport) && !FileInfo.isModuleHeader &&
FileInfo.getControllingMacro(ExternalLookup))
TryEnterHdr = true;
return TryEnterHdr;
diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp
index 61bcef8..c42443c 100644
--- a/lib/Lex/Lexer.cpp
+++ b/lib/Lex/Lexer.cpp
@@ -1259,6 +1259,35 @@
return TokenLoc.getLocWithOffset(Tok.getLength() + NumWhitespaceChars);
}
+SourceLocation Lexer::findNextTokenLocationAfterTokenAt(
+ SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) {
+ // TODO: Share the code with the function above when upstreaming.
+ if (Loc.isMacroID()) {
+ if (!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
+ return SourceLocation();
+ }
+ Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
+
+ // Break down the source location.
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
+
+ // Try to load the file buffer.
+ bool InvalidTemp = false;
+ StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
+ if (InvalidTemp)
+ return SourceLocation();
+
+ const char *TokenBegin = File.data() + LocInfo.second;
+
+ // Lex from the start of the given location.
+ Lexer lexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
+ TokenBegin, File.end());
+ // Find the token.
+ Token Tok;
+ lexer.LexFromRawLexer(Tok);
+ return Tok.getLocation();
+}
+
/// getCharAndSizeSlow - Peek a single 'character' from the specified buffer,
/// get its size, and return it. This is tricky in several cases:
/// 1. If currently at the start of a trigraph, we warn about the trigraph,
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 40f78ce..8c0da2c 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -742,10 +742,25 @@
if (LangOpts.CurrentModule == Name)
SourceModule = Result;
Modules[Name] = Result;
+ ModuleScopeIDs[Result] = CurrentModuleScopeID;
}
return std::make_pair(Result, true);
}
+Module *ModuleMap::createShadowedModule(StringRef Name, bool IsFramework,
+ Module *ShadowingModule) {
+
+ // Create a new module with this name.
+ Module *Result =
+ new Module(Name, SourceLocation(), /*Parent=*/nullptr, IsFramework,
+ /*IsExplicit=*/false, NumCreatedModules++);
+ Result->ShadowingModule = ShadowingModule;
+ Result->IsAvailable = false;
+ ModuleScopeIDs[Result] = CurrentModuleScopeID;
+
+ return Result;
+}
+
Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
StringRef Name) {
assert(LangOpts.CurrentModule == Name && "module name mismatch");
@@ -888,6 +903,8 @@
Module *Result = new Module(ModuleName, SourceLocation(), Parent,
/*IsFramework=*/true, /*IsExplicit=*/false,
NumCreatedModules++);
+ if (!Parent)
+ ModuleScopeIDs[Result] = CurrentModuleScopeID;
InferredModuleAllowedBy[Result] = ModuleMapFile;
Result->IsInferred = true;
if (!Parent) {
@@ -1186,6 +1203,7 @@
ExcludeKeyword,
ExplicitKeyword,
ExportKeyword,
+ ExportAsKeyword,
ExternKeyword,
FrameworkKeyword,
LinkKeyword,
@@ -1297,6 +1315,7 @@
SourceLocation LeadingLoc);
void parseUmbrellaDirDecl(SourceLocation UmbrellaLoc);
void parseExportDecl();
+ void parseExportAsDecl();
void parseUseDecl();
void parseLinkDecl();
void parseConfigMacros();
@@ -1348,6 +1367,7 @@
.Case("exclude", MMToken::ExcludeKeyword)
.Case("explicit", MMToken::ExplicitKeyword)
.Case("export", MMToken::ExportKeyword)
+ .Case("export_as", MMToken::ExportAsKeyword)
.Case("extern", MMToken::ExternKeyword)
.Case("framework", MMToken::FrameworkKeyword)
.Case("header", MMToken::HeaderKeyword)
@@ -1558,6 +1578,8 @@
AT_extern_c,
/// \brief The 'exhaustive' attribute.
AT_exhaustive,
+ // \brief The 'swift_infer_import_as_member' attribute.
+ AT_swift_infer_import_as_member,
/// \brief The 'no_undeclared_includes' attribute.
AT_no_undeclared_includes
};
@@ -1575,6 +1597,7 @@
/// header-declaration
/// submodule-declaration
/// export-declaration
+/// export-as-declaration
/// link-declaration
///
/// submodule-declaration:
@@ -1694,6 +1717,7 @@
SourceLocation LBraceLoc = consumeToken();
// Determine whether this (sub)module has already been defined.
+ Module *ShadowingModule = nullptr;
if (Module *Existing = Map.lookupModuleQualified(ModuleName, ActiveModule)) {
// We might see a (re)definition of a module that we already have a
// definition for in two cases:
@@ -1719,23 +1743,35 @@
}
return;
}
-
- Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
- << ModuleName;
- Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
-
- // Skip the module definition.
- skipUntil(MMToken::RBrace);
- if (Tok.is(MMToken::RBrace))
- consumeToken();
-
- HadError = true;
- return;
+
+ if (!Existing->Parent && Map.mayShadowNewModule(Existing)) {
+ ShadowingModule = Existing;
+ } else {
+ // This is not a shawdowed module decl, it is an illegal redefinition.
+ Diags.Report(ModuleNameLoc, diag::err_mmap_module_redefinition)
+ << ModuleName;
+ Diags.Report(Existing->DefinitionLoc, diag::note_mmap_prev_definition);
+
+ // Skip the module definition.
+ skipUntil(MMToken::RBrace);
+ if (Tok.is(MMToken::RBrace))
+ consumeToken();
+
+ HadError = true;
+ return;
+ }
}
// Start defining this module.
- ActiveModule = Map.findOrCreateModule(ModuleName, ActiveModule, Framework,
- Explicit).first;
+ if (ShadowingModule) {
+ ActiveModule =
+ Map.createShadowedModule(ModuleName, Framework, ShadowingModule);
+ } else {
+ ActiveModule =
+ Map.findOrCreateModule(ModuleName, ActiveModule, Framework, Explicit)
+ .first;
+ }
+
ActiveModule->DefinitionLoc = ModuleNameLoc;
if (Attrs.IsSystem || IsSystem)
ActiveModule->IsSystem = true;
@@ -1809,6 +1845,10 @@
parseExportDecl();
break;
+ case MMToken::ExportAsKeyword:
+ parseExportAsDecl();
+ break;
+
case MMToken::UseKeyword:
parseUseDecl();
break;
@@ -2269,6 +2309,41 @@
ActiveModule->UnresolvedExports.push_back(Unresolved);
}
+/// \brief Parse a module export_as declaration.
+///
+/// export-as-declaration:
+/// 'export_as' identifier
+void ModuleMapParser::parseExportAsDecl() {
+ assert(Tok.is(MMToken::ExportAsKeyword));
+ consumeToken();
+
+ if (!Tok.is(MMToken::Identifier)) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_module_id);
+ HadError = true;
+ return;
+ }
+
+ if (ActiveModule->Parent) {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_submodule_export_as);
+ consumeToken();
+ return;
+ }
+
+ if (!ActiveModule->ExportAsModule.empty()) {
+ if (ActiveModule->ExportAsModule == Tok.getString()) {
+ Diags.Report(Tok.getLocation(), diag::warn_mmap_redundant_export_as)
+ << ActiveModule->Name << Tok.getString();
+ } else {
+ Diags.Report(Tok.getLocation(), diag::err_mmap_conflicting_export_as)
+ << ActiveModule->Name << ActiveModule->ExportAsModule
+ << Tok.getString();
+ }
+ }
+
+ ActiveModule->ExportAsModule = Tok.getString();
+ consumeToken();
+}
+
/// \brief Parse a module use declaration.
///
/// use-declaration:
@@ -2611,6 +2686,7 @@
.Case("extern_c", AT_extern_c)
.Case("no_undeclared_includes", AT_no_undeclared_includes)
.Case("system", AT_system)
+ .Case("swift_infer_import_as_member", AT_swift_infer_import_as_member)
.Default(AT_unknown);
switch (Attribute) {
case AT_unknown:
@@ -2626,6 +2702,10 @@
Attrs.IsExternC = true;
break;
+ case AT_swift_infer_import_as_member:
+ Attrs.IsSwiftInferImportAsMember = true;
+ break;
+
case AT_exhaustive:
Attrs.IsExhaustive = true;
break;
@@ -2674,6 +2754,7 @@
case MMToken::Exclaim:
case MMToken::ExcludeKeyword:
case MMToken::ExportKeyword:
+ case MMToken::ExportAsKeyword:
case MMToken::HeaderKeyword:
case MMToken::Identifier:
case MMToken::LBrace:
diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp
index b2450f5..946a29d 100644
--- a/lib/Lex/PPDirectives.cpp
+++ b/lib/Lex/PPDirectives.cpp
@@ -78,7 +78,8 @@
}
/// \brief Read and discard all tokens remaining on the current line until
-/// the tok::eod token is found.
+/// the tok::eod token is found. If the discarded tokens are in a skipped range,
+/// complete the range and pass it to the \c SourceRangeSkipped callback.
void Preprocessor::DiscardUntilEndOfDirective() {
Token Tmp;
do {
@@ -349,7 +350,8 @@
/// If ElseOk is true, then \#else directives are ok, if not, then we have
/// already seen one so a \#else directive is a duplicate. When this returns,
/// the caller can lex the first valid token.
-void Preprocessor::SkipExcludedConditionalBlock(SourceLocation IfTokenLoc,
+void Preprocessor::SkipExcludedConditionalBlock(const Token &HashToken,
+ SourceLocation IfTokenLoc,
bool FoundNonSkipPortion,
bool FoundElse,
SourceLocation ElseLoc) {
@@ -557,10 +559,10 @@
// the #if block.
CurPPLexer->LexingRawMode = false;
- if (Callbacks) {
- SourceLocation BeginLoc = ElseLoc.isValid() ? ElseLoc : IfTokenLoc;
- Callbacks->SourceRangeSkipped(SourceRange(BeginLoc, Tok.getLocation()));
- }
+ if (Callbacks)
+ Callbacks->SourceRangeSkipped(
+ SourceRange(HashToken.getLocation(), CurPPLexer->getSourceLocation()),
+ Tok.getLocation());
}
void Preprocessor::PTHSkipExcludedConditionalBlock() {
@@ -948,15 +950,17 @@
default: break;
// C99 6.10.1 - Conditional Inclusion.
case tok::pp_if:
- return HandleIfDirective(Result, ReadAnyTokensBeforeDirective);
+ return HandleIfDirective(Result, SavedHash, ReadAnyTokensBeforeDirective);
case tok::pp_ifdef:
- return HandleIfdefDirective(Result, false, true/*not valid for miopt*/);
+ return HandleIfdefDirective(Result, SavedHash, false,
+ true /*not valid for miopt*/);
case tok::pp_ifndef:
- return HandleIfdefDirective(Result, true, ReadAnyTokensBeforeDirective);
+ return HandleIfdefDirective(Result, SavedHash, true,
+ ReadAnyTokensBeforeDirective);
case tok::pp_elif:
- return HandleElifDirective(Result);
+ return HandleElifDirective(Result, SavedHash);
case tok::pp_else:
- return HandleElseDirective(Result);
+ return HandleElseDirective(Result, SavedHash);
case tok::pp_endif:
return HandleEndifDirective(Result);
@@ -1652,12 +1656,18 @@
DiagnosticsEngine &Diags, Module *M) {
Module::Requirement Requirement;
Module::UnresolvedHeaderDirective MissingHeader;
- if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader))
+ Module *ShadowingModule = nullptr;
+ if (M->isAvailable(LangOpts, TargetInfo, Requirement, MissingHeader,
+ ShadowingModule))
return false;
if (MissingHeader.FileNameLoc.isValid()) {
Diags.Report(MissingHeader.FileNameLoc, diag::err_module_header_missing)
<< MissingHeader.IsUmbrella << MissingHeader.FileName;
+ } else if (ShadowingModule) {
+ Diags.Report(M->DefinitionLoc, diag::err_module_shadowed) << M->Name;
+ Diags.Report(ShadowingModule->DefinitionLoc,
+ diag::note_previous_definition);
} else {
// FIXME: Track the location at which the requirement was specified, and
// use it here.
@@ -2614,7 +2624,9 @@
/// true if any tokens have been returned or pp-directives activated before this
/// \#ifndef has been lexed.
///
-void Preprocessor::HandleIfdefDirective(Token &Result, bool isIfndef,
+void Preprocessor::HandleIfdefDirective(Token &Result,
+ const Token &HashToken,
+ bool isIfndef,
bool ReadAnyTokensBeforeDirective) {
++NumIf;
Token DirectiveTok = Result;
@@ -2626,8 +2638,8 @@
if (MacroNameTok.is(tok::eod)) {
// Skip code until we get to #endif. This helps with recovery by not
// emitting an error when the #endif is reached.
- SkipExcludedConditionalBlock(DirectiveTok.getLocation(),
- /*Foundnonskip*/false, /*FoundElse*/false);
+ SkipExcludedConditionalBlock(HashToken, DirectiveTok.getLocation(),
+ /*Foundnonskip*/ false, /*FoundElse*/ false);
return;
}
@@ -2675,15 +2687,16 @@
/*foundelse*/false);
} else {
// No, skip the contents of this block.
- SkipExcludedConditionalBlock(DirectiveTok.getLocation(),
- /*Foundnonskip*/false,
- /*FoundElse*/false);
+ SkipExcludedConditionalBlock(HashToken, DirectiveTok.getLocation(),
+ /*Foundnonskip*/ false,
+ /*FoundElse*/ false);
}
}
/// HandleIfDirective - Implements the \#if directive.
///
void Preprocessor::HandleIfDirective(Token &IfToken,
+ const Token &HashToken,
bool ReadAnyTokensBeforeDirective) {
++NumIf;
@@ -2721,8 +2734,9 @@
/*foundnonskip*/true, /*foundelse*/false);
} else {
// No, skip the contents of this block.
- SkipExcludedConditionalBlock(IfToken.getLocation(), /*Foundnonskip*/false,
- /*FoundElse*/false);
+ SkipExcludedConditionalBlock(HashToken, IfToken.getLocation(),
+ /*Foundnonskip*/ false,
+ /*FoundElse*/ false);
}
}
@@ -2754,7 +2768,7 @@
/// HandleElseDirective - Implements the \#else directive.
///
-void Preprocessor::HandleElseDirective(Token &Result) {
+void Preprocessor::HandleElseDirective(Token &Result, const Token &HashToken) {
++NumElse;
// #else directive in a non-skipping conditional... start skipping.
@@ -2785,13 +2799,14 @@
}
// Finally, skip the rest of the contents of this block.
- SkipExcludedConditionalBlock(CI.IfLoc, /*Foundnonskip*/true,
- /*FoundElse*/true, Result.getLocation());
+ SkipExcludedConditionalBlock(HashToken, CI.IfLoc, /*Foundnonskip*/ true,
+ /*FoundElse*/ true, Result.getLocation());
}
/// HandleElifDirective - Implements the \#elif directive.
///
-void Preprocessor::HandleElifDirective(Token &ElifToken) {
+void Preprocessor::HandleElifDirective(Token &ElifToken,
+ const Token &HashToken) {
++NumElse;
// #elif directive in a non-skipping conditional... start skipping.
@@ -2828,7 +2843,7 @@
}
// Finally, skip the rest of the contents of this block.
- SkipExcludedConditionalBlock(CI.IfLoc, /*Foundnonskip*/true,
- /*FoundElse*/CI.FoundElse,
+ SkipExcludedConditionalBlock(HashToken, CI.IfLoc, /*Foundnonskip*/ true,
+ /*FoundElse*/ CI.FoundElse,
ElifToken.getLocation());
}
diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp
index 3f8ede2..15968d5 100644
--- a/lib/Lex/PPMacroExpansion.cpp
+++ b/lib/Lex/PPMacroExpansion.cpp
@@ -1106,6 +1106,7 @@
.Case("attribute_availability_with_version_underscores", true)
.Case("attribute_availability_tvos", true)
.Case("attribute_availability_watchos", true)
+ .Case("attribute_availability_swift", true)
.Case("attribute_availability_with_strict", true)
.Case("attribute_availability_with_replacement", true)
.Case("attribute_availability_in_templates", true)
@@ -1131,6 +1132,7 @@
.Case("cxx_exceptions", LangOpts.CXXExceptions)
.Case("cxx_rtti", LangOpts.RTTI && LangOpts.RTTIData)
.Case("enumerator_attributes", true)
+ .Case("generalized_swift_name", true)
.Case("nullability", true)
.Case("nullability_on_arrays", true)
.Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory))
diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp
index 03c4cbe..954b569 100644
--- a/lib/Lex/PreprocessingRecord.cpp
+++ b/lib/Lex/PreprocessingRecord.cpp
@@ -400,8 +400,9 @@
MacroNameTok.getLocation());
}
-void PreprocessingRecord::SourceRangeSkipped(SourceRange Range) {
- SkippedRanges.push_back(Range);
+void PreprocessingRecord::SourceRangeSkipped(SourceRange Range,
+ SourceLocation EndifLoc) {
+ SkippedRanges.emplace_back(Range.getBegin(), EndifLoc);
}
void PreprocessingRecord::MacroExpands(const Token &Id,
diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp
index 27651c9..32724c4 100644
--- a/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/lib/Parse/ParseCXXInlineMethods.cpp
@@ -47,6 +47,7 @@
VS, ICIS_NoInit);
if (FnD) {
Actions.ProcessDeclAttributeList(getCurScope(), FnD, AccessAttrs);
+ Actions.ProcessAPINotes(FnD);
if (PureSpecLoc.isValid())
Actions.ActOnPureSpecifier(FnD, PureSpecLoc);
}
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index a461069..2397e51 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -244,6 +244,38 @@
return IL;
}
+void Parser::ParseSwiftNewtypeAttribute(
+ IdentifierInfo &SwiftNewtype, SourceLocation SwiftNewtypeLoc,
+ ParsedAttributes &attrs, SourceLocation *endLoc, IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc, AttributeList::Syntax Syntax) {
+
+ BalancedDelimiterTracker Parens(*this, tok::l_paren);
+ Parens.consumeOpen();
+
+ if (Tok.is(tok::r_paren)) {
+ Diag(Tok.getLocation(), diag::err_argument_required_after_attribute);
+ Parens.consumeClose();
+ return;
+ }
+ if (Tok.isNot(tok::kw_struct) && Tok.isNot(tok::kw_enum)) {
+ Diag(Tok.getLocation(), diag::warn_attribute_type_not_supported)
+ << &SwiftNewtype << Tok.getIdentifierInfo();
+ if (!isTokenSpecial())
+ ConsumeToken();
+ Parens.consumeClose();
+ return;
+ }
+ auto IL = IdentifierLoc::create(Actions.Context, Tok.getLocation(),
+ Tok.getIdentifierInfo());
+ ConsumeToken();
+ auto identLoc = ArgsUnion(IL);
+
+ attrs.addNew(&SwiftNewtype,
+ SourceRange(SwiftNewtypeLoc, Parens.getCloseLocation()),
+ ScopeName, ScopeLoc, &identLoc, 1, Syntax);
+ Parens.consumeClose();
+}
+
void Parser::ParseAttributeWithTypeArg(IdentifierInfo &AttrName,
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs,
@@ -370,6 +402,10 @@
ParseTypeTagForDatatypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
ScopeName, ScopeLoc, Syntax);
return;
+ } else if (AttrKind == AttributeList::AT_SwiftNewtype) {
+ ParseSwiftNewtypeAttribute(*AttrName, AttrNameLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, Syntax);
+ return;
} else if (attributeIsTypeArgAttr(*AttrName)) {
ParseAttributeWithTypeArg(*AttrName, AttrNameLoc, Attrs, EndLoc, ScopeName,
ScopeLoc, Syntax);
@@ -880,7 +916,7 @@
///
/// version-arg:
/// 'introduced' '=' version
-/// 'deprecated' '=' version
+/// 'deprecated' ['=' version]
/// 'obsoleted' = version
/// 'unavailable'
/// opt-replacement:
@@ -912,13 +948,18 @@
return;
}
IdentifierLoc *Platform = ParseIdentifierLoc();
- // Canonicalize platform name from "macosx" to "macos".
- if (Platform->Ident && Platform->Ident->getName() == "macosx")
- Platform->Ident = PP.getIdentifierInfo("macos");
- // Canonicalize platform name from "macosx_app_extension" to
- // "macos_app_extension".
- if (Platform->Ident && Platform->Ident->getName() == "macosx_app_extension")
- Platform->Ident = PP.getIdentifierInfo("macos_app_extension");
+ if (const IdentifierInfo *const Ident = Platform->Ident) {
+ // Canonicalize platform name from "macosx" to "macos".
+ if (Ident->getName() == "macosx")
+ Platform->Ident = PP.getIdentifierInfo("macos");
+ // Canonicalize platform name from "macosx_app_extension" to
+ // "macos_app_extension".
+ else if (Ident->getName() == "macosx_app_extension")
+ Platform->Ident = PP.getIdentifierInfo("macos_app_extension");
+ else
+ Platform->Ident = PP.getIdentifierInfo(
+ AvailabilityAttr::canonicalizePlatformName(Ident->getName()));
+ }
// Parse the ',' following the platform name.
if (ExpectAndConsume(tok::comma)) {
@@ -968,6 +1009,21 @@
continue;
}
+ if (Keyword == Ident_deprecated && Platform->Ident &&
+ Platform->Ident->getName() == "swift") {
+ // For swift, we deprecate for all versions.
+ if (!Changes[Deprecated].KeywordLoc.isInvalid()) {
+ Diag(KeywordLoc, diag::err_availability_redundant)
+ << Keyword
+ << SourceRange(Changes[Deprecated].KeywordLoc);
+ }
+
+ Changes[Deprecated].KeywordLoc = KeywordLoc;
+ // Use a fake version here.
+ Changes[Deprecated].Version = VersionTuple(1);
+ continue;
+ }
+
if (Tok.isNot(tok::equal)) {
Diag(Tok, diag::err_expected_after) << Keyword << tok::equal;
SkipUntil(tok::r_paren, StopAtSemi);
@@ -6785,3 +6841,68 @@
}
return false;
}
+
+TypeResult Parser::parseTypeFromString(StringRef typeStr, StringRef context,
+ SourceLocation includeLoc) {
+ // Consume (unexpanded) tokens up to the end-of-directive.
+ SmallVector<Token, 4> tokens;
+ {
+ // Create a new buffer from which we will parse the type.
+ auto &sourceMgr = PP.getSourceManager();
+ FileID fileID = sourceMgr.createFileID(
+ llvm::MemoryBuffer::getMemBufferCopy(typeStr, context),
+ SrcMgr::C_User, 0, 0, includeLoc);
+
+ // Form a new lexer that references the buffer.
+ Lexer lexer(fileID, sourceMgr.getBuffer(fileID), PP);
+ lexer.setParsingPreprocessorDirective(true);
+ lexer.setIsPragmaLexer(true);
+
+ // Lex the tokens from that buffer.
+ Token tok;
+ do {
+ lexer.Lex(tok);
+ tokens.push_back(tok);
+ } while (tok.isNot(tok::eod));
+ }
+
+ // Replace the "eod" token with an "eof" token identifying the end of
+ // the provided string.
+ Token &endToken = tokens.back();
+ endToken.startToken();
+ endToken.setKind(tok::eof);
+ endToken.setLocation(Tok.getLocation());
+ endToken.setEofData(typeStr.data());
+
+ // Add the current token back.
+ tokens.push_back(Tok);
+
+ // Enter the tokens into the token stream.
+ PP.EnterTokenStream(tokens, /*DisableMacroExpansion=*/false);
+
+ // Consume the current token so that we'll start parsing the tokens we
+ // added to the stream.
+ ConsumeAnyToken();
+
+ // Enter a new scope.
+ ParseScope localScope(this, 0);
+
+ // Parse the type.
+ TypeResult result = ParseTypeName(nullptr);
+
+ // Check if we parsed the whole thing.
+ if (result.isUsable() &&
+ (Tok.isNot(tok::eof) || Tok.getEofData() != typeStr.data())) {
+ Diag(Tok.getLocation(), diag::err_type_unparsed);
+ }
+
+ // There could be leftover tokens (e.g. because of an error).
+ // Skip through until we reach the 'end of directive' token.
+ while (Tok.isNot(tok::eof))
+ ConsumeAnyToken();
+
+ // Consume the end token.
+ if (Tok.is(tok::eof) && Tok.getEofData() == typeStr.data())
+ ConsumeAnyToken();
+ return result;
+}
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index 2301284..030f17a 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -2772,8 +2772,10 @@
// initialize it.
ThisDecl = VT->getTemplatedDecl();
- if (ThisDecl && AccessAttrs)
+ if (ThisDecl) {
Actions.ProcessDeclAttributeList(getCurScope(), ThisDecl, AccessAttrs);
+ Actions.ProcessAPINotes(ThisDecl);
+ }
}
// Error recovery might have converted a non-static member into a static
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index dcafbad..7332bfb 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -966,6 +966,8 @@
// that would be an error.
ParsedType InitCaptureType;
+ if (!Init.isInvalid())
+ Init = Actions.CorrectDelayedTyposInExpr(Init.get());
if (Init.isUsable()) {
// Get the pointer and store it in an lvalue, so we can use it as an
// out argument.
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index 01b1bf4..d1bd67a 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -211,6 +211,8 @@
/// __attribute__((unavailable))
/// __attribute__((objc_exception)) - used by NSException on 64-bit
/// __attribute__((objc_root_class))
+/// __attribute__((objc_subclassing_restricted))
+/// __attribute__((objc_complete_definition))
///
Decl *Parser::ParseObjCAtInterfaceDeclaration(SourceLocation AtLoc,
ParsedAttributes &attrs) {
diff --git a/lib/Parse/ParseStmtAsm.cpp b/lib/Parse/ParseStmtAsm.cpp
index d6f16bb..181bd41 100644
--- a/lib/Parse/ParseStmtAsm.cpp
+++ b/lib/Parse/ParseStmtAsm.cpp
@@ -578,8 +578,7 @@
llvm::SourceMgr TempSrcMgr;
llvm::MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &TempSrcMgr);
- MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, llvm::CodeModel::Default,
- Ctx);
+ MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, Ctx);
std::unique_ptr<llvm::MemoryBuffer> Buffer =
llvm::MemoryBuffer::getMemBuffer(AsmString, "<MS inline asm>");
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index 1ed7ef9..d2f70fc 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -67,6 +67,11 @@
PP.addCommentHandler(CommentSemaHandler.get());
PP.setCodeCompletionHandler(*this);
+
+ Actions.ParseTypeFromStringCallback =
+ [this](StringRef typeStr, StringRef context, SourceLocation includeLoc) {
+ return this->parseTypeFromString(typeStr, context, includeLoc);
+ };
}
DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) {
@@ -406,6 +411,9 @@
//===----------------------------------------------------------------------===//
Parser::~Parser() {
+ // Clear out the parse-type-from-string callback.
+ Actions.ParseTypeFromStringCallback = nullptr;
+
// If we still have scopes active, delete the scope tree.
delete getCurScope();
Actions.CurScope = nullptr;
diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt
index 7d9ae62..75fca76 100644
--- a/lib/Sema/CMakeLists.txt
+++ b/lib/Sema/CMakeLists.txt
@@ -21,6 +21,7 @@
Sema.cpp
SemaAccess.cpp
SemaAttr.cpp
+ SemaAPINotes.cpp
SemaCXXScopeSpec.cpp
SemaCast.cpp
SemaChecking.cpp
@@ -62,4 +63,5 @@
clangBasic
clangEdit
clangLex
+ clangAPINotes
)
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index a18f714..6f22620 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -75,7 +75,13 @@
: ExternalSource(nullptr), isMultiplexExternalSource(false),
FPFeatures(pp.getLangOpts()), LangOpts(pp.getLangOpts()), PP(pp),
Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()),
- SourceMgr(PP.getSourceManager()), CollectStats(false),
+ SourceMgr(PP.getSourceManager()),
+
+ // Don't you dare clang-format this.
+ // APINotes is a never-ending source of conflicts. Don't make it worse.
+ APINotes(SourceMgr, LangOpts),
+
+ CollectStats(false),
CodeCompleter(CodeCompleter), CurContext(nullptr),
OriginalLexicalContext(nullptr), MSStructPragmaOn(false),
MSPointerToMemberRepresentationMethod(
diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp
new file mode 100644
index 0000000..0b3cdbb
--- /dev/null
+++ b/lib/Sema/SemaAPINotes.cpp
@@ -0,0 +1,899 @@
+//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the mapping from API notes to declaration attributes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/SemaInternal.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/APINotes/APINotesReader.h"
+using namespace clang;
+
+namespace {
+ enum IsActive_t : bool {
+ IsNotActive,
+ IsActive
+ };
+ enum IsReplacement_t : bool {
+ IsNotReplacement,
+ IsReplacement
+ };
+
+ struct VersionedInfoMetadata {
+ /// An empty version refers to unversioned metadata.
+ VersionTuple Version;
+ unsigned IsActive: 1;
+ unsigned IsReplacement: 1;
+
+ VersionedInfoMetadata(VersionTuple version, IsActive_t active,
+ IsReplacement_t replacement)
+ : Version(version), IsActive(active == IsActive_t::IsActive),
+ IsReplacement(replacement == IsReplacement_t::IsReplacement) {}
+ };
+} // end anonymous namespace
+
+/// Determine whether this is a multi-level pointer type.
+static bool isMultiLevelPointerType(QualType type) {
+ QualType pointee = type->getPointeeType();
+ if (pointee.isNull())
+ return false;
+
+ return pointee->isAnyPointerType() || pointee->isObjCObjectPointerType() ||
+ pointee->isMemberPointerType();
+}
+
+// Apply nullability to the given declaration.
+static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
+ VersionedInfoMetadata metadata) {
+ if (!metadata.IsActive)
+ return;
+
+ QualType type;
+
+ // Nullability for a function/method appertains to the retain type.
+ if (auto function = dyn_cast<FunctionDecl>(decl)) {
+ type = function->getReturnType();
+ } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
+ type = method->getReturnType();
+ } else if (auto value = dyn_cast<ValueDecl>(decl)) {
+ type = value->getType();
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
+ type = property->getType();
+ } else {
+ return;
+ }
+
+ // Check the nullability specifier on this type.
+ QualType origType = type;
+ S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(),
+ /*isContextSensitive=*/false,
+ isa<ParmVarDecl>(decl), /*implicit=*/true,
+ /*overrideExisting=*/true);
+ if (type.getTypePtr() == origType.getTypePtr())
+ return;
+
+ if (auto function = dyn_cast<FunctionDecl>(decl)) {
+ const FunctionType *fnType = function->getType()->castAs<FunctionType>();
+ if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) {
+ function->setType(S.Context.getFunctionType(type, proto->getParamTypes(),
+ proto->getExtProtoInfo()));
+ } else {
+ function->setType(S.Context.getFunctionNoProtoType(type,
+ fnType->getExtInfo()));
+ }
+ } else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
+ method->setReturnType(type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (!isMultiLevelPointerType(type)) {
+ method->setObjCDeclQualifier(
+ Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() |
+ Decl::OBJC_TQ_CSNullability));
+ }
+ } else if (auto value = dyn_cast<ValueDecl>(decl)) {
+ value->setType(type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (auto parm = dyn_cast<ParmVarDecl>(decl)) {
+ if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) {
+ parm->setObjCDeclQualifier(
+ Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() |
+ Decl::OBJC_TQ_CSNullability));
+ }
+ }
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
+ property->setType(type, property->getTypeSourceInfo());
+
+ // Make it a property attribute if we can.
+ if (!isMultiLevelPointerType(type)) {
+ property->setPropertyAttributes(
+ ObjCPropertyDecl::OBJC_PR_null_resettable);
+ }
+ } else {
+ llvm_unreachable("cannot handle nullability here");
+ }
+}
+
+/// Copy a string into ASTContext-allocated memory.
+static StringRef CopyString(ASTContext &ctx, StringRef string) {
+ void *mem = ctx.Allocate(string.size(), alignof(char));
+ memcpy(mem, string.data(), string.size());
+ return StringRef(static_cast<char *>(mem), string.size());
+}
+
+namespace {
+ template <typename A>
+ struct AttrKindFor {};
+
+#define ATTR(X) \
+ template <> struct AttrKindFor<X##Attr> { \
+ static const attr::Kind value = attr::X; \
+ };
+#include "clang/Basic/AttrList.inc"
+
+ /// Handle an attribute introduced by API notes.
+ ///
+ /// \param shouldAddAttribute Whether we should add a new attribute
+ /// (otherwise, we might remove an existing attribute).
+ /// \param createAttr Create the new attribute to be added.
+ template<typename A>
+ void handleAPINotedAttribute(
+ Sema &S, Decl *D, bool shouldAddAttribute,
+ VersionedInfoMetadata metadata,
+ llvm::function_ref<A *()> createAttr,
+ llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) {
+ if (metadata.IsActive) {
+ auto end = D->specific_attr_end<A>();
+ auto existing = getExistingAttr(D);
+ if (existing != end) {
+ // Remove the existing attribute, and treat it as a superseded
+ // non-versioned attribute.
+ auto *versioned = SwiftVersionedAttr::CreateImplicit(
+ S.Context, metadata.Version, *existing, /*IsReplacedByActive*/true);
+
+ D->getAttrs().erase(existing.getCurrent());
+ D->addAttr(versioned);
+ }
+
+ // If we're supposed to add a new attribute, do so.
+ if (shouldAddAttribute) {
+ if (auto attr = createAttr()) {
+ D->addAttr(attr);
+ }
+ }
+
+ } else {
+ if (shouldAddAttribute) {
+ if (auto attr = createAttr()) {
+ auto *versioned = SwiftVersionedAttr::CreateImplicit(
+ S.Context, metadata.Version, attr,
+ /*IsReplacedByActive*/metadata.IsReplacement);
+ D->addAttr(versioned);
+ }
+ } else {
+ // FIXME: This isn't preserving enough information for things like
+ // availability, where we're trying to remove a /specific/ kind of
+ // attribute.
+ auto *versioned = SwiftVersionedRemovalAttr::CreateImplicit(
+ S.Context, metadata.Version, AttrKindFor<A>::value,
+ /*IsReplacedByActive*/metadata.IsReplacement);
+ D->addAttr(versioned);
+ }
+ }
+ }
+
+ template<typename A>
+ void handleAPINotedAttribute(
+ Sema &S, Decl *D, bool shouldAddAttribute,
+ VersionedInfoMetadata metadata,
+ llvm::function_ref<A *()> createAttr) {
+ handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, createAttr,
+ [](Decl *decl) {
+ return decl->specific_attr_begin<A>();
+ });
+ }
+}
+
+static void ProcessAPINotes(Sema &S, Decl *D,
+ const api_notes::CommonEntityInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Availability
+ if (info.Unavailable) {
+ handleAPINotedAttribute<UnavailableAttr>(S, D, true, metadata,
+ [&] {
+ return UnavailableAttr::CreateImplicit(S.Context,
+ CopyString(S.Context,
+ info.UnavailableMsg));
+ });
+ }
+
+ if (info.UnavailableInSwift) {
+ handleAPINotedAttribute<AvailabilityAttr>(S, D, true, metadata, [&] {
+ return AvailabilityAttr::CreateImplicit(
+ S.Context,
+ &S.Context.Idents.get("swift"),
+ VersionTuple(),
+ VersionTuple(),
+ VersionTuple(),
+ /*Unavailable=*/true,
+ CopyString(S.Context, info.UnavailableMsg),
+ /*Strict=*/false,
+ /*Replacement=*/StringRef());
+ },
+ [](Decl *decl) {
+ auto existing = decl->specific_attr_begin<AvailabilityAttr>(),
+ end = decl->specific_attr_end<AvailabilityAttr>();
+ while (existing != end) {
+ if (auto platform = (*existing)->getPlatform()) {
+ if (platform->isStr("swift"))
+ break;
+ }
+
+ ++existing;
+ }
+
+ return existing;
+ });
+ }
+
+ // swift_private
+ if (auto swiftPrivate = info.isSwiftPrivate()) {
+ handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, metadata,
+ [&] {
+ return SwiftPrivateAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // swift_name
+ if (!info.SwiftName.empty()) {
+ handleAPINotedAttribute<SwiftNameAttr>(S, D, true, metadata,
+ [&]() -> SwiftNameAttr * {
+ auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note");
+
+ if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(),
+ &APINoteName)) {
+ return nullptr;
+ }
+
+ return SwiftNameAttr::CreateImplicit(S.Context,
+ CopyString(S.Context,
+ info.SwiftName));
+ });
+ }
+}
+
+static void ProcessAPINotes(Sema &S, Decl *D,
+ const api_notes::CommonTypeInfo &info,
+ VersionedInfoMetadata metadata) {
+ // swift_bridge
+ if (auto swiftBridge = info.getSwiftBridge()) {
+ handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(),
+ metadata, [&] {
+ return SwiftBridgeAttr::CreateImplicit(S.Context,
+ CopyString(S.Context,
+ *swiftBridge));
+ });
+ }
+
+ // ns_error_domain
+ if (auto nsErrorDomain = info.getNSErrorDomain()) {
+ handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(),
+ metadata, [&] {
+ return NSErrorDomainAttr::CreateImplicit(
+ S.Context,
+ &S.Context.Idents.get(*nsErrorDomain));
+ });
+ }
+
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Check that the replacement type provided by API notes is reasonable.
+///
+/// This is a very weak form of ABI check.
+static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc,
+ QualType origType,
+ QualType replacementType) {
+ if (S.Context.getTypeSize(origType) !=
+ S.Context.getTypeSize(replacementType)) {
+ S.Diag(loc, diag::err_incompatible_replacement_type)
+ << replacementType << origType;
+ return true;
+ }
+
+ return false;
+}
+
+/// Process API notes for a variable or property.
+static void ProcessAPINotes(Sema &S, Decl *D,
+ const api_notes::VariableInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Type override.
+ if (metadata.IsActive && !info.getType().empty() &&
+ S.ParseTypeFromStringCallback) {
+ auto parsedType = S.ParseTypeFromStringCallback(info.getType(),
+ "<API Notes>",
+ D->getLocation());
+ if (parsedType.isUsable()) {
+ QualType type = Sema::GetTypeFromParser(parsedType.get());
+ auto typeInfo =
+ S.Context.getTrivialTypeSourceInfo(type, D->getLocation());
+
+ if (auto var = dyn_cast<VarDecl>(D)) {
+ // Make adjustments to parameter types.
+ if (isa<ParmVarDecl>(var)) {
+ type = S.adjustParameterTypeForObjCAutoRefCount(type,
+ D->getLocation());
+ type = S.Context.getAdjustedParameterType(type);
+ }
+
+ if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(),
+ type)) {
+ var->setType(type);
+ var->setTypeSourceInfo(typeInfo);
+ }
+ } else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
+ if (!checkAPINotesReplacementType(S, property->getLocation(),
+ property->getType(),
+ type)) {
+ property->setType(type, typeInfo);
+ }
+ } else {
+ llvm_unreachable("API notes allowed a type on an unknown declaration");
+ }
+ }
+ }
+
+ // Nullability.
+ if (auto Nullability = info.getNullability()) {
+ applyNullability(S, D, *Nullability, metadata);
+ }
+
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a parameter.
+static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
+ const api_notes::ParamInfo &info,
+ VersionedInfoMetadata metadata) {
+ // noescape
+ if (auto noescape = info.isNoEscape()) {
+ handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, metadata, [&] {
+ return NoEscapeAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a global variable.
+static void ProcessAPINotes(Sema &S, VarDecl *D,
+ const api_notes::GlobalVariableInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C property.
+static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D,
+ const api_notes::ObjCPropertyInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
+ metadata);
+ if (auto asAccessors = info.getSwiftImportAsAccessors()) {
+ handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D,
+ *asAccessors,
+ metadata, [&] {
+ return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context);
+ });
+ }
+}
+
+namespace {
+ typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod;
+}
+
+/// Process API notes for a function or method.
+static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
+ const api_notes::FunctionInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Find the declaration itself.
+ FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>();
+ Decl *D = FD;
+ ObjCMethodDecl *MD = 0;
+ if (!D) {
+ MD = AnyFunc.get<ObjCMethodDecl *>();
+ D = MD;
+ }
+
+ // Nullability of return type.
+ if (info.NullabilityAudited) {
+ applyNullability(S, D, info.getReturnTypeInfo(), metadata);
+ }
+
+ // Parameters.
+ unsigned NumParams;
+ if (FD)
+ NumParams = FD->getNumParams();
+ else
+ NumParams = MD->param_size();
+
+ bool anyTypeChanged = false;
+ for (unsigned I = 0; I != NumParams; ++I) {
+ ParmVarDecl *Param;
+ if (FD)
+ Param = FD->getParamDecl(I);
+ else
+ Param = MD->param_begin()[I];
+
+ QualType paramTypeBefore = Param->getType();
+
+ if (I < info.Params.size()) {
+ ProcessAPINotes(S, Param, info.Params[I], metadata);
+ }
+
+ // Nullability.
+ if (info.NullabilityAudited)
+ applyNullability(S, Param, info.getParamTypeInfo(I), metadata);
+
+ if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr())
+ anyTypeChanged = true;
+ }
+
+ // Result type override.
+ QualType overriddenResultType;
+ if (metadata.IsActive && !info.ResultType.empty() &&
+ S.ParseTypeFromStringCallback) {
+ auto parsedType = S.ParseTypeFromStringCallback(info.ResultType,
+ "<API Notes>",
+ D->getLocation());
+ if (parsedType.isUsable()) {
+ QualType resultType = Sema::GetTypeFromParser(parsedType.get());
+
+ if (MD) {
+ if (!checkAPINotesReplacementType(S, D->getLocation(),
+ MD->getReturnType(), resultType)) {
+ auto resultTypeInfo =
+ S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation());
+ MD->setReturnType(resultType);
+ MD->setReturnTypeSourceInfo(resultTypeInfo);
+ }
+ } else if (!checkAPINotesReplacementType(S, FD->getLocation(),
+ FD->getReturnType(),
+ resultType)) {
+ overriddenResultType = resultType;
+ anyTypeChanged = true;
+ }
+ }
+ }
+
+ // If the result type or any of the parameter types changed for a function
+ // declaration, we have to rebuild the type.
+ if (FD && anyTypeChanged) {
+ if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) {
+ if (overriddenResultType.isNull())
+ overriddenResultType = fnProtoType->getReturnType();
+
+ SmallVector<QualType, 4> paramTypes;
+ for (auto param : FD->parameters()) {
+ paramTypes.push_back(param->getType());
+ }
+ FD->setType(S.Context.getFunctionType(overriddenResultType,
+ paramTypes,
+ fnProtoType->getExtProtoInfo()));
+ } else if (!overriddenResultType.isNull()) {
+ const auto *fnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>();
+ FD->setType(
+ S.Context.getFunctionNoProtoType(overriddenResultType,
+ fnNoProtoType->getExtInfo()));
+ }
+ }
+
+ // Handle common entity information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a global function.
+static void ProcessAPINotes(Sema &S, FunctionDecl *D,
+ const api_notes::GlobalFunctionInfo &info,
+ VersionedInfoMetadata metadata) {
+
+ // Handle common function information.
+ ProcessAPINotes(S, FunctionOrMethod(D),
+ static_cast<const api_notes::FunctionInfo &>(info), metadata);
+}
+
+/// Process API notes for an enumerator.
+static void ProcessAPINotes(Sema &S, EnumConstantDecl *D,
+ const api_notes::EnumConstantInfo &info,
+ VersionedInfoMetadata metadata) {
+
+ // Handle common information.
+ ProcessAPINotes(S, D,
+ static_cast<const api_notes::CommonEntityInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C method.
+static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D,
+ const api_notes::ObjCMethodInfo &info,
+ VersionedInfoMetadata metadata) {
+ // Designated initializers.
+ if (info.DesignatedInit) {
+ handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, metadata,
+ [&] {
+ if (ObjCInterfaceDecl *IFace = D->getClassInterface()) {
+ IFace->setHasDesignatedInitializers();
+ }
+ return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // Handle common function information.
+ ProcessAPINotes(S, FunctionOrMethod(D),
+ static_cast<const api_notes::FunctionInfo &>(info), metadata);
+}
+
+/// Process API notes for a tag.
+static void ProcessAPINotes(Sema &S, TagDecl *D,
+ const api_notes::TagInfo &info,
+ VersionedInfoMetadata metadata) {
+ if (auto extensibility = info.EnumExtensibility) {
+ using api_notes::EnumExtensibilityKind;
+ bool shouldAddAttribute = (*extensibility != EnumExtensibilityKind::None);
+ handleAPINotedAttribute<EnumExtensibilityAttr>(S, D, shouldAddAttribute,
+ metadata, [&] {
+ EnumExtensibilityAttr::Kind kind;
+ switch (extensibility.getValue()) {
+ case EnumExtensibilityKind::None:
+ llvm_unreachable("remove only");
+ case EnumExtensibilityKind::Open:
+ kind = EnumExtensibilityAttr::Open;
+ break;
+ case EnumExtensibilityKind::Closed:
+ kind = EnumExtensibilityAttr::Closed;
+ break;
+ }
+ return EnumExtensibilityAttr::CreateImplicit(S.Context, kind);
+ });
+ }
+
+ if (auto flagEnum = info.isFlagEnum()) {
+ handleAPINotedAttribute<FlagEnumAttr>(S, D, flagEnum.getValue(), metadata,
+ [&] {
+ return FlagEnumAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // Handle common type information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for a typedef.
+static void ProcessAPINotes(Sema &S, TypedefNameDecl *D,
+ const api_notes::TypedefInfo &info,
+ VersionedInfoMetadata metadata) {
+ // swift_wrapper
+ using SwiftWrapperKind = api_notes::SwiftWrapperKind;
+
+ if (auto swiftWrapper = info.SwiftWrapper) {
+ handleAPINotedAttribute<SwiftNewtypeAttr>(S, D,
+ *swiftWrapper != SwiftWrapperKind::None, metadata,
+ [&] {
+ SwiftNewtypeAttr::NewtypeKind kind;
+ switch (*swiftWrapper) {
+ case SwiftWrapperKind::None:
+ llvm_unreachable("Shouldn't build an attribute");
+
+ case SwiftWrapperKind::Struct:
+ kind = SwiftNewtypeAttr::NK_Struct;
+ break;
+
+ case SwiftWrapperKind::Enum:
+ kind = SwiftNewtypeAttr::NK_Enum;
+ break;
+ }
+ return SwiftNewtypeAttr::CreateImplicit(
+ S.Context,
+ SwiftNewtypeAttr::GNU_swift_wrapper,
+ kind);
+ });
+ }
+
+ // Handle common type information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C class or protocol.
+static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D,
+ const api_notes::ObjCContextInfo &info,
+ VersionedInfoMetadata metadata) {
+
+ // Handle common type information.
+ ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
+ metadata);
+}
+
+/// Process API notes for an Objective-C class.
+static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
+ const api_notes::ObjCContextInfo &info,
+ VersionedInfoMetadata metadata) {
+ if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) {
+ handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(S, D, *asNonGeneric,
+ metadata, [&] {
+ return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ if (auto objcMembers = info.getSwiftObjCMembers()) {
+ handleAPINotedAttribute<SwiftObjCMembersAttr>(S, D, *objcMembers,
+ metadata, [&] {
+ return SwiftObjCMembersAttr::CreateImplicit(S.Context);
+ });
+ }
+
+ // Handle information common to Objective-C classes and protocols.
+ ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info,
+ metadata);
+}
+
+/// If we're applying API notes with an active, non-default version, and the
+/// versioned API notes have a SwiftName but the declaration normally wouldn't
+/// have one, add a removal attribute to make it clear that the new SwiftName
+/// attribute only applies to the active version of \p D, not to all versions.
+///
+/// This must be run \em before processing API notes for \p D, because otherwise
+/// any existing SwiftName attribute will have been packaged up in a
+/// SwiftVersionedAttr.
+template <typename SpecificInfo>
+static void maybeAttachUnversionedSwiftName(
+ Sema &S, Decl *D,
+ const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
+ if (D->hasAttr<SwiftNameAttr>())
+ return;
+ if (!Info.getSelected())
+ return;
+
+ // Is the active slice versioned, and does it set a Swift name?
+ VersionTuple SelectedVersion;
+ SpecificInfo SelectedInfoSlice;
+ std::tie(SelectedVersion, SelectedInfoSlice) = Info[*Info.getSelected()];
+ if (SelectedVersion.empty())
+ return;
+ if (SelectedInfoSlice.SwiftName.empty())
+ return;
+
+ // Does the unversioned slice /not/ set a Swift name?
+ for (const auto &VersionAndInfoSlice : Info) {
+ if (!VersionAndInfoSlice.first.empty())
+ continue;
+ if (!VersionAndInfoSlice.second.SwiftName.empty())
+ return;
+ }
+
+ // Then explicitly call that out with a removal attribute.
+ VersionedInfoMetadata DummyFutureMetadata(SelectedVersion, IsNotActive,
+ IsReplacement);
+ handleAPINotedAttribute<SwiftNameAttr>(S, D, /*add*/false,
+ DummyFutureMetadata,
+ []() -> SwiftNameAttr * {
+ llvm_unreachable("should not try to add an attribute here");
+ });
+}
+
+/// Processes all versions of versioned API notes.
+///
+/// Just dispatches to the various ProcessAPINotes functions in this file.
+template <typename SpecificDecl, typename SpecificInfo>
+static void ProcessVersionedAPINotes(
+ Sema &S, SpecificDecl *D,
+ const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
+
+ maybeAttachUnversionedSwiftName(S, D, Info);
+
+ unsigned Selected = Info.getSelected().getValueOr(Info.size());
+
+ VersionTuple Version;
+ SpecificInfo InfoSlice;
+ for (unsigned i = 0, e = Info.size(); i != e; ++i) {
+ std::tie(Version, InfoSlice) = Info[i];
+ auto Active = (i == Selected) ? IsActive : IsNotActive;
+ auto Replacement = IsNotReplacement;
+ if (Active == IsNotActive && Version.empty()) {
+ Replacement = IsReplacement;
+ Version = Info[Selected].first;
+ }
+ ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active,
+ Replacement));
+ }
+}
+
+/// Process API notes that are associated with this declaration, mapping them
+/// to attributes as appropriate.
+void Sema::ProcessAPINotes(Decl *D) {
+ if (!D)
+ return;
+
+ // Globals.
+ if (D->getDeclContext()->isFileContext()) {
+ // Global variables.
+ if (auto VD = dyn_cast<VarDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupGlobalVariable(VD->getName());
+ ProcessVersionedAPINotes(*this, VD, Info);
+ }
+
+ return;
+ }
+
+ // Global functions.
+ if (auto FD = dyn_cast<FunctionDecl>(D)) {
+ if (FD->getDeclName().isIdentifier()) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupGlobalFunction(FD->getName());
+ ProcessVersionedAPINotes(*this, FD, Info);
+ }
+ }
+
+ return;
+ }
+
+ // Objective-C classes.
+ if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupObjCClassInfo(Class->getName());
+ ProcessVersionedAPINotes(*this, Class, Info);
+ }
+
+ return;
+ }
+
+ // Objective-C protocols.
+ if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName());
+ ProcessVersionedAPINotes(*this, Protocol, Info);
+ }
+
+ return;
+ }
+
+ // Tags
+ if (auto Tag = dyn_cast<TagDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupTag(Tag->getName());
+ ProcessVersionedAPINotes(*this, Tag, Info);
+ }
+
+ return;
+ }
+
+ // Typedefs
+ if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupTypedef(Typedef->getName());
+ ProcessVersionedAPINotes(*this, Typedef, Info);
+ }
+
+ return;
+ }
+
+ return;
+ }
+
+ // Enumerators.
+ if (D->getDeclContext()->getRedeclContext()->isFileContext()) {
+ if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ auto Info = Reader->lookupEnumConstant(EnumConstant->getName());
+ ProcessVersionedAPINotes(*this, EnumConstant, Info);
+ }
+
+ return;
+ }
+ }
+
+ if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) {
+ // Location function that looks up an Objective-C context.
+ auto GetContext = [&](api_notes::APINotesReader *Reader)
+ -> Optional<api_notes::ContextID> {
+ if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) {
+ if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName()))
+ return *Found;
+
+ return None;
+ }
+
+ if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) {
+ if (auto Cat = Impl->getCategoryDecl())
+ ObjCContainer = Cat;
+ else
+ return None;
+ }
+
+ if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) {
+ if (Category->getClassInterface())
+ ObjCContainer = Category->getClassInterface();
+ else
+ return None;
+ }
+
+ if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) {
+ if (Impl->getClassInterface())
+ ObjCContainer = Impl->getClassInterface();
+ else
+ return None;
+ }
+
+ if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) {
+ if (auto Found = Reader->lookupObjCClassID(Class->getName()))
+ return *Found;
+
+ return None;
+
+ }
+
+ return None;
+ };
+
+ // Objective-C methods.
+ if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ if (auto Context = GetContext(Reader)) {
+ // Map the selector.
+ Selector Sel = Method->getSelector();
+ SmallVector<StringRef, 2> SelPieces;
+ if (Sel.isUnarySelector())
+ SelPieces.push_back(Sel.getNameForSlot(0));
+ else {
+ for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i)
+ SelPieces.push_back(Sel.getNameForSlot(i));
+ }
+
+ api_notes::ObjCSelectorRef SelectorRef;
+ SelectorRef.NumPieces = Sel.getNumArgs();
+ SelectorRef.Identifiers = SelPieces;
+
+ auto Info = Reader->lookupObjCMethod(*Context, SelectorRef,
+ Method->isInstanceMethod());
+ ProcessVersionedAPINotes(*this, Method, Info);
+ }
+ }
+ }
+
+ // Objective-C properties.
+ if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
+ if (auto Context = GetContext(Reader)) {
+ bool isInstanceProperty =
+ (Property->getPropertyAttributesAsWritten() &
+ ObjCPropertyDecl::OBJC_PR_class) == 0;
+ auto Info = Reader->lookupObjCProperty(*Context, Property->getName(),
+ isInstanceProperty);
+ ProcessVersionedAPINotes(*this, Property, Info);
+ }
+ }
+
+ return;
+ }
+
+ return;
+ }
+}
diff --git a/lib/Sema/SemaCast.cpp b/lib/Sema/SemaCast.cpp
index d603101..ad63486 100644
--- a/lib/Sema/SemaCast.cpp
+++ b/lib/Sema/SemaCast.cpp
@@ -2458,24 +2458,17 @@
// GCC's cast to union extension.
if (DestRecordTy && DestRecordTy->getDecl()->isUnion()) {
RecordDecl *RD = DestRecordTy->getDecl();
- RecordDecl::field_iterator Field, FieldEnd;
- for (Field = RD->field_begin(), FieldEnd = RD->field_end();
- Field != FieldEnd; ++Field) {
- if (Self.Context.hasSameUnqualifiedType(Field->getType(), SrcType) &&
- !Field->isUnnamedBitfield()) {
- Self.Diag(OpRange.getBegin(), diag::ext_typecheck_cast_to_union)
- << SrcExpr.get()->getSourceRange();
- break;
- }
- }
- if (Field == FieldEnd) {
+ if (CastExpr::getTargetFieldForToUnionCast(RD, SrcType)) {
+ Self.Diag(OpRange.getBegin(), diag::ext_typecheck_cast_to_union)
+ << SrcExpr.get()->getSourceRange();
+ Kind = CK_ToUnion;
+ return;
+ } else {
Self.Diag(OpRange.getBegin(), diag::err_typecheck_cast_to_union_no_type)
<< SrcType << SrcExpr.get()->getSourceRange();
SrcExpr = ExprError();
return;
}
- Kind = CK_ToUnion;
- return;
}
// OpenCL v2.0 s6.13.10 - Allow casts from '0' to event_t type.
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index b2223b7..fb78159 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -3797,23 +3797,33 @@
if (checkVAStartIsInVariadicFunction(*this, Func))
return true;
- const struct {
- unsigned ArgNo;
- QualType Type;
- } ArgumentTypes[] = {
- { 1, Context.getPointerType(Context.CharTy.withConst()) },
- { 2, Context.getSizeType() },
- };
+ // __va_start on Windows does not validate the parameter qualifiers
- for (const auto &AT : ArgumentTypes) {
- const Expr *Arg = Call->getArg(AT.ArgNo)->IgnoreParens();
- if (Arg->getType().getCanonicalType() == AT.Type.getCanonicalType())
- continue;
- Diag(Arg->getLocStart(), diag::err_typecheck_convert_incompatible)
- << Arg->getType() << AT.Type << 1 /* different class */
- << 0 /* qualifier difference */ << 3 /* parameter mismatch */
- << AT.ArgNo + 1 << Arg->getType() << AT.Type;
- }
+ const Expr *Arg1 = Call->getArg(1)->IgnoreParens();
+ const Type *Arg1Ty = Arg1->getType().getCanonicalType().getTypePtr();
+
+ const Expr *Arg2 = Call->getArg(2)->IgnoreParens();
+ const Type *Arg2Ty = Arg2->getType().getCanonicalType().getTypePtr();
+
+ const QualType &ConstCharPtrTy =
+ Context.getPointerType(Context.CharTy.withConst());
+ if (!Arg1Ty->isPointerType() ||
+ Arg1Ty->getPointeeType().withoutLocalFastQualifiers() != Context.CharTy)
+ Diag(Arg1->getLocStart(), diag::err_typecheck_convert_incompatible)
+ << Arg1->getType() << ConstCharPtrTy
+ << 1 /* different class */
+ << 0 /* qualifier difference */
+ << 3 /* parameter mismatch */
+ << 2 << Arg1->getType() << ConstCharPtrTy;
+
+ const QualType SizeTy = Context.getSizeType();
+ if (Arg2Ty->getCanonicalTypeInternal().withoutLocalFastQualifiers() != SizeTy)
+ Diag(Arg2->getLocStart(), diag::err_typecheck_convert_incompatible)
+ << Arg2->getType() << SizeTy
+ << 1 /* different class */
+ << 0 /* qualifier difference */
+ << 3 /* parameter mismatch */
+ << 3 << Arg2->getType() << SizeTy;
return false;
}
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index 4de7d42..700cf38 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -432,53 +432,6 @@
return iterator(DeclOrVector.get<DeclIndexPairVector *>()->end());
}
-/// \brief Compute the qualification required to get from the current context
-/// (\p CurContext) to the target context (\p TargetContext).
-///
-/// \param Context the AST context in which the qualification will be used.
-///
-/// \param CurContext the context where an entity is being named, which is
-/// typically based on the current scope.
-///
-/// \param TargetContext the context in which the named entity actually
-/// resides.
-///
-/// \returns a nested name specifier that refers into the target context, or
-/// NULL if no qualification is needed.
-static NestedNameSpecifier *
-getRequiredQualification(ASTContext &Context,
- const DeclContext *CurContext,
- const DeclContext *TargetContext) {
- SmallVector<const DeclContext *, 4> TargetParents;
-
- for (const DeclContext *CommonAncestor = TargetContext;
- CommonAncestor && !CommonAncestor->Encloses(CurContext);
- CommonAncestor = CommonAncestor->getLookupParent()) {
- if (CommonAncestor->isTransparentContext() ||
- CommonAncestor->isFunctionOrMethod())
- continue;
-
- TargetParents.push_back(CommonAncestor);
- }
-
- NestedNameSpecifier *Result = nullptr;
- while (!TargetParents.empty()) {
- const DeclContext *Parent = TargetParents.pop_back_val();
-
- if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(Parent)) {
- if (!Namespace->getIdentifier())
- continue;
-
- Result = NestedNameSpecifier::Create(Context, Result, Namespace);
- }
- else if (const TagDecl *TD = dyn_cast<TagDecl>(Parent))
- Result = NestedNameSpecifier::Create(Context, Result,
- false,
- Context.getTypeDeclType(TD).getTypePtr());
- }
- return Result;
-}
-
/// Determine whether \p Id is a name reserved for the implementation (C99
/// 7.1.3, C++ [lib.global.names]).
static bool isReservedName(const IdentifierInfo *Id,
@@ -590,9 +543,8 @@
R.QualifierIsInformative = false;
if (!R.Qualifier)
- R.Qualifier = getRequiredQualification(SemaRef.Context,
- CurContext,
- R.Declaration->getDeclContext());
+ R.Qualifier = NestedNameSpecifier::getRequiredQualification(
+ SemaRef.Context, CurContext, R.Declaration->getDeclContext());
return false;
}
@@ -3366,9 +3318,8 @@
// If we need a nested-name-specifier, add one now.
if (!InContext) {
- NestedNameSpecifier *NNS
- = getRequiredQualification(S.Context, CurContext,
- Overridden->getDeclContext());
+ NestedNameSpecifier *NNS = NestedNameSpecifier::getRequiredQualification(
+ S.Context, CurContext, Overridden->getDeclContext());
if (NNS) {
std::string Str;
llvm::raw_string_ostream OS(Str);
@@ -4230,7 +4181,8 @@
// If there are no prior enumerators in C++, check whether we have to
// qualify the names of the enumerators that we suggest, because they
// may not be visible in this scope.
- Qualifier = getRequiredQualification(Context, CurContext, Enum);
+ Qualifier = NestedNameSpecifier::getRequiredQualification(Context,
+ CurContext, Enum);
}
// Add any enumerators that have not yet been mentioned.
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 692a77e..bf8238c 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2416,6 +2416,10 @@
NewAttr = S.mergeMinSizeAttr(D, MA->getRange(), AttrSpellingListIndex);
else if (const auto *OA = dyn_cast<OptimizeNoneAttr>(Attr))
NewAttr = S.mergeOptimizeNoneAttr(D, OA->getRange(), AttrSpellingListIndex);
+ else if (const auto *SNA = dyn_cast<SwiftNameAttr>(Attr))
+ NewAttr = S.mergeSwiftNameAttr(D, SNA->getRange(), SNA->getName(),
+ AMK == Sema::AMK_Override,
+ AttrSpellingListIndex);
else if (const auto *InternalLinkageA = dyn_cast<InternalLinkageAttr>(Attr))
NewAttr = S.mergeInternalLinkageAttr(
D, InternalLinkageA->getRange(),
@@ -2433,6 +2437,8 @@
(AMK == Sema::AMK_Override ||
AMK == Sema::AMK_ProtocolImplementation))
NewAttr = nullptr;
+ else if (isa<SwiftPrivateAttr>(Attr) && AMK == Sema::AMK_Override)
+ NewAttr = nullptr;
else if (const auto *UA = dyn_cast<UuidAttr>(Attr))
NewAttr = S.mergeUuidAttr(D, UA->getRange(), AttrSpellingListIndex,
UA->getGuid());
@@ -11802,10 +11808,8 @@
}
}
-ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
- SourceLocation NameLoc, IdentifierInfo *Name,
- QualType T, TypeSourceInfo *TSInfo,
- StorageClass SC) {
+QualType Sema::adjustParameterTypeForObjCAutoRefCount(QualType T,
+ SourceLocation Loc) {
// In ARC, infer a lifetime qualifier for appropriate parameter types.
if (getLangOpts().ObjCAutoRefCount &&
T.getObjCLifetime() == Qualifiers::OCL_None &&
@@ -11820,7 +11824,7 @@
if (!T.isConstQualified()) {
DelayedDiagnostics.add(
sema::DelayedDiagnostic::makeForbiddenType(
- NameLoc, diag::err_arc_array_param_no_ownership, T, false));
+ Loc, diag::err_arc_array_param_no_ownership, T, false));
}
lifetime = Qualifiers::OCL_ExplicitNone;
} else {
@@ -11829,6 +11833,16 @@
T = Context.getLifetimeQualifiedType(T, lifetime);
}
+ return T;
+}
+
+ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
+ SourceLocation NameLoc, IdentifierInfo *Name,
+ QualType T, TypeSourceInfo *TSInfo,
+ StorageClass SC) {
+ // Perform Objective-C ARC adjustments.
+ T = adjustParameterTypeForObjCAutoRefCount(T, NameLoc);
+
ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name,
Context.getAdjustedParameterType(T),
TSInfo, SC, nullptr);
@@ -12594,7 +12608,9 @@
// Always attach attributes to the underlying decl.
if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
D = TD->getTemplatedDecl();
- ProcessDeclAttributeList(S, D, Attrs.getList());
+
+ ProcessDeclAttributeList(S, D, Attrs.getList());
+ ProcessAPINotes(D);
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))
if (Method->isStatic())
@@ -13925,6 +13941,12 @@
Invalid = true;
}
+ if (!Invalid && TUK == TUK_Definition && DC->getDeclKind() == Decl::Enum) {
+ Diag(New->getLocation(), diag::err_type_defined_in_enum)
+ << Context.getTagDeclType(New);
+ Invalid = true;
+ }
+
// Maybe add qualifier info.
if (SS.isNotEmpty()) {
if (SS.isSet()) {
@@ -15248,6 +15270,7 @@
if (Attr)
ProcessDeclAttributeList(S, Record, Attr);
+ ProcessAPINotes(Record);
}
/// \brief Determine whether the given integral value is representable within
@@ -15548,6 +15571,8 @@
if (Attr) ProcessDeclAttributeList(S, New, Attr);
AddPragmaAttributes(S, New);
+ ProcessAPINotes(New);
+
// Register this decl in the current scope stack.
New->setAccess(TheEnumDecl->getAccess());
PushOnScopeChains(New, S);
@@ -15771,6 +15796,7 @@
if (Attr)
ProcessDeclAttributeList(S, Enum, Attr);
+ ProcessAPINotes(Enum);
if (Enum->isDependentType()) {
for (unsigned i = 0, e = Elements.size(); i != e; ++i) {
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 2a310bf..8249715 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -1517,6 +1517,26 @@
Attr.getAttributeSpellingListIndex()));
}
+static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D);
+ if (!PD)
+ return;
+
+ // noescape only applies to pointer types.
+ QualType T = PD->getType();
+ if (!T->isAnyPointerType() && !T->isBlockPointerType() &&
+ !T->isReferenceType() && !T->isArrayType() &&
+ !T->isMemberPointerType()) {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_noescape_non_pointer)
+ << T;
+ return;
+ }
+
+ D->addAttr(::new (S.Context) NoEscapeAttr(
+ Attr.getRange(), S.Context,
+ Attr.getAttributeSpellingListIndex()));
+}
+
static void handleAssumeAlignedAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
Expr *E = Attr.getArgAsExpr(0),
@@ -2441,6 +2461,15 @@
dyn_cast_or_null<StringLiteral>(Attr.getReplacementExpr()))
Replacement = SE->getString();
+ if (II->getName() == "swift") {
+ if (Introduced.isValid() || Obsoleted.isValid() ||
+ (!IsUnavailable && !Deprecated.isValid())) {
+ S.Diag(Attr.getLoc(),
+ diag::warn_availability_swift_unavailable_deprecated_only);
+ return;
+ }
+ }
+
AvailabilityAttr *NewAttr = S.mergeAvailabilityAttr(ND, Attr.getRange(), II,
false/*Implicit*/,
Introduced.Version,
@@ -4029,6 +4058,27 @@
AttrSpellingListIndex);
}
+SwiftNameAttr *Sema::mergeSwiftNameAttr(Decl *D, SourceRange Range,
+ StringRef Name, bool Override,
+ unsigned AttrSpellingListIndex) {
+ if (SwiftNameAttr *Inline = D->getAttr<SwiftNameAttr>()) {
+ if (Override) {
+ // FIXME: Warn about an incompatible override.
+ return nullptr;
+ }
+
+ if (Inline->getName() != Name && !Inline->isImplicit()) {
+ Diag(Inline->getLocation(), diag::warn_attribute_ignored) << Inline;
+ Diag(Range.getBegin(), diag::note_conflicting_attribute);
+ }
+
+ D->dropAttr<SwiftNameAttr>();
+ }
+
+ return ::new (Context) SwiftNameAttr(Range, Context, Name,
+ AttrSpellingListIndex);
+}
+
static void handleAlwaysInlineAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<NotTailCalledAttr>(S, D, Attr.getRange(),
@@ -4856,6 +4906,40 @@
attr.getAttributeSpellingListIndex()));
}
+static void handleNSErrorDomain(Sema &S, Decl *D, const AttributeList &Attr) {
+ if (!isa<TagDecl>(D)) {
+ S.Diag(D->getLocStart(), diag::err_nserrordomain_not_tagdecl)
+ << S.getLangOpts().CPlusPlus;
+ return;
+ }
+ IdentifierLoc *identLoc =
+ Attr.isArgIdent(0) ? Attr.getArgAsIdent(0) : nullptr;
+ if (!identLoc || !identLoc->Ident) {
+ // Try to locate the argument directly
+ SourceLocation loc = Attr.getLoc();
+ if (Attr.isArgExpr(0) && Attr.getArgAsExpr(0))
+ loc = Attr.getArgAsExpr(0)->getLocStart();
+
+ S.Diag(loc, diag::err_nserrordomain_requires_identifier);
+ return;
+ }
+
+ // Verify that the identifier is a valid decl in the C decl namespace
+ LookupResult lookupResult(S, DeclarationName(identLoc->Ident),
+ SourceLocation(),
+ Sema::LookupNameKind::LookupOrdinaryName);
+ if (!S.LookupName(lookupResult, S.TUScope) ||
+ !lookupResult.getAsSingle<VarDecl>()) {
+ S.Diag(identLoc->Loc, diag::err_nserrordomain_invalid_decl)
+ << identLoc->Ident;
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ NSErrorDomainAttr(Attr.getRange(), S.Context, identLoc->Ident,
+ Attr.getAttributeSpellingListIndex()));
+}
+
static void handleCFAuditedTransferAttr(Sema &S, Decl *D,
const AttributeList &Attr) {
if (checkAttrMutualExclusion<CFUnknownTransferAttr>(S, D, Attr.getRange(),
@@ -5045,6 +5129,421 @@
Attr.getAttributeSpellingListIndex()));
}
+static Optional<unsigned>
+validateSwiftFunctionName(StringRef Name,
+ unsigned &SwiftParamCount,
+ bool &IsSingleParamInit) {
+ SwiftParamCount = 0;
+
+ // Check whether this will be mapped to a getter or setter of a
+ // property.
+ bool isGetter = false;
+ bool isSetter = false;
+ if (Name.startswith("getter:")) {
+ isGetter = true;
+ Name = Name.substr(7);
+ } else if (Name.startswith("setter:")) {
+ isSetter = true;
+ Name = Name.substr(7);
+ }
+
+ if (Name.back() != ')')
+ return diag::warn_attr_swift_name_function;
+
+ StringRef BaseName, Parameters;
+ std::tie(BaseName, Parameters) = Name.split('(');
+
+ // Split at the first '.', if it exists, which separates the context
+ // name from the base name.
+ StringRef ContextName;
+ bool IsMember = false;
+ std::tie(ContextName, BaseName) = BaseName.split('.');
+ if (BaseName.empty()) {
+ BaseName = ContextName;
+ ContextName = StringRef();
+ } else if (ContextName.empty() || !isValidIdentifier(ContextName)) {
+ return diag::warn_attr_swift_name_context_name_invalid_identifier;
+ } else {
+ IsMember = true;
+ }
+
+ if (!isValidIdentifier(BaseName) || BaseName == "_")
+ return diag::warn_attr_swift_name_basename_invalid_identifier;
+
+ bool IsSubscript = BaseName == "subscript";
+ // A subscript accessor must be a getter or setter.
+ if (IsSubscript && !isGetter && !isSetter)
+ return diag::warn_attr_swift_name_subscript_not_accessor;
+
+ if (Parameters.empty())
+ return diag::warn_attr_swift_name_missing_parameters;
+ Parameters = Parameters.drop_back(); // ')'
+
+ if (Parameters.empty()) {
+ // Setters and subscripts must have at least one parameter.
+ if (IsSubscript)
+ return diag::warn_attr_swift_name_subscript_no_parameter;
+ if (isSetter)
+ return diag::warn_attr_swift_name_setter_parameters;
+
+ return None;
+ }
+
+ if (Parameters.back() != ':')
+ return diag::warn_attr_swift_name_function;
+
+ Optional<unsigned> SelfLocation;
+ Optional<unsigned> NewValueLocation;
+ unsigned NewValueCount = 0;
+ StringRef NextParam;
+ do {
+ std::tie(NextParam, Parameters) = Parameters.split(':');
+
+ if (!isValidIdentifier(NextParam))
+ return diag::warn_attr_swift_name_parameter_invalid_identifier;
+
+ // "self" indicates the "self" argument for a member.
+ if (IsMember && NextParam == "self") {
+ // More than one "self"?
+ if (SelfLocation) return diag::warn_attr_swift_name_multiple_selfs;
+
+ // The "self" location is the current parameter.
+ SelfLocation = SwiftParamCount;
+ }
+
+ // "newValue" indicates the "newValue" argument for a setter.
+ if (NextParam == "newValue") {
+ // There should only be one 'newValue', but it's only significant for
+ // subscript accessors, so don't error right away.
+ ++NewValueCount;
+
+ NewValueLocation = SwiftParamCount;
+ }
+ ++SwiftParamCount;
+ } while (!Parameters.empty());
+
+ // Only instance subscripts are currently supported.
+ if (IsSubscript && !SelfLocation)
+ return diag::warn_attr_swift_name_static_subscript;
+
+ IsSingleParamInit =
+ (SwiftParamCount == 1 && BaseName == "init" && NextParam != "_");
+
+ // Check the number of parameters for a getter/setter.
+ if (isGetter || isSetter) {
+ // Setters have one parameter for the new value.
+ unsigned NumExpectedParams;
+ unsigned ParamDiag;
+
+ if (isSetter) {
+ NumExpectedParams = 1;
+ ParamDiag = diag::warn_attr_swift_name_setter_parameters;
+ } else {
+ NumExpectedParams = 0;
+ ParamDiag = diag::warn_attr_swift_name_getter_parameters;
+ }
+
+ // Instance methods have one parameter for "self".
+ if (SelfLocation) ++NumExpectedParams;
+
+ // Subscripts may have additional parameters beyond the expected params for
+ // the index.
+ if (IsSubscript) {
+ if (SwiftParamCount < NumExpectedParams)
+ return ParamDiag;
+ // A subscript setter must explicitly label its newValue parameter to
+ // distinguish it from index parameters.
+ if (isSetter) {
+ if (!NewValueLocation)
+ return diag::warn_attr_swift_name_subscript_setter_no_newValue;
+ // There can only be one.
+ if (NewValueCount > 1)
+ return diag::warn_attr_swift_name_subscript_setter_multiple_newValues;
+ } else {
+ // Subscript getters should have no 'newValue:' parameter.
+ if (NewValueLocation)
+ return diag::warn_attr_swift_name_subscript_getter_newValue;
+ }
+ } else {
+ // Property accessors must have exactly the number of expected params.
+ if (SwiftParamCount != NumExpectedParams)
+ return ParamDiag;
+ }
+ }
+
+ return None;
+}
+
+/// Do a check to make sure \p Name looks like a legal swift_name
+/// attribute for the decl \p D. Raise a diagnostic if the name is invalid
+/// for the given declaration.
+///
+/// For a function, this will validate a compound Swift name,
+/// e.g. <code>init(foo:bar:baz:)</code> or <code>controllerForName(_:)</code>,
+/// and the function will output the number of parameter names, and whether this
+/// is a single-arg initializer.
+///
+/// For a type, enum constant, property, or variable declaration, this will
+/// validate either a simple identifier, or a qualified
+/// <code>context.identifier</code> name.
+///
+/// \returns true if the name is a valid swift name for \p D, false otherwise.
+bool Sema::DiagnoseSwiftName(Decl *D, StringRef Name,
+ SourceLocation ArgLoc,
+ IdentifierInfo *AttrName) {
+ if (isa<ObjCMethodDecl>(D) || isa<FunctionDecl>(D)) {
+ ArrayRef<ParmVarDecl*> Params;
+ unsigned ParamCount;
+
+ if (const auto *Method = dyn_cast<ObjCMethodDecl>(D)) {
+ ParamCount = Method->getSelector().getNumArgs();
+ Params = Method->parameters().slice(0, ParamCount);
+ } else {
+ const auto *Function = cast<FunctionDecl>(D);
+ ParamCount = Function->getNumParams();
+ Params = Function->parameters();
+
+ if (!Function->hasWrittenPrototype()) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_function_no_prototype)
+ << AttrName;
+ return false;
+ }
+ }
+
+ unsigned SwiftParamCount;
+ bool IsSingleParamInit;
+ if (auto diagID = validateSwiftFunctionName(Name, SwiftParamCount,
+ IsSingleParamInit)) {
+ Diag(ArgLoc, *diagID) << AttrName;
+ return false;
+ }
+
+ bool ParamsOK;
+ if (SwiftParamCount == ParamCount) {
+ ParamsOK = true;
+ } else if (SwiftParamCount > ParamCount) {
+ ParamsOK = IsSingleParamInit && ParamCount == 0;
+ } else {
+ // We have fewer Swift parameters than Objective-C parameters, but that
+ // might be because we've transformed some of them. Check for potential
+ // "out" parameters and err on the side of not warning.
+ unsigned MaybeOutParamCount =
+ std::count_if(Params.begin(), Params.end(),
+ [](const ParmVarDecl *Param) -> bool {
+ QualType ParamTy = Param->getType();
+ if (ParamTy->isReferenceType() || ParamTy->isPointerType())
+ return !ParamTy->getPointeeType().isConstQualified();
+ return false;
+ });
+ ParamsOK = (SwiftParamCount + MaybeOutParamCount >= ParamCount);
+ }
+
+ if (!ParamsOK) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_num_params)
+ << (SwiftParamCount > ParamCount) << AttrName
+ << ParamCount << SwiftParamCount;
+ return false;
+ }
+
+ } else if (isa<EnumConstantDecl>(D) || isa<ObjCProtocolDecl>(D) ||
+ isa<ObjCInterfaceDecl>(D) || isa<ObjCPropertyDecl>(D) ||
+ isa<VarDecl>(D) || isa<TypedefNameDecl>(D) || isa<TagDecl>(D) ||
+ isa<IndirectFieldDecl>(D) || isa<FieldDecl>(D)) {
+ StringRef ContextName, BaseName;
+ std::tie(ContextName, BaseName) = Name.split('.');
+ if (BaseName.empty()) {
+ BaseName = ContextName;
+ ContextName = StringRef();
+ } else if (!isValidIdentifier(ContextName)) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_context_name_invalid_identifier)
+ << AttrName;
+ return false;
+ }
+
+ if (!isValidIdentifier(BaseName)) {
+ Diag(ArgLoc, diag::warn_attr_swift_name_basename_invalid_identifier)
+ << AttrName;
+ return false;
+ }
+
+ } else {
+ Diag(ArgLoc, diag::warn_attr_swift_name_decl_kind) << AttrName;
+ return false;
+ }
+ return true;
+}
+
+static void handleSwiftName(Sema &S, Decl *D, const AttributeList &Attr) {
+ StringRef Name;
+ SourceLocation ArgLoc;
+ if (!S.checkStringLiteralArgumentAttr(Attr, 0, Name, &ArgLoc))
+ return;
+
+ if (!S.DiagnoseSwiftName(D, Name, ArgLoc, Attr.getName()))
+ return;
+
+ D->addAttr(::new (S.Context) SwiftNameAttr(Attr.getRange(), S.Context, Name,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static bool isErrorParameter(Sema &S, QualType paramType) {
+ if (auto ptr = paramType->getAs<PointerType>()) {
+ auto outerPointee = ptr->getPointeeType();
+
+ // NSError**.
+ if (auto objcPtr = outerPointee->getAs<ObjCObjectPointerType>()) {
+ if (auto iface = objcPtr->getInterfaceDecl())
+ if (iface->getIdentifier() == S.getNSErrorIdent())
+ return true;
+ }
+
+ // CFErrorRef*.
+ if (auto cPtr = outerPointee->getAs<PointerType>()) {
+ auto innerPointee = cPtr->getPointeeType();
+ if (auto recordType = innerPointee->getAs<RecordType>()) {
+ if (S.isCFError(recordType->getDecl()))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void handleSwiftError(Sema &S, Decl *D, const AttributeList &attr) {
+ SwiftErrorAttr::ConventionKind convention;
+ IdentifierLoc *conventionLoc = attr.getArgAsIdent(0);
+ StringRef conventionStr = conventionLoc->Ident->getName();
+ if (!SwiftErrorAttr::ConvertStrToConventionKind(conventionStr, convention)) {
+ S.Diag(attr.getLoc(), diag::warn_attribute_type_not_supported)
+ << attr.getName() << conventionLoc->Ident;
+ return;
+ }
+
+ auto requireErrorParameter = [&]() -> bool {
+ if (D->isInvalidDecl()) return true;
+
+ for (unsigned i = 0, e = getFunctionOrMethodNumParams(D); i != e; ++i) {
+ if (isErrorParameter(S, getFunctionOrMethodParamType(D, i)))
+ return true;
+ }
+
+ S.Diag(attr.getLoc(), diag::err_attr_swift_error_no_error_parameter)
+ << attr.getName() << isa<ObjCMethodDecl>(D);
+ return false;
+ };
+
+ auto requirePointerResult = [&] {
+ if (D->isInvalidDecl()) return true;
+
+ // C, ObjC, and block pointers are definitely okay.
+ // References are definitely not okay.
+ // nullptr_t is weird but acceptable.
+ QualType returnType = getFunctionOrMethodResultType(D);
+ if (returnType->hasPointerRepresentation() &&
+ !returnType->isReferenceType()) return true;
+
+ S.Diag(attr.getLoc(), diag::err_attr_swift_error_return_type)
+ << attr.getName() << conventionStr
+ << isa<ObjCMethodDecl>(D) << /*pointer*/ 1;
+ return false;
+ };
+
+ auto requireIntegerResult = [&] {
+ if (D->isInvalidDecl()) return true;
+
+ QualType returnType = getFunctionOrMethodResultType(D);
+ if (returnType->isIntegralType(S.Context)) return true;
+
+ S.Diag(attr.getLoc(), diag::err_attr_swift_error_return_type)
+ << attr.getName() << conventionStr
+ << isa<ObjCMethodDecl>(D) << /*integral*/ 0;
+ return false;
+ };
+
+ switch (convention) {
+ case SwiftErrorAttr::None:
+ // No additional validation required.
+ break;
+
+ case SwiftErrorAttr::NonNullError:
+ if (!requireErrorParameter()) return;
+ break;
+
+ case SwiftErrorAttr::NullResult:
+ if (!requireErrorParameter()) return;
+ if (!requirePointerResult()) return;
+ break;
+
+ case SwiftErrorAttr::NonZeroResult:
+ case SwiftErrorAttr::ZeroResult:
+ if (!requireErrorParameter()) return;
+ if (!requireIntegerResult()) return;
+ break;
+ }
+
+ D->addAttr(::new (S.Context)
+ SwiftErrorAttr(attr.getRange(), S.Context, convention,
+ attr.getAttributeSpellingListIndex()));
+}
+
+static void handleSwiftBridgeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // Make sure that there is a string literal as the annotation's single
+ // argument.
+ StringRef Str;
+ if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str))
+ return;
+
+ // Don't duplicate annotations that are already set.
+ if (D->hasAttr<SwiftBridgeAttr>()) {
+ S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr.getName();
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ SwiftBridgeAttr(Attr.getRange(), S.Context, Str,
+ Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleSwiftNewtypeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
+ // Make sure that there is an identifier as the annotation's single
+ // argument.
+ if (Attr.getNumArgs() != 1) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments)
+ << Attr.getName() << 1;
+ Attr.setInvalid();
+ return;
+ }
+ if (!Attr.isArgIdent(0)) {
+ S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
+ << Attr.getName() << AANT_ArgumentIdentifier;
+ Attr.setInvalid();
+ return;
+ }
+
+ IdentifierInfo *II = Attr.getArgAsIdent(0)->Ident;
+ SwiftNewtypeAttr::NewtypeKind Kind;
+ if (II->isStr("struct"))
+ Kind = SwiftNewtypeAttr::NK_Struct;
+ else if (II->isStr("enum"))
+ Kind = SwiftNewtypeAttr::NK_Enum;
+ else {
+ S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported)
+ << Attr.getName() << II;
+ Attr.setInvalid();
+ return;
+ }
+
+ if (!isa<TypedefNameDecl>(D)) {
+ S.Diag(Attr.getLoc(), diag::warn_swift_newtype_attribute_non_typedef);
+ return;
+ }
+
+ D->addAttr(::new (S.Context)
+ SwiftNewtypeAttr(Attr.getRange(), S.Context, Kind,
+ Attr.getAttributeSpellingListIndex()));
+}
+
//===----------------------------------------------------------------------===//
// Microsoft specific attribute handlers.
//===----------------------------------------------------------------------===//
@@ -6120,6 +6619,9 @@
case AttributeList::AT_ReturnsNonNull:
handleReturnsNonNullAttr(S, D, Attr);
break;
+ case AttributeList::AT_NoEscape:
+ handleNoEscapeAttr(S, D, Attr);
+ break;
case AttributeList::AT_AssumeAligned:
handleAssumeAlignedAttr(S, D, Attr);
break;
@@ -6186,6 +6688,9 @@
case AttributeList::AT_ObjCBoxable:
handleObjCBoxable(S, D, Attr);
break;
+ case AttributeList::AT_NSErrorDomain:
+ handleNSErrorDomain(S, D, Attr);
+ break;
case AttributeList::AT_CFAuditedTransfer:
handleCFAuditedTransferAttr(S, D, Attr);
break;
@@ -6245,6 +6750,9 @@
case AttributeList::AT_ObjCSubclassingRestricted:
handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, Attr);
break;
+ case AttributeList::AT_ObjCCompleteDefinition:
+ handleSimpleAttribute<ObjCCompleteDefinitionAttr>(S, D, Attr);
+ break;
case AttributeList::AT_ObjCExplicitProtocolImpl:
handleObjCSuppresProtocolAttr(S, D, Attr);
break;
@@ -6517,6 +7025,25 @@
case AttributeList::AT_RenderScriptKernel:
handleSimpleAttribute<RenderScriptKernelAttr>(S, D, Attr);
break;
+ // Swift attributes.
+ case AttributeList::AT_SwiftPrivate:
+ handleSimpleAttribute<SwiftPrivateAttr>(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftName:
+ handleSwiftName(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftError:
+ handleSwiftError(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftBridge:
+ handleSwiftBridgeAttr(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftObjCMembers:
+ handleSimpleAttribute<SwiftObjCMembersAttr>(S, D, Attr);
+ break;
+ case AttributeList::AT_SwiftNewtype:
+ handleSwiftNewtypeAttr(S, D, Attr);
+ break;
// XRay attributes.
case AttributeList::AT_XRayInstrument:
handleSimpleAttribute<XRayInstrumentAttr>(S, D, Attr);
@@ -6759,6 +7286,9 @@
// Apply additional attributes specified by '#pragma clang attribute'.
AddPragmaAttributes(S, D);
+
+ // Look for API notes that map to attributes.
+ ProcessAPINotes(D);
}
/// Is the given declaration allowed to use a forbidden type?
@@ -7003,6 +7533,49 @@
return dyn_cast<NamedDecl>(OrigCtx);
}
+namespace {
+
+struct AttributeInsertion {
+ StringRef Prefix;
+ SourceLocation Loc;
+ StringRef Suffix;
+
+ static AttributeInsertion createInsertionAfter(const NamedDecl *D) {
+ return {" ", D->getLocEnd(), ""};
+ }
+ static AttributeInsertion createInsertionAfter(SourceLocation Loc) {
+ return {" ", Loc, ""};
+ }
+ static AttributeInsertion createInsertionBefore(const NamedDecl *D) {
+ return {"", D->getLocStart(), "\n"};
+ }
+};
+
+} // end anonymous namespace
+
+/// Returns a source location in which it's appropriate to insert a new
+/// attribute for the given declaration \D.
+static Optional<AttributeInsertion>
+createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (isa<ObjCPropertyDecl>(D))
+ return AttributeInsertion::createInsertionAfter(D);
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (MD->hasBody())
+ return None;
+ return AttributeInsertion::createInsertionAfter(D);
+ }
+ if (const auto *TD = dyn_cast<TagDecl>(D)) {
+ SourceLocation Loc =
+ Lexer::getLocForEndOfToken(TD->getInnerLocStart(), 0, SM, LangOpts);
+ if (Loc.isInvalid())
+ return None;
+ // Insert after the 'struct'/whatever keyword.
+ return AttributeInsertion::createInsertionAfter(Loc);
+ }
+ return AttributeInsertion::createInsertionBefore(D);
+}
+
/// Actually emit an availability diagnostic for a reference to an unavailable
/// decl.
///
@@ -7192,8 +7765,29 @@
<< /*Anonymous*/1 << TD->getKindName();
return;
}
- S.Diag(Enclosing->getLocation(), diag::note_partial_availability_silence)
- << /*Named*/0 << Enclosing;
+ auto FixitNoteDiag = S.Diag(Enclosing->getLocation(),
+ diag::note_partial_availability_silence)
+ << /*Named*/ 0 << Enclosing;
+ // Don't offer a fixit for declarations with availability attributes.
+ if (Enclosing->hasAttr<AvailabilityAttr>())
+ return;
+ if (!S.getPreprocessor().isMacroDefined("API_AVAILABLE"))
+ return;
+ Optional<AttributeInsertion> Insertion = createAttributeInsertion(
+ Enclosing, S.getSourceManager(), S.getLangOpts());
+ if (!Insertion)
+ return;
+ std::string PlatformName =
+ AvailabilityAttr::getPlatformNameSourceSpelling(
+ S.getASTContext().getTargetInfo().getPlatformName())
+ .lower();
+ std::string Introduced =
+ OffendingDecl->getVersionIntroduced().getAsString();
+ FixitNoteDiag << FixItHint::CreateInsertion(
+ Insertion->Loc,
+ (llvm::Twine(Insertion->Prefix) + "API_AVAILABLE(" + PlatformName +
+ "(" + Introduced + "))" + Insertion->Suffix)
+ .str());
}
}
@@ -7581,8 +8175,7 @@
// If we're using the '*' case here or if this check is redundant, then we
// use the enclosing version to check both branches.
if (CondVersion.empty() || CondVersion <= AvailabilityStack.back())
- return Base::TraverseStmt(If->getThen()) &&
- Base::TraverseStmt(If->getElse());
+ return TraverseStmt(If->getThen()) && TraverseStmt(If->getElse());
} else {
// This isn't an availability checking 'if', we can just continue.
return Base::TraverseIfStmt(If);
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index c05e5f0..081864c 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -5726,53 +5726,6 @@
}
}
-/// Determine whether a type is permitted to be passed or returned in
-/// registers, per C++ [class.temporary]p3.
-static bool computeCanPassInRegisters(Sema &S, CXXRecordDecl *D) {
- if (D->isDependentType() || D->isInvalidDecl())
- return false;
-
- // Per C++ [class.temporary]p3, the relevant condition is:
- // each copy constructor, move constructor, and destructor of X is
- // either trivial or deleted, and X has at least one non-deleted copy
- // or move constructor
- bool HasNonDeletedCopyOrMove = false;
-
- if (D->needsImplicitCopyConstructor() &&
- !D->defaultedCopyConstructorIsDeleted()) {
- if (!D->hasTrivialCopyConstructor())
- return false;
- HasNonDeletedCopyOrMove = true;
- }
-
- if (S.getLangOpts().CPlusPlus11 && D->needsImplicitMoveConstructor() &&
- !D->defaultedMoveConstructorIsDeleted()) {
- if (!D->hasTrivialMoveConstructor())
- return false;
- HasNonDeletedCopyOrMove = true;
- }
-
- if (D->needsImplicitDestructor() && !D->defaultedDestructorIsDeleted() &&
- !D->hasTrivialDestructor())
- return false;
-
- for (const CXXMethodDecl *MD : D->methods()) {
- if (MD->isDeleted())
- continue;
-
- auto *CD = dyn_cast<CXXConstructorDecl>(MD);
- if (CD && CD->isCopyOrMoveConstructor())
- HasNonDeletedCopyOrMove = true;
- else if (!isa<CXXDestructorDecl>(MD))
- continue;
-
- if (!MD->isTrivial())
- return false;
- }
-
- return HasNonDeletedCopyOrMove;
-}
-
/// \brief Perform semantic checks on a class definition that has been
/// completing, introducing implicitly-declared members, checking for
/// abstract types, etc.
@@ -5917,8 +5870,6 @@
}
checkClassLevelDLLAttribute(Record);
-
- Record->setCanPassInRegisters(computeCanPassInRegisters(*this, Record));
}
/// Look up the special member function that would be called by a special
@@ -7545,7 +7496,8 @@
reinterpret_cast<Decl**>(FieldCollector->getCurFields()),
FieldCollector->getCurNumFields()), LBrac, RBrac, AttrList);
- CheckCompletedCXXClass(dyn_cast_or_null<CXXRecordDecl>(TagDecl));
+ CheckCompletedCXXClass(
+ dyn_cast_or_null<CXXRecordDecl>(TagDecl));
}
/// AddImplicitlyDeclaredMembersToClass - Adds any implicitly-declared
@@ -8514,6 +8466,7 @@
ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList);
AddPragmaAttributes(DeclRegionScope, Namespc);
+ ProcessAPINotes(Namespc);
// FIXME: Should we be merging attributes?
if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>())
@@ -8903,6 +8856,7 @@
if (UDir)
ProcessDeclAttributeList(S, UDir, AttrList);
+ ProcessAPINotes(UDir);
return UDir;
}
@@ -10001,6 +9955,7 @@
ProcessDeclAttributeList(S, NewTD, AttrList);
AddPragmaAttributes(S, NewTD);
+ ProcessAPINotes(NewTD);
CheckTypedefForVariablyModifiedType(S, NewTD);
Invalid |= NewTD->isInvalidDecl();
@@ -11977,10 +11932,8 @@
Scope *S = getScopeForContext(ClassDecl);
CheckImplicitSpecialMemberDeclaration(S, CopyConstructor);
- if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor)) {
- ClassDecl->setImplicitCopyConstructorIsDeleted();
+ if (ShouldDeleteSpecialMember(CopyConstructor, CXXCopyConstructor))
SetDeclDeleted(CopyConstructor, ClassLoc);
- }
if (S)
PushOnScopeChains(CopyConstructor, S, false);
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index 9675730..b132381 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -20,6 +20,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Edit/RefactoringFixits.h"
#include "clang/Sema/DeclSpec.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
@@ -1004,6 +1005,10 @@
ObjCInterfaceDecl *IDecl
= ObjCInterfaceDecl::Create(Context, CurContext, AtInterfaceLoc, ClassName,
typeParamList, PrevIDecl, ClassLoc);
+ if (AttrList)
+ ProcessDeclAttributeList(TUScope, IDecl, AttrList);
+ ProcessAPINotes(IDecl);
+
if (PrevIDecl) {
// Class already seen. Was it a definition?
if (ObjCInterfaceDecl *Def = PrevIDecl->getDefinition()) {
@@ -1014,8 +1019,6 @@
}
}
- if (AttrList)
- ProcessDeclAttributeList(TUScope, IDecl, AttrList);
AddPragmaAttributes(TUScope, IDecl);
PushOnScopeChains(IDecl, TUScope);
@@ -1116,7 +1119,8 @@
// Everything checked out, instantiate a new alias declaration AST.
ObjCCompatibleAliasDecl *AliasDecl =
- ObjCCompatibleAliasDecl::Create(Context, CurContext, AtLoc, AliasName, CDecl);
+ ObjCCompatibleAliasDecl::Create(Context, CurContext, AliasLocation,
+ AliasName, CDecl, ClassLocation, AtLoc);
if (!CheckObjCDeclScope(AliasDecl))
PushOnScopeChains(AliasDecl, TUScope);
@@ -1201,6 +1205,7 @@
if (AttrList)
ProcessDeclAttributeList(TUScope, PDecl, AttrList);
AddPragmaAttributes(TUScope, PDecl);
+ ProcessAPINotes(PDecl);
// Merge attributes from previous declarations.
if (PrevDecl)
@@ -1725,13 +1730,15 @@
= ObjCProtocolDecl::Create(Context, CurContext, Ident,
IdentPair.second, AtProtocolLoc,
PrevDecl);
-
+ ProcessAPINotes(PDecl);
+
PushOnScopeChains(PDecl, TUScope);
CheckObjCDeclScope(PDecl);
if (attrList)
ProcessDeclAttributeList(TUScope, PDecl, attrList);
AddPragmaAttributes(TUScope, PDecl);
+ ProcessAPINotes(PDecl);
if (PrevDecl)
mergeDeclAttributes(PDecl, PrevDecl);
@@ -2142,23 +2149,28 @@
Diag(IVI->getLocation(), diag::err_inconsistent_ivar_count);
}
-static void WarnUndefinedMethod(Sema &S, SourceLocation ImpLoc,
- ObjCMethodDecl *method,
- bool &IncompleteImpl,
- unsigned DiagID,
- NamedDecl *NeededFor = nullptr) {
+static bool shouldWarnUndefinedMethod(const ObjCMethodDecl *M) {
// No point warning no definition of method which is 'unavailable'.
- switch (method->getAvailability()) {
+ switch (M->getAvailability()) {
case AR_Available:
case AR_Deprecated:
- break;
+ return true;
- // Don't warn about unavailable or not-yet-introduced methods.
+ // Don't warn about unavailable or not-yet-introduced methods.
case AR_NotYetIntroduced:
case AR_Unavailable:
- return;
+ return false;
}
-
+ llvm_unreachable("Invalid availability");
+}
+
+static void WarnUndefinedMethod(Sema &S, SourceLocation ImpLoc,
+ ObjCMethodDecl *method, bool &IncompleteImpl,
+ unsigned DiagID,
+ NamedDecl *NeededFor = nullptr) {
+ if (!shouldWarnUndefinedMethod(method))
+ return;
+
// FIXME: For now ignore 'IncompleteImpl'.
// Previously we grouped all unimplemented methods under a single
// warning, but some users strongly voiced that they would prefer
@@ -2619,14 +2631,12 @@
/// CheckProtocolMethodDefs - This routine checks unimplemented methods
/// Declared in protocol, and those referenced by it.
-static void CheckProtocolMethodDefs(Sema &S,
- SourceLocation ImpLoc,
- ObjCProtocolDecl *PDecl,
- bool& IncompleteImpl,
- const Sema::SelectorSet &InsMap,
- const Sema::SelectorSet &ClsMap,
- ObjCContainerDecl *CDecl,
- LazyProtocolNameSet &ProtocolsExplictImpl) {
+static void CheckProtocolMethodDefs(
+ Sema &S, SourceLocation ImpLoc, ObjCProtocolDecl *PDecl,
+ bool &IncompleteImpl, const Sema::SelectorSet &InsMap,
+ const Sema::SelectorSet &ClsMap, ObjCContainerDecl *CDecl,
+ LazyProtocolNameSet &ProtocolsExplictImpl,
+ llvm::SmallPtrSetImpl<ObjCProtocolDecl *> *MissingRequirements) {
ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl);
ObjCInterfaceDecl *IDecl = C ? C->getClassInterface()
: dyn_cast<ObjCInterfaceDecl>(CDecl);
@@ -2686,6 +2696,7 @@
// protocol. This lookup is slow, but occurs rarely in correct code
// and otherwise would terminate in a warning.
+ bool HasMissingRequirements = false;
// check unimplemented instance methods.
if (!NSIDecl)
for (auto *method : PDecl->instance_methods()) {
@@ -2715,8 +2726,13 @@
continue;
unsigned DIAG = diag::warn_unimplemented_protocol_method;
if (!S.Diags.isIgnored(DIAG, ImpLoc)) {
- WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG,
- PDecl);
+ if (MissingRequirements) {
+ if (!HasMissingRequirements)
+ HasMissingRequirements = shouldWarnUndefinedMethod(method);
+ } else {
+ WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG,
+ PDecl);
+ }
}
}
}
@@ -2738,14 +2754,23 @@
unsigned DIAG = diag::warn_unimplemented_protocol_method;
if (!S.Diags.isIgnored(DIAG, ImpLoc)) {
- WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, PDecl);
+ if (MissingRequirements) {
+ if (!HasMissingRequirements)
+ HasMissingRequirements = shouldWarnUndefinedMethod(method);
+ } else {
+ WarnUndefinedMethod(S, ImpLoc, method, IncompleteImpl, DIAG, PDecl);
+ }
}
}
}
+ if (HasMissingRequirements) {
+ assert(MissingRequirements != nullptr && "No missing requirements!");
+ MissingRequirements->insert(PDecl);
+ }
// Check on this protocols's referenced protocols, recursively.
for (auto *PI : PDecl->protocols())
CheckProtocolMethodDefs(S, ImpLoc, PI, IncompleteImpl, InsMap, ClsMap,
- CDecl, ProtocolsExplictImpl);
+ CDecl, ProtocolsExplictImpl, MissingRequirements);
}
/// MatchAllMethodDeclarations - Check methods declared in interface
@@ -2957,23 +2982,49 @@
LazyProtocolNameSet ExplicitImplProtocols;
+ bool UseEditorDiagnostics = !getDiagnostics()
+ .getDiagnosticOptions()
+ .DiagnosticSerializationFile.empty() ||
+ getLangOpts().AllowEditorPlaceholders;
+ llvm::SmallPtrSet<ObjCProtocolDecl *, 4> MissingRequirements;
if (ObjCInterfaceDecl *I = dyn_cast<ObjCInterfaceDecl> (CDecl)) {
for (auto *PI : I->all_referenced_protocols())
CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), PI, IncompleteImpl,
- InsMap, ClsMap, I, ExplicitImplProtocols);
+ InsMap, ClsMap, I, ExplicitImplProtocols,
+ UseEditorDiagnostics ? &MissingRequirements
+ : nullptr);
} else if (ObjCCategoryDecl *C = dyn_cast<ObjCCategoryDecl>(CDecl)) {
// For extended class, unimplemented methods in its protocols will
// be reported in the primary class.
if (!C->IsClassExtension()) {
for (auto *P : C->protocols())
- CheckProtocolMethodDefs(*this, IMPDecl->getLocation(), P,
- IncompleteImpl, InsMap, ClsMap, CDecl,
- ExplicitImplProtocols);
+ CheckProtocolMethodDefs(
+ *this, IMPDecl->getLocation(), P, IncompleteImpl, InsMap, ClsMap,
+ CDecl, ExplicitImplProtocols,
+ UseEditorDiagnostics ? &MissingRequirements : nullptr);
DiagnoseUnimplementedProperties(S, IMPDecl, CDecl,
/*SynthesizeProperties=*/false);
}
} else
llvm_unreachable("invalid ObjCContainerDecl type.");
+ if (!MissingRequirements.empty()) {
+ {
+ auto DB = Diag(IMPDecl->getLocation(),
+ diag::warn_class_does_not_conform_protocol)
+ << (isa<ObjCCategoryDecl>(CDecl) ? /*category=*/1 : /*class=*/0)
+ << CDecl << (unsigned)MissingRequirements.size();
+ unsigned NumProtocols = 0;
+ for (const auto *PD : MissingRequirements) {
+ DB << PD;
+ if (++NumProtocols > 3)
+ break;
+ }
+ }
+ auto DB =
+ Diag(IMPDecl->getLocation(), diag::note_add_missing_protocol_stubs);
+ edit::fillInMissingProtocolStubs::addMissingProtocolStubs(
+ Context, IMPDecl, [&](const FixItHint &Hint) { DB << Hint; });
+ }
}
Sema::DeclGroupPtrTy
@@ -3063,6 +3114,7 @@
ClassName, TypeParams, PrevIDecl,
IdentLocs[i]);
IDecl->setAtEndRange(IdentLocs[i]);
+ ProcessAPINotes(IDecl);
PushOnScopeChains(IDecl, TUScope);
CheckObjCDeclScope(IDecl);
@@ -3863,7 +3915,7 @@
if (IDecl->getSuperClass() == nullptr) {
// This class has no superclass, so check that it has been marked with
// __attribute((objc_root_class)).
- if (!HasRootClassAttr) {
+ if (!HasRootClassAttr && !IDecl->hasAttr<ObjCCompleteDefinitionAttr>()) {
SourceLocation DeclLoc(IDecl->getLocation());
SourceLocation SuperClassLoc(getLocForEndOfToken(DeclLoc));
Diag(DeclLoc, diag::warn_objc_root_class_missing)
@@ -3958,12 +4010,10 @@
return (Decl::ObjCDeclQualifier) (unsigned) PQTVal;
}
-/// \brief Check whether the declared result type of the given Objective-C
-/// method declaration is compatible with the method's class.
-///
-static Sema::ResultTypeCompatibilityKind
-CheckRelatedResultTypeCompatibility(Sema &S, ObjCMethodDecl *Method,
- ObjCInterfaceDecl *CurrentClass) {
+Sema::ResultTypeCompatibilityKind
+Sema::checkRelatedResultTypeCompatibility(
+ const ObjCMethodDecl *Method,
+ const ObjCInterfaceDecl *CurrentClass) {
QualType ResultType = Method->getReturnType();
// If an Objective-C method inherits its related result type, then its
@@ -4463,6 +4513,7 @@
// Apply the attributes to the parameter.
ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs);
AddPragmaAttributes(TUScope, Param);
+ ProcessAPINotes(Param);
if (Param->hasAttr<BlocksAttr>()) {
Diag(Param->getLocation(), diag::err_block_on_nonlocal);
@@ -4494,6 +4545,7 @@
if (AttrList)
ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList);
AddPragmaAttributes(TUScope, ObjCMethod);
+ ProcessAPINotes(ObjCMethod);
// Add the method now.
const ObjCMethodDecl *PrevMethod = nullptr;
@@ -4549,7 +4601,7 @@
}
ResultTypeCompatibilityKind RTC
- = CheckRelatedResultTypeCompatibility(*this, ObjCMethod, CurrentClass);
+ = checkRelatedResultTypeCompatibility(ObjCMethod, CurrentClass);
CheckObjCMethodOverrides(ObjCMethod, CurrentClass, RTC);
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index d3d7d8b..9e09a2f 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -7396,6 +7396,14 @@
return CheckAssignmentConstraints(LHSType, RHSPtr, K, /*ConvertRHS=*/false);
}
+/// This helper function returns true if QT is a vector type that has element
+/// type ElementType.
+static bool isVector(QualType QT, QualType ElementType) {
+ if (const VectorType *VT = QT->getAs<VectorType>())
+ return VT->getElementType() == ElementType;
+ return false;
+}
+
/// CheckAssignmentConstraints (C99 6.5.16) - This routine currently
/// has code to accommodate several GCC extensions when type checking
/// pointers. Here are some objectionable examples that GCC considers warnings:
@@ -8018,6 +8026,25 @@
return false;
}
+/// Convert vector E to a vector with the same number of elements but different
+/// element type.
+static ExprResult convertVector(Expr *E, QualType ElementType, Sema &S) {
+ const auto *VecTy = E->getType()->getAs<VectorType>();
+ assert(VecTy && "Expression E must be a vector");
+ QualType NewVecTy = S.Context.getVectorType(ElementType,
+ VecTy->getNumElements(),
+ VecTy->getVectorKind());
+
+ // Look through the implicit cast. Return the subexpression if its type is
+ // NewVecTy.
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(E))
+ if (ICE->getSubExpr()->getType() == NewVecTy)
+ return ICE->getSubExpr();
+
+ auto Cast = ElementType->isIntegerType() ? CK_IntegralCast : CK_FloatingCast;
+ return S.ImpCastExprToType(E, NewVecTy, Cast);
+}
+
/// Test if a (constant) integer Int can be casted to another integer type
/// IntTy without losing precision.
static bool canConvertIntToOtherIntTy(Sema &S, ExprResult *Int,
@@ -11233,6 +11260,69 @@
return nullptr;
}
+// This helper function promotes a binary operator's operands (which are of a
+// half vector type) to a vector of floats and then truncates the result to
+// a vector of either half or short.
+static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS,
+ BinaryOperatorKind Opc, QualType ResultTy,
+ ExprValueKind VK, ExprObjectKind OK,
+ bool IsCompAssign, SourceLocation OpLoc,
+ FPOptions FPFeatures) {
+ auto &Context = S.getASTContext();
+ assert((isVector(ResultTy, Context.HalfTy) ||
+ isVector(ResultTy, Context.ShortTy)) &&
+ "Result must be a vector of half or short");
+ assert(isVector(LHS.get()->getType(), Context.HalfTy) &&
+ isVector(RHS.get()->getType(), Context.HalfTy) &&
+ "both operands expected to be a half vector");
+
+ RHS = convertVector(RHS.get(), Context.FloatTy, S);
+ QualType BinOpResTy = RHS.get()->getType();
+
+ // If Opc is a comparison, ResultType is a vector of shorts. In that case,
+ // change BinOpResTy to a vector of ints.
+ if (isVector(ResultTy, Context.ShortTy))
+ BinOpResTy = S.GetSignedVectorType(BinOpResTy);
+
+ if (IsCompAssign)
+ return new (Context) CompoundAssignOperator(
+ LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, BinOpResTy, BinOpResTy,
+ OpLoc, FPFeatures);
+
+ LHS = convertVector(LHS.get(), Context.FloatTy, S);
+ auto *BO = new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, BinOpResTy,
+ VK, OK, OpLoc, FPFeatures);
+ return convertVector(BO, ResultTy->getAs<VectorType>()->getElementType(), S);
+}
+
+static std::pair<ExprResult, ExprResult>
+CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr,
+ Expr *RHSExpr) {
+ ExprResult LHS = LHSExpr, RHS = RHSExpr;
+ if (!S.getLangOpts().CPlusPlus) {
+ // C cannot handle TypoExpr nodes on either side of a binop because it
+ // doesn't handle dependent types properly, so make sure any TypoExprs have
+ // been dealt with before checking the operands.
+ LHS = S.CorrectDelayedTyposInExpr(LHS);
+ RHS = S.CorrectDelayedTyposInExpr(RHS, [Opc, LHS](Expr *E) {
+ if (Opc != BO_Assign)
+ return ExprResult(E);
+ // Avoid correcting the RHS to the same Expr as the LHS.
+ Decl *D = getDeclFromExpr(E);
+ return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E;
+ });
+ }
+ return std::make_pair(LHS, RHS);
+}
+
+/// Returns true if conversion between vectors of halfs and vectors of floats
+/// is needed.
+static bool needsConversionOfHalfVec(bool OpRequiresConversion, ASTContext &Ctx,
+ QualType SrcType) {
+ return OpRequiresConversion && !Ctx.getLangOpts().NativeHalfType &&
+ Ctx.getLangOpts().HalfArgsAndReturns && isVector(SrcType, Ctx.HalfTy);
+}
+
/// CreateBuiltinBinOp - Creates a new built-in binary operation with
/// operator @p Opc at location @c TokLoc. This routine only supports
/// built-in operations; ActOnBinOp handles overloaded operators.
@@ -11264,22 +11354,11 @@
QualType CompResultTy; // Type of computation result
ExprValueKind VK = VK_RValue;
ExprObjectKind OK = OK_Ordinary;
+ bool ConvertHalfVec = false;
- if (!getLangOpts().CPlusPlus) {
- // C cannot handle TypoExpr nodes on either side of a binop because it
- // doesn't handle dependent types properly, so make sure any TypoExprs have
- // been dealt with before checking the operands.
- LHS = CorrectDelayedTyposInExpr(LHSExpr);
- RHS = CorrectDelayedTyposInExpr(RHSExpr, [Opc, LHS](Expr *E) {
- if (Opc != BO_Assign)
- return ExprResult(E);
- // Avoid correcting the RHS to the same Expr as the LHS.
- Decl *D = getDeclFromExpr(E);
- return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E;
- });
- if (!LHS.isUsable() || !RHS.isUsable())
- return ExprError();
- }
+ std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr);
+ if (!LHS.isUsable() || !RHS.isUsable())
+ return ExprError();
if (getLangOpts().OpenCL) {
QualType LHSTy = LHSExpr->getType();
@@ -11327,6 +11406,7 @@
break;
case BO_Mul:
case BO_Div:
+ ConvertHalfVec = true;
ResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, false,
Opc == BO_Div);
break;
@@ -11334,9 +11414,11 @@
ResultTy = CheckRemainderOperands(LHS, RHS, OpLoc);
break;
case BO_Add:
+ ConvertHalfVec = true;
ResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc);
break;
case BO_Sub:
+ ConvertHalfVec = true;
ResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc);
break;
case BO_Shl:
@@ -11347,10 +11429,12 @@
case BO_LT:
case BO_GE:
case BO_GT:
+ ConvertHalfVec = true;
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
break;
case BO_EQ:
case BO_NE:
+ ConvertHalfVec = true;
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false);
break;
case BO_And:
@@ -11362,10 +11446,12 @@
break;
case BO_LAnd:
case BO_LOr:
+ ConvertHalfVec = true;
ResultTy = CheckLogicalOperands(LHS, RHS, OpLoc, Opc);
break;
case BO_MulAssign:
case BO_DivAssign:
+ ConvertHalfVec = true;
CompResultTy = CheckMultiplyDivideOperands(LHS, RHS, OpLoc, true,
Opc == BO_DivAssign);
CompLHSTy = CompResultTy;
@@ -11379,11 +11465,13 @@
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy);
break;
case BO_AddAssign:
+ ConvertHalfVec = true;
CompResultTy = CheckAdditionOperands(LHS, RHS, OpLoc, Opc, &CompLHSTy);
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy);
break;
case BO_SubAssign:
+ ConvertHalfVec = true;
CompResultTy = CheckSubtractionOperands(LHS, RHS, OpLoc, &CompLHSTy);
if (!CompResultTy.isNull() && !LHS.isInvalid() && !RHS.isInvalid())
ResultTy = CheckAssignmentOperands(LHS.get(), RHS, OpLoc, CompResultTy);
@@ -11416,6 +11504,16 @@
if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid())
return ExprError();
+ // Some of the binary operations require promoting operands of half vector to
+ // float vectors and truncating the result back to half vector. For now, we do
+ // this only when HalfArgsAndReturn is set (that is, when the target is arm or
+ // arm64).
+ assert(isVector(RHS.get()->getType(), Context.HalfTy) ==
+ isVector(LHS.get()->getType(), Context.HalfTy) &&
+ "both sides are half vectors or neither sides are");
+ ConvertHalfVec = needsConversionOfHalfVec(ConvertHalfVec, Context,
+ LHS.get()->getType());
+
// Check for array bounds violations for both sides of the BinaryOperator
CheckArrayAccess(LHS.get());
CheckArrayAccess(RHS.get());
@@ -11438,14 +11536,26 @@
dyn_cast<ObjCIvarRefExpr>(LHS.get()->IgnoreParenCasts()))
DiagnoseDirectIsaAccess(*this, OIRE, OpLoc, RHS.get());
- if (CompResultTy.isNull())
+ // Opc is not a compound assignment if CompResultTy is null.
+ if (CompResultTy.isNull()) {
+ if (ConvertHalfVec)
+ return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false,
+ OpLoc, FPFeatures);
return new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, ResultTy, VK,
OK, OpLoc, FPFeatures);
+ }
+
+ // Handle compound assignments.
if (getLangOpts().CPlusPlus && LHS.get()->getObjectKind() !=
OK_ObjCProperty) {
VK = VK_LValue;
OK = LHS.get()->getObjectKind();
}
+
+ if (ConvertHalfVec)
+ return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, true,
+ OpLoc, FPFeatures);
+
return new (Context) CompoundAssignOperator(
LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, CompLHSTy, CompResultTy,
OpLoc, FPFeatures);
@@ -11693,6 +11803,13 @@
ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
BinaryOperatorKind Opc,
Expr *LHSExpr, Expr *RHSExpr) {
+ ExprResult LHS, RHS;
+ std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr);
+ if (!LHS.isUsable() || !RHS.isUsable())
+ return ExprError();
+ LHSExpr = LHS.get();
+ RHSExpr = RHS.get();
+
// We want to end up calling one of checkPseudoObjectAssignment
// (if the LHS is a pseudo-object), BuildOverloadedBinOp (if
// both expressions are overloadable or either is type-dependent),
@@ -11796,6 +11913,7 @@
ExprValueKind VK = VK_RValue;
ExprObjectKind OK = OK_Ordinary;
QualType resultType;
+ bool ConvertHalfVec = false;
if (getLangOpts().OpenCL) {
QualType Ty = InputExpr->getType();
// The only legal unary operation for atomics is '&'.
@@ -11835,6 +11953,16 @@
case UO_Minus:
Input = UsualUnaryConversions(Input.get());
if (Input.isInvalid()) return ExprError();
+ // Unary plus and minus require promoting an operand of half vector to a
+ // float vector and truncating the result back to a half vector. For now, we
+ // do this only when HalfArgsAndReturns is set (that is, when the target is
+ // arm or arm64).
+ ConvertHalfVec =
+ needsConversionOfHalfVec(true, Context, Input.get()->getType());
+
+ // If the operand is a half vector, promote it to a float vector.
+ if (ConvertHalfVec)
+ Input = convertVector(Input.get(), Context.FloatTy, *this);
resultType = Input.get()->getType();
if (resultType->isDependentType())
break;
@@ -11972,8 +12100,12 @@
if (Opc != UO_AddrOf && Opc != UO_Deref)
CheckArrayAccess(Input.get());
- return new (Context)
+ auto *UO = new (Context)
UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc);
+ // Convert the result back to a half vector.
+ if (ConvertHalfVec)
+ return convertVector(UO, Context.HalfTy, *this);
+ return UO;
}
/// \brief Determine whether the given expression is a qualified member
@@ -13997,6 +14129,8 @@
Field->setImplicit(true);
Field->setAccess(AS_private);
RD->addDecl(Field);
+ if (S.getLangOpts().OpenMP && RSI->CapRegionKind == CR_OpenMP)
+ S.setOpenMPCaptureKind(Field, Var, RSI->OpenMPLevel);
CopyExpr = new (S.Context) DeclRefExpr(Var, RefersToCapturedVariable,
DeclRefType, VK_LValue, Loc);
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index a9cf3ec..9c0bd6f 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -24,6 +24,7 @@
#include "clang/AST/ExprObjC.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TypeLoc.h"
+#include "clang/Basic/AlignedAllocation.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
@@ -1660,9 +1661,13 @@
bool IsAligned = false;
if (FD.isReplaceableGlobalAllocationFunction(&IsAligned) && IsAligned) {
+ const llvm::Triple &T = S.getASTContext().getTargetInfo().getTriple();
+ StringRef OSName = AvailabilityAttr::getPlatformNameSourceSpelling(
+ S.getASTContext().getTargetInfo().getPlatformName());
+
S.Diag(Loc, diag::warn_aligned_allocation_unavailable)
- << IsDelete << FD.getType().getAsString()
- << S.getASTContext().getTargetInfo().getTriple().str();
+ << IsDelete << FD.getType().getAsString() << OSName
+ << alignedAllocMinVersion(T.getOS()).getAsString();
S.Diag(Loc, diag::note_silence_unligned_allocation_unavailable);
}
}
diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp
index 28581ba..43ac6f1 100644
--- a/lib/Sema/SemaExprObjC.cpp
+++ b/lib/Sema/SemaExprObjC.cpp
@@ -4319,7 +4319,7 @@
// problems here. To catch them all, we'd need to rebuild arbitrary
// value-propagating subexpressions --- we can't reliably rebuild
// in-place because of expression sharing.
- if (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(e))
+ if (auto *ice = dyn_cast<ImplicitCastExpr>(e->IgnoreParens()))
if (ice->getCastKind() == CK_ARCReclaimReturnedObject)
return ice->getSubExpr();
diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp
index bfb0071..7192987 100644
--- a/lib/Sema/SemaObjCProperty.cpp
+++ b/lib/Sema/SemaObjCProperty.cpp
@@ -644,8 +644,6 @@
PDecl->setInvalidDecl();
}
- ProcessDeclAttributes(S, PDecl, FD.D);
-
// Regardless of setter/getter attribute, we save the default getter/setter
// selector names in anticipation of declaration of setter/getter methods.
PDecl->setGetterName(GetterSel, GetterNameLoc);
@@ -653,6 +651,8 @@
PDecl->setPropertyAttributesAsWritten(
makePropertyAttributesAsWritten(AttributesAsWritten));
+ ProcessDeclAttributes(S, PDecl, FD.D);
+
if (Attributes & ObjCDeclSpec::DQ_PR_readonly)
PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly);
@@ -1895,7 +1895,7 @@
/* property = */ Prop->getIdentifier(),
/* ivar = */ Prop->getDefaultSynthIvarName(Context),
Prop->getLocation(), Prop->getQueryKind()));
- if (PIDecl) {
+ if (PIDecl && !Prop->isUnavailable()) {
Diag(Prop->getLocation(), diag::warn_missing_explicit_synthesis);
Diag(IMPDecl->getLocation(), diag::note_while_in_implementation);
}
@@ -2394,6 +2394,8 @@
SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section,
SA->getName(), Loc));
+ ProcessAPINotes(GetterMethod);
+
if (getLangOpts().ObjCAutoRefCount)
CheckARCMethodDecl(GetterMethod);
} else
@@ -2459,6 +2461,9 @@
SetterMethod->addAttr(
SectionAttr::CreateImplicit(Context, SectionAttr::GNU_section,
SA->getName(), Loc));
+
+ ProcessAPINotes(SetterMethod);
+
// It's possible for the user to have set a very odd custom
// setter selector that causes it to have a method family.
if (getLangOpts().ObjCAutoRefCount)
diff --git a/lib/Sema/SemaOpenMP.cpp b/lib/Sema/SemaOpenMP.cpp
index 01f574b..30b1086 100644
--- a/lib/Sema/SemaOpenMP.cpp
+++ b/lib/Sema/SemaOpenMP.cpp
@@ -1139,6 +1139,39 @@
D, [](OpenMPClauseKind K) -> bool { return K == OMPC_private; }, Level);
}
+void Sema::setOpenMPCaptureKind(FieldDecl *FD, ValueDecl *D, unsigned Level) {
+ assert(LangOpts.OpenMP && "OpenMP is not allowed");
+ D = getCanonicalDecl(D);
+ OpenMPClauseKind OMPC = OMPC_unknown;
+ for (unsigned I = DSAStack->getNestingLevel() + 1; I > Level; --I) {
+ const unsigned NewLevel = I - 1;
+ if (DSAStack->hasExplicitDSA(D,
+ [&OMPC](const OpenMPClauseKind K) {
+ if (isOpenMPPrivate(K)) {
+ OMPC = K;
+ return true;
+ }
+ return false;
+ },
+ NewLevel))
+ break;
+ if (DSAStack->checkMappableExprComponentListsForDeclAtLevel(
+ D, NewLevel,
+ [](OMPClauseMappableExprCommon::MappableExprComponentListRef,
+ OpenMPClauseKind) { return true; })) {
+ OMPC = OMPC_map;
+ break;
+ }
+ if (DSAStack->hasExplicitDirective(isOpenMPTargetExecutionDirective,
+ NewLevel)) {
+ OMPC = OMPC_firstprivate;
+ break;
+ }
+ }
+ if (OMPC != OMPC_unknown)
+ FD->addAttr(OMPCaptureKindAttr::CreateImplicit(Context, OMPC));
+}
+
bool Sema::isOpenMPTargetCapturedDecl(ValueDecl *D, unsigned Level) {
assert(LangOpts.OpenMP && "OpenMP is not allowed");
// Return true if the current level is no longer enclosed in a target region.
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 2a38a1f..e89b26c 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -11,11 +11,10 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/Sema/SemaInternal.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
-#include "clang/AST/CharUnits.h"
#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/CharUnits.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/EvaluatedExprVisitor.h"
#include "clang/AST/ExprCXX.h"
@@ -26,11 +25,13 @@
#include "clang/AST/TypeLoc.h"
#include "clang/AST/TypeOrdering.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Edit/RefactoringFixits.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
@@ -1160,14 +1161,22 @@
// Produce a nice diagnostic if multiple values aren't handled.
if (!UnhandledNames.empty()) {
- DiagnosticBuilder DB = Diag(CondExpr->getExprLoc(),
- TheDefaultStmt ? diag::warn_def_missing_case
- : diag::warn_missing_case)
- << (int)UnhandledNames.size();
+ {
+ DiagnosticBuilder DB =
+ Diag(CondExpr->getExprLoc(), TheDefaultStmt
+ ? diag::warn_def_missing_case
+ : diag::warn_missing_case)
+ << (int)UnhandledNames.size();
- for (size_t I = 0, E = std::min(UnhandledNames.size(), (size_t)3);
- I != E; ++I)
- DB << UnhandledNames[I];
+ for (size_t I = 0, E = std::min(UnhandledNames.size(), (size_t)3);
+ I != E; ++I)
+ DB << UnhandledNames[I];
+ }
+ auto DB =
+ Diag(CondExpr->getExprLoc(), diag::note_fill_in_missing_cases);
+ edit::fillInMissingSwitchEnumCases(
+ Context, SS, ED, CurContext,
+ [&](const FixItHint &Hint) { DB << Hint; });
}
if (!hasCasesNotInSwitch)
diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp
index e9b3855..9b99f6a 100644
--- a/lib/Sema/SemaTemplate.cpp
+++ b/lib/Sema/SemaTemplate.cpp
@@ -1449,6 +1449,7 @@
if (Attr)
ProcessDeclAttributeList(S, NewClass, Attr);
+ ProcessAPINotes(NewClass);
if (PrevClassTemplate)
mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl());
@@ -7575,6 +7576,7 @@
if (Attr)
ProcessDeclAttributeList(S, Specialization, Attr);
+ ProcessAPINotes(Specialization);
// Add alignment attributes if necessary; these attributes are checked when
// the ASTContext lays out the structure.
@@ -8627,6 +8629,7 @@
bool PreviouslyDLLExported = Specialization->hasAttr<DLLExportAttr>();
if (Attr)
ProcessDeclAttributeList(S, Specialization, Attr);
+ ProcessAPINotes(Specialization);
// Add the explicit instantiation into its lexical context. However,
// since explicit instantiations are never found by name lookup, we
@@ -9055,6 +9058,7 @@
// Merge attributes.
if (AttributeList *Attr = D.getDeclSpec().getAttributes().getList())
ProcessDeclAttributeList(S, Prev, Attr);
+ ProcessAPINotes(Prev);
}
if (TSK == TSK_ExplicitInstantiationDefinition)
InstantiateVariableDefinition(D.getIdentifierLoc(), Prev);
@@ -9220,6 +9224,7 @@
Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc());
if (Attr)
ProcessDeclAttributeList(S, Specialization, Attr);
+ ProcessAPINotes(Specialization);
if (Specialization->isDefined()) {
// Let the ASTConsumer know that this function has been explicitly
diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp
index f4f0c80..643e018 100644
--- a/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2030,12 +2030,11 @@
bool MergeWithParentScope = !Instantiation->isDefinedOutsideFunctionOrMethod();
LocalInstantiationScope Scope(*this, MergeWithParentScope);
- // All dllexported classes created during instantiation should be fully
- // emitted after instantiation completes. We may not be ready to emit any
- // delayed classes already on the stack, so save them away and put them back
- // later.
- decltype(DelayedDllExportClasses) ExportedClasses;
- std::swap(ExportedClasses, DelayedDllExportClasses);
+ // Some class state isn't processed immediately but delayed till class
+ // instantiation completes. We may not be ready to handle any delayed state
+ // already on the stack as it might correspond to a different class, so save
+ // it now and put it back later.
+ SavePendingParsedClassStateRAII SavedPendingParsedClassState(*this);
// Pull attributes from the pattern onto the instantiation.
InstantiateAttrs(TemplateArgs, Pattern, Instantiation);
@@ -2122,9 +2121,6 @@
// default arg exprs for default constructors if necessary now.
ActOnFinishCXXNonNestedClass(Instantiation);
- // Put back the delayed exported classes that we moved out of the way.
- std::swap(ExportedClasses, DelayedDllExportClasses);
-
// Instantiate late parsed attributes, and attach them to their decls.
// See Sema::InstantiateAttrs
for (LateInstantiatedAttrVec::iterator I = LateAttrs.begin(),
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 598a113..62cdb6c 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -1157,20 +1157,6 @@
ResultTL = ObjCObjectPointerTL.getPointeeLoc();
}
- if (auto OTPTL = ResultTL.getAs<ObjCTypeParamTypeLoc>()) {
- // Protocol qualifier information.
- if (OTPTL.getNumProtocols() > 0) {
- assert(OTPTL.getNumProtocols() == Protocols.size());
- OTPTL.setProtocolLAngleLoc(ProtocolLAngleLoc);
- OTPTL.setProtocolRAngleLoc(ProtocolRAngleLoc);
- for (unsigned i = 0, n = Protocols.size(); i != n; ++i)
- OTPTL.setProtocolLoc(i, ProtocolLocs[i]);
- }
-
- // We're done. Return the completed type to the parser.
- return CreateParsedType(Result, ResultTInfo);
- }
-
auto ObjCObjectTL = ResultTL.castAs<ObjCObjectTypeLoc>();
// Type argument information.
@@ -3381,25 +3367,9 @@
if (auto recordType = type->getAs<RecordType>()) {
RecordDecl *recordDecl = recordType->getDecl();
- bool isCFError = false;
- if (S.CFError) {
- // If we already know about CFError, test it directly.
- isCFError = (S.CFError == recordDecl);
- } else {
- // Check whether this is CFError, which we identify based on its bridge
- // to NSError.
- if (recordDecl->getTagKind() == TTK_Struct && numNormalPointers > 0) {
- if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) {
- if (bridgeAttr->getBridgedType() == S.getNSErrorIdent()) {
- S.CFError = recordDecl;
- isCFError = true;
- }
- }
- }
- }
-
// If this is CFErrorRef*, report it as such.
- if (isCFError && numNormalPointers == 2 && numTypeSpecifierPointers < 2) {
+ if (numNormalPointers == 2 && numTypeSpecifierPointers < 2 &&
+ S.isCFError(recordDecl)) {
return PointerDeclaratorKind::CFErrorRefPointer;
}
break;
@@ -3423,6 +3393,26 @@
}
}
+bool Sema::isCFError(RecordDecl *recordDecl) {
+ // If we already know about CFError, test it directly.
+ if (CFError) {
+ return (CFError == recordDecl);
+ }
+
+ // Check whether this is CFError, which we identify based on being
+ // bridged to NSError.
+ if (recordDecl->getTagKind() == TTK_Struct) {
+ if (auto bridgeAttr = recordDecl->getAttr<ObjCBridgeAttr>()) {
+ if (bridgeAttr->getBridgedType() == getNSErrorIdent()) {
+ CFError = recordDecl;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static FileID getNullabilityCompletenessCheckFileID(Sema &S,
SourceLocation loc) {
// If we're anywhere in a function, method, or closure context, don't perform
@@ -6053,12 +6043,34 @@
return false;
}
+/// Rebuild an attributed type without the nullability attribute on it.
+static QualType rebuildAttributedTypeWithoutNullability(ASTContext &ctx,
+ QualType type) {
+ auto attributed = dyn_cast<AttributedType>(type.getTypePtr());
+ if (!attributed) return type;
+
+ // Skip the nullability attribute; we're done.
+ if (attributed->getImmediateNullability()) {
+ return attributed->getModifiedType();
+ }
+
+ // Build the modified type.
+ auto modified = rebuildAttributedTypeWithoutNullability(
+ ctx, attributed->getModifiedType());
+ assert(modified.getTypePtr() != attributed->getModifiedType().getTypePtr());
+ return ctx.getAttributedType(attributed->getAttrKind(), modified,
+ attributed->getEquivalentType());
+}
+
bool Sema::checkNullabilityTypeSpecifier(QualType &type,
NullabilityKind nullability,
SourceLocation nullabilityLoc,
bool isContextSensitive,
- bool allowOnArrayType) {
- recordNullabilitySeen(*this, nullabilityLoc);
+ bool allowOnArrayType,
+ bool implicit,
+ bool overrideExisting) {
+ if (!implicit)
+ recordNullabilitySeen(*this, nullabilityLoc);
// Check for existing nullability attributes on the type.
QualType desugared = type;
@@ -6067,6 +6079,9 @@
if (auto existingNullability = attributed->getImmediateNullability()) {
// Duplicated nullability.
if (nullability == *existingNullability) {
+ if (implicit)
+ break;
+
Diag(nullabilityLoc, diag::warn_nullability_duplicate)
<< DiagNullabilityKind(nullability, isContextSensitive)
<< FixItHint::CreateRemoval(nullabilityLoc);
@@ -6074,11 +6089,16 @@
break;
}
- // Conflicting nullability.
- Diag(nullabilityLoc, diag::err_nullability_conflicting)
- << DiagNullabilityKind(nullability, isContextSensitive)
- << DiagNullabilityKind(*existingNullability, false);
- return true;
+ if (!overrideExisting) {
+ // Conflicting nullability.
+ Diag(nullabilityLoc, diag::err_nullability_conflicting)
+ << DiagNullabilityKind(nullability, isContextSensitive)
+ << DiagNullabilityKind(*existingNullability, false);
+ return true;
+ }
+
+ // Rebuild the attributed type, dropping the existing nullability.
+ type = rebuildAttributedTypeWithoutNullability(Context, type);
}
desugared = attributed->getModifiedType();
@@ -6089,7 +6109,7 @@
// have nullability specifiers on them, which means we cannot
// provide a useful Fix-It.
if (auto existingNullability = desugared->getNullability(Context)) {
- if (nullability != *existingNullability) {
+ if (nullability != *existingNullability && !implicit) {
Diag(nullabilityLoc, diag::err_nullability_conflicting)
<< DiagNullabilityKind(nullability, isContextSensitive)
<< DiagNullabilityKind(*existingNullability, false);
@@ -6114,15 +6134,16 @@
// If this definitely isn't a pointer type, reject the specifier.
if (!desugared->canHaveNullability() &&
!(allowOnArrayType && desugared->isArrayType())) {
- Diag(nullabilityLoc, diag::err_nullability_nonpointer)
- << DiagNullabilityKind(nullability, isContextSensitive) << type;
+ if (!implicit) {
+ Diag(nullabilityLoc, diag::err_nullability_nonpointer)
+ << DiagNullabilityKind(nullability, isContextSensitive) << type;
+ }
return true;
}
// For the context-sensitive keywords/Objective-C property
// attributes, require that the type be a single-level pointer.
if (isContextSensitive) {
- // Make sure that the pointee isn't itself a pointer type.
const Type *pointeeType;
if (desugared->isArrayType())
pointeeType = desugared->getArrayElementTypeNoTypeQual();
@@ -6151,13 +6172,6 @@
}
bool Sema::checkObjCKindOfType(QualType &type, SourceLocation loc) {
- if (isa<ObjCTypeParamType>(type)) {
- // Build the attributed type to record where __kindof occurred.
- type = Context.getAttributedType(AttributedType::attr_objc_kindof,
- type, type);
- return false;
- }
-
// Find out if it's an Objective-C object or object pointer type;
const ObjCObjectPointerType *ptrType = type->getAs<ObjCObjectPointerType>();
const ObjCObjectType *objType = ptrType ? ptrType->getObjectType()
@@ -6926,7 +6940,7 @@
mapNullabilityAttrKind(attr.getKind()),
attr.getLoc(),
attr.isContextSensitiveKeywordAttribute(),
- allowOnArrayType)) {
+ allowOnArrayType, /*implicit=*/false)) {
attr.setInvalid();
}
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 91da9f8..a8b4c7a 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -6585,8 +6585,7 @@
// Rebuild the switch statement.
StmtResult Switch
- = getDerived().RebuildSwitchStmtStart(S->getSwitchLoc(),
- S->getInit(), Cond);
+ = getDerived().RebuildSwitchStmtStart(S->getSwitchLoc(), Init.get(), Cond);
if (Switch.isInvalid())
return StmtError();
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index 50be74f..a1c3272 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -4908,6 +4908,7 @@
bool IsExplicit = Record[Idx++];
bool IsSystem = Record[Idx++];
bool IsExternC = Record[Idx++];
+ bool IsSwiftInferImportAsMember = Record[Idx++];
bool InferSubmodules = Record[Idx++];
bool InferExplicitSubmodules = Record[Idx++];
bool InferExportWildcard = Record[Idx++];
@@ -4955,6 +4956,7 @@
CurrentModule->IsFromModuleFile = true;
CurrentModule->IsSystem = IsSystem || CurrentModule->IsSystem;
CurrentModule->IsExternC = IsExternC;
+ CurrentModule->IsSwiftInferImportAsMember = IsSwiftInferImportAsMember;
CurrentModule->InferSubmodules = InferSubmodules;
CurrentModule->InferExplicitSubmodules = InferExplicitSubmodules;
CurrentModule->InferExportWildcard = InferExportWildcard;
@@ -5109,7 +5111,7 @@
break;
}
- case SUBMODULE_INITIALIZERS:
+ case SUBMODULE_INITIALIZERS: {
if (!ContextObj)
break;
SmallVector<uint32_t, 16> Inits;
@@ -5118,6 +5120,11 @@
ContextObj->addLazyModuleInitializers(CurrentModule, Inits);
break;
}
+
+ case SUBMODULE_EXPORT_AS:
+ CurrentModule->ExportAsModule = Blob.str();
+ break;
+ }
}
}
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 0853415..93b5e20 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -1157,6 +1157,8 @@
void ASTDeclReader::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *CAD) {
VisitNamedDecl(CAD);
CAD->setClassInterface(ReadDeclAs<ObjCInterfaceDecl>());
+ CAD->setClassInterfaceLoc(ReadSourceLocation());
+ CAD->setAtLoc(ReadSourceLocation());
}
void ASTDeclReader::VisitObjCPropertyDecl(ObjCPropertyDecl *D) {
@@ -1559,11 +1561,9 @@
Data.HasUninitializedFields = Record.readInt();
Data.HasInheritedConstructor = Record.readInt();
Data.HasInheritedAssignment = Record.readInt();
- Data.NeedOverloadResolutionForCopyConstructor = Record.readInt();
Data.NeedOverloadResolutionForMoveConstructor = Record.readInt();
Data.NeedOverloadResolutionForMoveAssignment = Record.readInt();
Data.NeedOverloadResolutionForDestructor = Record.readInt();
- Data.DefaultedCopyConstructorIsDeleted = Record.readInt();
Data.DefaultedMoveConstructorIsDeleted = Record.readInt();
Data.DefaultedMoveAssignmentIsDeleted = Record.readInt();
Data.DefaultedDestructorIsDeleted = Record.readInt();
@@ -1572,7 +1572,6 @@
Data.HasIrrelevantDestructor = Record.readInt();
Data.HasConstexprNonCopyMoveConstructor = Record.readInt();
Data.HasDefaultedDefaultConstructor = Record.readInt();
- Data.CanPassInRegisters = Record.readInt();
Data.DefaultedDefaultConstructorIsConstexpr = Record.readInt();
Data.HasConstexprDefaultConstructor = Record.readInt();
Data.HasNonLiteralTypeFieldsOrBases = Record.readInt();
@@ -1700,11 +1699,9 @@
MATCH_FIELD(HasUninitializedFields)
MATCH_FIELD(HasInheritedConstructor)
MATCH_FIELD(HasInheritedAssignment)
- MATCH_FIELD(NeedOverloadResolutionForCopyConstructor)
MATCH_FIELD(NeedOverloadResolutionForMoveConstructor)
MATCH_FIELD(NeedOverloadResolutionForMoveAssignment)
MATCH_FIELD(NeedOverloadResolutionForDestructor)
- MATCH_FIELD(DefaultedCopyConstructorIsDeleted)
MATCH_FIELD(DefaultedMoveConstructorIsDeleted)
MATCH_FIELD(DefaultedMoveAssignmentIsDeleted)
MATCH_FIELD(DefaultedDestructorIsDeleted)
@@ -1713,7 +1710,6 @@
MATCH_FIELD(HasIrrelevantDestructor)
OR_FIELD(HasConstexprNonCopyMoveConstructor)
OR_FIELD(HasDefaultedDefaultConstructor)
- MATCH_FIELD(CanPassInRegisters)
MATCH_FIELD(DefaultedDefaultConstructorIsConstexpr)
OR_FIELD(HasConstexprDefaultConstructor)
MATCH_FIELD(HasNonLiteralTypeFieldsOrBases)
diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp
index 128e53b..07ad0db 100644
--- a/lib/Serialization/ASTWriter.cpp
+++ b/lib/Serialization/ASTWriter.cpp
@@ -1130,6 +1130,7 @@
RECORD(SUBMODULE_TEXTUAL_HEADER);
RECORD(SUBMODULE_PRIVATE_TEXTUAL_HEADER);
RECORD(SUBMODULE_INITIALIZERS);
+ RECORD(SUBMODULE_EXPORT_AS);
// Comments Block.
BLOCK(COMMENTS_BLOCK);
@@ -2719,6 +2720,7 @@
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExplicit
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSystem
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsExternC
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // IsSwiftInferIAM...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferSubmodules...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExplicit...
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // InferExportWild...
@@ -2789,6 +2791,11 @@
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Message
unsigned ConflictAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
+ Abbrev = std::make_shared<BitCodeAbbrev>();
+ Abbrev->Add(BitCodeAbbrevOp(SUBMODULE_EXPORT_AS));
+ Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Macro name
+ unsigned ExportAsAbbrev = Stream.EmitAbbrev(std::move(Abbrev));
+
// Write the submodule metadata block.
RecordData::value_type Record[] = {
getNumberOfModules(WritingModule),
@@ -2819,6 +2826,7 @@
Mod->IsExplicit,
Mod->IsSystem,
Mod->IsExternC,
+ Mod->IsSwiftInferImportAsMember,
Mod->InferSubmodules,
Mod->InferExplicitSubmodules,
Mod->InferExportWildcard,
@@ -2923,6 +2931,12 @@
if (!Inits.empty())
Stream.EmitRecord(SUBMODULE_INITIALIZERS, Inits);
+ // Emit the name of the re-exported module, if any.
+ if (!Mod->ExportAsModule.empty()) {
+ RecordData::value_type Record[] = {SUBMODULE_EXPORT_AS};
+ Stream.EmitRecordWithBlob(ExportAsAbbrev, Record, Mod->ExportAsModule);
+ }
+
// Queue up the submodules of this module.
for (auto *M : Mod->submodules())
Q.push(M);
@@ -5874,11 +5888,9 @@
Record->push_back(Data.HasUninitializedFields);
Record->push_back(Data.HasInheritedConstructor);
Record->push_back(Data.HasInheritedAssignment);
- Record->push_back(Data.NeedOverloadResolutionForCopyConstructor);
Record->push_back(Data.NeedOverloadResolutionForMoveConstructor);
Record->push_back(Data.NeedOverloadResolutionForMoveAssignment);
Record->push_back(Data.NeedOverloadResolutionForDestructor);
- Record->push_back(Data.DefaultedCopyConstructorIsDeleted);
Record->push_back(Data.DefaultedMoveConstructorIsDeleted);
Record->push_back(Data.DefaultedMoveAssignmentIsDeleted);
Record->push_back(Data.DefaultedDestructorIsDeleted);
@@ -5887,7 +5899,6 @@
Record->push_back(Data.HasIrrelevantDestructor);
Record->push_back(Data.HasConstexprNonCopyMoveConstructor);
Record->push_back(Data.HasDefaultedDefaultConstructor);
- Record->push_back(Data.CanPassInRegisters);
Record->push_back(Data.DefaultedDefaultConstructorIsConstexpr);
Record->push_back(Data.HasConstexprDefaultConstructor);
Record->push_back(Data.HasNonLiteralTypeFieldsOrBases);
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index ec21ca2..65aed14 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -784,6 +784,8 @@
void ASTDeclWriter::VisitObjCCompatibleAliasDecl(ObjCCompatibleAliasDecl *D) {
VisitNamedDecl(D);
Record.AddDeclRef(D->getClassInterface());
+ Record.AddSourceLocation(D->getClassInterfaceLoc());
+ Record.AddSourceLocation(D->getAtLoc());
Code = serialization::DECL_OBJC_COMPATIBLE_ALIAS;
}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 0891ea8..26fd83f 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -626,7 +626,7 @@
: public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> {
public:
IsObjCTypeParamDependentTypeVisitor() : Result(false) {}
- bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) {
+ bool VisitTypedefType(const TypedefType *Type) {
if (isa<ObjCTypeParamDecl>(Type->getDecl())) {
Result = true;
return false;
diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt
index 7b0c58e..3321450 100644
--- a/lib/Tooling/CMakeLists.txt
+++ b/lib/Tooling/CMakeLists.txt
@@ -4,6 +4,7 @@
)
add_subdirectory(Core)
+add_subdirectory(Refactor)
add_subdirectory(Refactoring)
add_clang_library(clangTooling
@@ -30,4 +31,6 @@
clangLex
clangRewrite
clangToolingCore
+ clangToolingRefactor
+ clangToolingRefactoring
)
diff --git a/lib/Tooling/Refactor/ASTSlice.cpp b/lib/Tooling/Refactor/ASTSlice.cpp
new file mode 100644
index 0000000..5447571
--- /dev/null
+++ b/lib/Tooling/Refactor/ASTSlice.cpp
@@ -0,0 +1,625 @@
+//===--- ASTSlice.cpp - Represents a portion of the AST -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTSlice.h"
+#include "SourceLocationUtilities.h"
+#include "StmtUtils.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/SaveAndRestore.h"
+#include <algorithm>
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+/// Searches for AST nodes around the given source location and range that can
+/// be used to initiate a refactoring operation.
+class ASTSliceFinder : public clang::RecursiveASTVisitor<ASTSliceFinder> {
+public:
+ explicit ASTSliceFinder(SourceLocation Location, SourceRange SelectionRange,
+ const ASTContext &Context)
+ : Location(Location), SelectionRange(SelectionRange), Context(Context) {}
+
+ bool TraverseDecl(Decl *D) {
+ if (!D)
+ return true;
+ if (isa<DeclContext>(D) && !D->isImplicit())
+ collectDeclIfInRange(D);
+ // TODO: Handle Lambda/Blocks.
+ if (!isa<FunctionDecl>(D) && !isa<ObjCMethodDecl>(D)) {
+ RecursiveASTVisitor::TraverseDecl(D);
+ return true;
+ }
+ const Decl *PreviousDecl = CurrentDecl;
+ CurrentDecl = D;
+ RecursiveASTVisitor::TraverseDecl(D);
+ CurrentDecl = PreviousDecl;
+ return true;
+ }
+
+ bool TraverseStmt(Stmt *S) {
+ if (!S)
+ return true;
+ // PseudoObjectExpressions don't have to be parents.
+ if (isa<PseudoObjectExpr>(S))
+ return RecursiveASTVisitor::TraverseStmt(S);
+ llvm::SaveAndRestore<const Stmt *> Parent(ParentStmt, CurrentStmt);
+ llvm::SaveAndRestore<const Stmt *> Current(CurrentStmt, S);
+ RecursiveASTVisitor::TraverseStmt(S);
+ return true;
+ }
+
+ bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
+ // Avoid traversing the getter/setter message sends for property
+ // expressions.
+ TraverseStmt(E->getSyntacticForm());
+ return true;
+ }
+
+ bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
+ RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E);
+ // Visit the opaque base manually as it won't be traversed by the
+ // PseudoObjectExpr.
+ if (E->isObjectReceiver()) {
+ if (const auto *Opaque = dyn_cast<OpaqueValueExpr>(E->getBase()))
+ TraverseStmt(Opaque->getSourceExpr());
+ }
+ return true;
+ }
+
+ // Statement visitors:
+
+ bool VisitStmt(Stmt *S) {
+ collectStmtIfInRange(S, S->getSourceRange());
+ return true;
+ }
+
+ // Ignore some implicit expressions.
+
+ bool WalkUpFromMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) {
+ return true;
+ }
+
+ bool WalkUpFromCXXThisExpr(CXXThisExpr *E) {
+ if (E->isImplicit())
+ return true;
+ return RecursiveASTVisitor::WalkUpFromCXXThisExpr(E);
+ }
+
+ /// Checks if the given statement and its source range has the location
+ /// of interest or overlaps with the selection range, and adds this node to
+ /// the set of statements for the slice that's being constructed.
+ void collectStmtIfInRange(const Stmt *S, SourceRange Range) {
+ SourceLocation Start = Range.getBegin();
+ const auto &SM = Context.getSourceManager();
+ bool IsStartMacroArg = false;
+ if (Start.isMacroID()) {
+ if (SM.isMacroArgExpansion(Start)) {
+ Start = SM.getSpellingLoc(Start);
+ IsStartMacroArg = true;
+ } else {
+ Start = SM.getExpansionLoc(Start);
+ }
+ }
+ SourceLocation End = Range.getEnd();
+ if (End.isMacroID() && SM.isMacroArgExpansion(End)) {
+ // Ignore the node that's span across normal code and a macro argument.
+ if (IsStartMacroArg)
+ End = SM.getSpellingLoc(End);
+ }
+ End = getPreciseTokenLocEnd(End, SM, Context.getLangOpts());
+ if (!isPairOfFileLocations(Start, End))
+ return;
+ if (SelectionRange.isValid()) {
+ if (!areRangesOverlapping(SelectionRange, SourceRange(Start, End),
+ Context.getSourceManager()))
+ return;
+ } else if (!isPointWithin(Location, Start, End, Context.getSourceManager()))
+ return;
+ Matches.emplace_back(S, ParentStmt, CurrentDecl, SourceRange(Start, End));
+ }
+
+ void collectDeclIfInRange(const Decl *D) {
+ SourceLocation Start = D->getSourceRange().getBegin();
+ SourceLocation End = getPreciseTokenLocEnd(
+ getLexicalEndLocForDecl(D, Context.getSourceManager(),
+ Context.getLangOpts()),
+ Context.getSourceManager(), Context.getLangOpts());
+ if (!isPairOfFileLocations(Start, End))
+ return;
+ if (SelectionRange.isValid()) {
+ if (!areRangesOverlapping(SelectionRange, SourceRange(Start, End),
+ Context.getSourceManager()))
+ return;
+ } else if (!isPointWithin(Location, Start, End, Context.getSourceManager()))
+ return;
+ Matches.emplace_back(D, CurrentDecl, SourceRange(Start, End));
+ }
+
+ SmallVector<ASTSlice::Node, 16> Matches;
+ /// The point of interest.
+ ///
+ /// Represents a location at which refactoring should be initiated.
+ const SourceLocation Location;
+ const SourceRange SelectionRange;
+ const ASTContext &Context;
+ const Decl *CurrentDecl = nullptr;
+ const Stmt *ParentStmt = nullptr, *CurrentStmt = nullptr;
+};
+
+} // end anonymous namespace
+
+ASTSlice::SelectedStmt::SelectedStmt(ASTSlice &Slice, const Stmt *S,
+ unsigned Index)
+ : Slice(Slice), S(S), Index(Index) {
+ assert(S && "No statement given!");
+}
+
+ASTSlice::SelectedDecl::SelectedDecl(const Decl *D) : D(D) {
+ assert(D && "No decl given!");
+}
+
+const Decl *ASTSlice::SelectedStmt::getParentDecl() {
+ return Slice.parentDeclForIndex(Index);
+}
+
+ASTSlice::ASTSlice(SourceLocation Location, SourceRange SelectionRange,
+ ASTContext &Context)
+ : Context(Context), SelectionLocation(Location),
+ SelectionRange(SelectionRange) {
+ FileID SearchFile = Context.getSourceManager().getFileID(Location);
+ ASTSliceFinder Visitor(Location, SelectionRange, Context);
+ SourceLocation EndLoc;
+ for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
+ if (EndLoc.isValid() &&
+ !Context.getSourceManager().isBeforeInTranslationUnit(
+ CurrDecl->getLocStart(), EndLoc))
+ break;
+ const SourceLocation FileLoc =
+ Context.getSourceManager().getSpellingLoc(CurrDecl->getLocStart());
+ if (Context.getSourceManager().getFileID(FileLoc) == SearchFile)
+ Visitor.TraverseDecl(CurrDecl);
+ // We are only interested in looking at a single top level declaration
+ // even if our selection range spans across multiple top level declarations.
+ if (!Visitor.Matches.empty()) {
+ // Objective-C @implementation declarations might have trailing functions
+ // that are declared outside of the @implementation, so continue looking
+ // through them.
+ if (isa<ObjCImplDecl>(CurrDecl)) {
+ EndLoc = CurrDecl->getLocEnd();
+ continue;
+ }
+ break;
+ }
+ }
+
+ for (auto I = Visitor.Matches.rbegin(), E = Visitor.Matches.rend(); I != E;
+ ++I)
+ NodeTree.push_back(*I);
+}
+
+bool ASTSlice::isSourceRangeSelected(CharSourceRange Range) const {
+ SourceRange R = Range.getAsRange();
+ if (Range.isTokenRange())
+ R.setEnd(getPreciseTokenLocEnd(R.getEnd(), Context.getSourceManager(),
+ Context.getLangOpts()));
+ if (SelectionRange.isInvalid())
+ return isPointWithin(SelectionLocation, R.getBegin(), R.getEnd(),
+ Context.getSourceManager());
+ return areRangesOverlapping(SelectionRange, R, Context.getSourceManager());
+}
+
+/// Find the 'if' statement that acts as the start of the
+/// 'if'/'else if'/'else' construct.
+static std::pair<const IfStmt *, unsigned>
+findIfStmtStart(const IfStmt *If, unsigned Index,
+ ArrayRef<ASTSlice::Node> NodeTree) {
+ if (Index >= NodeTree.size())
+ return {If, Index}; // We've reached the top of the tree, return.
+ const auto *ParentIf =
+ dyn_cast_or_null<IfStmt>(NodeTree[Index + 1].getStmtOrNull());
+ // The current 'if' is actually an 'else if' when the next 'if' has an else
+ // statement that points to the current 'if'.
+ if (!ParentIf || ParentIf->getElse() != If)
+ return {If, Index};
+ return findIfStmtStart(ParentIf, Index + 1, NodeTree);
+}
+
+/// Find an expression that best represents the given selected expression.
+static std::pair<const Stmt *, unsigned>
+canonicalizeSelectedExpr(const Stmt *S, unsigned Index,
+ ArrayRef<ASTSlice::Node> NodeTree) {
+ const auto Same = std::make_pair(S, Index);
+ if (Index + 1 >= NodeTree.size())
+ return Same;
+ const Stmt *Parent = NodeTree[Index + 1].getStmtOrNull();
+ if (!Parent)
+ return Same;
+
+ const auto Next = std::make_pair(Parent, Index + 1);
+ // The entire pseudo expression is selected when just its syntactic
+ // form is selected.
+ if (isa<Expr>(S)) {
+ if (const auto *POE = dyn_cast_or_null<PseudoObjectExpr>(Parent)) {
+ if (POE->getSyntacticForm() == S)
+ return Next;
+ }
+ }
+ // The entire ObjC string literal is selected when just its string
+ // literal is selected.
+ if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent))
+ return Next;
+ // The entire call should be selected when just the member expression
+ // that refers to the method is selected.
+ // FIXME: Check if this can be one of the call arguments.
+ if (isa<MemberExpr>(S) && isa<CXXMemberCallExpr>(Parent))
+ return Next;
+ // The entire call should be selected when just the callee is selected.
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(S)) {
+ if (const auto *Call = dyn_cast<CallExpr>(Parent)) {
+ if (Call->getCalleeDecl() == DRE->getDecl())
+ return Next;
+ }
+ }
+ return Same;
+}
+
+Optional<ASTSlice::SelectedStmt> ASTSlice::nearestSelectedStmt(
+ llvm::function_ref<bool(const Stmt *)> Predicate) {
+ for (const auto &Node : llvm::enumerate(NodeTree)) {
+ const Stmt *S = Node.value().getStmtOrNull();
+ if (!S || !Predicate(S))
+ continue;
+
+ // Found the match. Perform any additional adjustments.
+ if (isa<Expr>(S)) {
+ auto CanonicalExpr = canonicalizeSelectedExpr(S, Node.index(), NodeTree);
+ return SelectedStmt(*this, CanonicalExpr.first, CanonicalExpr.second);
+ }
+ switch (S->getStmtClass()) {
+ case Stmt::IfStmtClass: {
+ // TODO: Fix findIfStmtStart bug with Index where it will return the
+ // index of the last statement.
+ auto If = findIfStmtStart(cast<IfStmt>(S), Node.index(), NodeTree);
+ return SelectedStmt(*this, If.first, If.second);
+ }
+ default:
+ break;
+ }
+
+ return SelectedStmt(*this, S, Node.index());
+ }
+ return None;
+}
+
+Optional<ASTSlice::SelectedStmt>
+ASTSlice::nearestSelectedStmt(Stmt::StmtClass Class) {
+ return nearestSelectedStmt(
+ [Class](const Stmt *S) -> bool { return S->getStmtClass() == Class; });
+}
+
+const Stmt *ASTSlice::nearestStmt(Stmt::StmtClass Class) {
+ auto Result = nearestSelectedStmt(Class);
+ return Result ? Result->getStmt() : nullptr;
+}
+
+Optional<ASTSlice::SelectedDecl> ASTSlice::innermostSelectedDecl(
+ llvm::function_ref<bool(const Decl *)> Predicate, unsigned Options) {
+ if (SelectionRange.isValid()) {
+ if (Options & ASTSlice::InnermostDeclOnly) {
+ auto Result = getInnermostCompletelySelectedDecl();
+ if (!Result)
+ return None;
+ if (Predicate(Result->getDecl()))
+ return Result;
+ return None;
+ }
+ // Traverse down through all of the selected node checking the predicate.
+ // TODO: Cache the SelectionRangeOverlap kinds properly instead of relying
+ // on getInnermostCompletelySelectedDecl.
+ getInnermostCompletelySelectedDecl();
+ for (const auto &N : NodeTree) {
+ const Decl *D = N.getDeclOrNull();
+ if (!D)
+ continue;
+ if (N.SelectionRangeOverlap != Node::ContainsSelectionRange)
+ continue;
+ if (Predicate(D))
+ return SelectedDecl(D);
+ }
+ return None;
+ }
+ for (const auto &Node : llvm::enumerate(NodeTree)) {
+ const Decl *D = Node.value().getDeclOrNull();
+ if (!D)
+ continue;
+ if (Predicate(D))
+ return SelectedDecl(D);
+ if (Options & ASTSlice::InnermostDeclOnly)
+ return None;
+ }
+ return None;
+}
+
+Optional<ASTSlice::SelectedDecl>
+ASTSlice::innermostSelectedDecl(ArrayRef<Decl::Kind> Classes,
+ unsigned Options) {
+ assert(!Classes.empty() && "Expected at least one decl kind");
+ return innermostSelectedDecl(
+ [&](const Decl *D) {
+ for (Decl::Kind Class : Classes) {
+ if (D->getKind() == Class)
+ return true;
+ }
+ return false;
+ },
+ Options);
+}
+
+/// Compute the SelectionRangeOverlap kinds for matched AST nodes.
+///
+/// The overlap kinds are computed only upto the first node that contains the
+/// entire selection range.
+static void
+computeSelectionRangeOverlapKinds(MutableArrayRef<ASTSlice::Node> NodeTree,
+ SourceRange SelectionRange,
+ const SourceManager &SM) {
+ for (ASTSlice::Node &Node : NodeTree) {
+ bool HasStart =
+ isPointWithin(SelectionRange.getBegin(), Node.Range.getBegin(),
+ Node.Range.getEnd(), SM);
+ bool HasEnd = isPointWithin(SelectionRange.getEnd(), Node.Range.getBegin(),
+ Node.Range.getEnd(), SM);
+ if (HasStart && HasEnd)
+ Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRange;
+ else if (HasStart)
+ Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRangeStart;
+ else if (HasEnd)
+ Node.SelectionRangeOverlap = ASTSlice::Node::ContainsSelectionRangeEnd;
+ }
+}
+
+const Stmt *findFirstStatementAfter(const CompoundStmt *CS, SourceLocation Loc,
+ const SourceManager &SM) {
+ for (const Stmt *S : CS->body()) {
+ if (!SM.isBeforeInTranslationUnit(S->getLocStart(), Loc))
+ return S;
+ }
+ return nullptr;
+}
+
+const Stmt *findLastStatementBefore(const CompoundStmt *CS, SourceLocation Loc,
+ const Stmt *StartAt,
+ const SourceManager &SM) {
+ auto It = std::find(CS->body_begin(), CS->body_end(), StartAt);
+ assert(It != CS->body_end());
+ const Stmt *Last = StartAt;
+ for (auto E = CS->body_end(); It != E; ++It) {
+ const Stmt *S = *It;
+ if (!SM.isBeforeInTranslationUnit(S->getLocStart(), Loc))
+ return Last;
+ Last = S;
+ }
+ return Last;
+}
+
+/// Return the source construct that contains the given compound statement.
+///
+/// This is useful to find the source construct to which the given compound
+/// statement belongs to lexically. For example, if we've selected just the
+/// body of an if statement, we ideally want to select the entire if statement.
+static std::pair<const Stmt *, unsigned>
+findCompoundStatementSourceConstruct(const CompoundStmt *CS,
+ ArrayRef<ASTSlice::Node> NodeTree) {
+ for (const auto &Node : llvm::enumerate(NodeTree)) {
+ const Stmt *S = Node.value().getStmtOrNull();
+ if (!S)
+ continue;
+ for (const Stmt *Child : S->children()) {
+ if (Child == CS) {
+ if (isa<CompoundStmt>(S))
+ return {CS, 0};
+ if (const auto *If = dyn_cast<IfStmt>(S))
+ return findIfStmtStart(If, Node.index(), NodeTree);
+ return {S, Node.index()};
+ }
+ }
+ }
+ // This is the outer compound statement.
+ return {CS, 0};
+}
+
+/// Return the source construct that contains the given switch case.
+static std::pair<const Stmt *, unsigned>
+findSwitchSourceConstruct(const SwitchCase *Case,
+ ArrayRef<ASTSlice::Node> NodeTree) {
+ for (const auto &Node : llvm::enumerate(NodeTree)) {
+ const Stmt *S = Node.value().getStmtOrNull();
+ if (!S)
+ continue;
+ if (isa<SwitchStmt>(S))
+ return {S, Node.index()};
+ }
+ return {Case, 0};
+}
+
+SelectedStmtSet SelectedStmtSet::createFromEntirelySelected(const Stmt *S,
+ unsigned Index) {
+ SelectedStmtSet Result;
+ Result.containsSelectionRange = S;
+ Result.containsSelectionRangeIndex = Index;
+ return Result;
+}
+
+Optional<ASTSlice::SelectedDecl>
+ASTSlice::getInnermostCompletelySelectedDecl() {
+ assert(SelectionRange.isValid() && "No selection range!");
+ if (CachedSelectedInnermostDecl)
+ return *CachedSelectedInnermostDecl;
+ computeSelectionRangeOverlapKinds(NodeTree, SelectionRange,
+ Context.getSourceManager());
+ Optional<SelectedDecl> Result;
+ for (const auto &N : llvm::enumerate(NodeTree)) {
+ const Decl *D = N.value().getDeclOrNull();
+ if (!D)
+ continue;
+ if (N.value().SelectionRangeOverlap != Node::ContainsSelectionRange)
+ continue;
+ Result = SelectedDecl(D);
+ break;
+ }
+ CachedSelectedInnermostDecl = Result;
+ return Result;
+}
+
+static bool isCaseSelected(const SwitchStmt *S, SourceRange SelectionRange,
+ const SourceManager &SM) {
+ for (const SwitchCase *Case = S->getSwitchCaseList(); Case;
+ Case = Case->getNextSwitchCase()) {
+ SourceRange Range(Case->getLocStart(), Case->getColonLoc());
+ if (areRangesOverlapping(Range, SelectionRange, SM))
+ return true;
+ }
+ return false;
+}
+
+Optional<SelectedStmtSet> ASTSlice::computeSelectedStmtSet() {
+ if (SelectionRange.isInvalid())
+ return None;
+ computeSelectionRangeOverlapKinds(NodeTree, SelectionRange,
+ Context.getSourceManager());
+
+ SelectedStmtSet Result;
+ for (const auto &N : llvm::enumerate(NodeTree)) {
+ const auto *S = N.value().getStmtOrNull();
+ if (!S)
+ continue;
+ switch (N.value().SelectionRangeOverlap) {
+ case Node::ContainsSelectionRange: {
+ Result.containsSelectionRange = S;
+ Result.containsSelectionRangeIndex = N.index();
+
+ const auto *CS = dyn_cast<CompoundStmt>(Result.containsSelectionRange);
+ if (!CS) {
+ // The entire if should be selected when just the 'else if' overlaps
+ // with the selection range.
+ if (const auto *If = dyn_cast<IfStmt>(Result.containsSelectionRange)) {
+ auto IfConstruct = findIfStmtStart(If, N.index(), NodeTree);
+ return SelectedStmtSet::createFromEntirelySelected(
+ IfConstruct.first, IfConstruct.second);
+ }
+ // The entire switch should be selected when just a 'case' overlaps
+ // with the selection range.
+ if (const auto *Case =
+ dyn_cast<SwitchCase>(Result.containsSelectionRange)) {
+ auto Switch = findSwitchSourceConstruct(
+ Case, makeArrayRef(NodeTree).drop_front(N.index() + 1));
+ return SelectedStmtSet::createFromEntirelySelected(
+ Switch.first, N.index() + Switch.second);
+ }
+
+ auto CanonicalExpr = canonicalizeSelectedExpr(S, N.index(), NodeTree);
+ Result.containsSelectionRange = CanonicalExpr.first;
+ Result.containsSelectionRangeIndex = CanonicalExpr.second;
+ return Result;
+ }
+
+ bool IsLBraceSelected =
+ !Context.getSourceManager().isBeforeInTranslationUnit(
+ CS->getLBracLoc(), SelectionRange.getBegin());
+ bool IsRBraceSelected =
+ Context.getSourceManager().isBeforeInTranslationUnit(
+ CS->getRBracLoc(), SelectionRange.getEnd());
+
+ // Return the entire source construct that has the compound statement
+ // when one of the braces is selected, or when an actual `case` of the
+ // switch is selected.
+ auto Construct = findCompoundStatementSourceConstruct(
+ CS, makeArrayRef(NodeTree).drop_front(N.index() + 1));
+ if (Construct.first != CS &&
+ ((IsLBraceSelected || IsRBraceSelected) ||
+ (isa<SwitchStmt>(Construct.first) &&
+ isCaseSelected(cast<SwitchStmt>(Construct.first), SelectionRange,
+ Context.getSourceManager()))))
+ return SelectedStmtSet::createFromEntirelySelected(
+ Construct.first, N.index() + Construct.second);
+
+ // When both braces are selected the entire compound statement is
+ // considered to be selected.
+ if (IsLBraceSelected && IsRBraceSelected)
+ return Result;
+ if (IsLBraceSelected)
+ Result.containsSelectionRangeStart = CS->body_front();
+ else if (IsRBraceSelected)
+ Result.containsSelectionRangeEnd = CS->body_back();
+
+ if (!Result.containsSelectionRangeStart)
+ Result.containsSelectionRangeStart = findFirstStatementAfter(
+ CS, SelectionRange.getBegin(), Context.getSourceManager());
+
+ // Return an empty set when the compound statements os empty or the
+ // selection range starts after the last statement or the selection range
+ // doesn't overlap with any actual statements.
+ if (!Result.containsSelectionRangeStart ||
+ !Context.getSourceManager().isBeforeInTranslationUnit(
+ Result.containsSelectionRangeStart->getLocStart(),
+ SelectionRange.getEnd()))
+ return None;
+
+ if (!Result.containsSelectionRangeEnd)
+ Result.containsSelectionRangeEnd = findLastStatementBefore(
+ CS, SelectionRange.getEnd(), Result.containsSelectionRangeStart,
+ Context.getSourceManager());
+
+ return Result;
+ }
+ case Node::ContainsSelectionRangeStart:
+ Result.containsSelectionRangeStart = S;
+ break;
+ case Node::ContainsSelectionRangeEnd:
+ Result.containsSelectionRangeEnd = S;
+ break;
+ case Node::UnknownOverlap:
+ break;
+ }
+ }
+ return Result;
+}
+
+Optional<SelectedStmtSet> ASTSlice::getSelectedStmtSet() {
+ if (CachedSelectedStmtSet)
+ return *CachedSelectedStmtSet;
+ CachedSelectedStmtSet = computeSelectedStmtSet();
+ return *CachedSelectedStmtSet;
+}
+
+bool ASTSlice::isContainedInCompoundStmt(unsigned Index) {
+ assert(Index < NodeTree.size() && "Invalid node index");
+ for (unsigned I = Index + 1, E = NodeTree.size(); I != E; ++I) {
+ const Stmt *S = NodeTree[I].getStmtOrNull();
+ if (!S)
+ continue;
+ if (isa<CompoundStmt>(S))
+ return true;
+ }
+ return false;
+}
+
+const Decl *ASTSlice::parentDeclForIndex(unsigned Index) {
+ return NodeTree[Index].ParentDecl;
+}
+
+const Stmt *ASTSlice::parentStmtForIndex(unsigned Index) {
+ return NodeTree[Index].ParentStmt;
+}
diff --git a/lib/Tooling/Refactor/ASTSlice.h b/lib/Tooling/Refactor/ASTSlice.h
new file mode 100644
index 0000000..f5cb9c6
--- /dev/null
+++ b/lib/Tooling/Refactor/ASTSlice.h
@@ -0,0 +1,182 @@
+//===--- ASTSlice.h - Represents a portion of the AST ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H
+
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+
+class NamedDecl;
+
+namespace tooling {
+
+/// Represents a set of statements that overlap with the selection range.
+struct SelectedStmtSet {
+ /// The outermost statement that contains the start of the selection range.
+ const Stmt *containsSelectionRangeStart = nullptr;
+
+ /// The outermost statement that contains the end of the selection range.
+ const Stmt *containsSelectionRangeEnd = nullptr;
+
+ /// The innermost statement that contains the entire selection range.
+ const Stmt *containsSelectionRange = nullptr;
+
+ /// The index of the innermost statement that contains the entire selection
+ /// range. The index points into the NodeTree stored in the \c ASTSlice.
+ Optional<unsigned> containsSelectionRangeIndex;
+
+ static SelectedStmtSet createFromEntirelySelected(const Stmt *S,
+ unsigned Index);
+
+ /// Returns true if the compound statement is not fully selected.
+ bool isCompoundStatementPartiallySelected() const {
+ assert(containsSelectionRange && "No statement selected");
+ return isa<CompoundStmt>(containsSelectionRange) &&
+ (containsSelectionRangeStart || containsSelectionRangeEnd);
+ }
+};
+
+/// A portion of the AST that is located around the location and/or source
+/// range of interest.
+class ASTSlice {
+public:
+ struct Node {
+ enum SelectionRangeOverlapKind {
+ UnknownOverlap,
+ ContainsSelectionRangeStart,
+ ContainsSelectionRangeEnd,
+ ContainsSelectionRange
+ };
+ llvm::PointerUnion<const Stmt *, const Decl *> StmtOrDecl;
+ const Stmt *ParentStmt;
+ const Decl *ParentDecl;
+ SourceRange Range;
+ SelectionRangeOverlapKind SelectionRangeOverlap = UnknownOverlap;
+
+ const Stmt *getStmtOrNull() const {
+ return StmtOrDecl.dyn_cast<const Stmt *>();
+ }
+
+ const Decl *getDeclOrNull() const {
+ return StmtOrDecl.dyn_cast<const Decl *>();
+ }
+
+ Node(const Stmt *S, const Stmt *ParentStmt, const Decl *ParentDecl,
+ SourceRange Range)
+ : StmtOrDecl(S), ParentStmt(ParentStmt), ParentDecl(ParentDecl),
+ Range(Range) {}
+ Node(const Decl *D, const Decl *ParentDecl, SourceRange Range)
+ : StmtOrDecl(D), ParentStmt(nullptr), ParentDecl(ParentDecl),
+ Range(Range) {}
+ };
+
+ /// Represents a statement that overlaps with the selection range/point.
+ class SelectedStmt {
+ ASTSlice &Slice;
+ const Stmt *S;
+ unsigned Index;
+
+ friend class ASTSlice;
+
+ SelectedStmt(ASTSlice &Slice, const Stmt *S, unsigned Index);
+
+ public:
+ const Stmt *getStmt() { return S; }
+ const Decl *getParentDecl();
+ };
+
+ /// Represents a declaration that overlaps with the selection range/point.
+ class SelectedDecl {
+ const Decl *D;
+
+ friend class ASTSlice;
+
+ SelectedDecl(const Decl *D);
+
+ public:
+ const Decl *getDecl() { return D; }
+ };
+
+ ASTSlice(SourceLocation Location, SourceRange SelectionRange,
+ ASTContext &Context);
+
+ /// Returns true if the given source range overlaps with the selection.
+ bool isSourceRangeSelected(CharSourceRange Range) const;
+
+ enum SelectionSearchOptions {
+ /// Search with-in the innermost declaration only, including the declaration
+ /// itself without inspecting any other outer declarations.
+ InnermostDeclOnly = 1
+ };
+
+ /// Returns the statement that results in true when passed into \p Predicate
+ /// that's nearest to the location of interest, or \c None if such statement
+ /// isn't found.
+ Optional<SelectedStmt>
+ nearestSelectedStmt(llvm::function_ref<bool(const Stmt *)> Predicate);
+
+ /// Returns the statement of the given class that's nearest to the location
+ /// of interest, or \c None if such statement isn't found.
+ Optional<SelectedStmt> nearestSelectedStmt(Stmt::StmtClass Class);
+
+ /// TODO: Remove in favour of nearestStmt that returns \c SelectedStmt
+ const Stmt *nearestStmt(Stmt::StmtClass Class);
+
+ /// Returns the declaration that overlaps with the selection range, is
+ /// nearest to the location of interest and that results in true when passed
+ /// into \p Predicate, or \c None if such declaration isn't found.
+ Optional<SelectedDecl>
+ innermostSelectedDecl(llvm::function_ref<bool(const Decl *)> Predicate,
+ unsigned Options = 0);
+
+ /// Returns the declaration closest to the location of interest whose decl
+ /// kind is in \p Classes, or \c None if no such decl can't be found.
+ Optional<SelectedDecl> innermostSelectedDecl(ArrayRef<Decl::Kind> Classes,
+ unsigned Options = 0);
+
+ /// Returns the set of statements that overlap with the selection range.
+ Optional<SelectedStmtSet> getSelectedStmtSet();
+
+ /// Returns true if the statement with the given index is contained in a
+ /// compound statement that overlaps with the selection range.
+ bool isContainedInCompoundStmt(unsigned Index);
+
+ /// Returns the declaration that contains the statement at the given index.
+ const Decl *parentDeclForIndex(unsigned Index);
+
+ /// Returns the statement that contains the statement at the given index.
+ const Stmt *parentStmtForIndex(unsigned Index);
+
+private:
+ Optional<SelectedStmtSet> computeSelectedStmtSet();
+
+ /// Returns the innermost declaration that contains both the start and the
+ /// end of the selection range.
+ Optional<SelectedDecl> getInnermostCompletelySelectedDecl();
+
+ /// The lowest element is the top of the hierarchy
+ SmallVector<Node, 16> NodeTree;
+ ASTContext &Context;
+ SourceLocation SelectionLocation;
+ SourceRange SelectionRange;
+ Optional<Optional<SelectedStmtSet>> CachedSelectedStmtSet;
+ Optional<Optional<SelectedDecl>> CachedSelectedInnermostDecl;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_ASTSLICE_H
diff --git a/lib/Tooling/Refactor/ASTStateSerialization.cpp b/lib/Tooling/Refactor/ASTStateSerialization.cpp
new file mode 100644
index 0000000..2d10fbc
--- /dev/null
+++ b/lib/Tooling/Refactor/ASTStateSerialization.cpp
@@ -0,0 +1,70 @@
+//===-- ASTStateSerialization.cpp - Persists TU-specific state across TUs -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringContinuations.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::tooling::detail;
+
+namespace {
+
+class USRToDeclConverter
+ : public clang::RecursiveASTVisitor<USRToDeclConverter> {
+ llvm::StringMap<const Decl *> &USRs;
+ unsigned NumFound = 0;
+
+public:
+ USRToDeclConverter(llvm::StringMap<const Decl *> &USRs) : USRs(USRs) {}
+
+ bool isDone() const { return NumFound == USRs.size(); }
+
+ bool VisitNamedDecl(const NamedDecl *D) {
+ std::string USR = rename::getUSRForDecl(D);
+ auto It = USRs.find(USR);
+ if (It == USRs.end() || It->second)
+ return true;
+ It->second = D;
+ ++NumFound;
+ return NumFound != USRs.size();
+ }
+};
+
+} // end anonymous namespace
+
+const Decl *PersistentToASTSpecificStateConverter::lookupDecl(StringRef USR) {
+ if (USR.empty())
+ return nullptr;
+ auto It = ConvertedDeclRefs.find(USR);
+ if (It != ConvertedDeclRefs.end())
+ return It->second;
+ // FIXME: If we ever need to convert a PersistentDeclRef through the ASTQuery,
+ // we have to support conversion without coalesced conversion.
+ assert(false && "Persistent decl refs should be converted all at once");
+ return nullptr;
+}
+
+void PersistentToASTSpecificStateConverter::runCoalescedConversions() {
+ USRToDeclConverter Converter(ConvertedDeclRefs);
+ for (Decl *D : Context.getTranslationUnitDecl()->decls()) {
+ Converter.TraverseDecl(D);
+ if (Converter.isDone())
+ break;
+ }
+}
+
+FileID
+PersistentToASTSpecificStateConverter::convert(const PersistentFileID &Ref) {
+ FileManager &FM = Context.getSourceManager().getFileManager();
+ const FileEntry *Entry = FM.getFile(Ref.Filename);
+ if (!Entry)
+ return FileID();
+ return Context.getSourceManager().translateFile(Entry);
+}
diff --git a/lib/Tooling/Refactor/CMakeLists.txt b/lib/Tooling/Refactor/CMakeLists.txt
new file mode 100644
index 0000000..9828c1a
--- /dev/null
+++ b/lib/Tooling/Refactor/CMakeLists.txt
@@ -0,0 +1,44 @@
+set(LLVM_LINK_COMPONENTS support)
+
+add_clang_library(clangToolingRefactor
+ ASTSlice.cpp
+ ASTStateSerialization.cpp
+ Extract.cpp
+ ExtractRepeatedExpressionIntoVariable.cpp
+ ExtractionUtils.cpp
+ FillInEnumSwitchCases.cpp
+ FillInMissingMethodStubsFromAbstractClasses.cpp
+ FillInMissingProtocolStubs.cpp
+ IfSwitchConversion.cpp
+ ImplementDeclaredMethods.cpp
+ IndexerQueries.cpp
+ LocalizeObjCStringLiteral.cpp
+ RefactoringActions.cpp
+ RefactoringActionFinder.cpp
+ RefactoringOperation.cpp
+ RefactoringOptions.cpp
+ RenamingOperation.cpp
+ RenameIndexedFile.cpp
+ RenamedSymbol.cpp
+ SourceLocationUtilities.cpp
+ StmtUtils.cpp
+ SymbolOperation.cpp
+ SymbolOccurrenceFinder.cpp
+ SymbolName.cpp
+ SymbolUSRFinder.cpp
+ TypeUtils.cpp
+ USRFinder.cpp
+
+ DEPENDS
+ ClangDriverOptions
+
+ LINK_LIBS
+ clangAST
+ clangASTMatchers
+ clangBasic
+ clangEdit
+ clangIndex
+ clangLex
+ clangToolingCore
+ clangRewrite
+ )
diff --git a/lib/Tooling/Refactor/Extract.cpp b/lib/Tooling/Refactor/Extract.cpp
new file mode 100644
index 0000000..0dbc304
--- /dev/null
+++ b/lib/Tooling/Refactor/Extract.cpp
@@ -0,0 +1,1999 @@
+//===--- Extract.cpp - ---------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "extract" refactoring operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExtractionUtils.h"
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "StmtUtils.h"
+#include "TypeUtils.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/MacroInfo.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactor/RefactoringOptions.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/Path.h"
+#include <algorithm>
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+struct CompoundStatementRange {
+ CompoundStmt::const_body_iterator First, Last;
+
+ const Stmt *getFirst() const {
+ // We must have selected just the child of the case, since a selection that
+ // includes the case is treated like a selection of the entire switch.
+ if (const auto *Case = dyn_cast<SwitchCase>(*First)) {
+ if (const Stmt *S = Case->getSubStmt())
+ return S;
+ }
+ return *First;
+ }
+
+ const Stmt *getLast() const { return *Last; }
+
+ // TODO: We might not want to iterate over the switch case if we've just
+ // selected its child. We should switch over to an array of nodes instead of
+ // an iterator pair instead.
+ CompoundStmt::const_body_iterator begin() const { return First; }
+ CompoundStmt::const_body_iterator end() const { return Last + 1; }
+};
+
+enum class ExtractionKind { Function, Method, Expression };
+
+class ExtractOperation : public RefactoringOperation {
+public:
+ struct CandidateInfo {
+ CandidateInfo(SourceRange Range, StringRef PreInsertedText = "",
+ const Stmt *AnalyzedStatement = nullptr)
+ : Range(Range), PreInsertedText(PreInsertedText),
+ AnalyzedStatement(AnalyzedStatement) {}
+
+ /// The candidate token range, i.e. the end location is the starting
+ /// location of the last token.
+ SourceRange Range;
+ /// The text that should be inserted before the call to the extracted
+ /// function.
+ StringRef PreInsertedText;
+ /// The expression that should be analyzed for captured variables and the
+ /// return value.
+ const Stmt *AnalyzedStatement;
+ };
+
+ ExtractOperation(const Stmt *S, const Stmt *ParentStmt,
+ const Decl *FunctionLikeParentDecl,
+ std::vector<std::string> Candidates,
+ Optional<CompoundStatementRange> ExtractedStmtRange,
+ Optional<CandidateInfo> FirstCandidateInfo,
+ ExtractionKind Kind)
+ : S(S), ParentStmt(ParentStmt),
+ FunctionLikeParentDecl(FunctionLikeParentDecl),
+ Candidates(std::move(Candidates)),
+ ExtractedStmtRange(ExtractedStmtRange), Kind(Kind) {
+ if (FirstCandidateInfo)
+ CandidateExtractionInfo.push_back(*FirstCandidateInfo);
+ }
+
+ const Stmt *getTransformedStmt() const override {
+ if (ExtractedStmtRange)
+ return ExtractedStmtRange->getFirst();
+ return S;
+ }
+
+ const Stmt *getLastTransformedStmt() const override {
+ if (ExtractedStmtRange)
+ return ExtractedStmtRange->getLast();
+ return nullptr;
+ }
+
+ std::vector<std::string> getRefactoringCandidates() override {
+ return Candidates;
+ }
+
+ std::vector<RefactoringActionType> getAvailableSubActions() override {
+ std::vector<RefactoringActionType> SubActions;
+ if (isa<CXXMethodDecl>(FunctionLikeParentDecl) ||
+ isa<ObjCMethodDecl>(FunctionLikeParentDecl))
+ SubActions.push_back(RefactoringActionType::Extract_Method);
+ if (isLexicalExpression(S, ParentStmt))
+ SubActions.push_back(RefactoringActionType::Extract_Expression);
+ return SubActions;
+ }
+
+ bool isMethodExtraction() const { return Kind == ExtractionKind::Method; }
+
+ bool isExpressionExtraction() const {
+ return Kind == ExtractionKind::Expression;
+ }
+
+ llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ llvm::Expected<RefactoringResult>
+ performExpressionExtraction(ASTContext &Context, PrintingPolicy &PP);
+
+ const Stmt *S, *ParentStmt;
+ const Decl *FunctionLikeParentDecl;
+ std::vector<std::string> Candidates;
+ /// A set of extraction candidates that correspond to the extracted code.
+ SmallVector<CandidateInfo, 2> CandidateExtractionInfo;
+ Optional<CompoundStatementRange> ExtractedStmtRange;
+ ExtractionKind Kind;
+};
+
+} // end anonymous namespace
+
+bool isSimpleExpression(const Expr *E) {
+ switch (E->IgnoreParenCasts()->getStmtClass()) {
+ case Stmt::DeclRefExprClass:
+ case Stmt::PredefinedExprClass:
+ case Stmt::IntegerLiteralClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::ImaginaryLiteralClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::StringLiteralClass:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool isMultipleCandidateBinOp(BinaryOperatorKind Op) {
+ return Op == BO_Add || Op == BO_Sub;
+}
+
+/// Searches for the selected statement in the given CompoundStatement, looking
+/// through things like PseudoObjectExpressions.
+static CompoundStmt::const_body_iterator
+findSelectedStmt(CompoundStmt::body_const_range Statements,
+ const Stmt *Target) {
+ return llvm::find_if(Statements, [=](const Stmt *S) {
+ if (S == Target)
+ return true;
+ if (const auto *POE = dyn_cast<PseudoObjectExpr>(S)) {
+ if (POE->getSyntacticForm() == Target)
+ return true;
+ }
+ return false;
+ });
+}
+
+/// Returns the first and the last statements that should be extracted from a
+/// compound statement.
+Optional<CompoundStatementRange> getExtractedStatements(const CompoundStmt *CS,
+ const Stmt *Begin,
+ const Stmt *End) {
+ if (CS->body_empty())
+ return None;
+ assert(Begin && End);
+ CompoundStatementRange Result;
+ Result.First = findSelectedStmt(CS->body(), Begin);
+ assert(Result.First != CS->body_end());
+ Result.Last = findSelectedStmt(
+ CompoundStmt::body_const_range(Result.First, CS->body_end()), End);
+ assert(Result.Last != CS->body_end());
+ return Result;
+}
+
+static RefactoringOperationResult
+initiateAnyExtractOperation(ASTSlice &Slice, ASTContext &Context,
+ SourceLocation Location, SourceRange SelectionRange,
+ bool CreateOperation,
+ ExtractionKind Kind = ExtractionKind::Function) {
+ auto SelectedStmtsOpt = Slice.getSelectedStmtSet();
+ if (!SelectedStmtsOpt)
+ return None;
+ SelectedStmtSet Stmts = *SelectedStmtsOpt;
+ // The selection range is contained entirely within this statement (without
+ // taking leading/trailing comments and whitespace into account).
+ const Stmt *Selected = Stmts.containsSelectionRange;
+
+ // We only want to perform the extraction if the selection range is entirely
+ // within a body of a function or method.
+ if (!Selected)
+ return None;
+ const Decl *ParentDecl =
+ Slice.parentDeclForIndex(*Stmts.containsSelectionRangeIndex);
+
+ if (!ParentDecl ||
+ (!Stmts.isCompoundStatementPartiallySelected() &&
+ !Slice.isContainedInCompoundStmt(*Stmts.containsSelectionRangeIndex)))
+ return RefactoringOperationResult(
+ "the selected expression is not in a function");
+
+ if (isa<Expr>(Selected) && isSimpleExpression(cast<Expr>(Selected)))
+ return RefactoringOperationResult("the selected expression is too simple");
+ if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(Selected)) {
+ if (!PRE->isMessagingGetter())
+ return RefactoringOperationResult("property setter can't be extracted");
+ }
+
+ const Stmt *ParentStmt =
+ Slice.parentStmtForIndex(*Stmts.containsSelectionRangeIndex);
+ if (Kind == ExtractionKind::Expression &&
+ !isLexicalExpression(Selected, ParentStmt))
+ return None;
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+
+ Optional<CompoundStatementRange> ExtractedStmtRange;
+
+ // Check if there are multiple candidates that can be extracted.
+ std::vector<std::string> Candidates;
+ Optional<ExtractOperation::CandidateInfo> FirstCandidateInfo;
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(Selected)) {
+ // Binary '+' and '-' operators allow multiple candidates when the
+ // selection range starts after the LHS expression but still overlaps
+ // with the RHS.
+ if (isMultipleCandidateBinOp(BinOp->getOpcode()) &&
+ (!Stmts.containsSelectionRangeStart ||
+ getPreciseTokenLocEnd(
+ BinOp->getLHS()->getLocEnd(), Context.getSourceManager(),
+ Context.getLangOpts()) == SelectionRange.getBegin()) &&
+ Stmts.containsSelectionRangeEnd) {
+ SourceRange FirstCandidateRange =
+ SourceRange(SelectionRange.getBegin(), BinOp->getLocEnd());
+ if (FirstCandidateRange.getEnd().isMacroID())
+ FirstCandidateRange.setEnd(Context.getSourceManager().getExpansionLoc(
+ FirstCandidateRange.getEnd()));
+ FirstCandidateInfo = ExtractOperation::CandidateInfo(
+ FirstCandidateRange, "+ ",
+ /*AnalyzedStatement=*/BinOp->getRHS());
+ Candidates.push_back(
+ Lexer::getSourceText(
+ CharSourceRange::getTokenRange(FirstCandidateRange),
+ Context.getSourceManager(), Context.getLangOpts())
+ .trim());
+ Candidates.push_back(Lexer::getSourceText(
+ CharSourceRange::getTokenRange(BinOp->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts()));
+ }
+ } else if (const auto *CS = dyn_cast<CompoundStmt>(Selected)) {
+ // We want to extract some child statements from a compound statement unless
+ // we've selected the entire compound statement including the opening and
+ // closing brace.
+ if (Stmts.containsSelectionRangeStart)
+ ExtractedStmtRange =
+ getExtractedStatements(CS, Stmts.containsSelectionRangeStart,
+ Stmts.containsSelectionRangeEnd);
+ }
+
+ auto Operation = llvm::make_unique<ExtractOperation>(
+ Selected, ParentStmt, ParentDecl, std::move(Candidates),
+ ExtractedStmtRange, FirstCandidateInfo, Kind);
+ auto &CandidateExtractionInfo = Operation->CandidateExtractionInfo;
+ SourceRange Range;
+ if (ExtractedStmtRange)
+ Range = SourceRange(ExtractedStmtRange->getFirst()->getLocStart(),
+ ExtractedStmtRange->getLast()->getLocEnd());
+ else
+ Range = Selected->getSourceRange();
+ bool IsBeginMacroArgument = false;
+ if (Range.getBegin().isMacroID()) {
+ if (Context.getSourceManager().isMacroArgExpansion(Range.getBegin())) {
+ Range.setBegin(
+ Context.getSourceManager().getSpellingLoc(Range.getBegin()));
+ IsBeginMacroArgument = true;
+ } else {
+ Range.setBegin(
+ Context.getSourceManager().getExpansionLoc(Range.getBegin()));
+ }
+ }
+ if (Range.getEnd().isMacroID()) {
+ if (IsBeginMacroArgument &&
+ Context.getSourceManager().isMacroArgExpansion(Range.getEnd()))
+ Range.setEnd(Context.getSourceManager().getSpellingLoc(Range.getEnd()));
+ else
+ Range.setEnd(
+ Context.getSourceManager().getExpansionRange(Range.getEnd()).second);
+ }
+ CandidateExtractionInfo.push_back(ExtractOperation::CandidateInfo(Range));
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+}
+
+RefactoringOperationResult clang::tooling::initiateExtractOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange,
+ CreateOperation);
+}
+
+RefactoringOperationResult clang::tooling::initiateExtractMethodOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ // TODO: Verify that method extraction is actually possible.
+ return initiateAnyExtractOperation(Slice, Context, Location, SelectionRange,
+ CreateOperation, ExtractionKind::Method);
+}
+
+RefactoringOperationResult clang::tooling::initiateExtractExpressionOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ RefactoringOperationResult R =
+ initiateAnyExtractOperation(Slice, Context, Location, SelectionRange,
+ CreateOperation, ExtractionKind::Expression);
+ return R;
+}
+
+using ReferencedEntity =
+ llvm::PointerUnion<const DeclRefExpr *, const FieldDecl *>;
+
+/// Iterate over the entities (variables/instance variables) that are directly
+/// referenced by the given expression \p E.
+///
+/// Note: Objective-C ivars are always captured via 'self'.
+static void findEntitiesDirectlyReferencedInExpr(
+ const Expr *E,
+ llvm::function_ref<void(const ReferencedEntity &Entity)> Handler) {
+ E = E->IgnoreParenCasts();
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+ return Handler(DRE);
+
+ if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ if (isa<CXXThisExpr>(ME->getBase()->IgnoreParenCasts())) {
+ if (const auto *FD = dyn_cast_or_null<FieldDecl>(ME->getMemberDecl()))
+ Handler(FD);
+ return;
+ }
+ if (const auto *MD = ME->getMemberDecl()) {
+ if (isa<FieldDecl>(MD) || isa<IndirectFieldDecl>(MD))
+ findEntitiesDirectlyReferencedInExpr(ME->getBase(), Handler);
+ }
+ return;
+ }
+
+ if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {
+ findEntitiesDirectlyReferencedInExpr(CO->getTrueExpr(), Handler);
+ findEntitiesDirectlyReferencedInExpr(CO->getFalseExpr(), Handler);
+ return;
+ }
+
+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+ if (BO->getOpcode() == BO_Comma)
+ return findEntitiesDirectlyReferencedInExpr(BO->getRHS(), Handler);
+ }
+}
+
+template <typename T, typename Matcher>
+static void
+findMatchingParameters(Matcher &ParameterMatcher, const Stmt *S,
+ ASTContext &Context, StringRef Node,
+ llvm::function_ref<void(const T *E)> Handler) {
+ using namespace clang::ast_matchers;
+ auto Matches = match(findAll(callExpr(ParameterMatcher)), *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.template getNodeAs<T>(Node));
+ Matches = match(findAll(cxxConstructExpr(ParameterMatcher)), *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.template getNodeAs<T>(Node));
+}
+
+static void
+findUseOfConstThis(const Stmt *S, ASTContext &Context,
+ llvm::function_ref<void(const CXXThisExpr *E)> Handler) {
+ using namespace clang::ast_matchers;
+ // Check the receiver in method call and member operator calls.
+ auto This = cxxThisExpr().bind("this");
+ auto ThisReceiver = ignoringParenCasts(
+ anyOf(This, unaryOperator(hasOperatorName("*"),
+ hasUnaryOperand(ignoringParenCasts(This)))));
+ auto ConstMethodCallee = callee(cxxMethodDecl(isConst()));
+ auto Matches = match(
+ findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(ThisReceiver)),
+ cxxOperatorCallExpr(ConstMethodCallee,
+ hasArgument(0, ThisReceiver))))),
+ *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.getNodeAs<CXXThisExpr>("this"));
+ // Check parameters in calls.
+ auto ConstPointee = pointee(qualType(isConstQualified()));
+ auto RefParameter = forEachArgumentWithParam(
+ ThisReceiver,
+ parmVarDecl(hasType(qualType(referenceType(ConstPointee)))));
+ findMatchingParameters(RefParameter, S, Context, "this", Handler);
+ auto PtrParameter = forEachArgumentWithParam(
+ ignoringParenCasts(This),
+ parmVarDecl(hasType(qualType(pointerType(ConstPointee)))));
+ findMatchingParameters(PtrParameter, S, Context, "this", Handler);
+}
+
+static void findArgumentsPassedByNonConstReference(
+ const Stmt *S, ASTContext &Context,
+ llvm::function_ref<void(const Expr *E)> Handler) {
+ using namespace clang::ast_matchers;
+ // Check the receiver in method call and member operator calls.
+ auto NonPointerReceiver =
+ expr(unless(hasType(qualType(pointerType())))).bind("arg");
+ auto NonConstMethodCallee = callee(cxxMethodDecl(unless(isConst())));
+ auto Matches =
+ match(findAll(expr(anyOf(
+ cxxMemberCallExpr(NonConstMethodCallee, on(NonPointerReceiver)),
+ cxxOperatorCallExpr(NonConstMethodCallee,
+ hasArgument(0, NonPointerReceiver))))),
+ *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.getNodeAs<Expr>("arg"));
+ // Check parameters in calls.
+ auto RefParameter = forEachArgumentWithParam(
+ expr().bind("arg"), parmVarDecl(hasType(qualType(referenceType(unless(
+ pointee(qualType(isConstQualified()))))))));
+ Matches = match(findAll(callExpr(RefParameter)), *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.getNodeAs<Expr>("arg"));
+ Matches = match(findAll(cxxConstructExpr(RefParameter)), *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.getNodeAs<Expr>("arg"));
+}
+
+static void findAddressExpressionsPassedByConstPointer(
+ const Stmt *S, ASTContext &Context,
+ llvm::function_ref<void(const UnaryOperator *E)> Handler) {
+ using namespace clang::ast_matchers;
+ auto ConstPtrParameter = forEachArgumentWithParam(
+ ignoringParenImpCasts(unaryOperator(hasOperatorName("&")).bind("arg")),
+ parmVarDecl(hasType(
+ qualType(pointerType(pointee(qualType(isConstQualified())))))));
+ auto Matches = match(findAll(callExpr(ConstPtrParameter)), *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.getNodeAs<UnaryOperator>("arg"));
+ Matches = match(findAll(cxxConstructExpr(ConstPtrParameter)), *S, Context);
+ for (const auto &Match : Matches)
+ Handler(Match.getNodeAs<UnaryOperator>("arg"));
+}
+
+static bool isImplicitInitializer(const VarDecl *VD) {
+ assert(VD->hasInit());
+ const auto *E = VD->getInit();
+ if (isa<ExprWithCleanups>(E))
+ return false;
+ const auto *Construct = dyn_cast<CXXConstructExpr>(E);
+ if (!Construct)
+ return E->getLocStart() == VD->getLocation();
+ return Construct->getParenOrBraceRange().isInvalid();
+}
+
+static const Expr *getInitializerExprWithLexicalRange(const Expr *E) {
+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(E)) {
+ if (const auto *Construct = dyn_cast<CXXConstructExpr>(EWC->getSubExpr())) {
+ if (Construct->getNumArgs() == 1) {
+ if (const auto *ME =
+ dyn_cast<MaterializeTemporaryExpr>(Construct->getArg(0)))
+ return ME;
+ }
+ }
+ }
+ return E;
+}
+
+namespace {
+
+class ExtractedCodeVisitor : public RecursiveASTVisitor<ExtractedCodeVisitor> {
+ int DefineOrdering = 0;
+
+public:
+ struct CaptureInfo {
+ bool IsMutated = false;
+ bool IsDefined = false;
+ bool IsAddressTaken = false;
+ bool IsConstAddressTaken = false;
+ bool IsFieldCapturedWithThis = false;
+ bool IsUsed = false;
+ int DefineOrderingPriority = 0;
+
+ bool isPassedByRefOrPtr() const {
+ return IsMutated || IsAddressTaken || IsConstAddressTaken;
+ }
+ bool isRefOrPtrConst() const {
+ return IsConstAddressTaken && !IsMutated && !IsAddressTaken;
+ }
+ };
+
+ const ImplicitParamDecl *SelfDecl;
+
+ ExtractedCodeVisitor(const ImplicitParamDecl *SelfDecl)
+ : SelfDecl(SelfDecl) {}
+
+ bool HasReturnInExtracted = false;
+
+ CaptureInfo &captureVariable(const VarDecl *VD) {
+ CaptureInfo &Result = CapturedVariables[VD];
+ Result.IsUsed = true;
+ return Result;
+ }
+
+ CaptureInfo &captureField(const FieldDecl *FD) { return CapturedFields[FD]; }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *E) {
+ const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (!VD)
+ return true;
+ if (VD == SelfDecl) {
+ CaptureSelf = true;
+ SelfType = VD->getType();
+ return true;
+ }
+ if (!VD->isLocalVarDeclOrParm())
+ return true;
+ captureVariable(VD);
+ return true;
+ }
+
+ void captureThisWithoutConstConcerns(const CXXThisExpr *E) {
+ CaptureThis = true;
+ ThisRecordType = E->getType()->getPointeeType();
+ }
+
+ bool VisitCXXThisExpr(const CXXThisExpr *E) {
+ captureThisWithoutConstConcerns(E);
+ ThisUsesWithUnknownConstness.insert(E);
+ return true;
+ }
+
+ bool TraverseMemberExpr(MemberExpr *E) {
+ const auto *Base = dyn_cast<CXXThisExpr>(E->getBase()->IgnoreParenCasts());
+ if (!Base)
+ return RecursiveASTVisitor::TraverseMemberExpr(E);
+ const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(E->getMemberDecl());
+ if (!FD)
+ return RecursiveASTVisitor::TraverseMemberExpr(E);
+ CaptureInfo &Info = captureField(FD);
+ // Don't capture the implicit 'this' for private fields as we don't want to
+ // capture this if we only use the private fields.
+ if (FD->getAccess() == AS_public || !Base->isImplicit()) {
+ Info.IsFieldCapturedWithThis = true;
+ // The member might have an effect on the constness of the captured 'this'
+ // but this is checked via mutation/const tracking for the field itself,
+ // so we just capture 'this' without worrying about checking if it's used
+ // in a 'const' manner here.
+ captureThisWithoutConstConcerns(Base);
+ }
+ return true;
+ }
+
+ void captureSuper(QualType T) {
+ if (CaptureSuper)
+ return;
+ SuperType = T;
+ CaptureSuper = true;
+ }
+
+ bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
+ if (E->isSuperReceiver())
+ captureSuper(E->getSuperReceiverType());
+ // Base might be an opaque expression, so we have to visit it manually as
+ // we don't necessarily visit the setter/getter message sends if just the
+ // property was selected.
+ if (E->isObjectReceiver()) {
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E->getBase()))
+ TraverseStmt(OVE->getSourceExpr());
+ }
+ return RecursiveASTVisitor::TraverseObjCPropertyRefExpr(E);
+ }
+
+ bool TraverseBinAssign(BinaryOperator *S) {
+ // RHS might be an opaque expression, if this is a property assignment. We
+ // have to visit it manually as we don't necessarily visit the setter/getter
+ // message sends if just the property was selected.
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(S->getRHS()))
+ TraverseStmt(OVE->getSourceExpr());
+ return RecursiveASTVisitor::TraverseBinAssign(S);
+ }
+
+ void findCapturedVariableOrFieldsInExpression(
+ const Expr *E, llvm::function_ref<void(CaptureInfo &)> Handler) {
+ findEntitiesDirectlyReferencedInExpr(
+ E, [&Handler, this](const ReferencedEntity &Entity) {
+ if (const auto *DRE = Entity.dyn_cast<const DeclRefExpr *>()) {
+ const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (!VD || !VD->isLocalVarDeclOrParm() || VD->isImplicit())
+ return;
+ return Handler(captureVariable(VD));
+ }
+ return Handler(captureField(Entity.get<const FieldDecl *>()));
+ });
+ }
+
+ void
+ markDirectlyReferencedVariableOrFieldInExpressionAsMutated(const Expr *E) {
+ findCapturedVariableOrFieldsInExpression(
+ E, [](CaptureInfo &Capture) { Capture.IsMutated = true; });
+ }
+
+ bool VisitBinaryOperator(const BinaryOperator *E) {
+ if (E->isAssignmentOp())
+ markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getLHS());
+ return true;
+ }
+
+ bool VisitUnaryPreInc(const UnaryOperator *E) {
+ markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr());
+ return true;
+ }
+
+ bool VisitUnaryPostInc(const UnaryOperator *E) {
+ markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr());
+ return true;
+ }
+
+ bool VisitUnaryPreDec(const UnaryOperator *E) {
+ markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr());
+ return true;
+ }
+
+ bool VisitUnaryPostDec(const UnaryOperator *E) {
+ markDirectlyReferencedVariableOrFieldInExpressionAsMutated(E->getSubExpr());
+ return true;
+ }
+
+ /// If the given expression refers to a local/instance variable or a
+ /// a member of such variable that variable is marked as captured by
+ /// reference.
+ void captureVariableOrFieldInExpressionByReference(const Expr *E) {
+ findCapturedVariableOrFieldsInExpression(
+ E, [](CaptureInfo &Capture) { Capture.IsAddressTaken = true; });
+ }
+
+ bool VisitUnaryAddrOf(const UnaryOperator *E) {
+ // Capture the entity with 'const' reference/pointer when its address is
+ // passed into a function that takes a 'const' pointer and no other
+ // mutations or non-const address/reference acquisitions occur.
+ if (AddressExpressionsPassedToConstPointerParameter.count(E))
+ findCapturedVariableOrFieldsInExpression(
+ E->getSubExpr(),
+ [](CaptureInfo &Capture) { Capture.IsConstAddressTaken = true; });
+ else
+ captureVariableOrFieldInExpressionByReference(E->getSubExpr());
+ return true;
+ }
+
+ bool VisitObjCMessageExpr(const ObjCMessageExpr *E) {
+ if (E->getSuperLoc().isValid())
+ captureSuper(E->getSuperType());
+ const ObjCMethodDecl *MD = E->getMethodDecl();
+ if (!MD)
+ return true;
+ for (const auto &Param : llvm::enumerate(MD->parameters())) {
+ QualType T = Param.value()->getType();
+ if (Param.index() >= E->getNumArgs())
+ break;
+ if (T->isReferenceType() && !T->getPointeeType().isConstQualified())
+ captureVariableOrFieldInExpressionByReference(E->getArg(Param.index()));
+ if (T->isPointerType() && T->getPointeeType().isConstQualified()) {
+ // Check if this is an '&' passed into a const pointer parameter.
+ const Expr *Arg = E->getArg(Param.index());
+ if (const auto *Op =
+ dyn_cast<UnaryOperator>(Arg->IgnoreParenImpCasts())) {
+ if (Op->getOpcode() == UO_AddrOf)
+ AddressExpressionsPassedToConstPointerParameter.insert(Op);
+ }
+ }
+ }
+ return true;
+ }
+
+ bool VisitVarDecl(const VarDecl *VD) {
+ // Don't capture using the captureVariable method as we don't want to mark
+ // the declaration as a 'use'. This allows us to avoid passing in variables
+ // that are defined in extracted code, used afterwards, but never actually
+ // used in the extracted code.
+ CaptureInfo &Capture = CapturedVariables[VD];
+ Capture.IsDefined = true;
+ Capture.DefineOrderingPriority = ++DefineOrdering;
+ // Ensure the capture is marked as 'used' when the variable declaration has
+ // an explicit initialization expression. This allows us to pass it by
+ // reference when it's defined in extracted code, used afterwards, but never
+ // actually used in the extracted code. The main reason why we want to try
+ // to keep this initialization in the extracted code is to preserve
+ // semantics as the initialization expression might have side-effects.
+ if (!Capture.IsUsed && VD->hasInit() && !isImplicitInitializer(VD))
+ Capture.IsUsed = true;
+ QualType T = VD->getType();
+ if (T->isReferenceType() && !T->getPointeeType().isConstQualified() &&
+ VD->hasInit())
+ captureVariableOrFieldInExpressionByReference(VD->getInit());
+ return true;
+ }
+
+ bool VisitReturnStmt(const ReturnStmt *S) {
+ HasReturnInExtracted = true;
+ return true;
+ }
+
+ void InspectExtractedStmt(Stmt *S, ASTContext &Context) {
+ findAddressExpressionsPassedByConstPointer(
+ S, Context, [this](const UnaryOperator *Arg) {
+ AddressExpressionsPassedToConstPointerParameter.insert(Arg);
+ });
+ TraverseStmt(S);
+ findArgumentsPassedByNonConstReference(S, Context, [this](const Expr *Arg) {
+ captureVariableOrFieldInExpressionByReference(Arg);
+ });
+ if (CaptureThis && !ThisUsesWithUnknownConstness.empty()) {
+ // Compare the definite 'const' uses of 'this' to all the seen uses
+ // (except for the known field uses).
+ findUseOfConstThis(S, Context, [this](const CXXThisExpr *Arg) {
+ ThisUsesWithUnknownConstness.erase(Arg);
+ });
+ IsThisConstForNonCapturedFieldUses = ThisUsesWithUnknownConstness.empty();
+ }
+ }
+
+ llvm::DenseMap<const VarDecl *, CaptureInfo> CapturedVariables;
+ llvm::DenseMap<const FieldDecl *, CaptureInfo> CapturedFields;
+ llvm::SmallPtrSet<const UnaryOperator *, 8>
+ AddressExpressionsPassedToConstPointerParameter;
+ llvm::SmallPtrSet<const CXXThisExpr *, 16> ThisUsesWithUnknownConstness;
+ bool CaptureThis = false;
+ bool IsThisConstForNonCapturedFieldUses = true;
+ QualType ThisRecordType;
+ bool CaptureSelf = false, CaptureSuper = false;
+ QualType SelfType, SuperType;
+};
+
+/// Traverses the extracted code and finds the uses of captured variables
+/// that are passed into the extracted function using a pointer.
+class VariableDefinedInExtractedCodeUseAfterExtractionFinder
+ : public RecursiveASTVisitor<
+ VariableDefinedInExtractedCodeUseAfterExtractionFinder> {
+ bool IsAfterExtracted = false;
+
+public:
+ const Stmt *LastExtractedStmt;
+ const llvm::SmallPtrSetImpl<const VarDecl *> &VariablesDefinedInExtractedCode;
+ llvm::SmallPtrSet<const VarDecl *, 4> VariablesUsedAfterExtraction;
+
+ VariableDefinedInExtractedCodeUseAfterExtractionFinder(
+ const Stmt *LastExtractedStmt,
+ const llvm::SmallPtrSetImpl<const VarDecl *>
+ &VariablesDefinedInExtractedCode)
+ : LastExtractedStmt(LastExtractedStmt),
+ VariablesDefinedInExtractedCode(VariablesDefinedInExtractedCode) {}
+
+ bool TraverseStmt(Stmt *S) {
+ RecursiveASTVisitor::TraverseStmt(S);
+ if (S == LastExtractedStmt)
+ IsAfterExtracted = true;
+ return true;
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *E) {
+ if (!IsAfterExtracted)
+ return true;
+ const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (!VD)
+ return true;
+ if (VariablesDefinedInExtractedCode.count(VD))
+ VariablesUsedAfterExtraction.insert(VD);
+ return true;
+ }
+};
+
+class PossibleShadowingVariableFinder
+ : public RecursiveASTVisitor<PossibleShadowingVariableFinder> {
+ const VarDecl *TargetVD;
+
+ PossibleShadowingVariableFinder(const VarDecl *TargetVD)
+ : TargetVD(TargetVD) {}
+
+public:
+ bool VisitVarDecl(const VarDecl *VD) {
+ if (VD == TargetVD || VD->getName() != TargetVD->getName())
+ return true;
+ return false;
+ }
+
+ /// Returns true if the given statement \p S has a variable declaration whose
+ /// name is identical to the given variable declaration \p VD.
+ static bool hasShadowingVar(const VarDecl *VD, const Stmt *S) {
+ return !PossibleShadowingVariableFinder(VD).TraverseStmt(
+ const_cast<Stmt *>(S));
+ }
+};
+
+/// Traverses the extracted code and rewrites the 'return' statements to ensure
+/// that they now return some value.
+class ReturnRewriter : public RecursiveASTVisitor<ReturnRewriter> {
+ Rewriter &SourceRewriter;
+ std::string Text;
+
+public:
+ ReturnRewriter(Rewriter &SourceRewriter, StringRef Text)
+ : SourceRewriter(SourceRewriter), Text(std::string(" ") + Text.str()) {}
+
+ bool VisitReturnStmt(const ReturnStmt *S) {
+ SourceRewriter.InsertText(
+ getPreciseTokenLocEnd(S->getLocEnd(), SourceRewriter.getSourceMgr(),
+ SourceRewriter.getLangOpts()),
+ Text);
+ return true;
+ }
+};
+
+/// Prints the given initializer expression using the original source code if
+/// possible.
+static void printInitializerExpressionUsingOriginalSyntax(
+ const VarDecl *VD, const Expr *E, bool IsDeclaration, const ASTContext &Ctx,
+ llvm::raw_ostream &OS, const PrintingPolicy &PP) {
+ E = getInitializerExprWithLexicalRange(E);
+ SourceRange Range = E->getSourceRange();
+ bool UseEquals = true;
+ bool UseTypeName = false;
+ if (const auto *Construct = dyn_cast<CXXConstructExpr>(E)) {
+ SourceRange SubRange = Construct->getParenOrBraceRange();
+ if (SubRange.isValid()) {
+ UseEquals = false;
+ UseTypeName = true;
+ Range = SubRange;
+ }
+ }
+ if (Range.getBegin().isMacroID())
+ Range.setBegin(Ctx.getSourceManager().getExpansionLoc(Range.getBegin()));
+ if (Range.getEnd().isMacroID())
+ Range.setEnd(Ctx.getSourceManager().getExpansionLoc(Range.getEnd()));
+ bool IsInvalid = false;
+ StringRef Text = Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
+ Ctx.getSourceManager(),
+ Ctx.getLangOpts(), &IsInvalid);
+ if (IsDeclaration && UseEquals)
+ OS << " = ";
+ else if (!IsDeclaration && UseTypeName)
+ VD->getType().print(OS, PP);
+ if (IsInvalid)
+ E->printPretty(OS, nullptr, PP);
+ else
+ OS << Text;
+};
+
+/// Traverses the extracted code and rewrites the declaration statements that
+/// declare variables that are used after the extracted code.
+class DefinedInExtractedCodeDeclStmtRewriter
+ : public RecursiveASTVisitor<DefinedInExtractedCodeDeclStmtRewriter> {
+public:
+ Rewriter &SourceRewriter;
+ const llvm::SmallPtrSetImpl<const VarDecl *> &VariablesUsedAfterExtraction;
+ const PrintingPolicy &PP;
+
+ DefinedInExtractedCodeDeclStmtRewriter(
+ Rewriter &SourceRewriter, const llvm::SmallPtrSetImpl<const VarDecl *>
+ &VariablesUsedAfterExtraction,
+ const PrintingPolicy &PP)
+ : SourceRewriter(SourceRewriter),
+ VariablesUsedAfterExtraction(VariablesUsedAfterExtraction), PP(PP) {}
+
+ /// When a declaration statement declares variables that are all used
+ /// after extraction, we can rewrite it completely into a set of assignments
+ /// while still preserving the original initializer expressions when we
+ /// can.
+ void rewriteAllVariableDeclarationsToAssignments(const DeclStmt *S) {
+ SourceLocation StartLoc = S->getLocStart();
+ for (const Decl *D : S->decls()) {
+ const auto *VD = dyn_cast<VarDecl>(D);
+ if (!VD || !VariablesUsedAfterExtraction.count(VD))
+ continue;
+ if (!VD->hasInit() || isImplicitInitializer(VD)) {
+ // Remove the variable declarations without explicit initializers.
+ // This can affect the semantics of the program if the implicit
+ // initialization expression has side effects.
+ SourceRange Range = SourceRange(
+ StartLoc, S->isSingleDecl() ? S->getLocEnd() : VD->getLocation());
+ SourceRewriter.RemoveText(Range);
+ continue;
+ }
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ if (StartLoc != S->getLocStart())
+ OS << "; ";
+ const ASTContext &Ctx = D->getASTContext();
+ // Dereference the variable unless the source uses C++.
+ if (!Ctx.getLangOpts().CPlusPlus)
+ OS << '*';
+ OS << VD->getName() << " = ";
+ const Expr *Init = getInitializerExprWithLexicalRange(VD->getInit());
+ SourceLocation End = Init->getLocStart();
+ if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
+ SourceRange SubRange = Construct->getParenOrBraceRange();
+ if (SubRange.isValid()) {
+ End = SubRange.getBegin();
+ VD->getType().print(OS, PP);
+ }
+ }
+ if (End.isMacroID())
+ End = Ctx.getSourceManager().getExpansionLoc(End);
+ auto Range = CharSourceRange::getCharRange(StartLoc, End);
+ SourceRewriter.ReplaceText(StartLoc, SourceRewriter.getRangeSize(Range),
+ OS.str());
+ StartLoc = getPreciseTokenLocEnd(D->getLocEnd(), Ctx.getSourceManager(),
+ Ctx.getLangOpts());
+ }
+ }
+
+ /// When a declaration statement has variables that are both used after
+ /// extraction and not used after extraction, we create new declaration
+ /// statements that declare the unused variables, while creating assignment
+ /// statements that "initialize" the variables that are used after the
+ /// extraction. This way we can preserve the order of
+ /// initialization/assignment from the original declaration statement.
+ void rewriteMixedDeclarations(const DeclStmt *S) {
+ // Completely rewrite the declaration statement.
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ for (const Decl *D : S->decls()) {
+ const ASTContext &Ctx = D->getASTContext();
+ const VarDecl *VD = dyn_cast<VarDecl>(D);
+ bool IsLast = D == S->decl_end()[-1];
+ if (!VD) {
+ OS << "<<unsupported declaration>>;";
+ continue;
+ }
+
+ auto PrintInit = [&](bool IsDeclaration) {
+ printInitializerExpressionUsingOriginalSyntax(
+ VD, VD->getInit(), IsDeclaration, Ctx, OS, PP);
+ };
+ if (!VariablesUsedAfterExtraction.count(VD)) {
+ VD->getType().print(OS, PP);
+ OS << " " << VD->getName();
+ if (VD->hasInit() && !isImplicitInitializer(VD))
+ PrintInit(/*IsDeclaration=*/true);
+ OS << ";";
+ if (!IsLast)
+ OS << ' ';
+ continue;
+ }
+ if (VD->hasInit() && !isImplicitInitializer(VD)) {
+ // Dereference the variable unless the source uses C++.
+ if (!Ctx.getLangOpts().CPlusPlus)
+ OS << '*';
+ OS << VD->getName() << " = ";
+ PrintInit(/*IsDeclaration=*/false);
+ OS << ";";
+ if (!IsLast)
+ OS << ' ';
+ }
+ }
+ SourceRewriter.ReplaceText(S->getSourceRange(), OS.str());
+ }
+
+ bool VisitDeclStmt(const DeclStmt *S) {
+ bool AreAllUsed = true;
+ bool AreNoneUsed = true;
+ for (const Decl *D : S->decls()) {
+ const auto *VD = dyn_cast<VarDecl>(D);
+ if (!VD || !VariablesUsedAfterExtraction.count(VD)) {
+ AreAllUsed = false;
+ continue;
+ }
+ AreNoneUsed = false;
+ // Exit early when both flags were set in the loop.
+ if (!AreAllUsed)
+ break;
+ }
+ if (AreNoneUsed)
+ return true;
+
+ if (AreAllUsed)
+ rewriteAllVariableDeclarationsToAssignments(S);
+ else
+ rewriteMixedDeclarations(S);
+ return true;
+ }
+};
+
+/// Takes care of pseudo object expressions and Objective-C properties to avoid
+/// duplicate rewrites and missing rewrites.
+template <typename T>
+class PseudoObjectRewriter : public RecursiveASTVisitor<T> {
+ typedef RecursiveASTVisitor<T> Base;
+
+public:
+ bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
+ return Base::TraverseStmt(E->getSyntacticForm());
+ }
+
+ bool TraverseObjCPropertyRefExpr(ObjCPropertyRefExpr *E) {
+ // Base might be an opaque expression, so we have to visit it manually as
+ // we don't necessarily visit the setter/getter message sends if just the
+ // property was selected.
+ if (E->isObjectReceiver()) {
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E->getBase()))
+ Base::TraverseStmt(OVE->getSourceExpr());
+ }
+ return Base::TraverseObjCPropertyRefExpr(E);
+ }
+
+ bool TraverseBinAssign(BinaryOperator *S) {
+ // RHS might be an opaque expression, if this is a property assignment. We
+ // have to visit it manually as we don't necessarily visit the setter/getter
+ // message sends if just the property was selected.
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(S->getRHS()))
+ Base::TraverseStmt(OVE->getSourceExpr());
+ return Base::TraverseBinAssign(S);
+ }
+};
+
+/// Traverses the extracted code and rewrites the uses of captured variables
+/// that are passed into the extracted function using a pointer.
+class CapturedVariableCaptureByPointerRewriter
+ : public PseudoObjectRewriter<CapturedVariableCaptureByPointerRewriter> {
+public:
+ const VarDecl *TargetVD;
+ Rewriter &SourceRewriter;
+
+ CapturedVariableCaptureByPointerRewriter(const VarDecl *VD,
+ Rewriter &SourceRewriter)
+ : TargetVD(VD), SourceRewriter(SourceRewriter) {}
+
+ bool isTargetDeclRefExpr(const Expr *E) {
+ const auto *DRE = dyn_cast<DeclRefExpr>(E);
+ if (!DRE)
+ return false;
+ return dyn_cast<VarDecl>(DRE->getDecl()) == TargetVD;
+ }
+
+ void dereferenceTargetVar(const Expr *E, bool WrapInParens = false) {
+ SourceRewriter.InsertTextBefore(E->getLocStart(),
+ WrapInParens ? "(*" : "*");
+ if (WrapInParens)
+ SourceRewriter.InsertTextAfterToken(E->getLocEnd(), ")");
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *E) {
+ const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (VD != TargetVD)
+ return true;
+ dereferenceTargetVar(E);
+ return true;
+ }
+
+ bool TraverseUnaryAddrOf(UnaryOperator *E) {
+ if (const auto *DRE =
+ dyn_cast<DeclRefExpr>(E->getSubExpr()->IgnoreParenCasts())) {
+ const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (VD == TargetVD) {
+ // Remove the '&' as the variable is now a pointer.
+ SourceRewriter.RemoveText(
+ CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart()));
+ return true;
+ }
+ }
+ return RecursiveASTVisitor::TraverseUnaryAddrOf(E);
+ }
+
+ bool TraverseMemberExpr(MemberExpr *E) {
+ if (!E->isArrow()) {
+ if (const auto *DRE =
+ dyn_cast<DeclRefExpr>(E->getBase()->IgnoreParenCasts())) {
+ const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (VD == TargetVD) {
+ // Replace '.' with '->'.
+ SourceRewriter.ReplaceText(E->getOperatorLoc(), 1, "->");
+ return true;
+ }
+ }
+ } else if (isTargetDeclRefExpr(E->getBase()->IgnoreImpCasts())) {
+ // Ensure the variable is wrapped in parenthesis when it's the base of
+ // '->' operator.
+ dereferenceTargetVar(E->getBase(), /*WrapInParens=*/true);
+ return true;
+ }
+ return RecursiveASTVisitor::TraverseMemberExpr(E);
+ }
+};
+
+/// Traverses the extracted code and rewrites the uses of 'this' that can be
+/// rewritten as references.
+class CapturedThisReferenceRewriter
+ : public PseudoObjectRewriter<CapturedThisReferenceRewriter> {
+public:
+ Rewriter &SourceRewriter;
+ llvm::SmallPtrSet<const CXXThisExpr *, 8> RewrittenExpressions;
+
+ CapturedThisReferenceRewriter(Rewriter &SourceRewriter)
+ : SourceRewriter(SourceRewriter) {}
+
+ void rewriteThis(const CXXThisExpr *E) {
+ RewrittenExpressions.insert(E);
+ if (!E->isImplicit())
+ SourceRewriter.ReplaceText(E->getLocStart(), 4, "object");
+ else
+ SourceRewriter.InsertText(E->getLocStart(), "object");
+ }
+
+ bool VisitMemberExpr(const MemberExpr *E) {
+ const auto *This =
+ dyn_cast<CXXThisExpr>(E->getBase()->IgnoreParenImpCasts());
+ if (This) {
+ rewriteThis(This);
+ if (!This->isImplicit() && E->isArrow())
+ SourceRewriter.ReplaceText(E->getOperatorLoc(), 2, ".");
+ else
+ SourceRewriter.InsertText(E->getBase()->getLocEnd(), ".");
+ }
+ return true;
+ }
+};
+
+/// Traverses the extracted code and rewrites the uses of 'this' into '&object'.
+class CapturedThisPointerRewriter
+ : public PseudoObjectRewriter<CapturedThisPointerRewriter> {
+public:
+ Rewriter &SourceRewriter;
+ const llvm::SmallPtrSetImpl<const CXXThisExpr *> &RewrittenExpressions;
+
+ CapturedThisPointerRewriter(
+ Rewriter &SourceRewriter,
+ const llvm::SmallPtrSetImpl<const CXXThisExpr *> &RewrittenExpressions)
+ : SourceRewriter(SourceRewriter),
+ RewrittenExpressions(RewrittenExpressions) {}
+
+ void replace(const CXXThisExpr *E, StringRef Text) {
+ SourceRewriter.ReplaceText(E->getLocStart(), 4, Text);
+ }
+
+ bool VisitCXXThisExpr(const CXXThisExpr *E) {
+ if (RewrittenExpressions.count(E))
+ return true;
+ if (!E->isImplicit())
+ replace(E, "&object");
+ return true;
+ }
+
+ bool TraverseUnaryDeref(UnaryOperator *E) {
+ if (const auto *This =
+ dyn_cast<CXXThisExpr>(E->getSubExpr()->IgnoreParenImpCasts())) {
+ if (!This->isImplicit()) {
+ // Remove the '*' as the variable is now a reference.
+ SourceRewriter.RemoveText(
+ CharSourceRange::getTokenRange(E->getLocStart(), E->getLocStart()));
+ replace(This, "object");
+ return true;
+ }
+ }
+ return RecursiveASTVisitor::TraverseUnaryAddrOf(E);
+ }
+};
+
+/// Traverses the extracted code and rewrites the uses of 'self' into 'object'.
+class CapturedSelfRewriter : public PseudoObjectRewriter<CapturedSelfRewriter> {
+public:
+ Rewriter &SourceRewriter;
+ const ImplicitParamDecl *SelfDecl;
+
+ CapturedSelfRewriter(Rewriter &SourceRewriter,
+ const ImplicitParamDecl *SelfDecl)
+ : SourceRewriter(SourceRewriter), SelfDecl(SelfDecl) {
+ assert(SelfDecl);
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *E) {
+ const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (!VD || VD != SelfDecl)
+ return true;
+ if (E->getLocStart().isInvalid())
+ return true;
+ SourceRewriter.ReplaceText(E->getLocStart(), 4, "object");
+ return true;
+ }
+
+ void insertObjectForImplicitSelf(const Expr *E, SourceLocation Loc,
+ StringRef Text) {
+ const auto *DRE = dyn_cast<DeclRefExpr>(E);
+ if (!DRE)
+ return;
+ const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl());
+ if (!VD || VD != SelfDecl || DRE->getLocStart().isValid())
+ return;
+ SourceRewriter.InsertText(Loc, Text);
+ }
+
+ bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) {
+ insertObjectForImplicitSelf(E->getBase()->IgnoreImpCasts(),
+ E->getLocStart(), "object->");
+ return true;
+ }
+};
+
+/// Traverses the extracted code and rewrites the uses of 'self' into the name
+/// of the class.
+class CapturedClassSelfRewriter
+ : public PseudoObjectRewriter<CapturedClassSelfRewriter> {
+public:
+ Rewriter &SourceRewriter;
+ StringRef ClassName;
+ const ImplicitParamDecl *SelfDecl;
+
+ CapturedClassSelfRewriter(Rewriter &SourceRewriter, StringRef ClassName,
+ const ImplicitParamDecl *SelfDecl)
+ : SourceRewriter(SourceRewriter), ClassName(ClassName),
+ SelfDecl(SelfDecl) {
+
+ assert(SelfDecl);
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *E) {
+ const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
+ if (!VD || VD != SelfDecl || E->getLocStart().isInvalid())
+ return true;
+ SourceRewriter.ReplaceText(E->getLocStart(), 4, ClassName);
+ return true;
+ }
+};
+
+/// Traverses the extracted code and rewrites the uses of 'super' into
+/// 'superObject' or the name of the super class.
+class CapturedSuperRewriter
+ : public PseudoObjectRewriter<CapturedSuperRewriter> {
+public:
+ Rewriter &SourceRewriter;
+ StringRef ReplacementString;
+
+ CapturedSuperRewriter(Rewriter &SourceRewriter, StringRef ReplacementString)
+ : SourceRewriter(SourceRewriter), ReplacementString(ReplacementString) {}
+
+ void rewriteSuper(SourceLocation Loc) {
+ SourceRewriter.ReplaceText(Loc, strlen("super"), ReplacementString);
+ }
+
+ bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *E) {
+ if (E->isSuperReceiver())
+ rewriteSuper(E->getReceiverLocation());
+ return true;
+ }
+
+ bool VisitObjCMessageExpr(const ObjCMessageExpr *E) {
+ if (E->getSuperLoc().isValid())
+ rewriteSuper(E->getSuperLoc());
+ return true;
+ }
+};
+
+struct ExtractionSemicolonPolicy {
+ bool IsNeededInExtractedFunction;
+ bool IsNeededInOriginalFunction;
+
+ static ExtractionSemicolonPolicy neededInExtractedFunction() {
+ return {true, false};
+ }
+ static ExtractionSemicolonPolicy neededInOriginalFunction() {
+ return {false, true};
+ }
+ static ExtractionSemicolonPolicy neededInBoth() { return {true, true}; }
+};
+
+} // end anonymous namespace
+
+ExtractionSemicolonPolicy
+computeSemicolonExtractionPolicy(const Stmt *S, SourceRange &ExtractedRange,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (isa<Expr>(S))
+ return ExtractionSemicolonPolicy::neededInExtractedFunction();
+ bool NeedsSemi = isSemicolonRequiredAfter(S);
+ if (!NeedsSemi)
+ return ExtractionSemicolonPolicy::neededInOriginalFunction();
+ SourceLocation End = ExtractedRange.getEnd();
+ if (isSemicolonAtLocation(End, SM, LangOpts))
+ return ExtractionSemicolonPolicy::neededInOriginalFunction();
+ SourceLocation NextTokenLoc =
+ Lexer::findNextTokenLocationAfterTokenAt(End, SM, LangOpts);
+ if (NextTokenLoc.isValid() &&
+ isSemicolonAtLocation(NextTokenLoc, SM, LangOpts) &&
+ areOnSameLine(NextTokenLoc, End, SM)) {
+ ExtractedRange.setEnd(NextTokenLoc);
+ return ExtractionSemicolonPolicy::neededInOriginalFunction();
+ }
+ return ExtractionSemicolonPolicy::neededInBoth();
+}
+
+PrintingPolicy getPrintingPolicy(const ASTContext &Context,
+ const Preprocessor &PP) {
+ PrintingPolicy Policy = Context.getPrintingPolicy();
+ // Our printing policy is copied over the ASTContext printing policy whenever
+ // a diagnostic is emitted, so recompute it.
+ Policy.Bool = Context.getLangOpts().Bool;
+ // FIXME: This is duplicated with Sema.cpp. When upstreaming this should be
+ // cleaned up.
+ if (!Policy.Bool) {
+ if (const MacroInfo *BoolMacro = PP.getMacroInfo(Context.getBoolName())) {
+ Policy.Bool = BoolMacro->isObjectLike() &&
+ BoolMacro->getNumTokens() == 1 &&
+ BoolMacro->getReplacementToken(0).is(tok::kw__Bool);
+ }
+ }
+ return Policy;
+}
+
+static QualType getFunctionLikeParentDeclReturnType(const Decl *D) {
+ // FIXME: might need to handle ObjC blocks in the future.
+ if (const auto *M = dyn_cast<ObjCMethodDecl>(D))
+ return M->getReturnType();
+ return cast<FunctionDecl>(D)->getReturnType();
+}
+
+static const Stmt *getEnclosingDeclBody(const Decl *D) {
+ // FIXME: might need to handle ObjC blocks in the future.
+ if (const auto *M = dyn_cast<ObjCMethodDecl>(D))
+ return M->getBody();
+ return cast<FunctionDecl>(D)->getBody();
+}
+
+static bool isEnclosingMethodConst(const Decl *D) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+ return MD->isConst();
+ return false;
+}
+
+static bool isEnclosingMethodStatic(const Decl *D) {
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+ return MD->isStatic();
+ return false;
+}
+
+static bool isEnclosingMethodOutOfLine(const Decl *D) {
+ const auto *MD = dyn_cast<CXXMethodDecl>(D);
+ if (!MD)
+ return false;
+ return MD->isOutOfLine();
+}
+
+static void printEnclosingMethodScope(const Decl *D, llvm::raw_ostream &OS,
+ const PrintingPolicy &PP) {
+ const auto *MD = dyn_cast<CXXMethodDecl>(D);
+ if (!MD)
+ return;
+ if (!MD->isOutOfLine() || !MD->getQualifier())
+ return;
+ MD->getQualifier()->print(OS, PP);
+}
+
+static SourceLocation
+computeFunctionExtractionLocation(const Decl *D, bool IsMethodExtraction) {
+ if (!IsMethodExtraction && isa<CXXMethodDecl>(D)) {
+ // Code from methods that defined in class bodies should be extracted to a
+ // function defined just before the class.
+ while (const auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalDeclContext()))
+ D = RD;
+ }
+ return D->getLocStart();
+}
+
+namespace {
+enum class MethodDeclarationPlacement { After, Before };
+
+/// \brief Represents an entity captured from the original function that's
+/// passed into the new function/method.
+struct CapturedVariable {
+ const VarDecl *VD;
+ const FieldDecl *FD;
+ QualType ThisType;
+ bool PassByRefOrPtr;
+ bool IsRefOrPtrConst;
+ bool IsThisSelf = false;
+ bool IsThisSuper = false;
+ bool TakeAddress = false;
+ QualType ParameterType;
+
+ CapturedVariable(const VarDecl *VD, bool PassByRefOrPtr, bool IsRefOrPtrConst)
+ : VD(VD), FD(nullptr), PassByRefOrPtr(PassByRefOrPtr),
+ IsRefOrPtrConst(IsRefOrPtrConst) {}
+ CapturedVariable(const FieldDecl *FD, bool PassByRefOrPtr,
+ bool IsRefOrPtrConst)
+ : VD(nullptr), FD(FD), PassByRefOrPtr(PassByRefOrPtr),
+ IsRefOrPtrConst(IsRefOrPtrConst) {}
+ CapturedVariable(QualType ThisType, bool PassByRefOrPtr, bool IsConst)
+ : VD(nullptr), FD(nullptr), ThisType(ThisType),
+ PassByRefOrPtr(PassByRefOrPtr), IsRefOrPtrConst(IsConst) {}
+
+ static CapturedVariable getThis(QualType T, bool IsConst) {
+ return CapturedVariable(T, /*PassByRefOrPtr=*/true, /*IsConst*/ IsConst);
+ }
+
+ static CapturedVariable getSelf(QualType T) {
+ auto Result =
+ CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false);
+ Result.IsThisSelf = true;
+ return Result;
+ }
+
+ static CapturedVariable getSuper(QualType T) {
+ auto Result =
+ CapturedVariable(T, /*PassByRefOrPtr=*/false, /*IsConst*/ false);
+ Result.IsThisSuper = true;
+ return Result;
+ }
+
+ StringRef getName() const {
+ return VD ? VD->getName()
+ : FD ? FD->getName() : IsThisSuper ? "superObject" : "object";
+ }
+ StringRef getExpr() const {
+ return ThisType.isNull()
+ ? getName()
+ : IsThisSelf ? "self" : IsThisSuper ? "super.self" : "*this";
+ }
+ QualType getType() const {
+ return VD ? VD->getType() : FD ? FD->getType() : ThisType;
+ }
+};
+} // end anonymous namespace
+
+static std::pair<SourceLocation, MethodDeclarationPlacement>
+computeAppropriateExtractionLocationForMethodDeclaration(
+ const CXXMethodDecl *D) {
+ const CXXRecordDecl *RD = D->getParent();
+ // Try to put the new declaration after the last method, or just before the
+ // end of the class.
+ SourceLocation Loc;
+ for (const CXXMethodDecl *M : RD->methods()) {
+ if (M->isImplicit())
+ continue;
+ Loc = M->getLocEnd();
+ }
+ return Loc.isValid() ? std::make_pair(Loc, MethodDeclarationPlacement::After)
+ : std::make_pair(RD->getLocEnd(),
+ MethodDeclarationPlacement::Before);
+}
+
+static bool isInHeader(SourceLocation Loc, const SourceManager &SM) {
+ // Base the header decision on the filename.
+ StringRef Extension = llvm::sys::path::extension(SM.getFilename(Loc));
+ if (Extension.empty())
+ return false;
+ return llvm::StringSwitch<bool>(Extension.drop_front())
+ .Case("h", true)
+ .Case("hpp", true)
+ .Case("hh", true)
+ .Case("h++", true)
+ .Case("hxx", true)
+ .Case("inl", true)
+ .Case("def", true)
+ .Default(false);
+}
+
+llvm::Expected<RefactoringResult>
+ExtractOperation::performExpressionExtraction(ASTContext &Context,
+ PrintingPolicy &PP) {
+ assert(isExpressionExtraction() && "Not an expression extraction");
+ std::vector<RefactoringReplacement> Replacements;
+ const Expr *E = cast<Expr>(S);
+ QualType VarType = findExpressionLexicalType(FunctionLikeParentDecl, E,
+ E->getType(), PP, Context);
+ StringRef VarName = "extractedExpr";
+ auto CreatedSymbol =
+ llvm::make_unique<RefactoringResultAssociatedSymbol>(SymbolName(VarName));
+
+ SourceRange ExtractedTokenRange = CandidateExtractionInfo[0].Range;
+ SourceRange ExtractedCharRange = SourceRange(
+ ExtractedTokenRange.getBegin(),
+ getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(),
+ Context.getSourceManager(), Context.getLangOpts()));
+
+ // Create the variable that will hold the value of the duplicate expression.
+ std::string VariableDeclarationString;
+ llvm::raw_string_ostream OS(VariableDeclarationString);
+ VarType.print(OS, PP, /*PlaceHolder*/ VarName);
+ // FIXME: We should hook into the TypePrinter when moving over to llvm.org
+ // instead and get the offset from it.
+ unsigned NameOffset = StringRef(OS.str()).find(VarName);
+ OS << " = ";
+ OS << Lexer::getSourceText(CharSourceRange::getCharRange(ExtractedCharRange),
+ Context.getSourceManager(), Context.getLangOpts());
+ OS << ";\n";
+
+ // Variable declaration.
+ SourceLocation InsertionLoc =
+ extract::locationForExtractedVariableDeclaration(
+ E, FunctionLikeParentDecl, Context.getSourceManager());
+ Replacements.push_back(RefactoringReplacement(
+ SourceRange(InsertionLoc, InsertionLoc), OS.str(), CreatedSymbol.get(),
+ RefactoringReplacement::AssociatedSymbolLocation(
+ llvm::makeArrayRef(NameOffset), /*IsDeclaration=*/true)));
+ // Replace the expression with the variable.
+ Replacements.push_back(
+ RefactoringReplacement(ExtractedCharRange, VarName, CreatedSymbol.get(),
+ /*NameOffset=*/llvm::makeArrayRef(unsigned(0))));
+
+ RefactoringResult Result(std::move(Replacements));
+ Result.AssociatedSymbols.push_back(std::move(CreatedSymbol));
+ return std::move(Result);
+}
+
+llvm::Expected<RefactoringResult> ExtractOperation::perform(
+ ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+ SourceManager &SM = Context.getSourceManager();
+ const LangOptions &LangOpts = Context.getLangOpts();
+ Rewriter SourceRewriter(SM, LangOpts);
+ PrintingPolicy PP = getPrintingPolicy(Context, ThePreprocessor);
+ PP.UseStdFunctionForLambda = true;
+ PP.SuppressStrongLifetime = true;
+ PP.SuppressLifetimeQualifiers = true;
+ PP.SuppressUnwrittenScope = true;
+
+ if (isExpressionExtraction())
+ return performExpressionExtraction(Context, PP);
+
+ const Stmt *S =
+ CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement
+ ? CandidateExtractionInfo[SelectedCandidateIndex].AnalyzedStatement
+ : this->S;
+
+ const auto *EnclosingObjCMethod =
+ dyn_cast<ObjCMethodDecl>(FunctionLikeParentDecl);
+
+ // Find the variables that are captured by the extracted code.
+ ExtractedCodeVisitor Visitor(/*SelfDecl=*/EnclosingObjCMethod
+ ? EnclosingObjCMethod->getSelfDecl()
+ : nullptr);
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context);
+ } else
+ Visitor.InspectExtractedStmt(const_cast<Stmt *>(S), Context);
+ // Compute the return type.
+ bool IsExpr = isLexicalExpression(S, ParentStmt);
+ QualType ReturnType;
+ if (IsExpr || Visitor.HasReturnInExtracted) {
+ if (const auto *E = dyn_cast<Expr>(S)) {
+ assert(!ExtractedStmtRange);
+ ReturnType = findExpressionLexicalType(FunctionLikeParentDecl, E,
+ E->getType(), PP, Context);
+ } else
+ ReturnType = getFunctionLikeParentDeclReturnType(FunctionLikeParentDecl);
+ } else
+ ReturnType = Context.VoidTy;
+ // Sort the captured variables.
+ std::vector<CapturedVariable> CapturedVariables;
+ llvm::SmallPtrSet<const VarDecl *, 4> VariablesDefinedInExtractedCode;
+ CapturedVariables.reserve(Visitor.CapturedVariables.size() +
+ Visitor.CapturedFields.size());
+ for (const auto &I : Visitor.CapturedVariables) {
+ if (I.getSecond().IsDefined) {
+ VariablesDefinedInExtractedCode.insert(I.getFirst());
+ continue;
+ }
+ CapturedVariables.push_back(
+ CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(),
+ I.getSecond().isRefOrPtrConst()));
+ }
+ // Take a look at the variables that are defined in the extracted code.
+ VariableDefinedInExtractedCodeUseAfterExtractionFinder
+ UsedAfterExtractionFinder(ExtractedStmtRange ? *ExtractedStmtRange->Last
+ : S,
+ VariablesDefinedInExtractedCode);
+ UsedAfterExtractionFinder.TraverseStmt(
+ const_cast<Stmt *>(getEnclosingDeclBody(FunctionLikeParentDecl)));
+ struct RedeclaredVariable {
+ const VarDecl *VD;
+ int OrderingPriority;
+ };
+ llvm::SmallVector<RedeclaredVariable, 4> RedeclaredVariables;
+ bool CanUseReturnForVariablesUsedAfterwards =
+ !isa<Expr>(S) && ReturnType->isVoidType() &&
+ UsedAfterExtractionFinder.VariablesUsedAfterExtraction.size() == 1;
+ if (CanUseReturnForVariablesUsedAfterwards) {
+ // Avoid using the return value for the variable that's used afterwards as
+ // another variable might shadow it at the point of a 'return' that we
+ // have to rewrite to 'return var'.
+ const VarDecl *VD =
+ *UsedAfterExtractionFinder.VariablesUsedAfterExtraction.begin();
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange) {
+ if (PossibleShadowingVariableFinder::hasShadowingVar(VD, S)) {
+ CanUseReturnForVariablesUsedAfterwards = false;
+ break;
+ }
+ }
+ } else
+ CanUseReturnForVariablesUsedAfterwards =
+ !PossibleShadowingVariableFinder::hasShadowingVar(VD, S);
+ }
+ if (CanUseReturnForVariablesUsedAfterwards) {
+ for (const auto &I : Visitor.CapturedVariables) {
+ if (!I.getSecond().IsDefined ||
+ !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count(
+ I.getFirst()))
+ continue;
+ RedeclaredVariables.push_back(
+ {I.getFirst(), I.getSecond().DefineOrderingPriority});
+ ReturnType = I.getFirst()->getType();
+ // Const qualifier can be dropped as we don't want to declare the return
+ // type as 'const'.
+ if (ReturnType.isConstQualified())
+ ReturnType.removeLocalConst();
+ break;
+ }
+ if (Visitor.HasReturnInExtracted) {
+ ReturnRewriter ReturnsRewriter(SourceRewriter,
+ RedeclaredVariables.front().VD->getName());
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ ReturnsRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ ReturnsRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ }
+ } else {
+ for (const auto &I : Visitor.CapturedVariables) {
+ if (!I.getSecond().IsDefined ||
+ !UsedAfterExtractionFinder.VariablesUsedAfterExtraction.count(
+ I.getFirst()))
+ continue;
+ RedeclaredVariables.push_back(
+ {I.getFirst(), I.getSecond().DefineOrderingPriority});
+ if (!I.getSecond().IsUsed)
+ continue;
+ // Pass the variable that's defined in the extracted code but used
+ // afterwards as a parameter only when it's actually used in the extracted
+ // code.
+ CapturedVariables.push_back(CapturedVariable(I.getFirst(),
+ /*PassByRefOrPtr=*/true,
+ /*IsRefOrPtrConst=*/false));
+ }
+ std::sort(RedeclaredVariables.begin(), RedeclaredVariables.end(),
+ [](const RedeclaredVariable &X, const RedeclaredVariable &Y) {
+ return X.OrderingPriority < Y.OrderingPriority;
+ });
+ DefinedInExtractedCodeDeclStmtRewriter DeclRewriter(
+ SourceRewriter, UsedAfterExtractionFinder.VariablesUsedAfterExtraction,
+ PP);
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ DeclRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ DeclRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ }
+ // Capture any fields if necessary.
+ bool IsThisConstInCapturedFieldUses = true;
+ if (!isMethodExtraction()) {
+ for (const auto &I : Visitor.CapturedFields) {
+ if (I.getSecond().isPassedByRefOrPtr() &&
+ !I.getSecond().isRefOrPtrConst())
+ IsThisConstInCapturedFieldUses = false;
+ // Private fields that use explicit 'this' should be captured using 'this'
+ // even if they might end up being inaccessible in the extracted function.
+ if (I.getSecond().IsFieldCapturedWithThis)
+ continue;
+ CapturedVariables.push_back(
+ CapturedVariable(I.getFirst(), I.getSecond().isPassedByRefOrPtr(),
+ I.getSecond().isRefOrPtrConst()));
+ }
+ }
+ std::sort(CapturedVariables.begin(), CapturedVariables.end(),
+ [](const CapturedVariable &X, const CapturedVariable &Y) {
+ return X.getName() < Y.getName();
+ });
+ // 'This'/'self' should be passed-in first.
+ if (!isMethodExtraction() && Visitor.CaptureThis) {
+ CapturedVariables.insert(
+ CapturedVariables.begin(),
+ CapturedVariable::getThis(
+ Visitor.ThisRecordType,
+ IsThisConstInCapturedFieldUses &&
+ Visitor.IsThisConstForNonCapturedFieldUses));
+ CapturedThisReferenceRewriter ThisRewriter(SourceRewriter);
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ ThisRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ ThisRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ CapturedThisPointerRewriter PtrThisRewriter(
+ SourceRewriter, ThisRewriter.RewrittenExpressions);
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ PtrThisRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else if (!isMethodExtraction() && Visitor.CaptureSelf &&
+ EnclosingObjCMethod) {
+ if (EnclosingObjCMethod->isInstanceMethod()) {
+ // Instance methods rewrite 'self' into an 'object' parameter.
+ CapturedVariables.insert(CapturedVariables.begin(),
+ CapturedVariable::getSelf(Visitor.SelfType));
+ CapturedSelfRewriter SelfRewriter(SourceRewriter,
+ EnclosingObjCMethod->getSelfDecl());
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ SelfRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ SelfRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else {
+ // Class methods rewrite 'self' into the class name and don't pass 'self'
+ // as a parameter.
+ CapturedClassSelfRewriter SelfRewriter(
+ SourceRewriter, EnclosingObjCMethod->getClassInterface()->getName(),
+ EnclosingObjCMethod->getSelfDecl());
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ SelfRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ SelfRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ }
+ }
+ if (!isMethodExtraction() && Visitor.CaptureSuper && EnclosingObjCMethod) {
+ if (EnclosingObjCMethod->isInstanceMethod())
+ // Instance methods rewrite 'super' into an 'superObject' parameter.
+ CapturedVariables.insert(Visitor.CaptureSelf
+ ? CapturedVariables.begin() + 1
+ : CapturedVariables.begin(),
+ CapturedVariable::getSuper(Visitor.SuperType));
+ CapturedSuperRewriter SuperRewriter(
+ SourceRewriter, EnclosingObjCMethod->isInstanceMethod()
+ ? "superObject"
+ : EnclosingObjCMethod->getClassInterface()
+ ->getSuperClass()
+ ->getName());
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ SuperRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ SuperRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ }
+
+ // Compute the parameter types.
+ for (auto &Var : CapturedVariables) {
+ QualType T = Var.getType();
+
+ // Array types are passed into the extracted function using a pointer.
+ if (const auto *AT = Context.getAsArrayType(T))
+ T = Context.getPointerType(AT->getElementType());
+
+ // Captured records and other mutated variables are passed into the
+ // extracted function either using a reference (C++) or a pointer.
+ if ((T->isRecordType() || Var.PassByRefOrPtr) && !T->isReferenceType()) {
+ // Add a 'const' qualifier to the record when it's not mutated in the
+ // extracted code or when we are taking the address of the captured
+ // variable for just a 'const' use.
+ if (!Var.PassByRefOrPtr || Var.IsRefOrPtrConst)
+ T.addConst();
+
+ if (LangOpts.CPlusPlus)
+ T = Context.getLValueReferenceType(T);
+ else {
+ T = Context.getPointerType(T);
+ CapturedVariableCaptureByPointerRewriter UseRewriter(Var.VD,
+ SourceRewriter);
+ if (ExtractedStmtRange) {
+ for (const Stmt *S : *ExtractedStmtRange)
+ UseRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ } else
+ UseRewriter.TraverseStmt(const_cast<Stmt *>(S));
+ Var.TakeAddress = true;
+ }
+ }
+ // Const qualifier can be dropped as we don't want to declare the parameter
+ // as 'const'.
+ else if (T.isLocalConstQualified())
+ T.removeLocalConst();
+
+ Var.ParameterType = T;
+ }
+
+ // TODO: Choose a better name if there are collisions.
+ StringRef ExtractedName = "extracted";
+ llvm::SmallVector<StringRef, 4> ExtractedNamePieces;
+ ExtractedNamePieces.push_back(ExtractedName);
+ if (isMethodExtraction() && EnclosingObjCMethod &&
+ !CapturedVariables.empty()) {
+ for (const auto &Var : llvm::makeArrayRef(CapturedVariables).drop_front())
+ ExtractedNamePieces.push_back(Var.getName());
+ }
+ std::unique_ptr<RefactoringResultAssociatedSymbol> CreatedSymbol =
+ llvm::make_unique<RefactoringResultAssociatedSymbol>(
+ SymbolName(ExtractedNamePieces));
+
+ SourceLocation FunctionExtractionLoc = computeFunctionExtractionLocation(
+ FunctionLikeParentDecl, isMethodExtraction());
+ FunctionExtractionLoc =
+ getLocationOfPrecedingComment(FunctionExtractionLoc, SM, LangOpts);
+
+ // Create the replacement that contains the new function.
+ auto PrintFunctionHeader =
+ [&](llvm::raw_string_ostream &OS,
+ bool IsDefinition =
+ true) -> RefactoringReplacement::AssociatedSymbolLocation {
+ if (isMethodExtraction() && EnclosingObjCMethod) {
+ OS << (EnclosingObjCMethod->isClassMethod() ? '+' : '-') << " (";
+ ReturnType.print(OS, PP);
+ OS << ')';
+ llvm::SmallVector<unsigned, 4> NameOffsets;
+ NameOffsets.push_back(OS.str().size());
+ OS << ExtractedName;
+ bool IsFirst = true;
+ for (const auto &Var : CapturedVariables) {
+ if (!IsFirst) {
+ OS << ' ';
+ NameOffsets.push_back(OS.str().size());
+ OS << Var.getName();
+ }
+ IsFirst = false;
+ OS << ":(";
+ Var.ParameterType.print(OS, PP);
+ OS << ')' << Var.getName();
+ }
+ return RefactoringReplacement::AssociatedSymbolLocation(
+ NameOffsets, /*IsDeclaration=*/true);
+ }
+ auto *FD = dyn_cast<FunctionDecl>(FunctionLikeParentDecl);
+ if (isMethodExtraction() && IsDefinition &&
+ !FD->getDescribedFunctionTemplate()) {
+ // Print the class template parameter lists for an out-of-line method.
+ for (unsigned I = 0,
+ NumTemplateParams = FD->getNumTemplateParameterLists();
+ I < NumTemplateParams; ++I) {
+ FD->getTemplateParameterList(I)->print(OS, PP, Context);
+ OS << "\n";
+ }
+ }
+ if (isMethodExtraction() && isEnclosingMethodStatic(FunctionLikeParentDecl))
+ OS << "static ";
+ else if (!isMethodExtraction())
+ OS << (isInHeader(FunctionExtractionLoc, SM) ? "inline " : "static ");
+ ReturnType.print(OS, PP);
+ OS << ' ';
+ if (isMethodExtraction() && IsDefinition)
+ printEnclosingMethodScope(FunctionLikeParentDecl, OS, PP);
+ unsigned NameOffset = OS.str().size();
+ OS << ExtractedName << '(';
+ bool IsFirst = true;
+ for (const auto &Var : CapturedVariables) {
+ if (!IsFirst)
+ OS << ", ";
+ IsFirst = false;
+ Var.ParameterType.print(OS, PP, /*PlaceHolder=*/Var.getName());
+ }
+ OS << ')';
+ if (isMethodExtraction() && isEnclosingMethodConst(FunctionLikeParentDecl))
+ OS << " const";
+ return RefactoringReplacement::AssociatedSymbolLocation(
+ NameOffset, /*IsDeclaration=*/true);
+ ;
+ };
+
+ if (isMethodExtraction() &&
+ isEnclosingMethodOutOfLine(FunctionLikeParentDecl)) {
+ // The location of the declaration should be either before the original
+ // declararation, or, if this method has not declaration, somewhere
+ // appropriate in the class.
+ MethodDeclarationPlacement Placement;
+ SourceLocation DeclarationLoc;
+ if (FunctionLikeParentDecl->getCanonicalDecl() != FunctionLikeParentDecl) {
+ DeclarationLoc = computeFunctionExtractionLocation(
+ FunctionLikeParentDecl->getCanonicalDecl(), isMethodExtraction());
+ Placement = MethodDeclarationPlacement::Before;
+ } else {
+ auto LocAndPlacement =
+ computeAppropriateExtractionLocationForMethodDeclaration(
+ cast<CXXMethodDecl>(FunctionLikeParentDecl));
+ DeclarationLoc = LocAndPlacement.first;
+ Placement = LocAndPlacement.second;
+ }
+ if (Placement == MethodDeclarationPlacement::Before)
+ DeclarationLoc =
+ getLocationOfPrecedingComment(DeclarationLoc, SM, LangOpts);
+ else
+ DeclarationLoc = getLastLineLocationUnlessItHasOtherTokens(
+ getPreciseTokenLocEnd(DeclarationLoc, SM, LangOpts), SM, LangOpts);
+ // Add a replacement for the method declaration if necessary.
+ std::string DeclarationString;
+ llvm::raw_string_ostream OS(DeclarationString);
+ if (Placement == MethodDeclarationPlacement::After)
+ OS << "\n\n";
+ RefactoringReplacement::AssociatedSymbolLocation SymbolLoc =
+ PrintFunctionHeader(OS, /*IsDefinition=*/false);
+ OS << ";\n";
+ if (Placement == MethodDeclarationPlacement::Before)
+ OS << "\n";
+ Replacements.push_back(RefactoringReplacement(
+ SourceRange(DeclarationLoc, DeclarationLoc), std::move(OS.str()),
+ CreatedSymbol.get(), SymbolLoc));
+ }
+ std::string ExtractedCode;
+ llvm::raw_string_ostream ExtractedOS(ExtractedCode);
+ RefactoringReplacement::AssociatedSymbolLocation SymbolLoc =
+ PrintFunctionHeader(ExtractedOS);
+ ExtractedOS << " {\n";
+ if (IsExpr && !ReturnType->isVoidType())
+ ExtractedOS << "return ";
+ SourceRange ExtractedTokenRange =
+ CandidateExtractionInfo[SelectedCandidateIndex].Range;
+ auto Semicolons = computeSemicolonExtractionPolicy(
+ ExtractedStmtRange ? *(ExtractedStmtRange->Last) : S, ExtractedTokenRange,
+ SM, LangOpts);
+ ExtractedOS << SourceRewriter.getRewrittenText(ExtractedTokenRange);
+ if (Semicolons.IsNeededInExtractedFunction)
+ ExtractedOS << ';';
+ if (CanUseReturnForVariablesUsedAfterwards)
+ ExtractedOS << "\nreturn " << RedeclaredVariables.front().VD->getName()
+ << ";";
+ ExtractedOS << "\n}\n\n";
+ Replacements.push_back(RefactoringReplacement(
+ SourceRange(FunctionExtractionLoc, FunctionExtractionLoc),
+ std::move(ExtractedOS.str()), CreatedSymbol.get(), SymbolLoc));
+
+ // Create a replacements that removes the extracted code in favor of the
+ // function call.
+ std::string InsertedCode;
+ llvm::raw_string_ostream InsertedOS(InsertedCode);
+ // We might have to declare variables that were declared in the extracted code
+ // but still used afterwards.
+ if (CanUseReturnForVariablesUsedAfterwards) {
+ const auto &Var = RedeclaredVariables.front();
+ Var.VD->getType().print(InsertedOS, PP);
+ InsertedOS << ' ' << Var.VD->getName() << " = ";
+ } else {
+ for (const auto &Var : RedeclaredVariables) {
+ Var.VD->getType().print(InsertedOS, PP);
+ InsertedOS << ' ' << Var.VD->getName() << ";\n";
+ }
+ }
+ InsertedOS << CandidateExtractionInfo[SelectedCandidateIndex].PreInsertedText;
+ llvm::SmallVector<unsigned, 4> NameOffsets;
+ if (isMethodExtraction() && EnclosingObjCMethod) {
+ InsertedOS << "[self ";
+ NameOffsets.push_back(InsertedOS.str().size());
+ InsertedOS << ExtractedName;
+ bool IsFirst = true;
+ for (const auto &Var : CapturedVariables) {
+ if (!IsFirst) {
+ InsertedOS << ' ';
+ NameOffsets.push_back(InsertedOS.str().size());
+ InsertedOS << Var.getName();
+ }
+ IsFirst = false;
+ InsertedOS << ':';
+ if (Var.TakeAddress)
+ InsertedOS << '&';
+ InsertedOS << Var.getExpr();
+ }
+ InsertedOS << ']';
+ } else {
+ NameOffsets.push_back(InsertedOS.str().size());
+ InsertedOS << ExtractedName << '(';
+ bool IsFirst = true;
+ for (const auto &Var : CapturedVariables) {
+ if (!IsFirst)
+ InsertedOS << ", ";
+ IsFirst = false;
+ if (Var.TakeAddress)
+ InsertedOS << '&';
+ InsertedOS << Var.getExpr();
+ }
+ InsertedOS << ')';
+ }
+ if (Semicolons.IsNeededInOriginalFunction)
+ InsertedOS << ';';
+ SourceRange ExtractedCharRange = SourceRange(
+ ExtractedTokenRange.getBegin(),
+ getPreciseTokenLocEnd(ExtractedTokenRange.getEnd(), SM, LangOpts));
+ Replacements.push_back(RefactoringReplacement(
+ ExtractedCharRange, std::move(InsertedOS.str()), CreatedSymbol.get(),
+ llvm::makeArrayRef(NameOffsets)));
+
+ RefactoringResult Result(std::move(Replacements));
+ Result.AssociatedSymbols.push_back(std::move(CreatedSymbol));
+ return std::move(Result);
+}
diff --git a/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp b/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp
new file mode 100644
index 0000000..dc90d9e
--- /dev/null
+++ b/lib/Tooling/Refactor/ExtractRepeatedExpressionIntoVariable.cpp
@@ -0,0 +1,282 @@
+//===--- ExtractRepeatedExpressionIntoVariable.cpp - ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Extract repeated expression into variable" refactoring
+// operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExtractionUtils.h"
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+class ExtractRepeatedExpressionIntoVariableOperation
+ : public RefactoringOperation {
+public:
+ ExtractRepeatedExpressionIntoVariableOperation(
+ const Expr *E, ArrayRef<const Expr *> Duplicates, const Decl *ParentDecl)
+ : E(E), DuplicateExpressions(Duplicates.begin(), Duplicates.end()),
+ ParentDecl(ParentDecl) {}
+
+ const Stmt *getTransformedStmt() const override { return E; }
+
+ llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ const Expr *E;
+ SmallVector<const Expr *, 4> DuplicateExpressions;
+ const Decl *ParentDecl;
+};
+
+using UseOfDeclaration = std::pair<const Decl *, unsigned>;
+
+bool shouldIgnoreParens(const ParenExpr *E) {
+ if (!E)
+ return false;
+ const Expr *Child = E->getSubExpr();
+ // Ignore the parens unless they are around an expression that
+ // really needs them.
+ if (isa<UnaryOperator>(Child) || isa<BinaryOperator>(Child) ||
+ isa<AbstractConditionalOperator>(Child) ||
+ isa<CXXOperatorCallExpr>(Child))
+ return false;
+ return true;
+}
+
+/// Builds up a list of declarations that are used in an expression.
+class DuplicateExprSemanticProfiler
+ : public RecursiveASTVisitor<DuplicateExprSemanticProfiler> {
+ unsigned Index = 0;
+ llvm::SmallVectorImpl<UseOfDeclaration> &DeclRefs;
+
+public:
+ DuplicateExprSemanticProfiler(
+ llvm::SmallVectorImpl<UseOfDeclaration> &DeclRefs)
+ : DeclRefs(DeclRefs) {
+ DeclRefs.clear();
+ }
+
+ bool VisitStmt(const Stmt *S) {
+ if (!shouldIgnoreParens(dyn_cast<ParenExpr>(S)))
+ ++Index;
+ return true;
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *E) {
+ if (E->getDecl())
+ DeclRefs.emplace_back(E->getDecl(), Index);
+ return true;
+ }
+};
+
+class DuplicateExprFinder : public RecursiveASTVisitor<DuplicateExprFinder>,
+ PrinterHelper {
+ const Expr *Target;
+ const ASTContext &Context;
+ const PrintingPolicy &PP;
+ Stmt::StmtClass ExprKind;
+ QualType T;
+ std::string ExprString, OSString;
+ llvm::SmallVector<UseOfDeclaration, 8> ExprDecls, DeclUses;
+
+ void printExpr(std::string &Str, const Expr *E) {
+ llvm::raw_string_ostream OS(Str);
+ E->printPretty(OS, /*Helper=*/this, PP);
+ }
+
+public:
+ SmallVector<const Expr *, 4> DuplicateExpressions;
+
+ DuplicateExprFinder(const Expr *E, const ASTContext &Context,
+ const PrintingPolicy &PP)
+ : Target(E), Context(Context), PP(PP), ExprKind(E->getStmtClass()),
+ T(E->getType()) {
+ printExpr(ExprString, E);
+ DuplicateExprSemanticProfiler(ExprDecls).TraverseStmt(
+ const_cast<Expr *>(E));
+ }
+
+ bool handledStmt(Stmt *E, raw_ostream &OS) final override {
+ if (const auto *Paren = dyn_cast<ParenExpr>(E)) {
+ if (!shouldIgnoreParens(Paren))
+ return false;
+ Paren->getSubExpr()->printPretty(OS, /*Helper=*/this, PP);
+ return true;
+ }
+ return false;
+ }
+
+ bool VisitStmt(const Stmt *S) {
+ if (S->getStmtClass() != ExprKind)
+ return true;
+ const auto *E = cast<Expr>(S);
+ if (E == Target) {
+ DuplicateExpressions.push_back(E);
+ return true;
+ }
+ // The expression should not be in a macro.
+ SourceRange R = E->getSourceRange();
+ if (R.getBegin().isMacroID()) {
+ if (!Context.getSourceManager().isMacroArgExpansion(R.getBegin()))
+ return true;
+ }
+ if (R.getEnd().isMacroID()) {
+ if (!Context.getSourceManager().isMacroArgExpansion(R.getEnd()))
+ return true;
+ }
+ // The expression types should match.
+ if (E->getType() != T)
+ return true;
+ // Check if the expression is a duplicate by comparing their lexical
+ // representations.
+ OSString.clear();
+ printExpr(OSString, E);
+ if (OSString == ExprString) {
+ DuplicateExprSemanticProfiler(DeclUses).TraverseStmt(
+ const_cast<Expr *>(E));
+ // Check if they're semantically equivalent.
+ if (ExprDecls.size() == DeclUses.size() &&
+ std::equal(ExprDecls.begin(), ExprDecls.end(), DeclUses.begin()))
+ DuplicateExpressions.push_back(E);
+ }
+ return true;
+ }
+};
+
+} // end anonymous namespace
+
+static QualType returnTypeOfCall(const Expr *E) {
+ if (const auto *Call = dyn_cast<CallExpr>(E)) {
+ if (const auto *Fn = Call->getDirectCallee())
+ return Fn->getReturnType();
+ } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) {
+ if (const auto *M = Msg->getMethodDecl())
+ return M->getReturnType();
+ } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) {
+ if (PRE->isImplicitProperty()) {
+ if (const auto *M = PRE->getImplicitPropertyGetter())
+ return M->getReturnType();
+ } else if (const auto *Prop = PRE->getExplicitProperty())
+ return Prop->getType();
+ }
+ return QualType();
+}
+
+static bool isRepeatableExpression(const Stmt *S) {
+ if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(S))
+ return Op->getOperator() == OO_Call || Op->getOperator() == OO_Subscript;
+ return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) ||
+ isa<ObjCPropertyRefExpr>(S);
+}
+
+RefactoringOperationResult
+clang::tooling::initiateExtractRepeatedExpressionIntoVariableOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ const Stmt *S;
+ const Decl *ParentDecl;
+ if (SelectionRange.isValid()) {
+ auto SelectedStmt = Slice.getSelectedStmtSet();
+ if (!SelectedStmt)
+ return None;
+ if (!SelectedStmt->containsSelectionRange)
+ return None;
+ if (!isRepeatableExpression(SelectedStmt->containsSelectionRange))
+ return None;
+ S = SelectedStmt->containsSelectionRange;
+ ParentDecl =
+ Slice.parentDeclForIndex(*SelectedStmt->containsSelectionRangeIndex);
+ } else {
+ auto SelectedStmt = Slice.nearestSelectedStmt(isRepeatableExpression);
+ if (!SelectedStmt)
+ return None;
+ S = SelectedStmt->getStmt();
+ ParentDecl = SelectedStmt->getParentDecl();
+ }
+
+ const Expr *E = cast<Expr>(S);
+ // Check if the function/method returns a reference/pointer.
+ QualType T = returnTypeOfCall(E);
+ if (!T.getTypePtrOrNull() ||
+ (!T->isAnyPointerType() && !T->isReferenceType()))
+ return None;
+
+ DuplicateExprFinder DupFinder(E, Context, Context.getPrintingPolicy());
+ DupFinder.TraverseDecl(const_cast<Decl *>(ParentDecl));
+ if (DupFinder.DuplicateExpressions.size() < 2)
+ return None;
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+ auto Operation =
+ llvm::make_unique<ExtractRepeatedExpressionIntoVariableOperation>(
+ E, DupFinder.DuplicateExpressions, ParentDecl);
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+}
+
+static StringRef nameForExtractedVariable(const Expr *E) {
+ auto SuggestedName = extract::nameForExtractedVariable(E);
+ if (!SuggestedName)
+ return "duplicate";
+ return *SuggestedName;
+}
+
+llvm::Expected<RefactoringResult>
+ExtractRepeatedExpressionIntoVariableOperation::perform(
+ ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+
+ const SourceManager &SM = Context.getSourceManager();
+ SourceLocation InsertionLoc =
+ extract::locationForExtractedVariableDeclaration(DuplicateExpressions,
+ ParentDecl, SM);
+ if (InsertionLoc.isInvalid())
+ return llvm::make_error<RefactoringOperationError>(
+ "no appropriate insertion location found");
+
+ StringRef Name = nameForExtractedVariable(E);
+
+ // Create the variable that will hold the value of the duplicate expression.
+ std::string VariableDeclarationString;
+ llvm::raw_string_ostream OS(VariableDeclarationString);
+ QualType T = returnTypeOfCall(E);
+ PrintingPolicy PP = Context.getPrintingPolicy();
+ PP.SuppressStrongLifetime = true;
+ PP.SuppressLifetimeQualifiers = true;
+ PP.SuppressUnwrittenScope = true;
+ T.print(OS, PP, /*PlaceHolder*/ Name);
+ OS << " = ";
+ E->printPretty(OS, /*Helper=*/nullptr, Context.getPrintingPolicy());
+ OS << ";\n";
+ Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc), OS.str());
+
+ // Replace the duplicates with a reference to the variable.
+ for (const Expr *E : DuplicateExpressions)
+ Replacements.emplace_back(
+ SourceRange(SM.getSpellingLoc(E->getLocStart()),
+ getPreciseTokenLocEnd(SM.getSpellingLoc(E->getLocEnd()), SM,
+ Context.getLangOpts())),
+ Name);
+
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/ExtractionUtils.cpp b/lib/Tooling/Refactor/ExtractionUtils.cpp
new file mode 100644
index 0000000..a733add
--- /dev/null
+++ b/lib/Tooling/Refactor/ExtractionUtils.cpp
@@ -0,0 +1,137 @@
+//===--- ExtractionUtils.cpp - Extraction helper functions ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExtractionUtils.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/SaveAndRestore.h"
+
+using namespace clang;
+
+Optional<StringRef> tooling::extract::nameForExtractedVariable(const Expr *E) {
+ if (const auto *Call = dyn_cast<CallExpr>(E)) {
+ if (const auto *Fn = Call->getDirectCallee())
+ return Fn->getName();
+ } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) {
+ if (const auto *M = Msg->getMethodDecl()) {
+ if (M->getSelector().isUnarySelector())
+ return M->getSelector().getNameForSlot(0);
+ }
+ } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) {
+ if (PRE->isImplicitProperty()) {
+ if (const auto *M = PRE->getImplicitPropertyGetter())
+ return M->getSelector().getNameForSlot(0);
+ } else if (const auto *Prop = PRE->getExplicitProperty())
+ return Prop->getName();
+ }
+ return None;
+}
+
+namespace {
+
+/// Checks if a set of expressions is directly contained in some AST region.
+class StmtReachabilityChecker
+ : public RecursiveASTVisitor<StmtReachabilityChecker> {
+ const llvm::SmallPtrSetImpl<const Stmt *> &Expressions;
+ unsigned Count = 0;
+
+ StmtReachabilityChecker(
+ const llvm::SmallPtrSetImpl<const Stmt *> &Expressions)
+ : Expressions(Expressions) {}
+
+ bool areAllExpressionsReached() const { return Count == Expressions.size(); }
+
+public:
+ bool VisitStmt(const Stmt *S) {
+ if (Expressions.count(S)) {
+ ++Count;
+ if (areAllExpressionsReached())
+ return false;
+ }
+ return true;
+ }
+
+ static bool areAllExpressionsReachableFrom(
+ CompoundStmt *S, const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) {
+ StmtReachabilityChecker Checker(Expressions);
+ Checker.TraverseStmt(S);
+ return Checker.areAllExpressionsReached();
+ }
+};
+
+/// Figures out where the extracted variable should go.
+class ExtractedVariableInsertionLocFinder
+ : public RecursiveASTVisitor<ExtractedVariableInsertionLocFinder> {
+ llvm::SmallPtrSet<const Stmt *, 4> Expressions;
+ llvm::SmallVector<std::pair<CompoundStmt *, const Stmt *>, 4>
+ InsertionCandidateStack;
+ bool IsPrevCompoundStmt = false;
+
+public:
+ SourceLocation Loc;
+
+ /// Initializes the insertion location finder using the set of duplicate
+ /// \p Expressions from one function.
+ ExtractedVariableInsertionLocFinder(ArrayRef<const Expr *> Expressions) {
+ for (const Expr *E : Expressions)
+ this->Expressions.insert(E);
+ }
+
+ bool TraverseStmt(Stmt *S) {
+ if (!S)
+ return RecursiveASTVisitor::TraverseStmt(S);
+ if (IsPrevCompoundStmt && !InsertionCandidateStack.empty())
+ InsertionCandidateStack.back().second = S;
+ llvm::SaveAndRestore<bool> IsPrevCompoundStmtTracker(IsPrevCompoundStmt,
+ false);
+ if (auto *CS = dyn_cast<CompoundStmt>(S)) {
+ IsPrevCompoundStmt = true;
+ InsertionCandidateStack.emplace_back(CS, nullptr);
+ RecursiveASTVisitor::TraverseStmt(S);
+ InsertionCandidateStack.pop_back();
+ return true;
+ }
+ return RecursiveASTVisitor::TraverseStmt(S);
+ }
+
+ bool VisitStmt(const Stmt *S) {
+ if (Expressions.count(S)) {
+ // The insertion location should be in the first compound statement that
+ // includes all of the expressions as descendants as we want the new
+ // variable to be visible to all uses.
+ for (auto I = InsertionCandidateStack.rbegin(),
+ E = InsertionCandidateStack.rend();
+ I != E; ++I) {
+ if (StmtReachabilityChecker::areAllExpressionsReachableFrom(
+ I->first, Expressions) &&
+ I->second) {
+ Loc = I->second->getLocStart();
+ break;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+};
+
+} // end anonymous namespace
+
+SourceLocation tooling::extract::locationForExtractedVariableDeclaration(
+ ArrayRef<const Expr *> Expressions, const Decl *ParentDecl,
+ const SourceManager &SM) {
+ ExtractedVariableInsertionLocFinder LocFinder(Expressions);
+ LocFinder.TraverseDecl(const_cast<Decl *>(ParentDecl));
+ SourceLocation Result = LocFinder.Loc;
+ if (Result.isValid() && Result.isMacroID())
+ return SM.getExpansionLoc(Result);
+ return Result;
+}
diff --git a/lib/Tooling/Refactor/ExtractionUtils.h b/lib/Tooling/Refactor/ExtractionUtils.h
new file mode 100644
index 0000000..816a081
--- /dev/null
+++ b/lib/Tooling/Refactor/ExtractionUtils.h
@@ -0,0 +1,40 @@
+//===--- ExtractionUtils.h - Extraction helper functions ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H
+
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+
+class Expr;
+class Decl;
+class SourceManager;
+
+namespace tooling {
+namespace extract {
+
+/// Returns a good name for an extracted variable based on the declaration
+/// that's used in the given expression \p E.
+Optional<StringRef> nameForExtractedVariable(const Expr *E);
+
+/// Returns an appropriate location for a variable declaration that will be
+/// visible to all the given expressions.
+SourceLocation
+locationForExtractedVariableDeclaration(ArrayRef<const Expr *> Expressions,
+ const Decl *ParentDecl,
+ const SourceManager &SM);
+
+} // end namespace extract
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_EXTRACTION_UTILS_H
diff --git a/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp b/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp
new file mode 100644
index 0000000..2cab0ee
--- /dev/null
+++ b/lib/Tooling/Refactor/FillInEnumSwitchCases.cpp
@@ -0,0 +1,110 @@
+//===--- FillInEnumSwitchCases.cpp - -------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Add missing switch cases" refactoring operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringOperations.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Edit/RefactoringFixits.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+class FillInEnumSwitchCasesOperation : public RefactoringOperation {
+public:
+ FillInEnumSwitchCasesOperation(const EnumDecl *Enum, const SwitchStmt *Switch,
+ const DeclContext *SwitchContext)
+ : Enum(Enum), Switch(Switch), SwitchContext(SwitchContext) {}
+
+ const Stmt *getTransformedStmt() const override { return Switch; }
+
+ llvm::Expected<RefactoringResult>
+ perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ const EnumDecl *Enum;
+ const SwitchStmt *Switch;
+ const DeclContext *SwitchContext;
+};
+
+} // end anonymous namespace
+
+RefactoringOperationResult
+clang::tooling::initiateFillInEnumSwitchCasesOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ const SwitchStmt *Switch;
+ const Decl *ParentDecl;
+ if (SelectionRange.isValid()) {
+ auto SelectedSet = Slice.getSelectedStmtSet();
+ if (!SelectedSet)
+ return None;
+ Switch = dyn_cast_or_null<SwitchStmt>(SelectedSet->containsSelectionRange);
+ // FIXME: Improve the interface for this to make it similar to SelectedStmt
+ if (SelectedSet->containsSelectionRange)
+ ParentDecl =
+ Slice.parentDeclForIndex(*SelectedSet->containsSelectionRangeIndex);
+ } else {
+ auto SelectedStmt = Slice.nearestSelectedStmt(Stmt::SwitchStmtClass);
+ if (!SelectedStmt)
+ return None;
+ Switch = cast<SwitchStmt>(SelectedStmt->getStmt());
+ ParentDecl = SelectedStmt->getParentDecl();
+ }
+ if (!Switch)
+ return None;
+
+ // Ensure that the type is an enum.
+ const Expr *Cond = Switch->getCond()->IgnoreImpCasts();
+ const EnumDecl *ED = nullptr;
+ if (const auto *ET = Cond->getType()->getAs<EnumType>())
+ ED = ET->getDecl();
+ else {
+ // Enum literals are 'int' in C.
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Cond)) {
+ if (const auto *EC = dyn_cast<EnumConstantDecl>(DRE->getDecl()))
+ ED = dyn_cast<EnumDecl>(EC->getDeclContext());
+ }
+ }
+
+ if (!ED)
+ return RefactoringOperationResult("The switch doesn't operate on an enum");
+ if (!ED->isCompleteDefinition())
+ return RefactoringOperationResult("The enum type is incomplete");
+
+ if (Switch->isAllEnumCasesCovered())
+ return RefactoringOperationResult("All enum cases are already covered");
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+ auto Operation = llvm::make_unique<FillInEnumSwitchCasesOperation>(
+ ED, Switch, dyn_cast<DeclContext>(ParentDecl));
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+}
+
+llvm::Expected<RefactoringResult>
+FillInEnumSwitchCasesOperation::perform(ASTContext &Context,
+ const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+ edit::fillInMissingSwitchEnumCases(
+ Context, Switch, Enum, SwitchContext,
+ [&](const FixItHint &Hint) { Replacements.push_back(Hint); });
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp b/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp
new file mode 100644
index 0000000..ba730f6
--- /dev/null
+++ b/lib/Tooling/Refactor/FillInMissingMethodStubsFromAbstractClasses.cpp
@@ -0,0 +1,293 @@
+//===--- FillInMissingMethodStubsFromAbstractClasses.cpp - ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Add missing abstract class method overrides" refactoring
+// operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "llvm/ADT/DenseSet.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+class FillInMissingMethodStubsFromAbstractClassesOperation
+ : public RefactoringOperation {
+public:
+ FillInMissingMethodStubsFromAbstractClassesOperation(
+ const CXXRecordDecl *Class)
+ : Class(Class) {}
+
+ const Decl *getTransformedDecl() const override { return Class; }
+
+ llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ const CXXRecordDecl *Class;
+};
+
+} // end anonymous namespace
+
+static bool hasAbstractBases(const CXXRecordDecl *Class) {
+ for (const CXXBaseSpecifier &Base : Class->bases()) {
+ if (const auto *RD = Base.getType()->getAsCXXRecordDecl()) {
+ if (RD->isAbstract())
+ return true;
+ }
+ }
+ return false;
+}
+
+RefactoringOperationResult
+clang::tooling::initiateFillInMissingMethodStubsFromAbstractClassesOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ auto SelectedDecl = Slice.innermostSelectedDecl(
+ llvm::makeArrayRef(Decl::CXXRecord), ASTSlice::InnermostDeclOnly);
+ if (!SelectedDecl)
+ return None;
+ const auto *Class = cast<CXXRecordDecl>(SelectedDecl->getDecl());
+ if (Class->isUnion() || !Class->isThisDeclarationADefinition())
+ return None;
+ if (!hasAbstractBases(Class))
+ return RefactoringOperationResult("The class has no abstract bases");
+ if (!Class->isDependentType() && !Class->isAbstract())
+ return RefactoringOperationResult(
+ "The class has no missing abstract class methods");
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+ auto Operation =
+ llvm::make_unique<FillInMissingMethodStubsFromAbstractClassesOperation>(
+ Class);
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+}
+
+namespace {
+
+class PureMethodSet {
+ llvm::DenseMap<const CXXMethodDecl *, int> Methods;
+
+ void addPureMethodsFromAbstractClasses(const CXXRecordDecl *Class,
+ int &Priority) {
+ for (const CXXBaseSpecifier &Base : Class->bases()) {
+ const auto *RD = Base.getType()->getAsCXXRecordDecl();
+ if (!RD || !RD->isAbstract())
+ continue;
+ for (const CXXMethodDecl *M : RD->methods()) {
+ if (M->isPure())
+ Methods.insert(std::make_pair(M->getCanonicalDecl(), Priority++));
+ }
+ addPureMethodsFromAbstractClasses(RD, Priority);
+ }
+ }
+
+ void addPureMethodsFromAbstractClasses(const CXXRecordDecl *Class) {
+ int Priority = 0;
+ addPureMethodsFromAbstractClasses(Class, Priority);
+ }
+
+ void subtractImplementedPureMethods(const CXXRecordDecl *Class) {
+ for (const CXXMethodDecl *M : Class->methods()) {
+ if (!M->isVirtual() || M->isPure())
+ continue;
+ for (const CXXMethodDecl *OM : M->overridden_methods()) {
+ OM = OM->getCanonicalDecl();
+ if (OM->isPure())
+ Methods.erase(OM);
+ }
+ }
+ for (const CXXBaseSpecifier &Base : Class->bases()) {
+ const auto *RD = Base.getType()->getAsCXXRecordDecl();
+ if (!RD || !RD->isAbstract())
+ continue;
+ subtractImplementedPureMethods(RD);
+ }
+ }
+
+public:
+ static std::vector<const CXXMethodDecl *>
+ gatherMissingMethods(const CXXRecordDecl *Class) {
+ PureMethodSet MethodSet;
+ MethodSet.addPureMethodsFromAbstractClasses(Class);
+ MethodSet.subtractImplementedPureMethods(Class);
+ // Sort the missing methods. That will place methods from the same abstract
+ // class together in the order in which they were declared.
+ struct MethodInfo {
+ const CXXMethodDecl *M;
+ int Priority;
+ };
+ std::vector<MethodInfo> MissingMethods;
+ for (const auto &M : MethodSet.Methods)
+ MissingMethods.push_back({M.first, M.second});
+ std::sort(MissingMethods.begin(), MissingMethods.end(),
+ [](const MethodInfo &LHS, const MethodInfo &RHS) {
+ return LHS.Priority < RHS.Priority;
+ });
+ std::vector<const CXXMethodDecl *> Result;
+ Result.reserve(MissingMethods.size());
+ for (const auto &M : MissingMethods)
+ Result.push_back(M.M);
+ return Result;
+ }
+};
+
+} // end anonymous namespace
+
+static SourceLocation findInsertionLocationForMethodsFromAbstractClass(
+ const CXXRecordDecl *AbstractClass, const CXXRecordDecl *Class,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ SourceLocation Loc;
+ for (const CXXMethodDecl *M : Class->methods()) {
+ if (!M->isVirtual() || M->isPure() || M->isImplicit())
+ continue;
+ for (const CXXMethodDecl *OM : M->overridden_methods()) {
+ OM = OM->getCanonicalDecl();
+ if (OM->getLexicalDeclContext() == AbstractClass) {
+ SourceLocation EndLoc = M->getLocEnd();
+ if (EndLoc.isMacroID())
+ EndLoc = SM.getExpansionRange(EndLoc).second;
+ if (Loc.isInvalid())
+ Loc = EndLoc;
+ else if (SM.isBeforeInTranslationUnit(Loc, EndLoc))
+ Loc = EndLoc;
+ break;
+ }
+ }
+ }
+ if (Loc.isInvalid())
+ return Loc;
+ return getLastLineLocationUnlessItHasOtherTokens(Loc, SM, LangOpts);
+}
+
+/// Returns true if the given \p Class implements the majority of declared
+/// methods in the class itself.
+static bool shouldImplementMethodsInClass(const CXXRecordDecl *Class) {
+ // Check if this class implements the methods in the class itself.
+ unsigned NumMethods = 0, NumImplementedMethods = 0;
+ for (const CXXMethodDecl *M : Class->methods()) {
+ if (M->isImplicit())
+ continue;
+ // Only look at methods/operators.
+ if (isa<CXXConstructorDecl>(M) || isa<CXXDestructorDecl>(M))
+ continue;
+ ++NumMethods;
+ if (M->hasBody())
+ ++NumImplementedMethods;
+ }
+ if (!NumMethods)
+ return false;
+ // Use the following arbitrary heuristic:
+ // If the number of method declarations is less than 4, then all of the
+ // methods must have bodies. Otherwise, at least 75% of the methods must
+ // have bodies.
+ return NumMethods < 4
+ ? NumMethods == NumImplementedMethods
+ : float(NumImplementedMethods) / float(NumMethods) > 0.75;
+}
+
+llvm::Expected<RefactoringResult>
+FillInMissingMethodStubsFromAbstractClassesOperation::perform(
+ ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+
+ std::vector<const CXXMethodDecl *> MissingMethods =
+ PureMethodSet::gatherMissingMethods(Class);
+
+ bool GenerateBodyDummies = shouldImplementMethodsInClass(Class);
+
+ PrintingPolicy PP = Context.getPrintingPolicy();
+ PP.PolishForDeclaration = true;
+ PP.SuppressStrongLifetime = true;
+ PP.SuppressLifetimeQualifiers = true;
+ PP.SuppressUnwrittenScope = true;
+
+ std::string EndInsertionOSStr;
+ llvm::raw_string_ostream EndInsertionOS(EndInsertionOSStr);
+
+ std::string InsertionGroupOSStr;
+ llvm::raw_string_ostream InsertionGroupOS(InsertionGroupOSStr);
+
+ SourceLocation InsertionLoc = Class->getLocEnd();
+ const CXXRecordDecl *CurrentAbstractClass = nullptr;
+ SourceLocation CurrentGroupInsertionLoc;
+ for (const auto &I : llvm::enumerate(MissingMethods)) {
+ const CXXMethodDecl *Method = I.value();
+ const CXXRecordDecl *AbstractClass = Method->getParent();
+ if (CurrentAbstractClass != AbstractClass) {
+ if (!InsertionGroupOS.str().empty()) {
+ assert(CurrentGroupInsertionLoc.isValid());
+ Replacements.emplace_back(
+ SourceRange(CurrentGroupInsertionLoc, CurrentGroupInsertionLoc),
+ InsertionGroupOS.str());
+ }
+ InsertionGroupOSStr.clear();
+ CurrentAbstractClass = AbstractClass;
+ CurrentGroupInsertionLoc =
+ findInsertionLocationForMethodsFromAbstractClass(
+ CurrentAbstractClass, Class, Context.getSourceManager(),
+ Context.getLangOpts());
+ }
+ bool IsInsertingAfterRelatedMethods = CurrentGroupInsertionLoc.isValid();
+ raw_ostream &OS =
+ IsInsertingAfterRelatedMethods ? InsertionGroupOS : EndInsertionOS;
+
+ if (IsInsertingAfterRelatedMethods && InsertionGroupOS.str().empty())
+ OS << "\n\n";
+ // Print the method without the 'virtual' specifier and the pure '= 0'
+ // annotation.
+ auto *MD = const_cast<CXXMethodDecl *>(Method);
+ bool IsVirtual = MD->isVirtualAsWritten();
+ MD->setVirtualAsWritten(false);
+ bool IsPure = MD->isPure();
+ MD->setPure(false);
+ MD->print(OS, PP);
+ MD->setVirtualAsWritten(IsVirtual);
+ MD->setPure(IsPure);
+
+ OS << " override";
+ if (GenerateBodyDummies)
+ OS << " { \n <#code#>\n}\n";
+ else
+ OS << ";\n";
+ // Avoid an additional newline for the last method in an insertion group.
+ if (IsInsertingAfterRelatedMethods) {
+ const CXXRecordDecl *NextAbstractClass =
+ (I.index() + 1) != MissingMethods.size()
+ ? MissingMethods[I.index() + 1]->getParent()
+ : nullptr;
+ if (NextAbstractClass == CurrentAbstractClass)
+ OS << "\n";
+ } else
+ OS << "\n";
+ }
+ if (!InsertionGroupOS.str().empty()) {
+ assert(CurrentGroupInsertionLoc.isValid());
+ Replacements.emplace_back(
+ SourceRange(CurrentGroupInsertionLoc, CurrentGroupInsertionLoc),
+ InsertionGroupOS.str());
+ }
+ if (!EndInsertionOS.str().empty())
+ Replacements.emplace_back(SourceRange(InsertionLoc, InsertionLoc),
+ EndInsertionOS.str());
+
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp b/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp
new file mode 100644
index 0000000..de8cfbe
--- /dev/null
+++ b/lib/Tooling/Refactor/FillInMissingProtocolStubs.cpp
@@ -0,0 +1,91 @@
+//===--- FillInMissingProtocolStubs.cpp - --------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Add methods from protocol(s)" refactoring operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringOperations.h"
+#include "clang/AST/AST.h"
+#include "clang/Edit/RefactoringFixits.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace edit::fillInMissingProtocolStubs;
+
+namespace {
+
+class FillInMissingProtocolStubsOperation : public RefactoringOperation {
+public:
+ FillInMissingProtocolStubsOperation(const ObjCContainerDecl *Container,
+ FillInMissingProtocolStubs Impl)
+ : Container(Container), Impl(std::move(Impl)) {}
+
+ const Decl *getTransformedDecl() const override { return Container; }
+
+ llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ const ObjCContainerDecl *Container;
+ FillInMissingProtocolStubs Impl;
+};
+
+} // end anonymous namespace
+
+RefactoringOperationResult
+clang::tooling::initiateFillInMissingProtocolStubsOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ auto SelectedDecl = Slice.innermostSelectedDecl(
+ {Decl::ObjCImplementation, Decl::ObjCCategoryImpl, Decl::ObjCInterface,
+ Decl::ObjCCategory},
+ ASTSlice::InnermostDeclOnly);
+ if (!SelectedDecl)
+ return None;
+ const auto *Container = cast<ObjCContainerDecl>(SelectedDecl->getDecl());
+
+ // If this in a class extension, initiate the operation on the @implementation
+ // if it's in the same TU.
+ if (const auto *Category = dyn_cast<ObjCCategoryDecl>(Container)) {
+ if (Category->IsClassExtension()) {
+ const ObjCInterfaceDecl *I = Category->getClassInterface();
+ if (I && I->getImplementation())
+ Container = I->getImplementation();
+ else
+ return RefactoringOperationResult(
+ "Class extension without suitable @implementation");
+ }
+ }
+
+ FillInMissingProtocolStubs Impl;
+ if (Impl.initiate(Context, Container))
+ return None;
+ if (!Impl.hasMissingRequiredMethodStubs())
+ return RefactoringOperationResult("All of the @required methods are there");
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+ auto Operation = llvm::make_unique<FillInMissingProtocolStubsOperation>(
+ Container, std::move(Impl));
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+}
+
+llvm::Expected<RefactoringResult>
+FillInMissingProtocolStubsOperation::perform(
+ ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+ Impl.perform(Context,
+ [&](const FixItHint &Hint) { Replacements.push_back(Hint); });
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/IfSwitchConversion.cpp b/lib/Tooling/Refactor/IfSwitchConversion.cpp
new file mode 100644
index 0000000..5ce3955
--- /dev/null
+++ b/lib/Tooling/Refactor/IfSwitchConversion.cpp
@@ -0,0 +1,468 @@
+//===--- IfSwitchConversion.cpp - ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "convert to switch" refactoring operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+class IfSwitchConversionOperation : public RefactoringOperation {
+public:
+ IfSwitchConversionOperation(const IfStmt *If) : If(If) {}
+
+ const Stmt *getTransformedStmt() const override { return If; }
+
+ llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ const IfStmt *If;
+};
+
+class ValidIfBodyVerifier : public RecursiveASTVisitor<ValidIfBodyVerifier> {
+ bool CheckBreaks = true;
+
+public:
+ bool IsValid = true;
+
+ bool VisitBreakStmt(const BreakStmt *S) {
+ if (!CheckBreaks)
+ return true;
+ IsValid = false;
+ return false;
+ }
+ bool VisitDefaultStmt(const DefaultStmt *S) {
+ IsValid = false;
+ return false;
+ }
+ bool VisitCaseStmt(const CaseStmt *S) {
+ IsValid = false;
+ return false;
+ }
+
+// Handle nested loops:
+
+#define TRAVERSE_LOOP(STMT) \
+ bool Traverse##STMT(STMT *S) { \
+ bool Prev = CheckBreaks; \
+ CheckBreaks = false; \
+ RecursiveASTVisitor::Traverse##STMT(S); \
+ CheckBreaks = Prev; \
+ return true; \
+ }
+
+ TRAVERSE_LOOP(ForStmt)
+ TRAVERSE_LOOP(WhileStmt)
+ TRAVERSE_LOOP(DoStmt)
+ TRAVERSE_LOOP(CXXForRangeStmt)
+ TRAVERSE_LOOP(ObjCForCollectionStmt)
+
+#undef TRAVERSE_LOOP
+
+ // Handle switches:
+
+ bool TraverseSwitchStmt(SwitchStmt *S) {
+ // Don't visit the body as 'break'/'case'/'default' are all allowed inside
+ // switches.
+ return true;
+ }
+};
+
+} // end anonymous namespace
+
+/// Returns true if any of the if statements in the given if construct have
+/// conditions that aren't allowed by the "convert to switch" operation.
+static bool checkIfsHaveConditionExpression(const IfStmt *If) {
+ for (; If; If = dyn_cast_or_null<IfStmt>(If->getElse())) {
+ if (If->getConditionVariable() || If->getInit() || !If->getCond())
+ return true;
+ }
+ return false;
+}
+
+static Optional<std::pair<const Expr *, const Expr *>>
+matchBinOp(const Expr *E, BinaryOperator::Opcode Kind) {
+ const auto *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
+ if (!BinOp || BinOp->getOpcode() != Kind)
+ return None;
+ return std::pair<const Expr *, const Expr *>(
+ BinOp->getLHS()->IgnoreParenImpCasts(), BinOp->getRHS()->IgnoreParens());
+}
+
+typedef llvm::SmallDenseSet<int64_t, 4> RHSValueSet;
+
+/// Returns true if the conditional expression of an 'if' statement allows
+/// the "convert to switch" refactoring action.
+static bool isConditionValid(const Expr *E, ASTContext &Context,
+ Optional<llvm::FoldingSetNodeID> &MatchedLHSNodeID,
+ RHSValueSet &RHSValues) {
+ auto Equals = matchBinOp(E, BO_EQ);
+ if (!Equals.hasValue()) {
+ auto LogicalOr = matchBinOp(E, BO_LOr);
+ if (!LogicalOr.hasValue())
+ return false;
+ return isConditionValid(LogicalOr.getValue().first, Context,
+ MatchedLHSNodeID, RHSValues) &&
+ isConditionValid(LogicalOr.getValue().second, Context,
+ MatchedLHSNodeID, RHSValues);
+ }
+ const Expr *LHS = Equals.getValue().first;
+ const Expr *RHS = Equals.getValue().second;
+ if (!LHS->getType()->isIntegralOrEnumerationType() ||
+ !RHS->getType()->isIntegralOrEnumerationType())
+ return false;
+
+ // RHS must be a constant and unique.
+ llvm::APSInt Value;
+ if (!RHS->EvaluateAsInt(Value, Context))
+ return false;
+ // Only allow constant that fix into 64 bits.
+ if (Value.getMinSignedBits() > 64 ||
+ !RHSValues.insert(Value.getExtValue()).second)
+ return false;
+
+ // LHS must be identical to the other LHS expressions.
+ llvm::FoldingSetNodeID LHSNodeID;
+ LHS->Profile(LHSNodeID, Context, /*Canonical=*/false);
+ if (MatchedLHSNodeID.hasValue()) {
+ if (MatchedLHSNodeID.getValue() != LHSNodeID)
+ return false;
+ } else
+ MatchedLHSNodeID = std::move(LHSNodeID);
+ return true;
+}
+
+RefactoringOperationResult clang::tooling::initiateIfSwitchConversionOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ // FIXME: Add support for selections.
+ const auto *If = cast_or_null<IfStmt>(Slice.nearestStmt(Stmt::IfStmtClass));
+ if (!If)
+ return None;
+
+ // Don't allow if statements without any 'else' or 'else if'.
+ if (!If->getElse())
+ return None;
+
+ // Don't allow ifs with variable declarations in conditions or C++17
+ // initializer statements.
+ if (checkIfsHaveConditionExpression(If))
+ return None;
+
+ // Find the ranges in which initiation can be performed and verify that the
+ // ifs don't have any initialization expressions or condition variables.
+ SmallVector<SourceRange, 4> Ranges;
+ SourceLocation RangeStart = If->getLocStart();
+ const IfStmt *CurrentIf = If;
+ const SourceManager &SM = Context.getSourceManager();
+ while (true) {
+ const Stmt *Then = CurrentIf->getThen();
+ Ranges.emplace_back(RangeStart,
+ findLastLocationOfSourceConstruct(
+ CurrentIf->getCond()->getLocEnd(), Then, SM));
+ const auto *Else = CurrentIf->getElse();
+ if (!Else)
+ break;
+ RangeStart =
+ findFirstLocationOfSourceConstruct(CurrentIf->getElseLoc(), Then, SM);
+ if (const auto *If = dyn_cast<IfStmt>(Else)) {
+ CurrentIf = If;
+ continue;
+ }
+ Ranges.emplace_back(RangeStart, findLastLocationOfSourceConstruct(
+ CurrentIf->getElseLoc(), Else, SM));
+ break;
+ }
+
+ if (!isLocationInAnyRange(Location, Ranges, SM))
+ return None;
+
+ // Verify that the bodies don't have any 'break'/'default'/'case' statements.
+ ValidIfBodyVerifier BodyVerifier;
+ BodyVerifier.TraverseStmt(const_cast<IfStmt *>(If));
+ if (!BodyVerifier.IsValid)
+ return RefactoringOperationResult(
+ "if's body contains a 'break'/'default'/'case' statement");
+
+ // FIXME: Use ASTMatchers if possible.
+ Optional<llvm::FoldingSetNodeID> MatchedLHSNodeID;
+ RHSValueSet RHSValues;
+ for (const IfStmt *CurrentIf = If; CurrentIf;
+ CurrentIf = dyn_cast_or_null<IfStmt>(CurrentIf->getElse())) {
+ if (!isConditionValid(CurrentIf->getCond(), Context, MatchedLHSNodeID,
+ RHSValues))
+ return RefactoringOperationResult("unsupported conditional expression");
+ }
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (CreateOperation)
+ Result.RefactoringOp.reset(new IfSwitchConversionOperation(If));
+ return Result;
+}
+
+/// Returns the first LHS expression in the if's condition.
+const Expr *getConditionFirstLHS(const Expr *E) {
+ auto Equals = matchBinOp(E, BO_EQ);
+ if (!Equals.hasValue()) {
+ auto LogicalOr = matchBinOp(E, BO_LOr);
+ if (!LogicalOr.hasValue())
+ return nullptr;
+ return getConditionFirstLHS(LogicalOr.getValue().first);
+ }
+ return Equals.getValue().first;
+}
+
+/// Gathers all of the RHS operands of the == expressions in the if's condition.
+void gatherCaseValues(const Expr *E,
+ SmallVectorImpl<const Expr *> &CaseValues) {
+ auto Equals = matchBinOp(E, BO_EQ);
+ if (Equals.hasValue()) {
+ CaseValues.push_back(Equals.getValue().second);
+ return;
+ }
+ auto LogicalOr = matchBinOp(E, BO_LOr);
+ if (!LogicalOr.hasValue())
+ return;
+ gatherCaseValues(LogicalOr.getValue().first, CaseValues);
+ gatherCaseValues(LogicalOr.getValue().second, CaseValues);
+}
+
+/// Return true iff the given body should be terminated with a 'break' statement
+/// when used inside of a switch.
+static bool isBreakNeeded(const Stmt *Body) {
+ const auto *CS = dyn_cast<CompoundStmt>(Body);
+ if (!CS)
+ return !isa<ReturnStmt>(Body);
+ return CS->body_empty() ? true : isBreakNeeded(CS->body_back());
+}
+
+/// Returns true if the given statement declares a variable.
+static bool isVarDeclaringStatement(const Stmt *S) {
+ const auto *DS = dyn_cast<DeclStmt>(S);
+ if (!DS)
+ return false;
+ for (const Decl *D : DS->decls()) {
+ if (isa<VarDecl>(D))
+ return true;
+ }
+ return false;
+}
+
+/// Return true if the body of an if/else if/else needs to be wrapped in braces
+/// when put in a switch.
+static bool areBracesNeeded(const Stmt *Body) {
+ const auto *CS = dyn_cast<CompoundStmt>(Body);
+ if (!CS)
+ return isVarDeclaringStatement(Body);
+ for (const Stmt *S : CS->body()) {
+ if (isVarDeclaringStatement(S))
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+/// Information about the replacement that replaces 'if'/'else' with a 'case' or
+/// a 'default'.
+struct CasePlacement {
+ /// The location of the 'case' or 'default'.
+ SourceLocation CaseStartLoc;
+ /// True when this 'case' or 'default' statement needs a newline.
+ bool NeedsNewLine;
+ /// True if this the first 'if' in the source construct.
+ bool IsFirstIf;
+ /// True if we need to insert a 'break' to terminate the previous body
+ /// before the 'case' or 'default'.
+ bool IsBreakNeeded;
+ /// True if we need to insert a '}' before the case.
+ bool ArePreviousBracesNeeded;
+
+ CasePlacement(SourceLocation Loc)
+ : CaseStartLoc(Loc), NeedsNewLine(false), IsFirstIf(true),
+ IsBreakNeeded(false), ArePreviousBracesNeeded(false) {}
+
+ CasePlacement(const IfStmt *If, const SourceManager &SM,
+ bool AreBracesNeeded) {
+ CaseStartLoc = SM.getSpellingLoc(isa<CompoundStmt>(If->getThen())
+ ? If->getThen()->getLocEnd()
+ : If->getElseLoc());
+ SourceLocation BodyEndLoc = findLastNonCompoundLocation(If->getThen());
+ NeedsNewLine = BodyEndLoc.isValid()
+ ? areOnSameLine(CaseStartLoc, BodyEndLoc, SM)
+ : false;
+ IsFirstIf = false;
+ IsBreakNeeded = isBreakNeeded(If->getThen());
+ ArePreviousBracesNeeded = AreBracesNeeded;
+ }
+
+ std::string getCaseReplacementString(bool IsDefault = false,
+ bool AreNextBracesNeeded = false) const {
+ if (IsFirstIf)
+ return ") {\ncase ";
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ if (NeedsNewLine)
+ OS << '\n';
+ if (IsBreakNeeded)
+ OS << "break;\n";
+ if (ArePreviousBracesNeeded)
+ OS << "}\n";
+ OS << (IsDefault ? "default:" : "case ");
+ if (IsDefault && AreNextBracesNeeded)
+ OS << " {";
+ return std::move(OS.str());
+ }
+};
+
+} // end anonymous namespace
+
+static llvm::Error
+addCaseReplacements(const IfStmt *If, const CasePlacement &CaseInfo,
+ bool &AreBracesNeeded,
+ std::vector<RefactoringReplacement> &Replacements,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ SmallVector<const Expr *, 2> CaseValues;
+ gatherCaseValues(If->getCond(), CaseValues);
+ assert(!CaseValues.empty());
+ Replacements.emplace_back(
+ SourceRange(CaseInfo.CaseStartLoc,
+ SM.getSpellingLoc(CaseValues[0]->getLocStart())),
+ CaseInfo.getCaseReplacementString());
+
+ SourceLocation PrevCaseEnd = getPreciseTokenLocEnd(
+ SM.getSpellingLoc(CaseValues[0]->getLocEnd()), SM, LangOpts);
+ for (const Expr *CaseValue : llvm::makeArrayRef(CaseValues).drop_front()) {
+ Replacements.emplace_back(
+ SourceRange(PrevCaseEnd, SM.getSpellingLoc(CaseValue->getLocStart())),
+ StringRef(":\ncase "));
+ PrevCaseEnd = getPreciseTokenLocEnd(
+ SM.getSpellingLoc(CaseValue->getLocEnd()), SM, LangOpts);
+ }
+
+ AreBracesNeeded = areBracesNeeded(If->getThen());
+ StringRef ColonReplacement = AreBracesNeeded ? ": {" : ":";
+ if (isa<CompoundStmt>(If->getThen())) {
+ Replacements.emplace_back(
+ SourceRange(
+ PrevCaseEnd,
+ getPreciseTokenLocEnd(
+ SM.getSpellingLoc(If->getThen()->getLocStart()), SM, LangOpts)),
+ ColonReplacement);
+ } else {
+ // Find the location of the if's ')'
+ SourceLocation End = findClosingParenLocEnd(
+ SM.getSpellingLoc(If->getCond()->getLocEnd()), SM, LangOpts);
+ if (!End.isValid())
+ return llvm::make_error<RefactoringOperationError>(
+ "couldn't find the location of ')'");
+ Replacements.emplace_back(SourceRange(PrevCaseEnd, End), ColonReplacement);
+ }
+ return llvm::Error::success();
+}
+
+llvm::Expected<RefactoringResult>
+IfSwitchConversionOperation::perform(ASTContext &Context,
+ const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+ const SourceManager &SM = Context.getSourceManager();
+ const LangOptions &LangOpts = Context.getLangOpts();
+
+ // The first if should be replaced with a 'switch' and the text for first LHS
+ // should be preserved.
+ const Expr *LHS = getConditionFirstLHS(If->getCond());
+ assert(LHS && "Missing == expression");
+ Replacements.emplace_back(SourceRange(SM.getSpellingLoc(If->getLocStart()),
+ SM.getSpellingLoc(LHS->getLocStart())),
+ StringRef("switch ("));
+
+ bool AreBracesNeeded = false;
+ if (auto Error = addCaseReplacements(
+ If, CasePlacement(getPreciseTokenLocEnd(
+ SM.getSpellingLoc(LHS->getLocEnd()), SM, LangOpts)),
+ AreBracesNeeded, Replacements, SM, LangOpts))
+ return std::move(Error);
+
+ // Convert the remaining ifs to 'case' statements.
+ const IfStmt *CurrentIf = If;
+ while (true) {
+ const IfStmt *NextIf = dyn_cast_or_null<IfStmt>(CurrentIf->getElse());
+ if (!NextIf)
+ break;
+ if (auto Error = addCaseReplacements(
+ NextIf, CasePlacement(CurrentIf, SM, AreBracesNeeded),
+ AreBracesNeeded, Replacements, SM, LangOpts))
+ return std::move(Error);
+ CurrentIf = NextIf;
+ }
+
+ // Convert the 'else' to 'default'
+ if (const Stmt *Else = CurrentIf->getElse()) {
+ CasePlacement DefaultInfo(CurrentIf, SM, AreBracesNeeded);
+ AreBracesNeeded = areBracesNeeded(Else);
+
+ SourceLocation EndLoc = getPreciseTokenLocEnd(
+ SM.getSpellingLoc(isa<CompoundStmt>(Else) ? Else->getLocStart()
+ : CurrentIf->getElseLoc()),
+ SM, LangOpts);
+ Replacements.emplace_back(SourceRange(DefaultInfo.CaseStartLoc, EndLoc),
+ DefaultInfo.getCaseReplacementString(
+ /*IsDefault=*/true, AreBracesNeeded));
+ }
+
+ // Add the trailing break and one or two '}' if needed.
+ const Stmt *LastBody =
+ CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();
+ bool IsLastBreakNeeded = isBreakNeeded(LastBody);
+ SourceLocation TerminatingReplacementLoc;
+ std::string TerminatingReplacement;
+ llvm::raw_string_ostream OS(TerminatingReplacement);
+ if (!isa<CompoundStmt>(LastBody)) {
+ TerminatingReplacementLoc = LastBody->getLocEnd();
+ // Try to adjust the location in order to preserve any trailing comments on
+ // the last line of the last body.
+ if (!TerminatingReplacementLoc.isMacroID())
+ TerminatingReplacementLoc = getLastLineLocationUnlessItHasOtherTokens(
+ TerminatingReplacementLoc, SM, LangOpts);
+ if (IsLastBreakNeeded)
+ OS << "\nbreak;";
+ OS << "\n}";
+ if (AreBracesNeeded)
+ OS << "\n}";
+ } else {
+ TerminatingReplacementLoc = LastBody->getLocEnd();
+ if (IsLastBreakNeeded)
+ OS << "break;\n";
+ if (AreBracesNeeded)
+ OS << "}\n";
+ }
+
+ if (!OS.str().empty()) {
+ TerminatingReplacementLoc = SM.getSpellingLoc(TerminatingReplacementLoc);
+ Replacements.emplace_back(
+ SourceRange(TerminatingReplacementLoc, TerminatingReplacementLoc),
+ std::move(OS.str()));
+ }
+
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp b/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp
new file mode 100644
index 0000000..c5865e4
--- /dev/null
+++ b/lib/Tooling/Refactor/ImplementDeclaredMethods.cpp
@@ -0,0 +1,446 @@
+//===--- ImplementDeclaredMethods.cpp - ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Generate missing method definitions" refactoring
+// operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringContinuations.h"
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+template <typename ClassType, typename MethodType, typename Derived>
+class ImplementDeclaredMethodsOperation : public RefactoringOperation {
+public:
+ ImplementDeclaredMethodsOperation(
+ const ClassType *Container, ArrayRef<const MethodType *> SelectedMethods)
+ : Container(Container),
+ SelectedMethods(SelectedMethods.begin(), SelectedMethods.end()) {}
+
+ const Decl *getTransformedDecl() const override {
+ return SelectedMethods.front();
+ }
+
+ const Decl *getLastTransformedDecl() const override {
+ return SelectedMethods.back();
+ }
+
+ static RefactoringOperationResult
+ initiate(const ClassType *Container, ArrayRef<const MethodType *> Methods,
+ bool CreateOperation) {
+ if (Methods.empty())
+ return None;
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+ auto Operation = llvm::make_unique<Derived>(Container, Methods);
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+ }
+
+ const ClassType *Container;
+ llvm::SmallVector<const MethodType *, 8> SelectedMethods;
+};
+
+class ImplementDeclaredCXXMethodsOperation
+ : public ImplementDeclaredMethodsOperation<
+ CXXRecordDecl, CXXMethodDecl, ImplementDeclaredCXXMethodsOperation> {
+public:
+ ImplementDeclaredCXXMethodsOperation(
+ const CXXRecordDecl *Container,
+ ArrayRef<const CXXMethodDecl *> SelectedMethods)
+ : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {}
+
+ llvm::Expected<RefactoringResult>
+ perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ static void addInlineBody(const CXXMethodDecl *MD, const ASTContext &Context,
+ std::vector<RefactoringReplacement> &Replacements);
+
+ static llvm::Expected<RefactoringResult> runInImplementationAST(
+ ASTContext &Context, const FileID &File, const CXXRecordDecl *Class,
+ ArrayRef<indexer::Indexed<const CXXMethodDecl *>> SelectedMethods);
+};
+
+class ImplementDeclaredObjCMethodsOperation
+ : public ImplementDeclaredMethodsOperation<
+ ObjCContainerDecl, ObjCMethodDecl,
+ ImplementDeclaredObjCMethodsOperation> {
+ const ObjCInterfaceDecl *Interface;
+
+public:
+ ImplementDeclaredObjCMethodsOperation(
+ const ObjCContainerDecl *Container,
+ ArrayRef<const ObjCMethodDecl *> SelectedMethods)
+ : ImplementDeclaredMethodsOperation(Container, SelectedMethods) {
+ if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container))
+ Interface = CD->getClassInterface();
+ else
+ Interface = nullptr;
+ }
+
+ llvm::Expected<RefactoringResult>
+ perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ static llvm::Expected<RefactoringResult> runInImplementationAST(
+ ASTContext &Context, const FileID &File,
+ const ObjCContainerDecl *Container, const ObjCInterfaceDecl *Interface,
+ ArrayRef<std::string> MethodDeclarations,
+ ArrayRef<indexer::Indexed<const ObjCMethodDecl *>> SelectedMethods);
+};
+
+/// Returns true if the given Objective-C method has an implementation.
+bool isImplemented(const ObjCMethodDecl *M) {
+ if (M->hasBody() || M->isDefined())
+ return true;
+ return false;
+}
+
+} // end anonymous namespace
+
+RefactoringOperationResult
+clang::tooling::initiateImplementDeclaredMethodsOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ // Find the selected Class.
+ auto SelectedDecl = Slice.innermostSelectedDecl([](const Decl *D) {
+ return isa<CXXRecordDecl>(D) || isa<ObjCInterfaceDecl>(D) ||
+ isa<ObjCCategoryDecl>(D);
+ });
+ if (!SelectedDecl)
+ return None;
+ // Look at the set of methods that intersect with the selection.
+ if (const auto *CXXClass = dyn_cast<CXXRecordDecl>(SelectedDecl->getDecl())) {
+ if (CXXClass->isDependentType())
+ return RefactoringOperationResult("templates are unsupported");
+ llvm::SmallVector<const CXXMethodDecl *, 8> SelectedMethods;
+ for (const CXXMethodDecl *M : CXXClass->methods()) {
+ if (M->isImplicit() || M->hasBody() || M->isPure() || M->isDefaulted() ||
+ M->isDeletedAsWritten() || M->getDescribedFunctionTemplate())
+ continue;
+ if (Slice.isSourceRangeSelected(
+ CharSourceRange::getTokenRange(M->getSourceRange())))
+ SelectedMethods.push_back(M);
+ }
+ return ImplementDeclaredCXXMethodsOperation::initiate(
+ CXXClass, SelectedMethods, CreateOperation);
+ }
+ const ObjCContainerDecl *Container =
+ cast<ObjCContainerDecl>(SelectedDecl->getDecl());
+ llvm::SmallVector<const ObjCMethodDecl *, 8> SelectedMethods;
+ for (const ObjCMethodDecl *M : Container->methods()) {
+ if (M->isImplicit() || isImplemented(M))
+ continue;
+ if (Slice.isSourceRangeSelected(
+ CharSourceRange::getTokenRange(M->getSourceRange())))
+ SelectedMethods.push_back(M);
+ }
+ // Method declarations from class extensions should be defined in class
+ // @implementations.
+ if (const auto *Category = dyn_cast<ObjCCategoryDecl>(Container)) {
+ if (Category->IsClassExtension())
+ Container = Category->getClassInterface();
+ }
+ return ImplementDeclaredObjCMethodsOperation::initiate(
+ Container, SelectedMethods, CreateOperation);
+}
+
+llvm::Expected<RefactoringResult>
+ImplementDeclaredCXXMethodsOperation::perform(
+ ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) {
+ if (Container->isLexicallyWithinFunctionOrMethod()) {
+ // Local methods can be implemented inline.
+ std::vector<RefactoringReplacement> Replacements;
+ for (const CXXMethodDecl *MD : SelectedMethods)
+ addInlineBody(MD, Context, Replacements);
+ return std::move(Replacements);
+ }
+ using namespace indexer;
+ return continueInExternalASTUnit(
+ fileThatShouldContainImplementationOf(Container), runInImplementationAST,
+ Container, filter(llvm::makeArrayRef(SelectedMethods),
+ [](const DeclEntity &D) { return !D.isDefined(); }));
+}
+
+void ImplementDeclaredCXXMethodsOperation::addInlineBody(
+ const CXXMethodDecl *MD, const ASTContext &Context,
+ std::vector<RefactoringReplacement> &Replacements) {
+ SourceLocation EndLoc = MD->getLocEnd();
+ SourceRange SemiRange = getRangeOfNextToken(
+ EndLoc, tok::semi, Context.getSourceManager(), Context.getLangOpts());
+ if (SemiRange.isValid()) {
+ Replacements.push_back(RefactoringReplacement(SemiRange));
+ EndLoc = SemiRange.getEnd();
+ }
+ SourceLocation InsertionLoc = getLastLineLocationUnlessItHasOtherTokens(
+ EndLoc, Context.getSourceManager(), Context.getLangOpts());
+ Replacements.push_back(
+ RefactoringReplacement(SourceRange(InsertionLoc, InsertionLoc),
+ StringRef(" { \n <#code#>;\n}")));
+}
+
+static const RecordDecl *findOutermostRecord(const RecordDecl *RD) {
+ const RecordDecl *Result = RD;
+ for (const DeclContext *DC = Result->getLexicalDeclContext();
+ isa<RecordDecl>(DC); DC = Result->getLexicalDeclContext())
+ Result = cast<RecordDecl>(DC);
+ return Result;
+}
+
+static bool containsUsingOf(const NamespaceDecl *ND,
+ const ASTContext &Context) {
+ for (const Decl *D : Context.getTranslationUnitDecl()->decls()) {
+ if (const auto *UDD = dyn_cast<UsingDirectiveDecl>(D)) {
+ if (UDD->getNominatedNamespace() == ND)
+ return true;
+ }
+ }
+ return false;
+}
+
+llvm::Expected<RefactoringResult>
+ImplementDeclaredCXXMethodsOperation::runInImplementationAST(
+ ASTContext &Context, const FileID &File, const CXXRecordDecl *Class,
+ ArrayRef<indexer::Indexed<const CXXMethodDecl *>> SelectedMethods) {
+ if (!Class)
+ return llvm::make_error<RefactoringOperationError>(
+ "the target class is not defined in the continuation AST unit");
+
+ SourceManager &SM = Context.getSourceManager();
+
+ // Find the defined methods of the class.
+ llvm::SmallVector<const CXXMethodDecl *, 8> DefinedOutOfLineMethods;
+ for (const CXXMethodDecl *M : Class->methods()) {
+ if (M->isImplicit())
+ continue;
+ if (const FunctionDecl *MD = M->getDefinition()) {
+ if (!MD->isOutOfLine())
+ continue;
+ SourceLocation Loc = SM.getExpansionLoc(MD->getLocStart());
+ if (SM.getFileID(Loc) == File)
+ DefinedOutOfLineMethods.push_back(cast<CXXMethodDecl>(MD));
+ }
+ }
+
+ std::vector<RefactoringReplacement> Replacements;
+ std::string MethodString;
+ llvm::raw_string_ostream OS(MethodString);
+
+ // Pick a good insertion location.
+ SourceLocation InsertionLoc;
+ const CXXMethodDecl *InsertAfterMethod = nullptr;
+ NestedNameSpecifier *NamePrefix = nullptr;
+ if (DefinedOutOfLineMethods.empty()) {
+ const RecordDecl *OutermostRecord = findOutermostRecord(Class);
+ InsertionLoc = SM.getExpansionRange(OutermostRecord->getLocEnd()).second;
+ if (SM.getFileID(InsertionLoc) == File) {
+ // We can insert right after the class. Compute the appropriate
+ // qualification.
+ NamePrefix = NestedNameSpecifier::getRequiredQualification(
+ Context, OutermostRecord->getLexicalDeclContext(),
+ Class->getLexicalDeclContext());
+ } else {
+ // We can't insert after the end of the class, since the indexer told us
+ // that some file should have the implementation of it, even when there
+ // are no methods here. We should try to insert at the end of the file.
+ InsertionLoc = SM.getLocForEndOfFile(File);
+ NamePrefix = NestedNameSpecifier::getRequiredQualification(
+ Context, Context.getTranslationUnitDecl(),
+ Class->getLexicalDeclContext());
+ llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;
+ for (const NestedNameSpecifier *Qualifier = NamePrefix; Qualifier;
+ Qualifier = Qualifier->getPrefix()) {
+ if (const NamespaceDecl *ND = Qualifier->getAsNamespace())
+ Namespaces.push_back(ND);
+ }
+ // When the class is in a namespace, add a 'using' declaration if it's
+ // needed and adjust the out-of-line qualification.
+ if (!Namespaces.empty()) {
+ const NamespaceDecl *InnermostNamespace = Namespaces[0];
+ if (!containsUsingOf(InnermostNamespace, Context)) {
+ std::string NamespaceString;
+ llvm::raw_string_ostream NamespaceOS(NamespaceString);
+ for (const NamespaceDecl *ND : llvm::reverse(Namespaces)) {
+ if (!NamespaceOS.str().empty())
+ NamespaceOS << "::";
+ NamespaceOS << ND->getDeclName();
+ }
+ OS << "\nusing namespace " << NamespaceOS.str() << ";";
+ }
+ // Re-compute the name qualifier without the namespace.
+ NamePrefix = NestedNameSpecifier::getRequiredQualification(
+ Context, InnermostNamespace, Class->getLexicalDeclContext());
+ }
+ }
+ } else {
+ // Insert at the end of the defined methods.
+ for (const CXXMethodDecl *M : DefinedOutOfLineMethods) {
+ SourceLocation EndLoc = SM.getExpansionRange(M->getLocEnd()).second;
+ if (InsertionLoc.isInvalid() ||
+ SM.isBeforeInTranslationUnit(InsertionLoc, EndLoc)) {
+ InsertionLoc = EndLoc;
+ InsertAfterMethod = M;
+ }
+ }
+ }
+ InsertionLoc = getLastLineLocationUnlessItHasOtherTokens(
+ InsertionLoc, SM, Context.getLangOpts());
+
+ PrintingPolicy PP = Context.getPrintingPolicy();
+ PP.PolishForDeclaration = true;
+ PP.SupressStorageClassSpecifiers = true;
+ PP.SuppressStrongLifetime = true;
+ PP.SuppressLifetimeQualifiers = true;
+ PP.SuppressUnwrittenScope = true;
+ OS << "\n";
+ for (const auto &I : SelectedMethods) {
+ const CXXMethodDecl *MD = I.Decl;
+ // Check if the method is already defined.
+ if (!MD)
+ continue;
+
+ // Drop the 'virtual' specifier.
+ bool IsVirtual = MD->isVirtualAsWritten();
+ const_cast<CXXMethodDecl *>(MD)->setVirtualAsWritten(false);
+
+ // Drop the default arguments.
+ llvm::SmallVector<std::pair<ParmVarDecl *, Expr *>, 4> DefaultArgs;
+ for (const ParmVarDecl *P : MD->parameters()) {
+ if (!P->hasDefaultArg())
+ continue;
+ Expr *E = const_cast<ParmVarDecl *>(P)->getDefaultArg();
+ const_cast<ParmVarDecl *>(P)->setDefaultArg(nullptr);
+ DefaultArgs.emplace_back(const_cast<ParmVarDecl *>(P), E);
+ }
+
+ // Add the nested name specifiers that are appropriate for an out-of-line
+ // method.
+ auto *Qualifier =
+ InsertAfterMethod
+ ? InsertAfterMethod->getQualifier()
+ : NestedNameSpecifier::Create(
+ Context, /*Prefix=*/NamePrefix, /*Template=*/false,
+ Context.getRecordType(Class).getTypePtr());
+ NestedNameSpecifierLoc PrevQualifierInfo = MD->getQualifierLoc();
+ const_cast<CXXMethodDecl *>(MD)->setQualifierInfo(
+ NestedNameSpecifierLoc(Qualifier, /*Loc=*/nullptr));
+
+ OS << "\n";
+ MD->print(OS, PP);
+ OS << " { \n <#code#>;\n}\n";
+
+ // Restore the original method
+ for (const auto &DefaultArg : DefaultArgs)
+ DefaultArg.first->setDefaultArg(DefaultArg.second);
+ const_cast<CXXMethodDecl *>(MD)->setVirtualAsWritten(IsVirtual);
+ const_cast<CXXMethodDecl *>(MD)->setQualifierInfo(PrevQualifierInfo);
+ }
+
+ Replacements.push_back(RefactoringReplacement(
+ SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str())));
+
+ return std::move(Replacements);
+}
+
+llvm::Expected<RefactoringResult>
+ImplementDeclaredObjCMethodsOperation::perform(
+ ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options, unsigned SelectedCandidateIndex) {
+ using namespace indexer;
+
+ // Print the methods before running the continuation because the continuation
+ // TU might not have these method declarations (e.g. category implemented in
+ // the class implementation).
+ PrintingPolicy PP = Context.getPrintingPolicy();
+ PP.PolishForDeclaration = true;
+ PP.SuppressStrongLifetime = true;
+ PP.SuppressLifetimeQualifiers = true;
+ PP.SuppressUnwrittenScope = true;
+ std::vector<std::string> MethodDeclarations;
+ for (const ObjCMethodDecl *MD : SelectedMethods) {
+ std::string MethodDeclStr;
+ llvm::raw_string_ostream MethodOS(MethodDeclStr);
+ MD->print(MethodOS, PP);
+ MethodDeclarations.push_back(std::move(MethodOS.str()));
+ }
+
+ return continueInExternalASTUnit(
+ fileThatShouldContainImplementationOf(Container), runInImplementationAST,
+ Container, Interface, MethodDeclarations,
+ filter(llvm::makeArrayRef(SelectedMethods),
+ [](const DeclEntity &D) { return !D.isDefined(); }));
+}
+
+static const ObjCImplDecl *
+getImplementationContainer(const ObjCContainerDecl *Container,
+ const ObjCInterfaceDecl *Interface = nullptr) {
+ if (!Container)
+ return Interface ? getImplementationContainer(Interface) : nullptr;
+ if (const auto *ID = dyn_cast<ObjCInterfaceDecl>(Container))
+ return ID->getImplementation();
+ if (const auto *CD = dyn_cast<ObjCCategoryDecl>(Container)) {
+ if (const auto *Impl = CD->getImplementation())
+ return Impl;
+ return getImplementationContainer(Interface);
+ }
+ return nullptr;
+}
+
+llvm::Expected<RefactoringResult>
+ImplementDeclaredObjCMethodsOperation::runInImplementationAST(
+ ASTContext &Context, const FileID &File, const ObjCContainerDecl *Container,
+ const ObjCInterfaceDecl *Interface,
+ ArrayRef<std::string> MethodDeclarations,
+ ArrayRef<indexer::Indexed<const ObjCMethodDecl *>> SelectedMethods) {
+ const ObjCImplDecl *ImplementationContainer =
+ getImplementationContainer(Container, Interface);
+ if (!ImplementationContainer)
+ return llvm::make_error<RefactoringOperationError>(
+ "the target @interface is not implemented in the continuation AST "
+ "unit");
+
+ std::vector<RefactoringReplacement> Replacements;
+
+ std::string MethodString;
+ llvm::raw_string_ostream OS(MethodString);
+
+ assert(MethodDeclarations.size() >= SelectedMethods.size() &&
+ "fewer declarations than selected methods?");
+ for (const auto &I : llvm::enumerate(SelectedMethods)) {
+ indexer::Indexed<const ObjCMethodDecl *> Decl = I.value();
+ // Skip methods that are already defined.
+ if (!Decl.isNotDefined())
+ continue;
+
+ OS << StringRef(MethodDeclarations[I.index()]).drop_back(); // Drop the ';'
+ OS << " { \n <#code#>;\n}\n\n";
+ }
+ SourceLocation InsertionLoc = ImplementationContainer->getLocEnd();
+
+ Replacements.push_back(RefactoringReplacement(
+ SourceRange(InsertionLoc, InsertionLoc), std::move(OS.str())));
+
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/IndexerQueries.cpp b/lib/Tooling/Refactor/IndexerQueries.cpp
new file mode 100644
index 0000000..f2392e4
--- /dev/null
+++ b/lib/Tooling/Refactor/IndexerQueries.cpp
@@ -0,0 +1,172 @@
+//===--- IndexerQueries.cpp - Indexer queries -----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Core/RefactoringDiagnostic.h"
+#include "clang/Tooling/Refactor/IndexerQuery.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::tooling::indexer;
+using namespace clang::tooling::indexer::detail;
+using namespace llvm::yaml;
+
+const char *ASTProducerQuery::BaseUIDString = "ast.producer.query";
+const char *DeclarationsQuery::BaseUIDString = "decl.query";
+const char *ASTUnitForImplementationOfDeclarationQuery::NameUIDString =
+ "file.for.impl.of.decl";
+
+const char *DeclPredicateNodePredicate::NameUIDString = "decl.predicate";
+const char *DeclPredicateNotPredicate::NameUIDString = "not.decl.predicate";
+
+std::unique_ptr<DeclPredicateNode>
+DeclPredicateNode::create(const DeclPredicate &Predicate) {
+ return llvm::make_unique<DeclPredicateNodePredicate>(Predicate);
+}
+
+std::unique_ptr<DeclPredicateNode>
+DeclPredicateNode::create(const BoolDeclPredicate &Predicate) {
+ if (Predicate.IsInverted)
+ return llvm::make_unique<DeclPredicateNotPredicate>(
+ create(Predicate.Predicate));
+ return create(Predicate.Predicate);
+}
+
+std::unique_ptr<ASTUnitForImplementationOfDeclarationQuery>
+clang::tooling::indexer::fileThatShouldContainImplementationOf(const Decl *D) {
+ return llvm::make_unique<ASTUnitForImplementationOfDeclarationQuery>(D);
+}
+
+bool ASTUnitForImplementationOfDeclarationQuery::verify(ASTContext &Context) {
+ if (!D) {
+ assert(false && "Query should be verified before persisting");
+ return false;
+ }
+ // Check if we've got the filename.
+ if (!Result.Filename.empty())
+ return false;
+ Context.getDiagnostics().Report(
+ D->getLocation(), diag::err_ref_continuation_missing_implementation)
+ << isa<ObjCContainerDecl>(D) << cast<NamedDecl>(D);
+ return true;
+}
+
+bool DeclarationsQuery::verify(ASTContext &Context) {
+ if (Input.empty()) {
+ assert(false && "Query should be verified before persisting");
+ return false;
+ }
+ if (!Output.empty()) {
+ // At least one output declaration must be valid.
+ for (const auto &Ref : Output) {
+ if (!Ref.Decl.USR.empty())
+ return false;
+ }
+ }
+ // FIXME: This is too specific, the new refactoring engine at llvm.org should
+ // generalize this.
+ Context.getDiagnostics().Report(
+ Input[0]->getLocation(),
+ diag::err_implement_declared_methods_all_implemented);
+ return true;
+}
+
+namespace {
+
+struct QueryPredicateNode {
+ std::string Name;
+ std::vector<int> IntegerValues;
+};
+
+struct QueryYAMLNode {
+ std::string Name;
+ std::vector<QueryPredicateNode> PredicateResults;
+ std::string FilenameResult;
+};
+
+} // end anonymous namespace
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(QueryPredicateNode)
+LLVM_YAML_IS_SEQUENCE_VECTOR(QueryYAMLNode)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<QueryPredicateNode> {
+ static void mapping(IO &Yaml, QueryPredicateNode &Predicate) {
+ Yaml.mapRequired("name", Predicate.Name);
+ Yaml.mapRequired("intValues", Predicate.IntegerValues);
+ }
+};
+
+template <> struct MappingTraits<QueryYAMLNode> {
+ static void mapping(IO &Yaml, QueryYAMLNode &Query) {
+ Yaml.mapRequired("name", Query.Name);
+ Yaml.mapOptional("predicateResults", Query.PredicateResults);
+ Yaml.mapOptional("filenameResult", Query.FilenameResult);
+ // FIXME: Report an error if no results are provided at all.
+ }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+llvm::Error
+IndexerQuery::loadResultsFromYAML(StringRef Source,
+ ArrayRef<IndexerQuery *> Queries) {
+ std::vector<QueryYAMLNode> QueryResults;
+ Input YamlIn(Source);
+ YamlIn >> QueryResults;
+ if (YamlIn.error())
+ return llvm::make_error<llvm::StringError>("Failed to parse query results",
+ YamlIn.error());
+ if (QueryResults.size() != Queries.size())
+ return llvm::make_error<llvm::StringError>("Mismatch in query results size",
+ llvm::errc::invalid_argument);
+ for (const auto &QueryTuple : llvm::zip(Queries, QueryResults)) {
+ IndexerQuery *Query = std::get<0>(QueryTuple);
+ const QueryYAMLNode &Result = std::get<1>(QueryTuple);
+ if ((Query->NameUID && Query->NameUID != Result.Name) &&
+ (Query->BaseUID && Query->BaseUID != Result.Name))
+ continue;
+ if (auto *DQ = dyn_cast<DeclarationsQuery>(Query)) {
+ const DeclPredicateNode &Predicate = DQ->getPredicateNode();
+ DeclPredicate ActualPredicate("");
+ bool IsNot = false;
+ if (const auto *Not = dyn_cast<DeclPredicateNotPredicate>(&Predicate)) {
+ ActualPredicate =
+ cast<DeclPredicateNodePredicate>(Not->getChild()).getPredicate();
+ IsNot = true;
+ } else
+ ActualPredicate =
+ cast<DeclPredicateNodePredicate>(Predicate).getPredicate();
+ for (const auto &PredicateResult : Result.PredicateResults) {
+ if (PredicateResult.Name != ActualPredicate.Name)
+ continue;
+ std::vector<Indexed<PersistentDeclRef<Decl>>> Output;
+ for (const auto &ResultTuple :
+ zip(DQ->getInputs(), PredicateResult.IntegerValues)) {
+ const Decl *D = std::get<0>(ResultTuple);
+ int Result = std::get<1>(ResultTuple);
+ bool Value = (IsNot ? !Result : !!Result);
+ Output.push_back(Indexed<PersistentDeclRef<Decl>>(
+ PersistentDeclRef<Decl>::create(Value ? D : nullptr),
+ Value ? QueryBoolResult::Yes : QueryBoolResult::No));
+ }
+ DQ->setOutput(std::move(Output));
+ break;
+ }
+ } else if (auto *AQ =
+ dyn_cast<ASTUnitForImplementationOfDeclarationQuery>(Query))
+ AQ->setResult(Result.FilenameResult);
+ }
+ return llvm::Error::success();
+}
diff --git a/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp b/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp
new file mode 100644
index 0000000..08cf41d
--- /dev/null
+++ b/lib/Tooling/Refactor/LocalizeObjCStringLiteral.cpp
@@ -0,0 +1,83 @@
+//===--- LocalizeObjCString.cpp - ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements the "Wrap in NSLocalizedString" refactoring operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+class LocalizeObjCStringLiteralOperation : public RefactoringOperation {
+public:
+ LocalizeObjCStringLiteralOperation(const ObjCStringLiteral *E) : E(E) {}
+
+ const Stmt *getTransformedStmt() const override { return E; }
+
+ llvm::Expected<RefactoringResult> perform(ASTContext &Context, const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) override;
+
+ const ObjCStringLiteral *E;
+};
+
+} // end anonymous namespace
+
+RefactoringOperationResult
+clang::tooling::initiateLocalizeObjCStringLiteralOperation(
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location,
+ SourceRange SelectionRange, bool CreateOperation) {
+ const ObjCStringLiteral *E;
+ if (SelectionRange.isValid()) {
+ auto SelectedSet = Slice.getSelectedStmtSet();
+ if (!SelectedSet)
+ return None;
+ E = dyn_cast_or_null<ObjCStringLiteral>(
+ SelectedSet->containsSelectionRange);
+ } else
+ E = cast_or_null<ObjCStringLiteral>(
+ Slice.nearestStmt(Stmt::ObjCStringLiteralClass));
+ if (!E)
+ return None;
+
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (!CreateOperation)
+ return Result;
+ auto Operation = llvm::make_unique<LocalizeObjCStringLiteralOperation>(E);
+ Result.RefactoringOp = std::move(Operation);
+ return Result;
+}
+
+llvm::Expected<RefactoringResult>
+LocalizeObjCStringLiteralOperation::perform(ASTContext &Context,
+ const Preprocessor &ThePreprocessor,
+ const RefactoringOptionSet &Options,
+ unsigned SelectedCandidateIndex) {
+ std::vector<RefactoringReplacement> Replacements;
+ // TODO: New API: Replace by something like Node.wrap("NSLocalizedString(", ",
+ // @""")
+ SourceLocation LocStart =
+ Context.getSourceManager().getSpellingLoc(E->getLocStart());
+ Replacements.emplace_back(SourceRange(LocStart, LocStart),
+ StringRef("NSLocalizedString("));
+ SourceLocation LocEnd = getPreciseTokenLocEnd(
+ Context.getSourceManager().getSpellingLoc(E->getLocEnd()),
+ Context.getSourceManager(), Context.getLangOpts());
+ Replacements.emplace_back(SourceRange(LocEnd, LocEnd), StringRef(", @\"\")"));
+ return std::move(Replacements);
+}
diff --git a/lib/Tooling/Refactor/RefactoringActionFinder.cpp b/lib/Tooling/Refactor/RefactoringActionFinder.cpp
new file mode 100644
index 0000000..2fd7cd5
--- /dev/null
+++ b/lib/Tooling/Refactor/RefactoringActionFinder.cpp
@@ -0,0 +1,57 @@
+//===--- RefactoringActionFinder.cpp - Clang refactoring library ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implements methods that find the refactoring actions that can be
+/// performed at specific locations / source ranges in a translation unit.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RefactoringActionFinder.h"
+#include "clang/Tooling/Refactor/RefactoringOperation.h"
+#include "clang/Tooling/Refactor/USRFinder.h"
+
+namespace clang {
+namespace tooling {
+
+RefactoringActionSet findActionSetAt(SourceLocation Location,
+ SourceRange SelectionRange,
+ ASTContext &Context) {
+ RefactoringActionSet Result;
+ if (const auto *ND = rename::getNamedDeclAt(Context, Location))
+ Result.Actions.push_back(isLocalSymbol(ND, Context.getLangOpts())
+ ? RefactoringActionType::Rename_Local
+ : RefactoringActionType::Rename);
+
+ // FIXME: We can avoid checking if some actions can be initiated when they're
+ // not allowed in the current language mode.
+ RefactoringActionType Actions[] = {
+#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \
+ RefactoringActionType::Name,
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+ };
+
+ for (auto Action : Actions) {
+ auto Op = initiateRefactoringOperationAt(Location, SelectionRange, Context,
+ Action,
+ /*CreateOperation=*/true);
+ if (Op.Initiated) {
+ Result.Actions.push_back(Action);
+ if (Op.RefactoringOp) {
+ for (const auto &SubAction : Op.RefactoringOp->getAvailableSubActions())
+ Result.Actions.push_back(SubAction);
+ }
+ }
+ }
+
+ return Result;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/RefactoringActions.cpp b/lib/Tooling/Refactor/RefactoringActions.cpp
new file mode 100644
index 0000000..4b2b735
--- /dev/null
+++ b/lib/Tooling/Refactor/RefactoringActions.cpp
@@ -0,0 +1,30 @@
+//===--- RefactoringActions.cpp - Clang refactoring library ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Contains a list of all the supported refactoring actions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RefactoringActions.h"
+
+namespace clang {
+namespace tooling {
+
+StringRef getRefactoringActionTypeName(RefactoringActionType Action) {
+ switch (Action) {
+#define REFACTORING_ACTION(Name, Spelling) \
+ case RefactoringActionType::Name: \
+ return Spelling;
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+ }
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/RefactoringContinuations.h b/lib/Tooling/Refactor/RefactoringContinuations.h
new file mode 100644
index 0000000..ec2ee1b
--- /dev/null
+++ b/lib/Tooling/Refactor/RefactoringContinuations.h
@@ -0,0 +1,398 @@
+//===--- RefactoringContinuations.h - Defines refactoring continuations ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H
+
+#include "clang/AST/Decl.h"
+#include "clang/Tooling/Refactor/IndexerQuery.h"
+#include "clang/Tooling/Refactor/RefactoringOperation.h"
+#include "llvm/ADT/StringMap.h"
+#include <tuple>
+
+namespace clang {
+namespace tooling {
+
+namespace detail {
+
+struct ValidBase {};
+
+/// The ContinuationPassType determine which type is passed into the refactoring
+/// continuation.
+template <typename T> struct ContinuationPassType { using Type = T; };
+
+template <typename T> struct ContinuationPassType<std::vector<T>> {
+ using Type = ArrayRef<T>;
+};
+
+/// Refactoring operations can pass state to the continuations. Valid state
+/// values should have a corresponding \c StateTraits specialization.
+template <typename T> struct StateTraits {
+ /// Specializations should define the following types:
+ ///
+ /// StoredResultType: The TU-specific type which is then passed into the
+ /// continuation function. The continuation receives the result whose type is
+ /// \c ContinuationPassType<StoredResultType>::Type.
+ ///
+ /// PersistentType: The TU-independent type that's persisted even after the
+ /// TU in which the continuation was created is disposed.
+};
+
+template <typename T>
+struct StateTraits<const T *>
+ : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type {
+ using StoredResultType = const T *;
+ using PersistentType = PersistentDeclRef<T>;
+};
+
+template <typename T>
+struct StateTraits<ArrayRef<const T *>>
+ : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type {
+ using StoredResultType = std::vector<const T *>;
+ using PersistentType = std::vector<PersistentDeclRef<T>>;
+};
+
+template <typename T>
+struct StateTraits<std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>>>
+ : std::enable_if<std::is_base_of<Decl, T>::value, ValidBase>::type {
+ using StoredResultType = std::vector<indexer::Indexed<const T *>>;
+ using PersistentType = std::vector<indexer::Indexed<PersistentDeclRef<T>>>;
+};
+
+template <> struct StateTraits<std::vector<std::string>> {
+ using StoredResultType = std::vector<std::string>;
+ using PersistentType = std::vector<std::string>;
+};
+
+/// Conversion functions convert the TU-specific state to a TU independent
+/// state and vice-versa.
+template <typename T>
+PersistentDeclRef<T> convertToPersistentRepresentation(
+ const T *Declaration,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ return PersistentDeclRef<T>::create(Declaration);
+}
+
+template <typename T>
+std::vector<PersistentDeclRef<T>> convertToPersistentRepresentation(
+ ArrayRef<const T *> Declarations,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ std::vector<PersistentDeclRef<T>> Result;
+ Result.reserve(Declarations.size());
+ for (const T *D : Declarations)
+ Result.push_back(PersistentDeclRef<T>::create(D));
+ return Result;
+}
+
+template <typename T>
+std::vector<indexer::Indexed<PersistentDeclRef<T>>>
+convertToPersistentRepresentation(
+ std::unique_ptr<indexer::ManyToManyDeclarationsQuery<T>> &Query,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ Query->invalidateTUSpecificState();
+ return Query->getOutput();
+}
+
+inline std::vector<std::string>
+convertToPersistentRepresentation(const std::vector<std::string> &Values) {
+ return Values;
+}
+
+/// Converts the TU-independent state to the TU-specific state.
+class PersistentToASTSpecificStateConverter {
+ ASTContext &Context;
+ llvm::StringMap<const Decl *> ConvertedDeclRefs;
+
+ const Decl *lookupDecl(StringRef USR);
+
+public:
+ // FIXME: We can hide the addConvertible/convert interface so that
+ // the continuation will just invoke one conversion function for the entire
+ // tuple.
+ PersistentToASTSpecificStateConverter(ASTContext &Context)
+ : Context(Context) {}
+
+ template <typename T>
+ bool addConvertible(
+ const PersistentDeclRef<T> &Ref,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ if (!Ref.USR.empty())
+ ConvertedDeclRefs[Ref.USR] = nullptr;
+ return true;
+ }
+
+ template <typename T>
+ const T *
+ convert(const PersistentDeclRef<T> &Ref,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ return dyn_cast_or_null<T>(lookupDecl(Ref.USR));
+ }
+
+ template <typename T>
+ bool addConvertible(
+ const std::vector<PersistentDeclRef<T>> &Refs,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ for (const auto &Ref : Refs) {
+ if (!Ref.USR.empty())
+ ConvertedDeclRefs[Ref.USR] = nullptr;
+ }
+ return true;
+ }
+
+ template <typename T>
+ std::vector<const T *>
+ convert(const std::vector<PersistentDeclRef<T>> &Refs,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ std::vector<const T *> Results;
+ Results.reserve(Refs.size());
+ // Allow nulls in the produced array, the continuation will have to deal
+ // with them by itself.
+ for (const auto &Ref : Refs)
+ Results.push_back(dyn_cast_or_null<T>(lookupDecl(Ref.USR)));
+ return Results;
+ }
+
+ template <typename T>
+ bool addConvertible(
+ const std::vector<indexer::Indexed<PersistentDeclRef<T>>> &Refs,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ for (const auto &Ref : Refs) {
+ if (!Ref.Decl.USR.empty())
+ ConvertedDeclRefs[Ref.Decl.USR] = nullptr;
+ }
+ return true;
+ }
+
+ template <typename T>
+ std::vector<indexer::Indexed<const T *>>
+ convert(const std::vector<indexer::Indexed<PersistentDeclRef<T>>> &Refs,
+ typename std::enable_if<std::is_base_of<Decl, T>::value>::type * =
+ nullptr) {
+ std::vector<indexer::Indexed<const T *>> Results;
+ Results.reserve(Refs.size());
+ // Allow nulls in the produced array, the continuation will have to deal
+ // with them by itself.
+ for (const auto &Ref : Refs)
+ Results.push_back(indexer::Indexed<const T *>(
+ dyn_cast_or_null<T>(lookupDecl(Ref.Decl.USR)), Ref.IsNotDefined));
+ return Results;
+ }
+
+ bool addConvertible(const PersistentFileID &) {
+ // Do nothing since FileIDs are converted one-by-one.
+ return true;
+ }
+
+ FileID convert(const PersistentFileID &Ref);
+
+ bool addConvertible(const std::vector<std::string> &) { return true; }
+
+ std::vector<std::string> convert(const std::vector<std::string> &Values) {
+ return Values;
+ }
+
+ /// Converts the added persistent state into TU-specific state using one
+ /// efficient operation.
+ void runCoalescedConversions();
+};
+
+template <typename T, typename ASTQueryType, typename... QueryOrState>
+struct ContinuationFunction {
+ using Type = llvm::Expected<RefactoringResult> (*)(
+ ASTContext &, const T &,
+ typename ContinuationPassType<
+ typename StateTraits<QueryOrState>::StoredResultType>::Type...);
+
+ template <size_t... Is>
+ static llvm::Expected<RefactoringResult> dispatch(
+ Type Fn, detail::PersistentToASTSpecificStateConverter &Converter,
+ ASTContext &Context, const ASTQueryType &Query,
+ const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...>
+ &Arguments,
+ llvm::index_sequence<Is...>) {
+ auto ASTQueryResult = Converter.convert(Query.getResult());
+ return Fn(Context, ASTQueryResult, std::get<Is>(Arguments)...);
+ }
+};
+
+template <typename ASTQueryType, typename... QueryOrState>
+struct ContinuationFunction<void, ASTQueryType, QueryOrState...> {
+ using Type = llvm::Expected<RefactoringResult> (*)(
+ ASTContext &,
+ typename ContinuationPassType<
+ typename StateTraits<QueryOrState>::StoredResultType>::Type...);
+
+ template <size_t... Is>
+ static llvm::Expected<RefactoringResult> dispatch(
+ Type Fn, detail::PersistentToASTSpecificStateConverter &,
+ ASTContext &Context, const ASTQueryType &,
+ const std::tuple<typename StateTraits<QueryOrState>::StoredResultType...>
+ &Arguments,
+ llvm::index_sequence<Is...>) {
+ return Fn(Context, std::get<Is>(Arguments)...);
+ }
+};
+
+/// The refactoring contination contains a set of structures that implement
+/// the refactoring operation continuation mechanism.
+template <typename ASTQueryType, typename... QueryOrState>
+class SpecificRefactoringContinuation final : public RefactoringContinuation {
+public:
+ static_assert(std::is_base_of<indexer::ASTProducerQuery, ASTQueryType>::value,
+ "Invalid AST Query");
+ // TODO: Validate the QueryOrState types.
+
+ /// The consumer function is the actual continuation. It receives the state
+ /// that was passed-in in the request or the results of the indexing queries
+ /// that were passed-in in the request.
+ using ConsumerFn =
+ typename ContinuationFunction<typename ASTQueryType::ResultTy,
+ ASTQueryType, QueryOrState...>::Type;
+
+private:
+ ConsumerFn Consumer;
+ std::unique_ptr<ASTQueryType> ASTQuery;
+ /// Inputs store state that's dependent on the original TU.
+ llvm::Optional<std::tuple<QueryOrState...>> Inputs;
+ /// State contains TU-independent values.
+ llvm::Optional<
+ std::tuple<typename StateTraits<QueryOrState>::PersistentType...>>
+ State;
+
+ /// Converts a tuple that contains the TU dependent state to a tuple with
+ /// TU independent state.
+ template <size_t... Is>
+ std::tuple<typename StateTraits<QueryOrState>::PersistentType...>
+ convertToPersistentImpl(llvm::index_sequence<Is...>) {
+ assert(Inputs && "TU-dependent state is already converted");
+ return std::make_tuple(
+ detail::convertToPersistentRepresentation(std::get<Is>(*Inputs))...);
+ }
+
+ template <typename T>
+ bool gatherQueries(
+ std::vector<indexer::IndexerQuery *> &Queries,
+ const std::unique_ptr<T> &Query,
+ typename std::enable_if<
+ std::is_base_of<indexer::IndexerQuery, T>::value>::type * = nullptr) {
+ Queries.push_back(Query.get());
+ return true;
+ }
+
+ template <typename T>
+ bool gatherQueries(std::vector<indexer::IndexerQuery *> &, const T &) {
+ // This input element is not a query.
+ return true;
+ }
+
+ template <size_t... Is>
+ std::vector<indexer::IndexerQuery *>
+ gatherQueries(llvm::index_sequence<Is...>) {
+ assert(Inputs && "TU-dependent state is already converted");
+ std::vector<indexer::IndexerQuery *> Queries;
+ std::make_tuple(gatherQueries(Queries, std::get<Is>(*Inputs))...);
+ return Queries;
+ }
+
+ /// Calls the consumer function with the given \p Context and the state
+ /// whose values are converted from the TU-independent to TU-specific values.
+ template <size_t... Is>
+ llvm::Expected<RefactoringResult> dispatch(ASTContext &Context,
+ llvm::index_sequence<Is...> Seq) {
+ assert(State && "TU-independent state is not yet produced");
+ detail::PersistentToASTSpecificStateConverter Converter(Context);
+ (void)std::make_tuple(Converter.addConvertible(std::get<Is>(*State))...);
+ Converter.runCoalescedConversions();
+ auto Results = std::make_tuple(Converter.convert(std::get<Is>(*State))...);
+ // TODO: Check for errors?
+ return detail::ContinuationFunction<
+ typename ASTQueryType::ResultTy, ASTQueryType,
+ QueryOrState...>::dispatch(Consumer, Converter, Context, *ASTQuery,
+ Results, Seq);
+ }
+
+public:
+ SpecificRefactoringContinuation(ConsumerFn Consumer,
+ std::unique_ptr<ASTQueryType> ASTQuery,
+ QueryOrState... Inputs)
+ : Consumer(Consumer), ASTQuery(std::move(ASTQuery)),
+ Inputs(std::make_tuple(std::move(Inputs)...)) {}
+
+ SpecificRefactoringContinuation(SpecificRefactoringContinuation &&) = default;
+ SpecificRefactoringContinuation &
+ operator=(SpecificRefactoringContinuation &&) = default;
+
+ indexer::ASTProducerQuery *getASTUnitIndexerQuery() override {
+ return ASTQuery.get();
+ }
+
+ std::vector<indexer::IndexerQuery *> getAdditionalIndexerQueries() override {
+ return gatherQueries(llvm::index_sequence_for<QueryOrState...>());
+ }
+
+ /// Query results are fetched. State is converted to a persistent
+ /// representation.
+ void persistTUSpecificState() override {
+ ASTQuery->invalidateTUSpecificState();
+ State =
+ convertToPersistentImpl(llvm::index_sequence_for<QueryOrState...>());
+ Inputs = None;
+ }
+
+ /// The state is converted to the AST representation in the given ASTContext
+ /// and the continuation is dispatched.
+ llvm::Expected<RefactoringResult>
+ runInExternalASTUnit(ASTContext &Context) override {
+ return dispatch(Context, llvm::index_sequence_for<QueryOrState...>());
+ }
+};
+
+} // end namespace detail
+
+/// Returns a refactoring continuation that will run within the context of a
+/// single external AST unit.
+///
+/// The indexer determines which AST unit should receive the continuation by
+/// evaluation the AST query operation \p ASTQuery.
+///
+/// \param ASTQuery The query that will determine which AST unit should the
+/// continuation run in.
+///
+/// \param Consumer The continuation function that will be called once the
+/// external AST unit is loaded.
+///
+/// \param Inputs Each individiual input element can contain either some
+/// state value that will be passed into the \p Consumer function or an
+/// indexer query whose results will be passed into the \p Consumer function.
+template <typename ASTQueryType, typename... QueryOrState>
+typename std::enable_if<
+ std::is_base_of<indexer::ASTProducerQuery, ASTQueryType>::value,
+ std::unique_ptr<RefactoringContinuation>>::type
+continueInExternalASTUnit(
+ std::unique_ptr<ASTQueryType> ASTQuery,
+ typename detail::SpecificRefactoringContinuation<
+ ASTQueryType, QueryOrState...>::ConsumerFn Consumer,
+ QueryOrState... Inputs) {
+ return llvm::make_unique<
+ detail::SpecificRefactoringContinuation<ASTQueryType, QueryOrState...>>(
+ Consumer, std::move(ASTQuery), std::move(Inputs)...);
+}
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORING_CONTINUATIONS_H
diff --git a/lib/Tooling/Refactor/RefactoringOperation.cpp b/lib/Tooling/Refactor/RefactoringOperation.cpp
new file mode 100644
index 0000000..a9f431f
--- /dev/null
+++ b/lib/Tooling/Refactor/RefactoringOperation.cpp
@@ -0,0 +1,93 @@
+//===--- RefactoringOperation.cpp - Defines a refactoring operation -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RefactoringOperation.h"
+#include "ASTSlice.h"
+#include "RefactoringOperations.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Tooling/Refactor/SymbolOperation.h"
+#include "clang/Tooling/Refactor/USRFinder.h"
+#include "llvm/Support/Errc.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+char RefactoringOperationError::ID;
+
+void RefactoringOperationError::log(raw_ostream &OS) const {
+ OS << "Refactoring operation failed: " << FailureReason;
+}
+
+std::error_code RefactoringOperationError::convertToErrorCode() const {
+ return make_error_code(llvm::errc::operation_not_permitted);
+}
+
+RefactoringOperationResult clang::tooling::initiateRefactoringOperationAt(
+ SourceLocation Location, SourceRange SelectionRange, ASTContext &Context,
+ RefactoringActionType ActionType, bool CreateOperation) {
+ if (Location.isInvalid())
+ return None;
+ if (ActionType == RefactoringActionType::Rename ||
+ ActionType == RefactoringActionType::Rename_Local) {
+ const NamedDecl *FoundDecl = rename::getNamedDeclAt(Context, Location);
+ if (!FoundDecl)
+ return None;
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ if (CreateOperation)
+ Result.SymbolOp = llvm::make_unique<SymbolOperation>(FoundDecl, Context);
+ return Result;
+ }
+ SourceManager &SM = Context.getSourceManager();
+ if (Location.isMacroID())
+ Location = SM.getSpellingLoc(Location);
+ assert(Location.isFileID() && "Invalid location");
+
+ // TODO: Don't perform duplicate work when initiateRefactoringOperationAt is
+ // called from findRefactoringActionsAt.
+ if (SelectionRange.isValid()) {
+ if (SelectionRange.getBegin().isMacroID() ||
+ SelectionRange.getEnd().isMacroID())
+ SelectionRange = SourceRange(SM.getSpellingLoc(SelectionRange.getBegin()),
+ SM.getSpellingLoc(SelectionRange.getEnd()));
+ SelectionRange = trimSelectionRange(
+ SelectionRange, Context.getSourceManager(), Context.getLangOpts());
+ }
+ ASTSlice Slice(Location, SelectionRange, Context);
+
+ switch (ActionType) {
+#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \
+ case RefactoringActionType::Name: \
+ return initiate##Name##Operation(Slice, Context, Location, SelectionRange, \
+ CreateOperation);
+#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \
+ case RefactoringActionType::Parent##_##Name: \
+ return initiate##Parent##Name##Operation(Slice, Context, Location, \
+ SelectionRange, CreateOperation);
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+ default:
+ break;
+ }
+ return RefactoringOperationResult();
+}
+
+RefactoringOperationResult clang::tooling::initiateRefactoringOperationOnDecl(
+ StringRef DeclUSR, ASTContext &Context, RefactoringActionType ActionType) {
+ if (ActionType != RefactoringActionType::Rename)
+ return None;
+ const NamedDecl *FoundDecl = rename::getNamedDeclWithUSR(Context, DeclUSR);
+ if (!FoundDecl)
+ return None;
+ RefactoringOperationResult Result;
+ Result.Initiated = true;
+ Result.SymbolOp = llvm::make_unique<SymbolOperation>(FoundDecl, Context);
+ return Result;
+}
diff --git a/lib/Tooling/Refactor/RefactoringOperations.h b/lib/Tooling/Refactor/RefactoringOperations.h
new file mode 100644
index 0000000..e02a3cf
--- /dev/null
+++ b/lib/Tooling/Refactor/RefactoringOperations.h
@@ -0,0 +1,37 @@
+//===--- RefactoringOperations.h - The supported refactoring operations ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H
+
+#include "ASTSlice.h"
+#include "clang/Tooling/Refactor/RefactoringOperation.h"
+
+namespace clang {
+
+class Expr;
+class IfStmt;
+class VarDecl;
+
+namespace tooling {
+
+#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \
+ RefactoringOperationResult initiate##Name##Operation( \
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location, \
+ SourceRange SelectionRange, bool CreateOperation = true);
+#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \
+ RefactoringOperationResult initiate##Parent##Name##Operation( \
+ ASTSlice &Slice, ASTContext &Context, SourceLocation Location, \
+ SourceRange SelectionRange, bool CreateOperation = true);
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_REFACTORINGOPERATIONS_H
diff --git a/lib/Tooling/Refactor/RefactoringOptions.cpp b/lib/Tooling/Refactor/RefactoringOptions.cpp
new file mode 100644
index 0000000..fd2d8d1
--- /dev/null
+++ b/lib/Tooling/Refactor/RefactoringOptions.cpp
@@ -0,0 +1,65 @@
+//===--- RefactoringOptions.cpp - A set of all the refactoring options ----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RefactoringOptions.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::tooling::option;
+using namespace llvm::yaml;
+
+void RefactoringOptionSet::print(llvm::raw_ostream &OS) const {
+ Output YamlOut(OS);
+ if (YamlOut.preflightDocument(0)) {
+ YamlOut.beginFlowMapping();
+ for (const auto &Option : Options)
+ Option.getValue()->serialize(YamlOut);
+ YamlOut.endFlowMapping();
+ YamlOut.postflightDocument();
+ }
+}
+
+template <> struct CustomMappingTraits<RefactoringOptionSet> {
+ static void inputOne(IO &YamlIn, StringRef Key,
+ RefactoringOptionSet &Result) {
+#define HANDLE(Type) \
+ if (Key == Type::Name) { \
+ Type Value; \
+ Value.serialize(YamlIn); \
+ Result.add(Value); \
+ return; \
+ }
+ HANDLE(AvoidTextualMatches)
+#undef HANDLE
+ YamlIn.setError(Twine("Unknown refactoring option ") + Key);
+ }
+ static void output(IO &, RefactoringOptionSet &) {
+ llvm_unreachable("Output is done without mapping traits");
+ }
+};
+
+llvm::Expected<RefactoringOptionSet>
+RefactoringOptionSet::parse(StringRef Source) {
+ Input YamlIn(Source);
+ // FIXME: Don't dump errors to stderr.
+ RefactoringOptionSet Result;
+ YamlIn >> Result;
+ if (YamlIn.error())
+ return llvm::make_error<llvm::StringError>("Failed to parse the option set",
+ YamlIn.error());
+ return std::move(Result);
+}
+
+void RefactoringOption::serialize(const SerializationContext &) {}
+
+void clang::tooling::option::detail::BoolOptionBase::serializeImpl(
+ const SerializationContext &Context, const char *Name) {
+ Context.IO.mapRequired(Name, Value);
+}
diff --git a/lib/Tooling/Refactor/RenameIndexedFile.cpp b/lib/Tooling/Refactor/RenameIndexedFile.cpp
new file mode 100644
index 0000000..ef88e1f
--- /dev/null
+++ b/lib/Tooling/Refactor/RenameIndexedFile.cpp
@@ -0,0 +1,624 @@
+//===--- RenameIndexedFile.cpp - ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RenameIndexedFile.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/Refactor/RefactoringOptions.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang;
+
+namespace clang {
+namespace tooling {
+namespace rename {
+
+IndexedFileOccurrenceProducer::IndexedFileOccurrenceProducer(
+ ArrayRef<IndexedSymbol> Symbols, IndexedFileOccurrenceConsumer &Consumer,
+ const RefactoringOptionSet *Options)
+ : Symbols(Symbols), Consumer(Consumer), Options(Options) {
+ IsMultiPiece = false;
+ for (const auto &Symbol : Symbols) {
+ if (Symbol.Name.size() > 1) {
+ IsMultiPiece = true;
+ break;
+ }
+ }
+ if (IsMultiPiece) {
+ for (const auto &Symbol : Symbols) {
+ (void)Symbol;
+ assert(Symbol.Name.size() > 1 &&
+ "Mixed multi-piece and single piece symbols "
+ "are unsupported");
+ }
+ }
+}
+
+namespace {
+
+enum class MatchKind {
+ SourceMatch,
+ SourcePropSetterMatch,
+ MacroExpansion,
+ None
+};
+
+} // end anonymous namespace
+
+static bool isSetterNameEqualToPropName(StringRef SetterName,
+ StringRef PropertyName) {
+ assert(SetterName.startswith("set") && "invalid setter name");
+ SetterName = SetterName.drop_front(3);
+ return SetterName[0] == toUppercase(PropertyName[0]) &&
+ SetterName.drop_front() == PropertyName.drop_front();
+}
+
+static MatchKind checkOccurrence(const IndexedOccurrence &Occurrence,
+ const IndexedSymbol &Symbol,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ SourceRange &SymbolRange,
+ bool AllowObjCSetterProp = false) {
+ if (!Occurrence.Line || !Occurrence.Column)
+ return MatchKind::None; // Ignore any invalid indexed locations.
+
+ // Ensure that the first string in the name is present at the given
+ // location.
+ SourceLocation BeginLoc = SM.translateLineCol(
+ SM.getMainFileID(), Occurrence.Line, Occurrence.Column);
+ if (BeginLoc.isInvalid())
+ return MatchKind::None;
+ StringRef SymbolNameStart = Symbol.Name[0];
+ // Extract the token at the location.
+ auto DecomposedLoc = SM.getDecomposedLoc(BeginLoc);
+ const llvm::MemoryBuffer *File = SM.getBuffer(DecomposedLoc.first);
+ Lexer RawLex(
+ BeginLoc, LangOpts, File->getBufferStart() + DecomposedLoc.second,
+ File->getBufferStart() + DecomposedLoc.second, File->getBufferEnd());
+ Token Tok;
+ RawLex.LexFromRawLexer(Tok);
+ if (Tok.isNot(tok::raw_identifier) || Tok.getLocation() != BeginLoc) {
+ if (SymbolNameStart.empty() && Tok.is(tok::colon) &&
+ Tok.getLocation() == BeginLoc) {
+ // Must be the location of an empty Objective-C selector piece.
+ SymbolRange = SourceRange(BeginLoc, BeginLoc);
+ return MatchKind::SourceMatch;
+ }
+ // FIXME: Handle empty selector piece in a macro?
+ return MatchKind::None;
+ }
+ SymbolRange = SourceRange(BeginLoc, Tok.getEndLoc());
+ if (Tok.getRawIdentifier() == SymbolNameStart)
+ return MatchKind::SourceMatch;
+ // Match 'prop' when looking for 'setProp'.
+ // FIXME: Verify that the previous token is a '.' to be sure.
+ if (AllowObjCSetterProp &&
+ Occurrence.Kind == IndexedOccurrence::IndexedObjCMessageSend &&
+ SymbolNameStart.startswith("set") &&
+ isSetterNameEqualToPropName(SymbolNameStart, Tok.getRawIdentifier()))
+ return MatchKind::SourcePropSetterMatch;
+ return MatchKind::MacroExpansion;
+}
+
+static void
+findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI,
+ ArrayRef<IndexedSymbol> Symbols,
+ IndexedFileOccurrenceConsumer &Consumer);
+
+namespace {
+
+struct TextualMatchOccurrence {
+ SourceLocation Location;
+ unsigned SymbolIndex;
+};
+
+/// Finds '@selector' expressions by looking at tokens one-by-one.
+class SelectorParser {
+ enum ParseState {
+ None,
+ At,
+ Selector,
+ ExpectingSelectorPiece,
+ ExpectingColon,
+ ExpectingRParenOrColon,
+ ExpectingRParen,
+ Success
+ };
+ ParseState State = None;
+ const SymbolName &Name;
+
+ ParseState stateForToken(const Token &RawTok);
+
+public:
+ unsigned SymbolIndex;
+ llvm::SmallVector<SourceLocation, 8> SelectorLocations;
+
+ SelectorParser(const SymbolName &Name, unsigned SymbolIndex)
+ : Name(Name), SymbolIndex(SymbolIndex) {}
+
+ /// Returns true if the parses has found a '@selector' expression.
+ bool handleToken(const Token &RawTok);
+};
+
+class InclusionLexer final : public Lexer {
+public:
+ InclusionLexer(SourceLocation FileLoc, const LangOptions &LangOpts,
+ const char *BufStart, const char *BufEnd)
+ : Lexer(FileLoc, LangOpts, BufStart, BufStart, BufEnd) {}
+
+ void IndirectLex(Token &Result) override { LexFromRawLexer(Result); }
+};
+
+} // end anonymous namespace
+
+SelectorParser::ParseState SelectorParser::stateForToken(const Token &RawTok) {
+ assert(RawTok.isNot(tok::comment) && "unexpected comment token");
+ switch (State) {
+ case None:
+ break;
+ case At:
+ if (RawTok.is(tok::raw_identifier) &&
+ RawTok.getRawIdentifier() == "selector")
+ return Selector;
+ break;
+ case Selector:
+ if (RawTok.isNot(tok::l_paren))
+ break;
+ SelectorLocations.clear();
+ return ExpectingSelectorPiece;
+ case ExpectingSelectorPiece: {
+ assert(SelectorLocations.size() < Name.size() &&
+ "Expecting invalid selector piece");
+ StringRef NamePiece = Name[SelectorLocations.size()];
+ if ((RawTok.isNot(tok::raw_identifier) ||
+ RawTok.getRawIdentifier() != NamePiece) &&
+ !(NamePiece.empty() && RawTok.is(tok::colon))) {
+ break;
+ }
+ SelectorLocations.push_back(RawTok.getLocation());
+ if (SelectorLocations.size() == Name.size()) {
+ // We found the selector that we were looking for, now check for ')'.
+ return NamePiece.empty() ? ExpectingRParen : ExpectingRParenOrColon;
+ }
+ return NamePiece.empty() ? ExpectingSelectorPiece : ExpectingColon;
+ }
+ case ExpectingColon:
+ if (RawTok.is(tok::colon))
+ return ExpectingSelectorPiece;
+ break;
+ case ExpectingRParenOrColon:
+ if (RawTok.is(tok::colon))
+ return ExpectingRParen;
+ // Fallthrough
+ case ExpectingRParen:
+ if (RawTok.is(tok::r_paren)) {
+ // We found the selector that we were looking for.
+ return Success;
+ }
+ break;
+ case Success:
+ llvm_unreachable("should not get here");
+ }
+ // Look for the start of the selector expression.
+ return RawTok.is(tok::at) ? At : None;
+}
+
+bool SelectorParser::handleToken(const Token &RawTok) {
+ if (RawTok.is(tok::coloncolon)) {
+ // Split the '::' into two ':'.
+ Token T(RawTok);
+ T.setKind(tok::colon);
+ T.setLength(1);
+ handleToken(T);
+ T.setLocation(T.getLocation().getLocWithOffset(1));
+ return handleToken(T);
+ }
+ State = stateForToken(RawTok);
+ if (State != Success)
+ return false;
+ State = None;
+ return true;
+}
+
+static void collectTextualMatchesInComment(
+ ArrayRef<IndexedSymbol> Symbols, SourceLocation CommentLoc,
+ StringRef Comment, llvm::SmallVectorImpl<TextualMatchOccurrence> &Result) {
+ for (const auto &Symbol : llvm::enumerate(Symbols)) {
+ const SymbolName &Name = Symbol.value().Name;
+ if (Name.containsEmptyPiece()) // Ignore Objective-C selectors with empty
+ // pieces.
+ continue;
+ size_t Offset = 0;
+ while (true) {
+ Offset = Comment.find(Name[0], /*From=*/Offset);
+ if (Offset == StringRef::npos)
+ break;
+ Result.push_back(
+ {CommentLoc.getLocWithOffset(Offset), (unsigned)Symbol.index()});
+ Offset += Name[0].size();
+ }
+ }
+}
+
+/// Lex the comment to figure out if textual matches in a comment are standalone
+/// tokens.
+static void findTextualMatchesInComment(
+ const SourceManager &SM, const LangOptions &LangOpts,
+ ArrayRef<IndexedSymbol> Symbols,
+ ArrayRef<TextualMatchOccurrence> TextualMatches, SourceRange CommentRange,
+ llvm::function_ref<void(SymbolOccurrence::OccurrenceKind,
+ ArrayRef<SourceLocation> Locations,
+ unsigned SymbolIndex)>
+ MatchHandler) {
+ std::string Source =
+ Lexer::getSourceText(CharSourceRange::getCharRange(CommentRange), SM,
+ LangOpts)
+ .str();
+ SymbolOccurrence::OccurrenceKind Kind =
+ RawComment(SM, CommentRange, /*Merged=*/false, /*ParseAllComments=*/false)
+ .isDocumentation()
+ ? SymbolOccurrence::MatchingDocComment
+ : SymbolOccurrence::MatchingComment;
+ // Replace some special characters with ' ' to avoid comments and literals.
+ std::replace_if(
+ Source.begin(), Source.end(),
+ [](char c) -> bool { return c == '/' || c == '"' || c == '\''; }, ' ');
+ Lexer RawLex(CommentRange.getBegin(), LangOpts, Source.c_str(),
+ Source.c_str(), Source.c_str() + Source.size());
+ Token RawTok;
+ RawLex.LexFromRawLexer(RawTok);
+ while (RawTok.isNot(tok::eof)) {
+ auto It = std::find_if(TextualMatches.begin(), TextualMatches.end(),
+ [&](const TextualMatchOccurrence &Match) {
+ return Match.Location == RawTok.getLocation();
+ });
+ if (It != TextualMatches.end()) {
+ StringRef TokenName =
+ Lexer::getSourceText(CharSourceRange::getCharRange(
+ RawTok.getLocation(), RawTok.getEndLoc()),
+ SM, LangOpts);
+ // Only report matches that are identical to the symbol. When dealing with
+ // multi-piece selectors we only look for the first selector piece as we
+ // assume that textual matches correspond to a match of the first selector
+ // piece.
+ if (TokenName == Symbols[It->SymbolIndex].Name[0])
+ MatchHandler(Kind, It->Location, It->SymbolIndex);
+ }
+ RawLex.LexFromRawLexer(RawTok);
+ }
+}
+
+static void findMatchingTextualOccurrences(
+ const SourceManager &SM, const LangOptions &LangOpts,
+ ArrayRef<IndexedSymbol> Symbols,
+ llvm::function_ref<void(SymbolOccurrence::OccurrenceKind,
+ ArrayRef<SourceLocation> Locations,
+ unsigned SymbolIndex)>
+ MatchHandler) {
+ const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID());
+ Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts);
+ RawLex.SetCommentRetentionState(true);
+
+ llvm::SmallVector<TextualMatchOccurrence, 4> CommentMatches;
+ llvm::SmallVector<SelectorParser, 2> SelectorParsers;
+ for (const auto &Symbol : llvm::enumerate(Symbols)) {
+ if (Symbol.value().IsObjCSelector)
+ SelectorParsers.push_back(
+ SelectorParser(Symbol.value().Name, Symbol.index()));
+ }
+
+ Token RawTok;
+ RawLex.LexFromRawLexer(RawTok);
+ while (RawTok.isNot(tok::eof)) {
+ if (RawTok.is(tok::comment)) {
+ SourceRange Range(RawTok.getLocation(), RawTok.getEndLoc());
+ StringRef Comment = Lexer::getSourceText(
+ CharSourceRange::getCharRange(Range), SM, LangOpts);
+ collectTextualMatchesInComment(Symbols, Range.getBegin(), Comment,
+ CommentMatches);
+ if (!CommentMatches.empty()) {
+ findTextualMatchesInComment(SM, LangOpts, Symbols, CommentMatches,
+ Range, MatchHandler);
+ CommentMatches.clear();
+ }
+ } else if (!SelectorParsers.empty()) {
+ for (auto &Parser : SelectorParsers) {
+ if (Parser.handleToken(RawTok))
+ MatchHandler(SymbolOccurrence::MatchingSelector,
+ Parser.SelectorLocations, Parser.SymbolIndex);
+ }
+ }
+ RawLex.LexFromRawLexer(RawTok);
+ }
+}
+
+static void findInclusionDirectiveOccurrence(
+ const IndexedOccurrence &Occurrence, const IndexedSymbol &Symbol,
+ unsigned SymbolIndex, SourceManager &SM, const LangOptions &LangOpts,
+ IndexedFileOccurrenceConsumer &Consumer) {
+ if (!Occurrence.Line || !Occurrence.Column)
+ return; // Ignore any invalid indexed locations.
+
+ SourceLocation Loc = SM.translateLineCol(SM.getMainFileID(), Occurrence.Line,
+ Occurrence.Column);
+ if (Loc.isInvalid())
+ return;
+ unsigned Offset = SM.getDecomposedLoc(Loc).second;
+ const llvm::MemoryBuffer *File = SM.getBuffer(SM.getMainFileID());
+
+ InclusionLexer RawLex(Loc, LangOpts, File->getBufferStart() + Offset,
+ File->getBufferEnd());
+ Token RawTok;
+ RawLex.LexFromRawLexer(RawTok);
+ if (RawTok.isNot(tok::hash))
+ return;
+ // include/import
+ RawLex.LexFromRawLexer(RawTok);
+ if (RawTok.isNot(tok::raw_identifier))
+ return;
+ // string literal/angled literal.
+ RawLex.setParsingPreprocessorDirective(true);
+ RawLex.LexIncludeFilename(RawTok);
+ if (RawTok.isNot(tok::string_literal) &&
+ RawTok.isNot(tok::angle_string_literal))
+ return;
+ StringRef Filename = llvm::sys::path::filename(
+ StringRef(RawTok.getLiteralData(), RawTok.getLength())
+ .drop_front()
+ .drop_back());
+ size_t NameOffset = Filename.rfind_lower(Symbol.Name[0]);
+ if (NameOffset == StringRef::npos)
+ return;
+ SymbolOccurrence Result(
+ SymbolOccurrence::MatchingFilename,
+ /*IsMacroExpansion=*/false, SymbolIndex,
+ RawTok.getLocation().getLocWithOffset(
+ NameOffset + (Filename.data() - RawTok.getLiteralData())));
+ Consumer.handleOccurrence(Result, SM, LangOpts);
+}
+
+void IndexedFileOccurrenceProducer::ExecuteAction() {
+ Preprocessor &PP = getCompilerInstance().getPreprocessor();
+ PP.EnterMainSourceFile();
+
+ SourceManager &SM = getCompilerInstance().getSourceManager();
+ const LangOptions &LangOpts = getCompilerInstance().getLangOpts();
+ if (IsMultiPiece) {
+ findObjCMultiPieceSelectorOccurrences(getCompilerInstance(), Symbols,
+ Consumer);
+ } else {
+ for (const auto &Symbol : llvm::enumerate(Symbols)) {
+ for (const IndexedOccurrence &Occurrence :
+ Symbol.value().IndexedOccurrences) {
+ if (Occurrence.Kind == IndexedOccurrence::InclusionDirective) {
+ findInclusionDirectiveOccurrence(Occurrence, Symbol.value(),
+ Symbol.index(), SM, LangOpts,
+ Consumer);
+ continue;
+ }
+ SourceRange SymbolRange;
+ MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM,
+ LangOpts, SymbolRange,
+ /*AllowObjCSetterProp=*/true);
+ if (Match == MatchKind::None)
+ continue;
+ llvm::SmallVector<SourceLocation, 2> Locs;
+ Locs.push_back(SymbolRange.getBegin());
+ bool IsImpProp = Match == MatchKind::SourcePropSetterMatch;
+ if (IsImpProp)
+ Locs.push_back(SymbolRange.getEnd());
+ SymbolOccurrence Result(
+ IsImpProp ? SymbolOccurrence::MatchingImplicitProperty
+ : SymbolOccurrence::MatchingSymbol,
+ /*IsMacroExpansion=*/Match == MatchKind::MacroExpansion,
+ Symbol.index(), Locs);
+ Consumer.handleOccurrence(Result, SM, LangOpts);
+ }
+ }
+ }
+
+ if (Options && Options->get(option::AvoidTextualMatches()))
+ return;
+ findMatchingTextualOccurrences(
+ SM, LangOpts, Symbols,
+ [&](SymbolOccurrence::OccurrenceKind Kind,
+ ArrayRef<SourceLocation> Locations, unsigned SymbolIndex) {
+ SymbolOccurrence Result(Kind, /*IsMacroExpansion=*/false, SymbolIndex,
+ Locations);
+ Consumer.handleOccurrence(Result, SM, LangOpts);
+ });
+}
+
+namespace {
+
+/// Maps from source locations to the indexed occurrences.
+typedef llvm::DenseMap<unsigned, std::pair<IndexedOccurrence, unsigned>>
+ SourceLocationsToIndexedOccurrences;
+
+enum class ObjCSymbolSelectorKind { MessageSend, MethodDecl };
+
+} // end anonymous namespace
+
+static bool isMatchingSelectorName(const Token &Tok, const Token &Next,
+ StringRef NamePiece) {
+ if (NamePiece.empty())
+ return Tok.is(tok::colon);
+ return Tok.is(tok::raw_identifier) && Next.is(tok::colon) &&
+ Tok.getRawIdentifier() == NamePiece;
+}
+
+static bool
+findObjCSymbolSelectorPieces(ArrayRef<Token> Tokens, const SymbolName &Name,
+ SmallVectorImpl<SourceLocation> &Pieces,
+ ObjCSymbolSelectorKind Kind) {
+ assert(!Tokens.empty() && "no tokens");
+ assert(Name[0].empty() || Tokens[0].getRawIdentifier() == Name[0]);
+ assert(Name.size() > 1);
+ assert(Pieces.empty());
+
+ Pieces.push_back(Tokens[0].getLocation());
+
+ // We have to track square brackets, parens and braces as we want to skip the
+ // tokens inside them. This ensures that we don't use identical selector
+ // pieces in inner message sends, blocks, lambdas and @selector expressions.
+ unsigned SquareCount = 0;
+ unsigned ParenCount = 0;
+ unsigned BraceCount = 0;
+
+ // Start looking for the next selector piece.
+ unsigned Last = Tokens.size() - 1;
+ // Skip the ':' or any other token after the first selector piece token.
+ for (unsigned Index = Name[0].empty() ? 1 : 2; Index < Last; ++Index) {
+ const auto &Tok = Tokens[Index];
+
+ bool NoScoping = SquareCount == 0 && BraceCount == 0 && ParenCount == 0;
+ if (NoScoping &&
+ isMatchingSelectorName(Tok, Tokens[Index + 1], Name[Pieces.size()])) {
+ if (!Name[Pieces.size()].empty()) {
+ // Skip the ':' after the name. This ensures that it won't match a
+ // follow-up selector piece with an empty name.
+ ++Index;
+ }
+ Pieces.push_back(Tok.getLocation());
+ // All the selector pieces have been found.
+ if (Pieces.size() == Name.size())
+ return true;
+ } else if (Tok.is(tok::r_square)) {
+ // Stop scanning at the end of the message send.
+ // Also account for spurious ']' in blocks or lambdas.
+ if (Kind == ObjCSymbolSelectorKind::MessageSend && !SquareCount &&
+ !BraceCount)
+ break;
+ if (SquareCount)
+ --SquareCount;
+ } else if (Tok.is(tok::l_square))
+ ++SquareCount;
+ else if (Tok.is(tok::l_paren))
+ ++ParenCount;
+ else if (Tok.is(tok::r_paren)) {
+ if (!ParenCount)
+ break;
+ --ParenCount;
+ } else if (Tok.is(tok::l_brace)) {
+ // Stop scanning at the start of the of the method's body.
+ // Also account for any spurious blocks inside argument parameter types
+ // or parameter attributes.
+ if (Kind == ObjCSymbolSelectorKind::MethodDecl && !BraceCount &&
+ !ParenCount)
+ break;
+ ++BraceCount;
+ } else if (Tok.is(tok::r_brace)) {
+ if (!BraceCount)
+ break;
+ --BraceCount;
+ }
+ // Stop scanning at the end of the method's declaration.
+ if (Kind == ObjCSymbolSelectorKind::MethodDecl && NoScoping &&
+ (Tok.is(tok::semi) || Tok.is(tok::minus) || Tok.is(tok::plus)))
+ break;
+ }
+ return false;
+}
+
+// Scan the file and find multi-piece selector occurrences in a token stream.
+static void
+findObjCMultiPieceSelectorOccurrences(CompilerInstance &CI,
+ ArrayRef<IndexedSymbol> Symbols,
+ IndexedFileOccurrenceConsumer &Consumer) {
+ for (const auto &Symbol : Symbols) {
+ (void)Symbol;
+ assert(Symbol.Name.size() > 1 && "Not a multi-piece symbol!");
+ }
+
+ SourceManager &SM = CI.getSourceManager();
+ const LangOptions &LangOpts = CI.getLangOpts();
+ // Create a mapping from source locations to the indexed occurrences.
+ SourceLocationsToIndexedOccurrences MappedIndexedOccurrences;
+ for (const auto &Symbol : llvm::enumerate(Symbols)) {
+ for (const IndexedOccurrence &Occurrence :
+ Symbol.value().IndexedOccurrences) {
+ // Selectors and names in #includes shouldn't really mix.
+ if (Occurrence.Kind == IndexedOccurrence::InclusionDirective)
+ continue;
+ SourceRange SymbolRange;
+ MatchKind Match = checkOccurrence(Occurrence, Symbol.value(), SM,
+ LangOpts, SymbolRange);
+ if (Match == MatchKind::None)
+ continue;
+ SourceLocation Loc = SymbolRange.getBegin();
+ if (Match == MatchKind::MacroExpansion) {
+ SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol,
+ /*IsMacroExpansion=*/true, Symbol.index(), Loc);
+ Consumer.handleOccurrence(Result, SM, LangOpts);
+ continue;
+ }
+ MappedIndexedOccurrences.try_emplace(Loc.getRawEncoding(), Occurrence,
+ Symbol.index());
+ }
+ }
+
+ // Lex the file and look for tokens.
+ // Start lexing the specified input file.
+ const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID());
+ Lexer RawLex(SM.getMainFileID(), FromFile, SM, LangOpts);
+
+ std::vector<Token> Tokens;
+ bool SaveTokens = false;
+ Token RawTok;
+ RawLex.LexFromRawLexer(RawTok);
+ while (RawTok.isNot(tok::eof)) {
+ // Start saving tokens only when we've got a match
+ if (!SaveTokens) {
+ if (MappedIndexedOccurrences.find(
+ RawTok.getLocation().getRawEncoding()) !=
+ MappedIndexedOccurrences.end())
+ SaveTokens = true;
+ }
+ if (SaveTokens)
+ Tokens.push_back(RawTok);
+ RawLex.LexFromRawLexer(RawTok);
+ }
+
+ for (const auto &I : llvm::enumerate(Tokens)) {
+ const auto &Tok = I.value();
+ auto It = MappedIndexedOccurrences.find(Tok.getLocation().getRawEncoding());
+ if (It == MappedIndexedOccurrences.end())
+ continue;
+ unsigned SymbolIndex = It->second.second;
+ if (Tok.getKind() != tok::raw_identifier &&
+ !(Symbols[SymbolIndex].Name[0].empty() && Tok.is(tok::colon)))
+ continue;
+ const IndexedOccurrence &Occurrence = It->second.first;
+
+ // Scan the source for the remaining selector pieces.
+ SmallVector<SourceLocation, 4> SelectorPieces;
+ ObjCSymbolSelectorKind Kind =
+ Occurrence.Kind == IndexedOccurrence::IndexedObjCMessageSend
+ ? ObjCSymbolSelectorKind::MessageSend
+ : ObjCSymbolSelectorKind::MethodDecl;
+ if (findObjCSymbolSelectorPieces(
+ llvm::makeArrayRef(Tokens).drop_front(I.index()),
+ Symbols[SymbolIndex].Name, SelectorPieces, Kind)) {
+ SymbolOccurrence Result(SymbolOccurrence::MatchingSymbol,
+ /*IsMacroExpansion=*/false, SymbolIndex,
+ std::move(SelectorPieces));
+ Consumer.handleOccurrence(Result, SM, LangOpts);
+ }
+ }
+}
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/RenamedSymbol.cpp b/lib/Tooling/Refactor/RenamedSymbol.cpp
new file mode 100644
index 0000000..16ec01f
--- /dev/null
+++ b/lib/Tooling/Refactor/RenamedSymbol.cpp
@@ -0,0 +1,41 @@
+//===--- RenamedSymbol.cpp - ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RenamedSymbol.h"
+#include "clang/AST/DeclObjC.h"
+#include <algorithm>
+
+using namespace clang;
+
+namespace clang {
+namespace tooling {
+namespace rename {
+
+Symbol::Symbol(const NamedDecl *FoundDecl, unsigned SymbolIndex,
+ const LangOptions &LangOpts)
+ : Name(FoundDecl->getNameAsString(), LangOpts), SymbolIndex(SymbolIndex),
+ FoundDecl(FoundDecl) {
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(FoundDecl))
+ ObjCSelector = MD->getSelector();
+}
+
+bool operator<(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS) {
+ assert(!LHS.Locations.empty() && !RHS.Locations.empty());
+ return LHS.Locations[0] < RHS.Locations[0];
+}
+
+bool operator==(const SymbolOccurrence &LHS, const SymbolOccurrence &RHS) {
+ return LHS.Kind == RHS.Kind && LHS.SymbolIndex == RHS.SymbolIndex &&
+ std::equal(LHS.Locations.begin(), LHS.Locations.end(),
+ RHS.Locations.begin());
+}
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/RenamingOperation.cpp b/lib/Tooling/Refactor/RenamingOperation.cpp
new file mode 100644
index 0000000..80aa1bc
--- /dev/null
+++ b/lib/Tooling/Refactor/RenamingOperation.cpp
@@ -0,0 +1,98 @@
+//===--- RenamingOperation.cpp - ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/RenamingOperation.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactor/SymbolOperation.h"
+
+using namespace clang;
+
+/// \brief Lexes the given name string.
+///
+/// \return False if the name was consumed fully, true otherwise.
+static bool lexNameString(StringRef Name, Token &Result,
+ const LangOptions &LangOpts) {
+ Lexer Lex(SourceLocation(), LangOpts, Name.data(), Name.data(),
+ Name.data() + Name.size());
+ return !Lex.LexFromRawLexer(Result);
+}
+
+namespace clang {
+namespace tooling {
+namespace rename {
+
+bool isNewNameValid(const SymbolName &NewName, bool IsSymbolObjCSelector,
+ IdentifierTable &IDs, const LangOptions &LangOpts) {
+ Token Tok;
+ if (IsSymbolObjCSelector) {
+ // Check if the name is a valid selector.
+ for (const auto &Name : NewName.strings()) {
+ // Lex the name and verify that it was fully consumed. Then make sure that
+ // it's a valid identifier.
+ if (lexNameString(Name, Tok, LangOpts) || !Tok.isAnyIdentifier())
+ return false;
+ }
+ return true;
+ }
+
+ for (const auto &Name : NewName.strings()) {
+ // Lex the name and verify that it was fully consumed. Then make sure that
+ // it's a valid identifier that's also not a language keyword.
+ if (lexNameString(Name, Tok, LangOpts) || !Tok.isAnyIdentifier() ||
+ !tok::isAnyIdentifier(IDs.get(Name).getTokenID()))
+ return false;
+ }
+ return true;
+}
+
+bool isNewNameValid(const SymbolName &NewName, const SymbolOperation &Operation,
+ IdentifierTable &IDs, const LangOptions &LangOpts) {
+ assert(!Operation.symbols().empty());
+ return isNewNameValid(NewName,
+ Operation.symbols().front().ObjCSelector.hasValue(),
+ IDs, LangOpts);
+}
+
+void determineNewNames(SymbolName NewName, const SymbolOperation &Operation,
+ SmallVectorImpl<SymbolName> &NewNames,
+ const LangOptions &LangOpts) {
+ auto Symbols = Operation.symbols();
+ assert(!Symbols.empty());
+ NewNames.push_back(std::move(NewName));
+ if (const auto *PropertyDecl =
+ dyn_cast<ObjCPropertyDecl>(Symbols.front().FoundDecl)) {
+ assert(NewNames.front().size() == 1 &&
+ "Property's name should have one string only");
+ StringRef PropertyName = NewNames.front()[0];
+ Symbols = Symbols.drop_front();
+
+ auto AddName = [&](const NamedDecl *D, StringRef Name) {
+ assert(Symbols.front().FoundDecl == D && "decl is missing");
+ NewNames.push_back(SymbolName(Name, LangOpts));
+ Symbols = Symbols.drop_front();
+ };
+
+ if (!PropertyDecl->hasExplicitGetterName()) {
+ if (const auto *Getter = PropertyDecl->getGetterMethodDecl())
+ AddName(Getter, PropertyName);
+ }
+ if (!PropertyDecl->hasExplicitSetterName()) {
+ if (const auto *Setter = PropertyDecl->getSetterMethodDecl()) {
+ auto SetterName = SelectorTable::constructSetterName(PropertyName);
+ AddName(Setter, SetterName);
+ }
+ }
+ }
+}
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/SourceLocationUtilities.cpp b/lib/Tooling/Refactor/SourceLocationUtilities.cpp
new file mode 100644
index 0000000..0a0c4cd
--- /dev/null
+++ b/lib/Tooling/Refactor/SourceLocationUtilities.cpp
@@ -0,0 +1,260 @@
+//===--- SourceLocationUtilities.cpp - Source location helper functions ---===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SourceLocationUtilities.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Lex/Lexer.h"
+#include <limits>
+
+namespace clang {
+namespace tooling {
+
+SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd,
+ const Stmt *Body,
+ const SourceManager &SM) {
+ SourceLocation BodyStart = SM.getSpellingLoc(Body->getLocStart());
+ unsigned BodyLine = SM.getSpellingLineNumber(BodyStart);
+ unsigned HeaderLine = SM.getSpellingLineNumber(HeaderEnd);
+
+ if (BodyLine > HeaderLine) {
+ // The Last location on the previous line if the body is not on the same
+ // line as the last known location.
+ SourceLocation LineLocThatPrecedesBody =
+ SM.translateLineCol(SM.getFileID(BodyStart), BodyLine - 1,
+ std::numeric_limits<unsigned>::max());
+ if (LineLocThatPrecedesBody.isValid())
+ return LineLocThatPrecedesBody;
+ }
+ // We want to include the location of the '{'.
+ return isa<CompoundStmt>(Body) ? BodyStart : BodyStart.getLocWithOffset(-1);
+}
+
+SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart,
+ const Stmt *PreviousBody,
+ const SourceManager &SM) {
+ if (!isa<CompoundStmt>(PreviousBody))
+ return HeaderStart;
+ SourceLocation BodyEnd = SM.getSpellingLoc(PreviousBody->getLocEnd());
+ unsigned BodyLine = SM.getSpellingLineNumber(BodyEnd);
+ unsigned HeaderLine = SM.getSpellingLineNumber(HeaderStart);
+ if (BodyLine >= HeaderLine)
+ return BodyEnd;
+ return HeaderStart;
+}
+
+bool isLocationInAnyRange(SourceLocation Location, ArrayRef<SourceRange> Ranges,
+ const SourceManager &SM) {
+ for (const SourceRange &Range : Ranges) {
+ if (!isPointWithin(Location, Range.getBegin(), Range.getEnd(), SM))
+ continue;
+ return true;
+ }
+ return false;
+}
+
+SourceLocation getPreciseTokenLocEnd(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ return Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
+}
+
+SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ return Lexer::findLocationAfterToken(
+ LastKnownLoc, tok::r_paren, SM, LangOpts,
+ /*SkipTrailingWhitespaceAndNewLine=*/false);
+}
+
+SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation NextLoc =
+ Lexer::findLocationAfterToken(Loc, Kind, SM, LangOpts,
+ /*SkipTrailingWhitespaceAndNewLine=*/false);
+ if (NextLoc.isInvalid())
+ return SourceRange();
+ return SourceRange(
+ Lexer::GetBeginningOfToken(NextLoc.getLocWithOffset(-1), SM, LangOpts),
+ NextLoc);
+}
+
+SourceLocation findLastNonCompoundLocation(const Stmt *S) {
+ const auto *CS = dyn_cast<CompoundStmt>(S);
+ if (!CS)
+ return S->getLocEnd();
+ return CS->body_back() ? CS->body_back()->getLocEnd() : SourceLocation();
+}
+
+bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,
+ const SourceManager &SM) {
+ return !Loc1.isMacroID() && !Loc2.isMacroID() &&
+ SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2);
+}
+
+SourceLocation
+getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ assert(!SpellingLoc.isMacroID() && "Expecting a spelling location");
+ SourceLocation NextTokenLoc =
+ Lexer::findNextTokenLocationAfterTokenAt(SpellingLoc, SM, LangOpts);
+ if (NextTokenLoc.isValid()) {
+ bool IsSameLine = areOnSameLine(SpellingLoc, NextTokenLoc, SM);
+ if (IsSameLine) {
+ // Could be a ';' on the same line, so try looking after the ';'
+ if (isSemicolonAtLocation(NextTokenLoc, SM, LangOpts))
+ return getLastLineLocationUnlessItHasOtherTokens(NextTokenLoc, SM,
+ LangOpts);
+ } else {
+ SourceLocation LastLoc = SM.translateLineCol(
+ SM.getFileID(SpellingLoc), SM.getSpellingLineNumber(SpellingLoc),
+ std::numeric_limits<unsigned>::max());
+ if (LastLoc.isValid())
+ return LastLoc;
+ }
+ }
+ return getPreciseTokenLocEnd(SpellingLoc, SM, LangOpts);
+}
+
+bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ return Lexer::getSourceText(
+ CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM,
+ LangOpts) == ";";
+}
+
+SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ bool IsInvalid = false;
+ StringRef Text = Lexer::getSourceText(CharSourceRange::getCharRange(Range),
+ SM, LangOpts, &IsInvalid);
+ if (IsInvalid || Text.empty())
+ return Range;
+ assert(Range.getBegin().isFileID() && "Not a file range!");
+
+ std::string Source = Text.str();
+ Lexer Lex(Range.getBegin(), LangOpts, Source.c_str(), Source.c_str(),
+ Source.c_str() + Source.size());
+ // Get comment tokens as well.
+ Lex.SetCommentRetentionState(true);
+ SourceLocation StartLoc, EndLoc;
+ while (true) {
+ Token Tok;
+ Lex.LexFromRawLexer(Tok);
+ if (Tok.getKind() == tok::eof)
+ break;
+ if (StartLoc.isInvalid())
+ StartLoc = Tok.getLocation();
+ if (Tok.getKind() != tok::semi)
+ EndLoc = Tok.getEndLoc();
+ }
+ return StartLoc.isValid() && EndLoc.isValid() ? SourceRange(StartLoc, EndLoc)
+ : SourceRange();
+}
+
+/// Tokenize the given file and check if it contains a comment that ends at the
+/// given location.
+static SourceLocation findCommentThatEndsAt(FileID FID,
+ SourceLocation StartOfFile,
+ const SourceManager &SM,
+ const LangOptions &LangOpts,
+ SourceLocation ExpectedEndLoc) {
+ // Try to load the file buffer.
+ bool InvalidTemp = false;
+ StringRef File = SM.getBufferData(FID, &InvalidTemp);
+ if (InvalidTemp)
+ return SourceLocation();
+
+ // Search for the comment that ends at the given location.
+ Lexer Lex(StartOfFile, LangOpts, File.begin(), File.begin(), File.end());
+ Lex.SetCommentRetentionState(true);
+ Token Tok;
+ while (!Lex.LexFromRawLexer(Tok)) {
+ if (Tok.is(tok::comment) && Tok.getEndLoc() == ExpectedEndLoc)
+ return Tok.getLocation();
+ }
+ // Find the token.
+ return SourceLocation();
+}
+
+SourceLocation getLocationOfPrecedingComment(SourceLocation Location,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation PrevResult = Location;
+ SourceLocation Result = Location;
+ if (Result.isMacroID())
+ Result = SM.getExpansionLoc(Result);
+ FileID FID = SM.getFileID(Result);
+ SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+ Token Tok;
+ Tok.setKind(tok::unknown);
+ SourceLocation TokenLoc = Result;
+ auto GetPreviousToken = [&]() -> bool {
+ TokenLoc =
+ Lexer::GetBeginningOfToken(TokenLoc.getLocWithOffset(-1), SM, LangOpts);
+ return !Lexer::getRawToken(TokenLoc, Tok, SM, LangOpts);
+ };
+ // Look for a comment token.
+ while (TokenLoc != StartOfFile) {
+ bool LocHasToken = GetPreviousToken();
+ if (LocHasToken && Tok.is(tok::slash)) {
+ // Check if this the end of a multiline '/*' comment before returning.
+ SourceLocation CommentLoc = findCommentThatEndsAt(
+ FID, StartOfFile, SM, LangOpts, Tok.getEndLoc());
+ return CommentLoc.isInvalid() ? Result : CommentLoc;
+ }
+ if (LocHasToken && Tok.isNot(tok::comment))
+ break;
+ if (!LocHasToken)
+ continue;
+ // We found a preceding comment. Check if there are other preceding
+ // comments.
+ PrevResult = Result;
+ Result = Tok.getLocation();
+ while (TokenLoc != StartOfFile) {
+ bool LocHasToken = GetPreviousToken();
+ if (LocHasToken && Tok.isNot(tok::comment)) {
+ // Reset the result to the previous location if this comment trails
+ // another token on the same line.
+ if (SM.getSpellingLineNumber(Tok.getEndLoc()) ==
+ SM.getSpellingLineNumber(Result))
+ Result = PrevResult;
+ break;
+ }
+ if (!LocHasToken)
+ continue;
+ // The location of this comment is accepted only when the next comment
+ // is located immediately after this comment.
+ if (SM.getSpellingLineNumber(Tok.getEndLoc()) !=
+ SM.getSpellingLineNumber(Result) - 1)
+ break;
+ PrevResult = Result;
+ Result = Tok.getLocation();
+ }
+ break;
+ }
+ return Result;
+}
+
+SourceLocation getLocationOfPrecedingToken(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ SourceLocation Result = Loc;
+ if (Result.isMacroID())
+ Result = SM.getExpansionLoc(Result);
+ FileID FID = SM.getFileID(Result);
+ SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+ if (Loc == StartOfFile)
+ return SourceLocation();
+ return Lexer::GetBeginningOfToken(Result.getLocWithOffset(-1), SM, LangOpts);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/SourceLocationUtilities.h b/lib/Tooling/Refactor/SourceLocationUtilities.h
new file mode 100644
index 0000000..ba7425b
--- /dev/null
+++ b/lib/Tooling/Refactor/SourceLocationUtilities.h
@@ -0,0 +1,174 @@
+//===--- SourceLocationUtilities.h - Source location helper functions -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+
+namespace clang {
+
+class Stmt;
+class LangOptions;
+
+namespace tooling {
+
+inline bool isPairOfFileLocations(SourceLocation Start, SourceLocation End) {
+ return Start.isValid() && Start.isFileID() && End.isValid() && End.isFileID();
+}
+
+/// Return true if the Point is within Start and End.
+inline bool isPointWithin(SourceLocation Location, SourceLocation Start,
+ SourceLocation End, const SourceManager &SM) {
+ return Location == Start || Location == End ||
+ (SM.isBeforeInTranslationUnit(Start, Location) &&
+ SM.isBeforeInTranslationUnit(Location, End));
+}
+
+/// Return true if the two given ranges overlap with each other.
+inline bool areRangesOverlapping(SourceRange R1, SourceRange R2,
+ const SourceManager &SM) {
+ return isPointWithin(R1.getBegin(), R2.getBegin(), R2.getEnd(), SM) ||
+ isPointWithin(R2.getBegin(), R1.getBegin(), R1.getEnd(), SM);
+}
+
+/// \brief Return the source location that can be considered the last location
+/// of the source construct before its body.
+///
+/// The returned location is determined using the following rules:
+///
+/// 1) If the source construct has a compound body that starts on the same line,
+/// then this function will return the location of the opening '{'.
+///
+/// if (condition) {
+/// ^
+///
+/// 2) If the source construct's body is not a compound statement that starts
+/// on the same line, then this function will return the location just before
+/// the starting location of the body.
+///
+/// if (condition) foo()
+/// ^
+///
+/// 3) Otherwise, this function will return the last location on the line prior
+/// to the the line on which the body starts.
+///
+/// if (condition)
+/// ^
+/// foo()
+///
+/// \param HeaderEnd The last known location of the pre-body portion of the
+/// source construct. For example, for an if statement, HeaderEnd should
+/// be the ending location of its conditional expression.
+SourceLocation findLastLocationOfSourceConstruct(SourceLocation HeaderEnd,
+ const Stmt *Body,
+ const SourceManager &SM);
+
+/// \brief Return the source location that can be considered the first location
+/// of the source construct prior to the previous portion of its body.
+///
+/// The returned location is determined using the following rules:
+///
+/// 1) If the source construct's body is a compound statement that ends
+/// on the same line, then this function will return the location of the
+/// closing '}'.
+///
+/// } else if (condition)
+/// ^
+///
+/// 2) Otherwise, this function will return the starting location of the source
+/// construct.
+///
+/// foo();
+/// else if (condition)
+/// ^
+///
+/// }
+/// else if (condition)
+/// ^
+///
+/// \param HeaderStart The first known location of the post-body portion of the
+/// source construct. For example, for an if statement, HeaderStart should
+/// be the starting location of the if keyword.
+SourceLocation findFirstLocationOfSourceConstruct(SourceLocation HeaderStart,
+ const Stmt *PreviousBody,
+ const SourceManager &SM);
+
+/// Return true if the given \p Location is within any range.
+bool isLocationInAnyRange(SourceLocation Location, ArrayRef<SourceRange> Ranges,
+ const SourceManager &SM);
+
+/// Return the precise end location for the given token.
+SourceLocation getPreciseTokenLocEnd(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// \brief Find the source location right after the location of the next ')'.
+///
+/// If the token that's located after \p LastKnownLoc isn't ')', then this
+/// function returns an invalid source location.
+SourceLocation findClosingParenLocEnd(SourceLocation LastKnownLoc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// Return the range of the next token if it has the given kind.
+SourceRange getRangeOfNextToken(SourceLocation Loc, tok::TokenKind Kind,
+ const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// Return the end location of the body when \p S is a compound statement or an
+/// invalid location when \p S is an empty compound statement. Otherwise,
+/// return the end location of the given statement \p S.
+SourceLocation findLastNonCompoundLocation(const Stmt *S);
+
+/// Return true if the two locations are on the same line and aren't
+/// macro locations.
+bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,
+ const SourceManager &SM);
+
+/// Return the last location of the line which contains the given spellling
+/// location \p SpellingLoc unless that line has other tokens after the given
+/// location.
+SourceLocation
+getLastLineLocationUnlessItHasOtherTokens(SourceLocation SpellingLoc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// Return true if the token at the given location is a semicolon.
+bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// Shrink the given range by ignoring leading whitespace and trailing
+/// whitespace and semicolons.
+///
+/// Returns an invalid source range if the source range consists of whitespace
+/// or semicolons only.
+SourceRange trimSelectionRange(SourceRange Range, const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// Return the source location of the conjoined comment(s) that precede the
+/// given location \p Loc, or the same location if there's no comment before
+/// \p Loc.
+SourceLocation getLocationOfPrecedingComment(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// Return the source location of the token that comes before the token at the
+/// given location.
+SourceLocation getLocationOfPrecedingToken(SourceLocation Loc,
+ const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_SOURCE_LOCATION_UTILITIES_H
diff --git a/lib/Tooling/Refactor/StmtUtils.cpp b/lib/Tooling/Refactor/StmtUtils.cpp
new file mode 100644
index 0000000..d5644b2
--- /dev/null
+++ b/lib/Tooling/Refactor/StmtUtils.cpp
@@ -0,0 +1,72 @@
+//===--- StmtUtils.cpp - Statement helper functions -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StmtUtils.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+
+SourceLocation
+clang::tooling::getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (!isa<ObjCImplDecl>(D))
+ return D->getSourceRange().getEnd();
+ auto AtEnd = D->getSourceRange().getEnd();
+ auto AdjustedEnd =
+ Lexer::findNextTokenLocationAfterTokenAt(AtEnd, SM, LangOpts);
+ return AdjustedEnd.isValid() ? AdjustedEnd : AtEnd;
+}
+
+bool clang::tooling::isSemicolonRequiredAfter(const Stmt *S) {
+ if (isa<CompoundStmt>(S))
+ return false;
+ if (const auto *If = dyn_cast<IfStmt>(S))
+ return isSemicolonRequiredAfter(If->getElse() ? If->getElse()
+ : If->getThen());
+ if (const auto *While = dyn_cast<WhileStmt>(S))
+ return isSemicolonRequiredAfter(While->getBody());
+ if (const auto *For = dyn_cast<ForStmt>(S))
+ return isSemicolonRequiredAfter(For->getBody());
+ if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S))
+ return isSemicolonRequiredAfter(CXXFor->getBody());
+ if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S))
+ return isSemicolonRequiredAfter(ObjCFor->getBody());
+ switch (S->getStmtClass()) {
+ case Stmt::SwitchStmtClass:
+ case Stmt::CXXTryStmtClass:
+ case Stmt::ObjCAtSynchronizedStmtClass:
+ case Stmt::ObjCAutoreleasePoolStmtClass:
+ case Stmt::ObjCAtTryStmtClass:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool isAssignmentOperator(const Stmt *S) {
+ if (const auto *PseudoExpr = dyn_cast<PseudoObjectExpr>(S))
+ return isAssignmentOperator(PseudoExpr->getSyntacticForm());
+ if (const auto *BO = dyn_cast<BinaryOperator>(S))
+ return BO->isAssignmentOp();
+ return false;
+}
+
+bool clang::tooling::isLexicalExpression(const Stmt *S, const Stmt *Parent) {
+ if (!isa<Expr>(S))
+ return false;
+ // Assignment operators should be treated as statements unless they are a part
+ // of an expression.
+ if (isAssignmentOperator(S) && (!Parent || !isa<Expr>(Parent)))
+ return false;
+ return !cast<Expr>(S)->getType()->isVoidType();
+}
diff --git a/lib/Tooling/Refactor/StmtUtils.h b/lib/Tooling/Refactor/StmtUtils.h
new file mode 100644
index 0000000..5bc3195
--- /dev/null
+++ b/lib/Tooling/Refactor/StmtUtils.h
@@ -0,0 +1,38 @@
+//===--- StmtUtils.h - Statement helper functions -------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H
+
+#include "clang/Basic/SourceLocation.h"
+
+namespace clang {
+
+class Decl;
+class LangOptions;
+class Stmt;
+
+namespace tooling {
+
+SourceLocation getLexicalEndLocForDecl(const Decl *D, const SourceManager &SM,
+ const LangOptions &LangOpts);
+
+/// \brief Returns true if there should be a semicolon after the given
+/// statement.
+bool isSemicolonRequiredAfter(const Stmt *S);
+
+/// Returns true if the given statement \p S is an actual expression in the
+/// source. Assignment expressions are considered to be statements unless they
+/// are a part of an expression.
+bool isLexicalExpression(const Stmt *S, const Stmt *Parent);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_STMT_UTILS_H
diff --git a/lib/Tooling/Refactor/SymbolName.cpp b/lib/Tooling/Refactor/SymbolName.cpp
new file mode 100644
index 0000000..2d30d18
--- /dev/null
+++ b/lib/Tooling/Refactor/SymbolName.cpp
@@ -0,0 +1,58 @@
+//===--- SymbolName.cpp - Clang refactoring library -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/SymbolName.h"
+#include "clang/Basic/LangOptions.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+namespace tooling {
+
+static void initNames(std::vector<std::string> &Strings, StringRef Name,
+ bool IsObjectiveCSelector) {
+ if (!IsObjectiveCSelector) {
+ Strings.push_back(Name.str());
+ return;
+ }
+ // Decompose an Objective-C selector name into multiple strings.
+ do {
+ auto StringAndName = Name.split(':');
+ Strings.push_back(StringAndName.first.str());
+ Name = StringAndName.second;
+ } while (!Name.empty());
+}
+
+SymbolName::SymbolName(StringRef Name, const LangOptions &LangOpts) {
+ initNames(Strings, Name, LangOpts.ObjC1);
+}
+
+SymbolName::SymbolName(StringRef Name, bool IsObjectiveCSelector) {
+ initNames(Strings, Name, IsObjectiveCSelector);
+}
+
+SymbolName::SymbolName(ArrayRef<StringRef> Name) {
+ for (const auto &Piece : Name)
+ Strings.push_back(Piece.str());
+}
+
+void SymbolName::print(raw_ostream &OS) const {
+ for (size_t I = 0, E = Strings.size(); I != E; ++I) {
+ if (I != 0)
+ OS << ':';
+ OS << Strings[I];
+ }
+}
+
+raw_ostream &operator<<(raw_ostream &OS, const SymbolName &N) {
+ N.print(OS);
+ return OS;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp b/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp
new file mode 100644
index 0000000..38f4cc8
--- /dev/null
+++ b/lib/Tooling/Refactor/SymbolOccurrenceFinder.cpp
@@ -0,0 +1,412 @@
+//===--- SymbolOccurrenceFinder.cpp - Clang refactoring library -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for finding all instances of a USR. Our strategy is very
+/// simple; we just compare the USR at every relevant AST node with the one
+/// provided.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/SymbolOccurrenceFinder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DependentASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactor/USRFinder.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+namespace rename {
+
+namespace {
+// \brief This visitor recursively searches for all instances of a USR in a
+// translation unit and stores them for later usage.
+class SymbolOccurrenceFinderASTVisitor
+ : public DependentASTVisitor<SymbolOccurrenceFinderASTVisitor> {
+public:
+ explicit SymbolOccurrenceFinderASTVisitor(
+ const SymbolOperation &Operation, const ASTContext &Context,
+ std::vector<SymbolOccurrence> &Occurrences)
+ : Operation(Operation), Context(Context), Occurrences(Occurrences) {}
+
+ /// Returns a \c Symbol if the given declaration corresponds to the symbol
+ /// that we're looking for.
+ const Symbol *symbolForDecl(const Decl *D) const {
+ if (!D)
+ return nullptr;
+ std::string USR = getUSRForDecl(D);
+ return Operation.getSymbolForUSR(USR);
+ }
+
+ void checkDecl(const Decl *D, SourceLocation Loc,
+ SymbolOccurrence::OccurrenceKind Kind =
+ SymbolOccurrence::MatchingSymbol) {
+ if (!D)
+ return;
+ std::string USR = getUSRForDecl(D);
+ if (const Symbol *S = Operation.getSymbolForUSR(USR))
+ checkAndAddLocations(S->SymbolIndex, Loc, Kind);
+ }
+
+ // Declaration visitors:
+
+ bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+ for (const auto *Initializer : ConstructorDecl->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+ if (const clang::FieldDecl *FieldDecl = Initializer->getMember())
+ checkDecl(FieldDecl, Initializer->getSourceLocation());
+ }
+ return true;
+ }
+
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ checkDecl(Decl, Decl->getLocation());
+ return true;
+ }
+
+ bool WalkUpFromTypedefNameDecl(const TypedefNameDecl *D) {
+ // Don't visit the NamedDecl for TypedefNameDecl.
+ return VisitTypedefNamedDecl(D);
+ }
+
+ bool VisitTypedefNamedDecl(const TypedefNameDecl *D) {
+ if (D->isTransparentTag()) {
+ if (const auto *Underlying = D->getUnderlyingType()->getAsTagDecl()) {
+ checkDecl(Underlying, D->getLocation());
+ return true;
+ }
+ }
+ return VisitNamedDecl(D);
+ }
+
+ bool WalkUpFromUsingDecl(const UsingDecl *D) {
+ // Don't visit the NamedDecl for UsingDecl.
+ return VisitUsingDecl(D);
+ }
+
+ bool VisitUsingDecl(const UsingDecl *D) {
+ for (const auto *Shadow : D->shadows()) {
+ const NamedDecl *UD = Shadow->getUnderlyingDecl();
+ if (UD->isImplicit() || UD == D)
+ continue;
+ if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(UD)) {
+ UD = FTD->getTemplatedDecl();
+ if (!UD)
+ continue;
+ }
+ checkDecl(UD, D->getLocation());
+ }
+ return true;
+ }
+
+ bool WalkUpFromUsingDirectiveDecl(const UsingDirectiveDecl *D) {
+ // Don't visit the NamedDecl for UsingDirectiveDecl.
+ return VisitUsingDirectiveDecl(D);
+ }
+
+ bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
+ checkDecl(D->getNominatedNamespaceAsWritten(), D->getLocation());
+ return true;
+ }
+
+ bool WalkUpFromUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) {
+ // Don't visit the NamedDecl for UnresolvedUsingValueDecl.
+ // FIXME: Can we try to lookup the name?
+ return true;
+ }
+
+ bool
+ WalkUpFromUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) {
+ // Don't visit the NamedDecl for UnresolvedUsingTypenameDecl.
+ // FIXME: Can we try to lookup the name?
+ return true;
+ }
+
+ bool WalkUpFromObjCMethodDecl(const ObjCMethodDecl *Decl) {
+ // Don't visit the NamedDecl for Objective-C methods.
+ return VisitObjCMethodDecl(Decl);
+ }
+
+ bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) {
+ const Symbol *S = symbolForDecl(Decl);
+ if (!S)
+ return true;
+ SmallVector<SourceLocation, 8> SelectorLocs;
+ Decl->getSelectorLocs(SelectorLocs);
+ checkAndAddLocations(S->SymbolIndex, SelectorLocs);
+ return true;
+ }
+
+ bool handleObjCProtocolList(const ObjCProtocolList &Protocols) {
+ for (auto It : enumerate(Protocols))
+ checkDecl(It.value(), Protocols.loc_begin()[It.index()]);
+ return true;
+ }
+
+ bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
+ if (!Decl->hasDefinition())
+ return true;
+ return handleObjCProtocolList(Decl->getReferencedProtocols());
+ }
+
+ bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
+ if (!Decl->hasDefinition())
+ return true;
+ return handleObjCProtocolList(Decl->getReferencedProtocols());
+ }
+
+ bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
+ checkDecl(Decl, Decl->getCategoryNameLoc());
+ // The location of the class name is the location of the declaration.
+ checkDecl(Decl->getClassInterface(), Decl->getLocation());
+ return handleObjCProtocolList(Decl->getReferencedProtocols());
+ }
+
+ bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) {
+ checkDecl(Decl, Decl->getCategoryNameLoc());
+ // The location of the class name is the location of the declaration.
+ checkDecl(Decl->getClassInterface(), Decl->getLocation());
+ return true;
+ }
+
+ bool VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *Decl) {
+ checkDecl(Decl->getClassInterface(), Decl->getClassInterfaceLoc());
+ return true;
+ }
+
+ bool VisitObjCPropertyDecl(const ObjCPropertyDecl *Decl) {
+ if (Decl->hasExplicitGetterName())
+ checkDecl(Decl->getGetterMethodDecl(), Decl->getGetterNameLoc());
+ if (Decl->hasExplicitSetterName())
+ checkDecl(Decl->getSetterMethodDecl(), Decl->getSetterNameLoc());
+ return true;
+ }
+
+ bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *Decl) {
+ checkDecl(Decl->getPropertyDecl(), Decl->getLocation());
+ if (Decl->isIvarNameSpecified())
+ checkDecl(Decl->getPropertyIvarDecl(), Decl->getPropertyIvarDeclLoc());
+ return true;
+ }
+
+ // Expression visitors:
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ checkDecl(Expr->getFoundDecl(), Expr->getLocation());
+ return true;
+ }
+
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ checkDecl(Expr->getFoundDecl().getDecl(), Expr->getMemberLoc());
+ return true;
+ }
+
+ bool VisitObjCMessageExpr(const ObjCMessageExpr *Expr) {
+ const Symbol *S = symbolForDecl(Expr->getMethodDecl());
+ if (!S)
+ return true;
+ SmallVector<SourceLocation, 8> SelectorLocs;
+ Expr->getSelectorLocs(SelectorLocs);
+ checkAndAddLocations(S->SymbolIndex, SelectorLocs);
+ return true;
+ }
+
+ bool VisitObjCProtocolExpr(const ObjCProtocolExpr *Expr) {
+ checkDecl(Expr->getProtocol(), Expr->getProtocolIdLoc());
+ return true;
+ }
+
+ bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Expr) {
+ checkDecl(Expr->getDecl(), Expr->getLocation());
+ return true;
+ }
+
+ bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Expr) {
+ if (Expr->isClassReceiver())
+ checkDecl(Expr->getClassReceiver(), Expr->getReceiverLocation());
+ if (Expr->isImplicitProperty()) {
+ // Class properties that are explicitly defined using @property
+ // declarations are represented implicitly as there is no ivar for class
+ // properties.
+ if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) {
+ if (Getter->isClassMethod())
+ if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl()) {
+ checkDecl(PD, Expr->getLocation());
+ return true;
+ }
+ }
+
+ checkDecl(Expr->getImplicitPropertyGetter(), Expr->getLocation(),
+ SymbolOccurrence::MatchingImplicitProperty);
+ // Add a manual location for a setter since a token like 'property' won't
+ // match the the name of the renamed symbol like 'setProperty'.
+ if (const auto *S = symbolForDecl(Expr->getImplicitPropertySetter()))
+ addLocation(S->SymbolIndex, Expr->getLocation(),
+ SymbolOccurrence::MatchingImplicitProperty);
+ return true;
+ }
+ checkDecl(Expr->getExplicitProperty(), Expr->getLocation());
+ return true;
+ }
+
+ // Other visitors:
+
+ bool VisitTypeLoc(const TypeLoc Loc) {
+ TypedefTypeLoc TTL = Loc.getAs<TypedefTypeLoc>();
+ if (TTL) {
+ const auto *TND = TTL.getTypedefNameDecl();
+ if (TND->isTransparentTag()) {
+ if (const auto *Underlying = TND->getUnderlyingType()->getAsTagDecl()) {
+ checkDecl(Underlying, TTL.getNameLoc());
+ return true;
+ }
+ }
+ checkDecl(TND, TTL.getNameLoc());
+ return true;
+ }
+ TypeSpecTypeLoc TSTL = Loc.getAs<TypeSpecTypeLoc>();
+ if (TSTL) {
+ checkDecl(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc());
+ }
+ if (const auto *TemplateTypeParm =
+ dyn_cast<TemplateTypeParmType>(Loc.getType())) {
+ checkDecl(TemplateTypeParm->getDecl(), Loc.getBeginLoc());
+ }
+ if (const auto *TemplateSpecType =
+ dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+ checkDecl(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
+ Loc.getBeginLoc());
+ }
+ return true;
+ }
+
+ bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc Loc) {
+ checkDecl(Loc.getIFaceDecl(), Loc.getNameLoc());
+ return true;
+ }
+
+ bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc Loc) {
+ for (unsigned I = 0, E = Loc.getNumProtocols(); I < E; ++I)
+ checkDecl(Loc.getProtocol(I), Loc.getProtocolLoc(I));
+ return true;
+ }
+
+ bool VisitDependentSymbolReference(const NamedDecl *Symbol,
+ SourceLocation SymbolNameLoc) {
+ checkDecl(Symbol, SymbolNameLoc);
+ return true;
+ }
+
+ // Non-visitors:
+
+ // Namespace traversal:
+ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+ while (NameLoc) {
+ checkDecl(NameLoc.getNestedNameSpecifier()->getAsNamespace(),
+ NameLoc.getLocalBeginLoc());
+ NameLoc = NameLoc.getPrefix();
+ }
+ }
+
+private:
+ size_t getOffsetForString(SourceLocation Loc, StringRef PrevNameString) {
+ const SourceLocation BeginLoc = Loc;
+ const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
+ BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
+ StringRef TokenName =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
+ Context.getSourceManager(), Context.getLangOpts());
+ return TokenName.find(PrevNameString);
+ }
+
+ void checkAndAddLocations(unsigned SymbolIndex,
+ ArrayRef<SourceLocation> Locations,
+ SymbolOccurrence::OccurrenceKind Kind =
+ SymbolOccurrence::MatchingSymbol) {
+ if (Locations.size() != Operation.symbols()[SymbolIndex].Name.size())
+ return;
+
+ SmallVector<SourceLocation, 4> StringLocations;
+ for (size_t I = 0, E = Locations.size(); I != E; ++I) {
+ SourceLocation Loc = Locations[I];
+ bool IsMacroExpansion = Loc.isMacroID();
+ if (IsMacroExpansion) {
+ const SourceManager &SM = Context.getSourceManager();
+ if (SM.isMacroArgExpansion(Loc)) {
+ Loc = SM.getSpellingLoc(Loc);
+ IsMacroExpansion = false;
+ } else
+ Loc = SM.getExpansionLoc(Loc);
+ }
+ if (IsMacroExpansion) {
+ Occurrences.push_back(SymbolOccurrence(Kind, /*IsMacroExpansion=*/true,
+ SymbolIndex, Loc));
+ return;
+ }
+ size_t Offset =
+ getOffsetForString(Loc, Operation.symbols()[SymbolIndex].Name[I]);
+ if (Offset == StringRef::npos)
+ return;
+ StringLocations.push_back(Loc.getLocWithOffset(Offset));
+ }
+
+ Occurrences.push_back(SymbolOccurrence(Kind, /*IsMacroExpansion=*/false,
+ SymbolIndex, StringLocations));
+ }
+
+ /// Adds a location without checking if the name is actually there.
+ void addLocation(unsigned SymbolIndex, SourceLocation Location,
+ SymbolOccurrence::OccurrenceKind Kind) {
+ if (1 != Operation.symbols()[SymbolIndex].Name.size())
+ return;
+ bool IsMacroExpansion = Location.isMacroID();
+ if (IsMacroExpansion) {
+ const SourceManager &SM = Context.getSourceManager();
+ if (SM.isMacroArgExpansion(Location)) {
+ Location = SM.getSpellingLoc(Location);
+ IsMacroExpansion = false;
+ } else
+ Location = SM.getExpansionLoc(Location);
+ }
+ Occurrences.push_back(
+ SymbolOccurrence(Kind, IsMacroExpansion, SymbolIndex, Location));
+ }
+
+ const SymbolOperation &Operation;
+ const ASTContext &Context;
+ std::vector<SymbolOccurrence> &Occurrences;
+};
+} // namespace
+
+std::vector<SymbolOccurrence>
+findSymbolOccurrences(const SymbolOperation &Operation, Decl *Decl) {
+ std::vector<SymbolOccurrence> Occurrences;
+ SymbolOccurrenceFinderASTVisitor Visitor(Operation, Decl->getASTContext(),
+ Occurrences);
+ Visitor.TraverseDecl(Decl);
+ NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
+
+ for (const auto &Location : Finder.getNestedNameSpecifierLocations())
+ Visitor.handleNestedNameSpecifierLoc(Location);
+
+ return Occurrences;
+}
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/SymbolOperation.cpp b/lib/Tooling/Refactor/SymbolOperation.cpp
new file mode 100644
index 0000000..111b6c0
--- /dev/null
+++ b/lib/Tooling/Refactor/SymbolOperation.cpp
@@ -0,0 +1,210 @@
+//===--- SymbolOperation.cpp - --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/SymbolOperation.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Tooling/Refactor/RefactoringActionFinder.h"
+
+using namespace clang;
+
+/// Return true if the given local record decl escapes the given enclosing
+/// function or block \p Ctx.
+static bool escapesEnclosingDecl(const DeclContext *Ctx, const RecordDecl *RD) {
+ QualType ReturnType;
+ bool DependentBlock = false;
+ if (const auto *FD = dyn_cast<FunctionDecl>(Ctx))
+ ReturnType = FD->getReturnType();
+ else if (const auto *BD = dyn_cast<BlockDecl>(Ctx)) {
+ ReturnType = BD->getSignatureAsWritten()->getType();
+ // Blocks that don't have an explicitly specified type (represented with a
+ // dependent type) could potentially return the record, e.g.
+ // auto block = ^ {
+ // struct Foo { };
+ // return Foo();
+ // };
+ if (const auto *FT = ReturnType->getAs<FunctionType>())
+ ReturnType = FT->getReturnType();
+ if (ReturnType->isDependentType())
+ DependentBlock = true;
+ } else
+ return false;
+
+ // The record can be returned from its enclosing function when the function's
+ // return type is auto.
+ //
+ // FIXME: Use a smarter heuristic that detects if the record type is
+ // actually returned from the function. Have to account for inner records,
+ // like in the example below:
+ //
+ // auto foo() {
+ // struct Foo { struct Bar { }; };
+ // return Foo::Bar();
+ // };
+ //
+ // for types that depend on the record, like in the example below:
+ //
+ // auto foo() {
+ // template<typename T> struct C<T> { T x; };
+ // struct Foo { struct Bar { }; };
+ // return C<Bar>();
+ // }
+ //
+ // and for things like typedefs and function types as well.
+ if (!DependentBlock && !ReturnType->getContainedAutoType())
+ return false;
+
+ // Even if the enclosing function returns the local record, this record is
+ // still local if the enclosing function is inside a function/method that
+ // doesn't return this record.
+ const auto *D = cast<Decl>(Ctx);
+ if (D->isLexicallyWithinFunctionOrMethod())
+ return escapesEnclosingDecl(D->getParentFunctionOrMethod(), RD);
+
+ return true;
+}
+
+static bool escapesEnclosingDecl(const RecordDecl *RD,
+ const LangOptions &LangOpts) {
+ // We only care about things that escape in header files since things that
+ // escape in source files will be used only in the initial TU.
+ return LangOpts.IsHeaderFile &&
+ escapesEnclosingDecl(RD->getParentFunctionOrMethod(), RD);
+}
+
+/// Return true if the given declaration corresponds to a local symbol.
+bool clang::tooling::isLocalSymbol(const NamedDecl *FoundDecl,
+ const LangOptions &LangOpts) {
+ // Template parameters aren't indexed, so use local rename.
+ if (isa<TemplateTypeParmDecl>(FoundDecl) ||
+ isa<NonTypeTemplateParmDecl>(FoundDecl) ||
+ isa<TemplateTemplateParmDecl>(FoundDecl))
+ return true;
+
+ if (const auto *VD = dyn_cast<VarDecl>(FoundDecl))
+ return VD->isLocalVarDeclOrParm();
+
+ // Objective-C selector renames must be global.
+ if (isa<ObjCMethodDecl>(FoundDecl))
+ return false;
+
+ // Local declarations are defined in a function or a method, or are anonymous.
+ if (!FoundDecl->isLexicallyWithinFunctionOrMethod())
+ return false;
+
+ // A locally defined record is global when it is returned from the enclosing
+ // function because we can refer to its destructor externally.
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(FoundDecl))
+ return !escapesEnclosingDecl(RD, LangOpts);
+
+ // A locally defined field is global when its record is returned from the
+ // enclosing function.
+ if (const auto *FD = dyn_cast<FieldDecl>(FoundDecl))
+ return !escapesEnclosingDecl(FD->getParent(), LangOpts);
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FoundDecl)) {
+ // A locally defined method is global when its record is returned from the
+ // enclosing function.
+ if (escapesEnclosingDecl(MD->getParent(), LangOpts))
+ return false;
+
+ // Method renames can be local only iff this method doesn't override
+ // a global method, for example:
+ //
+ // void func() {
+ // struct Foo: GlobalSuper {
+ // // When renaming foo we should also rename GlobalSuper's foo
+ // void foo() override;
+ // }
+ // }
+ //
+ // FIXME: We can try to be smarter about it and check if we override
+ // a local method, which would make this method local as well.
+ return !MD->isVirtual();
+ }
+
+ return true;
+}
+
+static const NamedDecl *
+findDeclThatRequiresImplementationTU(const NamedDecl *FoundDecl) {
+ // TODO: implement the rest.
+ if (const ObjCIvarDecl *IVarDecl = dyn_cast<ObjCIvarDecl>(FoundDecl)) {
+ // We need the implementation TU when the IVAR is declared in an @interface
+ // without an @implementation.
+ if (const auto *ID =
+ dyn_cast<ObjCInterfaceDecl>(IVarDecl->getDeclContext())) {
+ if (!ID->getImplementation())
+ return IVarDecl;
+ }
+ }
+ return nullptr;
+}
+
+namespace clang {
+namespace tooling {
+
+SymbolOperation::SymbolOperation(const NamedDecl *FoundDecl,
+ ASTContext &Context)
+ : IsLocal(isLocalSymbol(FoundDecl, Context.getLangOpts())) {
+ // Take the category declaration if this is a category implementation.
+ if (const auto *CategoryImplDecl =
+ dyn_cast<ObjCCategoryImplDecl>(FoundDecl)) {
+ if (const auto *CategoryDecl = CategoryImplDecl->getCategoryDecl())
+ FoundDecl = CategoryDecl;
+ }
+ // Use the property if this method is a getter/setter.
+ else if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl)) {
+ if (const auto *PropertyDecl =
+ MethodDecl->getCanonicalDecl()->findPropertyDecl()) {
+ // Don't use the property if the getter/setter method has an explicitly
+ // specified name.
+ if (MethodDecl->param_size() == 0
+ ? !PropertyDecl->hasExplicitGetterName()
+ : !PropertyDecl->hasExplicitSetterName())
+ FoundDecl = PropertyDecl;
+ }
+ }
+
+ DeclThatRequiresImplementationTU =
+ findDeclThatRequiresImplementationTU(FoundDecl);
+
+ // TODO: Split into initiation that works after implementation TU is loaded.
+
+ // Find the set of symbols that this operation has to work on.
+ auto AddSymbol = [this, &Context](const NamedDecl *FoundDecl) {
+ unsigned Index = Symbols.size();
+ Symbols.push_back(rename::Symbol(FoundDecl, Index, Context.getLangOpts()));
+ for (const auto &USR : findSymbolsUSRSet(FoundDecl, Context))
+ USRToSymbol.insert(std::make_pair(USR.getKey(), Index));
+ };
+ AddSymbol(FoundDecl);
+ // Take getters, setters and ivars into account when dealing with
+ // Objective-C @property declarations.
+ if (const auto *PropertyDecl = dyn_cast<ObjCPropertyDecl>(FoundDecl)) {
+ // FIXME: findSymbolsUSRSet is called for every symbol we add, which is
+ // inefficient since we currently have to traverse the AST every time it is
+ // called. Fix this so that the AST isn't traversed more than once.
+ if (!PropertyDecl->hasExplicitGetterName()) {
+ if (const auto *Getter = PropertyDecl->getGetterMethodDecl())
+ AddSymbol(Getter);
+ }
+ if (!PropertyDecl->hasExplicitSetterName()) {
+ if (const auto *Setter = PropertyDecl->getSetterMethodDecl())
+ AddSymbol(Setter);
+ }
+ }
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/SymbolUSRFinder.cpp b/lib/Tooling/Refactor/SymbolUSRFinder.cpp
new file mode 100644
index 0000000..267f90c
--- /dev/null
+++ b/lib/Tooling/Refactor/SymbolUSRFinder.cpp
@@ -0,0 +1,206 @@
+//===--- SymbolUSRFinder.cpp - Clang refactoring library ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implements methods that find the set of USRs that correspond to
+/// a symbol that's required for a refactoring operation.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Tooling/Refactor/RefactoringActionFinder.h"
+#include "clang/Tooling/Refactor/USRFinder.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <vector>
+
+using namespace clang;
+using namespace clang::tooling::rename;
+
+namespace {
+
+/// \brief NamedDeclFindingConsumer delegates finding USRs of a found Decl to
+/// \c AdditionalUSRFinder. \c AdditionalUSRFinder adds USRs of ctors and dtor
+/// if the found declaration refers to a class and adds USRs of all overridden
+/// methods if the declaration refers to a virtual C++ method or an ObjC method.
+class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
+public:
+ AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
+ : FoundDecl(FoundDecl), Context(Context) {}
+
+ llvm::StringSet<> Find() {
+ llvm::StringSet<> USRSet;
+
+ // Fill OverriddenMethods and PartialSpecs storages.
+ TraverseDecl(Context.getTranslationUnitDecl());
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
+ addUSRsOfOverridenFunctions(MethodDecl, USRSet);
+ // FIXME: Use a more efficient/optimal algorithm to find the related
+ // methods.
+ for (const auto &OverriddenMethod : OverriddenMethods) {
+ if (checkIfOverriddenFunctionAscends(OverriddenMethod, USRSet))
+ USRSet.insert(getUSRForDecl(OverriddenMethod));
+ }
+ } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
+ handleCXXRecordDecl(RecordDecl, USRSet);
+ } else if (const auto *TemplateDecl =
+ dyn_cast<ClassTemplateDecl>(FoundDecl)) {
+ handleClassTemplateDecl(TemplateDecl, USRSet);
+ } else if (const auto *MethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl)) {
+ addUSRsOfOverriddenObjCMethods(MethodDecl, USRSet);
+ for (const auto &PotentialOverrider : PotentialObjCMethodOverridders)
+ if (checkIfPotentialObjCMethodOverriddes(PotentialOverrider, USRSet))
+ USRSet.insert(getUSRForDecl(PotentialOverrider));
+ } else {
+ USRSet.insert(getUSRForDecl(FoundDecl));
+ }
+ return USRSet;
+ }
+
+ bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
+ if (MethodDecl->isVirtual())
+ OverriddenMethods.push_back(MethodDecl);
+ return true;
+ }
+
+ bool VisitObjCMethodDecl(const ObjCMethodDecl *MethodDecl) {
+ if (const auto *FoundMethodDecl = dyn_cast<ObjCMethodDecl>(FoundDecl))
+ if (DeclarationName::compare(MethodDecl->getDeclName(),
+ FoundMethodDecl->getDeclName()) == 0 &&
+ MethodDecl->isOverriding())
+ PotentialObjCMethodOverridders.push_back(MethodDecl);
+ return true;
+ }
+
+ bool VisitClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *PartialSpec) {
+ if (!isa<ClassTemplateDecl>(FoundDecl) && !isa<CXXRecordDecl>(FoundDecl))
+ return true;
+ PartialSpecs.push_back(PartialSpec);
+ return true;
+ }
+
+private:
+ void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl,
+ llvm::StringSet<> &USRSet) {
+ const auto *RD = RecordDecl->getDefinition();
+ if (!RD) {
+ USRSet.insert(getUSRForDecl(RecordDecl));
+ return;
+ }
+ if (const auto *ClassTemplateSpecDecl =
+ dyn_cast<ClassTemplateSpecializationDecl>(RD))
+ handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate(),
+ USRSet);
+ addUSRsOfCtorDtors(RD, USRSet);
+ }
+
+ void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl,
+ llvm::StringSet<> &USRSet) {
+ for (const auto *Specialization : TemplateDecl->specializations())
+ addUSRsOfCtorDtors(Specialization, USRSet);
+
+ for (const auto *PartialSpec : PartialSpecs) {
+ if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
+ addUSRsOfCtorDtors(PartialSpec, USRSet);
+ }
+ addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl(), USRSet);
+ }
+
+ void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl,
+ llvm::StringSet<> &USRSet) {
+ const CXXRecordDecl *RD = RecordDecl;
+ RecordDecl = RD->getDefinition();
+ if (!RecordDecl) {
+ USRSet.insert(getUSRForDecl(RD));
+ return;
+ }
+
+ for (const auto *CtorDecl : RecordDecl->ctors()) {
+ auto USR = getUSRForDecl(CtorDecl);
+ if (!USR.empty())
+ USRSet.insert(USR);
+ }
+
+ auto USR = getUSRForDecl(RecordDecl->getDestructor());
+ if (!USR.empty())
+ USRSet.insert(USR);
+ USRSet.insert(getUSRForDecl(RecordDecl));
+ }
+
+ void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl,
+ llvm::StringSet<> &USRSet) {
+ USRSet.insert(getUSRForDecl(MethodDecl));
+ // Recursively visit each OverridenMethod.
+ for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
+ addUSRsOfOverridenFunctions(OverriddenMethod, USRSet);
+ }
+
+ bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl,
+ const llvm::StringSet<> &USRSet) {
+ for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
+ if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
+ return true;
+ return checkIfOverriddenFunctionAscends(OverriddenMethod, USRSet);
+ }
+ return false;
+ }
+
+ /// \brief Recursively visit all the methods which the given method
+ /// declaration overrides and adds them to the USR set.
+ void addUSRsOfOverriddenObjCMethods(const ObjCMethodDecl *MethodDecl,
+ llvm::StringSet<> &USRSet) {
+ // Exit early if this method was already visited.
+ if (!USRSet.insert(getUSRForDecl(MethodDecl)).second)
+ return;
+ SmallVector<const ObjCMethodDecl *, 8> Overrides;
+ MethodDecl->getOverriddenMethods(Overrides);
+ for (const auto &OverriddenMethod : Overrides)
+ addUSRsOfOverriddenObjCMethods(OverriddenMethod, USRSet);
+ }
+
+ /// \brief Returns true if the given Objective-C method overrides the
+ /// found Objective-C method declaration.
+ bool checkIfPotentialObjCMethodOverriddes(const ObjCMethodDecl *MethodDecl,
+ const llvm::StringSet<> &USRSet) {
+ SmallVector<const ObjCMethodDecl *, 8> Overrides;
+ MethodDecl->getOverriddenMethods(Overrides);
+ for (const auto &OverriddenMethod : Overrides) {
+ if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
+ return true;
+ if (checkIfPotentialObjCMethodOverriddes(OverriddenMethod, USRSet))
+ return true;
+ }
+ return false;
+ }
+
+ const Decl *FoundDecl;
+ ASTContext &Context;
+ std::vector<const CXXMethodDecl *> OverriddenMethods;
+ std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
+ /// \brief An array of Objective-C methods that potentially override the
+ /// found Objective-C method declaration \p FoundDecl.
+ std::vector<const ObjCMethodDecl *> PotentialObjCMethodOverridders;
+};
+} // end anonymous namespace
+
+namespace clang {
+namespace tooling {
+
+llvm::StringSet<> findSymbolsUSRSet(const NamedDecl *FoundDecl,
+ ASTContext &Context) {
+ return AdditionalUSRFinder(FoundDecl, Context).Find();
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/TypeUtils.cpp b/lib/Tooling/Refactor/TypeUtils.cpp
new file mode 100644
index 0000000..c07136b
--- /dev/null
+++ b/lib/Tooling/Refactor/TypeUtils.cpp
@@ -0,0 +1,200 @@
+//===--- TypeUtils.cpp - Type helper functions ----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "TypeUtils.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/NSAPI.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+using namespace clang;
+
+namespace {
+
+/// Returns false if a BOOL expression is found.
+class BOOLUseFinder : public RecursiveASTVisitor<BOOLUseFinder> {
+public:
+ NSAPI API;
+
+ BOOLUseFinder(const ASTContext &Context)
+ : API(const_cast<ASTContext &>(Context)) {}
+
+ bool VisitStmt(const Stmt *S) {
+ if (const auto *E = dyn_cast<Expr>(S))
+ return !API.isObjCBOOLType(E->getType());
+ return true;
+ }
+
+ static bool hasUseOfObjCBOOL(const ASTContext &Ctx, const Expr *E) {
+ return !BOOLUseFinder(Ctx).TraverseStmt(const_cast<Expr *>(E));
+ }
+};
+
+} // end anonymous namespace
+
+static QualType preferredBoolType(const Decl *FunctionLikeParentDecl,
+ const Expr *E, QualType T,
+ const PrintingPolicy &Policy,
+ const ASTContext &Ctx) {
+ // We want to target expressions that return either 'int' or 'bool'
+ const auto *BTy = T->getAs<BuiltinType>();
+ if (!BTy)
+ return T;
+ switch (BTy->getKind()) {
+ case BuiltinType::Int:
+ case BuiltinType::Bool:
+ // In Objective-C[++] we want to try to use 'BOOL' when the 'BOOL' typedef
+ // is defined.
+ if (Ctx.getLangOpts().ObjC1 && Ctx.getBOOLDecl()) {
+ if (Ctx.getLangOpts().CPlusPlus && FunctionLikeParentDecl) {
+ // When extracting expression from a standalone function in
+ // Objective-C++ we should use BOOL when expression uses BOOL, otherwise
+ // we should use bool.
+ if (isa<FunctionDecl>(FunctionLikeParentDecl)) {
+ if (BOOLUseFinder::hasUseOfObjCBOOL(Ctx, E))
+ return Ctx.getBOOLType();
+ return T;
+ }
+ }
+ return Ctx.getBOOLType();
+ }
+ // In C mode we want to use 'bool' instead of 'int' when the 'bool' macro
+ // is defined.
+ if (!Ctx.getLangOpts().CPlusPlus && Policy.Bool)
+ return Ctx.BoolTy;
+ break;
+ default:
+ break;
+ }
+ return T;
+}
+
+static bool isInStdNamespace(const Decl *D) {
+ const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext();
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
+ if (!ND)
+ return false;
+
+ while (const DeclContext *Parent = ND->getParent()) {
+ if (!isa<NamespaceDecl>(Parent))
+ break;
+ ND = cast<NamespaceDecl>(Parent);
+ }
+
+ return ND->isStdNamespace();
+}
+
+static QualType desugarStdTypedef(QualType T) {
+ const auto *TT = T->getAs<TypedefType>();
+ if (!TT)
+ return QualType();
+ const TypedefNameDecl *TND = TT->getDecl();
+ if (!isInStdNamespace(TND))
+ return QualType();
+ return TT->desugar();
+}
+
+// Desugars a typedef of a typedef that are both defined in STL.
+//
+// This is used to find the right type for a c_str() call on a std::string
+// object: we want to return const char *, not const value_type *.
+static QualType desugarStdType(QualType T) {
+ QualType DesugaredType = T;
+ if (const auto *PT = T->getAs<PointerType>())
+ DesugaredType = PT->getPointeeType();
+ DesugaredType = desugarStdTypedef(DesugaredType);
+ if (DesugaredType.isNull())
+ return T;
+ if (const auto *ET = DesugaredType->getAs<ElaboratedType>())
+ DesugaredType = ET->desugar();
+ DesugaredType = desugarStdTypedef(DesugaredType);
+ if (DesugaredType.isNull())
+ return T;
+ return T.getCanonicalType();
+}
+
+// Given an operator call like std::string() + "", we would like to ensure
+// that we return std::string instead of std::basic_string.
+static QualType canonicalizeStdOperatorReturnType(const Expr *E, QualType T) {
+ const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E->IgnoreParenImpCasts());
+ if (!OCE)
+ return T;
+ if (OCE->getNumArgs() < 2 || !isInStdNamespace(OCE->getCalleeDecl()))
+ return T;
+ QualType CanonicalReturn = T.getCanonicalType();
+ if (const auto *RD = CanonicalReturn->getAsCXXRecordDecl()) {
+ if (!isInStdNamespace(RD))
+ return T;
+ } else
+ return T;
+ for (unsigned I = 0, E = OCE->getNumArgs(); I < E; ++I) {
+ const Expr *Arg = OCE->getArgs()[I];
+ QualType T = Arg->getType();
+ if (const auto *ET = dyn_cast<ElaboratedType>(T))
+ T = ET->desugar();
+ if (desugarStdTypedef(T).isNull())
+ continue;
+ QualType CanonicalArg = Arg->getType().getCanonicalType();
+ CanonicalArg.removeLocalFastQualifiers();
+ if (CanonicalArg == CanonicalReturn) {
+ QualType Result = Arg->getType();
+ Result.removeLocalFastQualifiers();
+ return Result;
+ }
+ }
+ return T;
+}
+
+namespace clang {
+namespace tooling {
+
+/// Tthe return type of the extracted function should match user's intent,
+/// e.g. we want to use bool type whenever possible.
+QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl,
+ const Expr *E, QualType T,
+ const PrintingPolicy &Policy,
+ const ASTContext &Ctx) {
+ // Get the correct property type.
+ if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) {
+ if (PRE->isMessagingGetter()) {
+ if (PRE->isExplicitProperty()) {
+ QualType ReceiverType = PRE->getReceiverType(Ctx);
+ return PRE->getExplicitProperty()->getUsageType(ReceiverType);
+ }
+ if (const ObjCMethodDecl *M = PRE->getImplicitPropertyGetter()) {
+ if (!PRE->isObjectReceiver())
+ return M->getSendResultType(PRE->getReceiverType(Ctx));
+ const Expr *Base = PRE->getBase();
+ return M->getSendResultType(findExpressionLexicalType(
+ FunctionLikeParentDecl, Base, Base->getType(), Policy, Ctx));
+ }
+ }
+ }
+
+ // Perform STL-specific type corrections.
+ if (Ctx.getLangOpts().CPlusPlus) {
+ T = desugarStdType(T);
+ T = canonicalizeStdOperatorReturnType(E, T);
+ }
+
+ // The bool type adjustment is required only in C or Objective-C[++].
+ if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().ObjC1)
+ return T;
+ E = E->IgnoreParenImpCasts();
+ if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) {
+ if (BinOp->isLogicalOp() || BinOp->isComparisonOp())
+ return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx);
+ } else if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) {
+ if (UnOp->getOpcode() == UO_LNot)
+ return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx);
+ }
+ return T;
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactor/TypeUtils.h b/lib/Tooling/Refactor/TypeUtils.h
new file mode 100644
index 0000000..8d575e8
--- /dev/null
+++ b/lib/Tooling/Refactor/TypeUtils.h
@@ -0,0 +1,35 @@
+//===--- TypeUtils.h - Type helper functions ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H
+
+#include "clang/AST/Type.h"
+
+namespace clang {
+
+class Decl;
+
+namespace tooling {
+
+/// \brief Find the most lexically appropriate type that can be used to describe
+/// the return type of the given expression \p E.
+///
+/// When extracting code, we want to produce a function that returns a type
+/// that matches the user's intent. This function can be used to find such a
+/// type.
+QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl,
+ const Expr *E, QualType T,
+ const PrintingPolicy &Policy,
+ const ASTContext &Ctx);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTOR_TYPE_UTILS_H
diff --git a/lib/Tooling/Refactor/USRFinder.cpp b/lib/Tooling/Refactor/USRFinder.cpp
new file mode 100644
index 0000000..6959490
--- /dev/null
+++ b/lib/Tooling/Refactor/USRFinder.cpp
@@ -0,0 +1,706 @@
+//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
+/// point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactor/USRFinder.h"
+#include "SourceLocationUtilities.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DependentASTVisitor.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/RefactoringDiagnostic.h"
+#include "llvm/ADT/SmallVector.h"
+#include <functional>
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+namespace rename {
+
+typedef std::function<bool(const NamedDecl *, SourceLocation, SourceLocation)>
+ OccurrenceCheckerType;
+
+// NamedDeclFindingASTVisitor recursively visits each AST node to find the
+// symbol underneath the cursor.
+// FIXME: move to seperate .h/.cc file if this gets too large.
+namespace {
+class NamedDeclFindingASTVisitor
+ : public DependentASTVisitor<NamedDeclFindingASTVisitor> {
+public:
+ // \brief Finds the NamedDecl at a point in the source.
+ // \param Point the location in the source to search for the NamedDecl.
+ explicit NamedDeclFindingASTVisitor(
+ const OccurrenceCheckerType &OccurrenceChecker, const ASTContext &Context)
+ : Result(nullptr), OccurrenceChecker(OccurrenceChecker),
+ Context(Context) {}
+
+ // Declaration visitors:
+
+ // \brief Checks if the point falls within the NameDecl. This covers every
+ // declaration of a named entity that we may come across. Usually, just
+ // checking if the point lies within the length of the name of the declaration
+ // and the start location is sufficient.
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ return dyn_cast<CXXConversionDecl>(Decl)
+ ? true
+ : checkOccurrence(Decl, Decl->getLocation(),
+ Decl->getNameAsString().length());
+ }
+
+ bool WalkUpFromTypedefNameDecl(const TypedefNameDecl *D) {
+ // Don't visit the NamedDecl for TypedefNameDecl.
+ return VisitTypedefNamedDecl(D);
+ }
+
+ bool VisitTypedefNamedDecl(const TypedefNameDecl *D) {
+ if (D->isTransparentTag()) {
+ if (const auto *Underlying = D->getUnderlyingType()->getAsTagDecl())
+ return checkOccurrence(Underlying, D->getLocation(),
+ D->getNameAsString().size());
+ }
+ return VisitNamedDecl(D);
+ }
+
+ bool WalkUpFromUsingDecl(const UsingDecl *D) {
+ // Don't visit the NamedDecl for UsingDecl.
+ return VisitUsingDecl(D);
+ }
+
+ bool VisitUsingDecl(const UsingDecl *D) {
+ for (const auto *Shadow : D->shadows()) {
+ // Currently we always find the first declaration, but is this the right
+ // behaviour?
+ const NamedDecl *UD = Shadow->getUnderlyingDecl();
+ if (UD->isImplicit() || UD == D)
+ continue;
+ if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(UD)) {
+ UD = FTD->getTemplatedDecl();
+ if (!UD)
+ continue;
+ }
+ if (!checkOccurrence(UD, D->getLocation()))
+ return false;
+ }
+ return true;
+ }
+
+ bool WalkUpFromUsingDirectiveDecl(const UsingDirectiveDecl *D) {
+ // Don't visit the NamedDecl for UsingDirectiveDecl.
+ return VisitUsingDirectiveDecl(D);
+ }
+
+ bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) {
+ return checkOccurrence(D->getNominatedNamespaceAsWritten(),
+ D->getLocation());
+ }
+
+ bool WalkUpFromUnresolvedUsingValueDecl(const UnresolvedUsingValueDecl *D) {
+ // Don't visit the NamedDecl for UnresolvedUsingValueDecl.
+ // FIXME: Can we try to lookup the name?
+ return true;
+ }
+
+ bool
+ WalkUpFromUnresolvedUsingTypenameDecl(const UnresolvedUsingTypenameDecl *D) {
+ // Don't visit the NamedDecl for UnresolvedUsingTypenameDecl.
+ // FIXME: Can we try to lookup the name?
+ return true;
+ }
+
+ bool WalkUpFromObjCMethodDecl(const ObjCMethodDecl *Decl) {
+ // Don't visit the NamedDecl for Objective-C methods.
+ return VisitObjCMethodDecl(Decl);
+ }
+
+ bool VisitObjCMethodDecl(const ObjCMethodDecl *Decl) {
+ // Check all of the selector source ranges.
+ for (unsigned I = 0, E = Decl->getNumSelectorLocs(); I != E; ++I) {
+ SourceLocation Loc = Decl->getSelectorLoc(I);
+ if (!checkOccurrence(Decl, Loc,
+ Loc.getLocWithOffset(
+ Decl->getSelector().getNameForSlot(I).size())))
+ return false;
+ }
+ return true;
+ }
+
+ bool VisitObjCProtocolList(const ObjCProtocolList &Protocols) {
+ for (unsigned I = 0, E = Protocols.size(); I != E; ++I) {
+ if (!checkOccurrence(Protocols[I], Protocols.loc_begin()[I]))
+ return false;
+ }
+ return true;
+ }
+
+ bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
+ if (!Decl->hasDefinition())
+ return true;
+ return VisitObjCProtocolList(Decl->getReferencedProtocols());
+ }
+
+ bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
+ if (!Decl->hasDefinition())
+ return true;
+ return VisitObjCProtocolList(Decl->getReferencedProtocols());
+ }
+
+ bool WalkUpFromObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
+ // Don't visit the NamedDecl for Objective-C categories because the location
+ // of the name refers to the interface declaration.
+ return VisitObjCCategoryDecl(Decl);
+ }
+
+ bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
+ if (!checkOccurrence(Decl, Decl->getCategoryNameLoc()))
+ return false;
+ if (const auto *Class = Decl->getClassInterface()) {
+ // The location of the class name is the location of the declaration.
+ if (!checkOccurrence(Class, Decl->getLocation()))
+ return false;
+ }
+ return VisitObjCProtocolList(Decl->getReferencedProtocols());
+ }
+
+ bool WalkUpFromObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) {
+ // Don't visit the NamedDecl for Objective-C categories because the location
+ // of the name refers to the interface declaration.
+ return VisitObjCCategoryImplDecl(Decl);
+ }
+
+ bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *Decl) {
+ if (!checkOccurrence(Decl, Decl->getCategoryNameLoc()))
+ return false;
+ if (const auto *Class = Decl->getClassInterface()) {
+ // The location of the class name is the location of the declaration.
+ if (!checkOccurrence(Class, Decl->getLocation()))
+ return false;
+ }
+ return true;
+ }
+
+ bool VisitObjCCompatibleAliasDecl(const ObjCCompatibleAliasDecl *Decl) {
+ return checkOccurrence(Decl->getClassInterface(),
+ Decl->getClassInterfaceLoc());
+ }
+
+ bool WalkUpFromObjCIvarDecl(ObjCIvarDecl *Decl) {
+ // Don't visit the NamedDecl for automatically synthesized ivars as the
+ // implicit ivars have the same location as the property declarations, and
+ // we want to find the property declarations.
+ if (Decl->getSynthesize())
+ return true;
+ return RecursiveASTVisitor::WalkUpFromObjCIvarDecl(Decl);
+ }
+
+ bool VisitObjCPropertyDecl(const ObjCPropertyDecl *Decl) {
+ if (Decl->hasExplicitGetterName()) {
+ if (const auto *Getter = Decl->getGetterMethodDecl())
+ if (!checkOccurrence(Getter, Decl->getGetterNameLoc(),
+ Decl->getGetterName().getNameForSlot(0).size()))
+ return false;
+ }
+ if (Decl->hasExplicitSetterName()) {
+ if (const auto *Setter = Decl->getSetterMethodDecl())
+ return checkOccurrence(Setter, Decl->getSetterNameLoc(),
+ Decl->getSetterName().getNameForSlot(0).size());
+ }
+ return true;
+ }
+
+ bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *Decl) {
+ if (!checkOccurrence(Decl->getPropertyDecl(), Decl->getLocation()))
+ return false;
+ if (Decl->isIvarNameSpecified())
+ return checkOccurrence(Decl->getPropertyIvarDecl(),
+ Decl->getPropertyIvarDeclLoc());
+ return true;
+ }
+
+ // Expression visitors:
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+ return checkOccurrence(Decl, Expr->getLocation(),
+ Decl->getNameAsString().length());
+ }
+
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
+ return checkOccurrence(Decl, Expr->getMemberLoc(),
+ Decl->getNameAsString().length());
+ }
+
+ bool VisitObjCMessageExpr(const ObjCMessageExpr *Expr) {
+ const ObjCMethodDecl *Decl = Expr->getMethodDecl();
+ if (Decl == nullptr)
+ return true;
+
+ // Check all of the selector source ranges.
+ for (unsigned I = 0, E = Expr->getNumSelectorLocs(); I != E; ++I) {
+ SourceLocation Loc = Expr->getSelectorLoc(I);
+ if (!checkOccurrence(Decl, Loc,
+ Loc.getLocWithOffset(
+ Decl->getSelector().getNameForSlot(I).size())))
+ return false;
+ }
+ return true;
+ }
+
+ bool VisitObjCProtocolExpr(const ObjCProtocolExpr *Expr) {
+ return checkOccurrence(Expr->getProtocol(), Expr->getProtocolIdLoc());
+ }
+
+ bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *Expr) {
+ return checkOccurrence(Expr->getDecl(), Expr->getLocation());
+ }
+
+ bool VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *Expr) {
+ if (Expr->isClassReceiver())
+ checkOccurrence(Expr->getClassReceiver(), Expr->getReceiverLocation());
+ if (Expr->isImplicitProperty()) {
+ // Class properties that are explicitly defined using @property
+ // declarations are represented implicitly as there is no ivar for class
+ // properties.
+ if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter()) {
+ if (Getter->isClassMethod()) {
+ if (const auto *PD = Getter->getCanonicalDecl()->findPropertyDecl())
+ return checkOccurrence(PD, Expr->getLocation());
+ }
+ }
+
+ if (Expr->isMessagingGetter()) {
+ if (const ObjCMethodDecl *Getter = Expr->getImplicitPropertyGetter())
+ return checkOccurrence(Getter, Expr->getLocation());
+ } else if (const ObjCMethodDecl *Setter =
+ Expr->getImplicitPropertySetter()) {
+ return checkOccurrence(Setter, Expr->getLocation());
+ }
+
+ return true;
+ }
+ return checkOccurrence(Expr->getExplicitProperty(), Expr->getLocation());
+ }
+
+ // Other visitors:
+
+ bool VisitTypeLoc(const TypeLoc Loc) {
+ const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
+ const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
+ TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
+ if (const auto *TemplateTypeParm =
+ dyn_cast<TemplateTypeParmType>(Loc.getType()))
+ return checkOccurrence(TemplateTypeParm->getDecl(), TypeBeginLoc,
+ TypeEndLoc);
+ if (const auto *TemplateSpecType =
+ dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+ return checkOccurrence(
+ TemplateSpecType->getTemplateName().getAsTemplateDecl(), TypeBeginLoc,
+ TypeEndLoc);
+ }
+ TypedefTypeLoc TTL = Loc.getAs<TypedefTypeLoc>();
+ if (TTL) {
+ const auto *TND = TTL.getTypedefNameDecl();
+ if (TND->isTransparentTag()) {
+ if (const auto *Underlying = TND->getUnderlyingType()->getAsTagDecl())
+ return checkOccurrence(Underlying, TTL.getNameLoc());
+ }
+ return checkOccurrence(TND, TTL.getNameLoc());
+ }
+ TypeSpecTypeLoc TSTL = Loc.getAs<TypeSpecTypeLoc>();
+ if (TSTL) {
+ return checkOccurrence(Loc.getType()->getAsTagDecl(), TSTL.getNameLoc());
+ }
+ return true;
+ }
+
+ bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc Loc) {
+ return checkOccurrence(Loc.getIFaceDecl(), Loc.getNameLoc());
+ }
+
+ bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc Loc) {
+ for (unsigned I = 0, E = Loc.getNumProtocols(); I < E; ++I) {
+ if (!checkOccurrence(Loc.getProtocol(I), Loc.getProtocolLoc(I)))
+ return false;
+ }
+ return true;
+ }
+
+ bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+ for (const auto *Initializer : ConstructorDecl->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+ if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
+ const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
+ InitEndLoc = Lexer::getLocForEndOfToken(
+ InitBeginLoc, 0, Context.getSourceManager(),
+ Context.getLangOpts());
+ if (!checkOccurrence(FieldDecl, InitBeginLoc, InitEndLoc))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool VisitDependentSymbolReference(const NamedDecl *Symbol,
+ SourceLocation SymbolNameLoc) {
+ return checkOccurrence(Symbol, SymbolNameLoc);
+ }
+
+ // Other:
+
+ const NamedDecl *getNamedDecl() { return Result; }
+
+ bool isDone() const { return Result; }
+
+ // \brief Determines if a namespace qualifier contains the point.
+ // \returns false on success and sets Result.
+ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+ while (NameLoc) {
+ const NamespaceDecl *Decl =
+ NameLoc.getNestedNameSpecifier()->getAsNamespace();
+ checkOccurrence(Decl, NameLoc.getLocalBeginLoc(),
+ NameLoc.getLocalEndLoc());
+ NameLoc = NameLoc.getPrefix();
+ }
+ }
+
+private:
+ /// \brief Sets Result to \p Decl if the occurrence checker returns true.
+ ///
+ /// \returns false on success.
+ bool checkRange(const NamedDecl *Decl, SourceLocation Start,
+ SourceLocation End) {
+ assert(!Start.isMacroID() && !End.isMacroID() && "Macro location?");
+ if (!Decl)
+ return true;
+ if (isa<ImplicitParamDecl>(Decl))
+ return true;
+ if (const auto *FD = dyn_cast<FunctionDecl>(Decl)) {
+ // Don't match operators.
+ if (FD->isOverloadedOperator())
+ return true;
+ }
+ if (!OccurrenceChecker(Decl, Start, End))
+ return true;
+ Result = Decl;
+ return false;
+ }
+
+ /// Checks if the given declaration is valid, and if it is, sets Result to
+ /// \p Decl if the occurrence checker returns true.
+ ///
+ /// \returns false if the point of interest is inside the range that
+ /// corresponds the occurrence of this declaration.
+ bool checkOccurrence(const NamedDecl *Decl, SourceLocation Loc) {
+ if (!Decl)
+ return true;
+ return checkOccurrence(Decl, Loc, Decl->getNameAsString().size());
+ }
+
+ /// \brief Sets Result to \p Decl if the occurrence checker returns true.
+ ///
+ /// \returns false on success.
+ bool checkOccurrence(const NamedDecl *Decl, SourceLocation Loc,
+ unsigned Length) {
+ if (Loc.isMacroID()) {
+ const SourceManager &SM = Context.getSourceManager();
+ if (SM.isMacroArgExpansion(Loc))
+ Loc = SM.getSpellingLoc(Loc);
+ else
+ return true;
+ }
+
+ return Length == 0 ||
+ checkRange(Decl, Loc, Loc.getLocWithOffset(Length - 1));
+ }
+
+ bool checkOccurrence(const NamedDecl *ND, SourceLocation Start,
+ SourceLocation End) {
+ const SourceManager &SM = Context.getSourceManager();
+ if (Start.isMacroID()) {
+ if (SM.isMacroArgExpansion(Start))
+ Start = SM.getSpellingLoc(Start);
+ else
+ return true;
+ }
+ if (End.isMacroID()) {
+ if (SM.isMacroArgExpansion(End))
+ End = SM.getSpellingLoc(End);
+ else
+ return true;
+ }
+ return checkRange(ND, Start, End);
+ }
+
+ const NamedDecl *Result;
+ const OccurrenceCheckerType &OccurrenceChecker;
+ const ASTContext &Context;
+};
+
+} // namespace
+
+static const ExternalSourceSymbolAttr *getExternalSymAttr(const Decl *D) {
+ if (const auto *A = D->getAttr<ExternalSourceSymbolAttr>())
+ return A;
+ if (const auto *DCD = dyn_cast<Decl>(D->getDeclContext())) {
+ if (const auto *A = DCD->getAttr<ExternalSourceSymbolAttr>())
+ return A;
+ }
+ return nullptr;
+}
+
+static bool overridesSystemMethod(const ObjCMethodDecl *MD,
+ const SourceManager &SM) {
+ SmallVector<const ObjCMethodDecl *, 8> Overrides;
+ MD->getOverriddenMethods(Overrides);
+ for (const auto *Override : Overrides) {
+ SourceLocation Loc = Override->getLocStart();
+ if (Loc.isValid()) {
+ if (SM.getFileCharacteristic(Loc) != SrcMgr::C_User)
+ return true;
+ }
+ if (overridesSystemMethod(Override, SM))
+ return true;
+ }
+ return false;
+}
+
+// TODO: Share with the indexer?
+static bool isTemplateImplicitInstantiation(const Decl *D) {
+ TemplateSpecializationKind TKind = TSK_Undeclared;
+ if (const ClassTemplateSpecializationDecl *SD =
+ dyn_cast<ClassTemplateSpecializationDecl>(D)) {
+ TKind = SD->getSpecializationKind();
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ TKind = FD->getTemplateSpecializationKind();
+ } else if (auto *VD = dyn_cast<VarDecl>(D)) {
+ TKind = VD->getTemplateSpecializationKind();
+ } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
+ if (RD->getInstantiatedFromMemberClass())
+ TKind = RD->getTemplateSpecializationKind();
+ } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
+ if (ED->getInstantiatedFromMemberEnum())
+ TKind = ED->getTemplateSpecializationKind();
+ } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D) ||
+ isa<EnumConstantDecl>(D)) {
+ if (const auto *Parent = dyn_cast<Decl>(D->getDeclContext()))
+ return isTemplateImplicitInstantiation(Parent);
+ }
+ switch (TKind) {
+ case TSK_Undeclared:
+ case TSK_ExplicitSpecialization:
+ return false;
+ case TSK_ImplicitInstantiation:
+ case TSK_ExplicitInstantiationDeclaration:
+ case TSK_ExplicitInstantiationDefinition:
+ return true;
+ }
+ llvm_unreachable("invalid TemplateSpecializationKind");
+}
+
+static const CXXRecordDecl *
+getDeclContextForTemplateInstationPattern(const Decl *D) {
+ if (const auto *CTSD =
+ dyn_cast<ClassTemplateSpecializationDecl>(D->getDeclContext()))
+ return CTSD->getTemplateInstantiationPattern();
+ else if (const auto *RD = dyn_cast<CXXRecordDecl>(D->getDeclContext()))
+ return RD->getInstantiatedFromMemberClass();
+ return nullptr;
+}
+
+static const NamedDecl *
+adjustTemplateImplicitInstantiation(const NamedDecl *D) {
+ if (const ClassTemplateSpecializationDecl *SD =
+ dyn_cast<ClassTemplateSpecializationDecl>(D)) {
+ return SD->getTemplateInstantiationPattern();
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ return FD->getTemplateInstantiationPattern();
+ } else if (auto *VD = dyn_cast<VarDecl>(D)) {
+ return VD->getTemplateInstantiationPattern();
+ } else if (const auto *RD = dyn_cast<CXXRecordDecl>(D)) {
+ return RD->getInstantiatedFromMemberClass();
+ } else if (const auto *ED = dyn_cast<EnumDecl>(D)) {
+ return ED->getInstantiatedFromMemberEnum();
+ } else if (isa<FieldDecl>(D) || isa<TypedefNameDecl>(D)) {
+ const auto *ND = cast<NamedDecl>(D);
+ if (const CXXRecordDecl *Pattern =
+ getDeclContextForTemplateInstationPattern(ND)) {
+ for (const NamedDecl *BaseND : Pattern->lookup(ND->getDeclName())) {
+ if (BaseND->isImplicit())
+ continue;
+ if (BaseND->getKind() == ND->getKind())
+ return BaseND;
+ }
+ }
+ } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
+ if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
+ if (const EnumDecl *Pattern = ED->getInstantiatedFromMemberEnum()) {
+ for (const NamedDecl *BaseECD : Pattern->lookup(ECD->getDeclName()))
+ return BaseECD;
+ }
+ }
+ }
+ return D;
+}
+
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+ SourceLocation Point) {
+ if (Point.isMacroID())
+ Point = Context.getSourceManager().getSpellingLoc(Point);
+ // FIXME: If point is in a system header, return early here.
+
+ OccurrenceCheckerType PointChecker = [Point, &Context](
+ const NamedDecl *Decl, SourceLocation Start, SourceLocation End) -> bool {
+ return Start.isValid() && Start.isFileID() && End.isValid() &&
+ End.isFileID() &&
+ isPointWithin(Point, Start, End, Context.getSourceManager());
+ };
+ NamedDeclFindingASTVisitor Visitor(PointChecker, Context);
+
+ // We only want to search the decls that exist in the same file as the point.
+ FileID InitiationFile = Context.getSourceManager().getFileID(Point);
+ for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
+ const SourceRange DeclRange = CurrDecl->getSourceRange();
+ SourceLocation FileLoc;
+ if (DeclRange.getBegin().isMacroID() && !DeclRange.getEnd().isMacroID())
+ FileLoc = DeclRange.getEnd();
+ else
+ FileLoc = Context.getSourceManager().getSpellingLoc(DeclRange.getBegin());
+ // FIXME: Add test.
+ if (Context.getSourceManager().getFileID(FileLoc) == InitiationFile)
+ Visitor.TraverseDecl(CurrDecl);
+ if (Visitor.isDone())
+ break;
+ }
+
+ if (!Visitor.isDone()) {
+ NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
+ for (const auto &Location : Finder.getNestedNameSpecifierLocations()) {
+ Visitor.handleNestedNameSpecifierLoc(Location);
+ if (Visitor.isDone())
+ break;
+ }
+ }
+
+ const auto Diag = [&](unsigned DiagID) -> DiagnosticBuilder {
+ return Context.getDiagnostics().Report(Point, DiagID);
+ };
+ const auto *ND = Visitor.getNamedDecl();
+ if (!ND)
+ return nullptr;
+
+ // Canonicalize the found declaration.
+ //
+ // If FoundDecl is a constructor or destructor, we want to instead take
+ // the Decl of the corresponding class.
+ if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(ND))
+ ND = CtorDecl->getParent();
+ else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(ND))
+ ND = DtorDecl->getParent();
+
+ if (isTemplateImplicitInstantiation(ND))
+ ND = adjustTemplateImplicitInstantiation(ND);
+
+ // Builtins can't be renamed.
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ if (FD->getBuiltinID()) {
+ Diag(diag::err_rename_builtin_function) << ND->getDeclName();
+ return nullptr;
+ }
+ }
+ // Declarations with invalid locations are probably implicit.
+ if (ND->getLocStart().isInvalid())
+ return nullptr;
+ // Declarations in system headers can't be renamed.
+ auto CheckSystemLoc = [&](SourceLocation Loc) -> bool {
+ if (Context.getSourceManager().getFileCharacteristic(Loc) !=
+ SrcMgr::C_User) {
+ Diag(diag::err_rename_sys_header) << ND->getDeclName();
+ return true;
+ }
+ return false;
+ };
+ if (CheckSystemLoc(ND->getLocStart()))
+ return nullptr;
+ if (const auto *TD = dyn_cast<TypedefNameDecl>(ND)) {
+ if (const TypedefNameDecl *CTD = TD->getCanonicalDecl()) {
+ if (CheckSystemLoc(CTD->getLocStart()))
+ return nullptr;
+ }
+ } else if (const auto *TD = dyn_cast<TagDecl>(ND)) {
+ if (const TagDecl *CTD = TD->getCanonicalDecl()) {
+ if (CheckSystemLoc(CTD->getLocStart()))
+ return nullptr;
+ }
+ } else if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ if (const FunctionDecl *CFD = FD->getCanonicalDecl()) {
+ if (CheckSystemLoc(CFD->getLocStart()))
+ return nullptr;
+ }
+ } else if (const auto *VD = dyn_cast<VarDecl>(ND)) {
+ if (const VarDecl *CVD = VD->getCanonicalDecl()) {
+ if (CheckSystemLoc(CVD->getLocStart()))
+ return nullptr;
+ }
+ }
+ // Declarations from other languages can't be renamed.
+ if (const ExternalSourceSymbolAttr *ESSA = getExternalSymAttr(ND)) {
+ Diag(diag::err_rename_external_source_symbol) << ND->getDeclName()
+ << ESSA->getLanguage();
+ return nullptr;
+ }
+ // Methods that override methods from system headers can't be renamed.
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) {
+ if (overridesSystemMethod(MD, Context.getSourceManager())) {
+ Diag(diag::err_method_rename_override_sys_framework) << ND->getDeclName();
+ return nullptr;
+ }
+ }
+ return ND;
+}
+
+const NamedDecl *getNamedDeclWithUSR(const ASTContext &Context, StringRef USR) {
+ // TODO: Remove in favour of the new converter.
+ OccurrenceCheckerType USRChecker =
+ [USR](const NamedDecl *Decl, SourceLocation Start, SourceLocation End) {
+ return USR == getUSRForDecl(Decl);
+ };
+ NamedDeclFindingASTVisitor Visitor(USRChecker, Context);
+
+ for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
+ Visitor.TraverseDecl(CurrDecl);
+ if (Visitor.isDone())
+ break;
+ }
+
+ // Don't need to visit nested name specifiers as they refer to previously
+ // declared declarations that we've already seen.
+ return Visitor.getNamedDecl();
+}
+
+std::string getUSRForDecl(const Decl *Decl) {
+ llvm::SmallVector<char, 128> Buff;
+
+ // FIXME: Add test for the nullptr case.
+ if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
+ return "";
+
+ return std::string(Buff.data(), Buff.size());
+}
+
+} // end namespace rename
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactoring/CMakeLists.txt b/lib/Tooling/Refactoring/CMakeLists.txt
index 288582f..68c3004 100644
--- a/lib/Tooling/Refactoring/CMakeLists.txt
+++ b/lib/Tooling/Refactoring/CMakeLists.txt
@@ -3,7 +3,7 @@
Support
)
-add_clang_library(clangToolingRefactor
+add_clang_library(clangToolingRefactoring
AtomicChange.cpp
Rename/RenamingAction.cpp
Rename/USRFinder.cpp
diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt
index 27cb83a..470f190 100644
--- a/runtime/CMakeLists.txt
+++ b/runtime/CMakeLists.txt
@@ -108,7 +108,7 @@
USES_TERMINAL)
# Add top-level targets that build specific compiler-rt runtimes.
- set(COMPILER_RT_RUNTIMES asan builtins dfsan lsan msan profile tsan ubsan)
+ set(COMPILER_RT_RUNTIMES asan builtins dfsan lsan msan profile tsan ubsan ubsan-minimal)
foreach(runtime ${COMPILER_RT_RUNTIMES})
get_ext_project_build_command(build_runtime_cmd ${runtime})
add_custom_target(${runtime}
@@ -125,7 +125,7 @@
# Add top-level targets for various compiler-rt test suites.
set(COMPILER_RT_TEST_SUITES check-asan check-asan-dynamic check-dfsan
- check-lsan check-msan check-sanitizer check-tsan check-ubsan
+ check-lsan check-msan check-sanitizer check-tsan check-ubsan check-ubsan-minimal
check-profile check-cfi check-cfi-and-supported check-safestack)
foreach(test_suite ${COMPILER_RT_TEST_SUITES})
get_ext_project_build_command(run_test_suite ${test_suite})
diff --git a/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes b/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes
new file mode 100644
index 0000000..ccdc4e1
--- /dev/null
+++ b/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodB"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes b/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes
new file mode 100644
index 0000000..d547317
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders/APINotes.apinotes
@@ -0,0 +1,4 @@
+Name: SomeBrokenLib
+Functions:
+ - Name: do_something_with_pointers
+ Nu llabilityOfRet: O
diff --git a/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h b/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h
new file mode 100644
index 0000000..b09c6f6
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders/SomeBrokenLib.h
@@ -0,0 +1,6 @@
+#ifndef SOME_BROKEN_LIB_H
+#define SOME_BROKEN_LIB_H
+
+void do_something_with_pointers(int *ptr1, int *ptr2);
+
+#endif // SOME_BROKEN_LIB_H
diff --git a/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes b/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes
new file mode 100644
index 0000000..33eeaaa
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders2/APINotes.apinotes
@@ -0,0 +1,7 @@
+Name: SomeBrokenLib
+Functions:
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+
diff --git a/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h b/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h
new file mode 100644
index 0000000..b09c6f6
--- /dev/null
+++ b/test/APINotes/Inputs/BrokenHeaders2/SomeBrokenLib.h
@@ -0,0 +1,6 @@
+#ifndef SOME_BROKEN_LIB_H
+#define SOME_BROKEN_LIB_H
+
+void do_something_with_pointers(int *ptr1, int *ptr2);
+
+#endif // SOME_BROKEN_LIB_H
diff --git a/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes
new file mode 100644
index 0000000..336f168
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes
@@ -0,0 +1,6 @@
+Name: SimpleKit
+Tags:
+- Name: RenamedAgainInAPINotesA
+ SwiftName: SuccessfullyRenamedA
+- Name: RenamedAgainInAPINotesB
+ SwiftName: SuccessfullyRenamedB
diff --git a/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h b/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h
new file mode 100644
index 0000000..c30a1e7
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h
@@ -0,0 +1,7 @@
+struct RenamedAgainInAPINotesA {
+ int field;
+} __attribute__((swift_name("bad")));
+
+struct __attribute__((swift_name("bad"))) RenamedAgainInAPINotesB {
+ int field;
+};
diff --git a/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..2d07e76
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module SimpleKit {
+ umbrella header "SimpleKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
new file mode 100644
index 0000000..817af12
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
@@ -0,0 +1,74 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ Properties:
+ - Name: intValue
+ PropertyKind: Instance
+ Availability: none
+ AvailabilityMsg: "wouldn't work anyway"
+ - Name: nonnullAInstance
+ PropertyKind: Instance
+ Nullability: N
+ - Name: nonnullAClass
+ PropertyKind: Class
+ Nullability: N
+ - Name: nonnullABoth
+ Nullability: N
+ - Name: B
+ Availability: none
+ AvailabilityMsg: "just don't"
+ - Name: C
+ Methods:
+ - Selector: "initWithA:"
+ MethodKind: Instance
+ DesignatedInit: true
+ - Name: OverriddenTypes
+ Methods:
+ - Selector: "methodToMangle:second:"
+ MethodKind: Instance
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'SOMEKIT_DOUBLE *'
+ - Position: 1
+ Type: 'float *'
+ Properties:
+ - Name: intPropertyToMangle
+ PropertyKind: Instance
+ Type: 'double *'
+Functions:
+ - Name: global_int_fun
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'double *'
+ - Position: 1
+ Type: 'float *'
+Globals:
+ - Name: global_int_ptr
+ Type: 'double *'
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: O
+ Nullability: [ O, S ]
+ Properties:
+ - Name: explicitNonnullInstance
+ PropertyKind: Instance
+ Nullability: O
+ - Name: explicitNullableInstance
+ PropertyKind: Instance
+ Nullability: N
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes
new file mode 100644
index 0000000..28ede9d
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit_private.apinotes
@@ -0,0 +1,15 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "privateTransform:input:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ Properties:
+ - Name: internalProperty
+ Nullability: N
+Protocols:
+ - Name: InternalProtocol
+ Availability: none
+ AvailabilityMsg: "not for you"
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
new file mode 100644
index 0000000..ff88fdb
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.apinotes
@@ -0,0 +1,98 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ - Selector: "implicitGetOnlyInstance"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "implicitGetOnlyClass"
+ MethodKind: Class
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "implicitGetSetInstance"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "implicitGetSetClass"
+ MethodKind: Class
+ Availability: none
+ AvailabilityMsg: "getter gone"
+ - Selector: "setImplicitGetSetInstance:"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "setter gone"
+ - Selector: "setImplicitGetSetClass:"
+ MethodKind: Class
+ Availability: none
+ AvailabilityMsg: "setter gone"
+ Properties:
+ - Name: intValue
+ PropertyKind: Instance
+ Availability: none
+ AvailabilityMsg: "wouldn't work anyway"
+ - Name: nonnullAInstance
+ PropertyKind: Instance
+ Nullability: N
+ - Name: nonnullAClass
+ PropertyKind: Class
+ Nullability: N
+ - Name: nonnullABoth
+ Nullability: N
+ - Name: B
+ Availability: none
+ AvailabilityMsg: "just don't"
+ - Name: C
+ Methods:
+ - Selector: "initWithA:"
+ MethodKind: Instance
+ DesignatedInit: true
+ - Name: OverriddenTypes
+ Methods:
+ - Selector: "methodToMangle:second:"
+ MethodKind: Instance
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'SOMEKIT_DOUBLE *'
+ - Position: 1
+ Type: 'float *'
+ Properties:
+ - Name: intPropertyToMangle
+ PropertyKind: Instance
+ Type: 'double *'
+Functions:
+ - Name: global_int_fun
+ ResultType: 'char *'
+ Parameters:
+ - Position: 0
+ Type: 'double *'
+ - Position: 1
+ Type: 'float *'
+Globals:
+ - Name: global_int_ptr
+ Type: 'double (*)(int, int)'
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: A
+ Methods:
+ - Selector: "transform:integer:"
+ MethodKind: Instance
+ NullabilityOfRet: O
+ Nullability: [ O, S ]
+ Properties:
+ - Name: explicitNonnullInstance
+ PropertyKind: Instance
+ Nullability: O
+ - Name: explicitNullableInstance
+ PropertyKind: Instance
+ Nullability: N
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
new file mode 100644
index 0000000..1a192f5
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKit.h
@@ -0,0 +1,60 @@
+#ifndef SOMEKIT_H
+#define SOMEKIT_H
+
+__attribute__((objc_root_class))
+@interface A
+-(A*)transform:(A*)input;
+-(A*)transform:(A*)input integer:(int)integer;
+
+@property (nonatomic, readonly, retain) A* someA;
+@property (nonatomic, retain) A* someOtherA;
+
+@property (nonatomic) int intValue;
+@end
+
+@interface B : A
+@end
+
+@interface C : A
+- (instancetype)init;
+- (instancetype)initWithA:(A*)a;
+@end
+
+@interface ProcessInfo : A
++(instancetype)processInfo;
+@end
+
+@interface A(NonNullProperties)
+@property (nonatomic, readwrite, retain) A *nonnullAInstance;
+@property (class, nonatomic, readwrite, retain) A *nonnullAInstance;
+
+@property (nonatomic, readwrite, retain) A *nonnullAClass;
+@property (class, nonatomic, readwrite, retain) A *nonnullAClass;
+
+@property (nonatomic, readwrite, retain) A *nonnullABoth;
+@property (class, nonatomic, readwrite, retain) A *nonnullABoth;
+@end
+
+#import <SomeKit/SomeKitExplicitNullability.h>
+
+extern int *global_int_ptr;
+
+int *global_int_fun(int *ptr, int *ptr2);
+
+#define SOMEKIT_DOUBLE double
+
+__attribute__((objc_root_class))
+@interface OverriddenTypes
+-(int *)methodToMangle:(int *)ptr1 second:(int *)ptr2;
+@property int *intPropertyToMangle;
+@end
+
+@interface A(ImplicitGetterSetters)
+@property (nonatomic, readonly, retain) A *implicitGetOnlyInstance;
+@property (class, nonatomic, readonly, retain) A *implicitGetOnlyClass;
+
+@property (nonatomic, readwrite, retain) A *implicitGetSetInstance;
+@property (class, nonatomic, readwrite, retain) A *implicitGetSetClass;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h
new file mode 100644
index 0000000..40be241
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitExplicitNullability.h
@@ -0,0 +1,5 @@
+@interface A(ExplicitNullabilityProperties)
+@property (nonatomic, readwrite, retain, nonnull) A *explicitNonnullInstance;
+@property (nonatomic, readwrite, retain, nullable) A *explicitNullableInstance;
+@end
+
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h
new file mode 100644
index 0000000..d1eeb61
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Headers/SomeKitForNullAnnotation.h
@@ -0,0 +1,55 @@
+#ifndef SOMEKIT_H
+#define SOMEKIT_H
+
+#define ROOT_CLASS __attribute__((objc_root_class))
+
+ROOT_CLASS
+@interface A
+-(A*)transform:(A*)input;
+-(A*)transform:(A*)input integer:(int)integer;
+
+@property (nonatomic, readonly, retain) A* someA;
+@property (nonatomic, retain) A* someOtherA;
+
+@property (nonatomic) int intValue;
+@end
+
+@interface B : A
+@end
+
+@interface C : A
+- (instancetype)init;
+- (instancetype)initWithA:(A*)a;
+@end
+
+
+@interface MyClass : A
+- Inst;
++ Clas;
+@end
+
+struct CGRect {
+ float origin;
+ float size;
+};
+typedef struct CGRect NSRect;
+
+@interface I
+- (void) Meth : (NSRect[4])exposedRects;
+- (void) Meth1 : (const I*)exposedRects;
+- (void) Meth2 : (const I*)exposedRects;
+- (void) Meth3 : (I*)exposedRects;
+- (const I*) Meth4;
+- (const I*) Meth5 : (int) Arg1 : (const I*)Arg2 : (double)Arg3 : (const I*) Arg4 :(const volatile id) Arg5;
+- (volatile const I*) Meth6 : (const char *)Arg1 : (const char *)Arg2 : (double)Arg3 : (const I*) Arg4 :(const volatile id) Arg5;
+@end
+
+@class NSURL, NSArray, NSError;
+@interface INTF_BLOCKS
+ + (void)getNonLocalVersionsOfItemAtURL:(NSURL *)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler;
+ + (void *)getNonLocalVersionsOfItemAtURL2:(NSURL *)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler;
+ + (NSError **)getNonLocalVersionsOfItemAtURL3:(int)url completionHandler:(void (^)(NSArray *nonLocalFileVersions, NSError *error))completionHandler;
+ + (id)getNonLocalVersionsOfItemAtURL4:(NSURL *)url completionHandler:(void (^)(int nonLocalFileVersions, NSError *error, NSURL*))completionHandler;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..3abee2d
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module SomeKit {
+ umbrella header "SomeKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap
new file mode 100644
index 0000000..bbda9d0
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module.private.modulemap
@@ -0,0 +1,8 @@
+module SomeKit.Private {
+ header "SomeKit_Private.h"
+ export *
+
+ explicit module NullAnnotation {
+ header "SomeKit_PrivateForNullAnnotation.h"
+ }
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap
new file mode 100644
index 0000000..e310343
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/Modules/module_private.modulemap
@@ -0,0 +1,8 @@
+explicit framework module SomeKit.Private {
+ header "SomeKit_Private.h"
+ explicit NullAnnotation { header "SomeKit_PrivateForNullAnnotation.h" }
+ export *
+ module * { export * }
+syntax error
+
+}
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h
new file mode 100644
index 0000000..c761112
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_Private.h
@@ -0,0 +1,16 @@
+#ifndef SOMEKIT_PRIVATE_H
+#define SOMEKIT_PRIVATE_H
+
+#import <SomeKit/SomeKit.h>
+
+@interface A(Private)
+-(A*)privateTransform:(A*)input;
+
+@property (nonatomic) A* internalProperty;
+@end
+
+@protocol InternalProtocol
+@end
+
+#endif
+
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h
new file mode 100644
index 0000000..bae4456
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_PrivateForNullAnnotation.h
@@ -0,0 +1,17 @@
+#ifndef SOMEKIT_PRIVATE_H
+#define SOMEKIT_PRIVATE_H
+
+#import <SomeKit/SomeKitForNullAnnotation.h>
+
+@interface A(Private)
+-(A*)privateTransform:(A*)input;
+
+@property (nonatomic) A* internalProperty;
+@end
+
+@protocol InternalProtocol
+- (id) MomeMethod;
+@end
+
+#endif
+
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes
new file mode 100644
index 0000000..28ede9d
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/PrivateHeaders/SomeKit_private.apinotes
@@ -0,0 +1,15 @@
+Name: SomeKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "privateTransform:input:"
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ Properties:
+ - Name: internalProperty
+ Nullability: N
+Protocols:
+ - Name: InternalProtocol
+ Availability: none
+ AvailabilityMsg: "not for you"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes
new file mode 100644
index 0000000..2ad546b
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodA"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
new file mode 100644
index 0000000..2ad546b
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodA"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
new file mode 100644
index 0000000..3911d76
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
@@ -0,0 +1,9 @@
+#ifndef SOME_OTHER_KIT_H
+
+__attribute__((objc_root_class))
+@interface A
+-(void)methodA;
+-(void)methodB;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..0aaad92
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module SomeOtherKit {
+ umbrella header "SomeOtherKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
new file mode 100644
index 0000000..572c714
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.apinotes
@@ -0,0 +1,156 @@
+Name: VersionedKit
+Classes:
+ - Name: TestProperties
+ SwiftObjCMembers: true
+ Properties:
+ - Name: accessorsOnly
+ PropertyKind: Instance
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyForClass
+ PropertyKind: Class
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyExceptInVersion3
+ PropertyKind: Instance
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyForClassExceptInVersion3
+ PropertyKind: Class
+ SwiftImportAsAccessors: true
+Functions:
+ - Name: unversionedRenameDUMP
+ SwiftName: 'unversionedRename_NOTES()'
+Tags:
+ - Name: APINotedFlagEnum
+ FlagEnum: true
+ - Name: APINotedOpenEnum
+ EnumExtensibility: open
+ - Name: APINotedClosedEnum
+ EnumExtensibility: closed
+ - Name: SoonToBeCFEnum
+ EnumKind: CFEnum
+ - Name: SoonToBeNSEnum
+ EnumKind: NSEnum
+ - Name: SoonToBeCFOptions
+ EnumKind: CFOptions
+ - Name: SoonToBeNSOptions
+ EnumKind: NSOptions
+ - Name: SoonToBeCFClosedEnum
+ EnumKind: CFClosedEnum
+ - Name: SoonToBeNSClosedEnum
+ EnumKind: NSClosedEnum
+ - Name: UndoAllThatHasBeenDoneToMe
+ EnumKind: none
+Typedefs:
+ - Name: MultiVersionedTypedef34Notes
+ SwiftName: MultiVersionedTypedef34Notes_NEW
+ - Name: MultiVersionedTypedef345Notes
+ SwiftName: MultiVersionedTypedef345Notes_NEW
+ - Name: MultiVersionedTypedef4Notes
+ SwiftName: MultiVersionedTypedef4Notes_NEW
+ - Name: MultiVersionedTypedef45Notes
+ SwiftName: MultiVersionedTypedef45Notes_NEW
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: MyReferenceType
+ SwiftBridge: ''
+ - Name: TestGenericDUMP
+ SwiftImportAsNonGeneric: true
+ - Name: TestProperties
+ SwiftObjCMembers: false
+ Properties:
+ - Name: accessorsOnlyInVersion3
+ PropertyKind: Instance
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyForClassInVersion3
+ PropertyKind: Class
+ SwiftImportAsAccessors: true
+ - Name: accessorsOnlyExceptInVersion3
+ PropertyKind: Instance
+ SwiftImportAsAccessors: false
+ - Name: accessorsOnlyForClassExceptInVersion3
+ PropertyKind: Class
+ SwiftImportAsAccessors: false
+ - Name: Swift3RenamedOnlyDUMP
+ SwiftName: SpecialSwift3Name
+ - Name: Swift3RenamedAlsoDUMP
+ SwiftName: SpecialSwift3Also
+ Functions:
+ - Name: moveToPointDUMP
+ SwiftName: 'moveTo(a:b:)'
+ - Name: acceptClosure
+ Parameters:
+ - Position: 0
+ NoEscape: false
+ - Name: privateFunc
+ SwiftPrivate: false
+ Tags:
+ - Name: MyErrorCode
+ NSErrorDomain: ''
+ - Name: NewlyFlagEnum
+ FlagEnum: false
+ - Name: OpenToClosedEnum
+ EnumExtensibility: open
+ - Name: ClosedToOpenEnum
+ EnumExtensibility: closed
+ - Name: NewlyClosedEnum
+ EnumExtensibility: none
+ - Name: NewlyOpenEnum
+ EnumExtensibility: none
+ Typedefs:
+ - Name: MyDoubleWrapper
+ SwiftWrapper: none
+ - Name: MultiVersionedTypedef34
+ SwiftName: MultiVersionedTypedef34_3
+ - Name: MultiVersionedTypedef34Header
+ SwiftName: MultiVersionedTypedef34Header_3
+ - Name: MultiVersionedTypedef34Notes
+ SwiftName: MultiVersionedTypedef34Notes_3
+ - Name: MultiVersionedTypedef345
+ SwiftName: MultiVersionedTypedef345_3
+ - Name: MultiVersionedTypedef345Header
+ SwiftName: MultiVersionedTypedef345Header_3
+ - Name: MultiVersionedTypedef345Notes
+ SwiftName: MultiVersionedTypedef345Notes_3
+ - Version: 5
+ Typedefs:
+ - Name: MultiVersionedTypedef345
+ SwiftName: MultiVersionedTypedef345_5
+ - Name: MultiVersionedTypedef345Header
+ SwiftName: MultiVersionedTypedef345Header_5
+ - Name: MultiVersionedTypedef345Notes
+ SwiftName: MultiVersionedTypedef345Notes_5
+ - Name: MultiVersionedTypedef45
+ SwiftName: MultiVersionedTypedef45_5
+ - Name: MultiVersionedTypedef45Header
+ SwiftName: MultiVersionedTypedef45Header_5
+ - Name: MultiVersionedTypedef45Notes
+ SwiftName: MultiVersionedTypedef45Notes_5
+ - Version: 4 # Versions are deliberately ordered as "3, 5, 4" to catch bugs.
+ Classes:
+ - Name: Swift4RenamedDUMP
+ SwiftName: SpecialSwift4Name
+ Typedefs:
+ - Name: MultiVersionedTypedef34
+ SwiftName: MultiVersionedTypedef34_4
+ - Name: MultiVersionedTypedef34Header
+ SwiftName: MultiVersionedTypedef34Header_4
+ - Name: MultiVersionedTypedef34Notes
+ SwiftName: MultiVersionedTypedef34Notes_4
+ - Name: MultiVersionedTypedef345
+ SwiftName: MultiVersionedTypedef345_4
+ - Name: MultiVersionedTypedef345Header
+ SwiftName: MultiVersionedTypedef345Header_4
+ - Name: MultiVersionedTypedef345Notes
+ SwiftName: MultiVersionedTypedef345Notes_4
+ - Name: MultiVersionedTypedef4
+ SwiftName: MultiVersionedTypedef4_4
+ - Name: MultiVersionedTypedef4Header
+ SwiftName: MultiVersionedTypedef4Header_4
+ - Name: MultiVersionedTypedef4Notes
+ SwiftName: MultiVersionedTypedef4Notes_4
+ - Name: MultiVersionedTypedef45
+ SwiftName: MultiVersionedTypedef45_4
+ - Name: MultiVersionedTypedef45Header
+ SwiftName: MultiVersionedTypedef45Header_4
+ - Name: MultiVersionedTypedef45Notes
+ SwiftName: MultiVersionedTypedef45Notes_4
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
new file mode 100644
index 0000000..9ce9563
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Headers/VersionedKit.h
@@ -0,0 +1,137 @@
+void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
+
+void unversionedRenameDUMP(void) __attribute__((swift_name("unversionedRename_HEADER()")));
+
+void acceptClosure(void (^ __attribute__((noescape)) block)(void));
+
+void privateFunc(void) __attribute__((swift_private));
+
+typedef double MyDoubleWrapper __attribute__((swift_wrapper(struct)));
+
+#if __OBJC__
+@class NSString;
+
+extern NSString *MyErrorDomain;
+
+enum __attribute__((ns_error_domain(MyErrorDomain))) MyErrorCode {
+ MyErrorCodeFailed = 1
+};
+
+__attribute__((swift_bridge("MyValueType")))
+@interface MyReferenceType
+@end
+
+@interface TestProperties
+@property (nonatomic, readwrite, retain) id accessorsOnly;
+@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClass;
+
+@property (nonatomic, readwrite, retain) id accessorsOnlyInVersion3;
+@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassInVersion3;
+
+@property (nonatomic, readwrite, retain) id accessorsOnlyExceptInVersion3;
+@property (nonatomic, readwrite, retain, class) id accessorsOnlyForClassExceptInVersion3;
+@end
+
+@interface Base
+@end
+
+@interface TestGenericDUMP<Element> : Base
+- (Element)element;
+@end
+
+@interface Swift3RenamedOnlyDUMP
+@end
+
+__attribute__((swift_name("Swift4Name")))
+@interface Swift3RenamedAlsoDUMP
+@end
+
+@interface Swift4RenamedDUMP
+@end
+
+#endif
+
+
+enum __attribute__((flag_enum)) FlagEnum {
+ FlagEnumA = 1,
+ FlagEnumB = 2
+};
+
+enum __attribute__((flag_enum)) NewlyFlagEnum {
+ NewlyFlagEnumA = 1,
+ NewlyFlagEnumB = 2
+};
+
+enum APINotedFlagEnum {
+ APINotedFlagEnumA = 1,
+ APINotedFlagEnumB = 2
+};
+
+
+enum __attribute__((enum_extensibility(open))) OpenEnum {
+ OpenEnumA = 1,
+};
+
+enum __attribute__((enum_extensibility(open))) NewlyOpenEnum {
+ NewlyOpenEnumA = 1,
+};
+
+enum __attribute__((enum_extensibility(closed))) NewlyClosedEnum {
+ NewlyClosedEnumA = 1,
+};
+
+enum __attribute__((enum_extensibility(open))) ClosedToOpenEnum {
+ ClosedToOpenEnumA = 1,
+};
+
+enum __attribute__((enum_extensibility(closed))) OpenToClosedEnum {
+ OpenToClosedEnumA = 1,
+};
+
+enum APINotedOpenEnum {
+ APINotedOpenEnumA = 1,
+};
+
+enum APINotedClosedEnum {
+ APINotedClosedEnumA = 1,
+};
+
+
+enum SoonToBeCFEnum {
+ SoonToBeCFEnumA = 1
+};
+enum SoonToBeNSEnum {
+ SoonToBeNSEnumA = 1
+};
+enum SoonToBeCFOptions {
+ SoonToBeCFOptionsA = 1
+};
+enum SoonToBeNSOptions {
+ SoonToBeNSOptionsA = 1
+};
+enum SoonToBeCFClosedEnum {
+ SoonToBeCFClosedEnumA = 1
+};
+enum SoonToBeNSClosedEnum {
+ SoonToBeNSClosedEnumA = 1
+};
+enum UndoAllThatHasBeenDoneToMe {
+ UndoAllThatHasBeenDoneToMeA = 1
+} __attribute__((flag_enum)) __attribute__((enum_extensibility(closed)));
+
+
+typedef int MultiVersionedTypedef4;
+typedef int MultiVersionedTypedef4Notes;
+typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_NEW")));
+
+typedef int MultiVersionedTypedef34;
+typedef int MultiVersionedTypedef34Notes;
+typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_NEW")));
+
+typedef int MultiVersionedTypedef45;
+typedef int MultiVersionedTypedef45Notes;
+typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_NEW")));
+
+typedef int MultiVersionedTypedef345;
+typedef int MultiVersionedTypedef345Notes;
+typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_NEW")));
diff --git a/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..6d957fd
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/VersionedKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module VersionedKit {
+ umbrella header "VersionedKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/Headers/APINotes.apinotes b/test/APINotes/Inputs/Headers/APINotes.apinotes
new file mode 100644
index 0000000..08210fc
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/APINotes.apinotes
@@ -0,0 +1,18 @@
+Name: HeaderLib
+SwiftInferImportAsMember: true
+Functions:
+ - Name: custom_realloc
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ - Name: unavailable_function
+ Availability: none
+ AvailabilityMsg: "I beg you not to use this"
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+ Nullability: [ N, O ]
+
+Globals:
+ - Name: global_int
+ Nullability: N
+ - Name: unavailable_global_int
+ Availability: none
diff --git a/test/APINotes/Inputs/Headers/BrokenTypes.apinotes b/test/APINotes/Inputs/Headers/BrokenTypes.apinotes
new file mode 100644
index 0000000..00f7b50
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/BrokenTypes.apinotes
@@ -0,0 +1,10 @@
+Name: BrokenTypes
+Functions:
+ - Name: break_me_function
+ ResultType: 'int * with extra junk'
+ Parameters:
+ - Position: 0
+ Type: 'not_a_type'
+Globals:
+ - Name: break_me_variable
+ Type: 'double'
diff --git a/test/APINotes/Inputs/Headers/BrokenTypes.h b/test/APINotes/Inputs/Headers/BrokenTypes.h
new file mode 100644
index 0000000..fee054b
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/BrokenTypes.h
@@ -0,0 +1,8 @@
+#ifndef BROKEN_TYPES_H
+#define BROKEN_TYPES_H
+
+char break_me_function(void *ptr);
+
+extern char break_me_variable;
+
+#endif // BROKEN_TYPES_H
diff --git a/test/APINotes/Inputs/Headers/HeaderLib.apinotes b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
new file mode 100644
index 0000000..c822964
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
@@ -0,0 +1,37 @@
+Name: HeaderLib
+SwiftInferImportAsMember: true
+Functions:
+ - Name: custom_realloc
+ NullabilityOfRet: N
+ Nullability: [ N, S ]
+ - Name: unavailable_function
+ Availability: none
+ AvailabilityMsg: "I beg you not to use this"
+ - Name: do_something_with_pointers
+ NullabilityOfRet: O
+ Nullability: [ N, O ]
+ - Name: do_something_with_arrays
+ Parameters:
+ - Position: 0
+ Nullability: N
+ - Position: 1
+ Nullability: N
+ - Name: take_pointer_and_int
+ Parameters:
+ - Position: 0
+ Nullability: N
+ NoEscape: true
+ - Position: 1
+ NoEscape: true
+Globals:
+ - Name: global_int
+ Nullability: N
+ - Name: unavailable_global_int
+ Availability: none
+Tags:
+ - Name: unavailable_struct
+ Availability: none
+
+Typedefs:
+ - Name: unavailable_typedef
+ Availability: none
\ No newline at end of file
diff --git a/test/APINotes/Inputs/Headers/HeaderLib.h b/test/APINotes/Inputs/Headers/HeaderLib.h
new file mode 100644
index 0000000..8065249
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/HeaderLib.h
@@ -0,0 +1,19 @@
+#ifndef HEADER_LIB_H
+#define HEADER_LIB_H
+
+void *custom_realloc(void *member, unsigned size);
+
+int *global_int;
+
+int unavailable_function(void);
+int unavailable_global_int;
+
+void do_something_with_pointers(int *ptr1, int *ptr2);
+void do_something_with_arrays(int simple[], int nested[][2]);
+
+typedef int unavailable_typedef;
+struct unavailable_struct { int x, y, z; };
+
+void take_pointer_and_int(int *ptr1, int value);
+
+#endif
diff --git a/test/APINotes/Inputs/Headers/module.modulemap b/test/APINotes/Inputs/Headers/module.modulemap
new file mode 100644
index 0000000..54af0f5
--- /dev/null
+++ b/test/APINotes/Inputs/Headers/module.modulemap
@@ -0,0 +1,7 @@
+module HeaderLib {
+ header "HeaderLib.h"
+}
+
+module BrokenTypes {
+ header "BrokenTypes.h"
+}
diff --git a/test/APINotes/Inputs/os-availability.apinotes b/test/APINotes/Inputs/os-availability.apinotes
new file mode 100644
index 0000000..d59e79c
--- /dev/null
+++ b/test/APINotes/Inputs/os-availability.apinotes
@@ -0,0 +1,53 @@
+Name: Foundation
+Classes:
+ - Name: NSCountedSet
+ Availability: iOS
+ Methods:
+ - Selector: 'initWithCapacity:'
+ MethodKind: Instance
+ DesignatedInit: true
+ - Name: NSArray
+ Methods:
+ - Selector: 'init'
+ MethodKind: Instance
+ DesignatedInit: true
+ - Selector: 'initWithObjects:'
+ MethodKind: Instance
+ DesignatedInit: true
+ Availability: iOS
+ - Selector: 'initWithObjects:count:'
+ MethodKind: Instance
+ DesignatedInit: true
+ Availability: iOS
+ Properties:
+ - Name: 'familyNameios'
+ Nullability: N
+ Availability: iOS
+ - Name: 'fontName'
+ Nullability: N
+Protocols:
+ - Name: UIApplicationDelegate
+ AuditedForNullability: true
+ Methods:
+ - Selector: 'application:willFinishLaunchingWithOptions:'
+ MethodKind: Instance
+ Nullability: [ N, U ]
+ - Name: UIApplicationDelegateIOS
+ Availability: iOS
+ AuditedForNullability: true
+ Methods:
+ - Selector: 'application:willFinishLaunchingWithOptions:'
+ MethodKind: Instance
+ Nullability: [ N, U ]
+Functions:
+ - Name: NSAvailableWindowDepthsiOS
+ NullabilityOfRet: N
+ Availability: iOS
+ - Name: NSAvailableWindowDepths
+ NullabilityOfRet: N
+Globals:
+ - Name: NSCalibratedWhiteColorSpace
+ Nullability: N
+ Availability: OSX
+ AvailabilityMsg: ''
+
diff --git a/test/APINotes/Inputs/roundtrip.apinotes b/test/APINotes/Inputs/roundtrip.apinotes
new file mode 100644
index 0000000..65e3928
--- /dev/null
+++ b/test/APINotes/Inputs/roundtrip.apinotes
@@ -0,0 +1,185 @@
+---
+Name: AppKit
+Availability: available
+AvailabilityMsg: ''
+SwiftInferImportAsMember: true
+Classes:
+ - Name: NSCell
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ SwiftImportAsNonGeneric: true
+ SwiftObjCMembers: false
+ Methods:
+ - Selector: 'cellWithImage:'
+ MethodKind: Class
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ ResultType: id
+ - Selector: init
+ MethodKind: Instance
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: true
+ SwiftName: ''
+ DesignatedInit: true
+ - Selector: 'initImageCell:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ DesignatedInit: true
+ - Selector: 'initTextCell:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ DesignatedInit: true
+ - Selector: 'initWithCoder:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: U
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ DesignatedInit: true
+ Required: true
+ - Name: NSView
+ AuditedForNullability: true
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ SwiftBridge: View
+ SwiftObjCMembers: true
+ Methods:
+ - Selector: 'addSubview:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ - Selector: 'addSubview:positioned:relativeTo:'
+ MethodKind: Instance
+ Parameters:
+ - Position: 0
+ NoEscape: false
+ - Position: 1
+ - Position: 2
+ NoEscape: true
+ Type: id
+ Nullability: [ N, N, O ]
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: ''
+ - Selector: 'beginDraggingSessionWithItems:event:source:'
+ MethodKind: Instance
+ Nullability: [ U, U, N ]
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: 'beginDragginSession(_:event:source:)'
+ Properties:
+ - Name: enclosingScrollView
+ PropertyKind: Instance
+ Nullability: O
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: enclosing
+ Type: id
+ - Name: makeBackingLayer
+ PropertyKind: Class
+ Nullability: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: ''
+ SwiftImportAsAccessors: false
+Functions:
+ - Name: NSAvailableWindowDepths
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: 'availableWindowDepths()'
+ ResultType: NSInteger
+Globals:
+ - Name: NSCalibratedWhiteColorSpace
+ Nullability: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: calibratedWhite
+ Type: id
+Enumerators:
+ - Name: NSColorRed
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: Red
+Tags:
+ - Name: NSSomeEnum
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: SomeEnum
+ NSErrorDomain: some_error_domain
+ EnumExtensibility: closed
+ FlagEnum: false
+ - Name: NSSomeStruct
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: SomeStruct
+ NSErrorDomain: ''
+Typedefs:
+ - Name: NSTypedef
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: Typedef
+ SwiftBridge: ''
+ SwiftWrapper: struct
+SwiftVersions:
+ - Version: 3.0
+ Classes:
+ - Name: NSCell
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: false
+ SwiftName: NSBox
+ SwiftBridge: ''
+ Methods:
+ - Selector: init
+ MethodKind: Instance
+ NullabilityOfRet: N
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftPrivate: true
+ SwiftName: ''
+ DesignatedInit: true
+ - Name: NSView
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: ''
+ Properties:
+ - Name: makeBackingLayer
+ PropertyKind: Class
+ Availability: available
+ AvailabilityMsg: ''
+ SwiftName: ''
+ SwiftImportAsAccessors: true
diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m
new file mode 100644
index 0000000..7231eb4
--- /dev/null
+++ b/test/APINotes/availability.m
@@ -0,0 +1,48 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#include "HeaderLib.h"
+#import <SomeKit/SomeKit.h>
+#import <SomeKit/SomeKit_Private.h>
+
+int main() {
+ int i;
+ i = unavailable_function(); // expected-error{{'unavailable_function' is unavailable: I beg you not to use this}}
+ // expected-note@HeaderLib.h:8{{'unavailable_function' has been explicitly marked unavailable here}}
+ i = unavailable_global_int; // expected-error{{'unavailable_global_int' is unavailable}}
+ // expected-note@HeaderLib.h:9{{'unavailable_global_int' has been explicitly marked unavailable here}}
+
+ unavailable_typedef t; // expected-error{{'unavailable_typedef' is unavailable}}
+ // expected-note@HeaderLib.h:14{{'unavailable_typedef' has been explicitly marked unavailable here}}
+
+ struct unavailable_struct s; // expected-error{{'unavailable_struct' is unavailable}}
+ // expected-note@HeaderLib.h:15{{'unavailable_struct' has been explicitly marked unavailable here}}
+
+ B *b = 0; // expected-error{{'B' is unavailable: just don't}}
+ // expected-note@SomeKit/SomeKit.h:15{{'B' has been explicitly marked unavailable here}}
+
+ id<InternalProtocol> proto = 0; // expected-error{{'InternalProtocol' is unavailable: not for you}}
+ // expected-note@SomeKit/SomeKit_Private.h:12{{'InternalProtocol' has been explicitly marked unavailable here}}
+
+ A *a = 0;
+ i = a.intValue; // expected-error{{intValue' is unavailable: wouldn't work anyway}}
+ // expected-note@SomeKit/SomeKit.h:12{{'intValue' has been explicitly marked unavailable here}}
+
+ [a transform:a]; // expected-error{{'transform:' is unavailable: anything but this}}
+ // expected-note@SomeKit/SomeKit.h:6{{'transform:' has been explicitly marked unavailable here}}
+
+ [a implicitGetOnlyInstance]; // expected-error{{'implicitGetOnlyInstance' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:53{{'implicitGetOnlyInstance' has been explicitly marked unavailable here}}
+ [A implicitGetOnlyClass]; // expected-error{{'implicitGetOnlyClass' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:54{{'implicitGetOnlyClass' has been explicitly marked unavailable here}}
+ [a implicitGetSetInstance]; // expected-error{{'implicitGetSetInstance' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:56{{'implicitGetSetInstance' has been explicitly marked unavailable here}}
+ [a setImplicitGetSetInstance: a]; // expected-error{{'setImplicitGetSetInstance:' is unavailable: setter gone}}
+ // expected-note@SomeKit/SomeKit.h:56{{'setImplicitGetSetInstance:' has been explicitly marked unavailable here}}
+ [A implicitGetSetClass]; // expected-error{{'implicitGetSetClass' is unavailable: getter gone}}
+ // expected-note@SomeKit/SomeKit.h:57{{'implicitGetSetClass' has been explicitly marked unavailable here}}
+ [A setImplicitGetSetClass: a]; // expected-error{{'setImplicitGetSetClass:' is unavailable: setter gone}}
+ // expected-note@SomeKit/SomeKit.h:57{{'setImplicitGetSetClass:' has been explicitly marked unavailable here}}
+ return 0;
+}
+
diff --git a/test/APINotes/broken_types.m b/test/APINotes/broken_types.m
new file mode 100644
index 0000000..ee33ff7
--- /dev/null
+++ b/test/APINotes/broken_types.m
@@ -0,0 +1,19 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s 2> %t.err
+// RUN: FileCheck %s < %t.err
+
+#include "BrokenTypes.h"
+
+// CHECK: <API Notes>:1:1: error: unknown type name 'not_a_type'
+// CHECK-NEXT: not_a_type
+// CHECK-NEXT: ^
+
+// CHECK: <API Notes>:1:7: error: unparsed tokens following type
+// CHECK-NEXT: int * with extra junk
+// CHECK-NEXT: ^
+
+// CHECK: BrokenTypes.h:4:6: error: API notes replacement type 'int *' has a different size from original type 'char'
+
+// CHECK: BrokenTypes.h:6:13: error: API notes replacement type 'double' has a different size from original type 'char'
+
+// CHECK: 5 errors generated.
diff --git a/test/APINotes/module-cache.m b/test/APINotes/module-cache.m
new file mode 100644
index 0000000..19d6a27
--- /dev/null
+++ b/test/APINotes/module-cache.m
@@ -0,0 +1,100 @@
+// RUN: rm -rf %t
+
+// Set up directories
+// RUN: mkdir -p %t/APINotes
+// RUN: cp %S/Inputs/APINotes/SomeOtherKit.apinotes %t/APINotes/SomeOtherKit.apinotes
+// RUN: mkdir -p %t/Frameworks
+// RUN: cp -r %S/Inputs/Frameworks/SomeOtherKit.framework %t/Frameworks
+
+// First build: check that 'methodB' is unavailable but 'methodA' is available.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Do it again; now we're using caches.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Add a blank line to the header to force the module to rebuild, without
+// (yet) changing API notes.
+// RUN: echo >> %t/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Change the API notes file, after the module has rebuilt once.
+// RUN: echo ' - Selector: "methodA"' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' MethodKind: Instance' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' Availability: none' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' AvailabilityMsg: "not here either"' >> %t/APINotes/SomeOtherKit.apinotes
+
+// Build again: check that both methods are now unavailable and that the module rebuilt.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log
+
+// Run the build again: check that both methods are now unavailable
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -F %t/Frameworks %s > %t/after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log
+
+// Set up a directory with pre-compiled API notes.
+// RUN: mkdir -p %t/CompiledAPINotes
+// RUN: rm -rf %t/ModulesCache
+// RUN: rm -rf %t/APINotesCache
+// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %S/Inputs/APINotes/SomeOtherKit.apinotes
+
+// First build: check that 'methodB' is unavailable but 'methodA' is available.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log
+
+// Do it again; now we're using caches.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log
+
+// Compile a new API notes file to replace the old one.
+// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %t/APINotes/SomeOtherKit.apinotes
+
+// Build again: check that both methods are now unavailable and that the module rebuilt.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log
+
+// Run the build again: check that both methods are now unavailable
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -F %t/Frameworks %s > %t/compiled-after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log
+
+@import SomeOtherKit;
+
+void test(A *a) {
+ // CHECK-METHODA: error: 'methodA' is unavailable: not here either
+ [a methodA];
+
+ // CHECK-METHODB: error: 'methodB' is unavailable: anything but this
+ [a methodB];
+}
+
+// CHECK-REBUILD: remark: building module{{.*}}SomeOtherKit
+
+// CHECK-WITHOUT-REBUILD-NOT: remark: building module{{.*}}SomeOtherKit
+
+// CHECK-ONE-ERROR: 1 error generated.
+// CHECK-TWO-ERRORS: 2 errors generated.
+
diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c
new file mode 100644
index 0000000..e07fc2e
--- /dev/null
+++ b/test/APINotes/nullability.c
@@ -0,0 +1,21 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#include "HeaderLib.h"
+
+int main() {
+ custom_realloc(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+ int i = 0;
+ do_something_with_pointers(&i, 0);
+ do_something_with_pointers(0, &i); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ extern void *p;
+ do_something_with_arrays(0, p); // expected-warning{{null passed to a callee that requires a non-null argument}}
+ do_something_with_arrays(p, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ take_pointer_and_int(0, 0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ float *fp = global_int; // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'int * _Nonnull'}}
+ return 0;
+}
+
diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m
new file mode 100644
index 0000000..65c9c2c
--- /dev/null
+++ b/test/APINotes/nullability.m
@@ -0,0 +1,44 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+// Test with Swift version 3.0. This should only affect the few APIs that have an entry in the 3.0 tables.
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-swift-version=3.0 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify -DSWIFT_VERSION_3_0 -fmodules-ignore-macro=SWIFT_VERSION_3_0
+
+#import <SomeKit/SomeKit.h>
+
+int main() {
+ A *a;
+
+#if SWIFT_VERSION_3_0
+ float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A * _Nullable'}}
+ [a transform: 0 integer: 0];
+#else
+ float *fp = // expected-warning{{incompatible pointer types initializing 'float *' with an expression of type 'A *'}}
+ [a transform: 0 integer: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+#endif
+
+ [a setNonnullAInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [A setNonnullAInstance: 0]; // no warning
+
+ [a setNonnullAClass: 0]; // no warning
+ [A setNonnullAClass: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ [a setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [A setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+ [a setInternalProperty: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
+#if SWIFT_VERSION_3_0
+ // Version 3 information overrides header information.
+ [a setExplicitNonnullInstance: 0]; // okay
+ [a setExplicitNullableInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+#else
+ // Header information overrides unversioned information.
+ [a setExplicitNonnullInstance: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [a setExplicitNullableInstance: 0]; // okay
+#endif
+
+ return 0;
+}
+
diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m
new file mode 100644
index 0000000..24b317c
--- /dev/null
+++ b/test/APINotes/objc_designated_inits.m
@@ -0,0 +1,17 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+
+#include "HeaderLib.h"
+#import <SomeKit/SomeKit.h>
+
+@interface CSub : C
+-(instancetype)initWithA:(A*)a;
+@end
+
+@implementation CSub
+-(instancetype)initWithA:(A*)a { // expected-warning{{designated initializer missing a 'super' call to a designated initializer of the super class}}
+ // expected-note@SomeKit/SomeKit.h:20 2{{method marked as designated initializer of the class here}}
+ self = [super init]; // expected-warning{{designated initializer invoked a non-designated initializer}}
+ return self;
+}
+@end
diff --git a/test/APINotes/properties.m b/test/APINotes/properties.m
new file mode 100644
index 0000000..0b3d244
--- /dev/null
+++ b/test/APINotes/properties.m
@@ -0,0 +1,45 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' | FileCheck -check-prefix=CHECK -check-prefix=CHECK-4 %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fblocks -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'TestProperties::' -fapinotes-swift-version=3 | FileCheck -check-prefix=CHECK -check-prefix=CHECK-3 %s
+
+// I know, FileChecking an AST dump is brittle. However, the attributes being
+// tested aren't used for anything by Clang, and don't even have a spelling.
+
+@import VersionedKit;
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnly 'id'
+// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClass 'id'
+// CHECK-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyInVersion3 'id'
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}}
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassInVersion3 'id'
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedAttr {{.+}} 3.0{{$}}
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyExceptInVersion3 'id'
+// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}}
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}}
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: ObjCPropertyDecl {{.+}} accessorsOnlyForClassExceptInVersion3 'id'
+// CHECK-3-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}}
+// CHECK-3-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftImportPropertyAsAccessorsAttr {{.+}} Implicit
+// CHECK-4-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}}
+// CHECK-NOT: Attr
+
+// CHECK-LABEL: Decl
diff --git a/test/APINotes/search-order.m b/test/APINotes/search-order.m
new file mode 100644
index 0000000..aa2f21a
--- /dev/null
+++ b/test/APINotes/search-order.m
@@ -0,0 +1,25 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules %S/Inputs/APINotes -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+
+@import SomeOtherKit;
+
+void test(A *a) {
+#if FROM_FRAMEWORK
+ [a methodA]; // expected-error{{unavailable}}
+ [a methodB];
+
+ // expected-note@SomeOtherKit/SomeOtherKit.h:5{{'methodA' has been explicitly marked unavailable here}}
+#elif FROM_SEARCH_PATH
+ [a methodA];
+ [a methodB]; // expected-error{{unavailable}}
+
+ // expected-note@SomeOtherKit/SomeOtherKit.h:6{{'methodB' has been explicitly marked unavailable here}}
+#else
+# error Not something we need to test
+#endif
+}
diff --git a/test/APINotes/types.m b/test/APINotes/types.m
new file mode 100644
index 0000000..a0f728b
--- /dev/null
+++ b/test/APINotes/types.m
@@ -0,0 +1,28 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fdisable-module-hash -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/SimpleKit.pcm | FileCheck %s
+
+#import <SomeKit/SomeKit.h>
+#import <SimpleKit/SimpleKit.h>
+
+// CHECK: struct __attribute__((swift_name("SuccessfullyRenamedA"))) RenamedAgainInAPINotesA {
+// CHECK: struct __attribute__((swift_name("SuccessfullyRenamedB"))) RenamedAgainInAPINotesB {
+
+void test(OverriddenTypes *overridden) {
+ int *ip1 = global_int_ptr; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double (*)(int, int)'}}
+
+ int *ip2 = global_int_fun( // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}}
+ ip2, // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'double *'}}
+ ip2); // expected-warning{{incompatible pointer types passing 'int *' to parameter of type 'float *'}}
+
+ int *ip3 = [overridden // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'char *'}}
+ methodToMangle: ip3 // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'double *'}}
+ second: ip3]; // expected-warning{{incompatible pointer types sending 'int *' to parameter of type 'float *'}}
+
+ int *ip4 = overridden.intPropertyToMangle; // expected-warning{{incompatible pointer types initializing 'int *' with an expression of type 'double *'}}
+}
+
+// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr' here}}
+// expected-note@SomeKit/SomeKit.h:42{{passing argument to parameter 'ptr2' here}}
+// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr1' here}}
+// expected-note@SomeKit/SomeKit.h:48{{passing argument to parameter 'ptr2' here}}
diff --git a/test/APINotes/versioned-multi.c b/test/APINotes/versioned-multi.c
new file mode 100644
index 0000000..48c51fd
--- /dev/null
+++ b/test/APINotes/versioned-multi.c
@@ -0,0 +1,69 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// Build and check the unversioned module file.
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s
+
+// Build and check the various versions.
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned3 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned3/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-3 %s
+
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned4 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=4 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned4/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-4 %s
+
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned5 -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=5 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned5/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED-5 %s
+
+#import <VersionedKit/VersionedKit.h>
+
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4;
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef34;
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef45;
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef345;
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_NEW")));
+// CHECK-UNVERSIONED: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_NEW")));
+
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef4 __attribute__((swift_name("MultiVersionedTypedef4_4")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_4")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_4")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef34 __attribute__((swift_name("MultiVersionedTypedef34_3")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_3")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_3")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef45 __attribute__((swift_name("MultiVersionedTypedef45_4")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_4")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_4")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef345 __attribute__((swift_name("MultiVersionedTypedef345_3")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_3")));
+// CHECK-VERSIONED-3: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_3")));
+
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef4 __attribute__((swift_name("MultiVersionedTypedef4_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef34 __attribute__((swift_name("MultiVersionedTypedef34_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef45 __attribute__((swift_name("MultiVersionedTypedef45_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef345 __attribute__((swift_name("MultiVersionedTypedef345_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_4")));
+// CHECK-VERSIONED-4: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_4")));
+
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef4;
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef4Notes __attribute__((swift_name("MultiVersionedTypedef4Notes_NEW")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef4Header __attribute__((swift_name("MultiVersionedTypedef4Header_NEW")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef34;
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef34Notes __attribute__((swift_name("MultiVersionedTypedef34Notes_NEW")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef34Header __attribute__((swift_name("MultiVersionedTypedef34Header_NEW")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef45 __attribute__((swift_name("MultiVersionedTypedef45_5")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef45Notes __attribute__((swift_name("MultiVersionedTypedef45Notes_5")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef45Header __attribute__((swift_name("MultiVersionedTypedef45Header_5")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef345 __attribute__((swift_name("MultiVersionedTypedef345_5")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef345Notes __attribute__((swift_name("MultiVersionedTypedef345Notes_5")));
+// CHECK-VERSIONED-5: typedef int MultiVersionedTypedef345Header __attribute__((swift_name("MultiVersionedTypedef345Header_5")));
diff --git a/test/APINotes/versioned.m b/test/APINotes/versioned.m
new file mode 100644
index 0000000..e7ec2eb
--- /dev/null
+++ b/test/APINotes/versioned.m
@@ -0,0 +1,187 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// Build and check the unversioned module file.
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Unversioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-UNVERSIONED %s
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Unversioned -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-UNVERSIONED-DUMP %s
+
+// Build and check the versioned module file.
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -ast-print %t/ModulesCache/Versioned/VersionedKit.pcm | FileCheck -check-prefix=CHECK-VERSIONED %s
+// RUN: %clang_cc1 -fmodules -fblocks -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Versioned -fdisable-module-hash -fapinotes-modules -fapinotes-swift-version=3 -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -ast-dump -ast-dump-filter 'DUMP' | FileCheck -check-prefix=CHECK-DUMP -check-prefix=CHECK-VERSIONED-DUMP %s
+
+#import <VersionedKit/VersionedKit.h>
+
+// CHECK-UNVERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(x:y:)")));
+// CHECK-VERSIONED: void moveToPointDUMP(double x, double y) __attribute__((swift_name("moveTo(a:b:)")));
+
+// CHECK-DUMP-LABEL: Dumping moveToPointDUMP
+// CHECK-VERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "moveTo(x:y:)"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)"
+// CHECK-UNVERSIONED-DUMP: SwiftNameAttr {{.+}} "moveTo(x:y:)"
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "moveTo(a:b:)"
+// CHECK-DUMP-NOT: Attr
+
+// CHECK-DUMP-LABEL: Dumping unversionedRenameDUMP
+// CHECK-DUMP: in VersionedKit unversionedRenameDUMP
+// CHECK-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 0 IsReplacedByActive{{$}}
+// CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_HEADER()"
+// CHECK-DUMP-NEXT: SwiftNameAttr {{.+}} "unversionedRename_NOTES()"
+// CHECK-DUMP-NOT: Attr
+
+// CHECK-DUMP-LABEL: Dumping TestGenericDUMP
+// CHECK-VERSIONED-DUMP: SwiftImportAsNonGenericAttr {{.+}} Implicit
+// CHECK-UNVERSIONED-DUMP: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftImportAsNonGenericAttr {{.+}} Implicit
+// CHECK-DUMP-NOT: Attr
+
+// CHECK-DUMP-LABEL: Dumping Swift3RenamedOnlyDUMP
+// CHECK-DUMP: in VersionedKit Swift3RenamedOnlyDUMP
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 3.0 {{[0-9]+}} IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Name"
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Name"
+// CHECK-DUMP-NOT: Attr
+
+// CHECK-DUMP-LABEL: Dumping Swift3RenamedAlsoDUMP
+// CHECK-DUMP: in VersionedKit Swift3RenamedAlsoDUMP
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0 IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name"
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift3Also"
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} <line:{{.+}}, col:{{.+}}> "Swift4Name"
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 3.0{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift3Also"
+// CHECK-DUMP-NOT: Attr
+
+// CHECK-DUMP-LABEL: Dumping Swift4RenamedDUMP
+// CHECK-DUMP: in VersionedKit Swift4RenamedDUMP
+// CHECK-VERSIONED-DUMP-NEXT: SwiftVersionedRemovalAttr {{.+}} Implicit 4 {{[0-9]+}} IsReplacedByActive{{$}}
+// CHECK-VERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} "SpecialSwift4Name"
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftVersionedAttr {{.+}} Implicit 4{{$}}
+// CHECK-UNVERSIONED-DUMP-NEXT: SwiftNameAttr {{.+}} Implicit "SpecialSwift4Name"
+// CHECK-DUMP-NOT: Attr
+
+// CHECK-DUMP-NOT: Dumping
+
+// CHECK-UNVERSIONED: void acceptClosure(void (^block)(void) __attribute__((noescape)));
+// CHECK-VERSIONED: void acceptClosure(void (^block)(void));
+
+// CHECK-UNVERSIONED: void privateFunc() __attribute__((swift_private));
+
+// CHECK-UNVERSIONED: typedef double MyDoubleWrapper __attribute__((swift_wrapper("struct")));
+
+// CHECK-UNVERSIONED: enum MyErrorCode {
+// CHECK-UNVERSIONED-NEXT: MyErrorCodeFailed = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((ns_error_domain(MyErrorDomain)));
+
+// CHECK-UNVERSIONED: __attribute__((swift_bridge("MyValueType")))
+// CHECK-UNVERSIONED: @interface MyReferenceType
+
+// CHECK-VERSIONED: void privateFunc();
+
+// CHECK-VERSIONED: typedef double MyDoubleWrapper;
+
+// CHECK-VERSIONED: enum MyErrorCode {
+// CHECK-VERSIONED-NEXT: MyErrorCodeFailed = 1
+// CHECK-VERSIONED-NEXT: };
+
+// CHECK-VERSIONED-NOT: __attribute__((swift_bridge("MyValueType")))
+// CHECK-VERSIONED: @interface MyReferenceType
+
+// CHECK-UNVERSIONED: __attribute__((swift_objc_members)
+// CHECK-UNVERSIONED-NEXT: @interface TestProperties
+// CHECK-VERSIONED-NOT: __attribute__((swift_objc_members)
+// CHECK-VERSIONED: @interface TestProperties
+
+// CHECK-UNVERSIONED-LABEL: enum FlagEnum {
+// CHECK-UNVERSIONED-NEXT: FlagEnumA = 1,
+// CHECK-UNVERSIONED-NEXT: FlagEnumB = 2
+// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum));
+// CHECK-UNVERSIONED-LABEL: enum NewlyFlagEnum {
+// CHECK-UNVERSIONED-NEXT: NewlyFlagEnumA = 1,
+// CHECK-UNVERSIONED-NEXT: NewlyFlagEnumB = 2
+// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum));
+// CHECK-UNVERSIONED-LABEL: enum APINotedFlagEnum {
+// CHECK-UNVERSIONED-NEXT: APINotedFlagEnumA = 1,
+// CHECK-UNVERSIONED-NEXT: APINotedFlagEnumB = 2
+// CHECK-UNVERSIONED-NEXT: } __attribute__((flag_enum));
+// CHECK-UNVERSIONED-LABEL: enum OpenEnum {
+// CHECK-UNVERSIONED-NEXT: OpenEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-UNVERSIONED-LABEL: enum NewlyOpenEnum {
+// CHECK-UNVERSIONED-NEXT: NewlyOpenEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-UNVERSIONED-LABEL: enum NewlyClosedEnum {
+// CHECK-UNVERSIONED-NEXT: NewlyClosedEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+// CHECK-UNVERSIONED-LABEL: enum ClosedToOpenEnum {
+// CHECK-UNVERSIONED-NEXT: ClosedToOpenEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-UNVERSIONED-LABEL: enum OpenToClosedEnum {
+// CHECK-UNVERSIONED-NEXT: OpenToClosedEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+// CHECK-UNVERSIONED-LABEL: enum APINotedOpenEnum {
+// CHECK-UNVERSIONED-NEXT: APINotedOpenEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-UNVERSIONED-LABEL: enum APINotedClosedEnum {
+// CHECK-UNVERSIONED-NEXT: APINotedClosedEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+
+// CHECK-VERSIONED-LABEL: enum FlagEnum {
+// CHECK-VERSIONED-NEXT: FlagEnumA = 1,
+// CHECK-VERSIONED-NEXT: FlagEnumB = 2
+// CHECK-VERSIONED-NEXT: } __attribute__((flag_enum));
+// CHECK-VERSIONED-LABEL: enum NewlyFlagEnum {
+// CHECK-VERSIONED-NEXT: NewlyFlagEnumA = 1,
+// CHECK-VERSIONED-NEXT: NewlyFlagEnumB = 2
+// CHECK-VERSIONED-NEXT: };
+// CHECK-VERSIONED-LABEL: enum APINotedFlagEnum {
+// CHECK-VERSIONED-NEXT: APINotedFlagEnumA = 1,
+// CHECK-VERSIONED-NEXT: APINotedFlagEnumB = 2
+// CHECK-VERSIONED-NEXT: } __attribute__((flag_enum));
+// CHECK-VERSIONED-LABEL: enum OpenEnum {
+// CHECK-VERSIONED-NEXT: OpenEnumA = 1
+// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-VERSIONED-LABEL: enum NewlyOpenEnum {
+// CHECK-VERSIONED-NEXT: NewlyOpenEnumA = 1
+// CHECK-VERSIONED-NEXT: };
+// CHECK-VERSIONED-LABEL: enum NewlyClosedEnum {
+// CHECK-VERSIONED-NEXT: NewlyClosedEnumA = 1
+// CHECK-VERSIONED-NEXT: };
+// CHECK-VERSIONED-LABEL: enum ClosedToOpenEnum {
+// CHECK-VERSIONED-NEXT: ClosedToOpenEnumA = 1
+// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+// CHECK-VERSIONED-LABEL: enum OpenToClosedEnum {
+// CHECK-VERSIONED-NEXT: OpenToClosedEnumA = 1
+// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-VERSIONED-LABEL: enum APINotedOpenEnum {
+// CHECK-VERSIONED-NEXT: APINotedOpenEnumA = 1
+// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-VERSIONED-LABEL: enum APINotedClosedEnum {
+// CHECK-VERSIONED-NEXT: APINotedClosedEnumA = 1
+// CHECK-VERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+
+// These don't actually have versioned information, so we just check them once.
+// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFEnum {
+// CHECK-UNVERSIONED-NEXT: SoonToBeCFEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSEnum {
+// CHECK-UNVERSIONED-NEXT: SoonToBeNSEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open")));
+// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFOptions {
+// CHECK-UNVERSIONED-NEXT: SoonToBeCFOptionsA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))) __attribute__((flag_enum));
+// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSOptions {
+// CHECK-UNVERSIONED-NEXT: SoonToBeNSOptionsA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("open"))) __attribute__((flag_enum));
+// CHECK-UNVERSIONED-LABEL: enum SoonToBeCFClosedEnum {
+// CHECK-UNVERSIONED-NEXT: SoonToBeCFClosedEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+// CHECK-UNVERSIONED-LABEL: enum SoonToBeNSClosedEnum {
+// CHECK-UNVERSIONED-NEXT: SoonToBeNSClosedEnumA = 1
+// CHECK-UNVERSIONED-NEXT: } __attribute__((enum_extensibility("closed")));
+// CHECK-UNVERSIONED-LABEL: enum UndoAllThatHasBeenDoneToMe {
+// CHECK-UNVERSIONED-NEXT: UndoAllThatHasBeenDoneToMeA = 1
+// CHECK-UNVERSIONED-NEXT: };
diff --git a/test/APINotes/yaml-convert-diags.c b/test/APINotes/yaml-convert-diags.c
new file mode 100644
index 0000000..8d5c0fb
--- /dev/null
+++ b/test/APINotes/yaml-convert-diags.c
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/BrokenHeaders2 2>&1 | FileCheck %s
+
+#include "SomeBrokenLib.h"
+
+// CHECK: error: multiple definitions of global function 'do_something_with_pointers'
diff --git a/test/APINotes/yaml-os-availability.c b/test/APINotes/yaml-os-availability.c
new file mode 100644
index 0000000..62301af
--- /dev/null
+++ b/test/APINotes/yaml-os-availability.c
@@ -0,0 +1,31 @@
+# RUN: %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t-ios.apinotesc %S/Inputs/os-availability.apinotes
+# RUN: %clang -cc1apinotes -binary-to-yaml %t-ios.apinotesc -o %t.os-availability-ios.apinotes
+# RUN: FileCheck %s -check-prefix=IOS < %t.os-availability-ios.apinotes
+
+# RUN: %clang -cc1apinotes -yaml-to-binary -target x86_64-apple-macosx10.9 -o %t-osx.apinotesc %S/Inputs/os-availability.apinotes
+# RUN: %clang -cc1apinotes -binary-to-yaml %t-osx.apinotesc -o %t.os-availability-osx.apinotes
+# RUN: FileCheck %s -check-prefix=OSX < %t.os-availability-osx.apinotes
+
+# IOS: Foundation
+# IOS: NSArray
+# IOS: initWithObjects
+# IOS: familyNameios
+# IOS: fontName
+# IOS: NSCountedSet
+# IOS: UIApplicationDelegate
+# IOS: UIApplicationDelegateIOS
+# IOS: NSAvailableWindowDepths
+# IOS: NSAvailableWindowDepthsiOS
+# IOS-NOT: NSCalibratedWhiteColorSpace
+
+# OSX: Foundation
+# qqOSX: NSArray
+# OSX-NOT: initWithObjects
+# OSX-NOT: familyNameios
+# OSX: fontName
+# OSX-NOT: NSCountedSet
+# OSX: UIApplicationDelegate
+# OSX-NOT: UIApplicationDelegateIOS
+# OSX: NSAvailableWindowDepths
+# OSX-NOT: NSAvailableWindowDepthsiOS
+# OSX: NSCalibratedWhiteColorSpace
diff --git a/test/APINotes/yaml-parse-diags.c b/test/APINotes/yaml-parse-diags.c
new file mode 100644
index 0000000..a7b370a
--- /dev/null
+++ b/test/APINotes/yaml-parse-diags.c
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fsyntax-only -fapinotes %s -I %S/Inputs/BrokenHeaders -verify
+
+#include "SomeBrokenLib.h"
+
+// expected-error@APINotes.apinotes:4{{unknown key 'Nu llabilityOfRet'}}
diff --git a/test/APINotes/yaml-reader-errors.c b/test/APINotes/yaml-reader-errors.c
new file mode 100644
index 0000000..c02260b
--- /dev/null
+++ b/test/APINotes/yaml-reader-errors.c
@@ -0,0 +1,86 @@
+# RUN: not %clang -cc1apinotes -yaml-to-binary -target i386-apple-ios7 -o %t.apinotesc %s > %t.err 2>&1
+# RUN: FileCheck %s < %t.err
+
+---
+Name: UIKit
+Availability: iOS
+AvailabilityMsg: iOSOnly
+Classes:
+ - Name: UIFont
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ Methods:
+ - Selector: 'fontWithName:size:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ DesignatedInit: true
+# CHECK: duplicate definition of method '-[UIFont fontWithName:size:]'
+ - Selector: 'fontWithName:size:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ DesignatedInit: true
+ Properties:
+ - Name: familyName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: fontName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+# CHECK: duplicate definition of instance property 'UIFont.familyName'
+ - Name: familyName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+# CHECK: multiple definitions of class 'UIFont'
+ - Name: UIFont
+Protocols:
+ - Name: MyProto
+ AuditedForNullability: true
+# CHECK: multiple definitions of protocol 'MyProto'
+ - Name: MyProto
+ AuditedForNullability: true
+Functions:
+ - Name: 'globalFoo'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: 'globalFoo2'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+Globals:
+ - Name: globalVar
+ Nullability: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: globalVar2
+ Nullability: O
+Tags:
+# CHECK: cannot mix EnumKind and FlagEnum (for FlagAndEnumKind)
+ - Name: FlagAndEnumKind
+ FlagEnum: true
+ EnumKind: CFOptions
+# CHECK: cannot mix EnumKind and FlagEnum (for FlagAndEnumKind2)
+ - Name: FlagAndEnumKind2
+ EnumKind: CFOptions
+ FlagEnum: false
+# CHECK: cannot mix EnumKind and EnumExtensibility (for ExtensibilityAndEnumKind)
+ - Name: ExtensibilityAndEnumKind
+ EnumExtensibility: open
+ EnumKind: CFOptions
+# CHECK: cannot mix EnumKind and EnumExtensibility (for ExtensibilityAndEnumKind2)
+ - Name: ExtensibilityAndEnumKind2
+ EnumKind: CFOptions
+ EnumExtensibility: closed
+# CHECK: cannot mix EnumKind and EnumExtensibility (for ExtensibilityAndEnumKind3)
+ - Name: ExtensibilityAndEnumKind3
+ EnumKind: none
+ EnumExtensibility: none
diff --git a/test/APINotes/yaml-reader-test.c b/test/APINotes/yaml-reader-test.c
new file mode 100644
index 0000000..01ad90a
--- /dev/null
+++ b/test/APINotes/yaml-reader-test.c
@@ -0,0 +1,102 @@
+# RUN: %clang -cc1apinotes -dump %s | FileCheck %s
+---
+Name: UIKit
+Availability: iOS
+AvailabilityMsg: iOSOnly
+Classes:
+ - Name: UIFont
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ Methods:
+ - Selector: 'fontWithName:size:'
+ MethodKind: Instance
+ Nullability: [ N ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ DesignatedInit: true
+ Properties:
+ - Name: familyName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: fontName
+ Nullability: N
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+Protocols:
+ - Name: MyProto
+ AuditedForNullability: true
+ - Name: MyProto2
+ AuditedForNullability: true
+Functions:
+ - Name: 'globalFoo'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: 'globalFoo2'
+ Nullability: [ N, N, O, S ]
+ NullabilityOfRet: O
+Globals:
+ - Name: globalVar
+ Nullability: O
+ Availability: iOS
+ AvailabilityMsg: iOSOnly
+ - Name: globalVar2
+ Nullability: O
+
+
+# CHECK: Name: UIKit
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: Classes:
+# CHECK: - Name: UIFont
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: Methods:
+# CHECK: - Selector: 'fontWithName:size:'
+# CHECK: MethodKind: Instance
+# CHECK: Nullability: [ N ]
+# CHECK: NullabilityOfRet: O
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: DesignatedInit: true
+# CHECK: Properties:
+# CHECK: - Name: familyName
+# CHECK: Nullability: N
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: - Name: fontName
+# CHECK: Nullability: N
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK:Protocols:
+# CHECK: - Name: MyProto
+# CHECK: AuditedForNullability: true
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg: ''
+# CHECK: - Name: MyProto2
+# CHECK: AuditedForNullability: true
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg: ''
+# CHECK:Functions:
+# CHECK: - Name: globalFoo
+# CHECK: Nullability: [ N, N, O, U ]
+# CHECK: NullabilityOfRet: O
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: - Name: globalFoo2
+# CHECK: Nullability: [ N, N, O, U ]
+# CHECK: NullabilityOfRet: O
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg: ''
+# CHECK:Globals:
+# CHECK: - Name: globalVar
+# CHECK: Nullability: O
+# CHECK: Availability: iOS
+# CHECK: AvailabilityMsg: iOSOnly
+# CHECK: - Name: globalVar2
+# CHECK: Nullability: O
+# CHECK: Availability: available
+# CHECK: AvailabilityMsg:
diff --git a/test/APINotes/yaml-roundtrip.c b/test/APINotes/yaml-roundtrip.c
new file mode 100644
index 0000000..b721b6a
--- /dev/null
+++ b/test/APINotes/yaml-roundtrip.c
@@ -0,0 +1,10 @@
+# RUN: %clang -cc1apinotes -yaml-to-binary -o %t.apinotesc %S/Inputs/roundtrip.apinotes
+# RUN: %clang -cc1apinotes -binary-to-yaml -o %t.apinotes %t.apinotesc
+
+# Handle the infurating '...' the YAML writer adds but the parser
+# can't read.
+
+# RUN: cp %S/Inputs/roundtrip.apinotes %t-reference.apinotes
+# RUN: echo "..." >> %t-reference.apinotes
+# RUN: diff %t-reference.apinotes %t.apinotes
+
diff --git a/test/Analysis/misc-ps.m b/test/Analysis/misc-ps.m
index 9a75cfd..c610a7d 100644
--- a/test/Analysis/misc-ps.m
+++ b/test/Analysis/misc-ps.m
@@ -1086,7 +1086,7 @@
}
void test_enum_cases_positive(enum Cases C) {
- switch (C) { // expected-warning{{enumeration value 'C4' not handled in switch}}
+ switch (C) { // expected-warning{{enumeration value 'C4' not handled in switch}} expected-note {{add missing switch cases}}
case C1:
case C2:
case C3:
diff --git a/test/Analysis/ns_error_enum.m b/test/Analysis/ns_error_enum.m
new file mode 100644
index 0000000..bf61629
--- /dev/null
+++ b/test/Analysis/ns_error_enum.m
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -verify %s
+
+#define CF_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
+#define NS_ENUM(_type, _name) CF_ENUM(_type, _name)
+
+#define NS_ERROR_ENUM(_type, _name, _domain) \
+ enum _name : _type _name; enum __attribute__((ns_error_domain(_domain))) _name : _type
+
+typedef NS_ENUM(unsigned, MyEnum) {
+ MyFirst,
+ MySecond,
+};
+
+typedef NS_ENUM(invalidType, MyInvalidEnum) {
+// expected-error@-1{{unknown type name 'invalidType'}}
+// expected-error@-2{{unknown type name 'invalidType'}}
+ MyFirstInvalid,
+ MySecondInvalid,
+};
+
+const char *MyErrorDomain;
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnum, MyErrorDomain) {
+ MyErrFirst,
+ MyErrSecond,
+};
+struct __attribute__((ns_error_domain(MyErrorDomain))) MyStructErrorDomain {};
+
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, InvalidDomain) {
+ // expected-error@-1{{domain argument 'InvalidDomain' does not refer to global constant}}
+ MyErrFirstInvalid,
+ MyErrSecondInvalid,
+};
+
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalid, "domain-string");
+ // expected-error@-1{{domain argument must be an identifier}}
+
+int __attribute__((ns_error_domain(MyErrorDomain))) NotTagDecl;
+ // expected-error@-1{{ns_error_domain attribute only valid on enums, structs, and unions}}
+
+void foo() {}
+typedef NS_ERROR_ENUM(unsigned char, MyErrorEnumInvalidFunction, foo);
+ // expected-error@-1{{domain argument 'foo' does not refer to global constant}}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index fa926c5..a375695 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -46,10 +46,11 @@
c-index-test diagtool
clang-tblgen
clang-offload-bundler
+ clang-refactor-test
clang-import-test
clang-rename
)
-
+
if(CLANG_ENABLE_STATIC_ANALYZER)
list(APPEND CLANG_TEST_DEPS
clang-check
diff --git a/test/CodeGen/2004-02-20-Builtins.c b/test/CodeGen/2004-02-20-Builtins.c
index 9be0523..13f9701 100644
--- a/test/CodeGen/2004-02-20-Builtins.c
+++ b/test/CodeGen/2004-02-20-Builtins.c
@@ -1,5 +1,8 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | not grep builtin
+// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
double sqrt(double x);
+
+// CHECK-LABEL: @zsqrtxxx
+// CHECK-NOT: builtin
void zsqrtxxx(float num) {
num = sqrt(num);
}
diff --git a/test/CodeGen/2009-10-20-GlobalDebug.c b/test/CodeGen/2009-10-20-GlobalDebug.c
index 0d7c759..c8c247f 100644
--- a/test/CodeGen/2009-10-20-GlobalDebug.c
+++ b/test/CodeGen/2009-10-20-GlobalDebug.c
@@ -10,11 +10,11 @@
return 0;
}
-// CHECK: [[L]] = !DIGlobalVariableExpression(var: [[LV:.*]])
+// CHECK: [[L]] = !DIGlobalVariableExpression(var: [[LV:.*]], expr: !DIExpression())
// CHECK: [[LV]] = distinct !DIGlobalVariable(name: "localstatic"
// CHECK-NOT: linkageName:
// CHECK-SAME: line: 9,
-// CHECK: [[G]] = !DIGlobalVariableExpression(var: [[GV:.*]])
+// CHECK: [[G]] = !DIGlobalVariableExpression(var: [[GV:.*]], expr: !DIExpression())
// CHECK: [[GV]] = distinct !DIGlobalVariable(name: "global"
// CHECK-NOT: linkageName:
// CHECK-SAME: line: 7,
diff --git a/test/CodeGen/2010-08-10-DbgConstant.c b/test/CodeGen/2010-08-10-DbgConstant.c
index 68947ed..ad9d566 100644
--- a/test/CodeGen/2010-08-10-DbgConstant.c
+++ b/test/CodeGen/2010-08-10-DbgConstant.c
@@ -1,6 +1,5 @@
// RUN: %clang_cc1 -S -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s
-// CHECK: !DIGlobalVariableExpression(var: [[VAR:.*]], expr: [[EXPR:![0-9]+]])
-// CHECK: [[EXPR]] = !DIExpression(DW_OP_constu, 201, DW_OP_stack_value)
+// CHECK: !DIGlobalVariableExpression(var: [[VAR:.*]], expr: !DIExpression(DW_OP_constu, 201, DW_OP_stack_value))
static const unsigned int ro = 201;
void bar(int);
diff --git a/test/CodeGen/debug-info-global-constant.c b/test/CodeGen/debug-info-global-constant.c
index 4175f24..8cb7f44 100644
--- a/test/CodeGen/debug-info-global-constant.c
+++ b/test/CodeGen/debug-info-global-constant.c
@@ -5,11 +5,10 @@
// exactly once.
// CHECK: @i = internal constant i32 1, align 4, !dbg ![[I:[0-9]+]]
-// CHECK: ![[I]] = !DIGlobalVariableExpression(var: ![[VAR:.*]], expr: ![[EXPR:[0-9]+]])
+// CHECK: ![[I]] = !DIGlobalVariableExpression(var: ![[VAR:.*]], expr: !DIExpression(DW_OP_constu, 1, DW_OP_stack_value))
// CHECK: ![[VAR]] = distinct !DIGlobalVariable(name: "i",
// CHECK: !DICompileUnit({{.*}}globals: ![[GLOBALS:[0-9]+]])
// CHECK: ![[GLOBALS]] = !{![[I]]}
-// CHECK: ![[EXPR]] = !DIExpression(DW_OP_constu, 1, DW_OP_stack_value)
static const int i = 1;
void g(const int *, int);
diff --git a/test/CodeGen/debug-info-lto.c b/test/CodeGen/debug-info-lto.c
new file mode 100644
index 0000000..5dab0a1
--- /dev/null
+++ b/test/CodeGen/debug-info-lto.c
@@ -0,0 +1,4 @@
+// RUN: %clang_cc1 -flto -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
+// RUN: %clang_cc1 -flto=thin -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s
+// The "o" in LTO stands for optimization!
+// CHECK: !DICompileUnit({{.*}} isOptimized: true
diff --git a/test/CodeGen/debug-info-static-const-fp.c b/test/CodeGen/debug-info-static-const-fp.c
index 4dfe057..1b1da09 100644
--- a/test/CodeGen/debug-info-static-const-fp.c
+++ b/test/CodeGen/debug-info-static-const-fp.c
@@ -33,22 +33,19 @@
return hVal + fVal + dVal + ldVal;
}
-// CHECK: !DIGlobalVariableExpression(var: [[HVAL:.*]], expr: [[HEXPR:.*]])
+// CHECK: !DIGlobalVariableExpression(var: [[HVAL:.*]], expr: !DIExpression(DW_OP_constu, 16502, DW_OP_stack_value))
// CHECK: [[HVAL]] = distinct !DIGlobalVariable(name: "hVal",
// CHECK-SAME: isLocal: true, isDefinition: true
-// CHECK: [[HEXPR]] = !DIExpression(DW_OP_constu, 16502, DW_OP_stack_value)
-// CHECK: !DIGlobalVariableExpression(var: [[FVAL:.*]], expr: [[FEXPR:.*]])
+// CHECK: !DIGlobalVariableExpression(var: [[FVAL:.*]], expr: !DIExpression(DW_OP_constu, 3238681178, DW_OP_stack_value))
// CHECK: [[FVAL]] = distinct !DIGlobalVariable(name: "fVal",
// CHECK-SAME: isLocal: true, isDefinition: true
-// CHECK: [[FEXPR]] = !DIExpression(DW_OP_constu, 3238681178, DW_OP_stack_value)
-// CHECK: !DIGlobalVariableExpression(var: [[DVAL:.*]], expr: [[DEXPR:.*]])
+// CHECK: !DIGlobalVariableExpression(var: [[DVAL:.*]], expr: !DIExpression(DW_OP_constu, 4658387303597904457, DW_OP_stack_value))
// CHECK: [[DVAL]] = distinct !DIGlobalVariable(name: "dVal",
// CHECK-SAME: isLocal: true, isDefinition: true
-// CHECK: [[DEXPR]] = !DIExpression(DW_OP_constu, 4658387303597904457, DW_OP_stack_value)
// CHECK-LDlg-DAG: [[LDVAL:.*]] = distinct !DIGlobalVariable(name: "ldVal", {{.*}}, isLocal: true, isDefinition: true)
-// CHECK-LDlg-DAG: !DIGlobalVariableExpression(var: [[LDVAL]])
+// CHECK-LDlg-DAG: !DIGlobalVariableExpression(var: [[LDVAL]], expr: !DIExpression())
// CHECK-LDsm-DAG: [[LDVAL:.*]] = distinct !DIGlobalVariable(name: "ldVal", {{.*}}, isLocal: true, isDefinition: true)
// CHECK-LDsm-DAG: !DIGlobalVariableExpression(var: [[LDVAL]], expr:
diff --git a/test/CodeGen/debug-info-static.c b/test/CodeGen/debug-info-static.c
index 016f1e6..d6ade2a 100644
--- a/test/CodeGen/debug-info-static.c
+++ b/test/CodeGen/debug-info-static.c
@@ -2,7 +2,7 @@
// CHECK: @f.xyzzy = internal global i32 0, align 4, !dbg [[XYZZY:![0-9]+]]
-// CHECK: [[XYZZY]] = !DIGlobalVariableExpression(var: [[VAR:.*]])
+// CHECK: [[XYZZY]] = !DIGlobalVariableExpression(var: [[VAR:.*]], expr: !DIExpression())
// CHECK: [[VAR]] = distinct !DIGlobalVariable
void f(void)
{
diff --git a/test/CodeGen/debug-info-vla.c b/test/CodeGen/debug-info-vla.c
index 3b69773..7928ca7 100644
--- a/test/CodeGen/debug-info-vla.c
+++ b/test/CodeGen/debug-info-vla.c
@@ -3,8 +3,7 @@
void testVLAwithSize(int s)
{
// CHECK: dbg.declare
-// CHECK: dbg.declare({{.*}}, metadata ![[VAR:.*]], metadata ![[EXPR:.*]])
-// CHECK: ![[EXPR]] = !DIExpression()
+// CHECK: dbg.declare({{.*}}, metadata ![[VAR:.*]], metadata !DIExpression())
// CHECK: ![[VAR]] = !DILocalVariable(name: "vla",{{.*}} line: [[@LINE+1]]
int vla[s];
int i;
diff --git a/test/CodeGen/fp16vec-ops.c b/test/CodeGen/fp16vec-ops.c
new file mode 100644
index 0000000..a99be41
--- /dev/null
+++ b/test/CodeGen/fp16vec-ops.c
@@ -0,0 +1,162 @@
+// REQUIRES: arm-registered-target
+// RUN: %clang_cc1 -triple arm64-apple-ios9 -emit-llvm -o - -fallow-half-arguments-and-returns %s | FileCheck %s --check-prefix=CHECK
+// RUN: %clang_cc1 -triple armv7-apple-ios9 -emit-llvm -o - -fallow-half-arguments-and-returns %s | FileCheck %s --check-prefix=CHECK
+
+typedef __fp16 half4 __attribute__ ((vector_size (8)));
+typedef short short4 __attribute__ ((vector_size (8)));
+
+half4 hv0, hv1;
+short4 sv0;
+
+// CHECK-LABEL: testFP16Vec0
+// CHECK: %[[V0:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV:.*]] = fpext <4 x half> %[[V0]] to <4 x float>
+// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV1:.*]] = fpext <4 x half> %[[V1]] to <4 x float>
+// CHECK: %[[ADD:.*]] = fadd <4 x float> %[[CONV]], %[[CONV1]]
+// CHECK: %[[CONV2:.*]] = fptrunc <4 x float> %[[ADD]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV2]], <4 x half>* @hv0, align 8
+// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV3:.*]] = fpext <4 x half> %[[V2]] to <4 x float>
+// CHECK: %[[V3:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV4:.*]] = fpext <4 x half> %[[V3]] to <4 x float>
+// CHECK: %[[SUB:.*]] = fsub <4 x float> %[[CONV3]], %[[CONV4]]
+// CHECK: %[[CONV5:.*]] = fptrunc <4 x float> %[[SUB]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV5]], <4 x half>* @hv0, align 8
+// CHECK: %[[V4:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV6:.*]] = fpext <4 x half> %[[V4]] to <4 x float>
+// CHECK: %[[V5:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV7:.*]] = fpext <4 x half> %[[V5]] to <4 x float>
+// CHECK: %[[MUL:.*]] = fmul <4 x float> %[[CONV6]], %[[CONV7]]
+// CHECK: %[[CONV8:.*]] = fptrunc <4 x float> %[[MUL]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV8]], <4 x half>* @hv0, align 8
+// CHECK: %[[V6:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV9:.*]] = fpext <4 x half> %[[V6]] to <4 x float>
+// CHECK: %[[V7:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV10:.*]] = fpext <4 x half> %[[V7]] to <4 x float>
+// CHECK: %[[DIV:.*]] = fdiv <4 x float> %[[CONV9]], %[[CONV10]]
+// CHECK: %[[CONV11:.*]] = fptrunc <4 x float> %[[DIV]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV11]], <4 x half>* @hv0, align 8
+
+void testFP16Vec0() {
+ hv0 = hv0 + hv1;
+ hv0 = hv0 - hv1;
+ hv0 = hv0 * hv1;
+ hv0 = hv0 / hv1;
+}
+
+// CHECK-LABEL: testFP16Vec1
+// CHECK: %[[V0:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV:.*]] = fpext <4 x half> %[[V0]] to <4 x float>
+// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV1:.*]] = fpext <4 x half> %[[V1]] to <4 x float>
+// CHECK: %[[ADD:.*]] = fadd <4 x float> %[[CONV1]], %[[CONV]]
+// CHECK: %[[CONV2:.*]] = fptrunc <4 x float> %[[ADD]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV2]], <4 x half>* @hv0, align 8
+// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV3:.*]] = fpext <4 x half> %[[V2]] to <4 x float>
+// CHECK: %[[V3:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV4:.*]] = fpext <4 x half> %[[V3]] to <4 x float>
+// CHECK: %[[SUB:.*]] = fsub <4 x float> %[[CONV4]], %[[CONV3]]
+// CHECK: %[[CONV5:.*]] = fptrunc <4 x float> %[[SUB]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV5]], <4 x half>* @hv0, align 8
+// CHECK: %[[V4:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV6:.*]] = fpext <4 x half> %[[V4]] to <4 x float>
+// CHECK: %[[V5:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV7:.*]] = fpext <4 x half> %[[V5]] to <4 x float>
+// CHECK: %[[MUL:.*]] = fmul <4 x float> %[[CONV7]], %[[CONV6]]
+// CHECK: %[[CONV8:.*]] = fptrunc <4 x float> %[[MUL]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV8]], <4 x half>* @hv0, align 8
+// CHECK: %[[V6:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV9:.*]] = fpext <4 x half> %[[V6]] to <4 x float>
+// CHECK: %[[V7:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV10:.*]] = fpext <4 x half> %[[V7]] to <4 x float>
+// CHECK: %[[DIV:.*]] = fdiv <4 x float> %[[CONV10]], %[[CONV9]]
+// CHECK: %[[CONV11:.*]] = fptrunc <4 x float> %[[DIV]] to <4 x half>
+// CHECK: store <4 x half> %[[CONV11]], <4 x half>* @hv0, align 8
+
+void testFP16Vec1() {
+ hv0 += hv1;
+ hv0 -= hv1;
+ hv0 *= hv1;
+ hv0 /= hv1;
+}
+
+// CHECK-LABEL: testFP16Vec2
+// CHECK: %[[CADDR:.*]] = alloca i32, align 4
+// CHECK: store i32 %[[C:.*]], i32* %[[CADDR]], align 4
+// CHECK: %[[V0:.*]] = load i32, i32* %[[CADDR]], align 4
+// CHECK: %[[TOBOOL:.*]] = icmp ne i32 %[[V0]], 0
+// CHECK: br i1 %[[TOBOOL]], label %{{.*}}, label %{{.*}}
+//
+// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: br label %{{.*}}
+//
+// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: br label %{{.*}}
+//
+// CHECK: %[[COND:.*]] = phi <4 x half> [ %[[V1]], %{{.*}} ], [ %[[V2]], %{{.*}} ]
+// CHECK: store <4 x half> %[[COND]], <4 x half>* @hv0, align 8
+
+void testFP16Vec2(int c) {
+ hv0 = c ? hv0 : hv1;
+}
+
+// CHECK-LABEL: testFP16Vec3
+// CHECK: %[[V0:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV:.*]] = fpext <4 x half> %[[V0]] to <4 x float>
+// CHECK: %[[V1:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV1:.*]] = fpext <4 x half> %[[V1]] to <4 x float>
+// CHECK: %[[CMP:.*]] = fcmp oeq <4 x float> %[[CONV]], %[[CONV1]]
+// CHECK: %[[SEXT:.*]] = sext <4 x i1> %[[CMP]] to <4 x i32>
+// CHECK: %[[CONV2:.*]] = trunc <4 x i32> %[[SEXT]] to <4 x i16>
+// CHECK: store <4 x i16> %[[CONV2]], <4 x i16>* @sv0, align 8
+// CHECK: %[[V2:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV3:.*]] = fpext <4 x half> %[[V2]] to <4 x float>
+// CHECK: %[[V3:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV4:.*]] = fpext <4 x half> %[[V3]] to <4 x float>
+// CHECK: %[[CMP5:.*]] = fcmp une <4 x float> %[[CONV3]], %[[CONV4]]
+// CHECK: %[[SEXT6:.*]] = sext <4 x i1> %[[CMP5]] to <4 x i32>
+// CHECK: %[[CONV7:.*]] = trunc <4 x i32> %[[SEXT6]] to <4 x i16>
+// CHECK: store <4 x i16> %[[CONV7]], <4 x i16>* @sv0, align 8
+// CHECK: %[[V4:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV8:.*]] = fpext <4 x half> %[[V4]] to <4 x float>
+// CHECK: %[[V5:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV9:.*]] = fpext <4 x half> %[[V5]] to <4 x float>
+// CHECK: %[[CMP10:.*]] = fcmp olt <4 x float> %[[CONV8]], %[[CONV9]]
+// CHECK: %[[SEXT11:.*]] = sext <4 x i1> %[[CMP10]] to <4 x i32>
+// CHECK: %[[CONV12:.*]] = trunc <4 x i32> %[[SEXT11]] to <4 x i16>
+// CHECK: store <4 x i16> %[[CONV12]], <4 x i16>* @sv0, align 8
+// CHECK: %[[V6:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV13:.*]] = fpext <4 x half> %[[V6]] to <4 x float>
+// CHECK: %[[V7:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV14:.*]] = fpext <4 x half> %[[V7]] to <4 x float>
+// CHECK: %[[CMP15:.*]] = fcmp ogt <4 x float> %[[CONV13]], %[[CONV14]]
+// CHECK: %[[SEXT16:.*]] = sext <4 x i1> %[[CMP15]] to <4 x i32>
+// CHECK: %[[CONV17:.*]] = trunc <4 x i32> %[[SEXT16]] to <4 x i16>
+// CHECK: store <4 x i16> %[[CONV17]], <4 x i16>* @sv0, align 8
+// CHECK: %[[V8:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV18:.*]] = fpext <4 x half> %[[V8]] to <4 x float>
+// CHECK: %[[V9:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV19:.*]] = fpext <4 x half> %[[V9]] to <4 x float>
+// CHECK: %[[CMP20:.*]] = fcmp ole <4 x float> %[[CONV18]], %[[CONV19]]
+// CHECK: %[[SEXT21:.*]] = sext <4 x i1> %[[CMP20]] to <4 x i32>
+// CHECK: %[[CONV22:.*]] = trunc <4 x i32> %[[SEXT21]] to <4 x i16>
+// CHECK: store <4 x i16> %[[CONV22]], <4 x i16>* @sv0, align 8
+// CHECK: %[[V10:.*]] = load <4 x half>, <4 x half>* @hv0, align 8
+// CHECK: %[[CONV23:.*]] = fpext <4 x half> %[[V10]] to <4 x float>
+// CHECK: %[[V11:.*]] = load <4 x half>, <4 x half>* @hv1, align 8
+// CHECK: %[[CONV24:.*]] = fpext <4 x half> %[[V11]] to <4 x float>
+// CHECK: %[[CMP25:.*]] = fcmp oge <4 x float> %[[CONV23]], %[[CONV24]]
+// CHECK: %[[SEXT26:.*]] = sext <4 x i1> %[[CMP25]] to <4 x i32>
+// CHECK: %[[CONV27:.*]] = trunc <4 x i32> %[[SEXT26]] to <4 x i16>
+// CHECK: store <4 x i16> %[[CONV27]], <4 x i16>* @sv0, align 8
+
+void testFP16Vec3() {
+ sv0 = hv0 == hv1;
+ sv0 = hv0 != hv1;
+ sv0 = hv0 < hv1;
+ sv0 = hv0 > hv1;
+ sv0 = hv0 <= hv1;
+ sv0 = hv0 >= hv1;
+}
diff --git a/test/CodeGen/mangle-blocks.c b/test/CodeGen/mangle-blocks.c
index e8de92d..4ea5a55 100644
--- a/test/CodeGen/mangle-blocks.c
+++ b/test/CodeGen/mangle-blocks.c
@@ -12,12 +12,12 @@
}
// CHECK: @__func__.__mangle_block_invoke_2 = private unnamed_addr constant [22 x i8] c"mangle_block_invoke_2\00", align 1
-// CHECK: @.str = private unnamed_addr constant {{.*}}, align 1
-// CHECK: @.str.1 = private unnamed_addr constant [7 x i8] c"mangle\00", align 1
+// CHECK: @.str{{.*}} = private unnamed_addr constant {{.*}}, align 1
+// CHECK: @.str[[STR1:.*]] = private unnamed_addr constant [7 x i8] c"mangle\00", align 1
// CHECK: define internal void @__mangle_block_invoke(i8* %.block_descriptor)
// CHECK: define internal void @__mangle_block_invoke_2(i8* %.block_descriptor){{.*}}{
-// CHECK: call void @__assert_rtn(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @__func__.__mangle_block_invoke_2, i32 0, i32 0), i8* getelementptr inbounds {{.*}}, i32 9, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.1, i32 0, i32 0))
+// CHECK: call void @__assert_rtn(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @__func__.__mangle_block_invoke_2, i32 0, i32 0), i8* getelementptr inbounds {{.*}}, i32 9, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str[[STR1]], i32 0, i32 0))
// CHECK: }
diff --git a/test/CodeGen/ubsan-builtin-checks.c b/test/CodeGen/ubsan-builtin-checks.c
new file mode 100644
index 0000000..9733b0e
--- /dev/null
+++ b/test/CodeGen/ubsan-builtin-checks.c
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -w -emit-llvm -o - %s -fsanitize=builtin | FileCheck %s
+// RUN: %clang_cc1 -triple arm64-none-linux-gnu -w -emit-llvm -o - %s -fsanitize=builtin | FileCheck %s --check-prefix=NOT-UB
+
+// NOT-UB-NOT: __ubsan_handle_invalid_builtin
+
+// CHECK: define void @check_ctz
+void check_ctz(int n) {
+ // CHECK: [[NOT_ZERO:%.*]] = icmp ne i32 [[N:%.*]], 0, !nosanitize
+ // CHECK-NEXT: br i1 [[NOT_ZERO]]
+ //
+ // Handler block:
+ // CHECK: call void @__ubsan_handle_invalid_builtin
+ // CHECK-NEXT: unreachable
+ //
+ // Continuation block:
+ // CHECK: call i32 @llvm.cttz.i32(i32 [[N]], i1 true)
+ __builtin_ctz(n);
+
+ // CHECK: call void @__ubsan_handle_invalid_builtin
+ __builtin_ctzl(n);
+
+ // CHECK: call void @__ubsan_handle_invalid_builtin
+ __builtin_ctzll(n);
+}
+
+// CHECK: define void @check_clz
+void check_clz(int n) {
+ // CHECK: [[NOT_ZERO:%.*]] = icmp ne i32 [[N:%.*]], 0, !nosanitize
+ // CHECK-NEXT: br i1 [[NOT_ZERO]]
+ //
+ // Handler block:
+ // CHECK: call void @__ubsan_handle_invalid_builtin
+ // CHECK-NEXT: unreachable
+ //
+ // Continuation block:
+ // CHECK: call i32 @llvm.ctlz.i32(i32 [[N]], i1 true)
+ __builtin_clz(n);
+
+ // CHECK: call void @__ubsan_handle_invalid_builtin
+ __builtin_clzl(n);
+
+ // CHECK: call void @__ubsan_handle_invalid_builtin
+ __builtin_clzll(n);
+}
diff --git a/test/CodeGen/unsigned-overflow-minimal.c b/test/CodeGen/unsigned-overflow-minimal.c
new file mode 100644
index 0000000..d4b8966
--- /dev/null
+++ b/test/CodeGen/unsigned-overflow-minimal.c
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=unsigned-integer-overflow -fsanitize-minimal-runtime %s -emit-llvm -o - | FileCheck %s
+
+unsigned long li, lj, lk;
+
+// CHECK-LABEL: define void @testlongadd()
+void testlongadd() {
+ // CHECK: call void @__ubsan_handle_add_overflow_minimal_abort()
+ li = lj + lk;
+}
+
+// CHECK-LABEL: define void @testlongsub()
+void testlongsub() {
+ // CHECK: call void @__ubsan_handle_sub_overflow_minimal_abort()
+ li = lj - lk;
+}
+
+// CHECK-LABEL: define void @testlongmul()
+void testlongmul() {
+ // CHECK: call void @__ubsan_handle_mul_overflow_minimal_abort()
+ li = lj * lk;
+}
diff --git a/test/CodeGenCXX/atomic-inline.cpp b/test/CodeGenCXX/atomic-inline.cpp
new file mode 100644
index 0000000..fe72758
--- /dev/null
+++ b/test/CodeGenCXX/atomic-inline.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - -triple=x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 %s -std=c++11 -emit-llvm -o - -triple=x86_64-linux-gnu -target-cpu core2 | FileCheck %s --check-prefix=CORE2
+// Check the atomic code generation for cpu targets w/wo cx16 support.
+
+struct alignas(8) AM8 {
+ int f1, f2;
+};
+AM8 m8;
+AM8 load8() {
+ AM8 am;
+ // CHECK-LABEL: @_Z5load8v
+ // CHECK: load atomic i64, {{.*}} monotonic
+ // CORE2-LABEL: @_Z5load8v
+ // CORE2: load atomic i64, {{.*}} monotonic
+ __atomic_load(&m8, &am, 0);
+ return am;
+}
+
+AM8 s8;
+void store8() {
+ // CHECK-LABEL: @_Z6store8v
+ // CHECK: store atomic i64 {{.*}} monotonic
+ // CORE2-LABEL: @_Z6store8v
+ // CORE2: store atomic i64 {{.*}} monotonic
+ __atomic_store(&m8, &s8, 0);
+}
+
+bool cmpxchg8() {
+ AM8 am;
+ // CHECK-LABEL: @_Z8cmpxchg8v
+ // CHECK: cmpxchg i64* {{.*}} monotonic
+ // CORE2-LABEL: @_Z8cmpxchg8v
+ // CORE2: cmpxchg i64* {{.*}} monotonic
+ return __atomic_compare_exchange(&m8, &s8, &am, 0, 0, 0);
+}
+
+struct alignas(16) AM16 {
+ long f1, f2;
+};
+
+AM16 m16;
+AM16 load16() {
+ AM16 am;
+ // CHECK-LABEL: @_Z6load16v
+ // CHECK: call void @__atomic_load
+ // CORE2-LABEL: @_Z6load16v
+ // CORE2: load atomic i128, {{.*}} monotonic
+ __atomic_load(&m16, &am, 0);
+ return am;
+}
+
+AM16 s16;
+void store16() {
+ // CHECK-LABEL: @_Z7store16v
+ // CHECK: call void @__atomic_store
+ // CORE2-LABEL: @_Z7store16v
+ // CORE2: store atomic i128 {{.*}} monotonic
+ __atomic_store(&m16, &s16, 0);
+}
+
+bool cmpxchg16() {
+ AM16 am;
+ // CHECK-LABEL: @_Z9cmpxchg16v
+ // CHECK: call zeroext i1 @__atomic_compare_exchange
+ // CORE2-LABEL: @_Z9cmpxchg16v
+ // CORE2: cmpxchg i128* {{.*}} monotonic
+ return __atomic_compare_exchange(&m16, &s16, &am, 0, 0, 0);
+}
+
diff --git a/test/CodeGenCXX/catch-undef-behavior.cpp b/test/CodeGenCXX/catch-undef-behavior.cpp
index d58853c..aa3da10 100644
--- a/test/CodeGenCXX/catch-undef-behavior.cpp
+++ b/test/CodeGenCXX/catch-undef-behavior.cpp
@@ -16,6 +16,10 @@
// Check that type mismatch handler is not modified by ASan.
// CHECK-ASAN: private unnamed_addr global { { [{{.*}} x i8]*, i32, i32 }, { i16, i16, [4 x i8] }*, i8*, i8 } { {{.*}}, { i16, i16, [4 x i8] }* [[TYPE_DESCR]], {{.*}} }
+// CHECK: [[IndirectRTTI_ZTIFvPFviEE:@.+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
+// CHECK-X86: [[IndirectRTTI_ZTIFvPFviEE:@.+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
+// CHECK-X32: [[IndirectRTTI_ZTIFvPFviEE:@.+]] = private constant i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*)
+
struct T : S {};
// CHECK-LABEL: @_Z17reference_binding
@@ -395,23 +399,30 @@
// CHECK-NEXT: br i1 [[AND]]
}
-// CHECK-LABEL: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i8* }> <{ i32 1413876459, i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*) }>
-// CHECK-X32: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i8* }> <{ i32 1413875435, i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*) }>
-// CHECK-X86: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i8* }> <{ i32 1413875435, i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*) }>
+//
+// CHECK-LABEL: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i32 }> <{ i32 846595819, i32 trunc (i64 sub (i64 ptrtoint (i8** {{.*}} to i64), i64 ptrtoint (void (void (i32)*)* @_Z22indirect_function_callPFviE to i64)) to i32) }>
+// CHECK-X32: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i32 }> <{ i32 846595819, i32 sub (i32 ptrtoint (i8** [[IndirectRTTI_ZTIFvPFviEE]] to i32), i32 ptrtoint (void (void (i32)*)* @_Z22indirect_function_callPFviE to i32)) }>
+// CHECK-X86: @_Z22indirect_function_callPFviE({{.*}} prologue <{ i32, i32 }> <{ i32 846595819, i32 sub (i32 ptrtoint (i8** [[IndirectRTTI_ZTIFvPFviEE]] to i32), i32 ptrtoint (void (void (i32)*)* @_Z22indirect_function_callPFviE to i32)) }>
void indirect_function_call(void (*p)(int)) {
- // CHECK: [[PTR:%.+]] = bitcast void (i32)* {{.*}} to <{ i32, i8* }>*
+ // CHECK: [[PTR:%.+]] = bitcast void (i32)* {{.*}} to <{ i32, i32 }>*
// Signature check
- // CHECK-NEXT: [[SIGPTR:%.+]] = getelementptr <{ i32, i8* }>, <{ i32, i8* }>* [[PTR]], i32 0, i32 0
+ // CHECK-NEXT: [[SIGPTR:%.+]] = getelementptr <{ i32, i32 }>, <{ i32, i32 }>* [[PTR]], i32 0, i32 0
// CHECK-NEXT: [[SIG:%.+]] = load i32, i32* [[SIGPTR]]
- // CHECK-NEXT: [[SIGCMP:%.+]] = icmp eq i32 [[SIG]], 1413876459
+ // CHECK-NEXT: [[SIGCMP:%.+]] = icmp eq i32 [[SIG]], 846595819
// CHECK-NEXT: br i1 [[SIGCMP]]
// RTTI pointer check
- // CHECK: [[RTTIPTR:%.+]] = getelementptr <{ i32, i8* }>, <{ i32, i8* }>* [[PTR]], i32 0, i32 1
- // CHECK-NEXT: [[RTTI:%.+]] = load i8*, i8** [[RTTIPTR]]
+ // CHECK: [[RTTIPTR:%.+]] = getelementptr <{ i32, i32 }>, <{ i32, i32 }>* [[PTR]], i32 0, i32 1
+ // CHECK-NEXT: [[RTTIEncIntTrunc:%.+]] = load i32, i32* [[RTTIPTR]]
+ // CHECK-NEXT: [[RTTIEncInt:%.+]] = sext i32 [[RTTIEncIntTrunc]] to i64
+ // CHECK-NEXT: [[FuncAddrInt:%.+]] = ptrtoint void (i32)* {{.*}} to i64
+ // CHECK-NEXT: [[IndirectGVInt:%.+]] = add i64 [[RTTIEncInt]], [[FuncAddrInt]]
+ // CHECK-NEXT: [[IndirectGV:%.+]] = inttoptr i64 [[IndirectGVInt]] to i8**
+ // CHECK-NEXT: [[RTTI:%.+]] = load i8*, i8** [[IndirectGV]], align 8
// CHECK-NEXT: [[RTTICMP:%.+]] = icmp eq i8* [[RTTI]], bitcast ({ i8*, i8* }* @_ZTIFviE to i8*)
// CHECK-NEXT: br i1 [[RTTICMP]]
+
p(42);
}
@@ -454,7 +465,7 @@
void this_align_lambda_2();
};
void ThisAlign::this_align_lambda() {
- // CHECK-LABEL: define {{.*}}@"_ZZN9ThisAlign17this_align_lambdaEvENK3$_0clEv"
+ // CHECK-LABEL: define internal %struct.ThisAlign* @"_ZZN9ThisAlign17this_align_lambdaEvENK3$_0clEv"
// CHECK-SAME: (%{{.*}}* %[[this:[^)]*]])
// CHECK: %[[this_addr:.*]] = alloca
// CHECK: store %{{.*}}* %[[this]], %{{.*}}** %[[this_addr]],
@@ -555,7 +566,7 @@
}
void ThisAlign::this_align_lambda_2() {
- // CHECK-LABEL: define {{.*}}@"_ZZN9ThisAlign19this_align_lambda_2EvENK3$_1clEv"
+ // CHECK-LABEL: define internal void @"_ZZN9ThisAlign19this_align_lambda_2EvENK3$_1clEv"
// CHECK-SAME: (%{{.*}}* %[[this:[^)]*]])
// CHECK: %[[this_addr:.*]] = alloca
// CHECK: store %{{.*}}* %[[this]], %{{.*}}** %[[this_addr]],
diff --git a/test/CodeGenCXX/clang-abi-compat.cpp b/test/CodeGenCXX/clang-abi-compat.cpp
deleted file mode 100644
index 409c721..0000000
--- a/test/CodeGenCXX/clang-abi-compat.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.0 %s -emit-llvm -o - | FileCheck --check-prefix=PRE39 --check-prefix=PRE5 %s
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.8 %s -emit-llvm -o - | FileCheck --check-prefix=PRE39 --check-prefix=PRE5 %s
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=3.9 %s -emit-llvm -o - | FileCheck --check-prefix=V39 --check-prefix=PRE5 %s
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=4.0 %s -emit-llvm -o - | FileCheck --check-prefix=V39 --check-prefix=PRE5 %s
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=5 %s -emit-llvm -o - | FileCheck --check-prefix=V39 --check-prefix=V5 %s
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-linux-gnu -fclang-abi-compat=latest %s -emit-llvm -o - | FileCheck --check-prefix=V39 --check-prefix=V5 %s
-
-typedef __attribute__((vector_size(8))) long long v1xi64;
-void clang39(v1xi64) {}
-// PRE39: @_Z7clang39Dv1_x(i64
-// V39: @_Z7clang39Dv1_x(double
-
-struct A {
- A(const A&) = default;
- A(A&&);
-};
-void clang5(A) {}
-// PRE5: @_Z6clang51A()
-// V5: @_Z6clang51A(%{{.*}}*
diff --git a/test/CodeGenCXX/debug-info-inheriting-constructor.cpp b/test/CodeGenCXX/debug-info-inheriting-constructor.cpp
index e3708e2..8e47a0d 100644
--- a/test/CodeGenCXX/debug-info-inheriting-constructor.cpp
+++ b/test/CodeGenCXX/debug-info-inheriting-constructor.cpp
@@ -13,7 +13,7 @@
// CHECK: call void @llvm.dbg.declare
// CHECK-NOT ret void
// CHECK: call void @llvm.dbg.declare(metadata %{{.*}}** %{{[^,]+}},
-// CHECK-SAME: metadata ![[THIS:[0-9]+]], metadata !{{[0-9]+}}), !dbg ![[LOC:[0-9]+]]
+// CHECK-SAME: metadata ![[THIS:[0-9]+]], metadata !DIExpression()), !dbg ![[LOC:[0-9]+]]
// CHECK: ret void, !dbg ![[NOINL:[0-9]+]]
// CHECK: ![[FOO:.*]] = distinct !DISubprogram(name: "foo"
// CHECK-DAG: ![[A:.*]] = distinct !DISubprogram(name: "A", linkageName: "_ZN1BCI11AEiz"
diff --git a/test/CodeGenCXX/debug-info-inlined.cpp b/test/CodeGenCXX/debug-info-inlined.cpp
index 9969ef7..53e2cc9 100644
--- a/test/CodeGenCXX/debug-info-inlined.cpp
+++ b/test/CodeGenCXX/debug-info-inlined.cpp
@@ -1,45 +1,29 @@
// RUN: %clang_cc1 -emit-llvm -triple i686-pc-windows-msvc19.0.24213 -gcodeview -debug-info-kind=limited -std=c++14 %s -o - | FileCheck %s
// PR33997.
-struct already_AddRefed {
- ~already_AddRefed();
+struct WithDtor {
+ ~WithDtor();
};
-struct RefPtr {
- operator int *();
+struct Base {
+ Base(WithDtor);
};
-struct ServoCssRulesStrong {
- already_AddRefed Consume();
+class Forward : Base {
+ using Base::Base;
};
-struct GroupRule {
- GroupRule(already_AddRefed);
+class A : Forward {
+ A();
};
-class ConditionRule : GroupRule {
- using GroupRule::GroupRule;
+class B : Forward {
+ B();
};
-class CSSMediaRule : ConditionRule {
- using ConditionRule::ConditionRule;
-};
-class CSSMozDocumentRule : ConditionRule {
- using ConditionRule::ConditionRule;
-};
-class ServoDocumentRule : CSSMozDocumentRule {
- ServoDocumentRule(RefPtr);
-};
-class ServoMediaRule : CSSMediaRule {
- ServoMediaRule(RefPtr);
-};
-ServoCssRulesStrong Servo_MediaRule_GetRules(int *);
-ServoCssRulesStrong Servo_DocumentRule_GetRules(int *);
-ServoDocumentRule::ServoDocumentRule(RefPtr aRawRule)
- : CSSMozDocumentRule(Servo_DocumentRule_GetRules(aRawRule).Consume()) {}
+A::A() : Forward(WithDtor()) {}
-ServoMediaRule::ServoMediaRule(RefPtr aRawRule)
- : CSSMediaRule(Servo_MediaRule_GetRules(aRawRule).Consume()) {}
+B::B() : Forward(WithDtor()) {}
-// CHECK: define{{.*}}ServoMediaRule
+// CHECK: define{{.*}}A
// CHECK-NOT: {{ ret }}
-// CHECK: store %class.ConditionRule* %
-// CHECK-SAME: %class.ConditionRule** %
+// CHECK: store %class.Forward* %
+// CHECK-SAME: %class.Forward** %
// CHECK-SAME: !dbg ![[INL:[0-9]+]]
-// CHECK: ![[INL]] = !DILocation(line: 16, scope: ![[SP:[0-9]+]], inlinedAt:
-// CHECK: ![[SP]] = distinct !DISubprogram(name: "GroupRule", {{.*}}isDefinition: true
+// CHECK: ![[INL]] = !DILocation(line: 10, scope: ![[SP:[0-9]+]], inlinedAt:
+// CHECK: ![[SP]] = distinct !DISubprogram(name: "Base", {{.*}}isDefinition: true
diff --git a/test/CodeGenCXX/debug-info-static-member.cpp b/test/CodeGenCXX/debug-info-static-member.cpp
index c777ccb..3537754 100644
--- a/test/CodeGenCXX/debug-info-static-member.cpp
+++ b/test/CodeGenCXX/debug-info-static-member.cpp
@@ -32,7 +32,7 @@
// why the definition of "a" comes before the declarations while
// "b" and "c" come after.
-// CHECK: [[A]] = !DIGlobalVariableExpression(var: [[AV:.*]])
+// CHECK: [[A]] = !DIGlobalVariableExpression(var: [[AV:.*]], expr: !DIExpression())
// CHECK: [[AV]] = distinct !DIGlobalVariable(name: "a",
// CHECK-SAME: declaration: ![[DECL_A:[0-9]+]])
//
@@ -45,7 +45,7 @@
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "static_decl_templ_var"
int C::a = 4;
-// CHECK: [[B]] = !DIGlobalVariableExpression(var: [[BV:.*]])
+// CHECK: [[B]] = !DIGlobalVariableExpression(var: [[BV:.*]], expr: !DIExpression())
// CHECK: [[BV]] = distinct !DIGlobalVariable(name: "b",
// CHECK-SAME: declaration: ![[DECL_B:[0-9]+]])
// CHECK: ![[DECL_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b"
@@ -93,7 +93,7 @@
// CHECK-SAME: flags: DIFlagPublic | DIFlagStaticMember)
int C::b = 2;
-// CHECK: [[C]] = !DIGlobalVariableExpression(var: [[CV:.*]])
+// CHECK: [[C]] = !DIGlobalVariableExpression(var: [[CV:.*]], expr: !DIExpression())
// CHECK: [[CV]] = distinct !DIGlobalVariable(name: "c", {{.*}} declaration: ![[DECL_C]])
int C::c = 1;
diff --git a/test/CodeGenCXX/debug-info-template-member.cpp b/test/CodeGenCXX/debug-info-template-member.cpp
index 2b84074..a6aa1a0 100644
--- a/test/CodeGenCXX/debug-info-template-member.cpp
+++ b/test/CodeGenCXX/debug-info-template-member.cpp
@@ -19,7 +19,7 @@
}
// The compile unit pulls in the global variables first.
-// CHECK: [[X]] = !DIGlobalVariableExpression(var: [[XV:.*]])
+// CHECK: [[X]] = !DIGlobalVariableExpression(var: [[XV:.*]], expr: !DIExpression())
// CHECK: [[XV]] = distinct !DIGlobalVariable(name: "x",
// CHECK-SAME: type: ![[OUTER_FOO_INNER_ID:[0-9]+]]
diff --git a/test/CodeGenCXX/debug-info-template.cpp b/test/CodeGenCXX/debug-info-template.cpp
index 54fac0b..0c34145 100644
--- a/test/CodeGenCXX/debug-info-template.cpp
+++ b/test/CodeGenCXX/debug-info-template.cpp
@@ -25,7 +25,7 @@
int glb;
void func();
-// CHECK: [[TCI]] = !DIGlobalVariableExpression(var: [[TCIV:.*]])
+// CHECK: [[TCI]] = !DIGlobalVariableExpression(var: [[TCIV:.*]], expr: !DIExpression())
// CHECK: [[TCIV]] = distinct !DIGlobalVariable(name: "tci",
// CHECK-SAME: type: ![[TCNESTED:[0-9]+]]
// CHECK: ![[TCNESTED]] ={{.*}}!DICompositeType(tag: DW_TAG_structure_type, name: "nested",
@@ -84,7 +84,7 @@
// CHECK: [[TCARG7_3]] = !DITemplateValueParameter(type: [[INT]], value: i32 3)
3>::nested tci;
-// CHECK: [[TCN]] = !DIGlobalVariableExpression(var: [[TCNV:.*]])
+// CHECK: [[TCN]] = !DIGlobalVariableExpression(var: [[TCNV:.*]], expr: !DIExpression())
// CHECK: [[TCNV]] = distinct !DIGlobalVariable(name: "tcn"
// CHECK-SAME: type: ![[TCNT:[0-9]+]]
TC
@@ -125,7 +125,7 @@
struct NN {
};
-// CHECK: [[NN]] = !DIGlobalVariableExpression(var: [[NNV:.*]])
+// CHECK: [[NN]] = !DIGlobalVariableExpression(var: [[NNV:.*]], expr: !DIExpression())
// CHECK: [[NNV]] = distinct !DIGlobalVariable(name: "nn"
// CHECK-SAME: type: ![[NNT:[0-9]+]]
diff --git a/test/CodeGenCXX/debug-info.cpp b/test/CodeGenCXX/debug-info.cpp
index 2b86150..6d6d0c7 100644
--- a/test/CodeGenCXX/debug-info.cpp
+++ b/test/CodeGenCXX/debug-info.cpp
@@ -4,11 +4,11 @@
// CHECK: @_ZN6pr96081xE = global [3 x i8]* null, align 8, !dbg [[X:![0-9]+]]
// CHECK: define void @_ZN7pr147634funcENS_3fooE
-// CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[F:[0-9]+]], metadata ![[EXPR:[0-9]+]])
+// CHECK: call void @llvm.dbg.declare({{.*}}, metadata ![[F:[0-9]+]], metadata !DIExpression())
// !llvm.dbg.cu pulls in globals and their types first.
// CHECK-NOT: !DIGlobalVariable(name: "c"
-// CHECK: [[X]] = !DIGlobalVariableExpression(var: [[XV:!.*]])
+// CHECK: [[X]] = !DIGlobalVariableExpression(var: [[XV:!.*]], expr: !DIExpression())
// CHECK: [[XV]] = distinct !DIGlobalVariable(name: "x", linkageName: "_ZN6pr96081xE"
// CHECK-SAME: type: [[INCARRAYPTR:![0-9]*]]
// CHECK: [[INCARRAYPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: [[INCARRAY:![0-9]+]]
@@ -21,7 +21,6 @@
// CHECK: ![[INCTYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "incomplete"
// CHECK-SAME: DIFlagFwdDecl
-// CHECK: ![[EXPR]] = !DIExpression()
template<typename T> struct Identity {
typedef T Type;
diff --git a/test/CodeGenCXX/inline-dllexport-member.cpp b/test/CodeGenCXX/inline-dllexport-member.cpp
index 66ef459..a98f560 100644
--- a/test/CodeGenCXX/inline-dllexport-member.cpp
+++ b/test/CodeGenCXX/inline-dllexport-member.cpp
@@ -7,7 +7,7 @@
static const unsigned int ui = 0;
};
-// CHECK: [[UI]] = !DIGlobalVariableExpression(var: [[UIV:.*]])
+// CHECK: [[UI]] = !DIGlobalVariableExpression(var: [[UIV:.*]], expr: !DIExpression())
// CHECK: [[UIV]] = distinct !DIGlobalVariable(name: "ui", linkageName: "\01?ui@s@@2IB", scope: ![[SCOPE:[0-9]+]],
// CHECK: ![[SCOPE]] = distinct !DICompileUnit(
diff --git a/test/CodeGenCXX/member-expr-references-variable.cpp b/test/CodeGenCXX/member-expr-references-variable.cpp
new file mode 100644
index 0000000..8c1dded
--- /dev/null
+++ b/test/CodeGenCXX/member-expr-references-variable.cpp
@@ -0,0 +1,104 @@
+// RUN: %clang_cc1 -std=c++11 %s -triple x86_64-apple-darwin10 -emit-llvm -o - | FileCheck %s
+
+struct Agg { const char * x; const char * y; constexpr Agg() : x(0), y(0) {} };
+
+struct Struct {
+ constexpr static const char *name = "foo";
+
+ constexpr static __complex float complexValue = 42.0;
+
+ static constexpr const Agg &agg = Agg();
+
+ Struct();
+ Struct(int x);
+};
+
+void use(int n, const char *c);
+
+Struct *getPtr();
+
+// CHECK: @[[STR:.*]] = private unnamed_addr constant [4 x i8] c"foo\00", align 1
+
+void scalarStaticVariableInMemberExpr(Struct *ptr, Struct &ref) {
+ use(1, Struct::name);
+// CHECK: call void @_Z3useiPKc(i32 1, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
+ Struct s;
+ use(2, s.name);
+// CHECK: call void @_Z3useiPKc(i32 2, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
+ use(3, ptr->name);
+// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
+// CHECK: call void @_Z3useiPKc(i32 3, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
+ use(4, ref.name);
+// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
+// CHECK: call void @_Z3useiPKc(i32 4, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
+ use(5, Struct(2).name);
+// CHECK: call void @_ZN6StructC1Ei(%struct.Struct* %{{.*}}, i32 2)
+// CHECK: call void @_Z3useiPKc(i32 5, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
+ use(6, getPtr()->name);
+// CHECK: call %struct.Struct* @_Z6getPtrv()
+// CHECK: call void @_Z3useiPKc(i32 6, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[STR]], i32 0, i32 0))
+}
+
+void use(int n, __complex float v);
+
+void complexStaticVariableInMemberExpr(Struct *ptr, Struct &ref) {
+ use(1, Struct::complexValue);
+// CHECK: store float 4.200000e+01, float* %[[coerce0:.*]].{{.*}}, align 4
+// CHECK: store float 0.000000e+00, float* %[[coerce0]].{{.*}}, align 4
+// CHECK: %[[cast0:.*]] = bitcast { float, float }* %[[coerce0]] to <2 x float>*
+// CHECK: %[[vector0:.*]] = load <2 x float>, <2 x float>* %[[cast0]], align 4
+// CHECK: call void @_Z3useiCf(i32 1, <2 x float> %[[vector0]])
+ Struct s;
+ use(2, s.complexValue);
+// CHECK: store float 4.200000e+01, float* %[[coerce1:.*]].{{.*}}, align 4
+// CHECK: store float 0.000000e+00, float* %[[coerce1]].{{.*}}, align 4
+// CHECK: %[[cast1:.*]] = bitcast { float, float }* %[[coerce1]] to <2 x float>*
+// CHECK: %[[vector1:.*]] = load <2 x float>, <2 x float>* %[[cast1]], align 4
+// CHECK: call void @_Z3useiCf(i32 2, <2 x float> %[[vector1]])
+ use(3, ptr->complexValue);
+// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
+// CHECK: store float 4.200000e+01, float* %[[coerce2:.*]].{{.*}}, align 4
+// CHECK: store float 0.000000e+00, float* %[[coerce2]].{{.*}}, align 4
+// CHECK: %[[cast2:.*]] = bitcast { float, float }* %[[coerce2]] to <2 x float>*
+// CHECK: %[[vector2:.*]] = load <2 x float>, <2 x float>* %[[cast2]], align 4
+// CHECK: call void @_Z3useiCf(i32 3, <2 x float> %[[vector2]])
+ use(4, ref.complexValue);
+// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
+// CHECK: store float 4.200000e+01, float* %[[coerce3:.*]].{{.*}}, align 4
+// CHECK: store float 0.000000e+00, float* %[[coerce3]].{{.*}}, align 4
+// CHECK: %[[cast3:.*]] = bitcast { float, float }* %[[coerce3]] to <2 x float>*
+// CHECK: %[[vector3:.*]] = load <2 x float>, <2 x float>* %[[cast3]], align 4
+// CHECK: call void @_Z3useiCf(i32 4, <2 x float> %[[vector3]])
+ use(5, Struct(2).complexValue);
+// CHECK: call void @_ZN6StructC1Ei(%struct.Struct* %{{.*}}, i32 2)
+// CHECK: store float 4.200000e+01, float* %[[coerce4:.*]].{{.*}}, align 4
+// CHECK: store float 0.000000e+00, float* %[[coerce4]].{{.*}}, align 4
+// CHECK: %[[cast4:.*]] = bitcast { float, float }* %[[coerce4]] to <2 x float>*
+// CHECK: %[[vector4:.*]] = load <2 x float>, <2 x float>* %[[cast4]], align 4
+// CHECK: call void @_Z3useiCf(i32 5, <2 x float> %[[vector4]])
+ use(6, getPtr()->complexValue);
+// CHECK: call %struct.Struct* @_Z6getPtrv()
+// CHECK: store float 4.200000e+01, float* %[[coerce5:.*]].{{.*}}, align 4
+// CHECK: store float 0.000000e+00, float* %[[coerce5]].{{.*}}, align 4
+// CHECK: %[[cast5:.*]] = bitcast { float, float }* %[[coerce5]] to <2 x float>*
+// CHECK: %[[vector5:.*]] = load <2 x float>, <2 x float>* %[[cast5]], align 4
+// CHECK: call void @_Z3useiCf(i32 6, <2 x float> %[[vector5]])
+}
+
+void aggregateRefInMemberExpr(Struct *ptr, Struct &ref) {
+ use(1, Struct::agg.x);
+// CHECK: %[[value0:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
+// CHECK: call void @_Z3useiPKc(i32 1, i8* %[[value0]])
+ Struct s;
+ use(2, s.agg.x);
+// CHECK: %[[value1:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
+// CHECK: call void @_Z3useiPKc(i32 2, i8* %[[value1]])
+ use(3, ptr->agg.x);
+// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
+// CHECK: %[[value2:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
+// CHECK: call void @_Z3useiPKc(i32 3, i8* %[[value2]])
+ use(4, ref.agg.x);
+// CHECK: load %struct.Struct*, %struct.Struct** %{{.*}}, align 8
+// CHECK: %[[value3:.*]] = load i8*, i8** getelementptr inbounds (%struct.Agg, %struct.Agg* @_ZGRN6Struct3aggE_, i32 0, i32 0), align 8
+// CHECK: call void @_Z3useiPKc(i32 4, i8* %[[value3]])
+}
diff --git a/test/CodeGenCXX/ubsan-devirtualized-calls.cpp b/test/CodeGenCXX/ubsan-devirtualized-calls.cpp
index bc8861a..3de4e37 100644
--- a/test/CodeGenCXX/ubsan-devirtualized-calls.cpp
+++ b/test/CodeGenCXX/ubsan-devirtualized-calls.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -emit-llvm -fsanitize=vptr %s -o - | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s
struct Base1 {
virtual void f1() {}
@@ -64,6 +64,11 @@
// CHECK-NEXT: call void @__ubsan_handle_dynamic_type_cache{{[_a-z]*}}({{.*}} [[UBSAN_TI_DERIVED3]] {{.*}}, i{{[0-9]+}} %[[P1]]
static_cast<Base1 *>(badp)->f1(); //< No devirt, test 'badp isa Base1'.
+ // We were able to skip the null check on the first type check becuase 'p'
+ // is backed by an alloca. We can't skip the second null check because 'badp'
+ // is a (bitcast (load ...)).
+ // CHECK: call void @__ubsan_handle_type_mismatch
+ //
// CHECK: %[[BADP1:[0-9]+]] = ptrtoint %struct.Base1* {{%[0-9]+}} to i{{[0-9]+}}, !nosanitize
// CHECK-NEXT: call void @__ubsan_handle_dynamic_type_cache{{[_a-z]*}}({{.*}} [[UBSAN_TI_BASE1]] {{.*}}, i{{[0-9]+}} %[[BADP1]]
}
@@ -76,6 +81,8 @@
// CHECK-NEXT: call void @__ubsan_handle_dynamic_type_cache{{[_a-z]*}}({{.*}} [[UBSAN_TI_DERIVED4_1]] {{.*}}, i{{[0-9]+}} %[[P1]]
static_cast<Base1 *>(badp)->f1(); //< Devirt Base1::f1 to Derived4::f1.
+ // CHECK: call void @__ubsan_handle_type_mismatch
+ //
// CHECK: %[[BADP1:[0-9]+]] = ptrtoint %struct.Derived4* {{%[0-9]+}} to i{{[0-9]+}}, !nosanitize
// CHECK-NEXT: call void @__ubsan_handle_dynamic_type_cache{{[_a-z]*}}({{.*}} [[UBSAN_TI_DERIVED4_2]] {{.*}}, i{{[0-9]+}} %[[BADP1]]
}
diff --git a/test/CodeGenCXX/ubsan-type-checks.cpp b/test/CodeGenCXX/ubsan-type-checks.cpp
index 786d049..e53ab24 100644
--- a/test/CodeGenCXX/ubsan-type-checks.cpp
+++ b/test/CodeGenCXX/ubsan-type-checks.cpp
@@ -1,6 +1,8 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=alignment | FileCheck %s -check-prefixes=ALIGN,COMMON
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=null | FileCheck %s -check-prefixes=NULL,COMMON
// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=object-size | FileCheck %s -check-prefixes=OBJSIZE,COMMON
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=null,vptr | FileCheck %s -check-prefixes=VPTR
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-apple-darwin10 -emit-llvm -o - %s -fsanitize=vptr | FileCheck %s -check-prefixes=VPTR_NO_NULL
struct A {
// COMMON-LABEL: define linkonce_odr void @_ZN1A10do_nothingEv
@@ -24,13 +26,60 @@
// NULL: icmp ne %struct.B* %{{.*}}, null, !nosanitize
// OBJSIZE-NOT: call i64 @llvm.objectsize
+ // OBJSIZE: ret void
}
};
-void force_irgen() {
+struct Animal {
+ virtual const char *speak() = 0;
+};
+
+struct Cat : Animal {
+ const char *speak() override { return "meow"; }
+};
+
+struct Dog : Animal {
+ const char *speak() override { return "woof"; }
+};
+
+// VPTR-LABEL: define void @_Z12invalid_castP3Cat
+void invalid_cast(Cat *cat = nullptr) {
+ // If -fsanitize=null is available, we'll reuse its check:
+ //
+ // VPTR: [[ICMP:%.*]] = icmp ne %struct.Dog* {{.*}}, null
+ // VPTR-NEXT: br i1 [[ICMP]]
+ // VPTR: call void @__ubsan_handle_type_mismatch
+ // VPTR-NOT: icmp ne %struct.Dog* {{.*}}, null
+ // VPTR: br i1 [[ICMP]]
+ // VPTR: call void @__ubsan_handle_dynamic_type_cache_miss
+ //
+ // Fall back to the vptr sanitizer's null check when -fsanitize=null isn't
+ // available.
+ //
+ // VPTR_NO_NULL-NOT: call void @__ubsan_handle_type_mismatch
+ // VPTR_NO_NULL: [[ICMP:%.*]] = icmp ne %struct.Dog* {{.*}}, null
+ // VPTR_NO_NULL-NEXT: br i1 [[ICMP]]
+ // VPTR_NO_NULL: call void @__ubsan_handle_dynamic_type_cache_miss
+ auto *badDog = reinterpret_cast<Dog *>(cat);
+ badDog->speak();
+}
+
+// VPTR_NO_NULL-LABEL: define void @_Z13invalid_cast2v
+void invalid_cast2() {
+ // We've got a pointer to an alloca, so there's no run-time null check needed.
+ // VPTR_NO_NULL-NOT: call void @__ubsan_handle_type_mismatch
+ // VPTR_NO_NULL: call void @__ubsan_handle_dynamic_type_cache_miss
+ Cat cat;
+ cat.speak();
+}
+
+int main() {
A a;
a.do_nothing();
B b;
b.do_nothing();
+
+ invalid_cast();
+ return 0;
}
diff --git a/test/CodeGenCXX/ubsan-vtable-checks.cpp b/test/CodeGenCXX/ubsan-vtable-checks.cpp
index e684ae9..5e17913 100644
--- a/test/CodeGenCXX/ubsan-vtable-checks.cpp
+++ b/test/CodeGenCXX/ubsan-vtable-checks.cpp
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NULL --check-prefix=ITANIUM
// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NULL --check-prefix=MSABI
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux -emit-llvm -fsanitize=vptr %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VPTR --check-prefix=ITANIUM
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=vptr %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VPTR --check-prefix=MSABI
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VPTR --check-prefix=ITANIUM
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows -emit-llvm -fsanitize=null,vptr %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VPTR --check-prefix=MSABI
struct T {
virtual ~T() {}
virtual int v() { return 1; }
diff --git a/test/CodeGenCXX/uncopyable-args.cpp b/test/CodeGenCXX/uncopyable-args.cpp
index 66d67e4..307a5cf 100644
--- a/test/CodeGenCXX/uncopyable-args.cpp
+++ b/test/CodeGenCXX/uncopyable-args.cpp
@@ -1,8 +1,5 @@
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=NEWABI
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -fclang-abi-compat=4.0 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=OLDABI
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-scei-ps4 -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=OLDABI
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=18 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-18
-// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s -fms-compatibility -fms-compatibility-version=19 | FileCheck %s -check-prefix=WIN64 -check-prefix=WIN64-19
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s | FileCheck %s -check-prefix=WIN64
namespace trivial {
// Trivial structs should be passed directly.
@@ -55,13 +52,12 @@
void bar() {
foo({});
}
-// CHECK-LABEL: define void @_ZN9move_ctor3barEv()
-// CHECK: call void @_Z{{.*}}C1Ev(
-// CHECK-NOT: call
-// NEWABI: call void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"* %{{.*}})
-// OLDABI: call void @_ZN9move_ctor3fooENS_1AE(i8* %{{.*}})
-// NEWABI-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"*)
-// OLDABI-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(i8*)
+// FIXME: The copy ctor is implicitly deleted.
+// CHECK-DISABLED-LABEL: define void @_ZN9move_ctor3barEv()
+// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
+// CHECK-DISABLED-NOT: call
+// CHECK-DISABLED: call void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"* %{{.*}})
+// CHECK-DISABLED-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"*)
// WIN64-LABEL: declare void @"\01?foo@move_ctor@@YAXUA@1@@Z"(%"struct.move_ctor::A"*)
}
@@ -77,13 +73,12 @@
void bar() {
foo({});
}
-// CHECK-LABEL: define void @_ZN11all_deleted3barEv()
-// CHECK: call void @_Z{{.*}}C1Ev(
-// CHECK-NOT: call
-// NEWABI: call void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"* %{{.*}})
-// OLDABI: call void @_ZN11all_deleted3fooENS_1AE(i8* %{{.*}})
-// NEWABI-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"*)
-// OLDABI-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(i8*)
+// FIXME: The copy ctor is deleted.
+// CHECK-DISABLED-LABEL: define void @_ZN11all_deleted3barEv()
+// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
+// CHECK-DISABLED-NOT: call
+// CHECK-DISABLED: call void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"* %{{.*}})
+// CHECK-DISABLED-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"*)
// WIN64-LABEL: declare void @"\01?foo@all_deleted@@YAXUA@1@@Z"(%"struct.all_deleted::A"*)
}
@@ -98,17 +93,14 @@
void bar() {
foo({});
}
-// CHECK-LABEL: define void @_ZN18implicitly_deleted3barEv()
-// CHECK: call void @_Z{{.*}}C1Ev(
-// CHECK-NOT: call
-// NEWABI: call void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"* %{{.*}})
-// OLDABI: call void @_ZN18implicitly_deleted3fooENS_1AE(i8* %{{.*}})
-// NEWABI-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"*)
-// OLDABI-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(i8*)
+// FIXME: The copy and move ctors are implicitly deleted.
+// CHECK-DISABLED-LABEL: define void @_ZN18implicitly_deleted3barEv()
+// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
+// CHECK-DISABLED-NOT: call
+// CHECK-DISABLED: call void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"* %{{.*}})
+// CHECK-DISABLED-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"*)
-// In MSVC 2013, the copy ctor is not deleted by a move assignment. In MSVC 2015, it is.
-// WIN64-18-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(i64
-// WIN64-19-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(%"struct.implicitly_deleted::A"*)
+// WIN64-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(%"struct.implicitly_deleted::A"*)
}
namespace one_deleted {
@@ -121,13 +113,12 @@
void bar() {
foo({});
}
-// CHECK-LABEL: define void @_ZN11one_deleted3barEv()
-// CHECK: call void @_Z{{.*}}C1Ev(
-// CHECK-NOT: call
-// NEWABI: call void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"* %{{.*}})
-// OLDABI: call void @_ZN11one_deleted3fooENS_1AE(i8* %{{.*}})
-// NEWABI-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"*)
-// OLDABI-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(i8*)
+// FIXME: The copy constructor is implicitly deleted.
+// CHECK-DISABLED-LABEL: define void @_ZN11one_deleted3barEv()
+// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
+// CHECK-DISABLED-NOT: call
+// CHECK-DISABLED: call void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"* %{{.*}})
+// CHECK-DISABLED-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"*)
// WIN64-LABEL: declare void @"\01?foo@one_deleted@@YAXUA@1@@Z"(%"struct.one_deleted::A"*)
}
@@ -204,12 +195,12 @@
void bar() {
foo({});
}
-// CHECK-LABEL: define void @_ZN14two_copy_ctors3barEv()
-// CHECK: call void @_Z{{.*}}C1Ev(
-// NEWABI: call void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* %{{.*}})
-// OLDABI: call void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* byval
-// NEWABI-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"*)
-// OLDABI-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* byval
+// FIXME: This class has a non-trivial copy ctor and a trivial copy ctor. It's
+// not clear whether we should pass by address or in registers.
+// CHECK-DISABLED-LABEL: define void @_ZN14two_copy_ctors3barEv()
+// CHECK-DISABLED: call void @_Z{{.*}}C1Ev(
+// CHECK-DISABLED: call void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* %{{.*}})
+// CHECK-DISABLED-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"*)
// WIN64-LABEL: declare void @"\01?foo@two_copy_ctors@@YAXUB@1@@Z"(%"struct.two_copy_ctors::B"*)
}
@@ -221,8 +212,6 @@
void *p;
};
void *foo(A a) { return a.p; }
-// NEWABI-LABEL: define i8* @_ZN15definition_only3fooENS_1AE(%"struct.definition_only::A"*
-// OLDABI-LABEL: define i8* @_ZN15definition_only3fooENS_1AE(i8*
// WIN64-LABEL: define i8* @"\01?foo@definition_only@@YAPEAXUA@1@@Z"(%"struct.definition_only::A"*
}
@@ -237,8 +226,6 @@
B b;
};
void *foo(A a) { return a.b.p; }
-// NEWABI-LABEL: define i8* @_ZN17deleted_by_member3fooENS_1AE(%"struct.deleted_by_member::A"*
-// OLDABI-LABEL: define i8* @_ZN17deleted_by_member3fooENS_1AE(i8*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member@@YAPEAXUA@1@@Z"(%"struct.deleted_by_member::A"*
}
@@ -252,8 +239,6 @@
A();
};
void *foo(A a) { return a.p; }
-// NEWABI-LABEL: define i8* @_ZN15deleted_by_base3fooENS_1AE(%"struct.deleted_by_base::A"*
-// OLDABI-LABEL: define i8* @_ZN15deleted_by_base3fooENS_1AE(i8*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base@@YAPEAXUA@1@@Z"(%"struct.deleted_by_base::A"*
}
@@ -268,8 +253,6 @@
B b;
};
void *foo(A a) { return a.b.p; }
-// NEWABI-LABEL: define i8* @_ZN22deleted_by_member_copy3fooENS_1AE(%"struct.deleted_by_member_copy::A"*
-// OLDABI-LABEL: define i8* @_ZN22deleted_by_member_copy3fooENS_1AE(i8*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_member_copy@@YAPEAXUA@1@@Z"(%"struct.deleted_by_member_copy::A"*
}
@@ -283,8 +266,6 @@
A();
};
void *foo(A a) { return a.p; }
-// NEWABI-LABEL: define i8* @_ZN20deleted_by_base_copy3fooENS_1AE(%"struct.deleted_by_base_copy::A"*
-// OLDABI-LABEL: define i8* @_ZN20deleted_by_base_copy3fooENS_1AE(i8*
// WIN64-LABEL: define i8* @"\01?foo@deleted_by_base_copy@@YAPEAXUA@1@@Z"(%"struct.deleted_by_base_copy::A"*
}
@@ -294,77 +275,6 @@
A(const A &o) = delete;
void *p;
};
-// NEWABI-LABEL: define i8* @_ZN15explicit_delete3fooENS_1AE(%"struct.explicit_delete::A"*
-// OLDABI-LABEL: define i8* @_ZN15explicit_delete3fooENS_1AE(i8*
// WIN64-LABEL: define i8* @"\01?foo@explicit_delete@@YAPEAXUA@1@@Z"(%"struct.explicit_delete::A"*
void *foo(A a) { return a.p; }
}
-
-namespace implicitly_deleted_copy_ctor {
-struct A {
- // No move ctor due to copy assignment.
- A &operator=(const A&);
- // Deleted copy ctor due to rvalue ref member.
- int &&ref;
-};
-// NEWABI-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1AE(%"struct.implicitly_deleted_copy_ctor::A"*
-// OLDABI-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1AE(i32*
-// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAAEAHUA@1@@Z"(%"struct.implicitly_deleted_copy_ctor::A"*
-int &foo(A a) { return a.ref; }
-
-struct B {
- // Passed direct: has non-deleted trivial copy ctor.
- B &operator=(const B&);
- int &ref;
-};
-int &foo(B b) { return b.ref; }
-// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1BE(i32*
-// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAAEAHUB@1@@Z"(i64
-
-struct X { X(const X&); };
-struct Y { Y(const Y&) = default; };
-
-union C {
- C &operator=(const C&);
- // Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.
- X x;
- int n;
-};
-int foo(C c) { return c.n; }
-// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1CE(%"union.implicitly_deleted_copy_ctor::C"*
-// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHTC@1@@Z"(%"union.implicitly_deleted_copy_ctor::C"*
-
-struct D {
- D &operator=(const D&);
- // Passed indirect: copy ctor deleted due to variant member with nontrivial copy ctor.
- union {
- X x;
- int n;
- };
-};
-int foo(D d) { return d.n; }
-// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1DE(%"struct.implicitly_deleted_copy_ctor::D"*
-// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHUD@1@@Z"(%"struct.implicitly_deleted_copy_ctor::D"*
-
-union E {
- // Passed direct: has non-deleted trivial copy ctor.
- E &operator=(const E&);
- Y y;
- int n;
-};
-int foo(E e) { return e.n; }
-// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1EE(i32
-// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHTE@1@@Z"(i32
-
-struct F {
- // Passed direct: has non-deleted trivial copy ctor.
- F &operator=(const F&);
- union {
- Y y;
- int n;
- };
-};
-int foo(F f) { return f.n; }
-// CHECK-LABEL: define {{.*}} @_ZN28implicitly_deleted_copy_ctor3fooENS_1FE(i32
-// WIN64-LABEL: define {{.*}} @"\01?foo@implicitly_deleted_copy_ctor@@YAHUF@1@@Z"(i32
-}
diff --git a/test/CodeGenObjC/arc-bridged-cast.m b/test/CodeGenObjC/arc-bridged-cast.m
index 97a45c5..93f5d7c 100644
--- a/test/CodeGenObjC/arc-bridged-cast.m
+++ b/test/CodeGenObjC/arc-bridged-cast.m
@@ -97,3 +97,10 @@
// CHECK-NEXT: ret void
}
+// CHECK-LABEL: define %struct.__CFString* @bridge_of_paren_expr()
+CFStringRef bridge_of_paren_expr() {
+ // CHECK-NOT: call i8* @objc_retainAutoreleasedReturnValue(
+ // CHECK-NOT: call void @objc_release(
+ CFStringRef r = (__bridge CFStringRef)(CreateNSString());
+ return r;
+}
diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m
index d34156e..9223d8e 100644
--- a/test/CodeGenObjC/arc.m
+++ b/test/CodeGenObjC/arc.m
@@ -7,30 +7,30 @@
// RUN: %clang_cc1 -fobjc-runtime=macosx-10.7.0 -triple x86_64-apple-darwin11 -Wno-objc-root-class -Wno-incompatible-pointer-types -Wno-arc-unsafe-retained-assign -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=ARC-NATIVE %s
// ARC-ALIEN: declare extern_weak void @objc_storeStrong(i8**, i8*)
-// ARC-ALIEN: declare extern_weak i8* @objc_retain(i8* returned)
-// ARC-ALIEN: declare extern_weak i8* @objc_autoreleaseReturnValue(i8* returned)
+// ARC-ALIEN: declare extern_weak i8* @objc_retain(i8*)
+// ARC-ALIEN: declare extern_weak i8* @objc_autoreleaseReturnValue(i8*)
// ARC-ALIEN: declare i8* @objc_msgSend(i8*, i8*, ...) [[NLB:#[0-9]+]]
// ARC-ALIEN: declare extern_weak void @objc_release(i8*)
-// ARC-ALIEN: declare extern_weak i8* @objc_retainAutoreleasedReturnValue(i8* returned)
+// ARC-ALIEN: declare extern_weak i8* @objc_retainAutoreleasedReturnValue(i8*)
// ARC-ALIEN: declare extern_weak i8* @objc_initWeak(i8**, i8*)
// ARC-ALIEN: declare extern_weak i8* @objc_storeWeak(i8**, i8*)
// ARC-ALIEN: declare extern_weak i8* @objc_loadWeakRetained(i8**)
// ARC-ALIEN: declare extern_weak void @objc_destroyWeak(i8**)
-// declare extern_weak i8* @objc_autorelease(i8*)
-// ARC-ALIEN: declare extern_weak i8* @objc_retainAutorelease(i8* returned)
+// ARC-ALIEN: declare extern_weak i8* @objc_autorelease(i8*)
+// ARC-ALIEN: declare extern_weak i8* @objc_retainAutorelease(i8*)
// ARC-NATIVE: declare void @objc_storeStrong(i8**, i8*)
-// ARC-NATIVE: declare i8* @objc_retain(i8* returned) [[NLB:#[0-9]+]]
-// ARC-NATIVE: declare i8* @objc_autoreleaseReturnValue(i8* returned)
+// ARC-NATIVE: declare i8* @objc_retain(i8*) [[NLB:#[0-9]+]]
+// ARC-NATIVE: declare i8* @objc_autoreleaseReturnValue(i8*)
// ARC-NATIVE: declare i8* @objc_msgSend(i8*, i8*, ...) [[NLB]]
// ARC-NATIVE: declare void @objc_release(i8*) [[NLB]]
-// ARC-NATIVE: declare i8* @objc_retainAutoreleasedReturnValue(i8* returned)
+// ARC-NATIVE: declare i8* @objc_retainAutoreleasedReturnValue(i8*)
// ARC-NATIVE: declare i8* @objc_initWeak(i8**, i8*)
// ARC-NATIVE: declare i8* @objc_storeWeak(i8**, i8*)
// ARC-NATIVE: declare i8* @objc_loadWeakRetained(i8**)
// ARC-NATIVE: declare void @objc_destroyWeak(i8**)
-// declare i8* @objc_autorelease(i8*)
-// ARC-NATIVE: declare i8* @objc_retainAutorelease(i8* returned)
+// ARC-NATIVE: declare i8* @objc_autorelease(i8*)
+// ARC-NATIVE: declare i8* @objc_retainAutorelease(i8*)
// CHECK-LABEL: define void @test0
void test0(id x) {
@@ -1504,9 +1504,7 @@
// CHECK: [[SELF:%.*]] = alloca [[TEST69:%.*]]*, align 8
// CHECK: [[T0:%.*]] = load [[TEST69]]*, [[TEST69]]** [[SELF]], align 8
// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST69]]* [[T0]] to i8*
-// CHECK-NEXT: [[RETAIN:%.*]] = call i8* @objc_retain(i8* [[T1]])
-// CHECK-NEXT: [[AUTORELEASE:%.*]] = tail call i8* @objc_autoreleaseReturnValue(i8* [[RETAIN]])
-// CHECK-NEXT: ret i8* [[AUTORELEASE]]
+// CHECK-NEXT: ret i8* [[T1]]
// rdar://problem/10907547
void test70(id i) {
diff --git a/test/CodeGenObjC/debug-info-block-captured-self.m b/test/CodeGenObjC/debug-info-block-captured-self.m
index e142a0b..302a011 100644
--- a/test/CodeGenObjC/debug-info-block-captured-self.m
+++ b/test/CodeGenObjC/debug-info-block-captured-self.m
@@ -56,7 +56,7 @@
// CHECK-NEXT: [[DBGADDR:%.*]] = alloca [[BLOCK_T:<{.*}>]]*, align 8
// CHECK: store i8* [[BLOCK_DESC:%.*]], i8** %[[MEM1]], align 8
// CHECK: %[[TMP0:.*]] = load i8*, i8** %[[MEM1]]
-// CHECK: call void @llvm.dbg.value(metadata i8* %[[TMP0]], i64 0, metadata ![[BDMD:[0-9]+]], metadata !{{.*}})
+// CHECK: call void @llvm.dbg.value(metadata i8* %[[TMP0]], metadata ![[BDMD:[0-9]+]], metadata !{{.*}})
// CHECK: call void @llvm.dbg.declare(metadata i8* [[BLOCK_DESC]], metadata ![[BDMD:[0-9]+]], metadata !{{.*}})
// CHECK: store [[BLOCK_T]]* {{%.*}}, [[BLOCK_T]]** [[DBGADDR]], align 8
// CHECK: call void @llvm.dbg.declare(metadata [[BLOCK_T]]** [[DBGADDR]], metadata ![[SELF:.*]], metadata !{{.*}})
diff --git a/test/CodeGenObjC/local-static-block.m b/test/CodeGenObjC/local-static-block.m
index 73c670f..67ede63 100644
--- a/test/CodeGenObjC/local-static-block.m
+++ b/test/CodeGenObjC/local-static-block.m
@@ -46,6 +46,17 @@
}
}
+void FUNC2() {
+ static void (^const block1)(int) = ^(int a){
+ if (a--)
+ block1(a);
+ };
+}
+
+// CHECK-LABEL-LP64: define void @FUNC2(
+// CHECK: define internal void @_block_invoke{{.*}}(
+// CHECK: call void %{{.*}}(i8* bitcast ({ i8**, i32, i32, i8*, %struct.__block_descriptor* }* @__block_literal_global{{.*}} to i8*), i32 %{{.*}})
+
void FUNC1()
{
static NSArray *(^ArrayRecurs)(NSArray *addresses, unsigned long level) = ^(NSArray *addresses, unsigned long level) {
diff --git a/test/CodeGenObjC/mangle-blocks.m b/test/CodeGenObjC/mangle-blocks.m
index 4cc3204..73522cd 100644
--- a/test/CodeGenObjC/mangle-blocks.m
+++ b/test/CodeGenObjC/mangle-blocks.m
@@ -18,11 +18,11 @@
@end
// CHECK: @"__func__.__14-[Test mangle]_block_invoke_2" = private unnamed_addr constant [30 x i8] c"-[Test mangle]_block_invoke_2\00", align 1
-// CHECK: @.str = private unnamed_addr constant {{.*}}, align 1
-// CHECK: @.str.1 = private unnamed_addr constant [7 x i8] c"mangle\00", align 1
+// CHECK: @.str{{.*}} = private unnamed_addr constant {{.*}}, align 1
+// CHECK: @.str[[STR1:.*]] = private unnamed_addr constant [7 x i8] c"mangle\00", align 1
// CHECK: define internal void @"__14-[Test mangle]_block_invoke"(i8* %.block_descriptor)
// CHECK: define internal void @"__14-[Test mangle]_block_invoke_2"(i8* %.block_descriptor){{.*}}{
-// CHECK: call void @__assert_rtn(i8* getelementptr inbounds ([30 x i8], [30 x i8]* @"__func__.__14-[Test mangle]_block_invoke_2", i32 0, i32 0), i8* getelementptr inbounds {{.*}}, i32 14, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.1, i32 0, i32 0))
+// CHECK: call void @__assert_rtn(i8* getelementptr inbounds ([30 x i8], [30 x i8]* @"__func__.__14-[Test mangle]_block_invoke_2", i32 0, i32 0), i8* getelementptr inbounds {{.*}}, i32 14, i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str[[STR1]], i32 0, i32 0))
// CHECK: }
diff --git a/test/CodeGenOpenCL/amdgpu-debug-info-variable-expression.cl b/test/CodeGenOpenCL/amdgpu-debug-info-variable-expression.cl
index d3b2869..4d46b40 100644
--- a/test/CodeGenOpenCL/amdgpu-debug-info-variable-expression.cl
+++ b/test/CodeGenOpenCL/amdgpu-debug-info-variable-expression.cl
@@ -1,131 +1,128 @@
// RUN: %clang -cl-std=CL2.0 -emit-llvm -g -O0 -S -target amdgcn-amd-amdhsa -mcpu=fiji -o - %s | FileCheck %s
// RUN: %clang -cl-std=CL2.0 -emit-llvm -g -O0 -S -target amdgcn-amd-amdhsa-opencl -mcpu=fiji -o - %s | FileCheck %s
-// CHECK-DAG: ![[LOCAL:[0-9]+]] = !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef)
-// CHECK-DAG: ![[PRIVATE:[0-9]+]] = !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)
-
// CHECK-DAG: ![[FILEVAR0:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar0", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR0]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR0]], expr: !DIExpression())
global int *FileVar0;
// CHECK-DAG: ![[FILEVAR1:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR1]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR1]], expr: !DIExpression())
constant int *FileVar1;
// CHECK-DAG: ![[FILEVAR2:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR2]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR2]], expr: !DIExpression())
local int *FileVar2;
// CHECK-DAG: ![[FILEVAR3:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar3", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR3]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR3]], expr: !DIExpression())
private int *FileVar3;
// CHECK-DAG: ![[FILEVAR4:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar4", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR4]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR4]], expr: !DIExpression())
int *FileVar4;
// CHECK-DAG: ![[FILEVAR5:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar5", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR5]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR5]], expr: !DIExpression())
global int *global FileVar5;
// CHECK-DAG: ![[FILEVAR6:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar6", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR6]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR6]], expr: !DIExpression())
constant int *global FileVar6;
// CHECK-DAG: ![[FILEVAR7:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar7", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR7]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR7]], expr: !DIExpression())
local int *global FileVar7;
// CHECK-DAG: ![[FILEVAR8:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar8", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR8]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR8]], expr: !DIExpression())
private int *global FileVar8;
// CHECK-DAG: ![[FILEVAR9:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar9", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR9]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR9]], expr: !DIExpression())
int *global FileVar9;
// CHECK-DAG: ![[FILEVAR10:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar10", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR10]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR10]], expr: !DIExpression())
global int *constant FileVar10 = 0;
// CHECK-DAG: ![[FILEVAR11:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar11", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR11]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR11]], expr: !DIExpression())
constant int *constant FileVar11 = 0;
// CHECK-DAG: ![[FILEVAR12:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar12", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR12]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR12]], expr: !DIExpression())
local int *constant FileVar12 = 0;
// CHECK-DAG: ![[FILEVAR13:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar13", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR13]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR13]], expr: !DIExpression())
private int *constant FileVar13 = 0;
// CHECK-DAG: ![[FILEVAR14:[0-9]+]] = distinct !DIGlobalVariable(name: "FileVar14", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: false, isDefinition: true)
-// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR14]])
+// CHECK-DAG: !DIGlobalVariableExpression(var: ![[FILEVAR14]], expr: !DIExpression())
int *constant FileVar14 = 0;
kernel void kernel1(
// CHECK-DAG: ![[KERNELARG0:[0-9]+]] = !DILocalVariable(name: "KernelArg0", arg: {{[0-9]+}}, scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(1)** {{.*}}, metadata ![[KERNELARG0]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(1)** {{.*}}, metadata ![[KERNELARG0]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
global int *KernelArg0,
// CHECK-DAG: ![[KERNELARG1:[0-9]+]] = !DILocalVariable(name: "KernelArg1", arg: {{[0-9]+}}, scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(2)** {{.*}}, metadata ![[KERNELARG1]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(2)** {{.*}}, metadata ![[KERNELARG1]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
constant int *KernelArg1,
// CHECK-DAG: ![[KERNELARG2:[0-9]+]] = !DILocalVariable(name: "KernelArg2", arg: {{[0-9]+}}, scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(3)** {{.*}}, metadata ![[KERNELARG2]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(3)** {{.*}}, metadata ![[KERNELARG2]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
local int *KernelArg2) {
private int *Tmp0;
int *Tmp1;
// CHECK-DAG: ![[FUNCVAR0:[0-9]+]] = !DILocalVariable(name: "FuncVar0", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(1)** {{.*}}, metadata ![[FUNCVAR0]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(1)** {{.*}}, metadata ![[FUNCVAR0]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
global int *FuncVar0 = KernelArg0;
// CHECK-DAG: ![[FUNCVAR1:[0-9]+]] = !DILocalVariable(name: "FuncVar1", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(2)** {{.*}}, metadata ![[FUNCVAR1]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(2)** {{.*}}, metadata ![[FUNCVAR1]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
constant int *FuncVar1 = KernelArg1;
// CHECK-DAG: ![[FUNCVAR2:[0-9]+]] = !DILocalVariable(name: "FuncVar2", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(3)** {{.*}}, metadata ![[FUNCVAR2]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(3)** {{.*}}, metadata ![[FUNCVAR2]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
local int *FuncVar2 = KernelArg2;
// CHECK-DAG: ![[FUNCVAR3:[0-9]+]] = !DILocalVariable(name: "FuncVar3", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32** {{.*}}, metadata ![[FUNCVAR3]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32** {{.*}}, metadata ![[FUNCVAR3]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
private int *FuncVar3 = Tmp0;
// CHECK-DAG: ![[FUNCVAR4:[0-9]+]] = !DILocalVariable(name: "FuncVar4", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(4)** {{.*}}, metadata ![[FUNCVAR4]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(4)** {{.*}}, metadata ![[FUNCVAR4]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
int *FuncVar4 = Tmp1;
// CHECK-DAG: ![[FUNCVAR5:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar5", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR5]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR5]], expr: !DIExpression())
global int *constant FuncVar5 = 0;
// CHECK-DAG: ![[FUNCVAR6:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar6", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR6]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR6]], expr: !DIExpression())
constant int *constant FuncVar6 = 0;
// CHECK-DAG: ![[FUNCVAR7:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar7", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR7]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR7]], expr: !DIExpression())
local int *constant FuncVar7 = 0;
// CHECK-DAG: ![[FUNCVAR8:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar8", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR8]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR8]], expr: !DIExpression())
private int *constant FuncVar8 = 0;
// CHECK-DAG: ![[FUNCVAR9:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar9", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR9]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR9]], expr: !DIExpression())
int *constant FuncVar9 = 0;
// CHECK-DAG: ![[FUNCVAR10:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar10", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR10]], expr: ![[LOCAL]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR10]], expr: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef))
global int *local FuncVar10; FuncVar10 = KernelArg0;
// CHECK-DAG: ![[FUNCVAR11:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar11", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR11]], expr: ![[LOCAL]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR11]], expr: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef))
constant int *local FuncVar11; FuncVar11 = KernelArg1;
// CHECK-DAG: ![[FUNCVAR12:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar12", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR12]], expr: ![[LOCAL]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR12]], expr: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef))
local int *local FuncVar12; FuncVar12 = KernelArg2;
// CHECK-DAG: ![[FUNCVAR13:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar13", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR13]], expr: ![[LOCAL]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR13]], expr: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef))
private int *local FuncVar13; FuncVar13 = Tmp0;
// CHECK-DAG: ![[FUNCVAR14:[0-9]+]] = distinct !DIGlobalVariable(name: "FuncVar14", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}}, isLocal: true, isDefinition: true)
- // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR14]], expr: ![[LOCAL]])
+ // CHECK-DAG: !DIGlobalVariableExpression(var: ![[FUNCVAR14]], expr: !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef))
int *local FuncVar14; FuncVar14 = Tmp1;
// CHECK-DAG: ![[FUNCVAR15:[0-9]+]] = !DILocalVariable(name: "FuncVar15", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(1)** {{.*}}, metadata ![[FUNCVAR15]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(1)** {{.*}}, metadata ![[FUNCVAR15]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
global int *private FuncVar15 = KernelArg0;
// CHECK-DAG: ![[FUNCVAR16:[0-9]+]] = !DILocalVariable(name: "FuncVar16", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(2)** {{.*}}, metadata ![[FUNCVAR16]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(2)** {{.*}}, metadata ![[FUNCVAR16]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
constant int *private FuncVar16 = KernelArg1;
// CHECK-DAG: ![[FUNCVAR17:[0-9]+]] = !DILocalVariable(name: "FuncVar17", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(3)** {{.*}}, metadata ![[FUNCVAR17]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(3)** {{.*}}, metadata ![[FUNCVAR17]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
local int *private FuncVar17 = KernelArg2;
// CHECK-DAG: ![[FUNCVAR18:[0-9]+]] = !DILocalVariable(name: "FuncVar18", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32** {{.*}}, metadata ![[FUNCVAR18]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32** {{.*}}, metadata ![[FUNCVAR18]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
private int *private FuncVar18 = Tmp0;
// CHECK-DAG: ![[FUNCVAR19:[0-9]+]] = !DILocalVariable(name: "FuncVar19", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
- // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(4)** {{.*}}, metadata ![[FUNCVAR19]], metadata ![[PRIVATE]]), !dbg !{{[0-9]+}}
+ // CHECK-DAG: call void @llvm.dbg.declare(metadata i32 addrspace(4)** {{.*}}, metadata ![[FUNCVAR19]], metadata !DIExpression(DW_OP_constu, 1, DW_OP_swap, DW_OP_xderef)), !dbg !{{[0-9]+}}
int *private FuncVar19 = Tmp1;
}
diff --git a/test/CoverageMapping/deferred-region.cpp b/test/CoverageMapping/deferred-region.cpp
new file mode 100644
index 0000000..743b635
--- /dev/null
+++ b/test/CoverageMapping/deferred-region.cpp
@@ -0,0 +1,173 @@
+// RUN: %clang_cc1 -std=c++11 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -fexceptions -fcxx-exceptions -emit-llvm-only -triple %itanium_abi_triple -main-file-name deferred-region.cpp %s | FileCheck %s
+
+#define IF if
+#define STMT(S) S
+
+// CHECK-LABEL: _Z3fooi:
+void foo(int x) {
+ if (x == 0) {
+ return;
+ } // CHECK: Gap,File 0, [[@LINE]]:4 -> [[@LINE+2]]:2 = (#0 - #1)
+
+}
+
+// CHECK-NEXT: _Z4foooi:
+void fooo(int x) {
+ if (x == 0) {
+ return;
+ } // CHECK: Gap,File 0, [[@LINE]]:4 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ if (x == 1) {
+ return;
+ } // CHECK: Gap,File 0, [[@LINE]]:4 -> [[@LINE+2]]:2 = ((#0 - #1) - #2)
+
+}
+
+// CHECK-LABEL: _Z3bazv:
+void baz() { // CHECK: [[@LINE]]:12 -> [[@LINE+2]]:2
+ return; // CHECK-NOT: File
+}
+
+// CHECK-LABEL: _Z3mazv:
+void maz() {
+ if (true)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> 36:3 = (#0 - #1)
+
+ return; // CHECK-NOT: Gap
+}
+
+// CHECK-LABEL: _Z3bari:
+void bar(int x) {
+ IF (x)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ IF (!x)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> [[@LINE+2]]:3 = ((#0 - #1) - #2)
+
+ foo(x);
+}
+
+// CHECK-LABEL: _Z4quuxi:
+// Deferred regions are not emitted within macro expansions.
+void quux(int x) {
+ STMT(
+ if (x == 0)
+ return;)
+
+ // CHECK-NOT: [[@LINE-2]]:{{.*}} -> [[@LINE+2]]
+
+ if (x == 1)
+ STMT(return;)
+
+ // CHECK-NOT: [[@LINE-2]]:{{.*}} -> [[@LINE+3]]
+
+ STMT(
+ if (x == 2)
+ return;
+
+ // CHECK-NOT: [[@LINE-2]]:{{.*}} -> [[@LINE+2]]
+
+ if (x == 3)
+ return;
+ )
+}
+
+// CHECK-LABEL: _Z8weird_ifv:
+void weird_if() {
+ int i = 0;
+
+ if (false)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ if (false)
+ i++;
+
+ if (i + 100 > 0) { // CHECK: [[@LINE]]:20 -> [[@LINE+6]]:4 = #3
+ if (false) // CHECK: [[@LINE+1]]:7 -> [[@LINE+1]]:13 = #4
+ return; // CHECK: Gap,File 0, [[@LINE]]:13 -> [[@LINE+2]]:5 = (#3 - #4)
+ // CHECK: [[@LINE+1]]:5 -> [[@LINE+3]]:4 = (#3 - #4)
+ return; // CHECK: Gap,File 0, [[@LINE]]:5 -> [[@LINE+4]]:3 = ((#0 - #1) - #3)
+
+ }
+
+ if (false)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> [[@LINE+1]]:2
+}
+
+// CHECK-LABEL: _Z8for_loopv:
+void for_loop() {
+ if (false)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ for (int i = 0; i < 10; ++i) {
+ if (i % 2 == 0)
+ continue; // CHECK: Gap,File 0, [[@LINE]]:15 -> [[@LINE+2]]:5 = (#2 - #3)
+
+ if (i % 5 == 0)
+ break; // CHECK: Gap,File 0, [[@LINE]]:12 -> [[@LINE+2]]:5 = ((#2 - #3) - #4)
+
+ int x = i; // CHECK: [[@LINE]]:5 -> [[@LINE+3]]:4 = ((#2 - #3) - #4)
+ return; // CHECK-NOT: [[@LINE]]:11 -> [[@LINE+2]]
+
+ }
+}
+
+struct Error {};
+
+// CHECK-LABEL: _Z10while_loopv:
+void while_loop() {
+ if (false)
+ return; // CHECK: Gap,File 0, [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ int x = 0;
+ while (++x < 10) {
+ if (x == 1)
+ continue; // CHECK: Gap,File 0, [[@LINE]]:15 -> [[@LINE+2]]:5 = (#2 - #3)
+
+ while (++x < 4) {
+ if (x == 3)
+ break; // CHECK: Gap,File 0, [[@LINE]]:14 -> [[@LINE+2]]:7 = (#4 - #5)
+
+ while (++x < 5) {}
+ }
+
+ if (x == 0)
+ throw Error(); // CHECK: Gap,File 0, [[@LINE]]:20 -> [[@LINE+2]]:5 = ((#2 - #3) - #7)
+
+ while (++x < 9) {
+ if (x == 0)
+ break; // CHECK-NOT: [[@LINE]]:14 -> [[@LINE+2]]
+
+ }
+ }
+}
+
+// CHECK-LABEL: _Z5gotosv:
+void gotos() {
+ if (false)
+ goto out; // CHECK: Gap,File 0, [[@LINE]]:13 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ return; // CHECK: [[@LINE]]:3 -> [[@LINE+4]]:2 = (#0 - #1)
+
+out:
+ return; // CHECK: Gap,File 0, [[@LINE]]:8 -> [[@LINE+1]]:2 = 0
+}
+
+int main() {
+ foo(0);
+ foo(1);
+ fooo(0);
+ fooo(1);
+ baz();
+ bar(0);
+ bar(1);
+ quux(0);
+ quux(1);
+ quux(2);
+ quux(3);
+ weird_if();
+ for_loop();
+ while_loop();
+ gotos();
+ return 0;
+}
diff --git a/test/CoverageMapping/label.cpp b/test/CoverageMapping/label.cpp
index 1c5111a6..aec5e4f 100644
--- a/test/CoverageMapping/label.cpp
+++ b/test/CoverageMapping/label.cpp
@@ -14,19 +14,22 @@
int m = 2;
} else
goto x; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = (#1 - #2)
- int k = 3; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = #3
- }
- static int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:2 = ((#0 + #3) - #1)
+ int k = 3; // CHECK-NEXT: File 0, [[@LINE-1]]:13 -> [[@LINE]]:5 = #3
+ } // CHECK-NEXT: File 0, [[@LINE-1]]:5 -> [[@LINE]]:4 = #3
+ static int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:2 = ((#0 + #3) - #1)
++j;
if(j == 1) // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:12 = ((#0 + #3) - #1)
goto x; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #4
+ // CHECK-NEXT: File 0, [[@LINE-1]]:11 -> [[@LINE+1]]:2 = (((#0 + #3) - #1) - #4)
}
// CHECK-NEXT: test1
void test1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> {{[0-9]+}}:2 = #0
if(x == 0) // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:12 = #0
goto a; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #1
- goto b; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:9 = (#0 - #1)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:11 -> [[@LINE+1]]:3 = (#0 - #1)
+ goto b; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:2 = (#0 - #1)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE+4]]:2 = #3
a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #2
b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+2]]:2 = #3
x = x + 1;
@@ -36,9 +39,11 @@
void test2(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> {{[0-9]+}}:2 = #0
if(x == 0) // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:12 = #0
goto a; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #1
- // CHECK-NEXT: File 0, [[@LINE+2]]:8 -> [[@LINE+2]]:25 = (#0 - #1)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:11 -> [[@LINE+3]]:8 = #0
+ // CHECK-NEXT: File 0, [[@LINE+2]]:8 -> [[@LINE+3]]:11 = (#0 - #1)
// CHECK-NEXT: File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:17 = (#0 - #1)
- else if(x == 1) goto b; // CHECK-NEXT: File 0, [[@LINE]]:19 -> [[@LINE]]:25 = #2
+ else if(x == 1) // CHECK-NEXT: File 0, [[@LINE+1]]:5 -> [[@LINE+1]]:11 = #2
+ goto b; // CHECK-NEXT: File 0, [[@LINE]]:11 -> [[@LINE+1]]:1 = #3
a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #3
b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+2]]:2 = #4
x = x + 1;
@@ -47,11 +52,13 @@
// CHECK-NEXT: main
int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> {{[0-9]+}}:2 = #0
int j = 0;
- for(int i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:31 -> [[@LINE+11]]:4 = #1
- a: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+10]]:4 = #2
+ for(int i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:31 -> [[@LINE+13]]:4 = #1
+ a: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+12]]:4 = #2
if(i < 3) // CHECK-NEXT: File 0, [[@LINE]]:8 -> [[@LINE]]:13 = #2
goto e; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = #3
- goto c; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = (#2 - #3)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:13 -> [[@LINE+1]]:5 = (#2 - #3)
+ goto c; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+8]]:4 = (#2 - #3)
+
b: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:4 = #4
j = 2;
c: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:4 = #5
diff --git a/test/CoverageMapping/md.cpp b/test/CoverageMapping/md.cpp
index 20c696c..5f2b2d6 100644
--- a/test/CoverageMapping/md.cpp
+++ b/test/CoverageMapping/md.cpp
@@ -27,6 +27,17 @@
#include "Inputs/md.def"
}
+// CHECK: bar
+// CHECK-NEXT: File 0, [[@LINE+3]]:12 -> [[@LINE+8]]:2 = #0
+bool isVal1();
+bool isVal2();
+bool bar() {
+ #define HANDLE_MD(X) is##X() ||
+ return
+#include "Inputs/md.def"
+ 0;
+}
+
int main(int argc, const char *argv[]) {
foo(MD::Val1);
return 0;
diff --git a/test/CoverageMapping/moremacros.c b/test/CoverageMapping/moremacros.c
index 5666227..630b75d 100644
--- a/test/CoverageMapping/moremacros.c
+++ b/test/CoverageMapping/moremacros.c
@@ -15,7 +15,7 @@
if (!argc) LBRAC
return 0;
// CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #2
- RBRAC
+ RBRAC // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+6]]:3 = (#0 - #2)
// CHECK-NEXT: File 0, [[@LINE+4]]:3 -> [[@LINE+15]]:2 = (#0 - #2)
// CHECK-NEXT: File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:12 = (#0 - #2)
@@ -23,7 +23,7 @@
// CHECK-NEXT: File 0, [[@LINE+1]]:19 -> [[@LINE+3]]:4 = #3
if (!argc) LBRAC
return 0;
- }
+ } // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+5]]:3 = ((#0 - #2) - #3)
// CHECK-NEXT: File 0, [[@LINE+3]]:3 -> [[@LINE+7]]:2 = ((#0 - #2) - #3)
// CHECK-NEXT: File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:12 = ((#0 - #2) - #3)
@@ -31,7 +31,7 @@
if (!argc) {
return 0;
// CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #4
- RBRAC
+ RBRAC // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+1]]:2 = (((#0 - #2) - #3) - #4)
}
// CHECK-NEXT: File 1, 3:15 -> 3:16 = #2
diff --git a/test/CoverageMapping/preprocessor.c b/test/CoverageMapping/preprocessor.c
index bd82b39..cce8b67 100644
--- a/test/CoverageMapping/preprocessor.c
+++ b/test/CoverageMapping/preprocessor.c
@@ -3,36 +3,69 @@
// CHECK: func
void func() { // CHECK: File 0, [[@LINE]]:13 -> [[@LINE+5]]:2 = #0
int i = 0;
-#ifdef MACRO // CHECK-NEXT: Skipped,File 0, [[@LINE]]:2 -> [[@LINE+2]]:2 = 0
+#ifdef MACRO // CHECK-NEXT: Skipped,File 0, [[@LINE]]:1 -> [[@LINE+3]]:1 = 0
int x = i;
#endif
}
-#if 0
- int g = 0;
-
- void bar() { }
-#endif
-
// CHECK: main
int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> {{[0-9]+}}:2 = #0
int i = 0;
-#if 0 // CHECK-NEXT: Skipped,File 0, [[@LINE]]:2 -> [[@LINE+4]]:2 = 0
+# if 0 // CHECK-NEXT: Skipped,File 0, [[@LINE]]:1 -> [[@LINE+5]]:1 = 0
if(i == 0) {
i = 1;
}
-#endif
+# endif // Mark me skipped!
#if 1
// CHECK-NEXT: File 0, [[@LINE+1]]:6 -> [[@LINE+1]]:12 = #0
if(i == 0) { // CHECK-NEXT: File 0, [[@LINE]]:14 -> [[@LINE+2]]:4 = #1
i = 1;
}
-#else // CHECK-NEXT: Skipped,File 0, [[@LINE]]:2 -> [[@LINE+5]]:2 = 0
+#else // CHECK-NEXT: Skipped,File 0, [[@LINE]]:1 -> [[@LINE+6]]:1 = 0
if(i == 1) {
i = 0;
}
}
#endif
+
+ // CHECK-NEXT: Skipped,File 0, [[@LINE+1]]:1 -> [[@LINE+5]]:1
+#\
+ if 0
+#\
+ endif // also skipped
+
+#if 1
+ // CHECK-NEXT: Skipped,File 0, [[@LINE+1]]:1 -> [[@LINE+4]]:1
+#\
+ elif 0
+#endif
+
+#if 1
+ // CHECK-NEXT: Skipped,File 0, [[@LINE+1]]:1 -> [[@LINE+4]]:1
+#\
+ else
+#endif
+
+ // CHECK-NEXT: Skipped,File 0, [[@LINE+1]]:1 -> [[@LINE+5]]:1
+#\
+ ifdef NOT_DEFINED
+#\
+ endif
+
+ // CHECK-NEXT: Skipped,File 0, [[@LINE+1]]:1 -> [[@LINE+5]]:1
+#\
+ ifndef __FILE__
+#\
+ endif
+
+ // CHECK-NEXT: Skipped,File 0, [[@LINE+1]]:1 -> [[@LINE+7]]:1
+#\
+ ifdef NOT_DEFINED
+#\
+ \
+ \
+ endif // also skipped
+
return 0;
}
diff --git a/test/CoverageMapping/return.c b/test/CoverageMapping/return.c
index 1b190b0..3cc271b 100644
--- a/test/CoverageMapping/return.c
+++ b/test/CoverageMapping/return.c
@@ -13,7 +13,7 @@
for(int i = 0; i < 10; ++i) { // CHECK-NEXT: File 0, [[@LINE]]:31 -> {{[0-9]+}}:4 = #1
// CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:13 = #1
if(i > 2) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+2]]:6 = #2
- return;
+ return; // CHECK-NEXT: File 0, [[@LINE+1]]:6 -> [[@LINE+3]]:5 = (#1 - #2)
} // CHECK-NEXT: File 0, [[@LINE+2]]:5 -> {{[0-9]+}}:4 = (#1 - #2)
// CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:14 = (#1 - #2)
if(i == 3) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE+2]]:6 = #3
diff --git a/test/CoverageMapping/switch.cpp b/test/CoverageMapping/switch.cpp
index 312f26c..d347e66 100644
--- a/test/CoverageMapping/switch.cpp
+++ b/test/CoverageMapping/switch.cpp
@@ -3,10 +3,10 @@
// CHECK: foo
void foo(int i) { // CHECK-NEXT: File 0, [[@LINE]]:17 -> [[@LINE+8]]:2 = #0
switch(i) {
- case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #2
+ case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = #2
return;
case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #3
- break;
+ break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+2]]:3 = #1
}
int x = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #1
}
@@ -48,23 +48,23 @@
int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+35]]:2 = #0
int i = 0;
switch(i) {
- case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+7]]:10 = #2
+ case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #2
i = 1;
break;
case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #3
i = 2;
break;
default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #4
- break;
+ break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+2]]:3 = #1
}
switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+23]]:2 = #1
- case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:10 = #6
+ case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #6
i = 1;
break;
case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #7
i = 2;
default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = (#7 + #8)
- break;
+ break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+3]]:3 = #5
}
switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+13]]:2 = #5
@@ -81,3 +81,32 @@
baz();
return 0;
}
+
+// FIXME: End location for "case 1" shouldn't point at the end of the switch.
+ // CHECK: fallthrough
+int fallthrough(int i) { // CHECK-NEXT: File 0, [[@LINE]]:24 -> [[@LINE+12]]:2 = #0
+ switch(i) {
+ case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+8]]:10 = #2
+ i = 23;
+ case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = (#2 + #3)
+ i = 11;
+ break;
+ case 3: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #4
+ case 4: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = (#4 + #5)
+ i = 99;
+ break;
+ }
+}
+
+void abort(void) __attribute((noreturn));
+ // CHECK: noret
+int noret(int x) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+9]]:2
+ switch (x) {
+ default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:12
+ abort();
+ case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:13
+ return 5;
+ case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:14
+ return 10;
+ }
+}
diff --git a/test/CoverageMapping/switchmacro.c b/test/CoverageMapping/switchmacro.c
index 55f93d8..8fb1b83 100644
--- a/test/CoverageMapping/switchmacro.c
+++ b/test/CoverageMapping/switchmacro.c
@@ -8,6 +8,7 @@
default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> {{[0-9]+}}:11 = #2
if (i == 1) // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:15 = #2
return 0; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:15 = #3
+ // CHECK-NEXT: File 0, [[@LINE-1]]:15 -> [[@LINE+3]]:5 = (#2 - #3)
// CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:8 = (#2 - #3) (Expanded file = 1)
// CHECK-NEXT: File 0, [[@LINE+1]]:8 -> {{[0-9]+}}:11 = (#2 - #3)
FOO(1);
diff --git a/test/CoverageMapping/trycatch.cpp b/test/CoverageMapping/trycatch.cpp
index 01d8fb9..37b35d3 100644
--- a/test/CoverageMapping/trycatch.cpp
+++ b/test/CoverageMapping/trycatch.cpp
@@ -18,7 +18,7 @@
// CHECK-NEXT: File 0, [[@LINE+1]]:10 -> [[@LINE+2]]:27 = (#0 - #1)
} else if(i == 8) // CHECK-NEXT: File 0, [[@LINE]]:13 -> [[@LINE]]:19 = (#0 - #1)
throw ImportantError(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:27 = #2
-}
+} // CHECK-NEXT: File 0, [[@LINE-1]]:27 -> [[@LINE]]:2 = ((#0 - #1) - #2)
// CHECK-NEXT: main
int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+13]]:2 = #0
diff --git a/test/Driver/Inputs/resource_dir/ubsan_blacklist.txt b/test/Driver/Inputs/resource_dir/ubsan_blacklist.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/Driver/Inputs/resource_dir/ubsan_blacklist.txt
diff --git a/test/Driver/clang-translation.c b/test/Driver/clang-translation.c
index 3b30f7a..7021f13 100644
--- a/test/Driver/clang-translation.c
+++ b/test/Driver/clang-translation.c
@@ -73,6 +73,22 @@
// RUN: FileCheck -check-prefix=ARM64-APPLE %s
// ARM64-APPLE: -munwind-table
+// RUN: %clang -target arm64-apple-ios10 -### -ffreestanding -S %s -arch arm64 2>&1 | \
+// RUN: FileCheck -check-prefix=ARM64-FREESTANDING-APPLE %s
+//
+// RUN: %clang -target arm64-apple-ios10 -### -fno-unwind-tables -ffreestanding -S %s -arch arm64 2>&1 | \
+// RUN: FileCheck -check-prefix=ARM64-FREESTANDING-APPLE %s
+//
+// ARM64-FREESTANDING-APPLE-NOT: -munwind-table
+
+// RUN: %clang -target arm64-apple-ios10 -### -funwind-tables -S %s -arch arm64 2>&1 | \
+// RUN: FileCheck -check-prefix=ARM64-EXPLICIT-UWTABLE-APPLE %s
+//
+// RUN: %clang -target arm64-apple-ios10 -### -ffreestanding -funwind-tables -S %s -arch arm64 2>&1 | \
+// RUN: FileCheck -check-prefix=ARM64-EXPLICIT-UWTABLE-APPLE %s
+//
+// ARM64-EXPLICIT-UWTABLE-APPLE: -munwind-table
+
// RUN: %clang -target arm64-apple-ios10 -fno-exceptions -### -S %s -arch arm64 2>&1 | \
// RUN: FileCheck -check-prefix=ARM64-APPLE-EXCEP %s
// ARM64-APPLE-EXCEP-NOT: -munwind-table
diff --git a/test/Driver/darwin-ld.c b/test/Driver/darwin-ld.c
index 36e7ee5..4358d38 100644
--- a/test/Driver/darwin-ld.c
+++ b/test/Driver/darwin-ld.c
@@ -341,3 +341,13 @@
// RUN: %clang -target x86_64-apple-darwin12 %t.o -fsave-optimization-record -fprofile-instr-use=blah -### -o foo/bar.out 2> %t.log
// RUN: FileCheck -check-prefix=PASS_REMARKS_WITH_HOTNESS %s < %t.log
// PASS_REMARKS_WITH_HOTNESS: "-mllvm" "-lto-pass-remarks-output" "-mllvm" "foo/bar.out.opt.yaml" "-mllvm" "-lto-pass-remarks-with-hotness"
+
+// RUN: %clang -target x86_64-apple-ios6.0 -miphoneos-version-min=6.0 -fprofile-instr-generate -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log
+// RUN: %clang -target x86_64-apple-darwin12 -fprofile-instr-generate -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log
+// RUN: %clang -target i386-apple-darwin9 -fprofile-instr-generate -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log
+// RUN: %clang -target arm64-apple-ios5.0 -miphoneos-version-min=5.0 -fprofile-instr-generate -### %t.o 2> %t.log
+// RUN: FileCheck -check-prefix=LINK_PROFILE_FIRST %s < %t.log
+// LINK_PROFILE_FIRST: {{ld(.exe)?"}} "{{[^"]+}}libclang_rt.profile_{{[a-z]+}}.a"
diff --git a/test/Driver/flags.c b/test/Driver/flags.c
index 656ac41..ff60caf 100644
--- a/test/Driver/flags.c
+++ b/test/Driver/flags.c
@@ -24,6 +24,3 @@
// RUN: %clang -target armv7-apple-darwin10 -### -S -mno-implicit-float -mimplicit-float %s 2>&1 | FileCheck -check-prefix=TEST8 %s
// TEST8-NOT: "-no-implicit-float"
-
-// RUN: %clang -target x86_64-linux-gnu -### -c -fclang-abi-compat=3.2 %s 2>&1 | FileCheck -check-prefix=TEST9 %s
-// TEST9: "-fclang-abi-compat=3.2"
diff --git a/test/Driver/fsanitize-blacklist.c b/test/Driver/fsanitize-blacklist.c
index adf776f..9268bb2 100644
--- a/test/Driver/fsanitize-blacklist.c
+++ b/test/Driver/fsanitize-blacklist.c
@@ -23,6 +23,12 @@
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-BLACKLIST --implicit-check-not=fdepfile-entry
// CHECK-DEFAULT-BLACKLIST: -fsanitize-blacklist={{.*}}asan_blacklist.txt
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=integer -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-UBSAN-BLACKLIST --implicit-check-not=fdepfile-entry
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=nullability -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-UBSAN-BLACKLIST --implicit-check-not=fdepfile-entry
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-UBSAN-BLACKLIST --implicit-check-not=fdepfile-entry
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=alignment -resource-dir=%S/Inputs/resource_dir %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-DEFAULT-UBSAN-BLACKLIST --implicit-check-not=fdepfile-entry
+// CHECK-DEFAULT-UBSAN-BLACKLIST: -fsanitize-blacklist={{.*}}ubsan_blacklist.txt
+
// Ignore -fsanitize-blacklist flag if there is no -fsanitize flag.
// RUN: %clang -target x86_64-linux-gnu -fsanitize-blacklist=%t.good %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SANITIZE --check-prefix=DELIMITERS
// CHECK-NO-SANITIZE-NOT: -fsanitize-blacklist
diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c
index 0752ef6..5f749d0 100644
--- a/test/Driver/fsanitize.c
+++ b/test/Driver/fsanitize.c
@@ -3,18 +3,18 @@
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
// RUN: %clang -target x86_64-linux-gnu -fsanitize-undefined-trap-on-error -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
-// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
-// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
-// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){19}"}}
+// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
-// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
+// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){20}"}}
// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN
-// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
+// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
// RUN: %clang -target i386-unknown-openbsd -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-OPENBSD
-// CHECK-UNDEFINED-OPENBSD: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
+// CHECK-UNDEFINED-OPENBSD: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
// RUN: %clang -target i386-pc-win32 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32
// RUN: %clang -target i386-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32 --check-prefix=CHECK-UNDEFINED-WIN-CXX
@@ -23,7 +23,7 @@
// CHECK-UNDEFINED-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone-i386.lib"
// CHECK-UNDEFINED-WIN64: "--dependent-lib={{[^"]*}}ubsan_standalone-x86_64.lib"
// CHECK-UNDEFINED-WIN-CXX: "--dependent-lib={{[^"]*}}ubsan_standalone_cxx{{[^"]*}}.lib"
-// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
+// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
// RUN: %clang -target i386-pc-win32 -fsanitize-coverage=bb %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-COVERAGE-WIN32
// CHECK-COVERAGE-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone-i386.lib"
@@ -42,7 +42,7 @@
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address,undefined -fno-sanitize=all -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FNO-SANITIZE-ALL
// CHECK-FNO-SANITIZE-ALL: "-fsanitize=thread"
-// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,builtin,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){15}"}}
// RUN: %clang -target x86_64-linux-gnu -fsanitize=shift -fno-sanitize=shift-base %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSANITIZE-SHIFT-PARTIAL
@@ -217,7 +217,7 @@
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=thread -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover=all -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN
-// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
+// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
// CHECK-NO-RECOVER-UBSAN-NOT: sanitize-recover
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=object-size,shift-base -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-RECOVER
@@ -354,10 +354,8 @@
// CHECK-TSAN-ATOMICS-BOTH-OFF: -cc1{{.*}}tsan-instrument-atomics=0
// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSAN-DARWIN
-// CHECK-FSAN-DARWIN: unsupported option '-fsanitize=function' for target 'x86_64-apple-darwin10'
-
-// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=function -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSAN-UBSAN-DARWIN
-// CHECK-FSAN-UBSAN-DARWIN: unsupported option '-fsanitize=function' for target 'x86_64-apple-darwin10'
+// RUN: %clang -target i386-apple-darwin10 -fsanitize=function %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSAN-DARWIN
+// CHECK-FSAN-DARWIN: -cc1{{.*}}"-fsanitize=function" "-fsanitize-recover=function"
// RUN: %clang -target x86_64-apple-darwin10 -mmacosx-version-min=10.8 -fsanitize=vptr %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-DARWIN-OLD
// CHECK-VPTR-DARWIN-OLD: unsupported option '-fsanitize=vptr' for target 'x86_64-apple-darwin10'
@@ -535,3 +533,32 @@
// Make sure there are no *.{o,bc} or -l passed before the ASan library.
// CHECK-ASAN-PS4-NOT: {{(\.(o|bc)"? |-l).*-lSceDbgAddressSanitizer_stub_weak}}
// CHECK-ASAN-PS4: -lSceDbgAddressSanitizer_stub_weak
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-MINIMAL
+// CHECK-ASAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=address'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TSAN-MINIMAL
+// CHECK-TSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=thread'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL
+// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){19}"}}
+// CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=vptr -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-VPTR-MINIMAL
+// CHECK-UBSAN-VPTR-MINIMAL: error: invalid argument '-fsanitize=vptr' not allowed with '-fsanitize-minimal-runtime'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-minimal-runtime -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-UBSAN-MINIMAL
+// CHECK-ASAN-UBSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=address'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -flto -fvisibility=hidden -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-MINIMAL
+// CHECK-CFI-MINIMAL: "-fsanitize=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-MINIMAL: "-fsanitize-trap=cfi-derived-cast,cfi-icall,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-MINIMAL: "-fsanitize-minimal-runtime"
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -fno-sanitize-trap=cfi-icall -flto -fvisibility=hidden -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOTRAP-MINIMAL
+// CHECK-CFI-NOTRAP-MINIMAL: error: invalid argument 'fsanitize-minimal-runtime' only allowed with 'fsanitize-trap=cfi'
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=cfi -fno-sanitize-trap=cfi-icall -fno-sanitize=cfi-icall -flto -fvisibility=hidden -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-CFI-NOICALL-MINIMAL
+// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize-trap=cfi-derived-cast,cfi-unrelated-cast,cfi-nvcall,cfi-vcall"
+// CHECK-CFI-NOICALL-MINIMAL: "-fsanitize-minimal-runtime"
diff --git a/test/Driver/fuzzer.c b/test/Driver/fuzzer.c
index 989b3b9..083f950 100644
--- a/test/Driver/fuzzer.c
+++ b/test/Driver/fuzzer.c
@@ -2,7 +2,7 @@
// RUN: %clang -fsanitize=fuzzer %s -target x86_64-apple-darwin14 -### 2>&1 | FileCheck --check-prefixes=CHECK-FUZZER-LIB,CHECK-COVERAGE-FLAGS %s
//
-// CHECK-FUZZER-LIB: libLLVMFuzzer.a
+// CHECK-FUZZER-LIB: libclang_rt.fuzzer
// CHECK-COVERAGE: -fsanitize-coverage-trace-pc-guard
// CHECK-COVERAGE-SAME: -fsanitize-coverage-indirect-calls
// CHECK-COVERAGE-SAME: -fsanitize-coverage-trace-cmp
@@ -18,7 +18,12 @@
// Check that we don't link in libFuzzer.a when producing a shared object.
// RUN: %clang -fsanitize=fuzzer %s -shared -o %t.so -### 2>&1 | FileCheck --check-prefixes=CHECK-NOLIB-SO %s
-// CHECK-NOLIB-SO-NOT: libLLVMFuzzer.a
+// CHECK-NOLIB-SO-NOT: libclang_rt.libfuzzer
+
+// Check that we don't link in libFuzzer when compiling with -fsanitize=fuzzer-no-link.
+// RUN: %clang -fsanitize=fuzzer-no-link %s -target x86_64-apple-darwin14 -### 2>&1 | FileCheck --check-prefixes=CHECK-NOLIB,CHECK-COV %s
+// CHECK-NOLIB-NOT: libclang_rt.libfuzzer
+// CHECK-COV: -fsanitize-coverage-trace-pc-guard
// RUN: %clang -fsanitize=fuzzer -fsanitize-coverage=trace-pc %s -### 2>&1 | FileCheck --check-prefixes=CHECK-MSG %s
// CHECK-MSG-NOT: argument unused during compilation
diff --git a/test/Driver/nostdlibxx.cpp b/test/Driver/nostdlibxx.cpp
new file mode 100644
index 0000000..02bd62d
--- /dev/null
+++ b/test/Driver/nostdlibxx.cpp
@@ -0,0 +1,8 @@
+// RUN: %clangxx -target i686-pc-linux-gnu -### -nostdlib++ %s 2> %t
+// RUN: FileCheck < %t %s
+
+// We should still have -lm and the C standard libraries, but not -lstdc++.
+
+// CHECK-NOT: -lstdc++
+// CHECK-NOT: -lc++
+// CHECK: -lm
diff --git a/test/Driver/sanitizer-ld.c b/test/Driver/sanitizer-ld.c
index efdd124..2de6957 100644
--- a/test/Driver/sanitizer-ld.c
+++ b/test/Driver/sanitizer-ld.c
@@ -234,6 +234,21 @@
// CHECK-UBSAN-LINUX-CXX-NOT: libclang_rt.asan
// CHECK-UBSAN-LINUX-CXX: "-lpthread"
+// RUN: %clang -fsanitize=undefined -fsanitize-minimal-runtime %s -### -o %t.o 2>&1 \
+// RUN: -target i386-unknown-linux -fuse-ld=ld \
+// RUN: --sysroot=%S/Inputs/basic_linux_tree \
+// RUN: | FileCheck --check-prefix=CHECK-UBSAN-MINIMAL-LINUX %s
+// CHECK-UBSAN-MINIMAL-LINUX: "{{.*}}ld{{(.exe)?}}"
+// CHECK-UBSAN-MINIMAL-LINUX: "-whole-archive" "{{.*}}libclang_rt.ubsan_minimal-i386.a" "-no-whole-archive"
+// CHECK-UBSAN-MINIMAL-LINUX: "-lpthread"
+
+// RUN: %clang -fsanitize=undefined -fsanitize-minimal-runtime %s -### -o %t.o 2>&1 \
+// RUN: -target x86_64-apple-darwin -fuse-ld=ld \
+// RUN: --sysroot=%S/Inputs/basic_linux_tree \
+// RUN: | FileCheck --check-prefix=CHECK-UBSAN-MINIMAL-DARWIN %s
+// CHECK-UBSAN-MINIMAL-DARWIN: "{{.*}}ld{{(.exe)?}}"
+// CHECK-UBSAN-MINIMAL-DARWIN: "{{.*}}libclang_rt.ubsan_minimal_osx_dynamic.dylib"
+
// RUN: %clang -fsanitize=address,undefined %s -### -o %t.o 2>&1 \
// RUN: -target i386-unknown-linux -fuse-ld=ld \
// RUN: --sysroot=%S/Inputs/basic_linux_tree \
diff --git a/test/Driver/stack-protector.c b/test/Driver/stack-protector.c
index 6769b65..a3e40b5 100644
--- a/test/Driver/stack-protector.c
+++ b/test/Driver/stack-protector.c
@@ -36,27 +36,20 @@
// Test default stack protector values for Darwin platforms
// RUN: %clang -target armv7k-apple-watchos2.0 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_WATCHOS
+// RUN: %clang -ffreestanding -target armv7k-apple-watchos2.0 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_WATCHOS
// SSP_WATCHOS: "-stack-protector" "1"
// RUN: %clang -target arm64-apple-ios8.0.0 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_IOS
+// RUN: %clang -ffreestanding -target arm64-apple-ios8.0.0 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_IOS
// SSP_IOS: "-stack-protector" "1"
// RUN: %clang -target x86_64-apple-darwin10 -mmacosx-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX
+// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacosx-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX
// SSP_MACOSX: "-stack-protector" "1"
// RUN: %clang -target x86_64-apple-darwin10 -mmacosx-version-min=10.5 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_10_5
+// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacosx-version-min=10.5 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_10_5
// SSP_MACOSX_10_5: "-stack-protector" "1"
// RUN: %clang -target x86_64-apple-darwin10 -mmacosx-version-min=10.5 -mkernel -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_KERNEL
// SSP_MACOSX_KERNEL-NOT: "-stack-protector"
// RUN: %clang -target x86_64-apple-darwin10 -mmacosx-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_10_6_KERNEL
+// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacosx-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_MACOSX_10_6_KERNEL
// SSP_MACOSX_10_6_KERNEL: "-stack-protector" "1"
-// Test default stack protector values for Darwin platforms with -ffreestanding
-
-// RUN: %clang -ffreestanding -target armv7k-apple-watchos2.0 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_FREE_WATCHOS
-// SSP_FREE_WATCHOS-NOT: "-stack-protector"
-// RUN: %clang -ffreestanding -target arm64-apple-ios8.0.0 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_FREE_IOS
-// SSP_FREE_IOS-NOT: "-stack-protector"
-// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacosx-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_FREE_MACOSX
-// SSP_FREE_MACOSX-NOT: "-stack-protector"
-// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacosx-version-min=10.5 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_FREE_MACOSX_10_5
-// SSP_FREE_MACOSX_10_5-NOT: "-stack-protector"
-// RUN: %clang -ffreestanding -target x86_64-apple-darwin10 -mmacosx-version-min=10.6 -### %s 2>&1 | FileCheck %s -check-prefix=SSP_FREE_MACOSX_10_6_KERNEL
-// SSP_FREE_MACOSX_10_6_KERNEL-NOT: "-stack-protector"
diff --git a/test/FixIt/fixit-availability.c b/test/FixIt/fixit-availability.c
index 038dee0..4b3c8a4 100644
--- a/test/FixIt/fixit-availability.c
+++ b/test/FixIt/fixit-availability.c
@@ -8,3 +8,11 @@
// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"if (__builtin_available(macOS 10.12, *)) {\n "
// CHECK-NEXT: fix-it:{{.*}}:{[[@LINE-2]]:14-[[@LINE-2]]:14}:"\n } else {\n // Fallback on earlier versions\n }"
}
+
+__attribute__((availability(macos, introduced=10.12)))
+struct New { };
+
+struct NoFixit {
+ struct New field;
+};
+// CHECK-NOT: API_AVAILABLE
diff --git a/test/FixIt/fixit-availability.mm b/test/FixIt/fixit-availability.mm
index d044a73..a566082 100644
--- a/test/FixIt/fixit-availability.mm
+++ b/test/FixIt/fixit-availability.mm
@@ -108,4 +108,43 @@
// CHECK-NEXT: fix-it:{{.*}}:{[[@LINE-2]]:24-[[@LINE-2]]:24}:"\n } else {\n // Fallback on earlier versions\n }"
anotherFunction(y);
anotherFunction(x);
+
+ if (@available(macOS 10.1, *)) {
+ int z = function();
+ (void)z;
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:5-[[@LINE-2]]:5}:"if (@available(macOS 10.12, *)) {\n "
+// CHECK-NEXT: fix-it:{{.*}}:{[[@LINE-2]]:13-[[@LINE-2]]:13}:"\n } else {\n // Fallback on earlier versions\n }"
+ anotherFunction(x);
+ }
}
+
+#define API_AVAILABLE(X) __attribute__((availability(macos, introduced=10.12))) // dummy macro
+
+API_AVAILABLE(macos(10.12))
+@interface NewClass
+@end
+
+@interface OldButOfferFixit
+@property(copy) NewClass *prop;
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"API_AVAILABLE(macos(10.12))\n"
+
+- (NewClass *)fixitMe;
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:22-[[@LINE-1]]:22}:" API_AVAILABLE(macos(10.12))"
+@end
+
+void oldButOfferFixitFn(NewClass *) {
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"API_AVAILABLE(macos(10.12))\n"
+}
+
+template<typename T>
+struct OldButOfferFixitTag {
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:7-[[@LINE-1]]:7}:" API_AVAILABLE(macos(10.12))"
+ NewClass *x;
+};
+
+// Avoid a fixit for declarations that already have an attribute:
+__attribute__((availability(macos, introduced=10.11)))
+@interface WithoutFixit
+@property(copy) NewClass *prop;
+// CHECK-NOT: API_AVAILABLE
+@end
diff --git a/test/FixIt/fixit-fill-in-missing-switch-cases.cpp b/test/FixIt/fixit-fill-in-missing-switch-cases.cpp
new file mode 100644
index 0000000..20b948f
--- /dev/null
+++ b/test/FixIt/fixit-fill-in-missing-switch-cases.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s
+
+enum Color {
+ Black,
+ Blue,
+ White,
+ Gold
+};
+
+void fillInCases(Color c) {
+ switch (c) {
+ case Black:
+ break;
+ }
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n"
+ switch (c) {
+ }
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n"
+}
+
+enum class NoDenseMap: long long {
+ Baddie = 0x7fffffffffffffffLL,
+ BigBaddie = -0x7fffffffffffffffLL-1
+};
+
+void fillInAllCases(NoDenseMap v) {
+ switch (v) {
+ }
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:3-[[@LINE-1]]:3}:"case NoDenseMap::Baddie:\n<#code#>\nbreak;\ncase NoDenseMap::BigBaddie:\n<#code#>\nbreak;\n"
+}
+
diff --git a/test/FixIt/fixit-fill-in-protocol-requirements.m b/test/FixIt/fixit-fill-in-protocol-requirements.m
new file mode 100644
index 0000000..642a70c
--- /dev/null
+++ b/test/FixIt/fixit-fill-in-protocol-requirements.m
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple=x86_64-apple-macos10.10 -verify -Wno-objc-root-class -serialize-diagnostic-file /dev/null %s
+// RUN: %clang_cc1 -triple=x86_64-apple-macos10.10 -fdiagnostics-parseable-fixits -serialize-diagnostic-file /dev/null %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -triple=x86_64-apple-macos10.12 -fdiagnostics-parseable-fixits -serialize-diagnostic-file /dev/null %s 2>&1 | FileCheck --check-prefix=AVAILABLE %s
+
+@protocol P1
+
+- (void)p2Method;
+
+@end
+
+@protocol P2 <P1>
+
+- (void)p1Method;
+
+@end
+
+@interface I <P2>
+
+@end
+
+@implementation I // expected-warning {{warning: class 'I' does not conform to protocols 'P2' and 'P1'}} expected-note {{add stubs for missing protocol requirements}}
+
+@end
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"- (void)p2Method { \n <#code#>\n}\n\n- (void)p1Method { \n <#code#>\n}\n\n"
+
+@protocol P3
+
++ (void)p3ClassMethod;
+
+@end
+
+@interface I (Category) <P3> // expected-warning {{warning: category 'Category' does not conform to protocols 'P3'}} expected-note {{add stubs for missing protocol requirements}}
+
+@end
+
+@implementation I (Category)
+
+@end
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"+ (void)p3ClassMethod { \n <#code#>\n}\n\n"
+
+@protocol P4
+
+- (void)anotherMethod;
+
+@end
+
+@interface ThreeProtocols <P2, P1, P3> // expected-warning {{class 'ThreeProtocols' does not conform to protocols 'P2', 'P1' and 'P3'}} expected-note {{add stubs for missing protocol requirements}}
+@end
+@implementation ThreeProtocols
+@end
+
+@interface FourProtocols <P2, P3, P4> // expected-warning {{class 'ThreeProtocols' does not conform to protocols 'P2', 'P1', 'P3', ...}} expected-note {{add stubs for missing protocol requirements}}
+@end
+@implementation FourProtocols
+@end
+
+// Unavailable methods
+@protocol TakeAvailabilityIntoAccount
+
+- (void)unavailableMethod __attribute__((availability(macos,unavailable)));
++ (void)notYetAvailableMethod __attribute__((availability(macos,introduced=10.11)));
+- (void)availableMethod;
+- (void)deprecatedMethod __attribute__((availability(macos,introduced=10.0, deprecated=10.6)));
+
+@end
+
+@interface ImplementsAllAvailable <TakeAvailabilityIntoAccount>
+@end
+
+@implementation ImplementsAllAvailable // No warning!
+
+- (void)availableMethod { }
+- (void)deprecatedMethod { }
+
+@end
+
+@interface FixitJustAvailable <TakeAvailabilityIntoAccount>
+@end
+
+@implementation FixitJustAvailable // expected-warning {{class 'FixitJustAvailable' does not conform to protocol 'TakeAvailabilityIntoAccount'}} expected-note {{add stubs for missing protocol requirements}}
+
+@end
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:1-[[@LINE-1]]:1}:"- (void)availableMethod { \n <#code#>\n}\n\n- (void)deprecatedMethod { \n <#code#>\n}\n\n"
+// AVAILABLE: fix-it:{{.*}}:{[[@LINE-2]]:1-[[@LINE-2]]:1}:"- (void)availableMethod { \n <#code#>\n}\n\n- (void)deprecatedMethod { \n <#code#>\n}\n\n+ (void)notYetAvailableMethod { \n <#code#>\n}\n\n"
diff --git a/test/FixIt/fixit-fill-in-switch-crash.cpp b/test/FixIt/fixit-fill-in-switch-crash.cpp
new file mode 100644
index 0000000..635fe81
--- /dev/null
+++ b/test/FixIt/fixit-fill-in-switch-crash.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -verify -std=c++11 %s
+// RUN: not %clang_cc1 -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s
+
+enum Color {
+ Black, Red
+};
+
+void dontCrashOnEmptySubStmt(Color c) { // expected-note {{to match this '{'}}
+ switch (c) { // expected-note {{to match this '{'}} \
+ // expected-warning {{enumeration value 'Red' not handled in switch}} \
+ // expected-note {{add missing switch cases}}
+ case Black: // CHECK: fix-it:{{.*}}:{[[@LINE+3]]:10-[[@LINE+3]]:10}:"case Red:\n<#code#>\nbreak;\n"
+ // expected-error@+2 {{expected expression}}
+ // expected-error@+1 2 {{expected '}'}}
+ case //
diff --git a/test/Frontend/clang-abi-compat.cpp b/test/Frontend/clang-abi-compat.cpp
deleted file mode 100644
index 7ee9464..0000000
--- a/test/Frontend/clang-abi-compat.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// RUN: not %clang_cc1 -fclang-abi-compat=banana %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=2.9 %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=8 %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=3.10 %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=4.1 %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=04 %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=4. %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// RUN: not %clang_cc1 -fclang-abi-compat=4.00 %s -fsyntax-only 2>&1 | FileCheck --check-prefix=INVALID %s
-// INVALID: error: invalid value '{{.*}}' in '-fclang-abi-compat={{.*}}'
-//
-// RUN: %clang_cc1 -fclang-abi-compat=3.0 %s -fsyntax-only
-// RUN: %clang_cc1 -fclang-abi-compat=3.9 %s -fsyntax-only
-// RUN: %clang_cc1 -fclang-abi-compat=4 %s -fsyntax-only
-// RUN: %clang_cc1 -fclang-abi-compat=4.0 %s -fsyntax-only
-// RUN: %clang_cc1 -fclang-abi-compat=latest %s -fsyntax-only
diff --git a/test/Index/Core/index-dependent-source.cpp b/test/Index/Core/index-dependent-source.cpp
index 59c6286..2e11133 100644
--- a/test/Index/Core/index-dependent-source.cpp
+++ b/test/Index/Core/index-dependent-source.cpp
@@ -158,3 +158,69 @@
// Shouldn't crash!
t.lookup;
}
+
+template <typename T>
+struct UsingA {
+// CHECK: [[@LINE+1]]:15 | type-alias/C | Type | c:index-dependent-source.cpp@ST>1#T@UsingA@T@Type | <no-cgname> | Def,RelChild | rel: 1
+ typedef int Type;
+// CHECK: [[@LINE+1]]:15 | static-method/C++ | func | c:@ST>1#T@UsingA@F@func#S | <no-cgname> | Decl,RelChild | rel: 1
+ static void func();
+// CHECK: [[@LINE+1]]:8 | instance-method/C++ | operator() | c:@ST>1#T@UsingA@F@operator()#I# | <no-cgname> | Decl,RelChild | rel: 1
+ void operator()(int);
+// CHECK: [[@LINE+1]]:8 | instance-method/C++ | operator+ | c:@ST>1#T@UsingA@F@operator+#&1>@ST>1#T@UsingA1t0.0# | <no-cgname> | Decl,RelChild | rel: 1
+ void operator+(const UsingA &);
+};
+
+template <typename T>
+struct OtherUsing {};
+
+template <typename T>
+struct UsingB : public UsingA<T> {
+// CHECK: [[@LINE+2]]:40 | type-alias/C | TypeB | c:index-dependent-source.cpp@ST>1#T@UsingB@T@TypeB | <no-cgname> | Def,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:20 | struct(Gen)/C++ | OtherUsing | c:@ST>1#T@OtherUsing | <no-cgname> | Ref,RelCont | rel: 1
+ typedef typename OtherUsing<T>::Type TypeB;
+// CHECK: [[@LINE+2]]:29 | using/using-typename(Gen)/C++ | Type | c:index-dependent-source.cpp@ST>1#T@UsingB@UUT@UsingA<T>::Type | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:18 | struct(Gen)/C++ | UsingA | c:@ST>1#T@UsingA | <no-cgname> | Ref,RelCont | rel: 1
+ using typename UsingA<T>::Type;
+// CHECK: [[@LINE+2]]:20 | using/using-value(Gen)/C++ | func | c:index-dependent-source.cpp@ST>1#T@UsingB@UUV@UsingA<T>::func | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:9 | struct(Gen)/C++ | UsingA | c:@ST>1#T@UsingA | <no-cgname> | Ref,RelCont | rel: 1
+ using UsingA<T>::func;
+
+// CHECK: [[@LINE+2]]:20 | using/using-value(Gen)/C++ | operator() | c:index-dependent-source.cpp@ST>1#T@UsingB@UUV@UsingA<T>::operator() | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:9 | struct(Gen)/C++ | UsingA | c:@ST>1#T@UsingA | <no-cgname> | Ref,RelCont | rel: 1
+ using UsingA<T>::operator();
+// CHECK: [[@LINE+2]]:20 | using/using-value(Gen)/C++ | operator+ | c:index-dependent-source.cpp@ST>1#T@UsingB@UUV@UsingA<T>::operator+ | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:9 | struct(Gen)/C++ | UsingA | c:@ST>1#T@UsingA | <no-cgname> | Ref,RelCont | rel: 1
+ using UsingA<T>::operator+;
+};
+
+template <typename T>
+struct UsingC : public UsingB<T> {
+ static void test() {
+// CHECK: [[@LINE+2]]:25 | type-alias/C | TypeB | c:index-dependent-source.cpp@ST>1#T@UsingB@T@TypeB | <no-cgname> | Ref,RelCont | rel: 1
+// CHECK: [[@LINE+1]]:14 | struct(Gen)/C++ | UsingB | c:@ST>1#T@UsingB | <no-cgname> | Ref,RelCont | rel: 1
+ typename UsingB<T>::TypeB value1;
+// CHECK: [[@LINE+2]]:25 | using/using-typename(Gen)/C++ | Type | c:index-dependent-source.cpp@ST>1#T@UsingB@UUT@UsingA<T>::Type | <no-cgname> | Ref,RelCont | rel: 1
+// CHECK: [[@LINE+1]]:14 | struct(Gen)/C++ | UsingB | c:@ST>1#T@UsingB | <no-cgname> | Ref,RelCont | rel: 1
+ typename UsingB<T>::Type value2;
+// CHECK: [[@LINE+2]]:16 | using/using-value(Gen)/C++ | func | c:index-dependent-source.cpp@ST>1#T@UsingB@UUV@UsingA<T>::func | <no-cgname> | Ref,Call,RelCall,RelCont | rel: 1
+// CHECK: [[@LINE+1]]:5 | struct(Gen)/C++ | UsingB | c:@ST>1#T@UsingB | <no-cgname> | Ref,RelCont | rel: 1
+ UsingB<T>::func();
+ }
+};
+
+template <typename T>
+struct UsingD {
+// CHECK: [[@LINE+1]]:8 | instance-method/C++ | foo | c:@ST>1#T@UsingD@F@foo#t0.0# | <no-cgname> | Decl,RelChild | rel: 1
+ void foo(T);
+};
+
+template <typename T, typename U>
+struct UsingE : public UsingD<T>, public UsingD<U> {
+// CHECK: [[@LINE+2]]:20 | using/using-value(Gen)/C++ | foo | c:index-dependent-source.cpp@ST>2#T#T@UsingE@UUV@UsingD<T>::foo | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:9 | struct(Gen)/C++ | UsingD | c:@ST>1#T@UsingD | <no-cgname> | Ref,RelCont | rel: 1
+ using UsingD<T>::foo;
+// CHECK: [[@LINE+2]]:20 | using/using-value(Gen)/C++ | foo | c:index-dependent-source.cpp@ST>2#T#T@UsingE@UUV@UsingD<U>::foo | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK: [[@LINE+1]]:9 | struct(Gen)/C++ | UsingD | c:@ST>1#T@UsingD | <no-cgname> | Ref,RelCont | rel: 1
+ using UsingD<U>::foo;
+};
diff --git a/test/Index/Core/index-source.cpp b/test/Index/Core/index-source.cpp
index 4864d6c..f9c88d5 100644
--- a/test/Index/Core/index-source.cpp
+++ b/test/Index/Core/index-source.cpp
@@ -497,6 +497,19 @@
}
+template<typename T>
+struct Guided { T t; };
+// CHECK: [[@LINE-1]]:8 | struct(Gen)/C++ | Guided | c:@ST>1#T@Guided | <no-cgname> | Def | rel: 0
+// CHECK-NEXT: [[@LINE-2]]:19 | field/C++ | t | c:@ST>1#T@Guided@FI@t | <no-cgname> | Def,RelChild | rel: 1
+// CHECK-NEXT: RelChild | Guided | c:@ST>1#T@Guided
+Guided(double) -> Guided<float>;
+// CHECK: [[@LINE-1]]:19 | struct(Gen)/C++ | Guided | c:@ST>1#T@Guided | <no-cgname> | Ref | rel: 0
+// CHECK-NEXT: [[@LINE-2]]:1 | struct(Gen)/C++ | Guided | c:@ST>1#T@Guided | <no-cgname> | Ref | rel: 0
+auto guided = Guided{1.0};
+// CHECK: [[@LINE-1]]:6 | variable/C | guided | c:@guided | _guided | Def | rel: 0
+// CHECK-NEXT: [[@LINE-2]]:15 | struct(Gen)/C++ | Guided | c:@ST>1#T@Guided | <no-cgname> | Ref,RelCont | rel: 1
+// CHECK-NEXT: RelCont | guided | c:@guided
+
namespace rd33122110 {
struct Outer {
diff --git a/test/Index/Core/index-source.m b/test/Index/Core/index-source.m
index 1333caf..2931e66 100644
--- a/test/Index/Core/index-source.m
+++ b/test/Index/Core/index-source.m
@@ -389,6 +389,15 @@
@interface ClassReceivers
@property(class) int p1;
+// CHECK: [[@LINE-1]]:22 | class-method/acc-get/ObjC | p1 | c:objc(cs)ClassReceivers(cm)p1 | +[ClassReceivers p1] | Decl,Dyn,Impl,RelChild,RelAcc | rel: 2
+// CHECK-NEXT: RelChild | ClassReceivers | c:objc(cs)ClassReceivers
+// CHECK-NEXT: RelAcc | p1 | c:objc(cs)ClassReceivers(cpy)p1
+// CHECK: [[@LINE-4]]:22 | class-method/acc-set/ObjC | setP1: | c:objc(cs)ClassReceivers(cm)setP1: | +[ClassReceivers setP1:] | Decl,Dyn,Impl,RelChild,RelAcc | rel: 2
+// CHECK-NEXT: RelChild | ClassReceivers | c:objc(cs)ClassReceivers
+// CHECK-NEXT: RelAcc | p1 | c:objc(cs)ClassReceivers(cpy)p1
+// CHECK: [[@LINE-7]]:22 | instance-property/ObjC | p1 | c:objc(cs)ClassReceivers(cpy)p1 | <no-cgname> | Decl,RelChild | rel: 1
+// CHECK-NEXT: RelChild | ClassReceivers | c:objc(cs)ClassReceivers
+
+ (int)implicit;
+ (void)setImplicit:(int)x;
@@ -399,13 +408,13 @@
// CHECK: [[@LINE-1]]:3 | class/ObjC | ClassReceivers | c:objc(cs)ClassReceivers | _OBJC_CLASS_$_ClassReceivers | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:18 | instance-property/ObjC | p1 | c:objc(cs)ClassReceivers(cpy)p1 | <no-cgname> | Ref,Writ,RelCont | rel: 1
// CHECK-NEXT: RelCont | classReceivers | c:@F@classReceivers
-// CHECK: [[@LINE-4]]:18 | class-method/ObjC | setP1: | c:objc(cs)ClassReceivers(cm)setP1: | +[ClassReceivers setP1:] | Ref,Call,Impl,RelCall,RelCont | rel: 1
+// CHECK: [[@LINE-4]]:18 | class-method/acc-set/ObjC | setP1: | c:objc(cs)ClassReceivers(cm)setP1: | +[ClassReceivers setP1:] | Ref,Call,Impl,RelCall,RelCont | rel: 1
// CHECK-NEXT: RelCall,RelCont | classReceivers | c:@F@classReceivers
(void)ClassReceivers.p1;
// CHECK: [[@LINE-1]]:9 | class/ObjC | ClassReceivers | c:objc(cs)ClassReceivers | _OBJC_CLASS_$_ClassReceivers | Ref,RelCont | rel: 1
// CHECK: [[@LINE-2]]:24 | instance-property/ObjC | p1 | c:objc(cs)ClassReceivers(cpy)p1 | <no-cgname> | Ref,RelCont | rel: 1
// CHECK-NEXT: RelCont | classReceivers | c:@F@classReceivers
-// CHECK: [[@LINE-4]]:24 | class-method/ObjC | p1 | c:objc(cs)ClassReceivers(cm)p1 | +[ClassReceivers p1] | Ref,Call,Impl,RelCall,RelCont | rel: 1
+// CHECK: [[@LINE-4]]:24 | class-method/acc-get/ObjC | p1 | c:objc(cs)ClassReceivers(cm)p1 | +[ClassReceivers p1] | Ref,Call,Impl,RelCall,RelCont | rel: 1
// CHECK-NEXT: RelCall,RelCont | classReceivers | c:@F@classReceivers
ClassReceivers.implicit = 0;
diff --git a/test/Index/Store/Inputs/head.h b/test/Index/Store/Inputs/head.h
new file mode 100644
index 0000000..6ac174d
--- /dev/null
+++ b/test/Index/Store/Inputs/head.h
@@ -0,0 +1,3 @@
+
+extern void test1_func(void);
+extern void test2_func(void);
diff --git a/test/Index/Store/Inputs/json.c.json b/test/Index/Store/Inputs/json.c.json
new file mode 100644
index 0000000..498022d
--- /dev/null
+++ b/test/Index/Store/Inputs/json.c.json
@@ -0,0 +1,151 @@
+{
+ "files": [
+ "/test1.o",
+ "/Inputs/test1.c",
+ "/Inputs/head.h",
+ "/test2.o",
+ "/Inputs/test2.c",
+ "/test3.o",
+ "/Inputs/test3.cpp"
+ ],
+ "symbols": [
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@test1_func",
+ "name": "test1_func",
+ "codegen": "_test1_func",
+ "roles": "Decl,Def"
+ },
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@test2_func",
+ "name": "test2_func",
+ "codegen": "_test2_func",
+ "roles": "Decl,Def"
+ },
+ {
+ "kind": "class",
+ "lang": "C++",
+ "usr": "c:@S@Base",
+ "name": "Base",
+ "roles": "Def,Ref,RelBase,RelCont"
+ },
+ {
+ "kind": "class",
+ "lang": "C++",
+ "usr": "c:@S@Sub",
+ "name": "Sub",
+ "roles": "Def",
+ "rel-roles": "RelBase,RelCont"
+ }
+ ],
+ "records": [
+ {
+ "occurrences": [
+ {
+ "symbol": 0,
+ "line": 3,
+ "col": 6,
+ "roles": "Def"
+ }
+ ]
+ },
+ {
+ "occurrences": [
+ {
+ "symbol": 0,
+ "line": 2,
+ "col": 13,
+ "roles": "Decl"
+ },
+ {
+ "symbol": 1,
+ "line": 3,
+ "col": 13,
+ "roles": "Decl"
+ }
+ ]
+ },
+ {
+ "occurrences": [
+ {
+ "symbol": 1,
+ "line": 3,
+ "col": 6,
+ "roles": "Def"
+ }
+ ]
+ },
+ {
+ "occurrences": [
+ {
+ "symbol": 2,
+ "line": 1,
+ "col": 7,
+ "roles": "Def"
+ },
+ {
+ "symbol": 3,
+ "line": 2,
+ "col": 7,
+ "roles": "Def"
+ },
+ {
+ "symbol": 2,
+ "line": 2,
+ "col": 20,
+ "roles": "Ref,RelBase,RelCont",
+ "relations": [
+ {
+ "symbol": 3,
+ "rel-roles": "RelBase,RelCont"
+ }
+ ]
+
+ }
+ ]
+ }
+ ],
+ "units": [
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 0,
+ "sources": [
+ {
+ "file": 1,
+ "records": [0]
+ },
+ {
+ "file": 2,
+ "records": [1]
+ }
+ ]
+ },
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 3,
+ "sources": [
+ {
+ "file": 4,
+ "records": [2]
+ },
+ {
+ "file": 2,
+ "records": [1]
+ }
+ ]
+ },
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 5,
+ "sources": [
+ {
+ "file": 6,
+ "records": [3]
+ }
+ ]
+ }
+ ]
+}
diff --git a/test/Index/Store/Inputs/module/ModDep.h b/test/Index/Store/Inputs/module/ModDep.h
new file mode 100644
index 0000000..e96ef54
--- /dev/null
+++ b/test/Index/Store/Inputs/module/ModDep.h
@@ -0,0 +1,3 @@
+#include "ModTop.h"
+
+void ModDep_func(ModTopStruct s);
diff --git a/test/Index/Store/Inputs/module/ModSystem.h b/test/Index/Store/Inputs/module/ModSystem.h
new file mode 100644
index 0000000..0419f97
--- /dev/null
+++ b/test/Index/Store/Inputs/module/ModSystem.h
@@ -0,0 +1,4 @@
+
+typedef struct {} ModSystemStruct;
+
+void ModSystem_func(void);
diff --git a/test/Index/Store/Inputs/module/ModTop.h b/test/Index/Store/Inputs/module/ModTop.h
new file mode 100644
index 0000000..60c5686
--- /dev/null
+++ b/test/Index/Store/Inputs/module/ModTop.h
@@ -0,0 +1,4 @@
+
+typedef struct {} ModTopStruct;
+
+void ModTop_func(void);
diff --git a/test/Index/Store/Inputs/module/ModTopSub1.h b/test/Index/Store/Inputs/module/ModTopSub1.h
new file mode 100644
index 0000000..e1e3cf3
--- /dev/null
+++ b/test/Index/Store/Inputs/module/ModTopSub1.h
@@ -0,0 +1 @@
+void ModTopSub1_func(void);
diff --git a/test/Index/Store/Inputs/module/ModTopSub2.h b/test/Index/Store/Inputs/module/ModTopSub2.h
new file mode 100644
index 0000000..39d37f1
--- /dev/null
+++ b/test/Index/Store/Inputs/module/ModTopSub2.h
@@ -0,0 +1 @@
+// This header has no symbols, intended to show up as file dependency.
diff --git a/test/Index/Store/Inputs/module/module.modulemap b/test/Index/Store/Inputs/module/module.modulemap
new file mode 100644
index 0000000..ada2f38
--- /dev/null
+++ b/test/Index/Store/Inputs/module/module.modulemap
@@ -0,0 +1,12 @@
+module ModTop {
+ header "ModTop.h"
+ export *
+ module Sub1 {
+ header "ModTopSub1.h"
+ }
+ module Sub2 {
+ header "ModTopSub2.h"
+ }
+}
+module ModDep { header "ModDep.h" export * }
+module ModSystem [system] { header "ModSystem.h" export * }
diff --git a/test/Index/Store/Inputs/overlay.yaml b/test/Index/Store/Inputs/overlay.yaml
new file mode 100644
index 0000000..7b55b30
--- /dev/null
+++ b/test/Index/Store/Inputs/overlay.yaml
@@ -0,0 +1,6 @@
+{
+ 'version': 0,
+ 'roots': [{ 'type': 'file', 'name': 'OUT_DIR/using-overlay.h',
+ 'external-contents': 'INPUT_DIR/using-overlay.h'
+ }]
+}
diff --git a/test/Index/Store/Inputs/print-unit.h b/test/Index/Store/Inputs/print-unit.h
new file mode 100644
index 0000000..62039c4
--- /dev/null
+++ b/test/Index/Store/Inputs/print-unit.h
@@ -0,0 +1,2 @@
+#include "head.h"
+#include "using-overlay.h"
diff --git a/test/Index/Store/Inputs/sys/another.h b/test/Index/Store/Inputs/sys/another.h
new file mode 100644
index 0000000..555b99b
--- /dev/null
+++ b/test/Index/Store/Inputs/sys/another.h
@@ -0,0 +1,2 @@
+
+extern void sys_another_func(void);
diff --git a/test/Index/Store/Inputs/sys/syshead.h b/test/Index/Store/Inputs/sys/syshead.h
new file mode 100644
index 0000000..8941fd6
--- /dev/null
+++ b/test/Index/Store/Inputs/sys/syshead.h
@@ -0,0 +1,4 @@
+
+#include "another.h"
+
+extern void sys_test1_func(void);
diff --git a/test/Index/Store/Inputs/test1.c b/test/Index/Store/Inputs/test1.c
new file mode 100644
index 0000000..505711d
--- /dev/null
+++ b/test/Index/Store/Inputs/test1.c
@@ -0,0 +1,3 @@
+#include "head.h"
+
+void test1_func(void) {}
diff --git a/test/Index/Store/Inputs/test2.c b/test/Index/Store/Inputs/test2.c
new file mode 100644
index 0000000..333b8ae
--- /dev/null
+++ b/test/Index/Store/Inputs/test2.c
@@ -0,0 +1,3 @@
+#include "head.h"
+
+void test2_func(void) {}
diff --git a/test/Index/Store/Inputs/test3.cpp b/test/Index/Store/Inputs/test3.cpp
new file mode 100644
index 0000000..06334a1
--- /dev/null
+++ b/test/Index/Store/Inputs/test3.cpp
@@ -0,0 +1,2 @@
+class Base {};
+class Sub : public Base {};
diff --git a/test/Index/Store/Inputs/using-overlay.h b/test/Index/Store/Inputs/using-overlay.h
new file mode 100644
index 0000000..bb361c3
--- /dev/null
+++ b/test/Index/Store/Inputs/using-overlay.h
@@ -0,0 +1 @@
+void using_overlay(void);
diff --git a/test/Index/Store/assembly-invocation.c b/test/Index/Store/assembly-invocation.c
new file mode 100644
index 0000000..ab9c197
--- /dev/null
+++ b/test/Index/Store/assembly-invocation.c
@@ -0,0 +1,3 @@
+// Make sure it doesn't crash.
+// RUN: %clang -target x86_64-apple-macosx10.7 -S %s -o %t.s
+// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -c %t.s -o %t.o
diff --git a/test/Index/Store/external-source-symbol-hash.m b/test/Index/Store/external-source-symbol-hash.m
new file mode 100644
index 0000000..243616d
--- /dev/null
+++ b/test/Index/Store/external-source-symbol-hash.m
@@ -0,0 +1,47 @@
+// RUN: rm -rf %t.idx
+// RUN: %clang_cc1 %s -index-store-path %t.idx -D USE_EXTERNAL
+// RUN: c-index-test core -print-record %t.idx | FileCheck %s
+// RUN: %clang_cc1 %s -index-store-path %t.idx
+// RUN: find %t.idx/*/records -name "external-source-symbol-hash*" | count 2
+
+#ifdef USE_EXTERNAL
+# define EXT_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name)))
+#else
+# define EXT_DECL(mod_name)
+#endif
+
+#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type
+
+// Forward declarations should pick up the attribute from later decls
+@protocol P1;
+// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref | rel: 0
+@class I2;
+// CHECK: [[@LINE-1]]:8 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref | rel: 0
+enum E3: int;
+// CHECK: [[@LINE-1]]:6 | enum/Swift | c:@M@third_module@E@E3 | Ref | rel: 0
+
+void test(id<P1> first, I2 *second, enum E3 third) {}
+// CHECK: [[@LINE-1]]:14 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Ref,RelCont | rel: 1
+// CHECK: [[@LINE-2]]:25 | class/Swift | c:@M@other_module@objc(cs)I2 | Ref,RelCont | rel: 1
+// CHECK: [[@LINE-3]]:42 | enum/Swift | c:@M@third_module@E@E3 | Ref,RelCont | rel: 1
+
+EXT_DECL("some_module")
+@protocol P1
+// CHECK: [[@LINE-1]]:11 | protocol/Swift | c:@M@some_module@objc(pl)P1 | Decl | rel: 0
+-(void)method;
+// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@some_module@objc(pl)P1(im)method | Decl,Dyn,RelChild | rel: 1
+@end
+
+EXT_DECL("other_module")
+@interface I2
+// CHECK: [[@LINE-1]]:12 | class/Swift | c:@M@other_module@objc(cs)I2 | Decl | rel: 0
+-(void)method;
+// CHECK: [[@LINE-1]]:8 | instance-method/Swift | c:@M@other_module@objc(cs)I2(im)method | Decl,Dyn,RelChild | rel: 1
+@end
+
+
+typedef NS_ENUM(E3, int) {
+// CHECK: [[@LINE-1]]:17 | enum/Swift | c:@M@third_module@E@E3 | Def | rel: 0
+ firstCase = 1,
+ // CHECK: [[@LINE-1]]:3 | enumerator/Swift | c:@M@third_module@E@E3@firstCase | Def,RelChild | rel: 1
+} EXT_DECL("third_module");
diff --git a/test/Index/Store/handle-prebuilt-module.m b/test/Index/Store/handle-prebuilt-module.m
new file mode 100644
index 0000000..f6a0c42
--- /dev/null
+++ b/test/Index/Store/handle-prebuilt-module.m
@@ -0,0 +1,25 @@
+// XFAIL: linux
+
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx1 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err1
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err2
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -fsyntax-only %s -o %t.o -index-store-path %t/idx2 -fmodules -fmodules-cache-path=%t/mcp -I %S/Inputs/module -Rindex-store 2> %t.err3
+// RUN: FileCheck -input-file=%t.err1 -check-prefix=CREATING_MODULES %s -allow-empty
+// RUN: FileCheck -input-file=%t.err2 -check-prefix=CREATING_INDEX_DATA_FROM_MODULE_FILES %s
+// RUN: FileCheck -input-file=%t.err3 -check-prefix=EXISTING_INDEX_DATA_FROM_MODULE_FILES %s -allow-empty
+// RUN: c-index-test core -print-unit %t/idx1 > %t/all-units1.txt
+// RUN: c-index-test core -print-unit %t/idx2 > %t/all-units2.txt
+// RUN: c-index-test core -print-record %t/idx1 > %t/all-records1.txt
+// RUN: c-index-test core -print-record %t/idx2 > %t/all-records2.txt
+// RUN: diff -u %t/all-units1.txt %t/all-units2.txt
+// RUN: diff -u %t/all-records1.txt %t/all-records2.txt
+
+@import ModDep;
+
+// CREATING_MODULES-NOT: remark:
+
+// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModDep{{.*}}.pcm
+// CREATING_INDEX_DATA_FROM_MODULE_FILES: remark: producing index data for module file {{.*}}ModTop{{.*}}.pcm
+
+// EXISTING_INDEX_DATA_FROM_MODULE_FILES-NOT: remark:
diff --git a/test/Index/Store/json-with-module.m b/test/Index/Store/json-with-module.m
new file mode 100644
index 0000000..1ef6969
--- /dev/null
+++ b/test/Index/Store/json-with-module.m
@@ -0,0 +1,9 @@
+// RUN: rm -rf %t.idx %t.mcp
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module
+// RUN: c-index-test core -aggregate-json %t.idx -o %t.json
+// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json
+// RUN: diff -u %s.json %t.final.json
+
+// XFAIL: linux
+
+@import ModDep;
diff --git a/test/Index/Store/json-with-module.m.json b/test/Index/Store/json-with-module.m.json
new file mode 100644
index 0000000..5c9bf3a
--- /dev/null
+++ b/test/Index/Store/json-with-module.m.json
@@ -0,0 +1,151 @@
+{
+ "files": [
+ "/json-with-module.m.tmp.mcp/ModDep.pcm",
+ "/json-with-module.m.tmp.mcp/ModTop.pcm",
+ "/Inputs/module/ModDep.h",
+ "/Inputs/module/ModTop.h",
+ "/Inputs/module/ModTopSub1.h",
+ "/Inputs/module/ModTopSub2.h",
+ "/json-with-module.m.tmp.o",
+ "/json-with-module.m",
+ "/Inputs/module/module.modulemap"
+ ],
+ "symbols": [
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@ModDep_func",
+ "name": "ModDep_func",
+ "roles": "Decl",
+ "rel-roles": "RelCont"
+ },
+ {
+ "kind": "type-alias",
+ "lang": "C",
+ "usr": "c:@T@ModTopStruct",
+ "name": "ModTopStruct",
+ "roles": "Def,Ref,RelCont"
+ },
+ {
+ "kind": "struct",
+ "lang": "C",
+ "usr": "c:@SA@ModTopStruct",
+ "name": "",
+ "roles": "Def"
+ },
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@ModTop_func",
+ "name": "ModTop_func",
+ "roles": "Decl"
+ },
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@ModTopSub1_func",
+ "name": "ModTopSub1_func",
+ "roles": "Decl"
+ }
+ ],
+ "records": [
+ {
+ "occurrences": [
+ {
+ "symbol": 0,
+ "line": 3,
+ "col": 6,
+ "roles": "Decl"
+ },
+ {
+ "symbol": 1,
+ "line": 3,
+ "col": 18,
+ "roles": "Ref,RelCont",
+ "relations": [
+ {
+ "symbol": 0,
+ "rel-roles": "RelCont"
+ }
+ ]
+
+ }
+ ]
+ },
+ {
+ "occurrences": [
+ {
+ "symbol": 2,
+ "line": 2,
+ "col": 9,
+ "roles": "Def"
+ },
+ {
+ "symbol": 1,
+ "line": 2,
+ "col": 19,
+ "roles": "Def"
+ },
+ {
+ "symbol": 3,
+ "line": 4,
+ "col": 6,
+ "roles": "Decl"
+ }
+ ]
+ },
+ {
+ "occurrences": [
+ {
+ "symbol": 4,
+ "line": 1,
+ "col": 6,
+ "roles": "Decl"
+ }
+ ]
+ }
+ ],
+ "units": [
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 0,
+ "unit-dependencies": [1],
+ "sources": [
+ {
+ "file": 2,
+ "records": [0]
+ }
+ ]
+ },
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 1,
+ "sources": [
+ {
+ "file": 3,
+ "records": [1]
+ },
+ {
+ "file": 4,
+ "records": [2]
+ },
+ {
+ "file": 5
+ }
+ ]
+ },
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 6,
+ "unit-dependencies": [0],
+ "sources": [
+ {
+ "file": 7
+ },
+ {
+ "file": 8
+ }
+ ]
+ }
+ ]
+}
diff --git a/test/Index/Store/json-with-pch.c b/test/Index/Store/json-with-pch.c
new file mode 100644
index 0000000..9ffe80f
--- /dev/null
+++ b/test/Index/Store/json-with-pch.c
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t.idx
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror
+// RUN: c-index-test core -aggregate-json %t.idx -o %t.json
+// RUN: sed -e "s:%S::g" -e "s:%T::g" %t.json > %t.final.json
+// RUN: diff -u %s.json %t.final.json
+// XFAIL: linux
+int main() {
+ test1_func();
+}
diff --git a/test/Index/Store/json-with-pch.c.json b/test/Index/Store/json-with-pch.c.json
new file mode 100644
index 0000000..605f33e
--- /dev/null
+++ b/test/Index/Store/json-with-pch.c.json
@@ -0,0 +1,96 @@
+{
+ "files": [
+ "/json-with-pch.c.tmp.h.pch",
+ "/Inputs/head.h",
+ "/json-with-pch.c.tmp.o",
+ "/json-with-pch.c"
+ ],
+ "symbols": [
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@test1_func",
+ "name": "test1_func",
+ "roles": "Decl,Ref,Call,RelCall,RelCont"
+ },
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@test2_func",
+ "name": "test2_func",
+ "roles": "Decl"
+ },
+ {
+ "kind": "function",
+ "lang": "C",
+ "usr": "c:@F@main",
+ "name": "main",
+ "roles": "Def",
+ "rel-roles": "RelCall,RelCont"
+ }
+ ],
+ "records": [
+ {
+ "occurrences": [
+ {
+ "symbol": 0,
+ "line": 2,
+ "col": 13,
+ "roles": "Decl"
+ },
+ {
+ "symbol": 1,
+ "line": 3,
+ "col": 13,
+ "roles": "Decl"
+ }
+ ]
+ },
+ {
+ "occurrences": [
+ {
+ "symbol": 2,
+ "line": 8,
+ "col": 5,
+ "roles": "Def"
+ },
+ {
+ "symbol": 0,
+ "line": 9,
+ "col": 3,
+ "roles": "Ref,Call,RelCall,RelCont",
+ "relations": [
+ {
+ "symbol": 2,
+ "rel-roles": "RelCall,RelCont"
+ }
+ ]
+
+ }
+ ]
+ }
+ ],
+ "units": [
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 0,
+ "sources": [
+ {
+ "file": 1,
+ "records": [0]
+ }
+ ]
+ },
+ {
+ "triple": "x86_64-apple-macosx10.7.0",
+ "out-file": 2,
+ "unit-dependencies": [0],
+ "sources": [
+ {
+ "file": 3,
+ "records": [1]
+ }
+ ]
+ }
+ ]
+}
diff --git a/test/Index/Store/json.c b/test/Index/Store/json.c
new file mode 100644
index 0000000..c4ea965
--- /dev/null
+++ b/test/Index/Store/json.c
@@ -0,0 +1,10 @@
+// RUN: rm -rf %t.idx
+// RUN: mkdir -p %t.o
+// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test1.c -o %t.o/test1.o
+// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test2.c -o %t.o/test2.o
+// RUN: env CLANG_PROJECT_INDEX_PATH=%t.idx %clang -target x86_64-apple-macosx10.7 -arch x86_64 -mmacosx-version-min=10.7 -c %S/Inputs/test3.cpp -o %t.o/test3.o
+// RUN: c-index-test core -aggregate-json %t.idx -o %t.json
+// RUN: sed -e "s:%S::g" -e "s:%t.o::g" %t.json > %t.final.json
+// RUN: diff -u %S/Inputs/json.c.json %t.final.json
+
+// XFAIL: linux
diff --git a/test/Index/Store/print-record.mm b/test/Index/Store/print-record.mm
new file mode 100644
index 0000000..ce24983
--- /dev/null
+++ b/test/Index/Store/print-record.mm
@@ -0,0 +1,28 @@
+// RUN: rm -rf %t.idx
+// RUN: %clang_cc1 %s -index-store-path %t.idx
+// RUN: c-index-test core -print-record %t.idx | FileCheck %s
+
+// XFAIL: linux
+
+@class MyCls;
+
+@interface MyCls
+@end
+
+// CHECK: [[@LINE+2]]:6 | function/C | c:@F@foo#*$objc(cs)MyCls# | Decl | rel: 0
+// CHECK: [[@LINE+1]]:10 | class/ObjC | c:objc(cs)MyCls | Ref,RelCont | rel: 1
+void foo(MyCls *p);
+
+
+// RANGE-NOT: before_range
+void before_range();
+
+// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range1# | Decl
+void in_range1();
+// RANGE: [[@LINE+1]]:6 | function/C | c:@F@in_range2# | Decl
+void in_range2();
+
+// RANGE-NOT: after_range
+void after_range();
+
+// RUN: c-index-test core -print-record %t.idx -filepath %s:21:23 | FileCheck -check-prefix=RANGE %s
diff --git a/test/Index/Store/print-unit.c b/test/Index/Store/print-unit.c
new file mode 100644
index 0000000..19254a1
--- /dev/null
+++ b/test/Index/Store/print-unit.c
@@ -0,0 +1,39 @@
+// XFAIL: linux
+
+#include "print-unit.h"
+#include "syshead.h"
+
+void foo(int i);
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx %s -triple x86_64-apple-macosx10.8
+// RUN: c-index-test core -print-unit %t/idx | FileCheck %s
+// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt1 %s -triple x86_64-apple-macosx10.8 -O2
+// RUN: c-index-test core -print-unit %t/idx_opt1 | FileCheck %s -check-prefix=OPT
+// RUN: %clang_cc1 -I %S/Inputs -isystem %S/Inputs/sys -index-store-path %t/idx_opt2 %s -triple x86_64-apple-macosx10.8 -Os
+// RUN: c-index-test core -print-unit %t/idx_opt2 | FileCheck %s -check-prefix=OPT
+
+// CHECK: print-unit.c.o
+// CHECK: provider: clang-
+// CHECK: is-system: 0
+// CHECK: has-main: 1
+// CHECK: main-path: {{.*}}/print-unit.c
+// CHECK: out-file: {{.*}}/print-unit.c.o
+// CHECK: target: x86_64-apple-macosx10.8
+// CHECK: is-debug: 1
+// CHECK: DEPEND START
+// CHECK: Record | user | {{.*}}/print-unit.c | print-unit.c-
+// CHECK: Record | user | {{.*}}/Inputs/head.h | head.h-
+// CHECK: Record | user | {{.*}}/Inputs/using-overlay.h | using-overlay.h-
+// CHECK: Record | system | {{.*}}/Inputs/sys/syshead.h | syshead.h-
+// CHECK: Record | system | {{.*}}/Inputs/sys/another.h | another.h-
+// CHECK: File | user | {{.*}}/Inputs/print-unit.h | | {{[0-9]*$}}
+// CHECK: DEPEND END (6)
+// CHECK: INCLUDE START
+// CHECK: {{.*}}/print-unit.c:3 | {{.*}}/Inputs/print-unit.h
+// CHECK: {{.*}}/print-unit.c:4 | {{.*}}/Inputs/sys/syshead.h
+// CHECK: {{.*}}/Inputs/print-unit.h:1 | {{.*}}/Inputs/head.h
+// CHECK: {{.*}}/Inputs/print-unit.h:2 | {{.*}}/Inputs/using-overlay.h
+// CHECK: INCLUDE END (4)
+
+// OPT: is-debug: 0
diff --git a/test/Index/Store/print-units-with-modules.m b/test/Index/Store/print-units-with-modules.m
new file mode 100644
index 0000000..cedfe2c
--- /dev/null
+++ b/test/Index/Store/print-units-with-modules.m
@@ -0,0 +1,59 @@
+// RUN: rm -rf %t.idx %t.mcp
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module
+// RUN: c-index-test core -print-unit %t.idx | FileCheck %s
+
+// XFAIL: linux
+
+@import ModDep;
+@import ModSystem;
+
+// CHECK: ModDep.pcm
+// CHECK: provider: clang-
+// CHECK: is-system: 0
+// CHECK: is-module: 1
+// CHECK: module-name: ModDep
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/ModDep.pcm
+// CHECK: DEPEND START
+// CHECK: Unit | user | ModTop | {{.*}}/ModTop.pcm | ModTop.pcm
+// CHECK: Record | user | ModDep | {{.*}}/Inputs/module/ModDep.h | ModDep.h
+// CHECK: DEPEND END (2)
+
+// CHECK: ModSystem.pcm
+// CHECK: is-system: 1
+// CHECK: is-module: 1
+// CHECK: module-name: ModSystem
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/ModSystem.pcm
+// CHECK: DEPEND START
+// CHECK: Record | system | ModSystem | {{.*}}/Inputs/module/ModSystem.h | ModSystem.h
+// CHECK: DEPEND END (1)
+
+// CHECK: ModTop.pcm
+// CHECK: is-system: 0
+// CHECK: is-module: 1
+// CHECK: module-name: ModTop
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/ModTop.pcm
+// CHECK: DEPEND START
+// CHECK: Record | user | ModTop | {{.*}}/Inputs/module/ModTop.h | ModTop.h
+// CHECK: Record | user | ModTop.Sub1 | {{.*}}/Inputs/module/ModTopSub1.h | ModTopSub1.h
+// CHECK: File | user | ModTop.Sub2 | {{.*}}/Inputs/module/ModTopSub2.h | | {{[0-9]*$}}
+// CHECK: DEPEND END (3)
+
+// CHECK: print-units-with-modules.m.tmp.o
+// CHECK: is-system: 0
+// CHECK: is-module: 0
+// CHECK: module-name: <none>
+// CHECK: has-main: 1
+// CHECK: main-path: {{.*}}/print-units-with-modules.m
+// CHECK: out-file: {{.*}}/print-units-with-modules.m.tmp.o
+// CHECK: DEPEND START
+// CHECK: Unit | user | ModDep | {{.*}}/ModDep.pcm | ModDep.pcm
+// CHECK: Unit | system | ModSystem | {{.*}}/ModSystem.pcm | ModSystem.pcm
+// CHECK: File | user | {{.*}}/print-units-with-modules.m | | {{[0-9]*$}}
+// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap | | {{[0-9]*$}}
+// CHECK: DEPEND END (4)
diff --git a/test/Index/Store/print-units-with-pch.c b/test/Index/Store/print-units-with-pch.c
new file mode 100644
index 0000000..1e533a2
--- /dev/null
+++ b/test/Index/Store/print-units-with-pch.c
@@ -0,0 +1,29 @@
+// RUN: rm -rf %t.idx
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -x c-header %S/Inputs/head.h -o %t.h.pch -index-store-path %t.idx
+// RUN: %clang -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -index-store-path %t.idx -include %t.h -Werror
+// RUN: c-index-test core -print-unit %t.idx | FileCheck %s
+
+// XFAIL: linux
+
+int main() {
+ test1_func();
+}
+
+// CHECK: print-units-with-pch.c.tmp.h.pch
+// CHECK: is-system: 0
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/print-units-with-pch.c.tmp.h.pch
+// CHECK: DEPEND START
+// CHECK: Record | user | {{.*}}/Inputs/head.h | head.h
+// CHECK: DEPEND END (1)
+
+// CHECK: print-units-with-pch.c.tmp.o
+// CHECK: is-system: 0
+// CHECK: has-main: 1
+// CHECK: main-path: {{.*}}/print-units-with-pch.c
+// CHECK: out-file: {{.*}}/print-units-with-pch.c.tmp.o
+// CHECK: DEPEND START
+// CHECK: Unit | user | {{.*}}/print-units-with-pch.c.tmp.h.pch | print-units-with-pch.c.tmp.h.pch
+// CHECK: Record | user | {{.*}}/print-units-with-pch.c | print-units-with-pch.c
+// CHECK: DEPEND END (2)
diff --git a/test/Index/Store/record-hash-crash-invalid-name.cpp b/test/Index/Store/record-hash-crash-invalid-name.cpp
new file mode 100644
index 0000000..4479789
--- /dev/null
+++ b/test/Index/Store/record-hash-crash-invalid-name.cpp
@@ -0,0 +1,17 @@
+// Makes sure it doesn't crash.
+
+// XFAIL: linux
+
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 %s -index-store-path %t/idx -std=c++14
+// RUN: c-index-test core -print-record %t/idx | FileCheck %s
+
+namespace rdar32474406 {
+void foo();
+typedef void (*Func_t)();
+// CHECK: [[@LINE+4]]:1 | type-alias/C | c:record-hash-crash-invalid-name.cpp@N@rdar32474406@T@Func_t | Ref,RelCont | rel: 1
+// CHECK-NEXT: RelCont | c:@N@rdar32474406
+// CHECK: [[@LINE+2]]:14 | function/C | c:@N@rdar32474406@F@foo# | Ref,RelCont | rel: 1
+// CHECK-NEXT: RelCont | c:@N@rdar32474406
+Func_t[] = { foo }; // invalid decomposition
+}
diff --git a/test/Index/Store/record-hash-crash.cpp b/test/Index/Store/record-hash-crash.cpp
new file mode 100644
index 0000000..3d71ac9
--- /dev/null
+++ b/test/Index/Store/record-hash-crash.cpp
@@ -0,0 +1,31 @@
+// Makes sure it doesn't crash.
+
+// XFAIL: linux
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 %s -index-store-path %t/idx -std=c++14
+// RUN: c-index-test core -print-record %t/idx | FileCheck %s
+
+namespace crash1 {
+// CHECK: [[@LINE+1]]:6 | function/C
+auto getit() { return []() {}; }
+}
+
+namespace crash2 {
+// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Ref,RelCont | rel: 1
+template <typename T>
+class Foo; // canonical decl
+
+// CHECK: [[@LINE+2]]:7 | class(Gen)/C++ | c:@N@crash2@ST>1#T@Foo | Def,RelChild | rel: 1
+template <typename T>
+class Foo {};
+
+// CHECK: [[@LINE+2]]:8 | struct(Gen)/C++ | c:@N@crash2@ST>1#t>1#pT@Wrapper | Def,RelChild | rel: 1
+template <template <typename... ARGS> class TYPE>
+struct Wrapper {};
+
+// CHECK: [[@LINE+3]]:8 | struct(Gen,TS)/C++ | c:@N@crash2@S@Wrapper>#@N@crash2@ST>1#T@Foo | Def,RelChild,RelSpecialization | rel: 2
+// CHECK: RelSpecialization | c:@N@crash2@ST>1#t>1#pT@Wrapper
+template <>
+struct Wrapper<Foo> {};
+}
diff --git a/test/Index/Store/record-hash-using.cpp b/test/Index/Store/record-hash-using.cpp
new file mode 100644
index 0000000..c8285d5e
--- /dev/null
+++ b/test/Index/Store/record-hash-using.cpp
@@ -0,0 +1,46 @@
+// XFAIL: linux
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=A -DTYPE3=T -DTYPE4=T
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=B -DTYPE3=T -DTYPE4=T
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=B -DTYPE2=B -DTYPE3=T -DTYPE4=T
+// RUN: find %t/idx/*/records -name "record-hash*" | count 4
+//
+// RUN: rm -rf %t
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=T
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=U -DTYPE4=T
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=T -DTYPE4=U
+// RUN: %clang_cc1 %s -std=c++11 -index-store-path %t/idx -DTYPE1=A -DTYPE2=A -DTYPE3=U -DTYPE4=U
+// RUN: find %t/idx/*/records -name "record-hash*" | count 4
+
+template<typename T>
+struct A {
+ typedef int X;
+ void foo();
+};
+
+template<typename T>
+struct B : public A<T> {
+ typedef float X;
+ void foo(int);
+};
+
+template<typename T>
+struct C : public B<T> {
+// This should result in different records, due to the different types.
+ using TYPE1<T>::X;
+ using TYPE2<T>::foo;
+};
+
+template <typename T>
+struct D {
+ typedef T X;
+ void foo(T);
+};
+template <typename T, typename U>
+struct E : public D<T>, public D<U> {
+// This should result in different records, due to the different template parameter.
+ using D<TYPE3>::X;
+ using D<TYPE4>::foo;
+};
diff --git a/test/Index/Store/record-hash.cpp b/test/Index/Store/record-hash.cpp
new file mode 100644
index 0000000..21a4dc4
--- /dev/null
+++ b/test/Index/Store/record-hash.cpp
@@ -0,0 +1,12 @@
+// XFAIL: linux
+
+// RUN: rm -rf %t
+// RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=long
+// RUN: %clang_cc1 %s -index-store-path %t/idx -D THE_TYPE=char
+// RUN: find %t/idx/*/records -name "record-hash*" | count 2
+
+template<typename T>
+class TC {};
+
+// This should result in different records, due to the different template parameter type.
+void some_func(TC<THE_TYPE>);
diff --git a/test/Index/Store/relative-out-path.c b/test/Index/Store/relative-out-path.c
new file mode 100644
index 0000000..1d47ea0
--- /dev/null
+++ b/test/Index/Store/relative-out-path.c
@@ -0,0 +1,19 @@
+// Needs 'find'.
+// REQUIRES: shell
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: %clang %s -index-store-path %t/idx1 -c -o %t/outfile.o
+// RUN: cd %t
+// RUN: %clang %s -index-store-path %t/idx2 -c -o outfile.o
+// RUN: cd ..
+// RUN: %clang %s -index-store-path %t/idx3 -fsyntax-only -o outfile.o -working-directory=%t
+// RUN: diff -r -u %t/idx2 %t/idx3
+
+// RUN: find %t/idx1 -name '*outfile.o*' > %t/hashes.txt
+// RUN: find %t/idx3 -name '*outfile.o*' >> %t/hashes.txt
+// RUN: FileCheck %s --input-file=%t/hashes.txt
+// CHECK: outfile.o[[OUT_HASH:.*$]]
+// CHECK-NEXT: outfile.o[[OUT_HASH]]
+
+void foo();
diff --git a/test/Index/Store/syntax-only.c b/test/Index/Store/syntax-only.c
new file mode 100644
index 0000000..53d22bc
--- /dev/null
+++ b/test/Index/Store/syntax-only.c
@@ -0,0 +1,11 @@
+// RUN: rm -rf %t.idx
+// RUN: %clang -fsyntax-only %s -index-store-path %t.idx -o %T/syntax-only.c.myoutfile
+// RUN: c-index-test core -print-unit %t.idx | FileCheck %s -check-prefix=CHECK-UNIT
+// RUN: c-index-test core -print-record %t.idx | FileCheck %s -check-prefix=CHECK-RECORD
+
+// XFAIL: linux
+
+// CHECK-UNIT: out-file: {{.*}}/syntax-only.c.myoutfile
+// CHECK-RECORD: function/C | foo | c:@F@foo
+
+void foo();
diff --git a/test/Index/Store/unit-with-vfs.c b/test/Index/Store/unit-with-vfs.c
new file mode 100644
index 0000000..cbed626
--- /dev/null
+++ b/test/Index/Store/unit-with-vfs.c
@@ -0,0 +1,12 @@
+// RUN: sed -e "s:INPUT_DIR:%S/Inputs:g" -e "s:OUT_DIR:%t:g" %S/Inputs/overlay.yaml > %t.yaml
+// REQUIRES: shell
+
+#include "using-overlay.h"
+
+// RUN: rm -rf %t.idx
+// RUN: %clang_cc1 %s -index-store-path %t.idx -I %t -ivfsoverlay %t.yaml
+// RUN: c-index-test core -print-unit %t.idx | FileCheck %s
+
+// XFAIL: linux
+
+// CHECK: Record | user | {{.*}}test/Index/Store/Inputs/using-overlay.h
diff --git a/test/Index/Store/unit-workdir-prefix.c b/test/Index/Store/unit-workdir-prefix.c
new file mode 100644
index 0000000..e7a3a71
--- /dev/null
+++ b/test/Index/Store/unit-workdir-prefix.c
@@ -0,0 +1,30 @@
+// XFAIL: linux
+
+#include "header.h"
+
+void foo(void) {
+ bar();
+}
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/Directory
+// RUN: mkdir -p %t/Directory.surprise
+// RUN: mkdir -p %t/sdk
+// RUN: mkdir -p %t/sdk_other
+// RUN: echo "void bar(void);" > %t/sdk_other/header.h
+// RUN: cp %s %t/Directory.surprise/main.c
+//
+// RUN: %clang_cc1 -isystem %t/sdk_other -isysroot %t/sdk -index-store-path %t/idx %t/Directory.surprise/main.c -triple x86_64-apple-macosx10.8 -working-directory %t/Directory
+// RUN: c-index-test core -print-unit %t/idx | FileCheck %s
+
+// CHECK: main.c.o
+// CHECK: provider: clang-
+// CHECK: is-system: 0
+// CHECK: has-main: 1
+// CHECK: main-path: {{.*}}Directory.surprise{{/|\\}}main.c
+// CHECK: out-file: {{.*}}Directory.surprise{{/|\\}}main.c.o
+// CHECK: target: x86_64-apple-macosx10.8
+// CHECK: is-debug: 1
+// CHECK: DEPEND START
+// CHECK: Record | user | {{.*}}Directory.surprise{{/|\\}}main.c | main.c-
+// CHECK: Record | system | {{.*}}sdk_other{{/|\\}}header.h | header.h-
diff --git a/test/Index/Store/using-libstdcpp-arc.mm b/test/Index/Store/using-libstdcpp-arc.mm
new file mode 100644
index 0000000..9738c86
--- /dev/null
+++ b/test/Index/Store/using-libstdcpp-arc.mm
@@ -0,0 +1,10 @@
+// Test to make sure we don't crash, rdar://30816887.
+
+// RUN: rm -rf %t.idx
+// RUN: %clang_cc1 %s -index-store-path %t.idx -fobjc-arc -fobjc-arc-cxxlib=libstdc++
+// RUN: c-index-test core -print-record %t.idx | FileCheck %s
+
+// XFAIL: linux
+
+// CHECK: [[@LINE+1]]:6 | function/C
+void test1(void);
diff --git a/test/Index/comment-to-html-xml-conversion-with-original-literals.cpp b/test/Index/comment-to-html-xml-conversion-with-original-literals.cpp
new file mode 100644
index 0000000..26ca223
--- /dev/null
+++ b/test/Index/comment-to-html-xml-conversion-with-original-literals.cpp
@@ -0,0 +1,26 @@
+// RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s -std=c++11 | FileCheck %s
+
+constexpr int value(float f) { return int(f); }
+
+enum MyEnum {
+hexadecimal = 0x10 //!< a
+// CHECK: <Declaration>hexadecimal = 0x10</Declaration>
+
+, withSuffix = 1u + 010 //!< b
+// CHECK: <Declaration>withSuffix = 1u + 010</Declaration>
+
+#define ARG(x) x
+, macroArg = ARG(0x1) //!< c
+// CHECK: <Declaration>macroArg = ARG(0x1)</Declaration>
+
+#define MACROCONCAT(x, y) 22##x##y
+, macroConcat = MACROCONCAT(3, 2) //!< d
+// CHECK: <Declaration>macroConcat = MACROCONCAT(3, 2)</Declaration>
+
+#define MACRO(a,n) = 0x##a##n
+, weirdMacros MACRO(2,1) //!< e
+// CHECK: <Declaration>weirdMacros = 33</Declaration>
+
+, floatLiteral = value(0.25e3) //!< f
+// CHECK: <Declaration>floatLiteral = value(0.25e3)</Declaration>
+};
diff --git a/test/Index/index-templates.cpp b/test/Index/index-templates.cpp
index 966cc4f..424a638 100644
--- a/test/Index/index-templates.cpp
+++ b/test/Index/index-templates.cpp
@@ -219,6 +219,6 @@
// CHECK-USRS: index-templates.cpp c:@ST>2#T#T@Y Extent=[27:1 - 31:2]
// CHECK-USRS: index-templates.cpp c:index-templates.cpp@443 Extent=[27:10 - 27:20]
// CHECK-USRS: index-templates.cpp c:index-templates.cpp@455 Extent=[27:22 - 27:32]
-// CHECK-USRS-NOT: type
+// CHECK-USRS: index-templates.cpp c:index-templates.cpp@ST>2#T#T@Y@UUT@T::type Extent=[29:3 - 29:25]
// CHECK-USRS: index-templates.cpp c:@S@Z3 Extent=[33:1 - 33:14]
// CHECK-USRS: index-templates.cpp c:@F@f#$@S@map>#$@S@Z4#$@S@Pair>#I#S1_#$@S@compare>#$@S@Pair>#S1_#S2_#$@S@allocator>#S4_#
diff --git a/test/Index/skipped-ranges.c b/test/Index/skipped-ranges.c
index bd16fb3..f6c6c7f 100644
--- a/test/Index/skipped-ranges.c
+++ b/test/Index/skipped-ranges.c
@@ -20,6 +20,6 @@
#endif // cool
// RUN: env CINDEXTEST_SHOW_SKIPPED_RANGES=1 c-index-test -test-annotate-tokens=%s:1:1:16:1 %s | FileCheck %s
-// CHECK: Skipping: [5:2 - 6:7]
-// CHECK: Skipping: [8:2 - 12:7]
-// CHECK: Skipping: [14:2 - 20:7]
+// CHECK: Skipping: [5:1 - 6:7]
+// CHECK: Skipping: [8:1 - 12:7]
+// CHECK: Skipping: [14:1 - 20:7]
diff --git a/test/Misc/ast-dump-decl.m b/test/Misc/ast-dump-decl.m
index 4cfb8aa..bc5a058 100644
--- a/test/Misc/ast-dump-decl.m
+++ b/test/Misc/ast-dump-decl.m
@@ -143,3 +143,6 @@
__typeof__(B.foo) Test;
}
// CHECK: VarDecl{{.*}}Test 'typeof (B.foo)':'int'
+
+@compatibility_alias TestCompatibilityAlias A;
+// CHECK: ObjCCompatibleAliasDecl{{.*}}col:22 TestCompatibilityAlias col:45
diff --git a/test/Misc/pragma-attribute-supported-attributes-list.test b/test/Misc/pragma-attribute-supported-attributes-list.test
index d698276..834dd4c 100644
--- a/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -2,7 +2,7 @@
// The number of supported attributes should never go down!
-// CHECK: #pragma clang attribute supports 62 attributes:
+// CHECK: #pragma clang attribute supports 65 attributes:
// CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function)
// CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function)
@@ -57,8 +57,11 @@
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)
// CHECK-NEXT: SwiftContext (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: SwiftError (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: SwiftErrorResult (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: SwiftIndirectResult (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: SwiftNewtype (SubjectMatchRule_type_alias)
+// CHECK-NEXT: SwiftObjCMembers (SubjectMatchRule_objc_interface)
// CHECK-NEXT: TLSModel (SubjectMatchRule_variable_is_thread_local)
// CHECK-NEXT: Target (SubjectMatchRule_function)
// CHECK-NEXT: TestTypestate (SubjectMatchRule_function_is_member)
diff --git a/test/Misc/warning-flags-tree.c b/test/Misc/warning-flags-tree.c
index d71c9f6..01ba497 100644
--- a/test/Misc/warning-flags-tree.c
+++ b/test/Misc/warning-flags-tree.c
@@ -1,6 +1,6 @@
-// RUN: diagtool tree | FileCheck -strict-whitespace %s
-// RUN: diagtool tree -Weverything | FileCheck -strict-whitespace %s
-// RUN: diagtool tree everything | FileCheck -strict-whitespace %s
+// RUN: diagtool tree --internal | FileCheck -strict-whitespace %s
+// RUN: diagtool tree --internal -Weverything | FileCheck -strict-whitespace %s
+// RUN: diagtool tree --internal everything | FileCheck -strict-whitespace %s
//
// These three ways of running diagtool tree are the same:
// they produce a tree for every top-level diagnostic flag.
@@ -29,8 +29,7 @@
// RUN: not diagtool tree -Wthis-is-not-a-valid-flag
-
-// RUN: diagtool tree -Wgnu | FileCheck -strict-whitespace -check-prefix CHECK-GNU %s
+// RUN: diagtool tree --internal -Wgnu | FileCheck -strict-whitespace -check-prefix CHECK-GNU %s
// CHECK-GNU: -Wgnu
// CHECK-GNU: -Wgnu-designator
// CHECK-GNU: ext_gnu_array_range
@@ -40,7 +39,7 @@
// CHECK-GNU: ext_vla
// There are more GNU extensions but we don't need to check them all.
-// RUN: diagtool tree --flags-only -Wgnu | FileCheck -check-prefix CHECK-FLAGS-ONLY %s
+// RUN: diagtool tree -Wgnu | FileCheck -check-prefix CHECK-FLAGS-ONLY %s
// CHECK-FLAGS-ONLY: -Wgnu
// CHECK-FLAGS-ONLY: -Wgnu-designator
// CHECK-FLAGS-ONLY-NOT: ext_gnu_array_range
diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp
index ed9d1e8..97386bc 100644
--- a/test/Modules/ExtDebugInfo.cpp
+++ b/test/Modules/ExtDebugInfo.cpp
@@ -69,6 +69,8 @@
void foo() {
anon.i = GlobalStruct.i = GlobalUnion.i = GlobalEnum;
+ A a;
+ Virtual virt;
}
// CHECK: ![[CPP:.*]] = !DIFile(filename: {{.*}}ExtDebugInfo.cpp"
@@ -210,3 +212,10 @@
// CHECK-SAME: dwoId:
// CHECK-PCH: !DICompileUnit({{.*}}splitDebugFilename:
// CHECK-PCH: dwoId: 18446744073709551614
+
+// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "A",
+// CHECK-SAME: DIFlagFwdDecl, identifier: "_ZTS1A")
+
+// There is a full definition of the type available in the module.
+// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Virtual",
+// CHECK-SAME: DIFlagFwdDecl, identifier: "_ZTS7Virtual")
diff --git a/test/Modules/Inputs/DebugCXX.h b/test/Modules/Inputs/DebugCXX.h
index c9cd68f..1ccf8d3 100644
--- a/test/Modules/Inputs/DebugCXX.h
+++ b/test/Modules/Inputs/DebugCXX.h
@@ -54,9 +54,9 @@
}
// Virtual class with a forward declaration.
-class FwdVirtual;
-class FwdVirtual {
- virtual ~FwdVirtual() {}
+struct Virtual;
+struct Virtual {
+ virtual ~Virtual() {}
};
struct PureForwardDecl;
diff --git a/test/Modules/Inputs/export_as_test.modulemap b/test/Modules/Inputs/export_as_test.modulemap
new file mode 100644
index 0000000..4aaec41
--- /dev/null
+++ b/test/Modules/Inputs/export_as_test.modulemap
@@ -0,0 +1,9 @@
+module PrivateFoo {
+ export_as Foo
+ export_as Bar
+ export_as Bar
+
+ module Sub {
+ export_as Wibble
+ }
+}
diff --git a/test/Modules/Inputs/import-textual/x.h b/test/Modules/Inputs/import-textual/x.h
new file mode 100644
index 0000000..9b41ccd
--- /dev/null
+++ b/test/Modules/Inputs/import-textual/x.h
@@ -0,0 +1,6 @@
+#ifndef RANDOM_DEP
+
+@interface X
+@end
+
+#endif // RANDOM_DEP
diff --git a/test/Modules/Inputs/shadow/A1/A.h b/test/Modules/Inputs/shadow/A1/A.h
new file mode 100644
index 0000000..f07c681
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A1/A.h
@@ -0,0 +1 @@
+#define A1_A_h
diff --git a/test/Modules/Inputs/shadow/A1/module.modulemap b/test/Modules/Inputs/shadow/A1/module.modulemap
new file mode 100644
index 0000000..9439a43
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A1/module.modulemap
@@ -0,0 +1,5 @@
+module A {
+ header "A.h"
+}
+
+module A1 {}
diff --git a/test/Modules/Inputs/shadow/A2/A.h b/test/Modules/Inputs/shadow/A2/A.h
new file mode 100644
index 0000000..9880ed0
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A2/A.h
@@ -0,0 +1 @@
+#define A2_A_h
diff --git a/test/Modules/Inputs/shadow/A2/module.modulemap b/test/Modules/Inputs/shadow/A2/module.modulemap
new file mode 100644
index 0000000..935d89b
--- /dev/null
+++ b/test/Modules/Inputs/shadow/A2/module.modulemap
@@ -0,0 +1,5 @@
+module A {
+ header "A.h"
+}
+
+module A2 {}
diff --git a/test/Modules/Inputs/swift_name/module.modulemap b/test/Modules/Inputs/swift_name/module.modulemap
new file mode 100644
index 0000000..b7ec6b1
--- /dev/null
+++ b/test/Modules/Inputs/swift_name/module.modulemap
@@ -0,0 +1,2 @@
+module SwiftNameInferred [swift_infer_import_as_member] {
+}
\ No newline at end of file
diff --git a/test/Modules/ModuleDebugInfo.cpp b/test/Modules/ModuleDebugInfo.cpp
index 71b05e5..008b3e4 100644
--- a/test/Modules/ModuleDebugInfo.cpp
+++ b/test/Modules/ModuleDebugInfo.cpp
@@ -86,10 +86,10 @@
// CHECK-SAME: flags: DIFlagFwdDecl
// CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIfNS_6traitsIfEEEE")
-// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "FwdVirtual"
+// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Virtual"
// CHECK-SAME: elements:
-// CHECK-SAME: identifier: "_ZTS10FwdVirtual")
-// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$FwdVirtual"
+// CHECK-SAME: identifier: "_ZTS7Virtual")
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "_vptr$Virtual"
// CHECK: !DICompositeType(tag: DW_TAG_union_type,
// CHECK-NOT: name:
@@ -111,6 +111,11 @@
// CHECK-SAME: name: "InAnonymousNamespace",
// CHECK-SAME: elements: !{{[0-9]+}})
+// CHECK: ![[A:.*]] = {{.*}}!DICompositeType(tag: DW_TAG_class_type, name: "A",
+// CHECK-SAME: elements:
+// CHECK-SAME: vtableHolder: ![[A]],
+// CHECK-SAME: identifier: "_ZTS1A")
+
// CHECK: ![[DERIVED:.*]] = {{.*}}!DICompositeType(tag: DW_TAG_class_type, name: "Derived",
// CHECK-SAME: identifier: "_ZTS7Derived")
// CHECK: !DICompositeType(tag: DW_TAG_class_type, name: "B", scope: ![[DERIVED]],
diff --git a/test/Modules/export_as_test.c b/test/Modules/export_as_test.c
new file mode 100644
index 0000000..a73d6bf
--- /dev/null
+++ b/test/Modules/export_as_test.c
@@ -0,0 +1,9 @@
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/export_as_test.modulemap %s 2> %t.err
+// RUN: FileCheck %s < %t.err
+
+// CHECK: export_as_test.modulemap:3:13: error: conflicting re-export of module 'PrivateFoo' as 'Foo' or 'Bar'
+// CHECK: export_as_test.modulemap:4:13: warning: module 'PrivateFoo' already re-exported as 'Bar'
+// CHECK: export_as_test.modulemap:7:15: error: only top-level modules can be re-exported as public
+
+
diff --git a/test/Modules/hash-werror.m b/test/Modules/hash-werror.m
new file mode 100644
index 0000000..85446ce
--- /dev/null
+++ b/test/Modules/hash-werror.m
@@ -0,0 +1,54 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+
+// (1) Test -Werror
+// RUN: echo 'int foo() { return fn(); }' > %t/foo.h
+// RUN: echo 'module foo { header "foo.h" export * }' > %t/module.modulemap
+//
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \
+// RUN: -I%t -fmodules-cache-path=%t/cache -Rmodule-build \
+// RUN: -fmodules-hash-error-diagnostics 2>%t/out
+//
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \
+// RUN: -I%t -Werror -fmodules-cache-path=%t/cache -Rmodule-build \
+// RUN: -Wno-implicit-function-declaration \
+// RUN: -fmodules-hash-error-diagnostics 2>>%t/out
+
+// RUN: FileCheck --check-prefix=CHECKWERROR %s -input-file %t/out
+// CHECKWERROR: remark: building module 'foo' as '/[[PATH:.*]]/foo-
+// CHECKWERROR-NOT: remark: building module 'foo' as '/[[PATH]]/foo-
+
+// (2) Test -Werror=
+// RUN: rm -rf %t/out %t/cache
+//
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \
+// RUN: -I%t -Werror -fmodules-cache-path=%t/cache -Rmodule-build \
+// RUN: -fmodules-hash-error-diagnostics 2>%t/out
+//
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \
+// RUN: -I%t -fmodules-cache-path=%t/cache -Rmodule-build \
+// RUN: -Werror=implicit-function-declaration \
+// RUN: -fmodules-hash-error-diagnostics 2>>%t/out
+
+// RUN: FileCheck --check-prefix=CHECKWERROREQUALS %s -input-file %t/out
+// CHECKWERROREQUALS: remark: building module 'foo' as '/[[PATH:.*]]/foo-
+// CHECKWERROREQUALS-NOT: remark: building module 'foo' as '/[[PATH]]/foo-
+
+// (3) Test -pedantic-errors
+// RUN: rm -rf %t/out %t/cache
+// RUN: echo '#ifdef foo' > %t/foo.h
+// RUN: echo '#endif bad // extension!' >> %t/foo.h
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \
+// RUN: -I%t -fmodules-cache-path=%t/cache -Rmodule-build -x c \
+// RUN: -fmodules-hash-error-diagnostics 2>%t/out
+//
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps %s -fsyntax-only \
+// RUN: -I%t -pedantic-errors -fmodules-cache-path=%t/cache -Rmodule-build -x c \
+// RUN: -fmodules-hash-error-diagnostics 2>>%t/out
+
+// RUN: FileCheck --check-prefix=CHECKPEDANTICERROR %s -input-file %t/out
+// CHECKPEDANTICERROR: remark: building module 'foo' as '/[[PATH:.*]]/foo-
+// CHECKPEDANTICERROR-NOT: remark: building module 'foo' as '/[[PATH]]/foo-
+
+#include "foo.h"
diff --git a/test/Modules/import-textual-nomodules.m b/test/Modules/import-textual-nomodules.m
new file mode 100644
index 0000000..7cf8c1e
--- /dev/null
+++ b/test/Modules/import-textual-nomodules.m
@@ -0,0 +1,8 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fsyntax-only -fmodules -fimplicit-module-maps -I%S/Inputs/import-textual -fmodules-cache-path=%t %s -verify
+
+// expected-no-diagnostics
+
+#import "x.h"
+#import "x.h"
+
diff --git a/test/Modules/infer_swift_name.m b/test/Modules/infer_swift_name.m
new file mode 100644
index 0000000..d4b4a5d
--- /dev/null
+++ b/test/Modules/infer_swift_name.m
@@ -0,0 +1,6 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/swift_name %s -verify
+// REQUIRES: shell
+
+@import SwiftNameInferred; // ok
+@import SwiftName; // expected-error{{module 'SwiftName' not found}}
diff --git a/test/Modules/shadow.m b/test/Modules/shadow.m
new file mode 100644
index 0000000..44320af
--- /dev/null
+++ b/test/Modules/shadow.m
@@ -0,0 +1,21 @@
+// RUN: rm -rf %t
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/shadow/A1 -I %S/Inputs/shadow/A2 %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -fmodule-map-file=%S/Inputs/shadow/A2/module.modulemap %s -fsyntax-only 2>&1 | FileCheck %s -check-prefix=REDEFINITION
+// REDEFINITION: error: redefinition of module 'A'
+// REDEFINITION: note: previously defined
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -fmodule-map-file=%S/Inputs/shadow/A1/module.modulemap -I %S/Inputs/shadow %s -verify
+
+@import A1;
+@import A2;
+@import A;
+
+#import "A2/A.h" // expected-note {{implicitly imported}}
+// expected-error@A2/module.modulemap:1 {{import of shadowed module 'A'}}
+// expected-note@A1/module.modulemap:1 {{previous definition}}
+
+#if defined(A2_A_h)
+#error got the wrong definition of module A
+#elif !defined(A1_A_h)
+#error missing definition from A1
+#endif
diff --git a/test/OpenMP/atomic_capture_codegen.cpp b/test/OpenMP/atomic_capture_codegen.cpp
index 0a22e42..5140359 100644
--- a/test/OpenMP/atomic_capture_codegen.cpp
+++ b/test/OpenMP/atomic_capture_codegen.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -emit-pch -o %t %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
// expected-no-diagnostics
#ifndef HEADER
#define HEADER
diff --git a/test/OpenMP/atomic_read_codegen.c b/test/OpenMP/atomic_read_codegen.c
index 0cd46e3..0cfb2d2 100644
--- a/test/OpenMP/atomic_read_codegen.c
+++ b/test/OpenMP/atomic_read_codegen.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -emit-pch -o %t %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
// expected-no-diagnostics
// REQUIRES: x86-registered-target
#ifndef HEADER
diff --git a/test/OpenMP/atomic_update_codegen.cpp b/test/OpenMP/atomic_update_codegen.cpp
index 8c02a43..bb3b42e 100644
--- a/test/OpenMP/atomic_update_codegen.cpp
+++ b/test/OpenMP/atomic_update_codegen.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -emit-pch -o %t %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
// expected-no-diagnostics
#ifndef HEADER
#define HEADER
diff --git a/test/OpenMP/atomic_write_codegen.c b/test/OpenMP/atomic_write_codegen.c
index 050d7a5..0c85b6e 100644
--- a/test/OpenMP/atomic_write_codegen.c
+++ b/test/OpenMP/atomic_write_codegen.c
@@ -1,6 +1,6 @@
-// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -emit-pch -o %t %s
-// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -target-cpu core2 -fopenmp -x c -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -x c -triple x86_64-apple-darwin10 -target-cpu core2 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
// expected-no-diagnostics
// REQUIRES: x86-registered-target
#ifndef HEADER
diff --git a/test/OpenMP/distribute_codegen.cpp b/test/OpenMP/distribute_codegen.cpp
index 37f00f0..4646351 100644
--- a/test/OpenMP/distribute_codegen.cpp
+++ b/test/OpenMP/distribute_codegen.cpp
@@ -34,7 +34,7 @@
}
}
-// CHECK: define {{.*}}void @.omp_outlined.(i32* noalias [[GBL_TIDP:%.+]], i32* noalias [[BND_TID:%.+]], float** dereferenceable({{[0-9]+}}) [[APTR:%.+]], float** dereferenceable({{[0-9]+}}) [[BPTR:%.+]], float** dereferenceable({{[0-9]+}}) [[CPTR:%.+]], float** dereferenceable({{[0-9]+}}) [[DPTR:%.+]])
+// CHECK: define {{.*}}void @{{.+}}(i32* noalias [[GBL_TIDP:%.+]], i32* noalias [[BND_TID:%.+]], float** dereferenceable({{[0-9]+}}) [[APTR:%.+]], float** dereferenceable({{[0-9]+}}) [[BPTR:%.+]], float** dereferenceable({{[0-9]+}}) [[CPTR:%.+]], float** dereferenceable({{[0-9]+}}) [[DPTR:%.+]])
// CHECK: [[TID_ADDR:%.+]] = alloca i32*
// CHECK: [[IV:%.+iv]] = alloca i32
// CHECK: [[LB:%.+lb]] = alloca i32
diff --git a/test/OpenMP/nvptx_target_firstprivate_codegen.cpp b/test/OpenMP/nvptx_target_firstprivate_codegen.cpp
index 5dcff8e..f750be0 100644
--- a/test/OpenMP/nvptx_target_firstprivate_codegen.cpp
+++ b/test/OpenMP/nvptx_target_firstprivate_codegen.cpp
@@ -1,15 +1,14 @@
-
// Test target codegen - host bc file has to be created first.
// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc
-// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple nvptx64-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s --check-prefix TCHECK --check-prefix TCHECK-64
+// RUN: %clang_cc1 -debug-info-kind=limited -verify -fopenmp -x c++ -triple nvptx64-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - | FileCheck %s --check-prefix TCHECK --check-prefix TCHECK-64
// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple i386-unknown-unknown -fopenmp-targets=nvptx-nvidia-cuda -emit-llvm-bc %s -o %t-x86-host.bc
-// RUN: %clang_cc1 -verify -fopenmp -x c++ -triple nvptx-unknown-unknown -fopenmp-targets=nvptx-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-x86-host.bc -o - | FileCheck %s --check-prefix TCHECK --check-prefix TCHECK-32
+// RUN: %clang_cc1 -debug-info-kind=limited -verify -fopenmp -x c++ -triple nvptx-unknown-unknown -fopenmp-targets=nvptx-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-x86-host.bc -o - | FileCheck %s --check-prefix TCHECK --check-prefix TCHECK-32
// expected-no-diagnostics
#ifndef HEADER
#define HEADER
-template<typename tx, typename ty>
-struct TT{
+template <typename tx, typename ty>
+struct TT {
tx X;
ty Y;
};
@@ -23,29 +22,32 @@
float b[10];
double c[5][10];
TT<long long, char> d;
-
- #pragma omp target firstprivate(a)
+
+#pragma omp target firstprivate(a) map(tofrom \
+ : b)
{
+ b[a] = a;
}
-
- // TCHECK: define void @__omp_offloading_{{.+}}(i{{[0-9]+}} [[A_IN:%.+]])
+
+ // TCHECK: define {{.*}}void @__omp_offloading_{{.+}}([10 x float] addrspace(1)* noalias [[B_IN:%.+]], i{{[0-9]+}} [[A_IN:%.+]])
// TCHECK: [[A_ADDR:%.+]] = alloca i{{[0-9]+}},
// TCHECK-NOT: alloca i{{[0-9]+}},
+ // TCHECK-64: call void @llvm.dbg.declare(metadata [10 x float] addrspace(1)** %{{.+}}, metadata !{{[0-9]+}}, metadata !DIExpression())
// TCHECK: store i{{[0-9]+}} [[A_IN]], i{{[0-9]+}}* [[A_ADDR]],
- // TCHECK: ret void
+ // TCHECK: ret void
-#pragma omp target firstprivate(aa,b,c,d)
+#pragma omp target firstprivate(aa, b, c, d)
{
aa += 1;
b[2] = 1.0;
c[1][2] = 1.0;
d.X = 1;
- d.Y = 1;
+ d.Y = 1;
}
-
+
// make sure that firstprivate variables are generated in all cases and that we use those instances for operations inside the
// target region
- // TCHECK: define void @__omp_offloading_{{.+}}(i{{[0-9]+}} [[A2_IN:%.+]], [10 x float]* {{.+}} [[B_IN:%.+]], [5 x [10 x double]]* {{.+}} [[C_IN:%.+]], [[TT]]* {{.+}} [[D_IN:%.+]])
+ // TCHECK: define {{.*}}void @__omp_offloading_{{.+}}(i{{[0-9]+}}{{.*}} [[A2_IN:%.+]], [10 x float]*{{.*}} [[B_IN:%.+]], [5 x [10 x double]]*{{.*}} [[C_IN:%.+]], [[TT]]*{{.*}} [[D_IN:%.+]])
// TCHECK: [[A2_ADDR:%.+]] = alloca i{{[0-9]+}},
// TCHECK: [[B_ADDR:%.+]] = alloca [10 x float]*,
// TCHECK: [[C_ADDR:%.+]] = alloca [5 x [10 x double]]*,
@@ -58,10 +60,12 @@
// TCHECK: store [10 x float]* [[B_IN]], [10 x float]** [[B_ADDR]],
// TCHECK: store [5 x [10 x double]]* [[C_IN]], [5 x [10 x double]]** [[C_ADDR]],
// TCHECK: store [[TT]]* [[D_IN]], [[TT]]** [[D_ADDR]],
- // TCHECK: [[CONV_A2ADDR:%.+]] = bitcast i{{[0-9]+}}* [[A2_ADDR]] to i{{[0-9]+}}*
// TCHECK: [[B_ADDR_REF:%.+]] = load [10 x float]*, [10 x float]** [[B_ADDR]],
+ // TCHECK: [[B_ADDR_REF:%.+]] = load [10 x float]*, [10 x float]** %
// TCHECK: [[C_ADDR_REF:%.+]] = load [5 x [10 x double]]*, [5 x [10 x double]]** [[C_ADDR]],
+ // TCHECK: [[C_ADDR_REF:%.+]] = load [5 x [10 x double]]*, [5 x [10 x double]]** %
// TCHECK: [[D_ADDR_REF:%.+]] = load [[TT]]*, [[TT]]** [[D_ADDR]],
+ // TCHECK: [[D_ADDR_REF:%.+]] = load [[TT]]*, [[TT]]** %
// firstprivate(aa): a_priv = a_in
@@ -74,16 +78,15 @@
// TCHECK: [[C_PRIV_BCAST:%.+]] = bitcast [5 x [10 x double]]* [[C_PRIV]] to i8*
// TCHECK: [[C_IN_BCAST:%.+]] = bitcast [5 x [10 x double]]* [[C_ADDR_REF]] to i8*
// TCHECK: call void @llvm.memcpy.{{.+}}(i8* [[C_PRIV_BCAST]], i8* [[C_IN_BCAST]],{{.+}})
-
+
// firstprivate(d)
// TCHECK: [[D_PRIV_BCAST:%.+]] = bitcast [[TT]]* [[D_PRIV]] to i8*
// TCHECK: [[D_IN_BCAST:%.+]] = bitcast [[TT]]* [[D_ADDR_REF]] to i8*
// TCHECK: call void @llvm.memcpy.{{.+}}(i8* [[D_PRIV_BCAST]], i8* [[D_IN_BCAST]],{{.+}})
- // TCHECK: load i16, i16* [[CONV_A2ADDR]],
+ // TCHECK: load i16, i16* [[A2_ADDR]],
-
- #pragma omp target firstprivate(ptr)
+#pragma omp target firstprivate(ptr)
{
ptr[0]++;
}
@@ -98,13 +101,12 @@
return a;
}
-
-template<typename tx>
+template <typename tx>
tx ftemplate(int n) {
tx a = 0;
tx b[10];
-#pragma omp target firstprivate(a,b)
+#pragma omp target firstprivate(a, b)
{
a += 1;
b[2] += 1;
@@ -113,13 +115,12 @@
return a;
}
-static
-int fstatic(int n) {
+static int fstatic(int n) {
int a = 0;
char aaa = 0;
int b[10];
-#pragma omp target firstprivate(a,aaa,b)
+#pragma omp target firstprivate(a, aaa, b)
{
a += 1;
aaa += 1;
@@ -129,7 +130,7 @@
return a;
}
-// TCHECK: define void @__omp_offloading_{{.+}}(i{{[0-9]+}} [[A_IN:%.+]], i{{[0-9]+}} [[A3_IN:%.+]], [10 x i{{[0-9]+}}]*{{.+}} [[B_IN:%.+]])
+// TCHECK: define {{.*}}void @__omp_offloading_{{.+}}(i{{[0-9]+}}{{.*}} [[A_IN:%.+]], i{{[0-9]+}}{{.*}} [[A3_IN:%.+]], [10 x i{{[0-9]+}}]*{{.+}} [[B_IN:%.+]])
// TCHECK: [[A_ADDR:%.+]] = alloca i{{[0-9]+}},
// TCHECK: [[A3_ADDR:%.+]] = alloca i{{[0-9]+}},
// TCHECK: [[B_ADDR:%.+]] = alloca [10 x i{{[0-9]+}}]*,
@@ -138,9 +139,8 @@
// TCHECK: store i{{[0-9]+}} [[A_IN]], i{{[0-9]+}}* [[A_ADDR]],
// TCHECK: store i{{[0-9]+}} [[A3_IN]], i{{[0-9]+}}* [[A3_ADDR]],
// TCHECK: store [10 x i{{[0-9]+}}]* [[B_IN]], [10 x i{{[0-9]+}}]** [[B_ADDR]],
-// TCHECK-64: [[A_CONV:%.+]] = bitcast i{{[0-9]+}}* [[A_ADDR]] to i{{[0-9]+}}*
-// TCHECK: [[A3_CONV:%.+]] = bitcast i{{[0-9]+}}* [[A3_ADDR]] to i8*
// TCHECK: [[B_ADDR_REF:%.+]] = load [10 x i{{[0-9]+}}]*, [10 x i{{[0-9]+}}]** [[B_ADDR]],
+// TCHECK: [[B_ADDR_REF:%.+]] = load [10 x i{{[0-9]+}}]*, [10 x i{{[0-9]+}}]** %
// firstprivate(a): a_priv = a_in
@@ -158,8 +158,8 @@
struct S1 {
double a;
- int r1(int n){
- int b = n+1;
+ int r1(int n) {
+ int b = n + 1;
#pragma omp target firstprivate(b)
{
@@ -169,7 +169,7 @@
return (int)b;
}
- // TCHECK: define void @__omp_offloading_{{.+}}([[S1]]* [[TH:%.+]], i{{[0-9]+}} [[B_IN:%.+]])
+ // TCHECK: define internal void @__omp_offloading_{{.+}}([[S1]]* [[TH:%.+]], i{{[0-9]+}} [[B_IN:%.+]])
// TCHECK: [[TH_ADDR:%.+]] = alloca [[S1]]*,
// TCHECK: [[B_ADDR:%.+]] = alloca i{{[0-9]+}},
// TCHECK-NOT: alloca i{{[0-9]+}},
@@ -185,9 +185,7 @@
// TCHECK: ret void
};
-
-
-int bar(int n, double *ptr){
+int bar(int n, double *ptr) {
int a = 0;
a += foo(n, ptr);
S1 S;
@@ -200,15 +198,15 @@
// template
-// TCHECK: define void @__omp_offloading_{{.+}}(i{{[0-9]+}} [[A_IN:%.+]], [10 x i{{[0-9]+}}]*{{.+}} [[B_IN:%.+]])
+// TCHECK: define internal void @__omp_offloading_{{.+}}(i{{[0-9]+}} [[A_IN:%.+]], [10 x i{{[0-9]+}}]*{{.+}} [[B_IN:%.+]])
// TCHECK: [[A_ADDR:%.+]] = alloca i{{[0-9]+}},
// TCHECK: [[B_ADDR:%.+]] = alloca [10 x i{{[0-9]+}}]*,
// TCHECK-NOT: alloca i{{[0-9]+}},
// TCHECK: [[B_PRIV:%.+]] = alloca [10 x i{{[0-9]+}}],
// TCHECK: store i{{[0-9]+}} [[A_IN]], i{{[0-9]+}}* [[A_ADDR]],
// TCHECK: store [10 x i{{[0-9]+}}]* [[B_IN]], [10 x i{{[0-9]+}}]** [[B_ADDR]],
-// TCHECK-64: [[A_ADDR_CONV:%.+]] = bitcast i{{[0-9]+}}* [[A_ADDR]] to i{{[0-9]+}}*
// TCHECK: [[B_ADDR_REF:%.+]] = load [10 x i{{[0-9]+}}]*, [10 x i{{[0-9]+}}]** [[B_ADDR]],
+// TCHECK: [[B_ADDR_REF:%.+]] = load [10 x i{{[0-9]+}}]*, [10 x i{{[0-9]+}}]** %
// firstprivate(a)
// TCHECK-NOT: store i{{[0-9]+}} %{{.+}}, i{{[0-9]+}}*
diff --git a/test/OpenMP/ordered_codegen.cpp b/test/OpenMP/ordered_codegen.cpp
index bd7c070..af6a872 100644
--- a/test/OpenMP/ordered_codegen.cpp
+++ b/test/OpenMP/ordered_codegen.cpp
@@ -216,7 +216,7 @@
// CHECK-LABEL: foo_simd
void foo_simd(int low, int up) {
// CHECK: store float 0.000000e+00, float* %{{.+}}, align {{[0-9]+}}, !llvm.mem.parallel_loop_access !
- // CHECK-NEXT: call void [[CAP_FUNC:@.+]](i32* %{{.+}}) #{{[0-9]+}}, !llvm.mem.parallel_loop_access !
+ // CHECK-NEXT: call void [[CAP_FUNC:@.+]](i32* %{{.+}}), !llvm.mem.parallel_loop_access !
#pragma omp simd
for (int i = low; i < up; ++i) {
f[i] = 0.0;
@@ -224,7 +224,7 @@
f[i] = 1.0;
}
// CHECK: store float 0.000000e+00, float* %{{.+}}, align {{[0-9]+}}
- // CHECK-NEXT: call void [[CAP_FUNC:@.+]](i32* %{{.+}}) #{{[0-9]+}}
+ // CHECK-NEXT: call void [[CAP_FUNC:@.+]](i32* %{{.+}})
#pragma omp for simd ordered
for (int i = low; i < up; ++i) {
f[i] = 0.0;
diff --git a/test/OpenMP/parallel_codegen.cpp b/test/OpenMP/parallel_codegen.cpp
index 23b3237..68a178e 100644
--- a/test/OpenMP/parallel_codegen.cpp
+++ b/test/OpenMP/parallel_codegen.cpp
@@ -109,7 +109,7 @@
// CHECK: call {{.*}}void @{{.+terminate.*|abort}}(
// CHECK-NEXT: unreachable
// CHECK-NEXT: }
-// CHECK-DEBUG: define internal void [[OMP_OUTLINED]](i32* noalias %.global_tid., i32* noalias %.bound_tid., i8*** dereferenceable({{4|8}}) %argc)
+// CHECK-DEBUG: define internal void [[OMP_OUTLINED_DEBUG:@.+]](i32* noalias %.global_tid., i32* noalias %.bound_tid., i8*** dereferenceable({{4|8}}) %argc)
// CHECK-DEBUG: store i8*** %argc, i8**** [[ARGC_PTR_ADDR:%.+]],
// CHECK-DEBUG: [[ARGC_REF:%.+]] = load i8***, i8**** [[ARGC_PTR_ADDR]]
// CHECK-DEBUG-NEXT: [[ARGC:%.+]] = load i8**, i8*** [[ARGC_REF]]
@@ -120,7 +120,9 @@
// CHECK-DEBUG-NEXT: }
// CHECK: define linkonce_odr {{.*}}void [[FOO1]](i8** %argc)
-// CHECK-DEBUG: define linkonce_odr void [[FOO1]](i8** %argc)
+// CHECK-DEBUG-DAG: define linkonce_odr void [[FOO1]](i8** %argc)
+// CHECK-DEBUG-DAG: define internal void [[OMP_OUTLINED]](i32* noalias %.global_tid., i32* noalias %.bound_tid., i8*** dereferenceable({{4|8}}) %argc)
+// CHECK-DEBUG-DAG: call void [[OMP_OUTLINED_DEBUG]]({{[^)]+}}){{[^,]*}}, !dbg
// CHECK: attributes #[[FN_ATTRS]] = {{.+}} nounwind
// CHECK-DEBUG: attributes #[[FN_ATTRS]] = {{.+}} nounwind
diff --git a/test/OpenMP/parallel_for_simd_codegen.cpp b/test/OpenMP/parallel_for_simd_codegen.cpp
index 06d9635..369ea17 100644
--- a/test/OpenMP/parallel_for_simd_codegen.cpp
+++ b/test/OpenMP/parallel_for_simd_codegen.cpp
@@ -114,6 +114,7 @@
int lin = 12;
#pragma omp parallel for simd linear(lin : get_val()), linear(g_ptr)
+// CHECK: alloca i32,
// Init linear private var.
// CHECK: [[LIN_VAR:%.+]] = load i32*, i32** %
// CHECK: [[LIN_LOAD:%.+]] = load i32, i32* [[LIN_VAR]]
diff --git a/test/OpenMP/target_parallel_debug_codegen.cpp b/test/OpenMP/target_parallel_debug_codegen.cpp
new file mode 100644
index 0000000..d7b6b89
--- /dev/null
+++ b/test/OpenMP/target_parallel_debug_codegen.cpp
@@ -0,0 +1,106 @@
+// RUN: %clang_cc1 -DCK1 -verify -fopenmp -x c++ -triple powerpc64le-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm-bc %s -o %t-ppc-host.bc
+// RUN: %clang_cc1 -DCK1 -verify -fopenmp -x c++ -triple nvptx64-unknown-unknown -fopenmp-targets=nvptx64-nvidia-cuda -emit-llvm %s -fopenmp-is-device -fopenmp-host-ir-file-path %t-ppc-host.bc -o - -debug-info-kind=limited | FileCheck %s
+// expected-no-diagnostics
+
+int main() {
+ /* int(*b)[a]; */
+ /* int *(**c)[a]; */
+ int a;
+ int b[10][10];
+ int c[10][10][10];
+#pragma omp target parallel firstprivate(a, b) map(tofrom \
+ : c)
+ {
+ int &f = c[1][1][1];
+ int &g = a;
+ int &h = b[1][1];
+ int d = 15;
+ a = 5;
+ b[0][a] = 10;
+ c[0][0][a] = 11;
+ b[0][a] = c[0][0][a];
+ }
+#pragma omp target parallel firstprivate(a) map(tofrom \
+ : c, b)
+ {
+ int &f = c[1][1][1];
+ int &g = a;
+ int &h = b[1][1];
+ int d = 15;
+ a = 5;
+ b[0][a] = 10;
+ c[0][0][a] = 11;
+ b[0][a] = c[0][0][a];
+ }
+#pragma omp target parallel map(tofrom \
+ : a, c, b)
+ {
+ int &f = c[1][1][1];
+ int &g = a;
+ int &h = b[1][1];
+ int d = 15;
+ a = 5;
+ b[0][a] = 10;
+ c[0][0][a] = 11;
+ b[0][a] = c[0][0][a];
+ }
+ return 0;
+}
+
+// CHECK: define internal void @__omp_offloading{{[^(]+}}([10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]]* {{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]] addrspace(1)* %{{.+}} to [10 x [10 x [10 x i32]]]*
+// CHECK: call void [[NONDEBUG_WRAPPER:.+]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]]* {{[^,]+}}, i64 {{[^,]+}}, [10 x [10 x i32]]* {{[^)]+}})
+
+// CHECK: define internal void [[DEBUG_PARALLEL:@.+]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]] addrspace(1)* noalias{{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]]* noalias{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]] addrspace(1)* %{{.+}} to [10 x [10 x [10 x i32]]]*
+
+// CHECK: define internal void [[NONDEBUG_WRAPPER]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]]* dereferenceable{{[^,]+}}, i64 {{[^,]+}}, [10 x [10 x i32]]* dereferenceable{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]]* %{{.+}} to [10 x [10 x [10 x i32]]] addrspace(1)*
+// CHECK: call void [[DEBUG_PARALLEL]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]]* {{[^)]+}})
+
+// CHECK: define void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]]* dereferenceable{{[^,]+}}, i64 {{[^,]+}}, [10 x [10 x i32]]* dereferenceable{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]]* %{{.+}} to [10 x [10 x [10 x i32]]] addrspace(1)*
+// CHECK: call void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]]* {{[^)]+}})
+
+// CHECK: define internal void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]] addrspace(1)* noalias {{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* noalias {{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]] addrspace(1)* %{{.+}} to [10 x [10 x [10 x i32]]]*
+// CHECK: addrspacecast [10 x [10 x i32]] addrspace(1)* %{{.+}} to [10 x [10 x i32]]*
+// CHECK: call void [[NONDEBUG_WRAPPER:.+]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]]* {{[^,]+}}, i64 {{[^,]+}}, [10 x [10 x i32]]* {{[^)]+}})
+
+// CHECK: define internal void [[DEBUG_PARALLEL:@.+]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]] addrspace(1)* noalias{{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* noalias{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]] addrspace(1)* %{{.+}} to [10 x [10 x [10 x i32]]]*
+// CHECK: addrspacecast [10 x [10 x i32]] addrspace(1)* %{{.+}} to [10 x [10 x i32]]*
+
+// CHECK: define internal void [[NONDEBUG_WRAPPER]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]]* dereferenceable{{[^,]+}}, i64 {{[^,]+}}, [10 x [10 x i32]]* dereferenceable{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]]* %{{.+}} to [10 x [10 x [10 x i32]]] addrspace(1)*
+// CHECK: addrspacecast [10 x [10 x i32]]* %{{.+}} to [10 x [10 x i32]] addrspace(1)*
+// CHECK: call void [[DEBUG_PARALLEL]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* {{[^)]+}})
+
+// CHECK: define void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]]* dereferenceable{{[^,]+}}, i64 {{[^,]+}}, [10 x [10 x i32]]* dereferenceable{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]]* %{{.+}} to [10 x [10 x [10 x i32]]] addrspace(1)*
+// CHECK: addrspacecast [10 x [10 x i32]]* %{{.+}} to [10 x [10 x i32]] addrspace(1)*
+// CHECK: call void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* {{[^)]+}})
+
+// CHECK: define internal void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]] addrspace(1)* noalias {{[^,]+}}, i32 addrspace(1)* noalias {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* noalias {{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]] addrspace(1)* %{{.+}} to [10 x [10 x [10 x i32]]]*
+// CHECK: addrspacecast i32 addrspace(1)* %{{.+}} to i32*
+// CHECK: addrspacecast [10 x [10 x i32]] addrspace(1)* %{{.+}} to [10 x [10 x i32]]*
+// CHECK: call void [[NONDEBUG_WRAPPER:@.+]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]]* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x i32]]* {{[^)]+}})
+
+// CHECK: define internal void [[DEBUG_PARALLEL:@.+]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]] addrspace(1)* noalias{{[^,]+}}, i32 addrspace(1)* noalias{{[^,]+}}, [10 x [10 x i32]] addrspace(1)* noalias{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]] addrspace(1)* %{{.+}} to [10 x [10 x [10 x i32]]]*
+// CHECK: addrspacecast i32 addrspace(1)* %{{.+}} to i32*
+// CHECK: addrspacecast [10 x [10 x i32]] addrspace(1)* %{{.+}} to [10 x [10 x i32]]*
+
+// CHECK: define internal void [[NONDEBUG_WRAPPER]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]]* dereferenceable{{[^,]+}}, i32* dereferenceable{{[^,]+}}, [10 x [10 x i32]]* dereferenceable{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]]* %{{.+}} to [10 x [10 x [10 x i32]]] addrspace(1)*
+// CHECK: addrspacecast i32* %{{.+}} to i32 addrspace(1)*
+// CHECK: addrspacecast [10 x [10 x i32]]* %{{.+}} to [10 x [10 x i32]] addrspace(1)*
+// CHECK: call void [[DEBUG_PARALLEL]](i32* {{[^,]+}}, i32* {{[^,]+}}, [10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 addrspace(1)* {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* {{[^)]+}})
+
+// CHECK: define void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]]* dereferenceable{{[^,]+}}, i32* dereferenceable{{[^,]+}}, [10 x [10 x i32]]* dereferenceable{{[^)]+}})
+// CHECK: addrspacecast [10 x [10 x [10 x i32]]]* %{{.+}} to [10 x [10 x [10 x i32]]] addrspace(1)*
+// CHECK: addrspacecast i32* %{{.+}} to i32 addrspace(1)*
+// CHECK: addrspacecast [10 x [10 x i32]]* %{{.+}} to [10 x [10 x i32]] addrspace(1)*
+// CHECK: call void @__omp_offloading_{{[^(]+}}([10 x [10 x [10 x i32]]] addrspace(1)* {{[^,]+}}, i32 addrspace(1)* {{[^,]+}}, [10 x [10 x i32]] addrspace(1)* {{[^)]+}})
+
diff --git a/test/Parser/switch-recovery.cpp b/test/Parser/switch-recovery.cpp
index 4b06d55..019682f 100644
--- a/test/Parser/switch-recovery.cpp
+++ b/test/Parser/switch-recovery.cpp
@@ -21,7 +21,7 @@
void test2() {
enum X { Xa, Xb } x;
- switch (x) { // expected-warning {{enumeration value 'Xb' not handled in switch}}
+ switch (x) { // expected-warning {{enumeration value 'Xb' not handled in switch}} expected-note {{add missing switch cases}}
case Xa; // expected-error {{expected ':' after 'case'}}
break;
}
diff --git a/test/Refactor/Extract/captured-variable-block-types.m b/test/Refactor/Extract/captured-variable-block-types.m
new file mode 100644
index 0000000..ef20181
--- /dev/null
+++ b/test/Refactor/Extract/captured-variable-block-types.m
@@ -0,0 +1,30 @@
+
+int capturedBlock(void (^block)(int x, int (^)())) {
+ return capturedBlock(block);
+}
+
+// CHECK1: extracted(void (^block)(int, int (^)()))
+
+typedef void (^BlockTypedef)();
+
+int capturedBlockTypedef(BlockTypedef fp) {
+ return capturedBlockTypedef(fp);
+}
+// CHECK1: extracted(BlockTypedef fp)
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:3:10-3:29 -selected=%s:11:10-11:34 %s -fblocks | FileCheck --check-prefix=CHECK1 %s
+
+@interface I
+@end
+
+@implementation I
+
+- (void)method {
+ void (^block)(int x, int (^)());
+ block(2, ^ (void) { return 0; });
+}
+// CHECK2: - (void)extracted:(void (^)(int, int (^)()))block {
+
+@end
+
+// RUN: clang-refactor-test perform -action extract-method -selected=%s:24:3-24:35 %s -fblocks | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/captured-variable-function-types.cpp b/test/Refactor/Extract/captured-variable-function-types.cpp
new file mode 100644
index 0000000..cf6ccee
--- /dev/null
+++ b/test/Refactor/Extract/captured-variable-function-types.cpp
@@ -0,0 +1,17 @@
+
+int capturedFunc(void (*fp)(int x)) {
+ auto fp2 = fp;
+ capturedFunc(fp);
+ return capturedFunc(fp2);
+}
+
+// CHECK1: extracted(void (*fp)(int))
+
+typedef void (*FuncTypedef)();
+
+int capturedFuncTypedef(FuncTypedef fp) {
+ return capturedFuncTypedef(fp);
+}
+// CHECK1: extracted(FuncTypedef fp)
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:4:3-4:19 -selected=%s:13:10-13:33 %s | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/Refactor/Extract/captured-variable-lambda-type.cpp b/test/Refactor/Extract/captured-variable-lambda-type.cpp
new file mode 100644
index 0000000..6042830
--- /dev/null
+++ b/test/Refactor/Extract/captured-variable-lambda-type.cpp
@@ -0,0 +1,18 @@
+
+int capturedLambda(int x) {
+ auto Lambda = [] () { };
+ Lambda();
+ auto Lambda2 = [] (int x, int y) -> int { return x + y * 2; };
+ int y = Lambda2(x, 1);
+ auto Lambda3 = [&] (int y) {
+ x = y + 2;
+ };
+ Lambda3(3);
+ return Lambda2(x, 1);
+}
+
+// CHECK1: extracted(const std::function<void ()> &Lambda)
+// CHECK1: extracted(const std::function<auto (int, int) -> int> &Lambda2, int x)
+// CHECK1: extracted(const std::function<auto (int, int) -> int> &Lambda2, const std::function<void (int)> &Lambda3, int x)
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:4:3-4:11 -selected=%s:6:3-6:24 -selected=%s:10:3-11:23 %s -std=c++11 | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/Refactor/Extract/captured-variable-typedef.cpp b/test/Refactor/Extract/captured-variable-typedef.cpp
new file mode 100644
index 0000000..f6991ea
--- /dev/null
+++ b/test/Refactor/Extract/captured-variable-typedef.cpp
@@ -0,0 +1,15 @@
+typedef signed int NSInteger;
+
+int capturedTypedef(NSInteger x) {
+ return capturedTypedef(x);
+}
+// CHECK1: "static int extracted(NSInteger x) {\nreturn capturedTypedef(x);\n}\n\n" [[@LINE-3]]
+// RUN: clang-refactor-test perform -action extract -selected=%s:4:10-4:28 %s -std=c++14 | FileCheck --check-prefix=CHECK1 %s
+
+using NSUInteger = unsigned int;
+
+int capturedUsing(NSUInteger x) {
+ return capturedUsing(x);
+}
+// CHECK2: "static int extracted(NSUInteger x) {\nreturn capturedUsing(x);\n}\n\n" [[@LINE-3]]
+// RUN: clang-refactor-test perform -action extract -selected=%s:12:10-12:26 %s -std=c++14 | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/captured-variable-types.cpp b/test/Refactor/Extract/captured-variable-types.cpp
new file mode 100644
index 0000000..1a83884
--- /dev/null
+++ b/test/Refactor/Extract/captured-variable-types.cpp
@@ -0,0 +1,223 @@
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+int basicTypes(int i, float f, char c, const int *ip, float *fp,
+ const Rectangle *structPointer) {
+ return basicTypes(i, f, c, ip, fp, structPointer);
+}
+// CHECK1: "static int extracted(char c, float f, float *fp, int i, const int *ip, const Rectangle *structPointer) {\nreturn basicTypes(i, f, c, ip, fp, structPointer);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK1-NEXT: "extracted(c, f, fp, i, ip, structPointer)" [[@LINE-3]]:10 -> [[@LINE-3]]:52
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:8:10-8:52 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:8:10-8:52 %s -x c | FileCheck --check-prefix=CHECK1 %s
+
+#ifndef __cplusplus
+#define bool _Bool
+#define true 1
+#define false 0
+#endif
+
+int boolType(bool b) {
+ bool b2 = true;
+ return boolType(b && b2 && true && false);
+}
+// CHECK2: "static int extracted(bool b, bool b2) {\nreturn boolType(b && b2 && true && false);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK2-NEXT: "extracted(b, b2)" [[@LINE-3]]:10 -> [[@LINE-3]]:44
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:24:10-24:44 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:24:10-24:44 %s -x c | FileCheck --check-prefix=CHECK2 %s
+
+int global = 0;
+
+void dontCaptureGlobalVariable() {
+ static int staticInFunction = 0;
+ int i = global + staticInFunction;
+}
+// CHECK3: "static int extracted(int staticInFunction) {\nreturn global + staticInFunction;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK3-NEXT: "extracted(staticInFunction)" [[@LINE-3]]:11 -> [[@LINE-3]]:36
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:36:11-36:36 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:36:11-36:36 %s -x c | FileCheck --check-prefix=CHECK3 %s
+
+#ifdef __cplusplus
+
+int referenceType(const Rectangle &r, int &i) {
+ return referenceType(r, i);
+}
+// CHECK4: "static int extracted(int &i, const Rectangle &r) {\nreturn referenceType(r, i);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK4-NEXT: "extracted(i, r)" [[@LINE-3]]:10 -> [[@LINE-3]]:29
+
+#endif
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:47:10-47:29 %s | FileCheck --check-prefix=CHECK4 %s
+
+typedef union {
+ int x;
+ Rectangle r;
+} Union;
+
+int aggregateTypeNotMutated(Rectangle r, Union u) {
+ Rectangle r2 = r;
+ Union u2 = u;
+ aggregateTypeNotMutated(r, u);
+ return aggregateTypeNotMutated(r2, u2);
+}
+// CHECK5-CPP: "static int extracted(const Rectangle &r, const Union &u) {\nreturn aggregateTypeNotMutated(r, u);\n}\n\n" [[@LINE-6]]:1 -> [[@LINE-6]]:1
+// CHECK5-CPP-NEXT: "extracted(r, u)" [[@LINE-4]]:3 -> [[@LINE-4]]:32
+// CHECK5-C: "static int extracted(const Rectangle *r, const Union *u) {\nreturn aggregateTypeNotMutated(*r, *u);\n}\n\n" [[@LINE-8]]:1 -> [[@LINE-8]]:1
+// CHECK5-C-NEXT: "extracted(&r, &u)" [[@LINE-6]]:3 -> [[@LINE-6]]:32
+// CHECK5-CPP: "static int extracted(const Rectangle &r2, const Union &u2) {\nreturn aggregateTypeNotMutated(r2, u2);\n}\n\n" [[@LINE-10]]:1 -> [[@LINE-10]]:1
+// CHECK5-CPP-NEXT: "extracted(r2, u2)" [[@LINE-7]]:10 -> [[@LINE-7]]:41
+// CHECK5-C: "static int extracted(const Rectangle *r2, const Union *u2) {\nreturn aggregateTypeNotMutated(*r2, *u2);\n}\n\n" [[@LINE-12]]:1 -> [[@LINE-12]]:1
+// CHECK5-C-NEXT: "extracted(&r2, &u2)" [[@LINE-9]]:10 -> [[@LINE-9]]:41
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:64:3-64:32 -selected=%s:65:10-65:41 %s | FileCheck --check-prefix=CHECK5-CPP %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:64:3-64:32 -selected=%s:65:10-65:41 %s -x c | FileCheck --check-prefix=CHECK5-C %s
+;
+int arrayType(const int x[]) {
+ int v[2] = {1, 2};
+ arrayType(x);
+ return arrayType(v);
+}
+// CHECK6: "static int extracted(const int *x) {\nreturn arrayType(x);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1
+// CHECK6-NEXT: "extracted(x)" [[@LINE-4]]:3 -> [[@LINE-4]]:15
+// CHECK6: "static int extracted(int *v) {\nreturn arrayType(v);\n}\n\n" [[@LINE-7]]:1 -> [[@LINE-7]]:1
+// CHECK6-NEXT: "extracted(v)" [[@LINE-5]]:10 -> [[@LINE-5]]:22
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:81:3-81:15 -selected=%s:82:10-82:22 %s | FileCheck --check-prefix=CHECK6 %s
+
+typedef enum {
+ EnumA, EnumB
+} Enum;
+
+int enumType(Enum e) {
+ return enumType(e);
+}
+// CHECK7: "static int extracted(Enum e) {\nreturn enumType(e);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK7-NEXT: "extracted(e)" [[@LINE-3]]:10 -> [[@LINE-3]]:21
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:96:10-96:21 %s | FileCheck --check-prefix=CHECK7 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:96:10-96:21 %s -x c | FileCheck --check-prefix=CHECK7 %s
+;
+int qualifierReduction(const int i, const int * const p, int *volatile pv, int *__restrict__ rv,
+ const Enum e) {
+ return qualifierReduction(i, p, pv, rv, e);
+}
+// CHECK8: "static int extracted(Enum e, int i, const int *p, int *volatile pv, int *__restrict rv) {\nreturn qualifierReduction(i, p, pv, rv, e);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK8-NEXT: "extracted(e, i, p, pv, rv)" [[@LINE-3]]:10 -> [[@LINE-3]]:45
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:106:10-106:45 %s | FileCheck --check-prefix=CHECK8 %s
+
+#ifdef __cplusplus
+
+int autoTypeHandling(int x, Rectangle &ref) {
+ auto i = x;
+ auto &r = ref;
+ return autoTypeHandling(i, r);
+}
+// CHECK9: "static int extracted(int i, Rectangle &r) {\nreturn autoTypeHandling(i, r);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1
+// CHECK9-NEXT: "extracted(i, r)" [[@LINE-3]]:10 -> [[@LINE-3]]:32
+
+#endif
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:118:10-118:32 %s | FileCheck --check-prefix=CHECK9 %s
+
+Rectangle globalRect;
+int capturedVariableMutation(int x, int y, int z, const int *ip, Rectangle r) {
+ return x = 0, y += 2, z &= 3, ip = 0, r = globalRect, capturedVariableMutation(x, 1, 2, ip, r);
+}
+// CHECK10-CPP: "static int extracted(const int *&ip, Rectangle &r, int &x, int &y, int &z) {\nreturn x = 0, y += 2, z &= 3, ip = 0, r = globalRect, capturedVariableMutation(x, 1, 2, ip, r);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK10-CPP-NEXT: "extracted(ip, r, x, y, z)" [[@LINE-3]]:10 -> [[@LINE-3]]:97
+
+// CHECK10-C: "static int extracted(const int **ip, Rectangle *r, int *x, int *y, int *z) {\nreturn *x = 0, *y += 2, *z &= 3, *ip = 0, *r = globalRect, capturedVariableMutation(*x, 1, 2, *ip, *r);\n}\n\n" [[@LINE-6]]:1 -> [[@LINE-6]]:1
+// CHECK10-C-NEXT: "extracted(&ip, &r, &x, &y, &z)" [[@LINE-6]]:10 -> [[@LINE-6]]:97
+
+#ifdef __cplusplus
+
+int capturedMutatedRef(Rectangle &r) {
+ return r = globalRect, capturedMutatedRef(r);
+}
+// CHECK10-CPP: "static int extracted(Rectangle &r) {\nreturn r = globalRect, capturedMutatedRef(r);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK10-CPP-NEXT: "extracted(r)" [[@LINE-3]]:10 -> [[@LINE-3]]:47
+
+#endif
+
+int capturedArrayMutation(int x[]) {
+ return (x) = 0, capturedArrayMutation(x);
+}
+// CHECK10-CPP: "static int extracted(int *&x) {\nreturn (x) = 0, capturedArrayMutation(x);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK10-CPP-NEXT: "extracted(x)" [[@LINE-3]]:10 -> [[@LINE-3]]:43
+// CHECK10-C: "static int extracted(int **x) {\nreturn (*x) = 0, capturedArrayMutation(*x);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1
+// CHECK10-C-NEXT: "extracted(&x)" [[@LINE-5]]:10 -> [[@LINE-5]]:43
+;
+void mutationOutsideOfExtractedCode(int x) {
+ x = 0;
+ x = 1, mutationOutsideOfExtractedCode(x + 1);
+ x = 2;
+ return 0;
+}
+// CHECK10-CPP: "static void extracted(int x) {\nmutationOutsideOfExtractedCode(x + 1);\n}\n\n" [[@LINE-6]]:1 -> [[@LINE-6]]:1
+// CHECK10-CPP-NEXT: "extracted(x)" [[@LINE-5]]:10 -> [[@LINE-5]]:47
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:129:10-129:97 -selected=%s:140:10-140:47 -selected=%s:148:10-148:43 -selected=%s:157:10-157:47 %s | FileCheck --check-prefix=CHECK10-CPP %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:129:10-129:97 -selected=%s:148:10-148:43 %s -x c | FileCheck --check-prefix=CHECK10-C %s
+;
+void extractStatementRangeRewritePointerUse(int x) {
+ extractStatementRangeRewritePointerUse(x);
+ x = 1;
+ extractStatementRangeRewritePointerUse(x);
+ extractStatementRangeRewritePointerUse(x);
+}
+// CHECK11: "static void extracted(int *x) {\n*x = 1;\n extractStatementRangeRewritePointerUse(*x);\n}\n\n" [[@LINE-6]]:1
+// CHECK11-NEXT: "extracted(&x)" [[@LINE-5]]:3 -> [[@LINE-4]]:44
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:169:3-170:10 %s -x c | FileCheck --check-prefix=CHECK11 %s
+
+void mutationAfterUse(int x) {
+ int y = x;
+ (void)y;
+ y = 2;
+}
+// CHECK12: "static void extracted(int &y) {\n(void)y;\n y = 2;\n}\n\n"
+// RUN: clang-refactor-test perform -action extract -selected=%s:180:3-181:4 %s | FileCheck --check-prefix=CHECK12 %s
+
+enum TagEnum {
+ TagEnumA, TagEnumB
+};
+
+int tagEnumType(enum TagEnum e) {
+ return tagEnumType(e);
+}
+// CHECK13: (enum TagEnum e)
+// RUN: clang-refactor-test perform -action extract -selected=%s:191:10-191:24 %s | FileCheck --check-prefix=CHECK13 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:191:10-191:24 %s -x c | FileCheck --check-prefix=CHECK13 %s
+
+struct TagStruct {
+ int x;
+};
+
+int tagStructType(struct TagStruct *s) {
+ return tagStructType(s);
+}
+// CHECK14: (struct TagStruct *s)
+// RUN: clang-refactor-test perform -action extract -selected=%s:202:10-202:26 %s | FileCheck --check-prefix=CHECK14 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:202:10-202:26 %s -x c | FileCheck --check-prefix=CHECK14 %s
+
+namespace {
+struct Foo { int x; };
+}
+
+void anonymousNamespaceTypes() {
+ Foo x;
+// anonymous-ns-type1-begin: +1:1
+ x.x = 0;
+// anonymous-ns-type1-end: +0:1
+// CHECK-ANON: "static void extracted(Foo &x) {\nx.x = 0;\n}\n\n"
+// anonymous-ns-type2-begin: +1:7
+ x = Foo { 0 };
+// anonymous-ns-type2-end: -1:16
+// CHECK-ANON: "static Foo extracted() {\nreturn Foo { 0 };\n}\n\n"
+}
+// RUN: clang-refactor-test perform -action extract -selected=anonymous-ns-type1 -selected=anonymous-ns-type2 %s -std=c++11 | FileCheck --check-prefix=CHECK-ANON %s
diff --git a/test/Refactor/Extract/captured-variable-types.m b/test/Refactor/Extract/captured-variable-types.m
new file mode 100644
index 0000000..d764217
--- /dev/null
+++ b/test/Refactor/Extract/captured-variable-types.m
@@ -0,0 +1,56 @@
+
+@interface Interface
+
+@end
+
+@protocol Protocol
+
+@end
+
+int basicObjCTypes(Interface *ip, id p, id<Protocol> pp, Interface<Protocol> *ipp) {
+ return basicObjCTypes(ip, p, pp, ipp);
+}
+// CHECK1: "static int extracted(Interface *ip, Interface<Protocol> *ipp, id p, id<Protocol> pp) {\nreturn basicObjCTypes(ip, p, pp, ipp);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK1-NEXT: "extracted(ip, ipp, p, pp)" [[@LINE-3]]:10 -> [[@LINE-3]]:40
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:10-11:40 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:10-11:40 %s -x objective-c++ | FileCheck --check-prefix=CHECK1 %s
+
+typedef signed char BOOL;
+#define YES __objc_yes
+#define NO __objc_no
+
+int boolType(BOOL b) {
+ BOOL b2 = YES;
+ return boolType(b && b2 && YES && NO);
+}
+// CHECK2: "static int extracted(BOOL b, BOOL b2) {\nreturn boolType(b && b2 && YES && NO);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK2-NEXT: "extracted(b, b2)" [[@LINE-3]]:10 -> [[@LINE-3]]:40
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:25:10-25:40 %s | FileCheck --check-prefix=CHECK2 %s
+;
+int mutationOfObjCPointer(Interface *ip) {
+ return ip = 0, mutationOfObjCPointer(ip);
+}
+// CHECK3-C: "static int extracted(Interface **ip) {\nreturn *ip = 0, mutationOfObjCPointer(*ip);\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK3-C-NEXT: "extracted(&ip)" [[@LINE-3]]:10 -> [[@LINE-3]]:43
+// CHECK3-CPP: "static int extracted(Interface *&ip) {\nreturn ip = 0, mutationOfObjCPointer(ip);\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1
+// CHECK3-CPP-NEXT: "extracted(ip)" [[@LINE-5]]:10 -> [[@LINE-5]]:43
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:33:10-33:43 %s | FileCheck --check-prefix=CHECK3-C %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:33:10-33:43 %s -x objective-c++ | FileCheck --check-prefix=CHECK3-CPP %s
+
+void silenceStrongInARC() {
+ Interface *pointer;
+// silence-strong-in-arc-begin: +1:1
+ mutationOfObjCPointer(pointer);
+// silence-strong-in-arc-end: +0:1
+// silence-strong2-in-arc-begin: +1:1
+ pointer = 0;
+// silence-strong2-in-arc-end: +0:1
+ (void)pointer;
+}
+// CHECK-ARC: "static int extracted(Interface *pointer) {\nreturn mutationOfObjCPointer(pointer);\n}\n\n"
+// CHECK-ARC: "static void extracted(Interface **pointer) {\n*pointer = 0;\n}\n\n"
+
+// RUN: clang-refactor-test perform -action extract -selected=silence-strong-in-arc -selected=silence-strong2-in-arc %s -fobjc-arc | FileCheck --check-prefix=CHECK-ARC %s
diff --git a/test/Refactor/Extract/disallowed-expressions.cpp b/test/Refactor/Extract/disallowed-expressions.cpp
new file mode 100644
index 0000000..522cb59
--- /dev/null
+++ b/test/Refactor/Extract/disallowed-expressions.cpp
@@ -0,0 +1,50 @@
+void disallowExtractOfSimpleExpressions(int param) {
+ int var = param;
+ short var2 = (short)(var);
+ int x = 0;
+ const char *s = "Test";
+ char c = 'C';
+ double y = ((2.1));
+ const char *f = __func__;
+}
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:2:13-2:18 -selected=%s:2:14-2:15 -selected=%s:2:16-2:18 %s 2>&1 | FileCheck %s
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:3:16-3:28 -selected=%s:4:11-4:12 -selected=%s:5:19-5:25 %s 2>&1 | FileCheck %s
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:6:12-6:15 -selected=%s:7:14-7:21 -selected=%s:8:19-8:27 %s 2>&1 | FileCheck %s
+
+// CHECK: Failed to initiate the refactoring action (the selected expression is too simple)!
+
+void allowOperationsOnSimpleExpression(int x, int y) {
+ int z = x + y;
+ int zz = 0 + 1;
+ allowOperationsOnSimpleExpression(1, y);
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:18:11-18:16 %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: Initiated the 'extract' action at 18:11 -> 18:16
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:19:12-19:17 %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK2: Initiated the 'extract' action at 19:12 -> 19:17
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:20:37-20:41 %s | FileCheck --check-prefix=CHECK3 %s
+// CHECK3: Initiated the 'extract' action at 20:3 -> 20:42
+
+void defaultParameter(int x = 0 + 1) {
+
+}
+
+struct Struct {
+ int y = 22 + 21;
+
+ Struct(int x) : y(x + 1) { }
+};
+
+int initializerExpression = 1 + 2;
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:32:31-32:36 -selected=%s:37:11-37:18 -selected=%s:39:21-39:26 -selected=%s:42:29-42:34 %s 2>&1 | FileCheck --check-prefix=NOT-IN-FUNC %s
+// NOT-IN-FUNC: Failed to initiate the refactoring action (the selected expression is not in a function)!
+
+void disallowWholeFunctionBody() {
+ int x = 0;
+}
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:47:34-49:2 %s 2>&1 | FileCheck --check-prefix=NOT-IN-FUNC %s
diff --git a/test/Refactor/Extract/extract-address-of-captured-variable.cpp b/test/Refactor/Extract/extract-address-of-captured-variable.cpp
new file mode 100644
index 0000000..c2d4a79
--- /dev/null
+++ b/test/Refactor/Extract/extract-address-of-captured-variable.cpp
@@ -0,0 +1,194 @@
+
+void takesVoidPtr(void *x) { }
+void takesPtr(int *x) { }
+void takesPtrPtr(int **x) { }
+
+void addressOfVariableImpliesPossibleMutation(int x, int *ip) {
+ takesPtr(&x);
+// CHECK1-C: (int *x) {\ntakesPtr(x);\n}
+// CHECK1-CPP: (int &x) {\ntakesPtr(&x);\n}
+ takesVoidPtr(&x);
+// CHECK1-C: (int *x) {\ntakesVoidPtr(x);\n}
+// CHECK1-CPP: (int &x) {\ntakesVoidPtr(&x);\n}
+ &x;
+// CHECK1-C: (int *x) {\nreturn x;\n}
+// CHECK1-CPP: (int &x) {\nreturn &x;\n}
+ *(&x) = 0;
+// CHECK1-C: extracted(int *x) {\n*(x) = 0;\n}
+// CHECK1-CPP: extracted(int &x) {\n*(&x) = 0;\n}
+ takesPtrPtr(&ip);
+// CHECK1-C: (int **ip) {\ntakesPtrPtr(ip);\n}
+// CHECK1-CPP: (int *&ip) {\ntakesPtrPtr(&ip);\n}
+ takesPtr(ip);
+// CHECK1-C: (int *ip) {\ntakesPtr(ip);\n}
+// CHECK1-CPP: (int *ip) {\ntakesPtr(ip);\n}
+ takesVoidPtr(ip);
+// CHECK1-C: (int *ip) {\ntakesVoidPtr(ip);\n}
+// CHECK1-CPP: (int *ip) {\ntakesVoidPtr(ip);\n}
+ takesPtr(&((x)));
+// CHECK1-C: (int *x) {\ntakesPtr(((x)));\n}
+// CHECK1-CPP: (int &x) {\ntakesPtr(&((x)));\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:7:3-7:15 -selected=%s:10:3-10:19 -selected=%s:13:3-13:5 -selected=%s:16:3-16:12 -selected=%s:19:3-19:19 -selected=%s:22:3-22:15 -selected=%s:25:3-25:19 -selected=%s:28:3-28:19 %s -x c | FileCheck --check-prefix=CHECK1-C %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:7:3-7:15 -selected=%s:10:3-10:19 -selected=%s:13:3-13:5 -selected=%s:16:3-16:12 -selected=%s:19:3-19:19 -selected=%s:22:3-22:15 -selected=%s:25:3-25:19 -selected=%s:28:3-28:19 %s | FileCheck --check-prefix=CHECK1-CPP %s
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+void takesStructPtr(Rectangle *sp) { }
+
+#ifdef __cplusplus
+
+void addressOfRef(int &x, int *&ip, Rectangle &r) {
+ takesPtr(&x);
+// CHECK2: (int &x) {\ntakesPtr(&x);\n}
+ takesPtrPtr(&ip);
+// CHECK2: (int *&ip) {\ntakesPtrPtr(&ip);\n}
+ takesStructPtr(&r);
+// CHECK2: (Rectangle &r) {\ntakesStructPtr(&r);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:45:3-45:15 -selected=%s:47:3-47:19 -selected=%s:49:3-49:21 %s | FileCheck --check-prefix=CHECK2 %s
+
+#endif
+
+void addressOfArray(int x[]) {
+ takesPtrPtr(&x);
+// CHECK3-C: (int **x) {\ntakesPtrPtr(x);\n}
+// CHECK3-CPP: (int *&x) {\ntakesPtrPtr(&x);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:58:3-58:18 %s -x c | FileCheck --check-prefix=CHECK3-C %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:58:3-58:18 %s | FileCheck --check-prefix=CHECK3-CPP %s
+
+typedef struct {
+ Rectangle r;
+} RectangleInStruct;
+
+void addressOfMember(Rectangle r, RectangleInStruct rs) {
+ takesPtr(&r.width);
+// CHECK4-C: (Rectangle *r) {\ntakesPtr(&r->width);\n}
+// CHECK4-CPP: (Rectangle &r) {\ntakesPtr(&r.width);\n}
+ takesPtr(&(rs).r.width);
+// CHECK4-C: (RectangleInStruct *rs) {\ntakesPtr(&(rs)->r.width);\n}
+// CHECK4-CPP: (RectangleInStruct &rs) {\ntakesPtr(&(rs).r.width);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:71:3-71:21 -selected=%s:74:3-74:26 %s -x c | FileCheck --check-prefix=CHECK4-C %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:71:3-71:21 -selected=%s:74:3-74:26 %s | FileCheck --check-prefix=CHECK4-CPP %s
+
+void takesConstPtr(const int *x) { }
+
+#ifdef __cplusplus
+
+void addressOfMember(const Rectangle &r) {
+ takesConstPtr(&r.width);
+// CHECK5: (const Rectangle &r) {\ntakesConstPtr(&r.width);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:87:3-87:26 %s | FileCheck --check-prefix=CHECK5 %s
+
+class PrivateInstanceVariables {
+ int x;
+ Rectangle r;
+
+ void method() {
+ takesPtr(&x);
+// CHECK6: extracted(int &x) {\ntakesPtr(&x);\n}
+ takesStructPtr(&(r));
+// CHECK6: extracted(Rectangle &r) {\ntakesStructPtr(&(r));\n}
+ takesPtr(&((r).width));
+// CHECK6: extracted(Rectangle &r) {\ntakesPtr(&((r).width));\n}
+ }
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:98:5-98:17 -selected=%s:100:5-100:25 -selected=%s:102:5-102:27 %s | FileCheck --check-prefix=CHECK6 %s
+
+#endif
+
+void takesCVoidPtr(const void *x) { }
+void takesCPtr(const int *x) { }
+void takesCPtrPtr(int * const *x) { }
+void takesBothPtrs(const int *x, int *y) {}
+void addressForConstUseShouldPassAsConst(int x, int *ip) {
+ takesCPtr(&x);
+// CHECK7-C: (const int *x) {\ntakesCPtr(x);\n}
+// CHECK7-CPP: (const int &x) {\ntakesCPtr(&x);\n}
+ takesCVoidPtr((&(x)));
+// CHECK7-C: (const int *x) {\ntakesCVoidPtr(((x)));\n}
+// CHECK7-CPP: (const int &x) {\ntakesCVoidPtr((&(x)));\n}
+ takesCPtrPtr(&ip);
+// CHECK7-C: (int *const *ip) {\ntakesCPtrPtr(ip);\n}
+// CHECK7-CPP: (int *const &ip) {\ntakesCPtrPtr(&ip);\n}
+ takesCPtr(ip);
+// CHECK7-C: (int *ip) {\ntakesCPtr(ip);\n}
+// CHECK7-CPP: (int *ip) {\ntakesCPtr(ip);\n}
+ takesBothPtrs(&x, &x);
+// CHECK7-C: (int *x) {\ntakesBothPtrs(x, x);\n}
+// CHECK7-CPP: (int &x) {\ntakesBothPtrs(&x, &x);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:116:3-116:16 -selected=%s:119:3-119:22 -selected=%s:122:3-122:20 -selected=%s:125:3-125:16 -selected=%s:128:3-128:24 %s -x c | FileCheck --check-prefix=CHECK7-C %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:116:3-116:16 -selected=%s:119:3-119:22 -selected=%s:122:3-122:20 -selected=%s:125:3-125:16 -selected=%s:128:3-128:24 %s | FileCheck --check-prefix=CHECK7-CPP %s
+
+void addressOfConstUseAndMutation(int x) {
+ x = 0;
+ takesCPtr(&x);
+ x = 1;
+}
+// CHECK8: extracted(int &x) {\nx = 0;\n takesCPtr(&x);\n}
+// CHECK8: extracted(int &x) {\ntakesCPtr(&x);\n x = 1;\n}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:137:3-138:16 -selected=%s:138:3-139:8 %s | FileCheck --check-prefix=CHECK8 %s
+
+void takesCStructPtr(const Rectangle *r) { }
+
+void constAddressOfMember(Rectangle r, RectangleInStruct rs) {
+ takesCStructPtr(&r);
+// CHECK9-C: extracted(const Rectangle *r) {\ntakesCStructPtr(r);\n}
+// CHECK9-CPP: extracted(const Rectangle &r) {\ntakesCStructPtr(&r);\n}
+ takesCPtr(&r.width);
+// CHECK9-C: (const Rectangle *r) {\ntakesCPtr(&r->width);\n}
+// CHECK9-CPP: (const Rectangle &r) {\ntakesCPtr(&r.width);\n}
+ takesCPtr((&(rs).r.height));
+// CHECK9-C: (const RectangleInStruct *rs) {\ntakesCPtr((&(rs)->r.height));\n}
+// CHECK9-CPP: (const RectangleInStruct &rs) {\ntakesCPtr((&(rs).r.height));\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:149:3-149:22 -selected=%s:152:3-152:22 -selected=%s:155:3-155:30 %s -x c | FileCheck --check-prefix=CHECK9-C %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:149:3-149:22 -selected=%s:152:3-152:22 -selected=%s:155:3-155:30 %s | FileCheck --check-prefix=CHECK9-CPP %s
+
+#ifdef __cplusplus
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:87:3-87:26 %s | FileCheck --check-prefix=CHECK5 %s
+
+class PrivateInstanceVariablesConstAddress {
+ int x;
+ Rectangle r;
+
+ void method() {
+ takesCPtr(&x);
+// CHECK10: extracted(const int &x) {\ntakesCPtr(&x);\n}
+ takesCStructPtr(&r);
+// CHECK10: extracted(const Rectangle &r) {\ntakesCStructPtr(&r);\n}
+ }
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:172:5-172:18 -selected=%s:174:5-174:24 %s | FileCheck --check-prefix=CHECK10 %s
+
+#endif
+
+void rewriteToPtrWithDerefParensForArrow(Rectangle *r) {
+ int y = r->width;
+ r = 0;
+// CHECK11: (Rectangle **r) {\nint y = (*r)->width;\n *r = 0;\n}\n\n"
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:184:3-185:8 %s -x c | FileCheck --check-prefix=CHECK11 %s
+
+void constAddressWithConditionalOperator(int x, int y) {
+ takesCPtr(&(x == 0 ? x : y));
+// CHECK12: (const int &x, const int &y) {\ntakesCPtr(&(x == 0 ? x : y));\n}
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:191:3-191:31 %s | FileCheck --check-prefix=CHECK12 %s
diff --git a/test/Refactor/Extract/extract-address-of-captured-variable.mm b/test/Refactor/Extract/extract-address-of-captured-variable.mm
new file mode 100644
index 0000000..9d6661c
--- /dev/null
+++ b/test/Refactor/Extract/extract-address-of-captured-variable.mm
@@ -0,0 +1,44 @@
+
+void takesVoidPtr(void *x) { }
+void takesPtr(int *x) { }
+void takesPtrPtr(int **x) { }
+
+#ifdef USECONST
+#define CONST const
+#else
+#define CONST
+#endif
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+@interface I
+
+- (int)takesPtr:(CONST int *)x;
++ (int)takesPtr:(CONST int *)x;
+- (int)takesVoidPtr:(CONST void *)x;
+- (int)takesStructPtr:(CONST Rectangle *)r;
+
+@end
+
+void methodTakesPtr(I *i, int x, Rectangle r) {
+ [i takesPtr: &x];
+// CHECK1: extracted(I *i, int &x) {\nreturn [i takesPtr: &x];\n}
+// CHECK2: extracted(I *i, const int &x) {\nreturn [i takesPtr: &x];\n}
+ [I takesPtr: (&x)];
+// CHECK1: extracted(int &x) {\nreturn [I takesPtr: (&x)];\n}
+// CHECK2: extracted(const int &x) {\nreturn [I takesPtr: (&x)];\n}
+ [i takesVoidPtr: (&(x))];
+// CHECK1: extracted(I *i, int &x) {\nreturn [i takesVoidPtr: (&(x))];\n}
+// CHECK2: extracted(I *i, const int &x) {\nreturn [i takesVoidPtr: (&(x))];\n}
+ [i takesStructPtr: &r];
+// CHECK1: extracted(I *i, Rectangle &r) {\nreturn [i takesStructPtr: &r];\n}
+// CHECK2: extracted(I *i, const Rectangle &r) {\nreturn [i takesStructPtr: &r];\n}
+ [I takesPtr: &(r).width];
+// CHECK1: extracted(Rectangle &r) {\nreturn [I takesPtr: &(r).width];\n}
+// CHECK2: extracted(const Rectangle &r) {\nreturn [I takesPtr: &(r).width];\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:26:3-26:19 -selected=%s:29:3-29:21 -selected=%s:32:3-32:27 -selected=%s:35:3-35:25 -selected=%s:38:3-38:27 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:26:3-26:19 -selected=%s:29:3-29:21 -selected=%s:32:3-32:27 -selected=%s:35:3-35:25 -selected=%s:38:3-38:27 %s -DUSECONST | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/extract-before-comments.cpp b/test/Refactor/Extract/extract-before-comments.cpp
new file mode 100644
index 0000000..f20e0ab
--- /dev/null
+++ b/test/Refactor/Extract/extract-before-comments.cpp
@@ -0,0 +1,64 @@
+
+// comment 1
+void extractBeforeComment1(int x) {
+ int y = x * x;
+}
+// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-4]]:1
+
+/* comment 2 */
+
+void extractBeforeComment2(int x) {
+ int y = x * x;
+}
+// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-5]]:1
+
+/// comment 1
+///
+/// line 2
+void extractBeforeDocComment1(int x) {
+ int y = x * x;
+}
+// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-6]]:1
+
+/**
+ * @brief extractBeforeDocComment2
+ * @param x
+ */
+void extractBeforeDocComment2(int x) {
+ int y = x * x;
+}
+// CHECK1: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-7]]:1
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:4:11-4:16 -selected=%s:11:11-11:16 -selected=%s:19:11-19:16 -selected=%s:28:11-28:16 %s | FileCheck --check-prefix=CHECK1 %s
+
+/**
+ * @brief The AClass class
+ */
+class AClass {
+
+ /// doc comment
+ int method(int x) {
+ return x * x;
+ }
+// CHECK2: "static int extracted(int x) {\nreturn x * x;\n}\n\n" [[@LINE-9]]:1
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:41:12-41:17 %s | FileCheck --check-prefix=CHECK2 %s
+
+namespace {
+
+} // end anonymous namespace
+
+void afterBraceAfterComment() { // CHECK3: "static void extracted() {\nint x = 0;\n}\n\n" [[@LINE]]:1
+// after-brace-begin: +1:1
+ int x = 0;
+// after-brace-end: +0:1
+} // another trailing
+// This is valid CHECK3: "static void extracted() {\nint x = 0;\n}\n\n" [[@LINE]]:1
+void inbetweenerTwoComments() {
+// inbetween-begin: +1:1
+ int x = 0;
+// inbetween-end: +0:1
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=after-brace -selected=inbetween %s | FileCheck --check-prefix=CHECK3 %s
diff --git a/test/Refactor/Extract/extract-capture-instance-variable.cpp b/test/Refactor/Extract/extract-capture-instance-variable.cpp
new file mode 100644
index 0000000..b98bc0a
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-instance-variable.cpp
@@ -0,0 +1,73 @@
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+class PrivateInstanceVariables {
+ int x;
+ Rectangle r;
+
+ int method() {
+ int y = x;
+ return r.width + r.height * x + y;
+ }
+// CHECK1: (int x) {\nint y = x;\nreturn y;\n}
+// CHECK1-NEXT: extracted(x)
+// CHECK1: extracted(const Rectangle &r, int x) {\nint y = x;\n return r.width + r.height * x + y;\n}
+// CHECK1-NEXT: extracted(r, x)
+// CHECK1: extracted(const Rectangle &r, int x, int y) {\nreturn r.width + r.height * x + y;\n}
+// CHECK1-NEXT: extracted(r, x, y)
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:5-11:14 -selected=%s:11:5-12:38 -selected=%s:12:12-12:38 %s | FileCheck --check-prefix=CHECK1 %s
+
+class PrivateInstanceVariablesExplicitThis {
+ int x;
+
+
+ int method(int y) {
+ y = this->x;
+ {
+ int x = (this)->x;
+ }
+ y = x;
+ x = 0;
+ this->x = 0;
+ y = (y == 0 ? this : (PrivateInstanceVariablesExplicitThis *)0) -> x;
+ }
+// CHECK2: (const PrivateInstanceVariablesExplicitThis &object, int &y) {\ny = object.x;\n {\n int x = (object).x;\n }\n y = object.x;\n}
+// CHECK2-NEXT: extracted(*this, y)
+// CHECK2: extracted(const PrivateInstanceVariablesExplicitThis &object) {\nint x = (object).x;\n}
+// CHECK2-NEXT: extracted(*this)
+// CHECK2: (PrivateInstanceVariablesExplicitThis &object, int &y) {\ny = object.x;\n {\n int x = (object).x;\n }\n y = object.x;\n object.x = 0;\n}
+// CHECK2: (PrivateInstanceVariablesExplicitThis &object) {\nobject.x = 0;\n}
+// CHECK2: (PrivateInstanceVariablesExplicitThis &object, int &y) {\ny = (y == 0 ? &object : (PrivateInstanceVariablesExplicitThis *)0) -> x;\n}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:29:5-33:10 -selected=%s:31:7-31:24 -selected=%s:29:5-34:10 -selected=%s:35:5-35:16 -selected=%s:36:5-36:73 %s | FileCheck --check-prefix=CHECK2 %s
+};
+
+class PublicInstanceVariables { int private_;
+public:
+ int x;
+ Rectangle r;
+
+ void method() {
+ int y; y = x;
+ x = 0;
+ int z = r.width + r.height * x + y;
+ this->x = (this)->r.height + (y == 0 ? this : (PublicInstanceVariables *)0) -> x;
+ x = private_;
+ }
+// CHECK3: (const PublicInstanceVariables &object, int &y) {\ny = object.x;\n}
+// CHECK3: (PublicInstanceVariables &object, int &y) {\ny = object.x;\n object.x = 0;\n}
+// CHECK3: (PublicInstanceVariables &object) {\nobject.x = 0;\n}
+// CHECK3: (const PublicInstanceVariables &object, int y) {\nint z = object.r.width + object.r.height * object.x + y;\n}
+// CHECK3: (PublicInstanceVariables &object, int y) {\nobject.x = (object).r.height + (y == 0 ? &object : (PublicInstanceVariables *)0) -> x;\n}
+// CHECK3: (PublicInstanceVariables &object, int private_) {\nobject.x = object.private_;\n}
+ void constMethod() const {
+ const_cast<PublicInstanceVariables *>(this)->x = 2;
+ }
+// CHECK3: (const PublicInstanceVariables &object) {\nconst_cast<PublicInstanceVariables *>(&object)->x = 2;\n}
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:55:12-55:17 -selected=%s:55:12-56:10 -selected=%s:56:5-56:10 -selected=%s:57:5-57:39 -selected=%s:58:5-58:85 -selected=%s:59:5-59:17 -selected=%s:68:5-68:55 %s | FileCheck --check-prefix=CHECK3 %s
diff --git a/test/Refactor/Extract/extract-capture-self.m b/test/Refactor/Extract/extract-capture-self.m
new file mode 100644
index 0000000..4f8375a
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-self.m
@@ -0,0 +1,56 @@
+
+@interface AClass {
+ int ivar1;
+}
+
+@property int prop;
+
+- (int)instanceMethod;
++ (int)classMethod;
+
+@end
+
+@implementation AClass {
+ int ivar2;
+}
+
+- (int)instanceMethod {
+ ivar2 = 0;
+ ivar1 = 0;
+ self->ivar2 = 0;
+ self.prop = 0;
+ int x = self->ivar1;
+ int y = self.prop;
+ [self instanceMethod];
+ [AClass classMethod];
+ return 0;
+}
+// CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n}
+// CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n}
+// CHECK1: (AClass *object) {\nobject.prop = 0;\n}\n\n"
+// CHECK1: (AClass *object) {\nint y = object.prop;\n}
+// CHECK1: (AClass *object) {\nreturn [object instanceMethod];\n}
+// CHECK1: (AClass *object) {\nobject->ivar2 = 0;\n object->ivar1 = 0;\n object->ivar2 = 0;\n object.prop = 0;\n int x = object->ivar1;\n int y = object.prop;\n [object instanceMethod];\n [AClass classMethod];\n}\n\n"
+// CHECK1: () {\nreturn [AClass classMethod];\n}\n\n"
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:18:3-18:12 -selected=%s:20:3-20:18 -selected=%s:21:3-21:16 -selected=%s:23:3-23:20 -selected=%s:24:3-24:24 -selected=%s:18:3-25:23 -selected=%s:25:3-25:23 %s | FileCheck --check-prefix=CHECK1 %s
+
++ (int)classMethod {
+ int x = self.classMethod;
+ [self classMethod];
+}
+
+// CHECK2: () {\nint x = AClass.classMethod;\n}
+// CHECK2: () {\nreturn [AClass classMethod];\n}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:39:3-39:27 -selected=%s:40:3-40:21 %s | FileCheck --check-prefix=CHECK2 %s
+
+- (void)rhsSelfCaptureAndRewrite:(AClass *)i { // CHECK3: "static void extracted(AClass *object, AClass *i) {\ni.prop= object.prop;\n}\n\n"
+// rhs-prop-begin: +1:3
+ i.prop= self.prop;
+// rhs-prop-end: -1:21
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=rhs-prop %s | FileCheck --check-prefix=CHECK3 %s
+
+@end
diff --git a/test/Refactor/Extract/extract-capture-static-var.cpp b/test/Refactor/Extract/extract-capture-static-var.cpp
new file mode 100644
index 0000000..097e176
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-static-var.cpp
@@ -0,0 +1,12 @@
+
+
+void captureStaticVars() {
+ static int x;
+ int y = x;
+ x += 1;
+// CHECK1: extracted(int x) {\nint y = x;\n}\n\n"
+// CHECK1: extracted(int &x) {\nx += 1;\n}
+// CHECK1: extracted() {\nstatic int x;\n int y = x;\n x += 1;\n}\n\n"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:5:3-5:12 -selected=%s:6:3-6:9 -selected=%s:4:3-6:9 %s | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/Refactor/Extract/extract-capture-super.m b/test/Refactor/Extract/extract-capture-super.m
new file mode 100644
index 0000000..0b18143
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-super.m
@@ -0,0 +1,42 @@
+// RUN: clang-refactor-test perform -action extract -selected=super-msg -selected=super-prop -selected=super-self -selected=super-class-msg -selected=super-class-prop %s | FileCheck %s
+
+@interface BaseClass
+
+@property int prop;
+
+- (int)instanceMethod;
++ (void)classMethod;
+
+@property(class) int classProp;
+
+@end
+
+@interface SubClass: BaseClass
+
+@end
+
+@implementation SubClass
+
+- (void)method {
+ // super-msg-begin: +1:1
+ [super instanceMethod]; // CHECK: extracted(BaseClass *superObject) {\n[superObject instanceMethod];\n}
+ // super-msg-end: +0:1 // CHECK: extracted(super.self)
+ // super-prop-begin: +1:11
+ int x = super.prop; // CHECK: extracted(BaseClass *superObject) {\nreturn superObject.prop;\n}
+ // super-prop-end: -1:21 // CHECK: extracted(super.self)
+ // super-self-begin: +1:1
+ int y = self.prop; // CHECK: extracted(SubClass *object, BaseClass *superObject) {\nint y = object.prop;\n int z = superObject.prop;\n}
+ int z = super.prop; // CHECK: extracted(self, super.self);
+ // super-self-end: +0:1
+}
+
++ (void)classMethod {
+ // super-class-msg-begin: +1:1
+ [super classMethod]; // CHECK: extracted() {\n[BaseClass classMethod];\n}
+ // super-class-msg-end: +0:1 // CHECK: extracted()
+ // super-class-prop-begin: +1:9
+ (void)super.classProp; // CHECK: extracted() {\nreturn BaseClass.classProp;\n}
+ // super-class-prop-end: -1:24 // CHECK: extracted()
+}
+
+@end
diff --git a/test/Refactor/Extract/extract-capture-this.cpp b/test/Refactor/Extract/extract-capture-this.cpp
new file mode 100644
index 0000000..cb15a5e
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-this.cpp
@@ -0,0 +1,66 @@
+
+class AClassWithMethods {
+ void method() {
+ }
+ void constMethod() const {
+ }
+ int operator << (int x) const { return x; }
+ int operator >> (int x) { return x; }
+
+ void toBeExtracted() {
+ method();
+ constMethod();
+ *this << 2;
+ *(this) >> 2;
+ this->constMethod();
+ (*(this)).constMethod();
+ }
+// CHECK1: (AClassWithMethods &object) {\nobject.method();\n}
+// CHECK1: (const AClassWithMethods &object) {\nobject.constMethod();\n}
+// CHECK1: (AClassWithMethods &object) {\nobject.method();\n object.constMethod();\n}
+// CHECK1: (const AClassWithMethods &object) {\nreturn object << 2;\n}
+// CHECK1: (AClassWithMethods &object) {\nreturn (object) >> 2;\n}
+// CHECK1: (const AClassWithMethods &object) {\nobject.constMethod();\n}
+// CHECK1: (const AClassWithMethods &object) {\n((object)).constMethod();\n}
+
+ void toBeExtracted2();
+};
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:5-11:13 -selected=%s:12:5-12:17 -selected=%s:11:5-12:17 -selected=%s:13:5-13:14 -selected=%s:14:5-14:16 -selected=%s:15:5-15:24 -selected=%s:16:5-16:28 %s | FileCheck --check-prefix=CHECK1 %s
+
+void takesRef(AClassWithMethods &object) {}
+void takesConstRef(const AClassWithMethods &object) {}
+void takesPtr(AClassWithMethods *object) {}
+void takesConstPtr(const AClassWithMethods *object) {}
+
+void AClassWithMethods::toBeExtracted2() {
+ takesRef(*this);
+ takesConstRef((*(this)));
+ takesPtr(this);
+ takesConstPtr((this));
+ takesConstPtr(false ? this : (AClassWithMethods*)0);
+}
+// CHECK2: (AClassWithMethods &object) {\ntakesRef(object);\n}
+// CHECK2: (const AClassWithMethods &object) {\ntakesConstRef(((object)));\n}
+// CHECK2: (AClassWithMethods &object) {\ntakesPtr(&object);\n}
+// CHECK2: (const AClassWithMethods &object) {\ntakesConstPtr((&object));\n}
+// CHECK2: (AClassWithMethods &object) {\ntakesConstPtr(false ? &object : (AClassWithMethods*)0);\n}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:36:3-36:18 -selected=%s:37:3-37:27 -selected=%s:38:3-38:17 -selected=%s:39:3-39:24 -selected=%s:40:3-40:54 %s | FileCheck --check-prefix=CHECK2 %s
+
+#ifdef USECONST
+#define CONST const
+#else
+#define CONST
+#endif
+
+class FallbackToMethodConstness {
+ int getter() const { return 0; }
+ int method(int x, FallbackToMethodConstness *other) CONST {
+ return (x == 0 ? this : other)->getter();
+ }
+// CHECK3: (FallbackToMethodConstness &object, FallbackToMethodConstness *other, int x) {\nreturn (x == 0 ? &object : other)->getter();\n}
+// CHECK3-CONST: (const FallbackToMethodConstness &object, FallbackToMethodConstness *other, int x) {\nreturn (x == 0 ? &object : other)->getter();\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:5-59:45 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:5-59:45 %s -DUSECONST | FileCheck --check-prefix=CHECK3-CONST %s
diff --git a/test/Refactor/Extract/extract-capture-used-after-extraction.cpp b/test/Refactor/Extract/extract-capture-used-after-extraction.cpp
new file mode 100644
index 0000000..03fff5c
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-used-after-extraction.cpp
@@ -0,0 +1,214 @@
+
+void takesInt(int x);
+
+typedef struct { int width; int height; } Rectangle;
+
+#ifdef USEINIT1
+#define INIT1 r.width = 0; { int r, y; }
+#else
+#define INIT1 { int r, y; }
+#endif
+
+void extractCaptureUsedAfterSimple(int x) {
+ Rectangle r;
+ INIT1;
+ int y = x * x;
+#ifdef USEAFTER1
+ takesInt(y);
+#endif
+#ifdef USEAFTER2
+ takesInt(r.height);
+#endif
+#ifdef USEAFTER3
+ r.width = 0;
+#endif
+#ifdef USEAFTER4
+ y += 1;
+#endif
+}
+// CHECK1: "static void extracted(int x) {\nRectangle r;\n INIT1;\n int y = x * x;\n}\n\n"
+// CHECK1-NEXT: "extracted(x);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s | FileCheck --check-prefix=CHECK1 %s
+
+// CHECK2: "static void extracted(Rectangle &r, int x, int &y) {\n\n INIT1;\n y = x * x;\n}\n\n"
+// CHECK2-NEXT: "Rectangle r;\nint y;\nextracted(r, x, y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER1 -DUSEAFTER2 | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER1 -DUSEAFTER2 -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK2 %s
+
+// CHECK3: "static void extracted(int x, int &y) {\n\n INIT1;\n y = x * x;\n}\n\n"
+// CHECK3-NEXT: "Rectangle r;\nint y;\nextracted(x, y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER1 -DUSEAFTER2 | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER1 -DUSEAFTER2 -DUSEAFTER3 -DUSEAFTER4 | FileCheck --check-prefix=CHECK3 %s
+
+// CHECK4: "static void extracted(int x, int &y) {\nRectangle r;\n INIT1;\n y = x * x;\n}\n\n"
+// CHECK4-NEXT: "int y;\nextracted(x, y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER1 | FileCheck --check-prefix=CHECK4 %s
+
+// CHECK5: "static void extracted(Rectangle &r, int x) {\n\n INIT1;\n int y = x * x;\n}\n\n"
+// CHECK5-NEXT: "Rectangle r;\nextracted(r, x);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEINIT1 -DUSEAFTER3 | FileCheck --check-prefix=CHECK5 %s
+
+// CHECK6: "static void extracted(int x) {\n\n INIT1;\n int y = x * x;\n}\n\n"
+// CHECK6-NEXT: "Rectangle r;\nextracted(x);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:13:3-15:16 %s -DUSEAFTER3 | FileCheck --check-prefix=CHECK6 %s
+
+void extractCaptureAfterUseMultipleDecls() {
+#ifdef MULTIPLE_DECL1
+ int x = 1, y = 2, z = 3; { int x, y, z; };
+#endif
+#ifdef MULTIPLE_DECL2
+ int x, y = 2, *z; { int y; }
+#endif
+#ifdef MULTIPLE_DECL3
+ int x = 1, y, * z = 0, a, b = {0}; { int a; }
+#endif
+#ifdef USEX
+ x;
+#endif
+#ifdef USEY
+ y;
+#endif
+#ifdef USEZ
+ z;
+#endif
+#ifdef USEA
+ a;
+#endif
+#ifdef USEB
+ b;
+#endif
+}
+
+// CHECK7: "static void extracted() {\nint x = 1, y = 2, z = 3;\n}\n\n"
+// CHECK7-NEXT; "extracted();"
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:26 %s -DMULTIPLE_DECL1 | FileCheck --check-prefix=CHECK7 %s
+// CHECK8: "static void extracted(int &x, int &y, int &z) {\nx = 1; y = 2; z = 3;\n}\n\n"
+// CHECK8-NEXT: "int x;\nint y;\nint z;\nextracted(x, y, z);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:26 %s -DMULTIPLE_DECL1 -DUSEX -DUSEY -DUSEZ | FileCheck --check-prefix=CHECK8 %s
+// CHECK9: "static void extracted(int &x) {\nx = 1; int y = 2; int z = 3; { int x, y, z; }\n}\n\n"
+// CHECK9-NEXT: "int x;\nextracted(x);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:44 %s -DMULTIPLE_DECL1 -DUSEX | FileCheck --check-prefix=CHECK9 %s
+// CHECK10: "static void extracted(int &y) {\nint x = 1; y = 2; int z = 3; { int x, y, z; }\n}\n\n"
+// CHECK10-NEXT: "int y;\nextracted(y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:44 %s -DMULTIPLE_DECL1 -DUSEY | FileCheck --check-prefix=CHECK10 %s
+// CHECK11: "static void extracted(int &z) {\nint x = 1; int y = 2; z = 3; { int x, y, z; }\n}\n\n"
+// CHECK11-NEXT: "int z;\nextracted(z);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:59:3-59:44 %s -DMULTIPLE_DECL1 -DUSEZ | FileCheck --check-prefix=CHECK11 %s
+
+// CHECK12: "static void extracted() {\nint x, y = 2, *z;\n}\n\n"
+// CHECK12-NEXT: "extracted();"
+// RUN: clang-refactor-test perform -action extract -selected=%s:62:3-62:19 %s -DMULTIPLE_DECL2 | FileCheck --check-prefix=CHECK12 %s
+// CHECK13: "static void extracted(int &y) {\ny = 2;\n}\n\n"
+// CHECK13-NEXT: "int x;\nint y;\nint * z;\nextracted(y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:62:3-62:19 %s -DMULTIPLE_DECL2 -DUSEX -DUSEY -DUSEZ | FileCheck --check-prefix=CHECK13 %s
+// CHECK14: "static void extracted(int &y) {\nint x; y = 2; int * z; { int y; }\n}\n\n"
+// CHECK14-NEXT: "int y;\nextracted(y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:62:3-62:31 %s -DMULTIPLE_DECL2 -DUSEY | FileCheck --check-prefix=CHECK14 %s
+
+// CHECK15: "static void extracted() {\nint x = 1, y, * z = 0, a, b = {0};\n}\n\n"
+// CHECK15-NEXT: "extracted();"
+// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:36 %s -DMULTIPLE_DECL3 | FileCheck --check-prefix=CHECK15 %s
+// CHECK16: "static void extracted(int &x, int *&z) {\nx = 1; z = 0; int a; int b = {0};\n}\n\n"
+// CHECK16-NEXT: "int x;\nint y;\nint * z;\nextracted(x, z);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:36 %s -DMULTIPLE_DECL3 -DUSEX -DUSEY -DUSEZ | FileCheck --check-prefix=CHECK16 %s
+// CHECK17: "static void extracted(int &b, int &x, int *&z) {\nx = 1; z = 0; b = {0};\n}\n\n"
+// CHECK17-NEXT: "int x;\nint y;\nint * z;\nint a;\nint b;\nextracted(b, x, z);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:36 %s -DMULTIPLE_DECL3 -DUSEX -DUSEY -DUSEZ -DUSEA -DUSEB | FileCheck --check-prefix=CHECK17 %s
+// CHECK18: "static void extracted() {\nint x = 1; int y; int * z = 0; int b = {0}; { int a; }\n}\n\n"
+// CHECK18-NEXT: "int a;\nextracted();"
+// RUN: clang-refactor-test perform -action extract -selected=%s:65:3-65:48 %s -DMULTIPLE_DECL3 -DUSEA | FileCheck --check-prefix=CHECK18 %s
+
+#define ONE 1
+
+void preserveInitExpressionText() {
+ int a = ONE;
+ int x = ONE, y = ONE;
+ a, y;
+}
+
+// CHECK19: "static void extracted(int &a, int &y) {\na = ONE;\n int x = ONE; y = ONE;\n}\n\n
+// CHECK19-NEXT: "int a;\nint y;\nextracted(a, y);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:126:3-127:23 %s | FileCheck --check-prefix=CHECK19 %s
+
+class Construct {
+public:
+ Construct();
+ Construct(int x, int y);
+};
+
+void handleConstruct() {
+ Construct a(1, 2);
+ Construct b(3, 4), c(5, 6);
+ a, c;
+}
+// CHECK20: "static void extracted(Construct &a, Construct &c) {\na = Construct(1, 2);\n Construct b(3, 4); c = Construct(5, 6);\n}\n\n"
+// CHECK20-NEXT: "Construct a;\nConstruct c;\nextracted(a, c);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:142:3-143:29 %s | FileCheck --check-prefix=CHECK20 %s
+
+struct Construct2 {
+ int x, y;
+};
+
+void handleConstruct2() {
+ Construct2 a = {1, 2};
+ Construct2 b = {3, 4}, c = {5, 6};
+ a, c;
+}
+// CHECK21: "static void extracted(Construct2 &a, Construct2 &c) {\na = {1, 2};\n Construct2 b = {3, 4}; c = {5, 6};\n}\n\n"
+// CHECK21-NEXT: "Construct2 a;\nConstruct2 c;\nextracted(a, c);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:155:3-156:29 %s -std=c++11 | FileCheck --check-prefix=CHECK21 %s
+
+class Construct3 {
+public:
+ Construct3();
+ Construct3(int x);
+};
+
+void handleConstruct3() {
+ Construct3 a = 1;
+ Construct3 b = 2, c = 3 + 3;
+ a, c;
+}
+// CHECK22: "static void extracted(Construct3 &a, Construct3 &c) {\na = 1;\n Construct3 b = 2; c = 3 + 3;\n}\n\n"
+// CHECK22-NEXT: "Construct3 a;\nConstruct3 c;\nextracted(a, c);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:170:3-171:26 %s -std=c++11 | FileCheck --check-prefix=CHECK22 %s
+
+void handleConstruct2InitList() {
+ Construct2 a { 5, 6 };
+ Construct2 b { 1, 2 }, c { 3, 4 };
+ a, c;
+}
+// CHECK23: "static void extracted(Construct2 &a, Construct2 &c) {\na = { 5, 6 };\n Construct2 b = { 1, 2 }; c = { 3, 4 };\n}\n\n"
+// CHECK23-NEXT: "Construct2 a;\nConstruct2 c;\nextracted(a, c);"
+// RUN: clang-refactor-test perform -action extract -selected=%s:179:3-180:36 %s -std=c++11 | FileCheck --check-prefix=CHECK23 %s
+
+void onlyOneUsedAfterExtractionIsReturned() {
+ const int x = 0;
+ x;
+ Construct a(1, 2);
+ a;
+}
+// CHECK24: "static int extracted() {\nconst int x = 0;\nreturn x;\n}\n\n"
+// CHECK24-NEXT: "const int x = extracted();"
+// CHECK24: "static Construct extracted() {\nConstruct a(1, 2);\nreturn a;\n}\n\n"
+// CHECK24-NEXT: "Construct a = extracted();"
+
+int avoidReturningWhenReturnUsed() {
+ int x = 0;
+ if (x != 0) { return 22; }
+ x;
+}
+// CHECK24: "static int extracted(int &x) {\nx = 0;\n if (x != 0) { return 22; }\n}\n\n"
+// CHECK24-NEXT: "int x;\nextracted(x);"
+
+void returnOnlyWhenReturnReturnsNothing() {
+ int x = 0;
+ if (x != 0) { return; }
+ x;
+}
+// CHECK24: "static int extracted() {\nint x = 0;\n if (x != 0) { return x; }\nreturn x;\n}\n\n"
+// CHECK24-NEXT: "int x = extracted();"
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:188:3-188:18 -selected=%s:190:3-190:20 -selected=%s:199:3-200:29 -selected=%s:207:3-208:26 %s -std=c++11 | FileCheck --check-prefix=CHECK24 %s
diff --git a/test/Refactor/Extract/extract-capture-used-after-extraction.m b/test/Refactor/Extract/extract-capture-used-after-extraction.m
new file mode 100644
index 0000000..c115bab
--- /dev/null
+++ b/test/Refactor/Extract/extract-capture-used-after-extraction.m
@@ -0,0 +1,25 @@
+void takesInt(int);
+void passByPointer() {
+ int x = 1;
+ int y = 0, z = 2;
+ takesInt(x);
+ z = 2;
+ x, z;
+}
+// CHECK1: "static void extracted(int *x, int *z) {\n*x = 1;\n int y = 0; *z = 2;\n takesInt(*x);\n *z = 2;\n}\n\n"
+// CHECK1-NEXT: "int x;\nint z;\nextracted(&x, &z)"
+// RUN: clang-refactor-test perform -action extract -selected=%s:3:3-6:8 %s | FileCheck --check-prefix=CHECK1 %s
+
+typedef struct { int width; int height; } Rectangle;
+
+void handleStructInit() {
+ Rectangle a = { 5, 6 };
+ Rectangle b = { 1, 2 }, c = { 3, 4 };
+ takesInt(a.width);
+ c.height = 10;
+ a, c;
+}
+// The produced code is invalid but we can let the user deal with it:
+// CHECK2: "static void extracted(Rectangle *a, Rectangle *c) {\n*a = { 5, 6 };\n Rectangle b = { 1, 2 }; *c = { 3, 4 };\n takesInt(a->width);\n c->height = 10;\n}\n\n" 15:1 -> 15:1
+// CHECK2: "Rectangle a;\nRectangle c;\nextracted(&a, &c)"
+// RUN: clang-refactor-test perform -action extract -selected=%s:16:3-19:16 %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/extract-expression-into-var.cpp b/test/Refactor/Extract/extract-expression-into-var.cpp
new file mode 100644
index 0000000..9fd4f7a
--- /dev/null
+++ b/test/Refactor/Extract/extract-expression-into-var.cpp
@@ -0,0 +1,47 @@
+int call(int x);
+
+void extractExpressionIntoVar(int x, int y, int z) {
+ {
+ // expr1-begin: +1:13
+ int p = x + y - z; // CHECK: "int extractedExpr = x + y;\n" [[@LINE]]:5 -> [[@LINE]]:5 [Symbol extracted-decl 0 1:5 -> 1:18]
+ // expr1-end: -1:19 // CHECK: "extractedExpr" [[@LINE-1]]:13 -> [[@LINE-1]]:18 [Symbol extracted-decl-ref 0 1:1 -> 1:14]
+ // expr2-begin: +1:6 // CHECK: "std::function<void (int)> extractedExpr = [&](int x) {\n p = x;\n };\n" [[@LINE+1]]:5 -> [[@LINE+1]]:5 [Symbol extracted-decl 0 1:27 -> 1:40]
+ ([&](int x) {
+ p = x;
+ })(0);
+ // expr2-end: -1:6 // CHECK: "extractedExpr" [[@LINE-3]]:6 -> [[@LINE-1]]:6
+ }
+ #define MACROARG(x) (x)
+ // expr3-begin: +1:20
+ int p = MACROARG(y + z) - x; // CHECK: "int extractedExpr = y + z;\n" [[@LINE]]:3 -> [[@LINE]]:3
+ // expr3-end: -1:25 // CHECK: "extractedExpr" [[@LINE-1]]:20 -> [[@LINE-1]]:25
+ #define MACROSTMT(x, y) int var = (x) + (y);
+ // expr4-begin: +1:16
+ MACROSTMT(0, call(p * z)) // CHECK: "int extractedExpr = call(p * z);\n" [[@LINE]]:3 -> [[@LINE]]:3
+ // expr4-end: -1:27 // CHECK: "extractedExpr" [[@LINE-1]]:16 -> [[@LINE-1]]:27
+}
+
+// RUN: clang-refactor-test perform -action extract-expression -selected=expr1 -selected=expr2 -selected=expr3 -selected=expr4 -emit-associated %s -std=c++11 | FileCheck %s
+
+// RUN: clang-refactor-test list-actions -at=%s:6:13 -selected=%s:6:13-6:18 %s -std=c++11 | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Extract Expression
+
+void dontExtractStatement(int x, int y) {
+ // stmt1-begin: +1:3
+ x = y;
+ // stmt1-end: -1:8
+ // stmt2-begin: +1:3
+ return;
+ // stmt2-end: +0:1
+}
+
+// RUN: not clang-refactor-test perform -action extract-expression -selected=stmt1 -selected=stmt2 %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-FAIL %s
+// CHECK-FAIL: Failed to initiate the refactoring action!
+
+void dontExtractVoidCall() {
+ // void-call-begin: +1:3
+ dontExtractVoidCall();
+ // void-call-end: -1:24
+}
+
+// RUN: not clang-refactor-test perform -action extract-expression -selected=void-call %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-FAIL %s
diff --git a/test/Refactor/Extract/extract-expression-into-var.m b/test/Refactor/Extract/extract-expression-into-var.m
new file mode 100644
index 0000000..ec94dab
--- /dev/null
+++ b/test/Refactor/Extract/extract-expression-into-var.m
@@ -0,0 +1,12 @@
+// RUN: clang-refactor-test perform -action extract-expression -selected=expr1 -selected=expr2 %s | FileCheck %s
+
+void extractExpressionIntoVar(int x, int y, int z) {
+ // expr1-begin: +1:9
+ (void)@selector(foo:bar:); // CHECK: "SEL extractedExpr = @selector(foo:bar:);\n" [[@LINE]]:3 -> [[@LINE]]:3
+ // expr1-end: -1:28 // CHECK: "extractedExpr" [[@LINE-1]]:9 -> [[@LINE-1]]:28
+ // expr2-begin: +1:4 // CHECK: "void (^extractedExpr)(void) = ^ {\n // do nothing\n };\n" [[@LINE+1]]:3 -> [[@LINE+1]]:3
+ (^ {
+ // do nothing
+ })();
+ // expr2-end: -1:4 // CHECK: "extractedExpr" [[@LINE-3]]:4 -> [[@LINE-1]]:4
+}
diff --git a/test/Refactor/Extract/extract-expression.cpp b/test/Refactor/Extract/extract-expression.cpp
new file mode 100644
index 0000000..56b26df
--- /dev/null
+++ b/test/Refactor/Extract/extract-expression.cpp
@@ -0,0 +1,60 @@
+
+struct Rectangle { int width, height; };
+
+int sumArea(Rectangle *rs, int count) {
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ Rectangle r = rs[i];
+ sum += r.width * r.height;
+ }
+ return sum;
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:8:12-8:30 %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: "static int extracted(const Rectangle &r) {\nreturn r.width * r.height;\n}\n\n" 4:1 -> 4:1
+// CHECK1-NEXT: "extracted(r)" 8:12 -> 8:30
+;
+void extractFullExpressionIfPartiallySelected(const Rectangle &r) {
+ int area = r.width * r.height;
+}
+// CHECK2: "static int extracted(const Rectangle &r) {\nreturn r.width * r.height;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK2-NEXT: "extracted(r)" [[@LINE-3]]:14 -> [[@LINE-3]]:32
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:18:20-18:25 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:18:15-18:32 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:18:14-18:30 %s | FileCheck --check-prefix=CHECK2 %s
+;
+int extractFullMultipleCandidates(const Rectangle &r1) {
+ int y = r1.width - r1.width * r1.height;
+}
+// CHECK3-1: "static int extracted(const Rectangle &r1) {\nreturn - r1.width * r1.height;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK3-1-NEXT: "+ extracted(r1)" [[@LINE-3]]:20 -> [[@LINE-3]]:42
+// CHECK3-2: "static int extracted(const Rectangle &r1) {\nreturn r1.width - r1.width * r1.height;\n}\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1
+// CHECK3-2-NEXT: "extracted(r1)" [[@LINE-5]]:11 -> [[@LINE-5]]:42
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:28:20-28:42 %s | FileCheck --check-prefix=CHECK3-1 %s
+// RUN: clang-refactor-test perform -action extract -candidate 0 -selected=%s:28:20-28:42 %s | FileCheck --check-prefix=CHECK3-1 %s
+// RUN: clang-refactor-test perform -action extract -candidate 1 -selected=%s:28:20-28:42 %s | FileCheck --check-prefix=CHECK3-2 %s
+
+// RUN: not clang-refactor-test perform -action extract -candidate 2 -selected=%s:28:20-28:42 %s 2>&1 | FileCheck --check-prefix=CHECK-CAND-FAIL %s
+// CHECK-CAND-FAIL: failed to select the refactoring candidate
+;
+int extractFullMultipleCandidatesCaptureJustExtractedVariables(
+ const Rectangle &r1, const Rectangle &r2) {
+ return r1.width - r2.width * r2.height;
+}
+// CHECK4: "static int extracted(const Rectangle &r2) {\nreturn - r2.width * r2.height;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK4-NEXT: "+ extracted(r2)" [[@LINE-3]]:19 -> [[@LINE-3]]:41
+
+// RUN: clang-refactor-test perform -action extract -candidate 0 -selected=%s:44:19-44:41 %s | FileCheck --check-prefix=CHECK4 %s
+
+// Even when the expression result is unused statement, we still want to extract
+// it as an expression.
+;
+void extractStatementExpression(const Rectangle &r) {
+ r.width * r.height;
+}
+// CHECK5: "static int extracted(const Rectangle &r) {\nreturn r.width * r.height;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK5-NEXT: "extracted(r)" [[@LINE-3]]:3 -> [[@LINE-3]]:21
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:55:3-55:21 %s | FileCheck --check-prefix=CHECK5 %s
diff --git a/test/Refactor/Extract/extract-from-class.cpp b/test/Refactor/Extract/extract-from-class.cpp
new file mode 100644
index 0000000..29d9a00
--- /dev/null
+++ b/test/Refactor/Extract/extract-from-class.cpp
@@ -0,0 +1,45 @@
+
+#ifdef MULTIPLE
+class OuterClass {
+#endif
+
+class AClass {
+
+ int method(int x) {
+ return x + x * 2;
+ }
+// CHECK1: "static int extracted(int x) {\nreturn x + x * 2;\n}\n\n" [[@LINE-5]]:1
+// CHECK2: "static int extracted(int x) {\nreturn x + x * 2;\n}\n\n" [[@LINE-9]]:1
+;
+ AClass(int x) {
+ int y = x * 1;
+ }
+// CHECK1: "static int extracted(int x) {\nreturn x * 1;\n}\n\n" [[@LINE-11]]:1
+// CHECK2: "static int extracted(int x) {\nreturn x * 1;\n}\n\n" [[@LINE-15]]:1
+ ~AClass() {
+ int x = 0 + 4;
+ }
+// CHECK1: "static int extracted() {\nreturn 0 + 4;\n}\n\n" [[@LINE-16]]:1
+// CHECK2: "static int extracted() {\nreturn 0 + 4;\n}\n\n" [[@LINE-20]]:1
+;
+ int operator +(int x) {
+ return x + x * 3;
+ }
+// CHECK1: "static int extracted(int x) {\nreturn x + x * 3;\n}\n\n" [[@LINE-22]]:1
+// CHECK2: "static int extracted(int x) {\nreturn x + x * 3;\n}\n\n" [[@LINE-26]]:1
+;
+ void otherMethod(int x);
+};
+
+#ifdef MULTIPLE
+}
+#endif
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:9:12-9:21 -selected=%s:15:13-15:18 -selected=%s:20:13-20:18 -selected=%s:26:12-26:21 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:9:12-9:21 -selected=%s:15:13-15:18 -selected=%s:20:13-20:18 -selected=%s:26:12-26:21 %s -DMULTIPLE | FileCheck --check-prefix=CHECK2 %s
+;
+void AClass::otherMethod(int x) {
+ int y = x * x + 10;
+}
+// CHECK3: "static int extracted(int x) {\nreturn x * x + 10;\n}\n\n" [[@LINE-3]]:1
+// RUN: clang-refactor-test perform -action extract -selected=%s:42:11-42:21 %s | FileCheck --check-prefix=CHECK3 %s
diff --git a/test/Refactor/Extract/extract-header-inline.h b/test/Refactor/Extract/extract-header-inline.h
new file mode 100644
index 0000000..a323a11
--- /dev/null
+++ b/test/Refactor/Extract/extract-header-inline.h
@@ -0,0 +1,7 @@
+// RUN: clang-refactor-test perform -action extract -selected=extract %s | FileCheck %s
+;
+void extractInline(int x) { // CHECK: "inline int extracted(int x) {\nreturn x + 1;\n}\n\n" [[@LINE]]:1
+// extract-begin: +1:11
+ int y = x + 1;
+// extract-end: -1:16
+}
diff --git a/test/Refactor/Extract/extract-initiate.cpp b/test/Refactor/Extract/extract-initiate.cpp
new file mode 100644
index 0000000..3ddbc9f
--- /dev/null
+++ b/test/Refactor/Extract/extract-initiate.cpp
@@ -0,0 +1,150 @@
+struct Rectangle { int width, height; };
+
+int sumArea(Rectangle *rs, int count) {
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ Rectangle r = rs[i];
+ sum += r.width * r.height;
+ }
+ return sum;
+}
+
+// RUN: clang-refactor-test list-actions -at=%s:7:30 -selected=%s:7:12-7:30 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Extract Function
+
+// Ensure the an entire expression can be extracted:
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:7:12-7:30 %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: Initiated the 'extract' action at 7:12 -> 7:30
+
+// Ensure that an expression can be extracted even when it's not fully selected:
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:7:13-7:30 -selected=%s:7:18-7:30 -selected=%s:7:20-7:30 -selected=%s:7:19-7:30 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:7:12-7:29 -selected=%s:7:12-7:23 -selected=%s:7:12-7:21 -selected=%s:7:12-7:22 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:7:19-7:22 -selected=%s:7:20-7:21 -selected=%s:7:15-7:25 %s | FileCheck --check-prefix=CHECK1 %s
+
+// Ensure that the action isn't allowed be when no expression is selected:
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:1:1-1:5 -selected=%s:2:1-2:1 -selected=%s:3:1-3:38 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action extract -at=%s:1:1 -in=%s:3:1-38 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action
+// CHECK-NO-NOT: Initiated the 'extract' action
+
+int multipleCandidates(Rectangle &r1, Rectangle &r2) {
+ int x = r1.width + r1.height; // CHECK2-SINGLE: Initiated the 'extract' action at [[@LINE]]:11 -> [[@LINE]]:31
+ int y = r1.width - r2.width * r2.height;
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:34:20-34:31 -selected=%s:34:19-34:23 %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK2: Initiated the 'extract' action with multiple candidates:
+// CHECK2-NEXT: + r1.height
+// CHECK2-NEXT: r1.width + r1.height
+// RUN: clang-refactor-test initiate -action extract -selected=%s:34:20-34:21 %s | FileCheck --check-prefix=CHECK2-SINGLE %s
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:35:19-35:42 %s | FileCheck --check-prefix=CHECK3 %s
+// CHECK3: Initiated the 'extract' action with multiple candidates:
+// CHECK3-NEXT: - r2.width * r2.height
+// CHECK3-NEXT: r1.width - r2.width * r2.height
+
+void trimWhitespaceAndSemiColons(const Rectangle &r) {
+ int x = r.width + r.width * r.height; ;
+ //CHECK4: Initiated the 'extract' action at [[@LINE-1]]:26 -> [[@LINE-1]]:44
+ //CHECK5: Initiated the 'extract' action at [[@LINE-2]]:12 -> [[@LINE-2]]:44
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:50:23-50:46 -selected=%s:50:23-50:45 %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:50:10-51:3 %s | FileCheck --check-prefix=CHECK5 %s
+
+void disallowBlankStatements() {
+ // comment
+ ;
+
+}
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:59:1-59:3 -selected=%s:60:1-62:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void allowCommentSelection(int y) {
+ char c = "//fake comment" [y] ;
+}
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:67:13-67:16 %s 2>&1 | FileCheck --check-prefix=CHECK-SIMPLE %s
+// CHECK-SIMPLE: Failed to initiate the refactoring action (the selected expression is too simple)
+
+void extractStatements(const Rectangle &r) {
+ (void)r;
+ int area = r.width * r.height;
+ int perimeter = r.width + r.height;
+ int sum = area + perimeter;
+ // CHECK6: Initiated the 'extract' action at [[@LINE-3]]:3 -> [[@LINE-2]]:38
+ // CHECK7: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-2]]:30
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:75:1-76:38 -selected=%s:74:11-77:3 -selected=%s:75:14-76:26 %s | FileCheck --check-prefix=CHECK6 %s
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:74:1-77:30 -selected=%s:73:45-78:1 %s | FileCheck --check-prefix=CHECK7 %s
+
+void extractStatementsCompoundChild(const Rectangle &r) {
+ int x = 0;
+ {
+ int area = r.width * r.height;
+ }
+ int y = 0;
+ int z = 1;
+ // CHECK8: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-2]]:13
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:89:5-91:12 %s | FileCheck --check-prefix=CHECK8 %s
+
+void extractStatementsTrimComments(const Rectangle &r) {
+ int x = 0;
+
+ // comment
+ int area = r.width * r.height;
+
+ // another comment
+ int y = 0;
+
+ // trailing comment
+}
+// CHECK9: Initiated the 'extract' action at [[@LINE-7]]:3 -> [[@LINE-7]]:33
+// CHECK10: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-5]]:13
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:100:1-102:32 -selected=%s:101:6-104:21 -selected=%s:100:1-105:3 %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:103:1-105:12 -selected=%s:104:6-107:22 -selected=%s:103:1-108:1 %s | FileCheck --check-prefix=CHECK10 %s
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:101:1-101:13 -selected=%s:106:1-107:22 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void disallowEmptyCompoundStatement() {
+ // comment
+}
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:118:1-118:13 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void trimLeadingCompoundStatementComment() {
+ // comment
+
+ int x = 0;
+}
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:124:1-124:13 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void extractEntireCompoundStatement() {
+ {
+ int x = 0;
+ int y = 0;
+ }
+ // CHECK11: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-1]]:4
+ // CHECK12: Initiated the 'extract' action at [[@LINE-4]]:5 -> [[@LINE-3]]:15
+ // CHECK13: Initiated the 'extract' action at [[@LINE-5]]:5 -> [[@LINE-4]]:15
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:132:3-135:4 %s | FileCheck --check-prefix=CHECK11 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:132:3-135:3 %s | FileCheck --check-prefix=CHECK12 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:132:4-135:4 %s | FileCheck --check-prefix=CHECK13 %s
+
+void disallowExtractionWhenSelectionRangeIsOutsideFunction() {
+ int x = 0;
+ int x = 1;
+}
+
+// RUN: not clang-refactor-test initiate -action extract -selected=%s:143:1-147:12 -selected=%s:146:3-150:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/Extract/extract-initiate.m b/test/Refactor/Extract/extract-initiate.m
new file mode 100644
index 0000000..4af03c6
--- /dev/null
+++ b/test/Refactor/Extract/extract-initiate.m
@@ -0,0 +1,14 @@
+
+@interface I
+@end
+
+@implementation I
+
+- (int)computeAreaSquaredGivenWidth:(int)width height:(int)height {
+ int area = width * height;
+ return area * area;
+}
+//CHECK1: Initiated the 'extract' action at [[@LINE-3]]:14 -> [[@LINE-3]]:28
+// RUN: clang-refactor-test initiate -action extract -selected=%s:8:14-8:28 %s | FileCheck --check-prefix=CHECK1 %s
+
+@end
diff --git a/test/Refactor/Extract/extract-macros.cpp b/test/Refactor/Extract/extract-macros.cpp
new file mode 100644
index 0000000..ecac1ce
--- /dev/null
+++ b/test/Refactor/Extract/extract-macros.cpp
@@ -0,0 +1,39 @@
+#define ONE 1
+
+void extractExprMacros(int x) {
+ bool b = x == ONE;
+// CHECK1: {\nreturn x == ONE;\n}
+ int y = ONE + x;
+// CHECK1: {\nreturn ONE + x;\n}
+ int z = ONE - ONE * ONE;
+// CHECK1: {\nreturn ONE - ONE * ONE;\n}
+// CHECK1: {\nreturn ONE * ONE;\n}
+// CHECK1: {\nreturn - ONE * ONE;\n}
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:4:12-4:20 -selected=%s:6:11-6:19 -selected=%s:8:12-8:26 -selected=%s:8:17-8:26 -selected=%s:8:15-8:26 %s | FileCheck --check-prefix=CHECK1 %s
+
+#define MACRO2(x, y) x, y
+
+int function(int x);
+
+// MACRO-ARG3: "static int extracted(int x, int &y, int z) {\nreturn y = MACRO2(x + 2, function(z));\n}\n\n" [[@LINE+4]]:1
+// MACRO-ARG2: "static int extracted(int z) {\nreturn function(z);\n}\n\n" [[@LINE+3]]:1
+// MACRO-ARG1: "static int extracted(int x) {\nreturn x + 2;\n}\n\n" [[@LINE+2]]:1
+;
+void extractFromMacroArgument(int x, int y, int z) {
+
+ // macro-arg-expr4-begin: +4:7
+ // macro-arg-expr3-begin: +3:14
+ // macro-arg-expr2-begin: +2:21
+ // macro-arg-expr1-begin: +1:14
+ y = MACRO2(x + 2, function(z)); // comment4
+ // macro-arg-expr1-end: -1:19 // MACRO-ARG1: "extracted(x)" [[@LINE-1]]:14 -> [[@LINE-1]]:19
+ // macro-arg-expr2-end: -2:32 // MACRO-ARG2: "extracted(z)" [[@LINE-2]]:21 -> [[@LINE-2]]:32
+ // macro-arg-expr3-end: -3:32 // MACRO-ARG3: "extracted(x, y, z)" [[@LINE-3]]:3 -> [[@LINE-3]]:33
+ // macro-arg-expr4-end: -4:19
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr1 %s | FileCheck --check-prefix=MACRO-ARG1 %s
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr2 %s | FileCheck --check-prefix=MACRO-ARG2 %s
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr3 %s | FileCheck --check-prefix=MACRO-ARG3 %s
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr4 %s | FileCheck --check-prefix=MACRO-ARG3 %s
diff --git a/test/Refactor/Extract/extract-method.cpp b/test/Refactor/Extract/extract-method.cpp
new file mode 100644
index 0000000..84beaa8
--- /dev/null
+++ b/test/Refactor/Extract/extract-method.cpp
@@ -0,0 +1,153 @@
+
+void function(int x) {
+ int y = x * x;
+}
+
+class InitiateMethodExtraction {
+public:
+
+ InitiateMethodExtraction() {
+ int x = constMethod(1);
+ }
+// CHECK1: "void extracted() {\nint x = constMethod(1);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK1-NEXT: "extracted();" [[@LINE-3]]:5 -> [[@LINE-3]]:28
+// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(1);\n}\n\n"
+// CHECK2-NEXT: "extracted(*this);"
+;
+ ~InitiateMethodExtraction() {
+ int x = constMethod(2);
+ }
+// CHECK1: "void extracted() {\nint x = constMethod(2);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK1-NEXT: "extracted();" [[@LINE-3]]:5 -> [[@LINE-3]]:28
+// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(2);\n}\n\n"
+// CHECK2-NEXT: "extracted(*this);"
+;
+ void method() {
+ int x = constMethod(3);
+ }
+// CHECK1: "void extracted() {\nint x = constMethod(3);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK1-NEXT: "extracted();" [[@LINE-3]]:5 -> [[@LINE-3]]:28
+// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(3);\n}\n\n"
+// CHECK2-NEXT: "extracted(*this);"
+;
+ int constMethod(int x) const {
+ return x + x * 2;
+ }
+// CHECK1: "int extracted(int x) const {\nreturn x + x * 2;\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK1-NEXT: "extracted(x);" [[@LINE-3]]:5 -> [[@LINE-3]]:22
+// CHECK2: "static int extracted(int x) {\nreturn x + x * 2;\n}\n\n"
+// CHECK2-NEXT: "extracted(x);"
+;
+ int operator << (int x) {
+ return constMethod(x);
+ }
+// CHECK1: "int extracted(int x) {\nreturn constMethod(x);\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK1-NEXT: "extracted(x)" [[@LINE-3]]:12 -> [[@LINE-3]]:26
+// CHECK2: "static int extracted(const InitiateMethodExtraction &object, int x) {\nreturn object.constMethod(x);\n}\n\n"
+// CHECK2-NEXT: "extracted(*this, x)"
+;
+ static void staticMethod(int x) {
+ int y = x * x;
+ }
+// CHECK1: "static void extracted(int x) {\nint y = x * x;\n}\n\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK1-NEXT: "extracted(x);" [[@LINE-3]]:5 -> [[@LINE-3]]:19
+// CHECK2: "static void extracted(int x) {\nint y = x * x;\n}\n\n"
+// CHECK2-NEXT: "extracted(x);"
+;
+ void otherMethod();
+};
+
+void InitiateMethodExtraction::otherMethod() {
+ int x = constMethod(4);
+}
+// CHECK1: "void extracted();\n\n" [[@LINE-6]]:3 -> [[@LINE-6]]:3
+// CHECK1-NEXT: "void InitiateMethodExtraction::extracted() {\nint x = constMethod(4);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK1-NEXT: "extracted();" [[@LINE-4]]:3 -> [[@LINE-4]]:26
+// CHECK2: "static void extracted(const InitiateMethodExtraction &object) {\nint x = object.constMethod(4);\n}\n\n"
+// CHECK2-NEXT: "extracted(*this);"
+
+// RUN: clang-refactor-test list-actions -at=%s:10:5 -selected=%s:10:5-10:27 %s | FileCheck --check-prefix=CHECK-METHOD %s
+
+// CHECK-METHOD: Extract Function{{$}}
+// CHECK-METHOD-NEXT: Extract Method{{$}}
+
+// RUN: clang-refactor-test list-actions -at=%s:3:3 -selected=%s:3:3-3:16 %s | FileCheck --check-prefix=CHECK-FUNC %s
+
+// CHECK-FUNC: Extract Function{{$}}
+// CHECK-FUNC-NOT: Extract Method
+
+// RUN: clang-refactor-test perform -action extract-method -selected=%s:10:5-10:27 -selected=%s:18:5-18:27 -selected=%s:26:5-26:27 -selected=%s:34:5-34:21 -selected=%s:42:12-42:26 -selected=%s:50:5-50:18 -selected=%s:61:3-61:25 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:10:5-10:27 -selected=%s:18:5-18:27 -selected=%s:26:5-26:27 -selected=%s:34:5-34:21 -selected=%s:42:12-42:26 -selected=%s:50:5-50:18 -selected=%s:61:3-61:25 %s | FileCheck --check-prefix=CHECK2 %s
+
+
+
+
+
+
+
+namespace ns {
+ struct Outer {
+ struct Inner {
+ Inner(int x);
+
+ // comment
+ void method2(int x) const; // this stays!
+ int field;
+ };
+ };
+}
+ns::Outer::Inner::Inner(int x) {
+ int y = x + x;
+}
+// CHECK3: "void extracted(int x);\n\n" [[@LINE-11]]:7 -> [[@LINE-11]]:7
+// CHECK3: "void ns::Outer::Inner::extracted(int x) {\nint y = x + x;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK3: "extracted(x);" [[@LINE-4]]:3 -> [[@LINE-4]]:17
+
+namespace ns {
+
+ // comment
+ void Outer::Inner::method2(int x) const {
+ if (x != 0)
+ int z = x * x;
+ else {
+ (void)x;
+ }
+ }
+// CHECK3: "void extracted(int x) const;\n\n" [[@LINE-23]]:7 -> [[@LINE-23]]:7
+// CHECK3: "void Outer::Inner::extracted(int x) const {\nif (x != 0)\n int z = x * x;\n else {\n (void)x;\n }\n}\n\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3
+// CHECK3: "extracted(x);" [[@LINE-8]]:5 -> [[@LINE-4]]:6
+;
+ void Outer::Inner::methodNotDeclared(int x) {
+ int z = x * x;
+ }
+}
+// CHECK3: "\n\nvoid extracted(int x);\n" [[@LINE-30]]:48 -> [[@LINE-30]]:48
+// CHECK3: "void Outer::Inner::extracted(int x) {\nint z = x * x;\n}\n\n" [[@LINE-5]]:3 -> [[@LINE-5]]:3
+// CHECK3: "extracted(x);" [[@LINE-5]]:5 -> [[@LINE-5]]:19
+
+struct Empty {
+ int field;
+};
+
+void Empty::method() {
+ field = 22;
+}
+// CHECK3: "void extracted();\n\n" [[@LINE-5]]:1 -> [[@LINE-5]]:1
+// CHECK3: "void Empty::extracted() {\nfield = 22;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK3: "extracted()" [[@LINE-4]]:3 -> [[@LINE-4]]:13
+
+// RUN: clang-refactor-test perform -action extract-method -selected=%s:100:3-100:16 -selected=%s:110:5-114:4 -selected=%s:121:5-121:18 -selected=%s:133:3-133:13 %s | FileCheck --check-prefix=CHECK3 %s
+
+template<typename T1, typename T2, int X>
+struct TemplateExtraction {
+ void method(); // CHECK4: "void extracted();\n\n" [[@LINE]]:3 -> [[@LINE]]:3
+};
+
+template<typename T1, typename T2, int x> // CHECK4: "template <typename T1, typename T2, int x> \nvoid TemplateExtraction<T1, T2, x>::extracted() {\nint y = x;\n}\n\n" [[@LINE]]:1 -> [[@LINE]]:1
+void TemplateExtraction<T1, T2, x>::method() {
+// template-method-begin: +1:1
+ int y = x;
+// template-method-end: +0:1
+}
+
+// RUN: clang-refactor-test perform -action extract-method -selected=template-method %s | FileCheck --check-prefix=CHECK4 %s
diff --git a/test/Refactor/Extract/extract-method.m b/test/Refactor/Extract/extract-method.m
new file mode 100644
index 0000000..6c136b0
--- /dev/null
+++ b/test/Refactor/Extract/extract-method.m
@@ -0,0 +1,57 @@
+
+void function(int x) {
+ int y = x * x;
+}
+
+@interface MethodExtraction
+
+- (int)method;
+- (void)classMethod;
+
+@end
+
+@implementation MethodExtraction
+
+- (void)aMethod:(int)x withY:(int)y {
+ int a = x + y;
+}
+// CHECK1: "- (void)extracted:(int)x y:(int)y {\nint a = x + y;\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK1-NEXT: "[self extracted:x y:y];" [[@LINE-3]]:3 -> [[@LINE-3]]:17
+// CHECK2: "static void extracted(int x, int y) {\nint a = x + y;\n}\n\n"
+// CHECK2-NEXT: "extracted(x, y);"
+;
++ (void)classMethod {
+ int x = 1;
+ int y = function(x);
+}
+// CHECK1: "+ (void)extracted {\nint x = 1;\n int y = function(x);\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+// CHECK1-NEXT: "[self extracted];" [[@LINE-4]]:3 -> [[@LINE-3]]:23
+// CHECK2: "static void extracted() {\nint x = 1;\n int y = function(x);\n}\n\n"
+// CHECK2-NEXT: "extracted();"
+
+@end
+
+@implementation MethodExtraction (Category)
+
+- (void)catMethod {
+ int x = [self method];
+}
+
++ (void)catClassMethod {
+ int x = function(42);
+}
+
+@end
+
+// RUN: clang-refactor-test list-actions -at=%s:16:3 -selected=%s:16:3-16:16 %s | FileCheck --check-prefix=CHECK-METHOD %s
+
+// CHECK-METHOD: Extract Function{{$}}
+// CHECK-METHOD-NEXT: Extract Method{{$}}
+
+// RUN: clang-refactor-test list-actions -at=%s:3:3 -selected=%s:3:3-3:16 %s | FileCheck --check-prefix=CHECK-FUNC %s
+
+// CHECK-FUNC: Extract Function{{$}}
+// CHECK-FUNC-NOT: Extract Method
+
+// RUN: clang-refactor-test perform -action extract-method -selected=%s:16:3-16:16 -selected=%s:24:3-25:22 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:16:3-16:16 -selected=%s:24:3-25:22 %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp b/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp
new file mode 100644
index 0000000..1b9e2ee
--- /dev/null
+++ b/test/Refactor/Extract/extract-mutation-of-captured-variable.cpp
@@ -0,0 +1,130 @@
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+#ifdef ADD
+#define MUT(x, y) x += y
+#endif
+
+#ifdef MUL
+#define MUT(x, y) x *= y
+#endif
+
+#ifdef BIT
+#define MUT(x, y) x |= y
+#endif
+
+#ifdef SHIFT
+#define MUT(x, y) x >>= y
+#endif
+
+#ifdef INC1
+#define MUT(x, y) ++x
+#endif
+
+#ifdef INC2
+#define MUT(x, y) x++
+#endif
+
+#ifdef DEC1
+#define MUT(x, y) --x
+#endif
+
+#ifdef DEC2
+#define MUT(x, y) x--
+#endif
+
+#ifndef MUT
+#define MUT(x, y) x = y
+#endif
+
+#ifdef FIELD
+class MutatePrivateInstanceVariables {
+ int x;
+ int y;
+ Rectangle r;
+
+#endif
+
+void mutateVariableOrField
+#ifndef FIELD
+ (int x, int y, Rectangle r)
+#else
+ ()
+#endif
+{
+ (MUT(x, 1));
+// CHECK1: (int &x) {\nreturn (MUT(x, 1));\n}
+
+ (MUT((x), 1));
+// CHECK1: (int &x) {\nreturn (MUT((x), 1));\n}
+
+ (MUT(r.width, 1));
+// CHECK1: (Rectangle &r) {\nreturn (MUT(r.width, 1));\n}
+
+ (MUT((x, r.height), 1));
+// CHECK1: (Rectangle &r, int x) {\nreturn (MUT((x, r.height), 1));\n}
+
+ (MUT((x == 0 ? x : y), 1));
+// CHECK1: (int &x, int &y) {\nreturn (MUT((x == 0 ? x : y), 1));\n}
+
+ Rectangle a, b;
+ (x == 0 ? (r) : b) = a;
+// CHECK2: (const Rectangle &a, Rectangle &b, Rectangle &r, int x) {\nreturn (x == 0 ? (r) : b) = a;\n}
+
+}
+
+#ifdef FIELD
+};
+#endif
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DFIELD | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:73:3-73:25 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:73:3-73:25 %s -DFIELD | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DADD | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DMUL -DFIELD | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DBIT | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DSHIFT -DFIELD | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DINC1 | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DINC2 -DFIELD | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DDEC1 | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:57:3-57:14 -selected=%s:60:3-60:16 -selected=%s:63:3-63:20 -selected=%s:66:3-66:26 -selected=%s:69:3-69:29 %s -DDEC2 -DFIELD | FileCheck --check-prefix=CHECK1 %s
+
+void dontMutateVariable(int *array, int x) {
+ array[x] = 0;
+// CHECK3: (int *array, int x) {\narray[x] = 0;\n}
+ *array = 0;
+// CHECK3: (int *array) {\n*array = 0;\n}
+ array = 0;
+// CHECK3: extracted(int *&array) {\narray = 0;\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:101:3-101:15 -selected=%s:103:3-103:13 -selected=%s:105:3-105:12 %s | FileCheck --check-prefix=CHECK3 %s
+
+#ifdef __cplusplus
+
+int &returnsRef(int x) {
+ static int result = 0;
+ return result;
+}
+
+void dontMutateCallArguments(int x) {
+ returnsRef(x) = 0;
+// CHECK4: extracted(int x) {\nreturnsRef(x) = 0;\n}
+}
+
+void mutateRefVar(int &x) {
+ x = 0;
+// CHECK4: extracted(int &x) {\nx = 0;\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:119:3-119:20 -selected=%s:124:3-124:8 %s | FileCheck --check-prefix=CHECK4 %s
+
+#endif
diff --git a/test/Refactor/Extract/extract-objc-property.m b/test/Refactor/Extract/extract-objc-property.m
new file mode 100644
index 0000000..7fc7919
--- /dev/null
+++ b/test/Refactor/Extract/extract-objc-property.m
@@ -0,0 +1,60 @@
+@interface HasProperty
+
+@property (strong) HasProperty *item;
+
+- (HasProperty *)implicitProp;
+
+- (void)setImplicitSetter:(HasProperty *)value;
+
+@end
+
+@implementation HasProperty
+
+- (void)test {
+// property-name-begin: +2:8
+// property-begin: +1:3
+ self.item;
+// property-end: -1:12
+// property-name-end: -2:12
+// CHECK: "static HasProperty * extracted(HasProperty *object) {\nreturn object.item;\n}\n\n"
+
+// implicit-name-begin: +2:8
+// implicit-begin: +1:3
+ self.implicitProp;
+// implicit-end: -1:20
+// implicit-name-end: -2:20
+// CHECK: "static HasProperty * extracted(HasProperty *object) {\nreturn object.implicitProp;\n}\n\n"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=property -selected=implicit %s -fobjc-arc | FileCheck %s
+// RUN: clang-refactor-test perform -action extract -selected=property-name -selected=implicit-name %s -fobjc-arc | FileCheck %s
+
+- (void)prohibitSetterExtraction {
+// setter-pref-begin: +2:8
+// setter-begin: +1:3
+ self.item = 0;
+// setter-end: -1:12
+// setter-pref-end: -2:12
+// implicit-setter-pref-begin: +2:8
+// implicit-setter-begin: +1:3
+ self.implicitSetter = 0;
+// implicit-setter-end: -1:22
+// implicit-setter-pref-end: -2:22
+}
+// CHECK-SETTER: Failed to initiate the refactoring action (property setter can't be extracted)!
+// RUN: not clang-refactor-test initiate -action extract -selected=setter -selected=setter-pref -selected=implicit-setter -selected=implicit-setter-pref %s -fobjc-arc 2>&1 | FileCheck --check-prefix=CHECK-SETTER %s
+
+@end
+
+@interface HasIntProp
+@property (readwrite) int item;
+@end
+
+// AVOID-CRASH: "static void extracted(HasIntProp *f) {\nf.item = !f.item;\n}\n\n"
+// avoid-extraction-crash-begin: +1:42
+void avoidExtractionCrash(HasIntProp *f) {
+ f.item = !f.item;
+// avoid-extraction-crash-end: -1:5
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=avoid-extraction-crash %s -fobjc-arc | FileCheck --check-prefix=AVOID-CRASH %s
diff --git a/test/Refactor/Extract/extract-reference-of-captured-variable.cpp b/test/Refactor/Extract/extract-reference-of-captured-variable.cpp
new file mode 100644
index 0000000..1c71169
--- /dev/null
+++ b/test/Refactor/Extract/extract-reference-of-captured-variable.cpp
@@ -0,0 +1,287 @@
+
+void takesPtr(int *x) { }
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+void takesStructPtr(Rectangle *sp) { }
+
+void variableTakesRef(int x, Rectangle r) {
+ int &y = x;
+ takesPtr(&y);
+// CHECK1: (int &x) {\nint &y = x;\n takesPtr(&y);\n}
+ Rectangle p = r;
+ Rectangle &rp = p;
+ takesStructPtr(&rp);
+// CHECK1: (const Rectangle &r) {\nRectangle p = r;\n Rectangle &rp = p;\n takesStructPtr(&rp);\n}
+// CHECK1: (Rectangle &p) {\nRectangle &rp = p;\n takesStructPtr(&rp);\n}
+ int &member = ((r).width);
+ int z = member;
+// CHECK1: (Rectangle &r) {\nint &member = ((r).width);\n int z = member;\n}
+
+// Even though y takes a reference to x, we still want to pass it by value here.
+ int a = x;
+// CHECK1: (int x) {\nint a = x;\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:3-12:15 -selected=%s:14:3-16:22 -selected=%s:15:3-16:22 -selected=%s:19:3-20:17 -selected=%s:24:3-24:12 %s | FileCheck --check-prefix=CHECK1 %s
+
+class PrivateInstanceVariables {
+ int x;
+ Rectangle r;
+
+ void method() {
+ int &y = x;
+// CHECK2: extracted(int &x) {\nint &y = x;\n}
+ Rectangle &rr = r;
+// CHECK2: extracted(Rectangle &r) {\nRectangle &rr = r;\n}
+ int &z = ((r).width);
+// CHECK2: extracted(Rectangle &r) {\nint &z = ((r).width);\n}
+ }
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:35:5-35:15 -selected=%s:37:5-37:22 -selected=%s:39:5-39:25 %s | FileCheck --check-prefix=CHECK2 %s
+
+#ifdef USECONST
+#define CONST const
+#else
+#define CONST
+#endif
+
+void takesRef(CONST int &x) { }
+
+void takesStructRef(CONST Rectangle &r) { }
+
+void takesValue(int x) { }
+
+struct ConsTakesRef {
+ ConsTakesRef(CONST int &x) { }
+
+ void takesRef(CONST int &x) const { }
+ void takesValue(int x) const { }
+};
+
+int operator << (CONST Rectangle &r, CONST int &x) { return 0; }
+
+void callTakesRef(int x, Rectangle r) {
+ takesRef(x);
+// CHECK3: extracted(int &x) {\ntakesRef(x);\n}
+// CHECK4: extracted(int x) {\ntakesRef(x);\n}
+ takesValue(x);
+// CHECK3: extracted(int x) {\ntakesValue(x);\n}
+// CHECK4: extracted(int x) {\ntakesValue(x);\n}
+ auto k = ConsTakesRef(x); auto y = ConsTakesRef(x);
+// CHECK3: extracted(int &x) {\nauto k = ConsTakesRef(x);\n}
+// CHECK4: extracted(int x) {\nauto k = ConsTakesRef(x);\n}
+ y.takesRef((x));
+// CHECK3: extracted(int &x, const ConsTakesRef &y) {\ny.takesRef((x));\n}
+// CHECK4: extracted(int x, const ConsTakesRef &y) {\ny.takesRef((x));\n}
+ y.takesValue(x);
+// CHECK3: extracted(int x, const ConsTakesRef &y) {\ny.takesValue(x);\n}
+// CHECK4: extracted(int x, const ConsTakesRef &y) {\ny.takesValue(x);\n}
+ takesStructRef((r));
+// CHECK3: extracted(Rectangle &r) {\ntakesStructRef((r));\n}
+// CHECK4: extracted(const Rectangle &r) {\ntakesStructRef((r));\n}
+ takesRef((r).height);
+// CHECK3: extracted(Rectangle &r) {\ntakesRef((r).height);\n}
+// CHECK4: extracted(const Rectangle &r) {\ntakesRef((r).height);\n}
+ y.takesRef(r.width);
+// CHECK3: extracted(Rectangle &r, const ConsTakesRef &y) {\ny.takesRef(r.width);\n}
+// CHECK4: extracted(const Rectangle &r, const ConsTakesRef &y) {\ny.takesRef(r.width);\n}
+ takesValue(r.width);
+// CHECK3: extracted(const Rectangle &r) {\ntakesValue(r.width);\n}
+// CHECK4: extracted(const Rectangle &r) {\ntakesValue(r.width);\n}
+ r << x;
+// CHECK3: extracted(Rectangle &r, int &x) {\nreturn r << x;\n}
+// CHECK4: extracted(const Rectangle &r, int x) {\nreturn r << x;\n}
+
+ int &r1 = x;
+ takesRef(x);
+// CHECK3: extracted(int &x) {\nint &r1 = x;\n takesRef(x);\n}
+// CHECK4: extracted(int &x) {\nint &r1 = x;\n takesRef(x);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:68:3-68:14 -selected=%s:71:3-71:16 -selected=%s:74:3-74:27 -selected=%s:77:3-77:18 -selected=%s:80:3-80:18 -selected=%s:83:3-83:22 -selected=%s:86:3-86:23 -selected=%s:89:3-89:22 -selected=%s:92:3-92:22 -selected=%s:95:3-95:9 -selected=%s:99:3-100:14 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:68:3-68:14 -selected=%s:71:3-71:16 -selected=%s:74:3-74:27 -selected=%s:77:3-77:18 -selected=%s:80:3-80:18 -selected=%s:83:3-83:22 -selected=%s:86:3-86:23 -selected=%s:89:3-89:22 -selected=%s:92:3-92:22 -selected=%s:95:3-95:9 -selected=%s:99:3-100:14 %s -DUSECONST | FileCheck --check-prefix=CHECK4 %s
+
+void takesConstRef(const int &x) { }
+
+void callTakeRefAndConstRef(int x) {
+ takesRef(x);
+ takesConstRef(x);
+// CHECK5: extracted(int &x) {\ntakesRef(x);\n takesConstRef(x);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:111:3-112:19 %s | FileCheck --check-prefix=CHECK5 %s
+
+class PrivateInstanceVariablesCallRefs {
+ int x;
+ Rectangle r;
+
+ void callsTakeRef() {
+ takesRef(x);
+// CHECK6: extracted(int &x) {\ntakesRef(x);\n}
+// CHECK7: extracted(int x) {\ntakesRef(x);\n}
+ takesStructRef(r);
+// CHECK6: extracted(Rectangle &r) {\ntakesStructRef(r);\n}
+// CHECK7: extracted(const Rectangle &r) {\ntakesStructRef(r);\n}
+ takesRef(r.width);
+// CHECK6: extracted(Rectangle &r) {\ntakesRef(r.width);\n}
+// CHECK7: extracted(const Rectangle &r) {\ntakesRef(r.width);\n}
+ }
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:123:5-123:16 -selected=%s:126:5-126:22 -selected=%s:129:5-129:22 %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:123:5-123:16 -selected=%s:126:5-126:22 -selected=%s:129:5-129:22 %s -DUSECONST | FileCheck --check-prefix=CHECK7 %s
+
+void variableTakesConstRef(int x, Rectangle r) {
+ const int &y = x;
+// CHECK8: extracted(int x) {\nconst int &y = x;\n}
+ const Rectangle &p = r;
+// CHECK8: extracted(const Rectangle &r) {\nconst Rectangle &p = r;\n}
+ const int &z = r.width;
+// CHECK8: extracted(const Rectangle &r) {\nconst int &z = r.width;\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:139:3-139:19 -selected=%s:141:3-141:25 -selected=%s:143:3-143:25 %s | FileCheck --check-prefix=CHECK8 %s
+
+class ClassWithMethod {
+public:
+ int method() CONST { return 0; }
+ int operator + (int x) CONST { return x; }
+};
+
+void nonConstMethodCallImpliesNonConstReceiver(ClassWithMethod x) {
+ x.method();
+// CHECK10: extracted(ClassWithMethod &x) {\nreturn x.method();\n}
+// CHECK11: extracted(const ClassWithMethod &x) {\nreturn x.method();\n}
+ x.operator +(2);
+// CHECK10: extracted(ClassWithMethod &x) {\nreturn x.operator +(2);\n}
+// CHECK11: extracted(const ClassWithMethod &x) {\nreturn x.operator +(2);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:156:3-156:14 -selected=%s:159:3-159:18 %s | FileCheck --check-prefix=CHECK10 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:156:3-156:14 -selected=%s:159:3-159:18 %s -DUSECONST | FileCheck --check-prefix=CHECK11 %s
+
+void ignoreMethodCallsOnPointer(ClassWithMethod *x) {
+ x->method();
+// CHECK12: extracted(ClassWithMethod *x) {\nreturn x->method();\n}
+ x->operator +(2);
+// CHECK12: extracted(ClassWithMethod *x) {\nreturn x->operator +(2);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:168:3-168:13 -selected=%s:170:3-170:19 %s | FileCheck --check-prefix=CHECK12 %s
+
+void takesRValueRef(int &&x) { }
+void takesRValueStructRef(Rectangle &&r) { }
+
+void callTakesRValueRef(int x) {
+ takesRValueRef(static_cast<int&&>(x));
+// CHECK13: extracted(int &x) {\ntakesRValueRef(static_cast<int&&>(x));\n}
+ Rectangle r;
+ takesRValueStructRef((static_cast<Rectangle&&>(r)));
+// CHECK13: extracted(Rectangle &r) {\ntakesRValueStructRef((static_cast<Rectangle&&>(r)));\n}
+ int &&y = static_cast<int&&>(r.height);
+// CHECK13: extracted(Rectangle &r) {\nint &&y = static_cast<int&&>(r.height);\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:180:3-180:20 -selected=%s:183:3-183:54 -selected=%s:185:3-185:41 %s | FileCheck --check-prefix=CHECK13 %s
+
+void referencesInConditionalOperator(int x, int y) {
+ takesRef(x == 0 ? x : y);
+// CHECK14: (int &x, int &y) {\ntakesRef(x == 0 ? x : y);\n}
+// CHECK15: (int x, int y) {\ntakesRef(x == 0 ? x : y);\n}
+ Rectangle a, b;
+ takesStructRef(y == 0 ? (a) : b);
+// CHECK14: (Rectangle &a, Rectangle &b, int y) {\ntakesStructRef(y == 0 ? (a) : b);\n}
+// CHECK15: (const Rectangle &a, const Rectangle &b, int y) {\ntakesStructRef(y == 0 ? (a) : b);\n}
+ takesRef(x == 0 ? (a).width : (y == 0 ? y : b.height));
+// CHECK14: (Rectangle &a, Rectangle &b, int x, int &y) {\ntakesRef(x == 0 ? (a).width : (y == 0 ? y : b.height));\n}
+// CHECK15: (const Rectangle &a, const Rectangle &b, int x, int y) {\ntakesRef(x == 0 ? (a).width : (y == 0 ? y : b.height));\n}
+ takesRef((x == 0 ? a : (b)).width);
+// CHECK14: (Rectangle &a, Rectangle &b, int x) {\ntakesRef((x == 0 ? a : (b)).width);\n}
+// CHECK15: (const Rectangle &a, const Rectangle &b, int x) {\ntakesRef((x == 0 ? a : (b)).width);\n}
+ takesRef(x == 0 ? y : y);
+// CHECK14: (int x, int &y) {\ntakesRef(x == 0 ? y : y);\n}
+// CHECK15: (int x, int y) {\ntakesRef(x == 0 ? y : y);\n}
+ ClassWithMethod caller1, caller2;
+ (x == 0 ? caller1 : caller2).method();
+// CHECK14: (ClassWithMethod &caller1, ClassWithMethod &caller2, int x) {\nreturn (x == 0 ? caller1 : caller2).method();\n}
+// CHECK15: (const ClassWithMethod &caller1, const ClassWithMethod &caller2, int x) {\nreturn (x == 0 ? caller1 : caller2).method();\n}
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:192:3-192:27 -selected=%s:196:3-196:35 -selected=%s:199:3-199:57 -selected=%s:202:3-202:37 -selected=%s:205:3-205:27 -selected=%s:209:3-209:40 %s | FileCheck --check-prefix=CHECK14 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:192:3-192:27 -selected=%s:196:3-196:35 -selected=%s:199:3-199:57 -selected=%s:202:3-202:37 -selected=%s:205:3-205:27 -selected=%s:209:3-209:40 %s -DUSECONST | FileCheck --check-prefix=CHECK15 %s
+
+class PrivateInstanceVariablesConditionalOperatorRefs {
+ int x;
+ Rectangle r;
+
+ void callsTakeRef(int y) {
+ takesRef(y == 0 ? x : r.width);
+ }
+};
+// CHECK16: (Rectangle &r, int &x, int y) {\ntakesRef(y == 0 ? x : r.width);\n}
+// RUN: clang-refactor-test perform -action extract -selected=%s:222:5-222:35 %s | FileCheck --check-prefix=CHECK16 %s
+
+class ReferencesInCommaOperator {
+ int x;
+
+ void callsTakeRef(int y, Rectangle r) {
+ takesRef((x, y));
+// CHECK17: (int x, int &y) {\ntakesRef((x, y));\n}
+ takesRef((y, x));
+// CHECK17: (int &x, int y) {\ntakesRef((y, x));\n}
+ takesStructRef((takesValue(x), r));
+// CHECK17: (Rectangle &r, int x) {\ntakesStructRef((takesValue(x), r));\n}
+ }
+};
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:232:5-232:21 -selected=%s:234:5-234:21 -selected=%s:236:5-236:39 %s | FileCheck --check-prefix=CHECK17 %s
+
+struct StaticMember {
+ static int staticMember;
+};
+
+void memberMustBeNonStaticField(StaticMember s) {
+ takesRef(s.staticMember);
+// CHECK18: (const StaticMember &s) {\ntakesRef(s.staticMember);\n}
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:248:3-248:27 %s | FileCheck --check-prefix=CHECK18 %s
+
+class ClassWithMethod2 {
+public:
+ ClassWithMethod member;
+};
+
+class ClassWithMethod;
+
+void nonConstMethodCallImpliesNonConstReceiver2(ClassWithMethod2 x) {
+ x.member.method();
+// CHECK19: (ClassWithMethod2 &x) {\nreturn x.member.method();\n}
+// CHECK20: (const ClassWithMethod2 &x) {\nreturn x.member.method();\n}
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:261:3-261:20 %s | FileCheck --check-prefix=CHECK19 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:261:3-261:20 %s -DUSECONST | FileCheck --check-prefix=CHECK20 %s
+
+class PrivateInstaceVariablesCallRefsBase {
+ int x;
+};
+
+class PrivateInstanceVariablesCallRefs2: public PrivateInstaceVariablesCallRefsBase {
+ int y;
+ Rectangle r;
+
+ void callsTakeRef() {
+ takesRef(this->x);
+ takesRef((this)->y);
+ takesRef(static_cast<PrivateInstaceVariablesCallRefsBase *>(this)->x);
+ takesRef((0, ((const_cast<PrivateInstanceVariablesCallRefs2 *>(this)->r.width))));
+ }
+// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef(object.x);\n}
+// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef((object).y);\n}
+// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef(static_cast<PrivateInstaceVariablesCallRefsBase *>(&object)->x);\n}
+// CHECK21: (PrivateInstanceVariablesCallRefs2 &object) {\ntakesRef((0, ((const_cast<PrivateInstanceVariablesCallRefs2 *>(&object)->r.width))));\n}
+};
+// RUN: clang-refactor-test perform -action extract -selected=%s:277:5-277:22 -selected=%s:278:5-278:24 -selected=%s:279:5-279:74 -selected=%s:280:5-280:86 %s | FileCheck --check-prefix=CHECK21 %s
diff --git a/test/Refactor/Extract/extract-reference-of-captured-variable.mm b/test/Refactor/Extract/extract-reference-of-captured-variable.mm
new file mode 100644
index 0000000..9d0f9c2
--- /dev/null
+++ b/test/Refactor/Extract/extract-reference-of-captured-variable.mm
@@ -0,0 +1,50 @@
+
+#ifdef USECONST
+#define CONST const
+#else
+#define CONST
+#endif
+
+typedef struct {
+ int width, height;
+} Rectangle;
+
+@interface I
+
+- (int)takesRef:(CONST int &)x;
++ (int)takesRef:(CONST int &)x;
+- (int)takesVal:(int)x;
+- (int)takesStructRef:(CONST Rectangle &)r;
+
+@end
+
+void methodTakesRef(I *i, int x, Rectangle r) {
+ [i takesRef: x];
+// CHECK1: extracted(I *i, int &x) {\nreturn [i takesRef: x];\n}
+// CHECK2: extracted(I *i, int x) {\nreturn [i takesRef: x];\n}
+ [I takesRef: x];
+// CHECK1: extracted(int &x) {\nreturn [I takesRef: x];\n}
+// CHECK2: extracted(int x) {\nreturn [I takesRef: x];\n}
+ [i takesVal: x];
+// CHECK1: extracted(I *i, int x) {\nreturn [i takesVal: x];\n}
+// CHECK2: extracted(I *i, int x) {\nreturn [i takesVal: x];\n}
+ [i takesStructRef: r];
+// CHECK1: extracted(I *i, Rectangle &r) {\nreturn [i takesStructRef: r];\n}
+// CHECK2: extracted(I *i, const Rectangle &r) {\nreturn [i takesStructRef: r];\n}
+ [I takesRef: (r).width];
+// CHECK1: extracted(Rectangle &r) {\nreturn [I takesRef: (r).width];\n}
+// CHECK2: extracted(const Rectangle &r) {\nreturn [I takesRef: (r).width];\n}
+}
+
+class PrivateInstanceVariablesMethodCallRefs {
+ int x;
+
+ void methodTakesRef(I *j) {
+ [j takesRef: x];
+// CHECK1: extracted(I *j, int &x) {\nreturn [j takesRef: x];\n}
+// CHECK2: extracted(I *j, int x) {\nreturn [j takesRef: x];\n}
+ }
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:22:3-22:18 -selected=%s:25:3-25:18 -selected=%s:28:3-28:18 -selected=%s:31:3-31:24 -selected=%s:34:3-34:26 -selected=%s:43:5-43:20 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:22:3-22:18 -selected=%s:25:3-25:18 -selected=%s:28:3-28:18 -selected=%s:31:3-31:24 -selected=%s:34:3-34:26 -selected=%s:43:5-43:20 %s -DUSECONST | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/extract-statement-macros.cpp b/test/Refactor/Extract/extract-statement-macros.cpp
new file mode 100644
index 0000000..b412a34
--- /dev/null
+++ b/test/Refactor/Extract/extract-statement-macros.cpp
@@ -0,0 +1,47 @@
+#define INT int
+#define MACRO INT y = x * x
+
+void extractStatementsTrimComments(int x) {
+ INT y = 0;
+
+ // comment
+ MACRO;
+
+ int z = 0;
+}
+// CHECK1: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-2]]:13
+// CHECK2: Initiated the 'extract' action at [[@LINE-8]]:3 -> [[@LINE-5]]:9
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:6:1-10:12 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action extract -selected=%s:5:3-9:1 %s | FileCheck --check-prefix=CHECK2 %s
+
+#define BLOCK __attribute__((__blocks__(byref)))
+void macroAtDeclStmt() {
+ // attr-begin: +1:38
+ BLOCK const char *Message = "HELLO";
+ int X = 123;
+ // attr-end: -1:13
+}
+// CHECK3: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-3]]:15
+// RUN: clang-refactor-test initiate -action extract -selected=attr %s -fblocks | FileCheck --check-prefix=CHECK3 %s
+
+#define MUT(x) (--(x))
+
+void macroExtractionEndsInMacroArgument(int x, int y) { // CHECK4: "static void extracted(int &x, int &y) {\ny = MUT(x);\n}\n\n" [[@LINE]]:1
+// CHECK5: "static int extracted(int &x) {\nreturn MUT(x);\n}\n\n" [[@LINE-1]]:1
+
+ // macro-arg-expr-begin: +3:7
+ // macro-arg-end1-begin: +2:1
+ // macro-arg-end2-begin: +1:1
+ y = MUT(x); // comment
+ // macro-arg-end1-end: -1:25
+ // macro-arg-end2-end: -2:14
+ // macro-arg-expr-end: -3:13
+
+ // CHECK4: "extracted(x, y)" [[@LINE-5]]:3 -> [[@LINE-5]]:13
+ // CHECK5: "extracted(x)" [[@LINE-6]]:7 -> [[@LINE-6]]:13
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-end1 %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-end2 %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test perform -action extract -selected=macro-arg-expr %s | FileCheck --check-prefix=CHECK5 %s
diff --git a/test/Refactor/Extract/extract-statements.cpp b/test/Refactor/Extract/extract-statements.cpp
new file mode 100644
index 0000000..4861d11
--- /dev/null
+++ b/test/Refactor/Extract/extract-statements.cpp
@@ -0,0 +1,178 @@
+
+struct Rectangle { int width, height; };
+
+void extractStatement(const Rectangle &r) {
+ int area = r.width * r.height;
+// CHECK1: "static void extracted(const Rectangle &r) {\nint area = r.width * r.height;\n}\n\n"
+// CHECK1-NEXT: "extracted(r);" [[@LINE-2]]:3 -> [[@LINE-2]]:33
+ if (r.width) {
+ int x = r.height;
+ }
+// CHECK1: "static void extracted(const Rectangle &r) {\nif (r.width) {\n int x = r.height;\n }\n}\n\n"
+// CHECK1-NEXT: "extracted(r);" [[@LINE-4]]:3 -> [[@LINE-2]]:4
+ if (r.width) {
+ int x = r.height;
+ } ; // This semicolon shouldn't be extracted.
+// CHECK1: "static void extracted(const Rectangle &r) {\nif (r.width) {\n int x = r.height;\n }\n}\n\n"
+// CHECK1-NEXT: "extracted(r);" [[@LINE-4]]:3 -> [[@LINE-2]]:4
+ do {
+ } while (true) ;
+// CHECK1: "static void extracted() {\ndo {\n } while (true) ;\n}\n\n"
+// CHECK1-NEXT: "extracted();" [[@LINE-3]]:3 -> [[@LINE-2]]:19
+ do {
+ } while (true) /*we still want to take this semicolon*/ ;
+// CHECK1: "static void extracted() {\ndo {\n } while (true) /*we still want to take this semicolon*/ ;\n}\n\n"
+// CHECK1-NEXT: "extracted();" [[@LINE-3]]:3 -> [[@LINE-2]]:60
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:5:3-5:32 -selected=%s:8:3-10:4 -selected=%s:13:3-15:4 -selected=%s:18:3-19:17 -selected=%s:22:3-23:17 %s | FileCheck --check-prefix=CHECK1 %s
+;
+void extractCantFindSemicolon() {
+ do {
+ } while (true)
+ // Add a semicolon in both the extracted and original function as we don't
+ // want to extract the semicolon below:
+ ;
+// CHECK2: "static void extracted() {\ndo {\n } while (true);\n}\n\n"
+// CHECK2-NEXT: "extracted();" [[@LINE-6]]:3 -> [[@LINE-5]]:17
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:31:3-32:17 %s | FileCheck --check-prefix=CHECK2 %s
+
+void extractedStmtNoNeedForSemicolon() {
+ {
+ int x = 0;
+ }
+// CHECK3: "static void extracted() {\n{\n int x = 0;\n }\n}\n\n"
+ switch (2) {
+ case 1:
+ break;
+ case 2:
+ break;
+ }
+// CHECK3: "static void extracted() {\nswitch (2) {\n case 1:\n break;\n case 2:\n break;\n }\n}\n\n"
+ while (true) {
+ int x = 0;
+ }
+// CHECK3: "static void extracted() {\nwhile (true) {\n int x = 0;\n }\n}\n\n"
+ for (int i = 0; i < 10; ++i) {
+ }
+// CHECK3: "static void extracted() {\nfor (int i = 0; i < 10; ++i) {\n }\n}\n\n"
+ struct XS {
+ int *begin() { return 0; }
+ int *end() { return 0; }
+ };
+ XS xs;
+ for (int i : xs) {
+ }
+// CHECK3: "static void extracted(const XS &xs) {\nfor (int i : xs) {\n }\n}\n\n"
+ try { int x = 0; }
+ catch (const int &i) {
+ int y = i;
+ }
+// CHECK3: "static void extracted() {\ntry { int x = 0; }\n catch (const int &i) {\n int y = i;\n }\n}\n\n"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:42:3-44:4 -selected=%s:46:3-51:4 -selected=%s:53:3-55:4 -selected=%s:57:3-58:4 -selected=%s:65:3-66:4 -selected=%s:68:3-71:4 %s -std=c++11 | FileCheck --check-prefix=CHECK3 %s
+;
+void extractStatementRange(int x) {
+ x = 2;
+ int y = 0;
+ extractStatementRange(x);
+ if (x == 2) {
+ int z = 0;
+ }
+ x = 2;
+
+// CHECK4: "static void extracted(int x) {\nextractStatementRange(x);\n if (x == 2) {\n int z = 0;\n }\n}\n\n" [[@LINE-9]]:1
+// CHECK4-NEXT: "extracted(x);" [[@LINE-7]]:3 -> [[@LINE-4]]:4
+
+// CHECK4: "static void extracted(int x) {\nint y = 0;\n extractStatementRange(x);\n}\n\n" [[@LINE-12]]:1
+// CHECK4-NEXT: "extracted(x)" [[@LINE-11]]:3 -> [[@LINE-10]]:27
+
+// CHECK4: "static void extracted(int &x) {\nx = 2;\n int y = 0;\n extractStatementRange(x);\n}\n\n" [[@LINE-15]]:1
+// CHECK4-NEXT: "extracted(x)" [[@LINE-15]]:3 -> [[@LINE-13]]:27
+
+// CHECK4: "static void extracted(int &x) {\nx = 2;\n int y = 0;\n}\n\n" [[@LINE-18]]:1
+// CHECK4-NEXT: "extracted(x);" [[@LINE-18]]:3 -> [[@LINE-17]]:13
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:80:3-83:4 -selected=%s:79:3-80:6 -selected=%s:78:3-80:6 -selected=%s:78:3-79:6 %s | FileCheck --check-prefix=CHECK4 %s
+
+void extractedVariableUsedAndDefinedInExtractedCode(int x) {
+ int y = x;
+ if (y == 1) {
+ int z = 0;
+ }
+// CHECK5: "static void extracted(int x) {\nint y = x;\n if (y == 1) {\n int z = 0;\n }\n}\n\n"
+// CHECK5-NEXT: "extracted(x);"
+// CHECK5: "static void extracted(int y) {\nif (y == 1) {\n int z = 0;\n }\n}\n\n"
+// CHECK5-NEXT: "extracted(y);"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:102:2-105:4 -selected=%s:103:2-105:4 %s | FileCheck --check-prefix=CHECK5 %s
+
+void extractAssignmentAsStatementOrExpr(int x) {
+ x = 2;
+// CHECK6: "static void extracted(int &x) {\nx = 2;\n}\n\n"
+ x = x = 3;
+// CHECK6: "static int extracted(int &x) {\nreturn x = 3;\n}\n\n"
+ (void)(x = 4);
+// CHECK6: "static int extracted(int &x) {\nreturn x = 4;\n}\n\n"
+ if (x = 5) {
+ }
+// CHECK6: "static int extracted(int &x) {\nreturn x = 5;\n}\n\n"
+ if (true)
+ x = 6;
+// CHECK6: "static void extracted(int &x) {\nx = 6;\n}\n\n"
+ bool b = 2;
+ if (b = false) {
+ }
+// CHECK6: "static bool extracted(bool &b) {\nreturn b = false;\n}\n\n"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:115:3-115:8 -selected=%s:117:7-117:12 -selected=%s:119:10-119:15 -selected=%s:121:7-121:12 -selected=%s:125:5-125:10 -selected=%s:128:7-128:16 %s | FileCheck --check-prefix=CHECK6 %s
+
+void extractCompoundAssignmentAsStatementOrExpr(int x) {
+ x += 2;
+// CHECK7: "static void extracted(int &x) {\nx += 2;\n}\n\n"
+ x = x += 3;
+// CHECK7: "static int extracted(int &x) {\nreturn x += 3;\n}\n\n"
+ if (x *= 4) {
+ }
+// CHECK7: "static int extracted(int &x) {\nreturn x *= 4;\n}\n\n"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:136:3-136:9 -selected=%s:138:7-138:13 -selected=%s:140:7-140:13 %s | FileCheck --check-prefix=CHECK7 %s
+
+int inferReturnTypeFromReturnStatement(int x) {
+ if (x == 0) {
+ return x;
+ }
+ if (x == 1) {
+ return x + 1;
+ }
+ return x + 2;
+}
+// CHECK8: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n return x + 2;\n}\n\n"
+// CHECK8: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n}\n\n"
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:151:3-154:15 -selected=%s:151:3-153:4 %s | FileCheck --check-prefix=CHECK8 %s
+
+void careForNonCompoundSemicolons() {
+// if-open-begin:+1:1
+ if (true)
+ careForNonCompoundSemicolons();
+// if-open-end: -1:35
+// CHECK9: "static void extracted() {\nif (true)\n careForNonCompoundSemicolons();\n}\n\n"
+// CHECK9: "extracted();" [[@LINE-4]]:3 -> [[@LINE-3]]:36
+
+// for-open-begin:+1:1
+ for (int i = 0; i < 10; ++i)
+ while (i != 0)
+ ;
+// for-open-end: +0:1
+// CHECK9:"static void extracted() {\nfor (int i = 0; i < 10; ++i)\n while (i != 0)\n ;\n}\n\n" [[@LINE-15]]:1
+// CHECK9: "extracted();" [[@LINE-5]]:3 -> [[@LINE-3]]:8
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=if-open -selected=for-open %s | FileCheck --check-prefix=CHECK9 %s
diff --git a/test/Refactor/Extract/extract-statements.m b/test/Refactor/Extract/extract-statements.m
new file mode 100644
index 0000000..db86ab8
--- /dev/null
+++ b/test/Refactor/Extract/extract-statements.m
@@ -0,0 +1,57 @@
+@interface NSArray
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
+@end
+
+void extractedStmtNoNeedForSemicolon(NSArray *array) {
+ for (id i in array) {
+ int x = 0;
+ }
+// CHECK1: "static void extracted(NSArray *array) {\nfor (id i in array) {\n int x = 0;\n }\n}\n\n"
+ id lock;
+ @synchronized(lock) {
+ int x = 0;
+ }
+// CHECK1: "static void extracted(id lock) {\n@synchronized(lock) {\n int x = 0;\n }\n}\n\n"
+ @autoreleasepool {
+ int x = 0;
+ }
+// CHECK1: "static void extracted() {\n@autoreleasepool {\n int x = 0;\n }\n}\n\n"
+ @try {
+ int x = 0;
+ } @finally {
+ }
+// CHECK1: "static void extracted() {\n@try {\n int x = 0;\n } @finally {\n }\n}\n\n"
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:6:3-8:4 -selected=%s:11:3-13:4 -selected=%s:15:3-17:4 -selected=%s:19:3-22:4 %s | FileCheck --check-prefix=CHECK1 %s
+
+@interface I
+
+@end
+
+@implementation I
+
+- (int)inferReturnTypeFromReturnStatement:(int)x {
+ if (x == 0) {
+ return x;
+ }
+ if (x == 1) {
+ return x + 1;
+ }
+ return x + 2;
+}
+// CHECK2: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n return x + 2;\n}\n\n"
+// CHECK2: "static int extracted(int x) {\nif (x == 1) {\n return x + 1;\n }\n}\n\n"
+
+// RUN: clang-refactor-test perform -action extract -selected=%s:38:3-41:15 -selected=%s:38:3-40:4 %s | FileCheck --check-prefix=CHECK2 %s
+
+@end
+
+void partiallySelectedWithImpCastCrash(I *object) {
+// partially-selected-begin: +1:3
+ object;
+// partially-selected-end: +1:11
+// comment
+// CHECK3: "static void extracted(I *object) {\nobject;\n}\n\n"
+// RUN: clang-refactor-test perform -action extract -selected=partially-selected %s | FileCheck --check-prefix=CHECK3 %s
+}
diff --git a/test/Refactor/Extract/extract-whole-source-construct.cpp b/test/Refactor/Extract/extract-whole-source-construct.cpp
new file mode 100644
index 0000000..93df9e0
--- /dev/null
+++ b/test/Refactor/Extract/extract-whole-source-construct.cpp
@@ -0,0 +1,120 @@
+void extractEntireIfWhenSelectedBody(int x) {
+ if (x == 1)
+ {
+ int z = x + 2;
+ }
+ else if (x == 3)
+ {
+ int y = x + 1;
+ }
+ // CHECK1: Initiated the 'extract' action at [[@LINE-8]]:3 -> [[@LINE-1]]:4
+}
+
+// RUN: clang-refactor-test initiate -action extract -selected=%s:3:3-5:4 -selected=%s:7:3-9:4 -selected=%s:4:5-8:19 -selected=%s:6:12-9:4 %s | FileCheck --check-prefix=CHECK1 %s
+
+void extractEntireSourceConstructWhenSelectedBody(int x) {
+ switch (x)
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ }
+// CHECK2: Initiated the 'extract' action at [[@LINE-7]]:3 -> [[@LINE-1]]:4
+// RUN: clang-refactor-test initiate -action extract -selected=%s:17:4-22:4 %s | FileCheck --check-prefix=CHECK2 %s
+
+ for (int i = 0; i < x; ++i)
+ {
+ break;
+ }
+// CHECK3: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-1]]:4
+// RUN: clang-refactor-test initiate -action extract -selected=%s:27:4-29:4 %s | FileCheck --check-prefix=CHECK3 %s
+
+ while (x < 0)
+ {
+ break;
+ }
+// CHECK4: Initiated the 'extract' action at [[@LINE-4]]:3 -> [[@LINE-1]]:4
+// RUN: clang-refactor-test initiate -action extract -selected=%s:34:4-36:4 %s | FileCheck --check-prefix=CHECK4 %s
+
+ do
+ {
+ break;
+ }
+ while (x != 0);
+// CHECK5: Initiated the 'extract' action at [[@LINE-5]]:3 -> [[@LINE-1]]:17
+// RUN: clang-refactor-test initiate -action extract -selected=%s:41:4-43:4 %s | FileCheck --check-prefix=CHECK5 %s
+}
+
+void extractJustTheCompoundStatement() {
+ {
+ {
+ int x = 0;
+ }
+ }
+// CHECK6: Initiated the 'extract' action at [[@LINE-4]]:5 -> [[@LINE-2]]:6
+// RUN: clang-refactor-test initiate -action extract -selected=%s:51:5-53:6 %s | FileCheck --check-prefix=CHECK6 %s
+}
+
+void extractSwitch(int x) {
+ switch (x) {
+ extractSwitch(x - 1);
+
+ case 0:
+ extractSwitch(x + 1);
+ break;
+
+ // comment
+ case 1:
+ extractSwitch(x + 2);
+ break;
+
+ default:
+ extractSwitch(x + 2);
+ break;
+
+ }
+// CHECK7: Initiated the 'extract' action at [[@LINE-17]]:3 -> [[@LINE-1]]:4
+// RUN: clang-refactor-test initiate -action extract -selected=%s:61:3-64:27 -selected=%s:63:5-63:11 -selected=%s:68:5-69:27 -selected=%s:72:5-72:13 %s | FileCheck --check-prefix=CHECK7 %s
+}
+
+class AClass {
+ void method();
+
+ void extractWholeCallWhenJustMethodSelected() {
+ method();
+ }
+};
+// CHECK8: Initiated the 'extract' action at [[@LINE-3]]:5 -> [[@LINE-3]]:13
+// RUN: clang-refactor-test initiate -action extract -selected=%s:85:5-85:6 -selected=%s:85:5-85:11 %s | FileCheck --check-prefix=CHECK8 %s
+
+void extractWholeCallWhenJustMethodSelected() {
+ AClass a;
+ a.method();
+}
+// CHECK9: Initiated the 'extract' action at [[@LINE-2]]:3 -> [[@LINE-2]]:13
+// RUN: clang-refactor-test initiate -action extract -selected=%s:93:3-93:7 -selected=%s:93:5-93:11 %s | FileCheck --check-prefix=CHECK9 %s
+;
+void avoidExtractingTooMuch(bool boolean) { // CHECK10: void extracted() {\nint x = 2;\n // avoid-{{.*}}-end:+1:15\n int y = x;\n}\n\n" [[@LINE]]:1
+ if (boolean) {
+ // avoid-too-much-begin:+1:1 // CHECK10: "extracted();" [[@LINE+1]]:5 -> [[@LINE+3]]:15
+ int x = 2;
+ // avoid-too-much-end:+1:15
+ int y = x;
+ } else {
+ int z = 3;
+ }
+
+ // switch-casesel-begin: +4:3 // switch-casesel-end: +4:4 // CHECK10: void extracted(bool boolean) {\nswitch ((int)boolean) {\n case 0:\n avoidExtractingTooMuch(boolean);\n avoidExtractingTooMuch(boolean);\n break;\n }\n}
+ // switch-case0-begin: +4:5 // switch-case0-end: +4:36 // CHECK10: void extracted(bool boolean) {\navoidExtractingTooMuch(boolean);\n}
+ // switch-case1-begin: +3:5 // switch-case1-end: +4:36 // CHECK10: void extracted(bool boolean) {\navoidExtractingTooMuch(boolean);\n avoidExtractingTooMuch(boolean);\n}\n\n"
+ switch ((int)boolean) {
+ case 0:
+ avoidExtractingTooMuch(boolean);
+ avoidExtractingTooMuch(boolean);
+ break;
+ }
+ // CHECK10: "extracted(boolean)" [[@LINE-4]]:5 -> [[@LINE-3]]:36
+}
+
+// RUN: clang-refactor-test perform -action extract -selected=avoid-too-much -selected=switch-casesel -selected=switch-case0 -selected=switch-case1 %s | FileCheck --check-prefix=CHECK10 %s
diff --git a/test/Refactor/Extract/extracted-declaration-name.mm b/test/Refactor/Extract/extracted-declaration-name.mm
new file mode 100644
index 0000000..f9d7228
--- /dev/null
+++ b/test/Refactor/Extract/extracted-declaration-name.mm
@@ -0,0 +1,50 @@
+
+int compute(int n, int x, int y) {
+ int sum = 0;
+ for (int i = 0; i < n; ++i) {
+// extract-func-begin: +1:12
+ sum += (x - i) * (y + i);
+// extract-func-end: -1:29
+ }
+ return sum;
+}
+// RUN: clang-refactor-test perform -action extract -emit-associated -selected=extract-func %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: "static int extracted(int i, int x, int y) {\nreturn (x - i) * (y + i);\n}\n\n" [[@LINE-10]]:1 -> [[@LINE-10]]:1 [Symbol extracted-decl 0 1:12 -> 1:21]
+// CHECK1-NEXT: "extracted(i, x, y)" [[@LINE-7]]:12 -> [[@LINE-7]]:29 [Symbol extracted-decl-ref 0 1:1 -> 1:10]
+
+struct Struct {
+ int func(int y) { return y; }
+
+ int compute(int x, int y);
+};
+
+int Struct::compute(int x, int y) {
+// extract-member-func-begin: +1:10
+ return x * func(y + x) + y;
+// extract-member-func-end: -1:25
+}
+// RUN: clang-refactor-test perform -action extract-method -emit-associated -selected=extract-member-func %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK2: "int extracted(int x, int y);\n\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3 [Symbol extracted-decl 0 1:5 -> 1:14]
+// CHECK2-NEXT: "int Struct::extracted(int x, int y) {\nreturn x * func(y + x);\n}\n\n" [[@LINE-7]]:1 -> [[@LINE-7]]:1 [Symbol extracted-decl 0 1:13 -> 1:22]
+// CHECK2-NEXT: "extracted(x, y)" [[@LINE-6]]:10 -> [[@LINE-6]]:25 [Symbol extracted-decl-ref 0 1:1 -> 1:10]
+
+@interface I
+
+@property int p;
+
+- (void)foo:(int)x with:(int)y;
+
+@end
+
+@implementation I
+
+- (void)foo:(int)x with:(int)y {
+// extract-selector-begin: +1:1
+ int m = compute(10, self.p + y, x);
+// extract-selector-end: +0:1
+}
+
+@end
+// RUN: clang-refactor-test perform -action extract-method -emit-associated -selected=extract-selector %s | FileCheck --check-prefix=CHECK3 %s
+// CHECK3: "- (void)extracted:(int)x y:(int)y {\nint m = compute(10, self.p + y, x);\n}\n\n" [[@LINE-8]]:1 -> [[@LINE-8]]:1 [Symbol extracted-decl 0 1:9 -> 1:18 1:26 -> 1:27]
+// CHECK3-NEXT: "[self extracted:x y:y];" [[@LINE-7]]:3 -> [[@LINE-7]]:38 [Symbol extracted-decl-ref 0 1:7 -> 1:16 1:19 -> 1:20]
diff --git a/test/Refactor/Extract/return-c-bool.c b/test/Refactor/Extract/return-c-bool.c
new file mode 100644
index 0000000..0cb5a64
--- /dev/null
+++ b/test/Refactor/Extract/return-c-bool.c
@@ -0,0 +1,28 @@
+#ifndef __cplusplus
+#define bool _Bool
+#define true 1
+#define false 0
+#endif
+typedef struct {
+ bool b;
+} HasBool;
+
+bool boolType(bool b, HasBool *s) {
+ bool x = b && true;
+ bool y = boolType(b, s);
+ bool z = s->b;
+ bool a = !b;
+ return (b || true);
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:12-11:21 -selected=%s:12:12-12:26 --selected=%s:13:12-13:16 --selected=%s:14:12-14:14 --selected=%s:15:10-15:21 %s | FileCheck %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:11:12-11:21 -selected=%s:12:12-12:26 --selected=%s:13:12-13:16 --selected=%s:14:12-14:14 --selected=%s:15:10-15:21 %s -x c++ | FileCheck %s
+// CHECK: "static bool extracted
+;
+int boolCompareOps(int x, int y) {
+ bool a = x == y;
+ bool b = x >= y;
+ bool c = ((x < y));
+ return 0;
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:22:12-22:18 -selected=%s:23:12-23:18 --selected=%s:24:12-24:21 %s | FileCheck %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:22:12-22:18 -selected=%s:23:12-23:18 --selected=%s:24:12-24:21 %s -x c++ | FileCheck %s
diff --git a/test/Refactor/Extract/return-correct-stl-type.cpp b/test/Refactor/Extract/return-correct-stl-type.cpp
new file mode 100644
index 0000000..f7b9444
--- /dev/null
+++ b/test/Refactor/Extract/return-correct-stl-type.cpp
@@ -0,0 +1,51 @@
+namespace std {
+
+struct Traits {
+ typedef char char_type;
+};
+
+template<typename TraitsType>
+struct BasicString {
+ typedef typename TraitsType::char_type value_type;
+ value_type value() const;
+ const value_type *data() const;
+};
+
+template<typename TraitsType>
+BasicString<TraitsType>
+operator+(const BasicString<TraitsType> &lhs,
+ const BasicString<TraitsType> &rhs);
+template<typename TraitsType>
+BasicString<TraitsType>
+operator+(const BasicString<TraitsType> &lhs,
+ const char *rhs);
+
+template<typename TraitsType>
+struct BasicString;
+typedef BasicString<Traits> String;
+
+} // end namespace std
+
+void returnCharTypeNotUselessValueType() {
+// CHECK1: "static char extracted(const std::String &x) {\nreturn x.value();\n}\n\n" [[@LINE-1]]:1
+// CHECK1: "static const char * extracted(const std::String &x) {\nreturn x.data();\n}\n\n" [[@LINE-2]]:1
+ std::String x;
+// return-char-begin: +1:9
+ (void)x.value();
+// return-char-end: +0:1
+// return-data-begin: +1:9
+ (void)x.data();
+// return-data-end: +0:1
+} // RUN: clang-refactor-test perform -action extract -selected=return-char -selected=return-data %s | FileCheck --check-prefix=CHECK1 %s
+
+void operatorTypeInferral() {
+// CHECK2: "static std::String extracted(const std::String &x) {\nreturn x + "hello";\n}\n\n" [[@LINE-1]]:1
+// CHECK2: "static std::String extracted(const std::String &x) {\nreturn x + x;\n}\n\n" [[@LINE-2]]:1
+ std::String x;
+// infer-string1-begin: +1:10
+ (void)(x + "hello");
+// infer-string1-end: -1:21
+// infer-string2-begin: +1:10
+ (void)(x + x);
+// infer-string2-end: -1:15
+} // RUN: clang-refactor-test perform -action extract -selected=infer-string1 -selected=infer-string2 %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Extract/return-objc-bool.m b/test/Refactor/Extract/return-objc-bool.m
new file mode 100644
index 0000000..109ae5b
--- /dev/null
+++ b/test/Refactor/Extract/return-objc-bool.m
@@ -0,0 +1,89 @@
+#ifdef SIGNED
+typedef signed char BOOL;
+#else
+
+#ifndef __cplusplus
+#define bool _Bool
+#endif
+
+typedef bool BOOL;
+
+#endif
+
+#define YES __objc_yes
+#define NO __objc_no
+
+typedef struct {
+ BOOL b;
+} HasBool;
+
+// Always prefer to use BOOL in the Objective-C methods.
+
+@interface I
+@end
+
+@implementation I
+
+- (BOOL)boolType:(BOOL)b with:(HasBool*)s {
+ BOOL x = b && YES;
+ BOOL y = [self boolType: b with: s];
+ BOOL z = s->b;
+ BOOL a = !b;
+ return (b == NO);
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s | FileCheck --check-prefix=CHECKBOOL %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:28:12-28:20 -selected=%s:29:12-29:38 -selected=%s:30:12-30:16 -selected=%s:31:12-31:14 -selected=%s:32:10-32:19 %s -x objective-c++ -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s
+
+// CHECKBOOL: "static BOOL extracted
+
+#ifdef __cplusplus
+
+// Prefer BOOL even in Objective-C++ methods.
+
+- (BOOL)chooseBOOLEvenInCPlusPlus:(bool)b and:(bool)c {
+ bool x = b && c;
+ bool n = !b;
+}
+
+#endif
+// RUN: clang-refactor-test perform -action extract -selected=%s:46:12-46:18 -selected=%s:47:12-47:14 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:46:12-46:18 -selected=%s:47:12-47:14 %s -x objective-c++ -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s
+
+@end
+
+#ifdef __cplusplus
+
+// In Objective-C++ functions/methods we want to pick the type based on the expression.
+
+BOOL boolObjCFunction(BOOL b, BOOL c) {
+ BOOL x = b && c;
+ BOOL y = boolObjCFunction(b, c);
+ return b;
+}
+// RUN: clang-refactor-test perform -action extract -selected=%s:61:12-61:18 -selected=%s:62:12-62:34 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:61:12-61:18 -selected=%s:62:12-62:34 %s -x objective-c++ -D SIGNED | FileCheck --check-prefix=CHECKBOOL %s
+
+bool boolCPlusPlusFunction(bool b, bool c) {
+ bool x = b && c;
+ bool y = boolCPlusPlusFunction(b, c);
+ return b;
+}
+// CHECKNORMAL: "static bool extracted
+// RUN: clang-refactor-test perform -action extract -selected=%s:69:12-69:18 -selected=%s:70:12-70:39 %s -x objective-c++ | FileCheck --check-prefix=CHECKNORMAL %s
+
+class AClass {
+ AClass(BOOL b, BOOL c, bool d, bool e) {
+ BOOL x = b && c;
+ bool y = d && e;
+ }
+ void method(BOOL b, BOOL c, bool d, bool e) {
+ BOOL x = b || c;
+ bool y = d || e;
+ }
+};
+// RUN: clang-refactor-test perform -action extract -selected=%s:78:14-78:20 -selected=%s:82:14-84:20 %s -x objective-c++ | FileCheck --check-prefix=CHECKBOOL %s
+// RUN: clang-refactor-test perform -action extract -selected=%s:79:14-79:20 -selected=%s:83:14-83:20 %s -x objective-c++ | FileCheck --check-prefix=CHECKNORMAL %s
+
+#endif
diff --git a/test/Refactor/Extract/return-objc-generic-argument-type.m b/test/Refactor/Extract/return-objc-generic-argument-type.m
new file mode 100644
index 0000000..42d9770
--- /dev/null
+++ b/test/Refactor/Extract/return-objc-generic-argument-type.m
@@ -0,0 +1,40 @@
+// RUN: clang-refactor-test perform -action extract -selected=prop -selected=imp-prop -selected=class-prop -selected=class-prop2 -selected=class-method %s | FileCheck %s
+
+@interface NSObject
+@end
+
+@interface Array<Element> : NSObject
+
+@property Element prop;
+
+- (Element)get;
+
+@property (class) Array<Element> *classProp;
+
++ (Element *)classGet;
+
+@end
+
+void foo(Array<NSObject *> *objects) {
+// prop-begin: +1:3
+ objects.prop;
+// prop-end: -1:15
+// CHECK: "static NSObject * extracted(Array<NSObject *> *objects) {\nreturn objects.prop;\n}\n\n"
+// imp-prop-begin: +1:3
+ objects.get;
+// imp-prop-end: -1:14
+// CHECK: "static NSObject * extracted(Array<NSObject *> *objects) {\nreturn objects.get;\n}\n\n"
+// class-prop-begin: +1:3
+ Array.classProp;
+// class-prop-end: -1:30
+// CHECK: "static Array * extracted() {\nreturn Array.classProp;\n}\n\n"
+ typedef Array<NSObject *> ObjectArray;
+// class-prop2-begin: +1:3
+ [ObjectArray classProp];
+// class-prop2-end: -1:26
+// CHECK: "static Array<NSObject *> * extracted() {\nreturn [ObjectArray classProp];\n}\n\n"
+// class-method-begin: +1:3
+ [ObjectArray classGet];
+// class-method-end: -1:25
+// CHECK: "static NSObject ** extracted() {\nreturn [ObjectArray classGet];\n}\n\n"
+}
diff --git a/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp
new file mode 100644
index 0000000..ba97e23
--- /dev/null
+++ b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.cpp
@@ -0,0 +1,63 @@
+
+struct AClass {
+ int method();
+ int method2();
+};
+struct AWrapperClass {
+ AClass &object(int x);
+};
+
+void duplicatesWithParens(AWrapperClass &wrapper) {
+ wrapper.object(0).method();
+// CHECK1: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-1]]:3
+ ((wrapper).object((0))).method();
+// CHECK2: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-1]]:4
+}
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:11:3-20 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:13:4-25 %s | FileCheck --check-prefix=CHECK2 %s
+
+
+void noDuplicatesWithParens(AWrapperClass &wrapper) {
+ wrapper.object(- 1).method();
+#ifndef DUPLICATE
+ wrapper.object((- 1)).method();
+#else
+ (wrapper).object(- 1).method();
+#endif
+// CHECK3: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-6]]:3
+
+ wrapper.object(1 + 2).method();
+#ifndef DUPLICATE
+ wrapper.object((1 + 2)).method();
+#else
+ ((wrapper)).object(1 + 2).method();
+#endif
+// CHECK4: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-6]]:3
+
+ wrapper.object(true ? 0 : 1).method();
+#ifndef DUPLICATE
+ wrapper.object((true ? 0 : 1)).method();
+#else
+ ((wrapper)).object(true ? (0) : (1)).method();
+#endif
+// CHECK5: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-6]]:3
+}
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:22:3-22 %s -DDUPLICATE | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:30:3-24 %s -DDUPLICATE | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:38:3-31 %s -DDUPLICATE | FileCheck --check-prefix=CHECK5 %s
+
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:22:1-32 -in=%s:30:1-34 -in=%s:38:1-41 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action!
+
+void noDuplicatesWhenSemanticsChange(AWrapperClass &wrapper) {
+ wrapper.object(0).method();
+ if (true) {
+ AWrapperClass wrapperBase;
+ AWrapperClass &wrapper = wrapperBase;
+ wrapper.object(0).method();
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:55:1-30 in=%s:59:1-32 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m
new file mode 100644
index 0000000..bafc192
--- /dev/null
+++ b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-duplicates.m
@@ -0,0 +1,32 @@
+
+@interface Object
+
+- (int)instanceMethod;
+
+@end
+
+@interface Wrapper
+
+- (Object *)returnsObject:(int)arg;
+
+- (Object *)classMethodReturnsObject;
++ (Object *)classMethodReturnsObject;
+
+@end
+
+void differentWrapperVariables(Wrapper *wrapper) {
+ [[wrapper returnsObject: 42] instanceMethod];
+ Wrapper *copyWrapper = wrapper;
+ if (wrapper) {
+ Wrapper *wrapper = copyWrapper;
+ [[wrapper returnsObject: 42] prop];
+ }
+ [[Wrapper classMethodReturnsObject] instanceMethod];
+ if (wrapper) {
+ __auto_type Wrapper = wrapper;
+ [[Wrapper classMethodReturnsObject] instanceMethod];
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:18:1-48 -in=%s:24:1-55 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action!
diff --git a/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp
new file mode 100644
index 0000000..de8b1d8
--- /dev/null
+++ b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.cpp
@@ -0,0 +1,109 @@
+
+struct AClass {
+ int method();
+ int method2();
+};
+struct AWrapperClass {
+ AClass &object();
+};
+
+void takesClass(AWrapperClass &wrapper) {
+ wrapper.object().method();
+ wrapper.object().method();
+ wrapper.object().method2();
+}
+// CHECK1: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3
+// CHECK2: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3
+// CHECK3: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3
+
+// Suggest extracting 'wrapper.object()'
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:11:3-19 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:12:3-19 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:13:3-19 %s | FileCheck --check-prefix=CHECK3 %s
+
+// CHECK-NO: Failed to initiate the refactoring action!
+
+// Avoid suggesting extraction of 'wrapper.object().method2()'
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:13:20-30 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test list-actions -at=%s:11:11 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Extract Repeated Expression
+
+AClass &returnsReference(int x);
+AClass &returnsAndTakesFunctionPointer(AClass& (*)(int));
+
+void checkReferenceCall() {
+ returnsReference(0).method();
+ returnsReference(0).method2();
+ returnsAndTakesFunctionPointer(returnsReference).method();
+ returnsAndTakesFunctionPointer(returnsReference).method2();
+}
+// CHECK4: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-5]]:3
+// CHECK5: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:36:3-22 %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:38:3-51 %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:36:23-32 -in=%s:37:23-32 -in=%s:38:52-61 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+struct WithFields {
+ int x, y;
+};
+struct WithFieldsOperators {
+ WithFields *operator ->();
+ WithFields &operator ()();
+
+ const WithFields &operator [](int x) const;
+ WithFields &operator [](int x);
+};
+
+void checkOperatorCalls(WithFieldsOperators &op, int id) {
+ op[id].x;
+ op[id].y;
+// CHECK6: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3
+ op().x;
+ op().x;
+// CHECK7: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3
+ op->x;
+ op->x;
+}
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:59:3-9 %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:62:3-7 %s | FileCheck --check-prefix=CHECK7 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:59:10-12 -in=%s:62:8-10 -in=%s:65:3-9 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+struct AWrapperClass2 {
+ AClass *object() const;
+};
+
+void checkPointerType(AWrapperClass *object, AWrapperClass2 *object2) {
+ object->object().method();
+ if (object) {
+ object->object().method2();
+ }
+// CHECK8: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-4]]:3
+ object2->object()->method();
+ int m = object2->object()->method2();
+// CHECK9: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3
+}
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:77:3-19 %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:82:3-20 %s | FileCheck --check-prefix=CHECK9 %s
+
+struct ConstVsNonConst {
+ int field;
+ void constMethod() const;
+ void method();
+};
+
+struct ConstVsNonConstWrapper {
+ const ConstVsNonConst &object() const;
+ ConstVsNonConst &object();
+};
+
+void checkFoo(ConstVsNonConstWrapper &object) {
+ object.object().constMethod();
+ object.object().method();
+}
+// CHECK10: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-3]]:3
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -at=%s:101:3 %s | FileCheck --check-prefix=CHECK10 %s
+
+// Check that the action can be initiate using a selection:
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -selected=%s:11:3-11:19 -selected=%s:11:15-11:19 -selected=%s:11:3-11:17 -selected=%s:11:15-11:18 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -selected=%s:11:3-11:22 -selected=%s:11:1-13:30 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m
new file mode 100644
index 0000000..11df71f
--- /dev/null
+++ b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-initiate.m
@@ -0,0 +1,106 @@
+
+@interface Base
+
+@end
+
+@interface Object: Base {
+ int ivar;
+}
+
+- (int)instanceMethod;
+
+@property int prop;
+@property void (^block)();
+
+@end
+
+@interface Wrapper
+
+- (Object *)returnsObject:(int)arg;
+
++ (Object *)classMethodReturnsObject;
+
+@property(class) Object *classObject;
+
+@property Object *object;
+
+@end
+
+void test(Wrapper *wrapper) {
+ [[wrapper returnsObject: 42] instanceMethod];
+ [[wrapper returnsObject: 42] prop];
+// CHECK1: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+// CHECK2: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+
+ wrapper.object.prop;
+ [wrapper.object instanceMethod];
+// CHECK3: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:3
+// CHECK4: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+
+ [[wrapper object] block];
+ [[wrapper object] instanceMethod];
+// CHECK5: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+// CHECK6: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+
+ [[Wrapper classMethodReturnsObject] instanceMethod];
+ [[Wrapper classMethodReturnsObject] prop];
+// CHECK7: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+// CHECK8: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:4
+
+ Wrapper.classObject.prop;
+ if (1)
+ [Wrapper.classObject instanceMethod];
+// CHECK9: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-3]]:3
+// CHECK10: Initiated the 'extract-repeated-expr-into-var' action at [[@LINE-2]]:6
+}
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:30:4-31 %s -fblocks | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:31:4-31 %s -fblocks | FileCheck --check-prefix=CHECK2 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:30:1-3 -in=%s:30:32-48 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:35:3-17 %s -fblocks | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:36:4-18 %s -fblocks | FileCheck --check-prefix=CHECK4 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:35:18-23 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:40:4-20 %s -fblocks | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:41:4-20 %s -fblocks | FileCheck --check-prefix=CHECK6 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:40:21-28 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:45:4-38 %s -fblocks | FileCheck --check-prefix=CHECK7 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:46:4-38 %s -fblocks | FileCheck --check-prefix=CHECK8 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:45:39-55 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:50:3-22 %s -fblocks | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:52:6-25 %s -fblocks | FileCheck --check-prefix=CHECK10 %s
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:50:23-28 -in=%s:51:1-9 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// CHECK-NO: Failed to initiate the refactoring action!
+
+void testInvalidMethod(Wrapper *ref) {
+ if (2)
+ [[ref classObject] instanceMethod];
+ [ref classObject].block();
+}
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -in=%s:81:6-23 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+@interface ImplicitPropertyWithoutGetter
+- (void) setValue: (int) value;
+@end
+void implicitPropertyWithoutGetter(ImplicitPropertyWithoutGetter *x) {
+ x.value = 0;
+ x.value = 1;
+}
+
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -at=%s:90:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// Prohibit ininiation in macros:
+
+#define MACROREF(X) X.object
+
+void prohibitMacroExpr(Wrapper *wrapper) {
+ // macro-prohibited: +1:3
+ wrapper.object.prop = 0;
+ MACROREF(wrapper).prop = 1;
+}
+
+// RUN: not clang-refactor-test initiate -action extract-repeated-expr-into-var -at=macro-prohibited %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp
new file mode 100644
index 0000000..4d8c400
--- /dev/null
+++ b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.cpp
@@ -0,0 +1,100 @@
+
+struct AClass {
+ void constMethod() const;
+ void method();
+};
+
+struct AWrapper {
+ const AClass &object(int x) const;
+ AClass &object(int x);
+};
+
+void takesClass(AWrapper &ref) {
+ int x = 0;
+ ref.object(x).constMethod();
+ int y = 0;
+ ref.object(x).method();
+}
+// CHECK1: "AClass &object = ref.object(x);\nobject" [[@LINE-4]]:3 -> [[@LINE-4]]:16
+
+// CHECK1-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:16
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:14:3 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:16:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+void variableNameSuggested(AWrapper &object) {
+#ifdef IN_COMPOUND
+ {
+#endif
+ object.object(21).constMethod();
+ object.object(21).method();
+#ifdef IN_COMPOUND
+ }
+#endif
+}
+// CHECK2: "AClass &object = object.object(21);\nobject" [[@LINE-6]]:3 -> [[@LINE-6]]:20
+
+// CHECK2-NEXT: "object" [[@LINE-7]]:3 -> [[@LINE-7]]:20
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:29:3 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:29:3 %s -D IN_COMPOUND | FileCheck --check-prefix=CHECK2 %s
+
+void takesClass2(AWrapper &ref) {
+ int x = 0;
+ if (x)
+ ref.object(x).constMethod();
+ ref.object(x).method();
+}
+// CHECK3: "AClass &object = ref.object(x);\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3
+// CHECK3-NEXT: "object" [[@LINE-4]]:5 -> [[@LINE-4]]:18
+// CHECK3-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:16
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:45:5 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:46:3 %s | FileCheck --check-prefix=CHECK3 %s
+
+void takesClass4(AWrapper &ref) {
+ int x = 0;
+ if (x) {
+ ref.object(x).constMethod();
+ ref.object(x).method();
+ }
+}
+// CHECK4: "AClass &object = ref.object(x);\nobject" [[@LINE-4]]:5 -> [[@LINE-4]]:18
+
+// CHECK4-NEXT: "object" [[@LINE-5]]:5 -> [[@LINE-5]]:18
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:58:5 %s | FileCheck --check-prefix=CHECK4 %s
+
+void insertIntoCommonCompound1(AWrapper &ref) {
+#ifdef EMBED
+ int x = 0;
+ while (true) {
+#endif
+ int x = 0;
+ if (x) {
+ if (true) {
+ int y = x;
+ ref.object(x).constMethod();
+ }
+ } else {
+ ref.object(x).method();
+ }
+// CHECK5: "AClass &object = ref.object(x);\n" [[@LINE-8]]:3 -> [[@LINE-8]]:3
+// CHECK5-NEXT: "object" [[@LINE-6]]:7 -> [[@LINE-6]]:20
+// CHECK5-NEXT: "object" [[@LINE-4]]:5 -> [[@LINE-4]]:18
+#ifdef EMBED
+ }
+#endif
+}
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:77:7 %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:80:5 %s -DEMBED | FileCheck --check-prefix=CHECK5 %s
+
+void checkFirstStmtInCompoundPlacement(AWrapper &ref) {
+ while (true) {
+ ref.object(20);
+ ref.object(20).method();
+// CHECK6: "AClass &object = ref.object(20);\nobject" [[@LINE-2]]:5 -> [[@LINE-2]]:19
+ }
+}
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:94:5 %s | FileCheck --check-prefix=CHECK6 %s
diff --git a/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m
new file mode 100644
index 0000000..23ed359
--- /dev/null
+++ b/test/Refactor/ExtractRepeatedExpression/extract-repeated-expr-perform.m
@@ -0,0 +1,98 @@
+
+@interface Object {
+ int ivar;
+}
+
+- (int)instanceMethod;
+
+@property int prop;
+@property void (^block)();
+
+@end
+
+@interface Wrapper
+
+- (Object *)returnsObject:(int)arg;
+
++ (Object *)classMethodReturnsObject;
+
+@property(class) Object *classObject;
+
+@property Object *object;
+
+@end
+
+void takesClass(Wrapper *ref) {
+ int x = 0;
+ [[ref returnsObject: x] instanceMethod];
+ int y = x;
+ [ref returnsObject: x].prop;
+}
+// CHECK1: "Object *duplicate = [ref returnsObject:x];\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3
+// CHECK1-NEXT: "duplicate" [[@LINE-5]]:4 -> [[@LINE-5]]:26
+// CHECK1-NEXT: "duplicate" [[@LINE-4]]:3 -> [[@LINE-4]]:25
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:27:4 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:29:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+void takesClass2(Wrapper *ref) {
+ if (2)
+ [[ref object] instanceMethod];
+ [ref object].block();
+}
+// CHECK2: "Object *object = [ref object];\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3
+// CHECK2-NEXT: "object" [[@LINE-4]]:6 -> [[@LINE-4]]:18
+// CHECK2-NEXT: "object" [[@LINE-4]]:3 -> [[@LINE-4]]:15
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:40:6 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:41:3 %s | FileCheck --check-prefix=CHECK2 %s
+
+void takesClass3(Wrapper *ref) {
+ if (ref) {
+ [ref.object instanceMethod];
+ ref.object.block();
+ }
+}
+// CHECK3: "Object *object = ref.object;\n" [[@LINE-4]]:5 -> [[@LINE-4]]:5
+// CHECK3-NEXT: "object" [[@LINE-5]]:6 -> [[@LINE-5]]:16
+// CHECK3-NEXT: "object" [[@LINE-5]]:5 -> [[@LINE-5]]:15
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:52:6 %s | FileCheck --check-prefix=CHECK3 %s
+
+void worksOnClass() {
+ [Wrapper classMethodReturnsObject]->ivar = 0;
+ [[Wrapper classMethodReturnsObject] instanceMethod];
+}
+// CHECK4: "Object *classMethodReturnsObject = [Wrapper classMethodReturnsObject];\nclassMethodReturnsObject" [[@LINE-3]]:3 -> [[@LINE-3]]:37
+
+// CHECK4-NEXT: "classMethodReturnsObject" [[@LINE-4]]:4 -> [[@LINE-4]]:38
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:63:4 %s | FileCheck --check-prefix=CHECK4 %s
+
+void worksOnClassProperty() {
+ Wrapper.classObject->ivar = 0;
+ Wrapper.classObject.prop = 2;
+}
+// CHECK5: "Object *classObject = Wrapper.classObject;\nclassObject" [[@LINE-3]]:3 -> [[@LINE-3]]:22
+
+// CHECK5-NEXT: "classObject" [[@LINE-4]]:3 -> [[@LINE-4]]:22
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=%s:73:3 %s | FileCheck --check-prefix=CHECK5 %s
+
+#define MACROARG1(X, Y) (X)
+#define MACROARG2(X) X; ref.object.prop = 0
+
+void macroArgument(Wrapper *ref) {
+ // macro-arg1: +1:13
+ MACROARG1(Wrapper.classObject->ivar, 1); // MACRO-ARG1: "Object *classObject = Wrapper.classObject;\n" [[@LINE]]:3 -> [[@LINE]]:3
+ // MACRO-ARG1-NEXT: "classObject" [[@LINE-1]]:13 -> [[@LINE-1]]:32
+ MACROARG1(Wrapper.classObject.prop, 0) = 0;// MACRO-ARG1-NEXT: "classObject" [[@LINE]]:13 -> [[@LINE]]:32
+
+ // macro-arg2: +3:13
+ MACROARG2(int x = 0; ref.object->ivar); // MACRO-ARG2: "Object *object = ref.object;\n" [[@LINE]]:3 -> [[@LINE]]:3
+ // MACRO-ARG2-NEXT: "object" [[@LINE-1]]:24 -> [[@LINE-1]]:34
+ MACROARG2(ref.object.prop);// MARO-ARG2-NEXT: "object" [[@LINE]]:13 -> [[@LINE]]:23
+}
+
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=macro-arg1 %s | FileCheck --check-prefix=MACRO-ARG1 %s
+// RUN: clang-refactor-test perform -action extract-repeated-expr-into-var -at=macro-arg2 %s | FileCheck --check-prefix=MACRO-ARG2 %s
diff --git a/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c
new file mode 100644
index 0000000..dd63951
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-forward-decl.c
@@ -0,0 +1,26 @@
+#ifndef AFTER
+enum ForwardEnumDecl;
+#endif
+
+enum ForwardEnumDecl {
+ A, B
+};
+
+#ifdef AFTER
+enum ForwardEnumDecl;
+#endif
+
+void dontInitiateOnIncompleteEnum(enum ForwardEnumDecl e) {
+ switch (e) {
+ }
+// CHECK: "case A:\n<#code#>\nbreak;\ncase B:\n<#code#>\nbreak;\n" [[@LINE-1]]:3
+
+ switch (e) {
+ case A:
+ break;
+ }
+// CHECK: "case B:\n<#code#>\nbreak;\n" [[@LINE-1]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:14:3 -at=%s:18:3 %s | FileCheck %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:14:3 -at=%s:18:3 %s -D AFTER | FileCheck %s
diff --git a/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp
new file mode 100644
index 0000000..3d8c1a7
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-initiate.cpp
@@ -0,0 +1,150 @@
+enum Color {
+ Black,
+ Blue,
+ White,
+ Gold
+};
+
+void initiate(Color c, int i) {
+ switch (c) {
+ case Black:
+ break;
+ }
+// CHECK1: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-4]]:3
+
+ switch (c) {
+ }
+// CHECK2: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-2]]:3
+}
+
+// RUN: clang-refactor-test list-actions -at=%s:9:3 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Add Missing Switch Cases
+
+// Ensure the the action can be initiated around a switch:
+
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:9:3-15 -in=%s:10:1-14 -in=%s:11:1-11 -in=%s:12:1-3 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:15:3-15 -in=%s:16:1-4 %s | FileCheck --check-prefix=CHECK2 %s
+
+// Ensure that the action can't be initiated in other places:
+
+// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:8:1-32 -in=%s:9:1-2 -in=%s:13:1-77 -in=%s:15:1-2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action
+
+void dontInitiate(Color c, int i) {
+ switch (c) {
+ case Black:
+ break;
+ case Blue:
+ break;
+ case White:
+ break;
+ case Gold:
+ break;
+ }
+
+ switch (i) {
+ case 0:
+ break;
+ }
+
+ switch ((int)c) {
+ case 0:
+ break;
+ }
+}
+
+// Ensure that the action can't be initiated on switches that have all cases or
+// that don't work with an enum.
+
+// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:34:3-15 %s 2>&1 | FileCheck --check-prefix=CHECK-ALL-COVERED %s
+// CHECK-ALL-COVERED: Failed to initiate the refactoring action (All enum cases are already covered)!
+// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -in=%s:45:3-15 -in=%s:50:3-20 %s 2>&1 | FileCheck --check-prefix=CHECK-NOT-ENUM %s
+// CHECK-NOT-ENUM: Failed to initiate the refactoring action (The switch doesn't operate on an enum)!
+
+void initiateWithDefault(Color c, int i) {
+ switch (c) {
+ case Black:
+ break;
+ default:
+ break;
+ }
+// CHECK3: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-6]]:3
+
+ switch (c) {
+ default:
+ break;
+ }
+// CHECK4: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-4]]:3
+}
+
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:65:3 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:73:3 %s | FileCheck --check-prefix=CHECK4 %s
+
+enum class Shape {
+ Rectangle,
+ Circle,
+ Octagon
+};
+
+typedef enum {
+ Anon1,
+ Anon2
+} AnonymousEnum;
+
+void initiateEnumClass(Shape shape, AnonymousEnum anon) {
+ switch (shape) {
+ }
+// CHECK5: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-2]]:3
+ switch (anon) {
+ }
+// CHECK6: Initiated the 'fill-in-enum-switch-cases' action at [[@LINE-2]]:3
+}
+
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:95:3 %s -std=c++11 | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:98:3 %s | FileCheck --check-prefix=CHECK6 %s
+
+// Ensure that the operation can be initiated from a selection:
+
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:9:3-12:4 -selected=%s:9:15-12:3 -selected=%s:9:15-12:3 -selected=%s:10:3-11:10 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:15:3-16:4 %s | FileCheck --check-prefix=CHECK2 %s
+
+void dontInitiateSelectedBody(Shape shape) {
+ switch (shape) {
+ case Shape::Rectangle: {
+ break;
+ }
+ case Shape::Circle:
+ break;
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:114:5-114:11 -selected=%s:117:5-117:10 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+enum IncompleteEnum : int;
+enum class IncompleteClassEnum : short;
+enum class IncompleteClassEnum2;
+void dontInitiateOnIncompleteEnum(IncompleteEnum e1, IncompleteClassEnum e2, IncompleteClassEnum2 e3) {
+ switch (e1) {
+ }
+ switch (e1) {
+ case 0:
+ break;
+ }
+ switch (e2) {
+ }
+ switch (e2) {
+ case (IncompleteClassEnum)0:
+ break;
+ }
+ switch (e3) {
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -at=%s:127:3 -at=%s:129:3 -at=%s:133:3 -at=%s:135:3 -at=%s:139:3 %s -std=c++11 2>&1 | FileCheck --check-prefix=CHECK-NOT-COMPLETE %s
+// CHECK-NOT-COMPLETE: Failed to initiate the refactoring action (The enum type is incomplete)!
+
+void initiateWhenSelectionIsPartial() {
+ int partiallySelected = 0;
+}
+int global = 0;
+// RUN: not clang-refactor-test initiate -action fill-in-enum-switch-cases -selected=%s:147:1-150:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp
new file mode 100644
index 0000000..97118ec
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-neatly-ordered.cpp
@@ -0,0 +1,157 @@
+
+enum Color {
+ Black,
+ Blue,
+ White,
+ Gold
+};
+
+void placeBeforeDefault(Color c) {
+ switch (c) {
+ case Black:
+ break;
+ case Blue:
+ break;
+ default:
+ break;
+ }
+// CHECK1: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+
+ switch (c) {
+ default:
+ break;
+ }
+// CHECK1: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:10:3 -at=%s:20:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+void dontPlaceBeforeDefault(Color c) {
+ switch (c) {
+ default:
+ break;
+ case Black:
+ break;
+ case Blue:
+ break;
+ }
+// CHECK2: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ case Black:
+ break;
+ default:
+ break;
+ case Blue:
+ break;
+ }
+// CHECK2: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:30:3 -at=%s:40:3 %s | FileCheck --check-prefix=CHECK2 %s
+
+void insertAtProperPlaces(Color c) {
+ switch (c) {
+ case Black:
+ break;
+ case White:
+ break;
+#ifdef USEDEFAULT
+ default:
+ break;
+#endif
+ }
+// CHECK3: "case Blue:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+// CHECK3-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3
+// CHECK4: "case Blue:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3
+// CHECK4-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+
+ switch (c) {
+ case White:
+ break;
+#ifdef USEDEFAULT
+ default:
+ break;
+#endif
+ }
+// CHECK3: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+// CHECK3-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3
+// CHECK4: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3
+// CHECK4-NEXT: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+
+ switch (c) {
+ case Gold:
+ break;
+#ifdef USEDEFAULT
+ default:
+ break;
+#endif
+ }
+// CHECK3: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+// CHECK4: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\n" [[@LINE-8]]:3 -> [[@LINE-8]]:3
+
+ switch (c) {
+ case Blue:
+ break;
+#ifdef USEDEFAULT
+ default:
+ break;
+#endif
+ }
+// CHECK3: "case Black:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+// CHECK3-NEXT: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3
+// CHECK4: "case Black:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3
+// CHECK4-NEXT: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-7]]:3 -> [[@LINE-7]]:3
+
+ switch (c) {
+ case White:
+ break;
+ case Gold:
+ break;
+#ifdef USEDEFAULT
+ default:
+ break;
+#endif
+ }
+// CHECK3: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-9]]:3 -> [[@LINE-9]]:3
+// CHECK4: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-10]]:3 -> [[@LINE-10]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:54:3 -at=%s:69:3 -at=%s:82:3 -at=%s:93:3 -at=%s:106:3 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:54:3 -at=%s:69:3 -at=%s:82:3 -at=%s:93:3 -at=%s:106:3 %s -D USEDEFAULT | FileCheck --check-prefix=CHECK4 %s
+
+void insertAtEndIfOrderingIsUncertain(Color c) {
+ switch (c) {
+ case Gold:
+ break;
+ case White:
+ break;
+ }
+// CHECK5: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ case Blue:
+ break;
+ case Black:
+ break;
+ }
+// CHECK5: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ case White:
+ break;
+ case Black:
+ break;
+ }
+// CHECK5: "case Blue:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ case Gold:
+ break;
+ case Blue:
+ break;
+ }
+// CHECK5: "case Black:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:124:3 -at=%s:132:3 -at=%s:140:3 -at=%s:148:3 %s | FileCheck --check-prefix=CHECK5 %s
diff --git a/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp
new file mode 100644
index 0000000..c1a4040
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-opaque-decl.cpp
@@ -0,0 +1,33 @@
+enum IncompleteEnum : int;
+
+enum IncompleteEnum : int {
+ A, B
+};
+
+enum class IncompleteClassEnum : short;
+
+enum class IncompleteClassEnum : short {
+ B, C
+};
+
+enum class IncompleteClassEnum2;
+
+enum class IncompleteClassEnum2 {
+ D, E
+};
+
+void dontInitiateOnIncompleteEnum(IncompleteEnum e1, IncompleteClassEnum e2, IncompleteClassEnum2 e3) {
+ switch (e1) {
+ }
+// CHECK: "case A:\n<#code#>\nbreak;\ncase B:\n<#code#>\nbreak;\n" [[@LINE-1]]:3
+
+ switch (e2) {
+ }
+// CHECK: "case IncompleteClassEnum::B:\n<#code#>\nbreak;\ncase IncompleteClassEnum::C:\n<#code#>\nbreak;\n" [[@LINE-1]]:3
+
+ switch (e3) {
+ }
+// CHECK: "case IncompleteClassEnum2::D:\n<#code#>\nbreak;\ncase IncompleteClassEnum2::E:\n<#code#>\nbreak;\n" [[@LINE-1]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:20:3 -at=%s:24:3 -at=%s:28:3 %s -std=c++11 | FileCheck %s
diff --git a/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp
new file mode 100644
index 0000000..70aa585
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-perform.cpp
@@ -0,0 +1,93 @@
+#ifdef NESTEDANON
+namespace {
+#endif
+#ifdef NESTED1
+namespace foo {
+struct Struct {
+#endif
+
+#ifdef ENUMCLASS
+enum class Color {
+#else
+enum Color {
+#endif
+ Black,
+ Blue,
+ White,
+ Gold
+};
+
+#ifdef NESTED1
+#define PREFIX foo::Struct::
+#else
+#define PREFIX
+#endif
+
+#ifdef ENUMCLASS
+#define CASE(x) PREFIX Color::x
+#else
+#define CASE(x) PREFIX x
+#endif
+
+#ifdef NESTED1
+}
+#ifndef NESTED1NS
+}
+#endif
+#endif
+#ifdef NESTEDANON
+}
+#endif
+
+void perform1(PREFIX Color c) {
+ switch (c) {
+ case CASE(Black):
+ break;
+ }
+// CHECK1: "case Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+// CHECK2: "case Color::Blue:\n<#code#>\nbreak;\ncase Color::White:\n<#code#>\nbreak;\ncase Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3
+// CHECK3: "case foo::Struct::Blue:\n<#code#>\nbreak;\ncase foo::Struct::White:\n<#code#>\nbreak;\ncase foo::Struct::Gold:\n<#code#>\nbreak;\n" [[@LINE-3]]:3 -> [[@LINE-3]]:3
+// CHECK4: "case foo::Struct::Color::Blue:\n<#code#>\nbreak;\ncase foo::Struct::Color::White:\n<#code#>\nbreak;\ncase foo::Struct::Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-4]]:3 -> [[@LINE-4]]:3
+// CHECK5: "case Struct::Blue:\n<#code#>\nbreak;\ncase Struct::White:\n<#code#>\nbreak;\ncase Struct::Gold:\n<#code#>\nbreak;\n" [[@LINE-5]]:3 -> [[@LINE-5]]:3
+// CHECK6: "case Struct::Color::Blue:\n<#code#>\nbreak;\ncase Struct::Color::White:\n<#code#>\nbreak;\ncase Struct::Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-6]]:3 -> [[@LINE-6]]:3
+
+ switch (c) {
+ case CASE(Black):
+ break;
+ case (Color)1: // Blue
+ break;
+ }
+// CHECK1: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+// CHECK2: "case Color::White:\n<#code#>\nbreak;\ncase Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3
+
+ switch (c) {
+ }
+// CHECK1: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+// CHECK2: "case Color::Black:\n<#code#>\nbreak;\ncase Color::Blue:\n<#code#>\nbreak;\ncase Color::White:\n<#code#>\nbreak;\ncase Color::Gold:\n<#code#>\nbreak;\n" [[@LINE-2]]:3 -> [[@LINE-2]]:3
+}
+
+#ifdef NESTED1NS
+}
+#endif
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D ENUMCLASS | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D ENUMCLASS | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D NESTED1NS | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 %s -std=c++11 -D NESTED1 -D NESTED1NS -D ENUMCLASS | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D NESTEDANON | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:43:3 -at=%s:54:3 -at=%s:63:3 %s -std=c++11 -D NESTEDANON -D ENUMCLASS | FileCheck --check-prefix=CHECK2 %s
+
+#define MACROARG(X) X
+
+void macroArg(PREFIX Color c) {
+ // macro-arg: +2:12
+ // macro-arg-range-begin: +1:12
+ MACROARG(switch (c) {
+ }); // MACRO-ARG: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE]]
+ // macro-arg-range-end: -1:4
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=macro-arg %s | FileCheck --check-prefix=MACRO-ARG %s
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -selected=macro-arg-range %s | FileCheck --check-prefix=MACRO-ARG %s
diff --git a/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp
new file mode 100644
index 0000000..3e1f9fe
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/fill-in-cases-wrap-in-compound.cpp
@@ -0,0 +1,63 @@
+
+enum Color {
+ Black,
+ Blue,
+ White,
+ Gold
+};
+
+// Wrap the inserted case bodies in '{' '}' when the majority of others are
+// wrapped as well.
+void wrapInBraces(Color c) {
+ switch (c) {
+ case Black: {
+ int x = 0;
+ break;
+ }
+ }
+// CHECK1: "case Blue: {\n<#code#>\nbreak;\n}\ncase White: {\n<#code#>\nbreak;\n}\ncase Gold: {\n<#code#>\nbreak;\n}\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ case Black: {
+ int x = 0;
+ break;
+ }
+ case Blue: {
+ int y = 0;
+ break;
+ }
+ }
+// CHECK1: "case White: {\n<#code#>\nbreak;\n}\ncase Gold: {\n<#code#>\nbreak;\n}\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:12:3 -at=%s:20:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+void dontWrapInBraces(Color c) {
+ switch (c) {
+ case Black: {
+ int x = 0;
+ break;
+ }
+ case Blue:
+ break;
+ }
+// CHECK2: "case White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ case Black: {
+ int x = 0;
+ break;
+ }
+ case Blue:
+ break;
+ case White:
+ break;
+ }
+// CHECK2: "case Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+ switch (c) {
+ }
+// CHECK2: "case Black:\n<#code#>\nbreak;\ncase Blue:\n<#code#>\nbreak;\ncase White:\n<#code#>\nbreak;\ncase Gold:\n<#code#>\nbreak;\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+}
+
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=%s:36:3 -at=%s:46:3 -at=%s:58:3 %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c b/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c
new file mode 100644
index 0000000..891b4bf
--- /dev/null
+++ b/test/Refactor/FillInEnumSwitchCases/initiate-on-enum-constant.c
@@ -0,0 +1,12 @@
+// RUN: clang-refactor-test perform -action fill-in-enum-switch-cases -at=enum-c %s | FileCheck %s
+
+enum Enum {
+ Enum_a,
+ Enum_b,
+};
+void testEnumConstantInCWithIntType() {
+// enum-c: +1:1
+switch (Enum_a) {
+case Enum_a: break;
+} // CHECK: "case Enum_b:\n<#code#>\nbreak;\n" [[@LINE]]:1 -> [[@LINE]]:1
+}
diff --git a/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp
new file mode 100644
index 0000000..7990cc5
--- /dev/null
+++ b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-initiate.cpp
@@ -0,0 +1,68 @@
+struct AbstractClass {
+ virtual void method() = 0;
+ virtual void otherMethod() { }
+};
+
+struct Base {
+ virtual void nonAbstractClassMethod() { }
+};
+
+struct Target : Base, AbstractClass {
+ int field = 0;
+
+ union SubRecord {
+ };
+
+ void outerMethod() const;
+
+ void innerMethod() {
+ int x = 0;
+ }
+};
+// CHECK1: Initiated the 'fill-in-missing-abstract-methods' action at [[@LINE-12]]:1
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:10:1-end -in=%s:11:1-end -in=%s:12:1-end -in=%s:13:1-2 -in=%s:15:1-end -in=%s:16:1-2 -in=%s:17:1-end -in=%s:18:1-2 -in=%s:21:1-2 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:13:3-end -in=%s:14:1-2 -in=%s:16:3-27 -in=%s:18:3-end -in=%s:19:1-end -in=%s:20:1-3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// CHECK-NO: Failed to initiate the refactoring action
+
+// RUN: clang-refactor-test list-actions -at=%s:10:1 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Add Missing Abstract Class Overrides
+
+
+void Target::outerMethod() const {
+}
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:33:1-end -in=%s:34:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+struct FinalTarget final : AbstractClass {
+};
+// CHECK2: Initiated the 'fill-in-missing-abstract-methods' action at [[@LINE-2]]:1
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:38:1-end -in=%s:39:1-2 %s | FileCheck --check-prefix=CHECK2 %s
+
+union Union { };
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:44:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+class NoAbstractParents : Base { };
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:47:1-35 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-ABSTRACT-BASE %s
+// CHECK-NO-ABSTRACT-BASE: Failed to initiate the refactoring action (The class has no abstract bases)
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -in=%s:1:1-end -in=%s:6:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO-ABSTRACT-BASE %s
+
+// Check selection:
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-abstract-methods -selected=%s:10:1-21:2 -selected=%s:12:1-16:10 -selected=%s:11:1-20:2 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -selected=%s:13:3-14:4 -selected=%s:16:3-16:27 -selected=%s:18:3-20:4 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+struct HasAllMethods: AbstractClass {
+ virtual void method() override { }
+};
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -at=%s:58:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-MISSING-METHODS %s
+// CHECK-NO-MISSING-METHODS: Failed to initiate the refactoring action (The class has no missing abstract class methods)
+
+// Shouldn't crash:
+// forward-decl: +1:1
+struct ForwardDecl;
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-abstract-methods -at=forward-decl %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp
new file mode 100644
index 0000000..e9f4404
--- /dev/null
+++ b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-no-attributes.cpp
@@ -0,0 +1,10 @@
+
+struct AbstractClass {
+ __attribute__((annotate("test")))
+ virtual void pureMethod() = 0;
+};
+
+struct Target : AbstractClass {
+};
+// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:7:1 %s | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp
new file mode 100644
index 0000000..8ae223b
--- /dev/null
+++ b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-perform.cpp
@@ -0,0 +1,126 @@
+template <typename T>
+struct Generic { T x; };
+
+struct AbstractClass {
+ virtual void pureMethod() = 0;
+ virtual void aPureMethod(int (*fptr)(), Generic<int> y) = 0;
+ virtual int anotherPureMethod(const int &x) const = 0;
+ virtual int operator + (int) const = 0;
+ virtual void otherMethod() { }
+};
+
+struct Base {
+ virtual void nonAbstractClassMethod() { }
+};
+
+struct Target : Base, AbstractClass {
+};
+// CHECK1: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint anotherPureMethod(const int &x) const override;\n\nint operator+(int) const override;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:16:1 %s | FileCheck --check-prefix=CHECK1 %s
+
+struct SubTarget : AbstractClass {
+ int anotherPureMethod(const int &) const { return 0; }
+#ifdef HAS_OP
+ int operator + (int) const override { return 2; }
+#endif
+};
+
+struct Target2 : SubTarget, Base {
+};
+// CHECK2: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint operator+(int) const override;\n\n" [[@LINE-1]]:1
+// CHECK3: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\n" [[@LINE-2]]:1
+
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:29:1 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:29:1 %s -DHAS_OP | FileCheck --check-prefix=CHECK3 %s
+
+struct Abstract2 {
+ virtual void firstMethod(int x, int y) = 0;
+ virtual void secondMethod(int, int) { }
+ virtual void thirdMethod(int a) = 0;
+ virtual void fourthMethod() = 0;
+};
+
+struct FillInGoodLocations : Base, Abstract2 {
+
+ void secondMethod(int, int) override; // comment
+
+ void unrelatedMethod();
+
+};
+// CHECK4: "\n\nvoid firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n" [[@LINE-5]]:51 -> [[@LINE-5]]:51
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:44:1 %s | FileCheck --check-prefix=CHECK4 %s
+
+struct FillInGoodLocations2 : FillInGoodLocations, AbstractClass {
+
+ void fourthMethod() override;
+
+ // comment
+ void unrelatedMethod();
+
+ int operator + (int) const override;
+};
+// CHECK5: "\n\nvoid firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n" [[@LINE-7]]:32 -> [[@LINE-7]]:32
+// CHECK5-NEXT: "\n\nvoid pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint anotherPureMethod(const int &x) const override;\n" [[@LINE-3]]:39 -> [[@LINE-3]]:39
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:54:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+struct FillInGoodLocations3 : Base, AbstractClass, Abstract2 {
+
+ // comment
+ void unrelatedMethod();
+
+ void thirdMethod(int a) override;
+
+ void firstMethod(int x, int y) override;
+
+};
+// CHECK6: "\n\nvoid fourthMethod() override;\n" [[@LINE-3]]:43 -> [[@LINE-3]]:43
+// CHECK6-NEXT: "void pureMethod() override;\n\nvoid aPureMethod(int (*fptr)(), Generic<int> y) override;\n\nint anotherPureMethod(const int &x) const override;\n\nint operator+(int) const override;\n\n" [[@LINE-2]]:1 -> [[@LINE-2]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:67:1 %s | FileCheck --check-prefix=CHECK6 %s
+
+struct FIllInGoodLocationsWithMacros : Abstract2 {
+#define METHOD(decl) void decl override;
+
+ METHOD(thirdMethod(int a))
+ METHOD(firstMethod(int x, int y)) void foo();
+};
+// CHECK7: "\n\nvoid fourthMethod() override;\n" [[@LINE-2]]:36 -> [[@LINE-2]]:36
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:81:1 %s | FileCheck --check-prefix=CHECK7 %s
+
+template<typename T>
+class GenericType : Abstract2 {
+
+};
+// CHECK8: "void firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n\n" [[@LINE-1]]:1
+
+struct GenericSubType : GenericType<int> {
+
+};
+// CHECK8: "void firstMethod(int x, int y) override;\n\nvoid thirdMethod(int a) override;\n\nvoid fourthMethod() override;\n\n" [[@LINE-1]]:1
+
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:91:1 -at=%s:96:1 %s | FileCheck --check-prefix=CHECK8 %s
+
+
+struct BaseClass2
+{
+ virtual ~BaseClass2();
+ virtual int load() = 0;
+};
+
+// correct-implicit-destructor-placement: +1:1
+struct DerivedImplicitDestructorClass2
+: public BaseClass2
+{
+
+}; // CHECK-DESTRUCTOR: "int load() override;\n\n" [[@LINE]]:1
+
+// Don't insert methods after the destructor:
+// correct-destructor-placement: +1:1
+struct DerivedExplicitDestructorClass2
+: public BaseClass2 {
+ ~DerivedImplicitDestructorClass2();
+
+
+}; // CHECK-DESTRUCTOR: "int load() override;\n\n" [[@LINE]]:1
+
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=correct-implicit-destructor-placement -at=correct-destructor-placement %s | FileCheck --check-prefix=CHECK-DESTRUCTOR %s
diff --git a/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp
new file mode 100644
index 0000000..2318a56
--- /dev/null
+++ b/test/Refactor/FillInMissingMethodStubsFromAbstractClasses/fill-in-missing-abstract-methods-with-bodies.cpp
@@ -0,0 +1,50 @@
+
+struct AbstractClass {
+ virtual void pureMethod() = 0;
+};
+
+#ifdef HAS_BODY
+ #define BODY { }
+#else
+ #define BODY ;
+#endif
+
+struct Target1 : AbstractClass {
+ void method1() BODY
+};
+// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1
+// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1
+
+struct Target2 : AbstractClass {
+ void method1() BODY
+ void method2() BODY
+};
+// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1
+// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1
+
+struct Target2_1 : AbstractClass {
+ void method1() BODY
+ void method2();
+};
+// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1
+// CHECK2: "void pureMethod() override;\n\n" [[@LINE-2]]:1
+
+struct Target3 : AbstractClass {
+ void method1() BODY
+ void method2() BODY
+ void method3() BODY
+};
+// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1
+// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1
+
+struct Target4 : AbstractClass {
+ void method1() BODY
+ void method2() BODY
+ void method3() BODY
+ void method4() BODY
+};
+// CHECK1: "void pureMethod() override;\n\n" [[@LINE-1]]:1
+// CHECK2: "void pureMethod() override { \n <#code#>\n}\n\n" [[@LINE-2]]:1
+
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:12:1 -at=%s:18:1 -at=%s:25:1 -at=%s:32:1 -at=%s:40:1 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-abstract-methods -at=%s:12:1 -at=%s:18:1 -at=%s:25:1 -at=%s:32:1 -at=%s:40:1 %s -DHAS_BODY | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m
new file mode 100644
index 0000000..ae785bc
--- /dev/null
+++ b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-class-extension.m
@@ -0,0 +1,51 @@
+@protocol Proto
+
+@required
+-(void)method:(int)x;
+
+@end
+
+@protocol Proto2
+
+@required
+- (void)method2:(int)y;
+
+@end
+
+@interface Base
+@end
+
+// Initiate the action from extension if the @implementation is in the same TU.
+@interface WithExtension: Base<Proto>
+@end
+@interface WithExtension()
+@end
+@interface WithExtension() <Proto2>
+@end
+@implementation WithExtension
+// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:21:1-27 -in=%s:22:1-5 -in=%s:23:1-36 %s | FileCheck --check-prefix=CHECK1 %s
+
+@interface WithoutImplementation: Base<Proto>
+@end
+@interface WithoutImplementation()
+@end
+@interface WithoutImplementation() <Proto2>
+@end
+// CHECK-NO-IMPL: Failed to initiate the refactoring action (Class extension without suitable @implementation)!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:32:1 -at=%s:34:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPL %s
+
+// Initiate from the implementation even when the class has no protocols, but
+// its extension does.
+
+@interface NoProtocols: Base
+@end
+@interface NoProtocols() <Proto2>
+@end
+@implementation NoProtocols
+@end
+// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-2]]:1
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:44:1 -at=%s:46:1 %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK-NO-EXT-FROM-INTERFACE: Failed to initiate the refactoring action!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:42:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO-EXT-FROM-INTERFACE %s
diff --git a/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m
new file mode 100644
index 0000000..249e273
--- /dev/null
+++ b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-required-only.m
@@ -0,0 +1,146 @@
+@protocol Proto
+
+#ifndef NO_REQUIRED
+@required
+-(void)ofCourseItisRequired:(int)x;
+#endif
+
+#ifndef NO_NOTHING
+- (void)nothingSpecified:(int)y;
+#endif
+
+#ifndef NO_OPTIONAL
+@optional;
+- (void)justOptional;
+#endif
+
+@end
+
+@protocol Proto2
+
+#ifndef NO_NOTHING
+// Effectively a @required.
+- (void)nothingSpecified2:(int)y;
+#endif
+
+#ifndef NO_REQUIRED
+@required
+-(void)ofCourseItisRequired2:(int)x;
+#endif
+
+#ifndef NO_OPTIONAL
+@optional;
+- (void)justOptional2;
+#endif
+
+@end
+
+@interface Base
+@end
+
+// Initiate in the @interface when the interface has missing @required
+// declarations.
+@interface I1 : Base<Proto>
+// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+#ifdef DEF_REQUIRED
+-(void)ofCourseItisRequired:(int)x;
+#endif
+#ifdef DEF_NOTHING
+- (void)nothingSpecified:(int)y;
+#endif
+#ifdef DEF_OPTIONAL
+- (void)justOptional;
+#endif
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK1 %s
+// CHECK-NO: Failed to initiate the refactoring action (All of the @required methods are there)!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_REQUIRED | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_NOTHING | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:43:1 %s -DDEF_REQUIRED -DDEF_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+@interface I1(Category) <Proto2>
+// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+#ifdef DEF_REQUIRED
+-(void)ofCourseItisRequired2:(int)x;
+#endif
+#ifdef DEF_NOTHING
+- (void)nothingSpecified2:(int)y;
+#endif
+#ifdef DEF_OPTIONAL
+- (void)justOptional2;
+#endif
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK2 %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_REQUIRED | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_NOTHING | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:67:1 %s -DDEF_REQUIRED -DDEF_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// Initiate in the @implementatino when the implementation has missing @required
+// methods.
+@implementation I1
+// CHECK3: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+#ifdef IMPL_REQUIRED
+-(void)ofCourseItisRequired:(int)x { }
+#endif
+#ifdef IMPL_NOTHING
+- (void)nothingSpecified:(int)y { }
+#endif
+#ifdef IMPL_OPTIONAL
+- (void)justOptional { }
+#endif
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK3 %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_REQUIRED | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_NOTHING | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_OPTIONAL | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_REQUIRED -DIMPL_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_REQUIRED -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_NOTHING -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:92:1 %s -DIMPL_OPTIONAL -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK3 %s
+
+@implementation I1 (Category)
+// CHECK4: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+#ifdef IMPL_REQUIRED
+-(void)ofCourseItisRequired2:(int)x { }
+#endif
+#ifdef IMPL_NOTHING
+- (void)nothingSpecified2:(int)y { }
+#endif
+#ifdef IMPL_OPTIONAL
+- (void)justOptional2 { }
+#endif
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DNO_REQUIRED -DNO_OPTIONAL | FileCheck --check-prefix=CHECK4 %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DNO_REQUIRED -DNO_NOTHING -DNO_OPTIONAL 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DNO_REQUIRED -DNO_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_REQUIRED | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_NOTHING | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_OPTIONAL | FileCheck --check-prefix=CHECK4 %s
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_REQUIRED -DIMPL_NOTHING 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_REQUIRED -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_NOTHING -DDEF_REQUIRED -DDEF_NOTHING | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:120:1 %s -DIMPL_OPTIONAL -DDEF_REQUIRED -DDEF_NOTHING -DDEF_OPTIONAL | FileCheck --check-prefix=CHECK4 %s
diff --git a/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m
new file mode 100644
index 0000000..0957f8b
--- /dev/null
+++ b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate-when-protocoled.m
@@ -0,0 +1,97 @@
+@protocol Proto
+
+@required
+-(void)method:(int)x;
+
+@required
+- (void)method2:(int)y;
+
+@end
+
+@interface Base
+@end
+
+// Initiate when @implementation's interface has a suitable protocol.
+@interface I1 : Base<Proto>
+@end
+// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-2]]:1
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:15:1 %s | FileCheck --check-prefix=CHECK1 %s
+
+@implementation I1
+
+@end
+// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-3]]:1
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:20:1 %s | FileCheck --check-prefix=CHECK2 %s
+
+@interface I2 : I1
+
+@end
+// CHECK3: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-3]]:1
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:26:1 %s | FileCheck --check-prefix=CHECK3 %s
+
+@implementation I2
+@end
+// CHECK4: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-2]]:1
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:32:1 %s | FileCheck --check-prefix=CHECK4 %s
+
+// Shouldn't initiate when the @interface is a forward declaration.
+@class ForwardDecl;
+// CHECK-FORWARD: Failed to initiate the refactoring action!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:38:1-19 %s 2>&1 | FileCheck --check-prefix=CHECK-FORWARD %s
+
+// Shouldn't initiate when the @interface has no protocols:
+
+@interface I3 : Base
+@end
+@implementation I3
+@end
+
+@implementation I4
+@end
+
+// CHECK-CLASS-NO-PROTO: Failed to initiate the refactoring action!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:11:1-16 -in=%s:12:1-5 -at=%s:44:1 -at=%s:46:1 -at=%s:49:1 %s 2>&1 | FileCheck --check-prefix=CHECK-CLASS-NO-PROTO %s
+
+@protocol Proto2
+
+@required
+-(int)method3;
+
+@end
+
+// Initiate when the category has a suitable protocol:
+@interface I3 (Category) <Proto2>
+// CHECK5: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+@implementation I3 (Category)
+// CHECK6: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+@interface I1 (Category) <Proto2>
+// CHECK7: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+@implementation I1 (Category)
+// CHECK8: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-1]]:1
+@end
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:63:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+// Shouldn't initiate when the category has no protocols (even when the class has them):
+@interface I1 (Category2)
+@end
+
+@implementation I1 (Category2)
+@end
+
+@interface I3 (Category2)
+@end
+
+@implementation I3 (Category2)
+@end
+
+// CHECK-CAT-NO-PROTO: Failed to initiate the refactoring action!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:84:1 -at=%s:87:1 -at=%s:90:1 -at=%s:93:1 %s 2>&1 | FileCheck --check-prefix=CHECK-CAT-NO-PROTO %s
diff --git a/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m
new file mode 100644
index 0000000..066d737
--- /dev/null
+++ b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-initiate.m
@@ -0,0 +1,100 @@
+@protocol Proto
+
+@required
+-(void)method:(int)x;
+
+@end
+
+@interface Base
+@end
+
+@interface I : Base<Proto>
+@property int p1;
+@property int p2;
+@end
+
+// Initiate the action within the @implementation
+@implementation I
+
+@dynamic p1;
+@synthesize p2 = _p2;
+
+- (void)anotherMethod {
+ int x = 0;
+}
+
+void function(int x) {
+ int y = x;
+}
+
+@end
+
+// RUN: clang-refactor-test list-actions -at=%s:18:1 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Add Missing Protocol Requirements
+
+// Ensure the the action can be initiated in the @implementation / @interface:
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:11:1-27 -in=%s:12:1-18 -in=%s:13:1-18 -in=%s:14:1-5 %s | FileCheck --check-prefix=CHECKI1 %s
+// CHECKI1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-27]]:1
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:17:1-18 -at=%s:18:1 -in=%s:19:1-13 -in=%s:20:1-22 -at=%s:21:1 -at=%s:25:1 -at=%s:29:1 -in=%s:30:1-5 %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-23]]:1
+
+// Ensure that the action can't be initiated in other places:
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:1:1-10 -in=%s:4:1-21 -in=%s:6:1-5 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action
+
+// Ensure that the action can't be initiated in methods/functions in @implementation:
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:22:1-24 -in=%s:23:1-13 -in=%s:24:1-2 -in=%s:26:1-23 -in=%s:27:1-13 -in=%s:28:1-2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+@protocol P2
+
+-(void)method2:(int)y;
+
+@end
+
+@interface I (Category) <P2>
+
+@end
+
+@implementation I (Category)
+
+- (void)anotherMethod2:(int)x {
+ int x = 0;
+}
+
+void aFunction(int x) {
+ int y = x;
+}
+
+@end
+
+// Ensure the the action can be initiated in the category @implementation:
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:57:1-29 -at=%s:58:1 -in=%s:59:1-5 %s | FileCheck --check-prefix=CHECKI2 %s
+// CHECKI2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-19]]:1
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:61:1-29 -at=%s:62:1 -at=%s:66:1 -at=%s:70:1 -in=%s:71:1-5 %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK2: Initiated the 'fill-in-missing-protocol-stubs' action at [[@LINE-18]]:1
+
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=%s:60:1 -at=%s:72:1 -at=%s:73:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -in=%s:63:1-32 -in=%s:64:1-13 -in=%s:65:1-2 -in=%s:67:1-24 -in=%s:68:1-13 -in=%s:69:1-2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+
+// Check that initiation works with selection as well:
+
+// RUN: clang-refactor-test initiate -action fill-in-missing-protocol-stubs -selected=%s:17:1-30:5 -selected=%s:18:1-29:1 -selected=%s:20:1-20:10 -selected=%s:17:1-23:3 -selected=%s:27:3-30:5 -selected=%s:23:3-27:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+// Not when just one entire method is selected though!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -selected=%s:22:1-24:2 -selected=%s:26:1-28:2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// And not when the container is just partially selected!
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -selected=%s:15:1-30:1 -selected=%s:17:1-40:1 -selected=%s:15:1-40:1 -selected=%s:1:1-90:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+@class ForwardClass;
+
+// forward-class: +1:1
+@implementation ForwardClass (ForwardClassCategory)
+@end
+// RUN: not clang-refactor-test initiate -action fill-in-missing-protocol-stubs -at=forward-class %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m
new file mode 100644
index 0000000..d2f3711
--- /dev/null
+++ b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-no-attributes.m
@@ -0,0 +1,28 @@
+@class AClass;
+
+@protocol Protocol
+
+- (void)methodAttribute __attribute__((availability(macos, introduced=10.10)));
+
+- (void)parameterTypeAttribute:(AClass * _Nullable)c;
+
+- (void)parameterTypeAttribute2:(AClass * _Nonnull)c;
+
+- (void)parameterAttribute:(int)p __attribute__((annotate("test")));
+
+@end
+
+@interface Base
+@end
+
+@interface I1 : Base<Protocol>
+
+@end
+// CHECK1: "- (void)methodAttribute;\n\n- (void)parameterAttribute:(int)p;\n\n- (void)parameterTypeAttribute2:(AClass * _Nonnull)c;\n\n- (void)parameterTypeAttribute:(AClass * _Nullable)c;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+
+@implementation I1
+
+@end
+// CHECK1: "- (void)methodAttribute { \n <#code#>\n}\n\n- (void)parameterAttribute:(int)p { \n <#code#>\n}\n\n- (void)parameterTypeAttribute2:(AClass * _Nonnull)c { \n <#code#>\n}\n\n- (void)parameterTypeAttribute:(AClass * _Nullable)c { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:18:1 -at=%s:23:1 %s | FileCheck --check-prefix=CHECK1 %s
+
diff --git a/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m
new file mode 100644
index 0000000..900df30
--- /dev/null
+++ b/test/Refactor/FillInMissingProtocolStubs/fill-in-protocol-stubs-perform.m
@@ -0,0 +1,196 @@
+@protocol Proto1
+
+- (void)requiredInstanceMethod:(int)y;
+
++ (void)aRequiredInstanceMethod:(int (*)(void))function with:(Proto *)p;
+
+@optional;
+- (void)anOptionalMethod;
+
+@end
+
+@protocol Proto2
+
+- (void)a;
+
++ (void)b;
+
+@end
+
+@protocol Proto3
+
+- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;
+
+@end
+
+@interface Base
+@end
+
+@interface I1 : Base<Proto3>
+
+@end
+// CHECK1: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:29:1 %s | FileCheck --check-prefix=CHECK1 %s
+
+@implementation I1
+
+@end
+// CHECK2: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:35:1 %s | FileCheck --check-prefix=CHECK2 %s
+
+@interface I2 : I1<Proto1, Proto2>
+
+@end
+// CHECK3: "+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p;\n\n- (void)requiredInstanceMethod:(int)y;\n\n- (void)a;\n\n+ (void)b;\n\n- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:41:1 %s | FileCheck --check-prefix=CHECK3 %s
+
+@implementation I2
+
+@end
+// CHECK4: "+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p { \n <#code#>\n}\n\n- (void)requiredInstanceMethod:(int)y { \n <#code#>\n}\n\n- (void)a { \n <#code#>\n}\n\n+ (void)b { \n <#code#>\n}\n\n- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:47:1 %s | FileCheck --check-prefix=CHECK4 %s
+
+@interface I1(Category) <Proto2>
+@end
+// CHECK5: "- (void)a;\n\n+ (void)b;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+
+@implementation I1(Category)
+@end
+// CHECK5-NEXT: "- (void)a { \n <#code#>\n}\n\n+ (void)b { \n <#code#>\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:53:1 -at=%s:57:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+@interface I3 : I1<Proto1, Proto2>
+
+- (void)requiredInstanceMethod:(int)y;
+
++ (void)b;
+
+#ifdef HAS_OTHER
+- (void) otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;
+#endif
+
+@end
+// CHECK6: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p;\n" [[@LINE-9]]:39 -> [[@LINE-9]]:39
+// CHECK6-NEXT: "\n\n- (void)a;\n" [[@LINE-8]]:11 -> [[@LINE-8]]:11
+// CHECK6-NEXT: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock;\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK7: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p;\n" [[@LINE-12]]:39 -> [[@LINE-12]]:39
+// CHECK7-NEXT: "\n\n- (void)a;\n" [[@LINE-11]]:11 -> [[@LINE-11]]:11
+
+@implementation I3
+
+- (void)requiredInstanceMethod:(int)y {
+}
+
++ (void)b {
+}
+
+#ifdef HAS_OTHER
+- (void) otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { }
+#endif
+
+@end
+// CHECK6: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p { \n <#code#>\n}\n" [[@LINE-10]]:2 -> [[@LINE-10]]:2
+// CHECK6-NEXT: "\n\n- (void)a { \n <#code#>\n}\n" [[@LINE-8]]:2 -> [[@LINE-8]]:2
+// CHECK6-NEXT: "- (void)otherProtocolMethod:(int (^)(id<Proto2>))takesBlock { \n <#code#>\n}\n\n" [[@LINE-3]]:1 -> [[@LINE-3]]:1
+// CHECK7: "\n\n+ (void)aRequiredInstanceMethod:(int (*)(void))function with:(id)p { \n <#code#>\n}\n" [[@LINE-13]]:2 -> [[@LINE-13]]:2
+// CHECK7-NEXT: "\n\n- (void)a { \n <#code#>\n}\n" [[@LINE-11]]:2 -> [[@LINE-11]]:2
+
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:62:1 -at=%s:79:1 %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:62:1 -at=%s:79:1 %s -DHAS_OTHER | FileCheck --check-prefix=CHECK7 %s
+
+@protocol ProtoWith3Methods
+
+- (void)a;
+- (void)b;
+- (void)c;
+
+@end
+
+@interface I4 : Base<ProtoWith3Methods>
+
+#ifndef USE_MACRO
+- (void)b;
+
+- (void)a; // comment
+
+#else
+
+#define METHOD(name) -(void)name;
+
+METHOD(b)
+METHOD(c) - (void)d;
+
+#endif
+
+@end
+// CHECK8: "\n\n- (void)c;\n" [[@LINE-12]]:22 -> [[@LINE-12]]:22
+// CHECK9: "\n\n- (void)a;\n" [[@LINE-6]]:10 -> [[@LINE-6]]:10
+
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:109:1 %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:109:1 %s -D USE_MACRO | FileCheck --check-prefix=CHECK9 %s
+
+@protocol NSObject
+
+- (void)nsObjectMethod;
+
+@end
+
+@protocol SubProto
+
+- (void)sub1;
+- (id<SubProto>)sub2;
+
+@end
+
+@protocol SubProto2 <NSObject>
+
+- (void)sub11;
+
+@end
+
+@protocol SuperProto <SubProto, NSObject, SubProto2>
+
+@optional
+- (void)mySub;
+
+@end
+
+@interface HasSubProtocolMethods: Base <SuperProto, NSObject>
+
+@end
+// CHECK10: "- (void)sub1;\n\n- (id<SubProto>)sub2;\n\n- (void)sub11;\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:158:1 %s | FileCheck --check-prefix=CHECK10 %s
+
+
+
+@interface SuperClassWithSomeDecls : Base<SuperProto>
+
+- (void)sub1;
+
+@end
+
+@interface SubClassOfSuperClassWithSomeDecls : SuperClassWithSomeDecls
+
+#ifdef HAS_SUB1_OVERRIDE
+- (void)sub1;
+#endif
+#ifdef HAS_SUB11
+- (void)sub11;
+#endif
+
+@end
+// CHECK11: "- (id<SubProto>)sub2;\n\n- (void)sub11;\n\n" [[@LINE-1]]:1
+// CHECK12: "\n\n- (id<SubProto>)sub2;\n" [[@LINE-8]]:14
+// CHECK12-NEXT: "- (void)sub11;\n\n" [[@LINE-3]]:1
+// CHECK13: "- (id<SubProto>)sub2;\n\n" [[@LINE-4]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:172:1 %s | FileCheck --check-prefix=CHECK11 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:172:1 %s -DHAS_SUB1_OVERRIDE | FileCheck --check-prefix=CHECK12 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:172:1 %s -DHAS_SUB11 | FileCheck --check-prefix=CHECK13 %s
+
+@implementation SubClassOfSuperClassWithSomeDecls
+
+@end
+// CHECK14: "- (id<SubProto>)sub2 { \n <#code#>\n}\n\n- (void)sub11 { \n <#code#>\n}\n\n" [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:190:1 %s | FileCheck --check-prefix=CHECK14 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:190:1 %s -DHAS_SUB1_OVERRIDE | FileCheck --check-prefix=CHECK14 %s
+// RUN: clang-refactor-test perform -action fill-in-missing-protocol-stubs -at=%s:190:1 %s -DHAS_SUB11 | FileCheck --check-prefix=CHECK14 %s
diff --git a/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp b/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp
new file mode 100644
index 0000000..e8d0631
--- /dev/null
+++ b/test/Refactor/IfSwitchConversion/if-switch-conversion-initiate.cpp
@@ -0,0 +1,566 @@
+void foo(); void foobar();
+
+void simpleCompoundBodyIf(int x) {
+ foo();
+
+ if (x == 2) {
+ int y = x;
+ } else if (x == 3) {
+ foo();
+ } else {
+ foobar();
+ }
+
+ foobar();
+}
+
+// RUN: clang-refactor-test list-actions -at=%s:6:3 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Convert to Switch
+
+// Ensure the the action can be initiated around the ifs:
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:6:3-15 -in=%s:8:3-22 -in=%s:10:3-10 %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: Initiated the 'if-switch-conversion' action at 6:3
+
+// Ensure that the action can't be initiated when not around the ifs:
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:1:1-10 -in=%s:3:1-18 -in=%s:4:1-9 -in=%s:6:1-2 -in=%s:14:1-12 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action
+// CHECK-NO-NOT: Initiated the 'if-switch-conversion' action
+
+// Ensure that the action can't be initiated inside the ifs:
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:7:1-15 -in=%s:9:1-11 -in=%s:11:1-14 -in=%s:12:1-4 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void nestedIf(int x) {
+ if (x == 2) {
+ if (x == 3) {
+ foo();
+ } else {
+ foo();
+ }
+ } else {
+ foobar();
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:35:3-15 -in=%s:35:3-10 %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK2: Initiated the 'if-switch-conversion' action at 35:3
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:36:5-17 -in=%s:38:5-12 %s | FileCheck --check-prefix=CHECK3 %s
+// CHECK3: Initiated the 'if-switch-conversion' action at 36:5
+
+
+
+
+
+void simpleFlatBodyIfs(int x) {
+ if (x == 2)
+ foo();
+ else if (x == 3)
+ foo();
+
+ else if (x == 4) foobar();
+
+ else foo();
+
+ if (x == 2) foobar();
+ else
+ //comment
+ foo();
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:57:3-14 -in=%s:59:3-19 -in=%s:62:3-19 -in=%s:64:3-7 %s | FileCheck --check-prefix=CHECK4 %s
+// CHECK4: Initiated the 'if-switch-conversion' action at 57:3
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:57:1-2 -in=%s:58:1-11 -in=%s:59:1-2 -in=%s:60:1-11 -in=%s:61:1-1 -in=%s:62:1-2 -in=%s:62:20-29 -in=%s:63:1-1 -in=%s:64:1-2 -in=%s:64:8-14 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:66:3-15 -in=%s:67:3-7 -in=%s:68:1-14 %s | FileCheck --check-prefix=CHECK5 %s
+// CHECK5: Initiated the 'if-switch-conversion' action at 66:3
+
+void differentLineCompoundIf(int x) {
+ if (x == 2)
+ {
+ foo();
+ }
+
+ else if (x == 3)
+
+ {
+ foo();
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:81:3-14 -in=%s:86:3-19 -in=%s:87:1-1 %s | FileCheck --check-prefix=CHECK6 %s
+// CHECK6: Initiated the 'if-switch-conversion' action at 81:3
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:81:1-2 -in=%s:82:1-4 -in=%s:84:1-4 -in=%s:85:1-1 -in=%s:86:1-2 -in=%s:88:1-4 -in=%s:90:1-4 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void simpleEmptyIf(int x) {
+ if (x == 1) ;
+ else if (x == 2) ;
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:99:3-14 -in=%s:100:3-19 %s | FileCheck --check-prefix=CHECK7 %s
+// CHECK7: Initiated the 'if-switch-conversion' action at 99:3
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:99:15-16 -in=%s:100:20-21 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void sameLineIfElse(int x) {
+ if (x == 1) foo(); else foo();
+ if (x == 2) { foo(); } else if (x == 3) { foo(); }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:109:3-14 -in=%s:109:22-26 %s | FileCheck --check-prefix=CHECK8 %s
+// CHECK8: Initiated the 'if-switch-conversion' action at 109:3
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:109:15-21 -in=%s:109:27-33 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -in=%s:110:3-15 -in=%s:110:24-43 %s | FileCheck --check-prefix=CHECK9 %s
+// CHECK9: Initiated the 'if-switch-conversion' action at 110:3
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:110:16-23 -in=%s:110:44-53 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void noIfsWithoutElses(int x) {
+ if (x == 1) {
+ foo();
+ }
+ if (x == 2) ;
+}
+
+// Ifs without any elses shouldn't be allowed:
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:124:1-16 -in=%s:127:1-16 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void noFancyIfs(const int *p) {
+ if (const int *x = p) {
+ }
+ else if (const int *y = p) {
+ }
+
+ if (const int *x = p; *x == 2) {
+ } else if (const int *y = p; *y == 3) {
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:134:1-26 -in=%s:136:1-31 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -in=%s:139:1-35 -in=%s:140:1-42 %s -std=c++1z 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+void prohibitBreaksCasesDefaults(int x) {
+ while (x != 0) {
+ break;
+ // Allowed:
+ if (x == 1) foo();
+ else foo();
+ // Not allowed:
+ if (x == 2) break;
+ else foo();
+ if (x == 2) { foo(); }
+ else { break; }
+ if (x == 2) { foo(); }
+ else if (x == 1) { if (x == 2) { break; } }
+ }
+ switch (x) {
+ case 1:
+ // Allowed:
+ if (x == 1) foo();
+ else foo();
+ // Not allowed:
+ if (x == 2) foo();
+ else if (x == 3) {
+ case 2:
+ foo();
+ }
+ if (x == 3) foo();
+ else {
+ default:
+ foo();
+ }
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:151:5 %s | FileCheck --check-prefix=CHECK10 %s
+// CHECK10: Initiated the 'if-switch-conversion' action at 151:5
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:154:5 -at=%s:156:5 -at=%s:158:5 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s
+// CHECK-INVALID-STATEMENTS: Failed to initiate the refactoring action (if's body contains a 'break'/'default'/'case' statement)
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:164:5 %s | FileCheck --check-prefix=CHECK11 %s
+// CHECK11: Initiated the 'if-switch-conversion' action at 164:5
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:167:5 -at=%s:172:5 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s
+
+#ifdef DISALLOWED
+ #define DISALLOW(x) x
+#else
+ #define DISALLOW(x)
+#endif
+
+void allowBreaksInNestedLoops(int x) {
+ DISALLOW(while (true)) {
+ // Allowed:
+ if (x == 1) {
+ foo();
+ } else if (x == 2) {
+ while (x != 0) {
+ break;
+ }
+ DISALLOW(break;)
+ }
+
+ if (x == 1) {
+ foo();
+ } else {
+ for (int y = 0; y < x; ++y) {
+ break;
+ }
+ DISALLOW(break;)
+ }
+
+ if (x == 1) {
+ do {
+ break;
+ } while (x < 10);
+ DISALLOW(break;)
+ } else {
+ foo();
+ }
+
+ if (x == 1) {
+ do {
+ // nested loop.
+ while (true) {
+ }
+ break;
+ } while (x < 10);
+ DISALLOW(break;)
+ } else {
+ foo();
+ }
+
+ }
+
+ // Still care about cases and defaults in loops:
+ switch (x) {
+ case 0:
+ if (x == 1) {
+ while (true) {
+ case 1:
+ }
+ } else {
+ foo();
+ }
+ break;
+ }
+
+ switch (x) {
+ case 0:
+ if (x == 1) {
+ while (true) {
+ default:
+ }
+ } else {
+ foo();
+ }
+ break;
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:200:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// CHECK-YES: Initiated the 'if-switch-conversion' action
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:209:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:218:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:227:3 %s | FileCheck --check-prefix=CHECK-YES %s
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:200:3 -at=%s:209:3 -at=%s:218:3 -at=%s:227:3 -at=%s:244:5 -at=%s:256:5 %s 2>&1 -D DISALLOWED | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s
+
+void allowBreakDefaultCaseInNestedSwitches(int x) {
+ DISALLOW(switch (x)) {
+ // Allowed:
+ if (x == 1) {
+ foo();
+ } else if (x == 2) {
+ switch (x) {
+ case 0:
+ foo();
+ }
+ DISALLOW(case 0: ;)
+ }
+
+ if (x == 1) {
+ foo();
+ } else {
+ switch (x) {
+ default:
+ foo();
+ }
+ DISALLOW(default: ;)
+ }
+
+ if (x == 1) {
+ switch (x) {
+ break;
+ }
+ DISALLOW(break;)
+ } else {
+ foo();
+ }
+
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:279:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:289:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:299:3 %s | FileCheck --check-prefix=CHECK-YES %s
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:279:3 -at=%s:289:3 -at=%s:299:3 %s 2>&1 -D DISALLOWED | FileCheck --check-prefix=CHECK-INVALID-STATEMENTS %s
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+bool isTrue();
+
+void allowOnlyEqualsOp(int x) {
+ if (x != 1) {
+ } else {
+ }
+
+ if (x == 1) {
+ } else if (x > 2) {
+ }
+
+ if (x == 3) {
+ } else if (x) {
+ }
+
+ if (isTrue()) {
+ } else {
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:335:3 -at=%s:339:3 -at=%s:343:3 -at=%s:347:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+// CHECK-INVALID-COND: Failed to initiate the refactoring action (unsupported conditional expression)!
+
+void allowEqualsOpInParens(int x) {
+ if ((x == 1)) {
+ } else {
+ }
+
+ if (x == 1) {
+ } else if (((x == 2))) {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:356:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: clang-refactor-test initiate -action if-switch-conversion -at=%s:360:3 %s | FileCheck --check-prefix=CHECK-YES %s
+
+enum Switchable {
+ A, B
+};
+
+struct Struct {
+};
+
+bool operator == (const Struct &, int);
+
+void allowSwitchableTypes(int x, bool b, long l, char c, Switchable e,
+ float f, double d, Struct s, int *ip) {
+ // Allowed:
+ if (b == true) {
+ } else {
+ }
+
+ if (1 == 1) {
+ } else {
+ }
+
+ if (l == 4) {
+ } else {
+ }
+
+ if (e == A) {
+ } else {
+ }
+
+ if (x == A) {
+ } else {
+ }
+
+ if (c == 'x') {
+ } else {
+ }
+
+ // Disallowed:
+ if (f == 0) {
+ } else {
+ }
+
+ if (d == 0) {
+ } else {
+ }
+
+ if (x == 0) {
+ } else if (x == 0.0) {
+ }
+
+ if (s == 0) {
+ } else {
+ }
+
+ if (ip == 0) {
+ } else {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:380:3 -at=%s:384:3 -at=%s:388:3 -at=%s:392:3 -at=%s:396:3 -at=%s:400:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:405:3 -at=%s:409:3 -at=%s:413:3 -at=%s:417:3 -at=%s:421:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+
+template<typename T>
+void prohibitDependentOperators(T x) {
+ if (x == 0) {
+ } else {
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:431:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+
+int integerFunction();
+
+void checkLHSSame(int x, int y) {
+ // Allowed:
+ if (integerFunction() == 1) {
+ } else if (integerFunction() == 2) {
+ }
+
+ // Disallowed:
+ if (x == 1) {
+ } else if (y == 2) {
+ }
+
+ if (x == 1) {
+ } else if (2 == 2) {
+ }
+
+ if (integerFunction() == 1) {
+ } else if (x == 2) {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:442:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:447:3 -at=%s:451:3 -at=%s:455:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+
+void checkRHSConstant(int x, int y, Switchable e) {
+ // Allowed:
+ if (x == (int)A) {
+ } else {
+ }
+
+ if (e == (Switchable)1) {
+ } else {
+ }
+
+ // Disallowed:
+ if (x == y) {
+ } else {
+ }
+
+ if (x == 1) {
+ } else if (x == integerFunction()) {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:465:3 -at=%s:469:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:474:3 -at=%s:478:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+
+void checkRHSUnique(int x, int y, Switchable e) {
+ // Disallowed:
+ if (x == 0) {
+ } else if (x == 0) {
+ }
+
+ if (e == A) {
+ } else if (e == (Switchable)0) {
+ }
+}
+
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:474:3 -at=%s:478:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+
+void allowLHSParens(int x) {
+ if ((x) == 0) {
+ } else {
+ }
+}
+
+void allowRHSParens(int x) {
+ if (x == (0)) {
+ } else {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:500:3 -at=%s:506:3 %s | FileCheck --check-prefix=CHECK-YES %s
+
+void allowLogicalOr(int x, int y) {
+ // Allowed:
+ if (x == 0 || x == 1) {
+ } else {
+ }
+
+ if (x == 0) {
+ } else if (x == 1 || x == 2) {
+ }
+
+ if (x == (0) || (x == 1)) {
+ } else {
+ }
+
+ if (x == 0) {
+ } else if ((x == 1 || x == 2)) {
+ }
+
+ // Disallowed:
+ if (x == 0 && x == 1) {
+ } else {
+ }
+
+ if (x == 0 | x == 1) {
+ } else {
+ }
+
+ if (x == 0 || isTrue()) {
+ } else if (y == 2) {
+ }
+
+ if (x == 0 || x == 1) {
+ } else if (y == 2) {
+ }
+
+ if (x == 0) {
+ } else if (x == 1 || x == integerFunction()) {
+ }
+
+ if (x == 1) {
+ } else if (x == 2 || x == 1) {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:515:3 -at=%s:519:3 -at=%s:523:3 -at=%s:527:3 %s | FileCheck --check-prefix=CHECK-YES %s
+// RUN: not clang-refactor-test initiate -action if-switch-conversion -at=%s:532:3 -at=%s:536:3 -at=%s:540:3 -at=%s:544:3 -at=%s:548:3 -at=%s:552:3 %s 2>&1 | FileCheck --check-prefix=CHECK-INVALID-COND %s
+
+void parenImpCastsLHSEquivalence(int x) {
+ if ((x) == 1) {
+ } else if (x == 2) {
+ }
+}
+
+// RUN: clang-refactor-test initiate -action if-switch-conversion -location-agnostic -at=%s:561:3 %s | FileCheck --check-prefix=CHECK-YES %s
diff --git a/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp b/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp
new file mode 100644
index 0000000..9874795
--- /dev/null
+++ b/test/Refactor/IfSwitchConversion/if-switch-conversion-perform.cpp
@@ -0,0 +1,321 @@
+void foo();
+
+void simpleCompoundBodyIf(int x) {
+ foo();
+
+ if (x == 2) { // CHECK1: "switch (" [[@LINE]]:3 -> [[@LINE]]:7
+ (void)x; // CHECK1-NEXT: ") {\ncase " [[@LINE-1]]:8 -> [[@LINE-1]]:12
+ // CHECK1-NEXT: ":" [[@LINE-2]]:13 -> [[@LINE-2]]:16
+ } else { // CHECK1-NEXT: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:11
+ foo();
+ }
+
+ if (((x) == 22 || x == 3) || x == 4) { // CHECK1: "switch (" [[@LINE]]:3 -> [[@LINE]]:9
+ // CHECK1-NEXT: ") {\ncase " [[@LINE-1]]:10 -> [[@LINE-1]]:15
+ // CHECK1-NEXT: ":\ncase " [[@LINE-2]]:17 -> [[@LINE-2]]:26
+ // CHECK1-NEXT: ":\ncase " [[@LINE-3]]:27 -> [[@LINE-3]]:37
+ // CHECK1-NEXT: ":" [[@LINE-4]]:38 -> [[@LINE-4]]:41
+ } else { // CHECK1-NEXT: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:11
+ }
+
+ foo();
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:6:3 -at=%s:13:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+void colonInsertionCompoundBody(int x) {
+ if (x == 2) // CHECK2: ":" [[@LINE]]:13 -> [[@LINE+2]]:4
+
+ {
+
+ }
+ else {
+ }
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:27:3 %s | FileCheck --check-prefix=CHECK2 %s
+
+void colonInsertionNonCompoundBody(int x) {
+ if (x == 2) foo(); // CHECK3: ":" [[@LINE]]:13 -> [[@LINE]]:14
+ else {
+ }
+
+ if (x == 2) // CHECK3: ":" [[@LINE]]:13 -> [[@LINE]]:14
+ foo();
+ else {
+ }
+
+ if ((x == (2)) /*comment*/) // CHECK3: ":" [[@LINE]]:15 -> [[@LINE]]:30
+ foo();
+ else {
+ }
+
+ if (x == 2 // CHECK3: ":" [[@LINE]]:13 -> [[@LINE+1]]:8
+ )
+ // comment
+ foo();
+ else {
+ }
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:39:3 -at=%s:43:3 -at=%s:48:3 -at=%s:53:3 %s | FileCheck --check-prefix=CHECK3 %s
+
+void colonInsertionFailure(int x) {
+#define EMPTY_MACRO
+ if (x == 1 EMPTY_MACRO ) foo();
+ else {
+ }
+}
+
+// RUN: not clang-refactor-test perform -action if-switch-conversion -at=%s:65:3 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR1 %s
+// CHECK-ERR1: failed to perform the refactoring operation (couldn't find the location of ')')
+
+void elseNonCompoundBody(int x) {
+#ifdef WITH_ELSEIF
+ if (x == 1) foo(); else
+#endif
+ if (x == 2)
+ foo();
+ else // CHECK4: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:7
+ foo();
+
+#ifdef WITH_ELSEIF
+ if (x == 1) foo(); else
+#endif
+ if (x == 2)
+ foo();
+ else foo(); // CHECK4: "break;\ndefault:" [[@LINE]]:3 -> [[@LINE]]:7
+
+#ifdef WITH_ELSEIF
+ if (x == 1) foo(); else
+#endif
+ if (x == 2) foo(); /*comment*/ else foo(); // CHECK4: "\nbreak;\ndefault:" [[@LINE]]:34 -> [[@LINE]]:38
+
+#ifdef WITH_ELSEIF
+ if (x == 1) foo(); else
+#endif
+ if (x == 2) ; else ; // CHECK4: "\nbreak;\ndefault:" [[@LINE]]:17 -> [[@LINE]]:21
+}
+
+void elseCompoundBody(int x) {
+#ifdef WITH_ELSEIF
+ if (x == 1) foo(); else
+#endif
+ if (x == 2) { foo(); } else { foo(); } // CHECK4: "\nbreak;\ndefault:" [[@LINE]]:24 -> [[@LINE]]:32
+
+#ifdef WITH_ELSEIF
+ if (x == 1) foo(); else
+#endif
+ if (x == 2) {
+ // comment.
+ }
+ else // CHECK4: "break;\ndefault:" [[@LINE-1]]:3 -> [[@LINE+1]]:4
+ {
+ foo();
+ }
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:77:3 -at=%s:85:3 -at=%s:92:3 -at=%s:97:3 -at=%s:104:3 -at=%s:109:3 %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:77:3 -at=%s:85:3 -at=%s:92:3 -at=%s:97:3 -at=%s:104:3 -at=%s:109:3 %s -D WITH_ELSEIF | FileCheck --check-prefix=CHECK4 %s
+
+void elseIfCompoundBody(int x) {
+ if (x == 2) {
+ foo();
+ } else if (x == 3) { // CHECK5: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:19
+ foo(); // CHECK5-NEXT: ":" [[@LINE-1]]:20 -> [[@LINE-1]]:23
+ } else if (x == 4 || x == 55) { // CHECK5-NEXT: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:19
+ foo(); // CHECK5-NEXT: ":\ncase " [[@LINE-1]]:20 -> [[@LINE-1]]:29
+ // CHECK5-NEXT: ":" [[@LINE-2]]:31 -> [[@LINE-2]]:34
+ }
+
+ if (x == 2) { foo(); } else if (x == 3) { foo(); } // CHECK5: "\nbreak;\ncase " [[@LINE]]:24 -> [[@LINE]]:40
+ // CHECK5-NEXT: ":" [[@LINE-1]]:41 -> [[@LINE-1]]:44
+
+ if (x == 2) {
+ // comment.
+ }
+ else if (x == 3) // CHECK5: "break;\ncase " [[@LINE-1]]:3 -> [[@LINE]]:17
+ { // CHECK5-NEXT: ":" [[@LINE-1]]:18 -> [[@LINE]]:4
+ foo();
+ }
+}
+
+void elseIfNonCompoundBody(int x) {
+ if (x == 2)
+ foo();
+ else if (x == 21) // CHECK5: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:17
+ foo(); // CHECK5-NEXT: ":" [[@LINE-1]]:19 -> [[@LINE-1]]:20
+ else if (x == 5) ;// CHECK5-NEXT: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:17
+ // CHECK5-NEXT: ":" [[@LINE-1]]:18 -> [[@LINE-1]]:19
+
+ if (x == 2) foo(); /*comment*/ else if (x == 3) foo(); // CHECK5: "\nbreak;\ncase " [[@LINE]]:34 -> [[@LINE]]:48
+ // CHECK5-NEXT: ":" [[@LINE-1]]:49 -> [[@LINE-1]]:50
+
+ if (x == 2) ; else if (x == 3) ; // CHECK5: "\nbreak;\ncase " [[@LINE]]:17 -> [[@LINE]]:31
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:122:3 -at=%s:131:3 -at=%s:134:3 -at=%s:144:3 -at=%s:151:3 -at=%s:154:3 %s | FileCheck --check-prefix=CHECK5 %s
+
+void closingBraceInsertion(int x) {
+ if (x == 2) {
+ } else if (x == 3) {
+ } else {
+ foo();
+ } // CHECK6: "break;\n" [[@LINE]]
+
+ if (x == 3) {
+ } else if (x == 4) {
+ } // CHECK6: "break;\n" [[@LINE]]
+
+ if (x == 2)
+ foo();
+ else if (x == 3)
+ foo();
+ else if (x == 4) // CHECK6: "\nbreak;\n}" [[@LINE+1]]:11 -> [[@LINE+1]]:11
+ foo();
+
+ if (x == 2)
+ foo();
+ else // CHECK6: "\nbreak;\n}" [[@LINE+1]]:11 -> [[@LINE+1]]:11
+ foo();
+
+ if (x == 2) foo(); // CHECK6: "\nbreak;\n}" [[@LINE+1]]:35 -> [[@LINE+1]]:35
+ else foo(); // preserve comments
+
+ if (x == 2) foo();
+ else if (x == 3) // CHECK6: "\nbreak;\n}" [[@LINE+1]]:12 -> [[@LINE+1]]:12
+ foo() ; foo(); // no preserve
+
+ if (x == 2) foo(); // CHECK6: "\nbreak;\n}" [[@LINE+1]]:11 -> [[@LINE+1]]:11
+ else ; ;
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:160:3 -at=%s:166:3 -at=%s:170:3 -at=%s:177:3 -at=%s:182:3 -at=%s:185:3 -at=%s:189:3 %s | FileCheck --check-prefix=CHECK6 %s
+
+void needBreaks(int x) {
+ if (x == 2) {
+ return;
+ x = 3;
+ } else if (x == 3) { // CHECK7: "break;\ncase " [[@LINE]]
+ foo();
+ return;
+ foo();
+ } else { // CHECK7: "break;\ndefault:" [[@LINE]]
+ if (x == 1) {
+ return;
+ }
+ } // CHECK7: "break;\n" [[@LINE]]:3
+
+ if (x == 2)
+ if (x == 3)
+ return;
+ else ; else // CHECK7: "\nbreak;\ndefault:" [[@LINE]]
+ while (x < 2)
+ return; // CHECK7: "\nbreak;\n}" [[@LINE]]:52
+}
+
+void noNeedForBreaks(int x) {
+ if (x == 2) {
+ return;
+ } else if (x == 3) { // CHECK7: "case " [[@LINE]]
+ foo();
+ return;
+ } else { // CHECK7: "default:" [[@LINE]]
+ if (x == 1) {
+ }
+ {
+ return;
+ }
+ } // CHECK7-NOT: "{{.*}}break{{.*}}" [[@LINE]]
+
+ if (x == 2) return; else return; // CHECK7: "\ndefault:" [[@LINE]]
+ // CHECK7: "\n}" [[@LINE-1]]
+
+ // Invalid returns should work as well.
+ if (x == 2)
+ return 1;
+ else // CHECK7: "default:" [[@LINE]]
+ return 2; // CHECK7: "\n}" [[@LINE]]
+}
+
+int noNeedForBreaksInvalidRets(int x) {
+ if (x == 2)
+ return; // This omits the 'break'.
+ // But this doesn't (should it?).
+ else { // CHECK7: "default:" [[@LINE]]
+ return "";
+ } // CHECK7: "break;\n" [[@LINE]]
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:196:3 -at=%s:209:3 -at=%s:218:3 -at=%s:231:3 -at=%s:235:3 -at=%s:242:3 %s | FileCheck --check-prefix=CHECK7 %s
+
+void needBraces(int x) {
+ if (x == 2) { // CHECK8: ": {" [[@LINE]]:13 -> [[@LINE]]:16
+ int a = x;
+ } else if (x == 1) { // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3
+ int a = 0, y = 1; // CHECK8-NEXT: ": {" [[@LINE-1]]:20 -> [[@LINE-1]]:23
+ return;
+ } else if (x == 3 || x == 4) { // CHECK8-NEXT: "}\ncase " [[@LINE]]:3
+ int m = 2; // CHECK8-NEXT: ":\ncase " [[@LINE-1]]
+ // CHECK8-NEXT: ": {" [[@LINE-2]]:30 -> [[@LINE-2]]:33
+ } else if (x == 5) { // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3
+ return; // CHECK8-NEXT: ":" [[@LINE-1]]:20 -> [[@LINE-1]]:23
+ } else { // CHECK8-NEXT: "default: {" [[@LINE]]:3 -> [[@LINE]]:11
+ foo();
+ int k = x;
+ foo();
+ } // CHECK8-NEXT: "break;\n}\n" [[@LINE]]:3 -> [[@LINE]]:3
+
+ if (x == 2) { // CHECK8: ": {" [[@LINE]]
+ int a = 2;
+ } else if (x == 3) { // CHECK8: "break;\n}\ncase " [[@LINE]]
+ int b = x; // CHECK8-NEXT: ": {" [[@LINE-1]]
+ } // CHECK8-NEXT: "break;\n}\n" [[@LINE]]
+
+ if (x == 2) // CHECK8: ": {" [[@LINE]]:13 -> [[@LINE]]:14
+ int a = x;
+ else if (x == 1 || x == 3) // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3 -> [[@LINE]]:17
+ int b = 2; // CHECK8-NEXT: ":\ncase " [[@LINE-1]]
+ // CHECK8-NEXT: ": {" [[@LINE-2]]:28 -> [[@LINE-2]]:29
+ else if (x == 4) // CHECK8-NEXT: "break;\n}\ncase " [[@LINE]]:3 -> [[@LINE]]:17
+ foo(); // CHECK8-NEXT: ":" [[@LINE-1]]
+ else if (x == 5) // CHECK8-NEXT: "break;\ncase " [[@LINE]]
+ return; // CHECK8-NEXT: ":" [[@LINE-1]]
+ else // CHECK8-NEXT: "default: {" [[@LINE]]:3 -> [[@LINE]]:7
+ int c = x;
+ // CHECK8-NEXT: "\nbreak;\n}\n}" [[@LINE-1]]:15 -> [[@LINE-1]]:15
+
+ if (x == 2) int a = 1; else int k = x;
+ // CHECK8: ": {" [[@LINE-1]]:13 -> [[@LINE-1]]:14
+ // CHECK8-NEXT: "\nbreak;\n}\ndefault: {" [[@LINE-2]]:26 -> [[@LINE-2]]:30
+ // CHECK8-NEXT: "\nbreak;\n}\n}" [[@LINE-3]]:41 -> [[@LINE-3]]:41
+}
+
+void noBracesNeeded(int x) {
+ if (x == 2) { // CHECK8: ":" [[@LINE]]
+ if (int *z = p) {
+ }
+ } else if (x == 3) { // CHECK8: "break;\ncase " [[@LINE]]
+ for (int z = 0; z < x ; ++z) ; // CHECK8: ":" [[@LINE-1]]
+ } else if (x == 4) { // CHECK8: "break;\ncase " [[@LINE]]
+ { // CHECK8: ":" [[@LINE-1]]
+ int a = 1;
+ }
+ }
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=%s:253:3 -at=%s:269:3 -at=%s:275:3 -at=%s:288:3 -at=%s:295:3 %s | FileCheck --check-prefix=CHECK8 %s
+
+#define MACRO(X) X
+
+void macroArg(int x) {
+ // macro-arg: +1:9
+ MACRO(if (x == 2) { // MACRO-ARG: "switch (" [[@LINE]]:9 -> [[@LINE]]:13
+ ; // MACRO-ARG: ") {\ncase " [[@LINE-1]]:14 -> [[@LINE-1]]:18
+ // MACRO-ARG: ":" [[@LINE-2]]:19 -> [[@LINE-2]]:22
+ } else if (x == 3) { // MACRO-ARG: "break;\ncase " [[@LINE]]:3 -> [[@LINE]]:19
+ ; // MACRO-ARG: ":" [[@LINE-1]]:20 -> [[@LINE-1]]:23
+ }); // MACRO-ARG: "break;\n" [[@LINE]]:3 -> [[@LINE]]:3
+}
+
+// RUN: clang-refactor-test perform -action if-switch-conversion -at=macro-arg %s | FileCheck --check-prefix=MACRO-ARG %s
diff --git a/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp b/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp
new file mode 100644
index 0000000..f6389e2
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/Inputs/class.cpp
@@ -0,0 +1,25 @@
+
+struct Class {
+ int field;
+
+ Class();
+
+ Class(int x) { }
+
+ ~Class();
+
+ // commment
+ static void method(const int &value, int defaultParam = 20);
+
+ virtual int voidMethod(int y) const;
+ void implementedMethod() const {
+
+ }
+
+ void outOfLineImpl(int x);
+
+ void anotherImplementedMethod() {
+
+ }
+};
+// CHECK1: "{{.*}}class.cpp" "\n\nClass::Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n" [[@LINE-1]]:3
diff --git a/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp b/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp
new file mode 100644
index 0000000..6c16bdf
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.cpp
@@ -0,0 +1,69 @@
+#include "classInHeader.h"
+
+#ifndef NO_IMPL
+
+#define PREFIX
+
+#ifdef USE_NAMESPACE
+#ifdef USE_NAMESPACE_PREFIX
+#define PREFIX ns::ns2::
+#else
+#ifdef USE_NAMESPACE_USING
+using namespace ns::ns2;
+#else
+namespace ns {
+namespace ns2 {
+#define CLOSE_NAMESPACES
+#endif
+#endif
+#endif
+
+void PREFIX ClassInHeader::implementedToo() {
+
+}
+
+void PREFIX ClassInHeader::implemented() {
+
+}
+// CHECK1: "{{.*}}classInHeader.cpp" "\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE-1]]:2
+// CHECK1-NS-PREFIX: "{{.*}}classInHeader.cpp" "\n\nvoid ns::ns2::ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ns::ns2::ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE-2]]:2
+
+#ifdef CLOSE_NAMESPACES
+}
+}
+#endif
+
+#endif
+
+namespace other {
+#ifndef USE_NAMESPACE_USING
+using namespace ns::ns2;
+#else
+}
+
+void usingCanBeHidden() {
+#ifndef USE_NAMESPACE_USING
+using namespace ns::ns2;
+#else
+}
+
+#ifdef USE_NAMESPACE_USING
+using namespace ns::ns2;
+#else
+// We still want to insert 'using namespace ns::ns2' if the outer is already
+// used.
+using namespace ns;
+#endif
+
+using namespace other;
+
+namespace ns {
+namespace ns2 {
+// Prefer to insert the methods at the end using 'using' instead of into a
+// namespace.
+}
+}
+
+// CHECK1-NO-IMPL-USING-NS-IN-RECORD: "{{.*}}classInHeader.cpp" "\nusing namespace ns::ns2;\n\nvoid OuterRecord::ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid OuterRecord::ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE+3]]:1
+// CHECK1-NO-IMPL-USING-NS: "{{.*}}classInHeader.cpp" "\nusing namespace ns::ns2;\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE+2]]:1
+// CHECK1-NO-IMPL: "{{.*}}classInHeader.cpp" "\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE+1]]:1 -> [[@LINE+1]]:1
diff --git a/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h b/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h
new file mode 100644
index 0000000..89c01bd
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/Inputs/classInHeader.h
@@ -0,0 +1,31 @@
+
+#ifdef USE_NAMESPACE
+namespace ns {
+namespace ns2 {
+#endif
+
+#ifdef USE_ENCLOSING_RECORD
+struct OuterRecord {
+#endif
+
+struct ClassInHeader {
+ void pleaseImplement();
+ void implemented();
+ void pleaseImplementThisAsWell();
+ void implementedToo();
+ void anotherMethod();
+};
+
+#ifdef USE_ENCLOSING_RECORD
+}
+#endif
+
+void ClassInHeader::anotherMethod() {
+}
+// CHECK: "{{.*}}classInHeader.h" "\n\nvoid ClassInHeader::pleaseImplement() { \n <#code#>;\n}\n\nvoid ClassInHeader::pleaseImplementThisAsWell() { \n <#code#>;\n}\n" [[@LINE-1]]:2 -> [[@LINE-1]]:2
+
+#ifdef USE_NAMESPACE
+}
+}
+#endif
+
diff --git a/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp b/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/Inputs/empty.cpp
@@ -0,0 +1 @@
+
diff --git a/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m b/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m
new file mode 100644
index 0000000..318b7b7
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/Inputs/objcClass.m
@@ -0,0 +1,15 @@
+#include "objcHeader.h"
+
+@implementation MyClass
+
+#ifdef MIX_IMPL
++ (void)classMethod { }
+
+- (void)method:(int)x with:(int)y { }
+#endif
+
+@end
+// CHECK1: "{{.*}}objcClass.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// CHECK2: "{{.*}}objcClass.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1
+
+// CHECK-CAT-NO-IMPL: "{{.*}}objcClass.m" "- (void)thisCategoryMethodShouldBeInTheClassImplementation { \n <#code#>;\n}\n\n" 11:1 -> 11:1
diff --git a/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h b/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h
new file mode 100644
index 0000000..8d79abe
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/Inputs/objcHeader.h
@@ -0,0 +1,13 @@
+
+@interface MyClass
+
+- (void)method;
+
++ (void)classMethod;
+
+- (void)implementedMethod;
+
+- (void)method:(int)x with:(int)y;
+
+@end
+
diff --git a/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp
new file mode 100644
index 0000000..b94d92b
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.cpp
@@ -0,0 +1,64 @@
+
+struct Class {
+ int field;
+
+ Class();
+
+ Class(int x) { }
+
+ ~Class();
+
+ // commment
+ void method();
+
+ virtual voidMethod(int y) const;
+ void implementedMethod() const {
+
+ }
+
+ void outOfLineImpl(int x);
+
+ void anotherImplementedMethod() {
+
+ }
+};
+// CHECK1: Initiated the 'implement-declared-methods' action at [[@LINE-20]]:3
+// CHECK2: Initiated the 'implement-declared-methods' action at [[@LINE-17]]:3
+// CHECK3: Initiated the 'implement-declared-methods' action at [[@LINE-15]]:3
+// CHECK4: Initiated the 'implement-declared-methods' action at [[@LINE-14]]:3
+
+void function();
+
+void function() {
+
+}
+
+void Class::outOfLineImpl(int x) {
+
+}
+
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:5:3-10 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:9:3-11 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:12:3-16 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:14:3-34 %s | FileCheck --check-prefix=CHECK4 %s
+
+// RUN: not clang-refactor-test initiate -action implement-declared-methods -in=%s:2:1-end -in=%s:3:1-end -in=%s:4:1-end -in=%s:5:1-2 -in=%s:7:1-end -in=%s:9:1-2 -in=%s:11:1-end -in=%s:15:1-end -in=%s:16:1-end -in=%s:17:1-end -in=%s:19:1-end %s -in=%s:30:1-end 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// CHECK-NO: Failed to initiate the refactoring action
+
+// RUN: clang-refactor-test list-actions -at=%s:5:3 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Generate Missing Function Definitions
+
+// Class, ~Class, method, voidMethod:
+// CHECK5: Initiated the 'implement-declared-methods' action at [[@LINE-48]]:3 -> [[@LINE-39]]:34
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:2:1-24:3 -selected=%s:3:1-23:1 -selected=%s:4:1-23:3 -selected=%s:5:1-14:35 -selected=%s:5:9-14:4 %s | FileCheck --check-prefix=CHECK5 %s
+
+// ~Class, method
+// CHECK6: Initiated the 'implement-declared-methods' action at [[@LINE-48]]:3 -> [[@LINE-45]]:16
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:9:1-12:16 -selected=%s:7:1-13:1 -selected=%s:7:17-12:4 %s | FileCheck --check-prefix=CHECK6 %s
+
+// voidMethod
+// CHECK7: Initiated the 'implement-declared-methods' action at [[@LINE-47]]:3
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:14:3-14:10 -selected=%s:14:22-14:27 %s | FileCheck --check-prefix=CHECK7 %s
+
+// RUN: not clang-refactor-test initiate -action implement-declared-methods -selected=%s:2:1-30:10 -selected=%s:15:1-15:10 -selected=%s:16:1-16:3 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m
new file mode 100644
index 0000000..626926b
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods-initiate.m
@@ -0,0 +1,106 @@
+
+@protocol P
+
+- (void)method;
+
+@end
+
+@interface MyClass {
+ int ivar;
+}
+
+@property int prop;
+
+- (void)method;
+
+// comment
++ (void)classMethod;
+
+- (void)implementedMethod;
+
+- (void)method:(int)x with:(int)y;
+
+@end
+
+@implementation MyClass
+
+- (void)implementedMethod {
+
+}
+
+@end
+
+
+// CHECK1: Initiated the 'implement-declared-methods' action at [[@LINE-20]]:1
+// CHECK2: Initiated the 'implement-declared-methods' action at [[@LINE-18]]:1
+// CHECK3: Initiated the 'implement-declared-methods' action at [[@LINE-15]]:1
+
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:14:1-14 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:17:1-20 %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test initiate -action implement-declared-methods -in=%s:21:1-34 %s | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: not clang-refactor-test initiate -action implement-declared-methods -in=%s:4:1-end -in=%s:27:1-end -in=%s:8:1-end -in=%s:9:1-end -in=%s:12:1-end -in=%s:16:1-end -in=%s:19:1-end -in=%s:23:1-end %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// CHECK-NO: Failed to initiate the refactoring action
+
+// method, classMethod, method:with: :
+// CHECK4: Initiated the 'implement-declared-methods' action at [[@LINE-33]]:1 -> [[@LINE-26]]:35
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:8:1-24:3 -selected=%s:9:1-23:3 -selected=%s:14:1-21:35 -selected=%s:14:14-21:2 %s | FileCheck --check-prefix=CHECK4 %s
+
+// classMethod, method:with:
+// CHECK5: Initiated the 'implement-declared-methods' action at [[@LINE-34]]:1 -> [[@LINE-30]]:35
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:17:1-21:35 -selected=%s:16:1-22:1 -selected=%s:17:20-21:2 %s | FileCheck --check-prefix=CHECK5 %s
+
+// classMethod
+// CHECK6: Initiated the 'implement-declared-methods' action at [[@LINE-38]]:1
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:17:1-17:10 -selected=%s:17:20-18:1 %s | FileCheck --check-prefix=CHECK6 %s
+
+// RUN: not clang-refactor-test initiate -action implement-declared-methods -selected=%s:2:1-30:10 -selected=%s:2:1-6:10 -selected=%s:6:1-25:2 -selected=%s:27:1-29:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// Methods declared in class extensions / categories should be supported:
+
+@interface I2
+
+@end
+
+@interface I2 ()
+
+- (void)method;
++ (void)classMethod;
+- (void)implementedMethod;
+
+@end
+// CHECK7: Initiated the 'implement-declared-methods' action at [[@LINE-5]]:1
+// RUN: clang-refactor-test initiate -action implement-declared-methods -at=%s:68:1 %s | FileCheck --check-prefix=CHECK7 %s
+// RUN: not clang-refactor-test initiate -action implement-declared-methods -at=%s:70:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// CHECK8: Initiated the 'implement-declared-methods' action at [[@LINE-9]]:1 -> [[@LINE-8]]:21
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:68:1-71:1 %s | FileCheck --check-prefix=CHECK8 %s
+
+@implementation I2
+
+- (void)implementedMethod {
+}
+
+@end
+
+@interface I2 (Extension)
+
+- (void)methodExt;
++ (void)classMethodExt;
+- (void)implementedMethodExt;
+
+@end
+// CHECK9: Initiated the 'implement-declared-methods' action at [[@LINE-5]]:1
+// RUN: clang-refactor-test initiate -action implement-declared-methods -at=%s:89:1 %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: not clang-refactor-test initiate -action implement-declared-methods -at=%s:91:1 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+
+// CHECK10: Initiated the 'implement-declared-methods' action at [[@LINE-9]]:1 -> [[@LINE-8]]:24
+// RUN: clang-refactor-test initiate -action implement-declared-methods -selected=%s:89:1-92:1 %s | FileCheck --check-prefix=CHECK10 %s
+
+@implementation I2 (Extension)
+
+- (void)implementedMethodExt {
+}
+
+@end
diff --git a/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp
new file mode 100644
index 0000000..3d8164f
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.cpp
@@ -0,0 +1,168 @@
+
+struct Class {
+ int field;
+
+ Class();
+
+ Class(int x) { }
+
+ ~Class();
+
+ // commment middle-methods-begin: +1:1
+ static void method(const int &value, int defaultParam = 20);
+
+ virtual int voidMethod(int y) const;
+ void implementedMethod() const { // middle-methods-end: -1:40
+
+ }
+
+ void outOfLineImpl(int x);
+
+ void anotherImplementedMethod() {
+
+ }
+};
+// CHECK1: "{{.*}}implement-declared-methods.cpp" "\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n\nint Class::voidMethod(int y) const { \n <#code#>;\n}\n" [[@LINE+5]]:37 -> [[@LINE+5]]:37
+// CHECK2: "{{.*}}implement-declared-methods.cpp" "\n\nClass::Class() { \n <#code#>;\n}\n\nClass::~Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n\nint Class::voidMethod(int y) const { \n <#code#>;\n}\n" [[@LINE+4]]:37
+// CHECK3: "{{.*}}implement-declared-methods.cpp" "\n\nClass::~Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n" [[@LINE+3]]:37
+// CHECK4: "{{.*}}implement-declared-methods.cpp" "\n\nClass::Class() { \n <#code#>;\n}\n\nvoid Class::method(const int &value, int defaultParam) { \n <#code#>;\n}\n" [[@LINE+2]]:37
+
+void Class::outOfLineImpl(int x) { }
+
+// query-all-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 0, 0, 0] }] }]
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=middle-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:8:1-12:10 -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK3 %s
+
+// Implement the constructor and method:
+// query-mix-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }]
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%s -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK4 %s
+
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%S/Inputs/class.cpp -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK1 %S/Inputs/class.cpp
+
+// Empty continuation TU should produce an error:
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=%s:5:1-20:1 -continuation-file=%S/Inputs/empty.cpp -query-results=query-mix-impl %s 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s
+// CHECK-EMPTY-ERR: failed to perform the refactoring continuation (the target class is not defined in the continuation AST unit)!
+
+#ifdef USE_NAMESPACE
+namespace ns {
+namespace ns2 {
+#endif
+
+#ifdef USE_ENCLOSING_RECORD
+struct OuterRecord {
+#endif
+
+struct ClassInHeader {
+// class-in-header-begin: +1:1
+ void pleaseImplement();
+ void implemented();
+ void pleaseImplementThisAsWell();
+ void implementedToo();
+// class-in-header-end: +1:1
+};
+
+#ifdef USE_ENCLOSING_RECORD
+struct }
+#endif
+
+#ifdef USE_NAMESPACE
+}
+}
+#endif
+
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK1 %S/Inputs/classInHeader.cpp
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DUSE_NAMESPACE | FileCheck --check-prefix=CHECK1 %S/Inputs/classInHeader.cpp
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DUSE_NAMESPACE -DUSE_NAMESPACE_USING | FileCheck --check-prefix=CHECK1 %S/Inputs/classInHeader.cpp
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DUSE_NAMESPACE -DUSE_NAMESPACE_PREFIX | FileCheck --check-prefix=CHECK1-NS-PREFIX %S/Inputs/classInHeader.cpp
+
+// Test when the implementation file has no out-of-line definitions.
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL | FileCheck --check-prefix=CHECK1-NO-IMPL %S/Inputs/classInHeader.cpp
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL -DUSE_NAMESPACE | FileCheck --check-prefix=CHECK1-NO-IMPL-USING-NS %S/Inputs/classInHeader.cpp
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL -DUSE_NAMESPACE -DUSE_ENCLOSING_RECORD | FileCheck --check-prefix=CHECK1-NO-IMPL-USING-NS-IN-RECORD %S/Inputs/classInHeader.cpp
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl %s -DNO_IMPL -DUSE_NAMESPACE -DUSE_NAMESPACE_USING | FileCheck --check-prefix=CHECK1-NO-IMPL %S/Inputs/classInHeader.cpp
+
+// query-mix-impl-header: [ { name: ast.producer.query, filenameResult: "%S/classInHeader.h" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }]
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-mix-impl-header %s | FileCheck %S/Inputs/classInHeader.h
+
+// query-no-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [1, 1, 1, 1] }] }]
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=class-in-header -continuation-file=%S/Inputs/classInHeader.cpp -query-results=query-no-impl %s 2>&1 | FileCheck --check-prefix=ALL-IMPLEMENTED-ERROR %s
+// ALL-IMPLEMENTED-ERROR: error: continuation failed: the selected methods are already implemented
+
+// Ensure that the methods which are placed right after the record are placed
+// after the outermost record:
+namespace ns {
+
+struct AfterRecordOuterOuter {
+struct AfterRecordOuter {
+ struct AfterRecordInner {
+// after-record-inner-begin: +1:1
+ void pleaseImplement();
+// after-record-inner-end: +0:1
+ };
+
+ AfterRecordOuter();
+};
+// comment
+};
+// CHECK-OUTERMOST: "{{.*}}implement-declared-methods.cpp" "\n\nvoid AfterRecordOuterOuter::AfterRecordOuter::AfterRecordInner::pleaseImplement() { \n <#code#>;\n}\n" [[@LINE-1]]:3 -> [[@LINE-1]]:3
+
+}
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=after-record-inner -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-OUTERMOST %s
+
+#ifdef INNER_TEMPLATE
+template<typename T>
+struct OuterTemplateRecord {
+#else
+ template<typename U>
+#endif
+ struct InnerTemplate {
+// inner-template-begin: +0:1
+ InnerTemplate();
+ void function();
+// inner-template-end: +1:1
+ };
+#ifdef INNER_TEMPLATE
+};
+#endif
+
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=inner-template %s 2>&1 | FileCheck --check-prefix=CHECK-TEMPLATE-NO %s
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=inner-template %s -DINNER_TEMPLATE 2>&1 | FileCheck --check-prefix=CHECK-TEMPLATE-NO %s
+
+// CHECK-TEMPLATE-NO: Failed to initiate the refactoring action (templates are unsupported)!
+
+template<int x, typename T>
+class TemplateSpecialization {
+};
+
+template<>
+class TemplateSpecialization<0, int> {
+// template-specialization-begin: +0:1
+ TemplateSpecialization();
+ void function();
+ void operator ()(int) const;
+ operator int() const;
+// template-specialization-end: +0:1
+};
+// CHECK-SPECIALIZATION: "{{.*}}implement-declared-methods.cpp" "\n\nTemplateSpecialization<0, int>::TemplateSpecialization() { \n <#code#>;\n}\n\nvoid TemplateSpecialization<0, int>::function() { \n <#code#>;\n}\n\nvoid TemplateSpecialization<0, int>::operator()(int) const { \n <#code#>;\n}\n\nTemplateSpecialization<0, int>::operator int() const { \n <#code#>;\n}\n" [[@LINE-1]]:3
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=template-specialization -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-SPECIALIZATION %s
+
+template<int x>
+class TemplateSpecialization<x, int> {
+// template-partial-specialization-begin: +0:1
+ void function();
+// template-partial-specialization-end: +0:1
+};
+
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=template-partial-specialization %s -DINNER_TEMPLATE 2>&1 | FileCheck --check-prefix=CHECK-TEMPLATE-NO %s
+
+struct ProhibitTemplateFunctions {
+// template-function-begin: +0:1
+ void function();
+ template<typename T>
+ void functionTemplate(const T &);
+ void anotherFunction();
+// template-function-end: +0:1
+};
+// CHECK-FUNCTION-TEMPLATE: "{{.*}}implement-declared-methods.cpp" "\n\nvoid ProhibitTemplateFunctions::function() { \n <#code#>;\n}\n\nvoid ProhibitTemplateFunctions::anotherFunction() { \n <#code#>;\n}\n" [[@LINE-1]]:3
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=template-function -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-FUNCTION-TEMPLATE %s
diff --git a/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m
new file mode 100644
index 0000000..834e20f
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/implement-declared-methods.m
@@ -0,0 +1,85 @@
+
+@interface MyClass
+
+// all-methods-begin: +1:1
+- (void)method;
+
++ (void)classMethod;
+
+- (void)implementedMethod;
+
+- (void)method:(int)x with:(int)y;
+// all-methods-end: +0:1
+
+@end
+
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s
+
+@interface MyClass ()
+
+// extension-methods-begin: +1:1
+- (void)anExtensionMethod;
+// extension-methods-end: +0:1
+
+@end
+
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=extension-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-EXT %s
+
+#ifndef NO_IMPL
+@implementation MyClass
+
+- (void)someOtherMethod { }
+
+@end
+// CHECK1: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n+ (void)classMethod { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n- (void)method:(int)x with:(int)y { \n <#code#>;\n}\n\n" [[@LINE-1]]:1 -> [[@LINE-1]]:1
+// CHECK2: "{{.*}}implement-declared-methods.m" "- (void)method { \n <#code#>;\n}\n\n- (void)implementedMethod { \n <#code#>;\n}\n\n" [[@LINE-2]]:1
+// CHECK-EXT: "{{.*}}implement-declared-methods.m" "- (void)anExtensionMethod { \n <#code#>;\n}\n\n" [[@LINE-3]]:1
+// CHECK-CAT-NO-IMPL: "{{.*}}implement-declared-methods.m" "- (void)thisCategoryMethodShouldBeInTheClassImplementation { \n <#code#>;\n}\n\n" [[@LINE-4]]:1 -> [[@LINE-4]]:1
+#endif
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-mix-impl %s | FileCheck --check-prefix=CHECK2 %s
+
+// query-all-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 0, 0, 0] }] }]
+// query-mix-impl: [ { name: ast.producer.query, filenameResult: "%s" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 1, 0, 1] }] }]
+
+// Empty continuation TU or TU without @implementation should produce an error:
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/empty.cpp -query-results=query-all-impl %s 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-all-impl %s -DNO_IMPL 2>&1 | FileCheck --check-prefix=CHECK-EMPTY-ERR %s
+// CHECK-EMPTY-ERR: failed to perform the refactoring continuation (the target @interface is not implemented in the continuation AST unit)!
+
+// query-no-file-all-impl: [ { name: ast.producer.query, filenameResult: "" } , { name: decl.query , predicateResults: [{name: decl.isDefined, intValues: [0, 0, 0, 0] }] }]
+// RUN: not clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%s -query-results=query-no-file-all-impl %s -DNO_IMPL 2>&1 | FileCheck --check-prefix=CHECK-NO-IMPLEMENTATION-ERROR %s
+// CHECK-NO-IMPLEMENTATION-ERROR: error: continuation failed: no @implementation declaration for the selected @interface 'MyClass'; please add one and run the refactoring action again
+
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/objcClass.m -query-results=query-all-impl %s -DNO_IMPL | FileCheck --check-prefix=CHECK1 %S/Inputs/objcClass.m
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods -continuation-file=%S/Inputs/objcClass.m -query-results=query-mix-impl %s -DNO_IMPL -DMIX_IMPL | FileCheck --check-prefix=CHECK2 %S/Inputs/objcClass.m
+
+@interface MyClass (Category)
+
+// all-category-methods-begin: +1:1
+- (void)categoryMethod;
++ (MyClass *)classCategoryMethod;
+// all-category-methods-end: +0:1
+
+@end
+
+@implementation MyClass (Category)
+
+- (void)anotherMethod {
+}
+
+@end
+// CHECK3: "{{.*}}implement-declared-methods.m" "- (void)categoryMethod { \n <#code#>;\n}\n\n+ (MyClass *)classCategoryMethod { \n <#code#>;\n}\n\n" [[@LINE-1]]:1
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-category-methods -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK3 %s
+
+@interface MyClass (NoCategoryImplementation)
+
+// category-no-impl-begin: +1:1
+- (void)thisCategoryMethodShouldBeInTheClassImplementation;
+// category-no-impl-end: +0:1
+
+@end
+
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=category-no-impl -continuation-file=%s -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-CAT-NO-IMPL %s
+// It should work even in another TU!
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=category-no-impl -continuation-file=%S/Inputs/objcClass.m -query-results=query-all-impl %s | FileCheck --check-prefix=CHECK-CAT-NO-IMPL %S/Inputs/objcClass.m
diff --git a/test/Refactor/ImplementDeclaredMethods/local-record.cpp b/test/Refactor/ImplementDeclaredMethods/local-record.cpp
new file mode 100644
index 0000000..6fbc3e4
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/local-record.cpp
@@ -0,0 +1,30 @@
+void function() {
+struct Class {
+ // one-method: +2:3
+ // all-methods-begin: +1:1
+ Class();
+
+ ~Class(); // comment
+
+ int constOverride() const override;
+
+ // comment
+ void method(const int &value, int defaultParam = 20)
+ ;
+
+ void implementedMethod() const {
+
+ }
+};
+// all-methods-end: -1:1
+}
+// CHECK1: " { \n <#code#>;\n}" [[@LINE-16]]:10 -> [[@LINE-16]]:11
+// RUN: clang-refactor-test perform -action implement-declared-methods -at=one-method %s | FileCheck --check-prefix=CHECK1 %s
+
+// CHECK2: " { \n <#code#>;\n}" [[@LINE-19]]:10 -> [[@LINE-19]]:11
+// CHECK2-NEXT: "" [[@LINE-18]]:11 -> [[@LINE-18]]:12
+// CHECK2-NEXT: " { \n <#code#>;\n}" [[@LINE-19]]:23 -> [[@LINE-19]]:23
+// CHECK2-NEXT: " { \n <#code#>;\n}" [[@LINE-18]]:37 -> [[@LINE-18]]:38
+// CHECK2-NEXT: " { \n <#code#>;\n}" [[@LINE-15]]:7 -> [[@LINE-15]]:8
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods %s | FileCheck --check-prefix=CHECK2 %s
+
diff --git a/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp b/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp
new file mode 100644
index 0000000..6f434d0
--- /dev/null
+++ b/test/Refactor/ImplementDeclaredMethods/prohibited-methods.cpp
@@ -0,0 +1,19 @@
+// RUN: clang-refactor-test perform -action implement-declared-methods -selected=all-methods %s -std=c++11 | FileCheck %s
+// default, deleted and pure methods should not be implemented!
+
+void function() {
+struct Class {
+// all-methods-begin: +1:1
+ Class();
+// CHECK: " { \n <#code#>;\n}" [[@LINE-1]]:10 -> [[@LINE-1]]:11
+
+ Class(const Class &Other) = default;
+ Class(const Class &&Other) = delete;
+
+ virtual void pureMethod(int x) = 0;
+
+ virtual void method() const;
+// CHECK-NEXT: " { \n <#code#>;\n}" [[@LINE-1]]:30 -> [[@LINE-1]]:31
+};
+// all-methods-end: -1:1
+}
diff --git a/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m b/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m
new file mode 100644
index 0000000..335608e
--- /dev/null
+++ b/test/Refactor/LocalizeObjCStringLiteral/localize-objc-initiate.m
@@ -0,0 +1,26 @@
+@class NSString;
+
+void initiate() {
+ NSString *string = @"hello";
+ const char *cString = "world";
+}
+
+// RUN: clang-refactor-test list-actions -at=%s:4:25 %s | FileCheck --check-prefix=CHECK-ACTION %s
+// CHECK-ACTION: Wrap in NSLocalizedString
+
+// Ensure the the action can be initiated in the string literal:
+
+// RUN: clang-refactor-test initiate -action localize-objc-string-literal -in=%s:4:22-30 %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: Initiated the 'localize-objc-string-literal' action at 4:22
+
+// Ensure that the action can't be initiated in other places:
+
+// RUN: not clang-refactor-test initiate -action localize-objc-string-literal -in=%s:1:1-10 -in=%s:3:1-18 -in=%s:4:1-21 -in=%s:5:1-32 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO: Failed to initiate the refactoring action
+
+// Ensure that the action can be initiated using a selection, and only when that
+// selection doesn't go out of the string.
+
+// RUN: clang-refactor-test initiate -action localize-objc-string-literal -selected=%s:4:22-4:30 -selected=%s:4:25-4:30 -selected=%s:4:22-4:27 -selected=%s:4:25-4:27 -selected=%s:4:24-4:29 -selected=%s:4:23-4:30 %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: not clang-refactor-test initiate -action localize-objc-string-literal -selected=%s:4:20-4:30 -selected=%s:4:1-4:25 -selected=%s:4:25-5:5 -selected=%s:3:17-6:2 %s 2>&1 | FileCheck --check-prefix=CHECK-NO %s
diff --git a/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m b/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m
new file mode 100644
index 0000000..96095de
--- /dev/null
+++ b/test/Refactor/LocalizeObjCStringLiteral/localize-objc-perform.m
@@ -0,0 +1,20 @@
+@class NSString;
+
+void perform() {
+ NSString *string = @"hello";
+}
+// CHECK1: "NSLocalizedString(" [[@LINE-2]]:22 -> [[@LINE-2]]:22
+// CHECK1-NEXT: ", @"")" [[@LINE-3]]:30 -> [[@LINE-3]]:30
+// RUN: clang-refactor-test perform -action localize-objc-string-literal -at=%s:4:22 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test perform -action localize-objc-string-literal -selected=%s:4:23-4:30 %s | FileCheck --check-prefix=CHECK1 %s
+
+#define MACRO(x, y) x
+
+void performInMacroArgument() {
+ // macro-arg: +2:9
+ // macro-arg-range-begin: +1:9
+ MACRO(@"hello", 1); // CHECK2: "NSLocalizedString(" [[@LINE]]:9 -> [[@LINE]]:9
+ // macro-arg-range-end: -1:17 // CHECK2: ", @"")" [[@LINE-1]]:17 -> [[@LINE-1]]:17
+}
+// RUN: clang-refactor-test perform -action localize-objc-string-literal -at=macro-arg %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test perform -action localize-objc-string-literal -selected=macro-arg-range %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp b/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp
new file mode 100644
index 0000000..f520a2e
--- /dev/null
+++ b/test/Refactor/Rename/CanonicalizeInstantiatedDecls.cpp
@@ -0,0 +1,86 @@
+
+template<typename T>
+class BaseTemplate {
+public:
+ T baseTemplateFunction();
+
+ T baseTemplateField;
+
+ struct NestedBaseType { };
+};
+
+template<typename T, typename S>
+class TemplateClass: public BaseTemplate<T> {
+public:
+ T function() { return T(); }
+
+ static void staticFunction() { }
+
+ T field;
+
+ struct NestedType {
+ T nestedField;
+
+ class SubNestedType {
+ public:
+ SubNestedType(int);
+ };
+ using TypeAlias = T;
+
+ typedef int Typedef;
+
+ enum Enum {
+ EnumCase
+ };
+ };
+};
+
+void canonicalizeInstaniationReferences(TemplateClass<int, float> &object) {
+ (void)object.function();
+// CHECK1: 'c:@ST>2#T#T@TemplateClass@F@function#'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s
+ (void)object.field;
+// CHECK2: 'c:@ST>2#T#T@TemplateClass@FI@field'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK2 %s
+ (void)object.baseTemplateFunction();
+// CHECK3: 'c:@ST>1#T@BaseTemplate@F@baseTemplateFunction#'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK3 %s
+ (void)object.baseTemplateField;
+// CHECK4: 'c:@ST>1#T@BaseTemplate@FI@baseTemplateField'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):16 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK4 %s
+
+ TemplateClass<int, float>::staticFunction();
+// CHECK5: 'c:@ST>2#T#T@TemplateClass@F@staticFunction#S'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):30 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK5 %s
+
+ TemplateClass<int, float>::NestedBaseType nestedBaseType;
+// CHECK6: 'c:@ST>1#T@BaseTemplate@S@NestedBaseType'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):30 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK6 %s
+ TemplateClass<int, float>::NestedType nestedSubType;
+// CHECK7: 'c:@ST>2#T#T@TemplateClass@S@NestedType'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):30 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK7 %s
+ (void)nestedSubType.nestedField;
+// CHECK8: 'c:@ST>2#T#T@TemplateClass@S@NestedType@FI@nestedField'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):23 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK8 %s
+
+ typedef TemplateClass<int, float> TT;
+ TT::NestedType::SubNestedType subNestedType(0);
+// CHECK9: 'c:@ST>2#T#T@TemplateClass@S@NestedType'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):7 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK9 %s
+// CHECK10: 'c:@ST>2#T#T@TemplateClass@S@NestedType@S@SubNestedType'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-4):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK10 %s
+
+ TT::NestedType::TypeAlias nestedTypeAlias;
+// CHECK11: 'c:@ST>2#T#T@TemplateClass@S@NestedType@TypeAlias'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK11 %s
+ TT::NestedType::Typedef nestedTypedef;
+// CHECK12: 'c:{{.*}}CanonicalizeInstantiatedDecls.cpp@ST>2#T#T@TemplateClass@S@NestedType@T@Typedef'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK12 %s
+
+ TT::NestedType::Enum nestedEnum;
+// CHECK13: 'c:@ST>2#T#T@TemplateClass@S@NestedType@E@Enum'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):19 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK13 %s
+ (void)TT::NestedType::Enum::EnumCase;
+// CHECK14: 'c:@ST>2#T#T@TemplateClass@S@NestedType@E@Enum@EnumCase'
+// RUN: clang-refactor-test rename-initiate -at=%s:%(line-2):31 -new-name=x -dump-symbols %s | FileCheck --check-prefix=CHECK14 %s
+}
diff --git a/test/Refactor/Rename/ClassAsTemplateArgument.cpp b/test/Refactor/Rename/ClassAsTemplateArgument.cpp
new file mode 100644
index 0000000..6310780
--- /dev/null
+++ b/test/Refactor/Rename/ClassAsTemplateArgument.cpp
@@ -0,0 +1,20 @@
+class Foo /* Test 1 */ {}; // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+
+template <typename T>
+void func() {}
+
+template <typename T>
+class Baz {};
+
+int main() {
+ func<Foo>(); /* Test 2 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+ Baz<Foo> /* Test 3 */ obj; // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:1:7 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:10:8 -new-name=Bar %s | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:11:7 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/ClassSimpleRenaming.cpp b/test/Refactor/Rename/ClassSimpleRenaming.cpp
new file mode 100644
index 0000000..78d213c
--- /dev/null
+++ b/test/Refactor/Rename/ClassSimpleRenaming.cpp
@@ -0,0 +1,38 @@
+class Foo /* Test 1 */ { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ void foo(int x);
+};
+
+void Foo::foo(int x) /* Test 2 */ {} // CHECK: rename [[@LINE]]:6 -> [[@LINE]]:9
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:1:7 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:6:6 -new-name=Bar %s | FileCheck %s
+
+struct ForwardDeclaration; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:26
+
+ForwardDeclaration *variable; // CHECK2: rename [[@LINE]]:1 -> [[@LINE]]:19
+
+// RUN: clang-refactor-test rename-initiate -at=%s:13:8 -at=%s:15:1 -new-name=Bar %s | FileCheck --check-prefix=CHECK2 %s
+
+template<typename T>
+struct SpecializationWithoutDefinition { }; // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:39
+
+template<>
+struct SpecializationWithoutDefinition<int> { }; // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:39
+
+template<>
+struct SpecializationWithoutDefinition<float>; // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:39
+
+// RUN: clang-refactor-test rename-initiate -at=%s:20:8 -at=%s:23:8 -new-name=Bar %s | FileCheck --check-prefix=CHECK3 %s
+
+template<typename T>
+struct Class {
+ void method();
+};
+
+template<typename T> // CHECK4: [[@LINE]]:19 -> [[@LINE]]:20
+void Class<T>::method() { } // CHECK4: [[@LINE]]:12 -> [[@LINE]]:13
+
+// RUN: clang-refactor-test rename-initiate -at=%s:35:19 -new-name=Bar %s | FileCheck --check-prefix=CHECK4 %s
diff --git a/test/Refactor/Rename/ComplexFunctionOverride.cpp b/test/Refactor/Rename/ComplexFunctionOverride.cpp
new file mode 100644
index 0000000..52f09e8
--- /dev/null
+++ b/test/Refactor/Rename/ComplexFunctionOverride.cpp
@@ -0,0 +1,76 @@
+struct A {
+ virtual void foo() {} /* Test 1 */ // CHECK: rename [[@LINE]]:16 -> [[@LINE]]:19
+};
+
+struct B : A {
+ void foo() override {} /* Test 2 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+
+struct C : B {
+ void foo() override {} /* Test 3 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+
+struct D : B {
+ void foo() override {} /* Test 4 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+
+struct E : D {
+ void foo() override {} /* Test 5 */ // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+
+int main() {
+ A a;
+ a.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ B b;
+ b.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ C c;
+ c.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ D d;
+ d.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ E e;
+ e.foo(); // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:2:16 -new-name=bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:6:8 -new-name=bar %s | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:10:8 -new-name=bar %s | FileCheck %s
+// Test 4.
+// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=bar %s | FileCheck %s
+// Test 5.
+// RUN: clang-refactor-test rename-initiate -at=%s:18:8 -new-name=bar %s | FileCheck %s
+
+// Check virtual inheritance
+
+struct A2 {
+ virtual void foo() {} // CHECK-VIRT: rename [[@LINE]]:16 -> [[@LINE]]:19
+};
+struct B2 : virtual A2 {
+ void foo() { } // CHECK-VIRT: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+struct C2 : virtual A2 {
+ void foo() override { } // CHECK-VIRT: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+struct D2 : B2, C2 {
+ void foo() override { // CHECK-VIRT: rename [[@LINE]]:8 -> [[@LINE]]:11
+ A2::foo(); // CHECK-VIRT: rename [[@LINE]]:9 -> [[@LINE]]:12
+ }
+};
+
+int bar() {
+ A2 a;
+ a.foo(); // CHECK-VIRT: rename [[@LINE]]:5 -> [[@LINE]]:8
+ D2 d;
+ d.foo(); // CHECK-VIRT: rename [[@LINE]]:5 -> [[@LINE]]:8
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:49:16 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
+// RUN: clang-refactor-test rename-initiate -at=%s:52:8 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
+// RUN: clang-refactor-test rename-initiate -at=%s:55:8 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
+// RUN: clang-refactor-test rename-initiate -at=%s:58:8 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
+// RUN: clang-refactor-test rename-initiate -at=%s:59:9 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
+// RUN: clang-refactor-test rename-initiate -at=%s:65:5 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
+// RUN: clang-refactor-test rename-initiate -at=%s:67:5 -new-name=bar %s | FileCheck --check-prefix=CHECK-VIRT %s
diff --git a/test/Refactor/Rename/ComplicatedClassType.cpp b/test/Refactor/Rename/ComplicatedClassType.cpp
new file mode 100644
index 0000000..eb0fea8
--- /dev/null
+++ b/test/Refactor/Rename/ComplicatedClassType.cpp
@@ -0,0 +1,62 @@
+// Forward declaration.
+class Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+
+class Baz {
+ virtual int getValue() const = 0;
+};
+
+class Foo : public Baz { /* Test 2 */// CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ Foo(int value = 0) : x(value) {} // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+
+ Foo &operator++(int) { // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ x++;
+ return *this;
+ }
+
+ bool operator<(Foo const &rhs) { // CHECK: rename [[@LINE]]:18 -> [[@LINE]]:21
+ return this->x < rhs.x;
+ }
+
+ int getValue() const {
+ return 0;
+ }
+
+private:
+ int x;
+};
+
+int main() {
+ Foo *Pointer = 0; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ Foo Variable = Foo(10); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ // CHECK: rename [[@LINE-1]]:18 -> [[@LINE-1]]:21
+ for (Foo it; it < Variable; it++) { // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+ }
+ const Foo *C = new Foo(); // CHECK: rename [[@LINE]]:9 -> [[@LINE]]:12
+ // CHECK: rename [[@LINE-1]]:22 -> [[@LINE-1]]:25
+ const_cast<Foo *>(C)->getValue(); // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17
+ Foo foo; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ const Baz &BazReference = foo;
+ const Baz *BazPointer = &foo;
+ dynamic_cast<const Foo &>(BazReference).getValue(); /* Test 3 */ // CHECK: rename [[@LINE]]:22 -> [[@LINE]]:25
+ dynamic_cast<const Foo *>(BazPointer)->getValue(); /* Test 4 */ // CHECK: rename [[@LINE]]:22 -> [[@LINE]]:25
+ reinterpret_cast<const Foo *>(BazPointer)->getValue(); /* Test 5 */ // CHECK: rename [[@LINE]]:26 -> [[@LINE]]:29
+ static_cast<const Foo &>(BazReference).getValue(); /* Test 6 */ // CHECK: rename [[@LINE]]:21 -> [[@LINE]]:24
+ static_cast<const Foo *>(BazPointer)->getValue(); /* Test 7 */ // CHECK: rename [[@LINE]]:21 -> [[@LINE]]:24
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=Bar %s -frtti | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:8:7 -new-name=Bar %s -frtti | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:41:22 -new-name=Bar %s -frtti | FileCheck %s
+// Test 4.
+// RUN: clang-refactor-test rename-initiate -at=%s:42:22 -new-name=Bar %s -frtti | FileCheck %s
+// Test 5.
+// RUN: clang-refactor-test rename-initiate -at=%s:43:26 -new-name=Bar %s -frtti | FileCheck %s
+// Test 6.
+// RUN: clang-refactor-test rename-initiate -at=%s:44:21 -new-name=Bar %s -frtti | FileCheck %s
+// Test 7.
+// RUN: clang-refactor-test rename-initiate -at=%s:45:21 -new-name=Bar %s -frtti | FileCheck %s
diff --git a/test/Refactor/Rename/Ctor.cpp b/test/Refactor/Rename/Ctor.cpp
new file mode 100644
index 0000000..73ac020
--- /dev/null
+++ b/test/Refactor/Rename/Ctor.cpp
@@ -0,0 +1,29 @@
+class Foo { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ Foo(); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ Foo(int x, int y); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ ~Foo(); // CHECK: rename [[@LINE]]:4 -> [[@LINE]]:7
+};
+
+Foo::Foo() {} // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4
+// CHECK: rename [[@LINE-1]]:6 -> [[@LINE-1]]:9
+
+Foo::Foo(int x, int y) { } // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4
+// CHECK: rename [[@LINE-1]]:6 -> [[@LINE-1]]:9
+
+Foo::~Foo() {} // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4
+// CHECK: rename [[@LINE-1]]:7 -> [[@LINE-1]]:10
+
+Foo f(const Foo &Other) { // CHECK: rename [[@LINE]]:1 -> [[@LINE]]:4
+ // CHECK: rename [[@LINE-1]]:13 -> [[@LINE-1]]:16
+ return Foo(Other); // CHECK: rename [[@LINE]]:10 -> [[@LINE]]:13
+}
+
+// Declarations.
+// RUN: clang-refactor-test rename-initiate -at=%s:3:3 -at=%s:4:3 -at=%s:5:4 -new-name=Bar %s | FileCheck %s
+
+// Definitions.
+// RUN: clang-refactor-test rename-initiate -at=%s:8:6 -at=%s:11:6 -at=%s:14:7 -new-name=Bar %s | FileCheck %s
+
+// Implicit copy constructor.
+// RUN: clang-refactor-test rename-initiate -at=%s:19:10 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/CtorInitializer.cpp b/test/Refactor/Rename/CtorInitializer.cpp
new file mode 100644
index 0000000..fe5b609
--- /dev/null
+++ b/test/Refactor/Rename/CtorInitializer.cpp
@@ -0,0 +1,14 @@
+class Baz {};
+
+class Qux {
+ Baz Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ Qux();
+};
+
+Qux::Qux() : Foo() /* Test 2 */ {} // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:4:7 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:9:14 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/DeclRefExpr.cpp b/test/Refactor/Rename/DeclRefExpr.cpp
new file mode 100644
index 0000000..3808343
--- /dev/null
+++ b/test/Refactor/Rename/DeclRefExpr.cpp
@@ -0,0 +1,21 @@
+class C {
+public:
+ static int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17
+};
+
+int foo(int x) { return 0; }
+#define MACRO(a) foo(a)
+
+int main() {
+ C::Foo = 1; /* Test 2 */ // CHECK: rename [[@LINE]]:6 -> [[@LINE]]:9
+ MACRO(C::Foo); // CHECK: rename [[@LINE]]:12 -> [[@LINE]]:15
+ int y = C::Foo; /* Test 3 */ // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:3:14 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:10:6 -new-name=Bar %s | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:12:14 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/DependentExpressions.cpp b/test/Refactor/Rename/DependentExpressions.cpp
new file mode 100644
index 0000000..0c7612c
--- /dev/null
+++ b/test/Refactor/Rename/DependentExpressions.cpp
@@ -0,0 +1,72 @@
+int invalid;
+
+class Base {
+ void baseFunction(); // CHECK3: [[@LINE]]:8 -> [[@LINE]]:20
+
+ int baseField;
+
+ static void staticBaseFunction(); // CHECK7: [[@LINE]]:15 -> [[@LINE]]:33
+};
+
+template<typename T>
+class BaseTemplate {
+public:
+ T baseTemplateFunction();
+
+ T baseTemplateField; // CHECK4: [[@LINE]]:5 -> [[@LINE]]:22
+
+ static T baseTemplateVariable; // CHECK8: [[@LINE]]:12 -> [[@LINE]]:32
+};
+
+template<typename T, typename S>
+class TemplateClass: public Base , public BaseTemplate<T> {
+public:
+ ~TemplateClass();
+
+ T function() { } // CHECK1: [[@LINE]]:5 -> [[@LINE]]:13
+
+ static void staticFunction() { } // CHECK5: [[@LINE]]:15 -> [[@LINE]]:29
+
+ T field; // CHECK2: [[@LINE]]:5 -> [[@LINE]]:10
+
+ static T variable; // CHECK6: [[@LINE]]:12 -> [[@LINE]]:20
+
+ struct Struct { }; // CHECK9: [[@LINE]]:10 -> [[@LINE]]:16
+
+ enum Enum { EnumValue }; // CHECK10: [[@LINE]]:8 -> [[@LINE]]:12
+
+ using TypeAlias = S; // CHECK11: [[@LINE]]:9 -> [[@LINE]]:18
+ typedef T Typedef;
+
+ void overload1(const T &);
+ void overload1(const S &);
+};
+
+template<typename T, typename S>
+void renameSimpleDependentDeclarations(const TemplateClass<T, S> &object) {
+
+ object.function(); // CHECK1: [[@LINE]]:10 -> [[@LINE]]:18
+// RUN: clang-refactor-test rename-initiate -at=%s:26:5 -at=%s:48:10 -new-name=x %s | FileCheck --check-prefix=CHECK1 %s
+ object.field; // CHECK2: [[@LINE]]:10 -> [[@LINE]]:15
+// RUN: clang-refactor-test rename-initiate -at=%s:30:5 -at=%s:50:10 -new-name=x %s | FileCheck --check-prefix=CHECK2 %s
+ object.baseFunction(); // CHECK3: [[@LINE]]:10 -> [[@LINE]]:22
+// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -at=%s:52:10 -new-name=x %s | FileCheck --check-prefix=CHECK3 %s
+ object.baseTemplateField; // CHECK4: [[@LINE]]:10 -> [[@LINE]]:27
+// RUN: clang-refactor-test rename-initiate -at=%s:16:5 -at=%s:54:10 -new-name=x %s | FileCheck --check-prefix=CHECK4 %s
+
+ TemplateClass<T, S>::staticFunction(); // CHECK5: [[@LINE]]:24 -> [[@LINE]]:38
+// RUN: clang-refactor-test rename-initiate -at=%s:28:15 -at=%s:57:24 -new-name=x %s | FileCheck --check-prefix=CHECK5 %s
+ TemplateClass<T, S>::variable; // CHECK6: [[@LINE]]:24 -> [[@LINE]]:32
+// RUN: clang-refactor-test rename-initiate -at=%s:32:12 -at=%s:59:24 -new-name=x %s | FileCheck --check-prefix=CHECK6 %s
+ TemplateClass<T, S>::staticBaseFunction(); // CHECK7: [[@LINE]]:24 -> [[@LINE]]:42
+// RUN: clang-refactor-test rename-initiate -at=%s:8:15 -at=%s:61:24 -new-name=x %s | FileCheck --check-prefix=CHECK7 %s
+ TemplateClass<T, S>::baseTemplateVariable; // CHECK8: [[@LINE]]:24 -> [[@LINE]]:44
+// RUN: clang-refactor-test rename-initiate -at=%s:18:12 -at=%s:63:24 -new-name=x %s | FileCheck --check-prefix=CHECK8 %s
+
+ typename TemplateClass<T, S>::Struct Val; // CHECK9: [[@LINE]]:33 -> [[@LINE]]:39
+// RUN: clang-refactor-test rename-initiate -at=%s:34:10 -at=%s:66:33 -new-name=x %s | FileCheck --check-prefix=CHECK9 %s
+ typename TemplateClass<T, S>::Enum EnumVal; // CHECK10: [[@LINE]]:33 -> [[@LINE]]:37
+// RUN: clang-refactor-test rename-initiate -at=%s:36:8 -at=%s:68:33 -new-name=x %s | FileCheck --check-prefix=CHECK10 %s
+ typename TemplateClass<T, S>::TypeAlias Val2; // CHECK11: [[@LINE]]:33 -> [[@LINE]]:42
+// RUN: clang-refactor-test rename-initiate -at=%s:38:9 -at=%s:70:33 -new-name=x %s | FileCheck --check-prefix=CHECK11 %s
+}
diff --git a/test/Refactor/Rename/Field.cpp b/test/Refactor/Rename/Field.cpp
new file mode 100644
index 0000000..94b144a
--- /dev/null
+++ b/test/Refactor/Rename/Field.cpp
@@ -0,0 +1,12 @@
+class Baz {
+ int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ Baz();
+};
+
+Baz::Baz() : Foo(0) /* Test 2 */ {} // CHECK: rename [[@LINE]]:14 -> [[@LINE]]:17
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:7:14 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/FunctionMacro.cpp b/test/Refactor/Rename/FunctionMacro.cpp
new file mode 100644
index 0000000..946c4d1
--- /dev/null
+++ b/test/Refactor/Rename/FunctionMacro.cpp
@@ -0,0 +1,17 @@
+#define moo foo
+
+int foo() /* Test 1 */ { // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ return 42;
+}
+
+void boo(int value) {}
+
+void qoo() {
+ foo(); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ boo(foo()); // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+ moo(); // CHECK: macro [[@LINE]]:3 -> [[@LINE]]:3
+ boo(moo()); // CHECK: macro [[@LINE]]:7 -> [[@LINE]]:7
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:3:5 -new-name=macro_function %s | FileCheck %s
diff --git a/test/Refactor/Rename/FunctionOverride.cpp b/test/Refactor/Rename/FunctionOverride.cpp
new file mode 100644
index 0000000..9c6fc34
--- /dev/null
+++ b/test/Refactor/Rename/FunctionOverride.cpp
@@ -0,0 +1,10 @@
+class A { virtual void foo(); /* Test 1 */ }; // CHECK: rename [[@LINE]]:24 -> [[@LINE]]:27
+class B : public A { void foo(); /* Test 2 */ }; // CHECK: rename [[@LINE]]:27 -> [[@LINE]]:30
+class C : public B { void foo(); /* Test 3 */ }; // CHECK: rename [[@LINE]]:27 -> [[@LINE]]:30
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:1:24 -new-name=bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:2:27 -new-name=bar %s | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:3:27 -new-name=bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/IndexedObjCMessageSend.mm b/test/Refactor/Rename/IndexedObjCMessageSend.mm
new file mode 100644
index 0000000..1fd6070
--- /dev/null
+++ b/test/Refactor/Rename/IndexedObjCMessageSend.mm
@@ -0,0 +1,882 @@
++(BOOL) onEntity {
+ call() = [_undef_ivar name: @"string literal" method: @"string literal"];
+ // CHECK1: [[@LINE-1]]:25 -> [[@LINE-1]]:29, [[@LINE-1]]:49 -> [[@LINE-1]]:55
+}
+// RUN: clang-refactor-test rename-indexed-file -name=name:method -new-name=object:world -indexed-file=%s -indexed-at=2:25 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK1 %s
+
+-(const Object &) a_200 {
+ some_type_t world = @"string literal";
+ [self withSomething: [] () {
+ some_type_t foo = [super struct: @"string literal"
+ world: [] () {
+ } name: [] {
+
+
+ } a_200: 12 a_200: globalArray[i]];
+ // CHECK2: [[@LINE-6]]:29 -> [[@LINE-6]]:35, [[@LINE-5]]:2 -> [[@LINE-5]]:7, [[@LINE-4]]:5 -> [[@LINE-4]]:9, [[@LINE-1]]:4 -> [[@LINE-1]]:9, [[@LINE-1]]:14 -> [[@LINE-1]]:19
+
+ } onEntity: ']' perform: "]"];
+ // CHECK3: [[@LINE-10]]:9 -> [[@LINE-10]]:22, [[@LINE-1]]:4 -> [[@LINE-1]]:12, [[@LINE-1]]:18 -> [[@LINE-1]]:25
+}
+// RUN: clang-refactor-test rename-indexed-file -name=struct:world:name:a_200:a_200 -new-name=withSomething:foo:part:struct:method -indexed-file=%s -indexed-at=10:29 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:onEntity:perform -new-name=method:foo:name -indexed-file=%s -indexed-at=9:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK3 %s
+
+-(int) struct {
+ // comment
+}
+
+-(int) bar {
+ return ']';
+}
+
+-(int) part {
+ call() = [self test: 12 method: globalArray[i] test: globalArray[i] world: ']'];
+ // CHECK4: [[@LINE-1]]:18 -> [[@LINE-1]]:22, [[@LINE-1]]:27 -> [[@LINE-1]]:33, [[@LINE-1]]:50 -> [[@LINE-1]]:54, [[@LINE-1]]:71 -> [[@LINE-1]]:76
+ [_undef_ivar withSomething: @{ @1, @3 } onEntity: [] () { ([self foo: "]" piece: foo: (']')]);
+ // CHECK5: [[@LINE-1]]:71 -> [[@LINE-1]]:74, [[@LINE-1]]:80 -> [[@LINE-1]]:85, [[@LINE-1]]:88 -> [[@LINE-1]]:91
+ }];
+ // CHECK6: [[@LINE-3]]:16 -> [[@LINE-3]]:29, [[@LINE-3]]:43 -> [[@LINE-3]]:51
+ int bar = [self name: ']' z_Z_42: [] () { [globalObject send: [self perform: globalArray[i] * "string" a_200: 12 class: 12 perform: ']' object: [] () {
+ }] other: 42];
+ // CHECK7: [[@LINE-2]]:74 -> [[@LINE-2]]:81, [[@LINE-2]]:109 -> [[@LINE-2]]:114, [[@LINE-2]]:119 -> [[@LINE-2]]:124, [[@LINE-2]]:129 -> [[@LINE-2]]:136, [[@LINE-2]]:142 -> [[@LINE-2]]:148
+ } bar: ^ {
+ [globalObject message] = ([self onEntity: globalArray[i] class: 12 foo: "string"]);
+ // CHECK8: [[@LINE-1]]:36 -> [[@LINE-1]]:44, [[@LINE-1]]:61 -> [[@LINE-1]]:66, [[@LINE-1]]:71 -> [[@LINE-1]]:74
+
+ } bar: ^ () {
+ some_type_t foo = [self perform: 12 bar: (^ () {
+ }) bar: ^ {
+ } perform: @"string literal" test: "]" + 12];
+ // CHECK9: [[@LINE-3]]:28 -> [[@LINE-3]]:35, [[@LINE-3]]:40 -> [[@LINE-3]]:43, [[@LINE-2]]:6 -> [[@LINE-2]]:9, [[@LINE-1]]:5 -> [[@LINE-1]]:12, [[@LINE-1]]:32 -> [[@LINE-1]]:36
+
+ }];
+ // CHECK10: [[@LINE-14]]:19 -> [[@LINE-14]]:23, [[@LINE-14]]:29 -> [[@LINE-14]]:35, [[@LINE-11]]:5 -> [[@LINE-11]]:8, [[@LINE-7]]:4 -> [[@LINE-7]]:7
+ [self piece: @"string literal" method: globalArray[i] method: "string" class: globalArray[i]];
+ // CHECK11: [[@LINE-1]]:9 -> [[@LINE-1]]:14, [[@LINE-1]]:34 -> [[@LINE-1]]:40, [[@LINE-1]]:57 -> [[@LINE-1]]:63, [[@LINE-1]]:74 -> [[@LINE-1]]:79
+}
+// RUN: clang-refactor-test rename-indexed-file -name=test:method:test:world -new-name=a_200:struct:perform:piece -indexed-file=%s -indexed-at=33:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:piece:foo -new-name=world:onEntity:name -indexed-file=%s -indexed-at=35:71 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:onEntity -new-name=class:object -indexed-file=%s -indexed-at=35:16 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:a_200:class:perform:object -new-name=method:perform:class:foo:name -indexed-file=%s -indexed-at=39:74 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK7 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:class:foo -new-name=piece:onEntity:bar -indexed-file=%s -indexed-at=43:36 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:bar:bar:perform:test -new-name=foo:world:class:struct:z_Z_42 -indexed-file=%s -indexed-at=47:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:z_Z_42:bar:bar -new-name=world:piece:perform:test -indexed-file=%s -indexed-at=39:19 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK10 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:method:method:class -new-name=a_200:withSomething:onEntity:onEntity -indexed-file=%s -indexed-at=54:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK11 %s
+
++(int) struct {
+ call() = [self name: ^ () { const Object & piece = 12;
+ } perform: @{ @1, @3 } a_200: ^ {
+ some_type_t foo = [self name: ']' name: ^ () {
+ } method: ']'];
+ // CHECK12: [[@LINE-2]]:28 -> [[@LINE-2]]:32, [[@LINE-2]]:38 -> [[@LINE-2]]:42, [[@LINE-1]]:5 -> [[@LINE-1]]:11
+
+ }];
+ // CHECK13: [[@LINE-7]]:18 -> [[@LINE-7]]:22, [[@LINE-6]]:5 -> [[@LINE-6]]:12, [[@LINE-6]]:26 -> [[@LINE-6]]:31
+}
+// RUN: clang-refactor-test rename-indexed-file -name=name:name:method -new-name=part:bar:usingThing -indexed-file=%s -indexed-at=69:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK12 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:perform:a_200 -new-name=a_200:piece:class -indexed-file=%s -indexed-at=67:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK13 %s
+
++(BOOL) world {
+ int bar = ([_undef_ivar world: "string" struct: @"string literal" world: globalArray[i]
+ perform: ^ { // comment
+ } < "string" name: [] () {
+ int bar = [self a_200: "string" * 12 method: globalArray[i] usingThing: @"string literal" part: ']'];
+ // CHECK14: [[@LINE-1]]:20 -> [[@LINE-1]]:25, [[@LINE-1]]:41 -> [[@LINE-1]]:47, [[@LINE-1]]:64 -> [[@LINE-1]]:74, [[@LINE-1]]:94 -> [[@LINE-1]]:98
+
+ }]);
+ // CHECK15: [[@LINE-7]]:27 -> [[@LINE-7]]:32, [[@LINE-7]]:43 -> [[@LINE-7]]:49, [[@LINE-7]]:69 -> [[@LINE-7]]:74, [[@LINE-6]]:2 -> [[@LINE-6]]:9, [[@LINE-5]]:16 -> [[@LINE-5]]:20
+ [self perform: [self object: globalArray[i] onEntity: /*]*/ [] () {
+ ; ;[self.undef_property test: [] () {
+
+
+ } onEntity: @"string literal"
+];
+ // CHECK16: [[@LINE-5]]:28 -> [[@LINE-5]]:32, [[@LINE-2]]:4 -> [[@LINE-2]]:12
+
+ } method: "]"] world: "]" withSomething: @"string literal"];
+ // CHECK17: [[@LINE-9]]:24 -> [[@LINE-9]]:30, [[@LINE-9]]:47 -> [[@LINE-9]]:55, [[@LINE-1]]:4 -> [[@LINE-1]]:10
+ // CHECK18: [[@LINE-10]]:9 -> [[@LINE-10]]:16, [[@LINE-2]]:17 -> [[@LINE-2]]:22, [[@LINE-2]]:28 -> [[@LINE-2]]:41
+ [self usingThing: ^ {
+ ;
+
+ } world: "string" test: "]"];
+ // CHECK19: [[@LINE-4]]:9 -> [[@LINE-4]]:19, [[@LINE-1]]:4 -> [[@LINE-1]]:9, [[@LINE-1]]:20 -> [[@LINE-1]]:24
+ call() = [self bar: [] { globalArray[12] = [self onEntity: ^ () { ] } foo: "string" piece: @{ @1, @3 } bar: ']'];
+ // CHECK20: [[@LINE-1]]:56 -> [[@LINE-1]]:64, [[@LINE-1]]:77 -> [[@LINE-1]]:80, [[@LINE-1]]:91 -> [[@LINE-1]]:96, [[@LINE-1]]:110 -> [[@LINE-1]]:113
+ }
+//comment
+ struct: [] { return ^ () {
+ };
+ } piece: "string" onEntity: ^ () { ] }];
+ // CHECK21: [[@LINE-7]]:18 -> [[@LINE-7]]:21, [[@LINE-3]]:2 -> [[@LINE-3]]:8, [[@LINE-1]]:5 -> [[@LINE-1]]:10, [[@LINE-1]]:21 -> [[@LINE-1]]:29
+ some_type_t foo = [_undef_ivar world: 12
+ bar: [] () {
+ [globalObject send: [super class: 12 world: [self perform: 12 perform: @{ @1, @3 }
+ object: "string"] class: "string" z_Z_42: @{ @1, @3 }] other: 42];
+ // CHECK22: [[@LINE-2]]:54 -> [[@LINE-2]]:61, [[@LINE-2]]:66 -> [[@LINE-2]]:73, [[@LINE-1]]:2 -> [[@LINE-1]]:8
+ // CHECK23: [[@LINE-3]]:31 -> [[@LINE-3]]:36, [[@LINE-3]]:41 -> [[@LINE-3]]:46, [[@LINE-2]]:20 -> [[@LINE-2]]:25, [[@LINE-2]]:36 -> [[@LINE-2]]:42
+
+ }];
+ // CHECK24: [[@LINE-8]]:34 -> [[@LINE-8]]:39, [[@LINE-7]]:2 -> [[@LINE-7]]:5
+}
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:method:usingThing:part -new-name=foo:bar:bar:class -indexed-file=%s -indexed-at=83:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK14 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:struct:world:perform:name -new-name=perform:bar:object:foo:object -indexed-file=%s -indexed-at=80:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK15 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:onEntity -new-name=name:class -indexed-file=%s -indexed-at=89:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK16 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:onEntity:method -new-name=usingThing:a_200:onEntity -indexed-file=%s -indexed-at=88:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK17 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:world:withSomething -new-name=onEntity:method:part -indexed-file=%s -indexed-at=88:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK18 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:world:test -new-name=name:object:onEntity -indexed-file=%s -indexed-at=99:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK19 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:foo:piece:bar -new-name=test:foo:test:name -indexed-file=%s -indexed-at=104:56 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK20 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:struct:piece:onEntity -new-name=usingThing:method:part:piece -indexed-file=%s -indexed-at=104:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK21 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:perform:object -new-name=usingThing:withSomething:withSomething -indexed-file=%s -indexed-at=114:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK22 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:world:class:z_Z_42 -new-name=bar:piece:class:z_Z_42 -indexed-file=%s -indexed-at=114:31 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK23 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:bar -new-name=foo:bar -indexed-file=%s -indexed-at=112:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK24 %s
+
+-(void) test {
+ call() = [self name: globalArray[i] onEntity: "]" bar: ^ { return [self withSomething: [] () {
+
+
+ } name: ']' part: 12];
+ // CHECK25: [[@LINE-4]]:79 -> [[@LINE-4]]:92, [[@LINE-1]]:4 -> [[@LINE-1]]:8, [[@LINE-1]]:14 -> [[@LINE-1]]:18
+ } usingThing: "]" object: ];
+ // CHECK26: [[@LINE-6]]:18 -> [[@LINE-6]]:22, [[@LINE-6]]:39 -> [[@LINE-6]]:47, [[@LINE-6]]:53 -> [[@LINE-6]]:56, [[@LINE-1]]:5 -> [[@LINE-1]]:15, [[@LINE-1]]:21 -> [[@LINE-1]]:27
+ [globalObject message] = [self a_200: globalArray[i] struct: @{ @1, @3 }
+];
+ // CHECK27: [[@LINE-2]]:34 -> [[@LINE-2]]:39, [[@LINE-2]]:56 -> [[@LINE-2]]:62
+}
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:name:part -new-name=z_Z_42:object:test -indexed-file=%s -indexed-at=135:79 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK25 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:onEntity:bar:usingThing:object -new-name=foo:method:perform:onEntity:perform -indexed-file=%s -indexed-at=135:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK26 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:struct -new-name=bar:perform -indexed-file=%s -indexed-at=142:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK27 %s
+
+-(int) class {
+ return /*]*/ [] () {
+ call() = [self struct: (@"string literal") method: ^ () {
+ } foo: @"string literal" onEntity: [] {
+
+
+ } < globalArray[i] method: ']'];
+ // CHECK28: [[@LINE-5]]:19 -> [[@LINE-5]]:25, [[@LINE-5]]:47 -> [[@LINE-5]]:53, [[@LINE-4]]:5 -> [[@LINE-4]]:8, [[@LINE-4]]:28 -> [[@LINE-4]]:36, [[@LINE-1]]:21 -> [[@LINE-1]]:27
+
+ };
+ [self name: [] () { if ([] {
+
+
+ }) {
+ BOOL class = @{ @1, @3 };
+
+ }
+ } perform: 12 onEntity: @"string literal"];
+ // CHECK29: [[@LINE-8]]:9 -> [[@LINE-8]]:13, [[@LINE-1]]:5 -> [[@LINE-1]]:12, [[@LINE-1]]:17 -> [[@LINE-1]]:25
+ return globalArray[i] * [self withSomething: [self part: 12 name: ^ { [globalObject send: [super withSomething: [self test: [] () {
+
+
+ } world: [_undef_ivar piece: 12 class: @"string literal" test: "string" bar: /*]*/ globalArray[i]]
+] withSomething: 12 name: ']' test: "]" usingThing: @"string literal"] other: 42];
+ // CHECK30: [[@LINE-2]]:24 -> [[@LINE-2]]:29, [[@LINE-2]]:34 -> [[@LINE-2]]:39, [[@LINE-2]]:59 -> [[@LINE-2]]:63, [[@LINE-2]]:74 -> [[@LINE-2]]:77
+ // CHECK31: [[@LINE-6]]:125 -> [[@LINE-6]]:129, [[@LINE-3]]:4 -> [[@LINE-3]]:9
+ // CHECK32: [[@LINE-7]]:104 -> [[@LINE-7]]:117, [[@LINE-3]]:3 -> [[@LINE-3]]:16, [[@LINE-3]]:21 -> [[@LINE-3]]:25, [[@LINE-3]]:31 -> [[@LINE-3]]:35, [[@LINE-3]]:41 -> [[@LINE-3]]:51
+ } part: @{ @1, @3 }] withSomething: ']' foo: globalArray[i]];
+ // CHECK33: [[@LINE-9]]:54 -> [[@LINE-9]]:58, [[@LINE-9]]:63 -> [[@LINE-9]]:67, [[@LINE-1]]:5 -> [[@LINE-1]]:9
+ // CHECK34: [[@LINE-10]]:33 -> [[@LINE-10]]:46, [[@LINE-2]]:24 -> [[@LINE-2]]:37, [[@LINE-2]]:43 -> [[@LINE-2]]:46
+}
+// RUN: clang-refactor-test rename-indexed-file -name=struct:method:foo:onEntity:method -new-name=object:piece:struct:foo:name -indexed-file=%s -indexed-at=152:19 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK28 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:perform:onEntity -new-name=z_Z_42:bar:z_Z_42 -indexed-file=%s -indexed-at=160:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK29 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:class:test:bar -new-name=world:bar:object:perform -indexed-file=%s -indexed-at=172:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK30 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:world -new-name=a_200:bar -indexed-file=%s -indexed-at=169:125 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK31 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:withSomething:name:test:usingThing -new-name=bar:class:class:perform:perform -indexed-file=%s -indexed-at=169:104 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK32 %s
+// RUN: clang-refactor-test rename-indexed-file -name=part:name:part -new-name=class:perform:name -indexed-file=%s -indexed-at=169:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK33 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:withSomething:foo -new-name=test:object:withSomething -indexed-file=%s -indexed-at=169:33 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK34 %s
+
++(Object *) usingThing {
+ return "string";
+}
+
++(void) class {
+ if (@{ @1, @3 }) {
+ globalArray[12] = [self foo: [] { [globalObject message] = [self object: @{ @1, @3 } usingThing: globalArray[i] perform: "]"];
+ // CHECK35: [[@LINE-1]]:76 -> [[@LINE-1]]:82, [[@LINE-1]]:96 -> [[@LINE-1]]:106, [[@LINE-1]]:123 -> [[@LINE-1]]:130
+ } class: usingThing: "]" perform: [self.undef_property name: ^ () { ] } piece: 12 name: ^ () {
+ globalArray[12] = [_undef_ivar foo: ']' foo: [] {
+ }
+ bar: ^ {
+ } + [] {
+
+
+ }
+];
+ // CHECK36: [[@LINE-8]]:35 -> [[@LINE-8]]:38, [[@LINE-8]]:44 -> [[@LINE-8]]:47, [[@LINE-6]]:2 -> [[@LINE-6]]:5
+
+ }]];
+ // CHECK37: [[@LINE-12]]:59 -> [[@LINE-12]]:63, [[@LINE-12]]:76 -> [[@LINE-12]]:81, [[@LINE-12]]:86 -> [[@LINE-12]]:90
+ // CHECK38: [[@LINE-15]]:31 -> [[@LINE-15]]:34, [[@LINE-13]]:5 -> [[@LINE-13]]:10, [[@LINE-13]]:13 -> [[@LINE-13]]:23, [[@LINE-13]]:29 -> [[@LINE-13]]:36
+
+ }
+}
+// RUN: clang-refactor-test rename-indexed-file -name=object:usingThing:perform -new-name=test:object:z_Z_42 -indexed-file=%s -indexed-at=195:76 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK35 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:foo:bar -new-name=method:part:class -indexed-file=%s -indexed-at=198:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK36 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:piece:name -new-name=perform:onEntity:struct -indexed-file=%s -indexed-at=197:59 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK37 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:class:usingThing:perform -new-name=name:z_Z_42:method:test -indexed-file=%s -indexed-at=195:31 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK38 %s
+
+-(some_type_t) struct {
+ globalArray[12] = [super a_200: globalArray[i]
+ name: globalArray[i] world: [] {
+ [globalObject send: [self part: ']' z_Z_42: ^ {
+ } + "]" struct: "string"
+ bar: [] () {
+
+
+ }] other: 42];
+ // CHECK39: [[@LINE-6]]:30 -> [[@LINE-6]]:34, [[@LINE-6]]:40 -> [[@LINE-6]]:46, [[@LINE-5]]:11 -> [[@LINE-5]]:17, [[@LINE-4]]:2 -> [[@LINE-4]]:5
+
+ } onEntity: ']' == globalArray[i]];
+ // CHECK40: [[@LINE-11]]:28 -> [[@LINE-11]]:33, [[@LINE-10]]:2 -> [[@LINE-10]]:6, [[@LINE-10]]:23 -> [[@LINE-10]]:28, [[@LINE-1]]:4 -> [[@LINE-1]]:12
+ [self a_200: [] () { BOOL class = 12;
+ } part: ']' test: ^ () { int z_Z_42 = [self struct: "]" perform: "]" method: globalArray[i]];
+ // CHECK41: [[@LINE-1]]:50 -> [[@LINE-1]]:56, [[@LINE-1]]:62 -> [[@LINE-1]]:69, [[@LINE-1]]:75 -> [[@LINE-1]]:81
+ }];
+ // CHECK42: [[@LINE-4]]:9 -> [[@LINE-4]]:14, [[@LINE-3]]:5 -> [[@LINE-3]]:9, [[@LINE-3]]:15 -> [[@LINE-3]]:19
+}
+// RUN: clang-refactor-test rename-indexed-file -name=part:z_Z_42:struct:bar -new-name=world:a_200:a_200:test -indexed-file=%s -indexed-at=222:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK39 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:name:world:onEntity -new-name=object:object:perform:z_Z_42 -indexed-file=%s -indexed-at=220:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK40 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:perform:method -new-name=perform:test:bar -indexed-file=%s -indexed-at=233:50 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK41 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:part:test -new-name=class:method:test -indexed-file=%s -indexed-at=232:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK42 %s
+
++(some_type_t) piece {
+ call() = [self class: "string" part: 12];
+ // CHECK43: [[@LINE-1]]:18 -> [[@LINE-1]]:23, [[@LINE-1]]:34 -> [[@LINE-1]]:38
+ [self struct: globalArray[i] part: "]" part: [self method: @"string literal" == globalArray[i] + [self onEntity: ^ { [_undef_ivar piece: @{ @1, @3 } world: globalArray[i]];
+ // CHECK44: [[@LINE-1]]:137 -> [[@LINE-1]]:142, [[@LINE-1]]:156 -> [[@LINE-1]]:161
+ } object: (^ () { [self class: 12 object: [] {
+
+
+ } == ^ () {
+
+
+ }];
+ // CHECK45: [[@LINE-7]]:30 -> [[@LINE-7]]:35, [[@LINE-7]]:40 -> [[@LINE-7]]:46
+ }) piece: ']' world: 12 part: (12)] struct: /*]*/ globalArray[i] foo: [self object: ^ { // comment
+ // CHECK46: [[@LINE-11]]:106 -> [[@LINE-11]]:114, [[@LINE-9]]:5 -> [[@LINE-9]]:11, [[@LINE-1]]:6 -> [[@LINE-1]]:11, [[@LINE-1]]:17 -> [[@LINE-1]]:22, [[@LINE-1]]:27 -> [[@LINE-1]]:31
+ } world: (^ {
+ int bar = [_undef_ivar test: globalArray[i] + "string" == ']' class: 12];
+ // CHECK47: [[@LINE-1]]:27 -> [[@LINE-1]]:31, [[@LINE-1]]:66 -> [[@LINE-1]]:71
+
+ }) part: ']'
+] perform: "]"] perform: @{ @1, @3 }];
+ // CHECK48: [[@LINE-8]]:79 -> [[@LINE-8]]:85, [[@LINE-6]]:5 -> [[@LINE-6]]:10, [[@LINE-2]]:5 -> [[@LINE-2]]:9
+ // CHECK49: [[@LINE-19]]:54 -> [[@LINE-19]]:60, [[@LINE-9]]:39 -> [[@LINE-9]]:45, [[@LINE-9]]:68 -> [[@LINE-9]]:71, [[@LINE-2]]:3 -> [[@LINE-2]]:10
+ // CHECK50: [[@LINE-20]]:9 -> [[@LINE-20]]:15, [[@LINE-20]]:32 -> [[@LINE-20]]:36, [[@LINE-20]]:42 -> [[@LINE-20]]:46, [[@LINE-3]]:17 -> [[@LINE-3]]:24
+ some_type_t foo = [self.undef_property class: "]" part: [] {
+ [self bar: [] () {
+ } z_Z_42: "]" name: globalArray[i]];
+ // CHECK51: [[@LINE-2]]:10 -> [[@LINE-2]]:13, [[@LINE-1]]:5 -> [[@LINE-1]]:11, [[@LINE-1]]:17 -> [[@LINE-1]]:21
+
+ }
+ onEntity: ^ { return [super withSomething: "string" usingThing: @"string literal" test: @"string literal" withSomething: ']' world: [] () {
+
+
+ }
+];
+ // CHECK52: [[@LINE-5]]:34 -> [[@LINE-5]]:47, [[@LINE-5]]:58 -> [[@LINE-5]]:68, [[@LINE-5]]:88 -> [[@LINE-5]]:92, [[@LINE-5]]:112 -> [[@LINE-5]]:125, [[@LINE-5]]:131 -> [[@LINE-5]]:136
+ } class: 12 struct: @"string literal" == globalArray[i]];
+ // CHECK53: [[@LINE-13]]:42 -> [[@LINE-13]]:47, [[@LINE-13]]:53 -> [[@LINE-13]]:57, [[@LINE-7]]:2 -> [[@LINE-7]]:10, [[@LINE-1]]:5 -> [[@LINE-1]]:10, [[@LINE-1]]:15 -> [[@LINE-1]]:21
+}
+// RUN: clang-refactor-test rename-indexed-file -name=class:part -new-name=a_200:class -indexed-file=%s -indexed-at=244:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK43 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:world -new-name=part:name -indexed-file=%s -indexed-at=246:137 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK44 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:object -new-name=onEntity:z_Z_42 -indexed-file=%s -indexed-at=248:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK45 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:object:piece:world:part -new-name=onEntity:piece:a_200:class:struct -indexed-file=%s -indexed-at=246:106 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK46 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:class -new-name=a_200:test -indexed-file=%s -indexed-at=259:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK47 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:world:part -new-name=class:z_Z_42:onEntity -indexed-file=%s -indexed-at=256:79 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK48 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:struct:foo:perform -new-name=onEntity:bar:part:test -indexed-file=%s -indexed-at=246:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK49 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:part:part:perform -new-name=piece:object:world:usingThing -indexed-file=%s -indexed-at=246:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK50 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:z_Z_42:name -new-name=bar:test:struct -indexed-file=%s -indexed-at=268:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK51 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:usingThing:test:withSomething:world -new-name=foo:test:usingThing:piece:piece -indexed-file=%s -indexed-at=273:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK52 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:part:onEntity:class:struct -new-name=z_Z_42:onEntity:onEntity:a_200:class -indexed-file=%s -indexed-at=267:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK53 %s
+
++(void) z_Z_42 {
+ int a_200 = @{ @1, @3 };
+ // comment
+ [globalObject send: ([self onEntity: [] () { [super onEntity: ^ {
+ } class: @"string literal" perform: @"string literal"] other: 42];
+ // CHECK54: [[@LINE-2]]:58 -> [[@LINE-2]]:66, [[@LINE-1]]:5 -> [[@LINE-1]]:10, [[@LINE-1]]:30 -> [[@LINE-1]]:37
+ } class: globalArray[i] a_200: ^ () { ] }
+]);
+ // CHECK55: [[@LINE-5]]:30 -> [[@LINE-5]]:38, [[@LINE-2]]:5 -> [[@LINE-2]]:10, [[@LINE-2]]:27 -> [[@LINE-2]]:32
+ globalArray[12] = [super method: @"string literal" test: ^ () { [self struct: "]" method: globalArray[i]];
+ // CHECK56: [[@LINE-1]]:76 -> [[@LINE-1]]:82, [[@LINE-1]]:88 -> [[@LINE-1]]:94
+ } name: globalArray[i]];
+ // CHECK57: [[@LINE-3]]:28 -> [[@LINE-3]]:34, [[@LINE-3]]:54 -> [[@LINE-3]]:58, [[@LINE-1]]:5 -> [[@LINE-1]]:9
+ BOOL struct = 12;
+}
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:class:perform -new-name=bar:z_Z_42:onEntity -indexed-file=%s -indexed-at=297:58 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK54 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:class:a_200 -new-name=withSomething:piece:foo -indexed-file=%s -indexed-at=297:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK55 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:method -new-name=onEntity:struct -indexed-file=%s -indexed-at=303:76 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK56 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:test:name -new-name=name:z_Z_42:object -indexed-file=%s -indexed-at=303:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK57 %s
+
++(int) onEntity {
+ call() = [self piece: [] { return "string";
+ } foo: ']' bar: globalArray[i]];
+ // CHECK58: [[@LINE-2]]:18 -> [[@LINE-2]]:23, [[@LINE-1]]:5 -> [[@LINE-1]]:8, [[@LINE-1]]:14 -> [[@LINE-1]]:17
+ [globalObject send: [self usingThing: [] {
+ [_undef_ivar test: globalArray[i] part: 12 world: "string" onEntity: 12] other: 42];
+ // CHECK59: [[@LINE-1]]:17 -> [[@LINE-1]]:21, [[@LINE-1]]:38 -> [[@LINE-1]]:42, [[@LINE-1]]:47 -> [[@LINE-1]]:52, [[@LINE-1]]:63 -> [[@LINE-1]]:71
+
+ } a_200: @"string literal" a_200: "string" object: ("string")
+//comment
+];
+ // CHECK60: [[@LINE-7]]:29 -> [[@LINE-7]]:39, [[@LINE-3]]:4 -> [[@LINE-3]]:9, [[@LINE-3]]:29 -> [[@LINE-3]]:34, [[@LINE-3]]:45 -> [[@LINE-3]]:51
+ [globalObject send: [super object: ']' name: ^ {
+ [globalObject send: [self.undef_property bar: == [self name: "string" bar: ']']
+ part: @"string literal" z_Z_42: ']' part: ']' test: "string"] other: 42] other: 42];
+ // CHECK61: [[@LINE-2]]:60 -> [[@LINE-2]]:64, [[@LINE-2]]:75 -> [[@LINE-2]]:78
+ // CHECK62: [[@LINE-3]]:45 -> [[@LINE-3]]:48, [[@LINE-2]]:2 -> [[@LINE-2]]:6, [[@LINE-2]]:26 -> [[@LINE-2]]:32, [[@LINE-2]]:38 -> [[@LINE-2]]:42, [[@LINE-2]]:48 -> [[@LINE-2]]:52
+
+ }
+ object: globalArray[i] foo: (']')];
+ // CHECK63: [[@LINE-8]]:30 -> [[@LINE-8]]:36, [[@LINE-8]]:42 -> [[@LINE-8]]:46, [[@LINE-1]]:2 -> [[@LINE-1]]:8, [[@LINE-1]]:25 -> [[@LINE-1]]:28
+ [self.undef_property world: globalArray[i] onEntity: ']' object: @"string literal" == 12 struct: [] {
+ int bar = [self onEntity: "]" piece: [] () {
+ }];
+ // CHECK64: [[@LINE-2]]:20 -> [[@LINE-2]]:28, [[@LINE-2]]:34 -> [[@LINE-2]]:39
+
+ } struct: @"string literal"];
+ // CHECK65: [[@LINE-6]]:24 -> [[@LINE-6]]:29, [[@LINE-6]]:46 -> [[@LINE-6]]:54, [[@LINE-6]]:60 -> [[@LINE-6]]:66, [[@LINE-6]]:92 -> [[@LINE-6]]:98, [[@LINE-1]]:4 -> [[@LINE-1]]:10
+}
+// RUN: clang-refactor-test rename-indexed-file -name=piece:foo:bar -new-name=class:onEntity:method -indexed-file=%s -indexed-at=315:18 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK58 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:part:world:onEntity -new-name=z_Z_42:bar:piece:perform -indexed-file=%s -indexed-at=319:17 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK59 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:a_200:a_200:object -new-name=a_200:object:method:perform -indexed-file=%s -indexed-at=318:29 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK60 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:bar -new-name=z_Z_42:z_Z_42 -indexed-file=%s -indexed-at=327:60 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK61 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:part:z_Z_42:part:test -new-name=name:a_200:bar:name:z_Z_42 -indexed-file=%s -indexed-at=327:45 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK62 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:name:object:foo -new-name=z_Z_42:object:usingThing:world -indexed-file=%s -indexed-at=326:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK63 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:piece -new-name=struct:piece -indexed-file=%s -indexed-at=336:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK64 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:onEntity:object:struct:struct -new-name=withSomething:test:foo:object:bar -indexed-file=%s -indexed-at=335:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK65 %s
+
++(const Object &) object {
+ globalArray[12] = [self world: globalArray[i] object: ']' usingThing: usingThing: globalArray[i]];
+ // CHECK66: [[@LINE-1]]:27 -> [[@LINE-1]]:32, [[@LINE-1]]:49 -> [[@LINE-1]]:55, [[@LINE-1]]:61 -> [[@LINE-1]]:71, [[@LINE-1]]:74 -> [[@LINE-1]]:84
+ globalArray[12] = ([self.undef_property struct: ']' usingThing: "string"]);
+ // CHECK67: [[@LINE-1]]:43 -> [[@LINE-1]]:49, [[@LINE-1]]:55 -> [[@LINE-1]]:65
+}
+// RUN: clang-refactor-test rename-indexed-file -name=world:object:usingThing:usingThing -new-name=a_200:struct:test:z_Z_42 -indexed-file=%s -indexed-at=353:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK66 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:usingThing -new-name=struct:a_200 -indexed-file=%s -indexed-at=355:43 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK67 %s
+
+-(void) part {
+ [self.undef_property withSomething: "string" method: [] { call() = [self.undef_property object: @"string literal" test: 12
+ test: globalArray[i]];
+ // CHECK68: [[@LINE-2]]:95 -> [[@LINE-2]]:101, [[@LINE-2]]:121 -> [[@LINE-2]]:125, [[@LINE-1]]:2 -> [[@LINE-1]]:6
+ }];
+ // CHECK69: [[@LINE-4]]:24 -> [[@LINE-4]]:37, [[@LINE-4]]:48 -> [[@LINE-4]]:54
+}
+// RUN: clang-refactor-test rename-indexed-file -name=object:test:test -new-name=piece:bar:a_200 -indexed-file=%s -indexed-at=362:95 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK68 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:method -new-name=withSomething:test -indexed-file=%s -indexed-at=362:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK69 %s
+
++(BOOL) method {
+ [self object: ^ () { ] } a_200: "]" foo: @{ @1, @3 } piece: [] { [self.undef_property a_200: 12 foo: 12 withSomething: globalArray[i] onEntity: ']' struct: 12];
+ // CHECK70: [[@LINE-1]]:93 -> [[@LINE-1]]:98, [[@LINE-1]]:103 -> [[@LINE-1]]:106, [[@LINE-1]]:111 -> [[@LINE-1]]:124, [[@LINE-1]]:141 -> [[@LINE-1]]:149, [[@LINE-1]]:155 -> [[@LINE-1]]:161
+ }
+//comment
+];
+ // CHECK71: [[@LINE-5]]:9 -> [[@LINE-5]]:15, [[@LINE-5]]:28 -> [[@LINE-5]]:33, [[@LINE-5]]:39 -> [[@LINE-5]]:42, [[@LINE-5]]:56 -> [[@LINE-5]]:61
+ [super method: 12 struct: ^ { /*comment*/[super foo: [self bar: @"string literal" method: "string" test: 12 test: "string" world: ']']
+ onEntity: globalArray[i] method: ^ {
+ }];
+ // CHECK72: [[@LINE-3]]:66 -> [[@LINE-3]]:69, [[@LINE-3]]:89 -> [[@LINE-3]]:95, [[@LINE-3]]:106 -> [[@LINE-3]]:110, [[@LINE-3]]:115 -> [[@LINE-3]]:119, [[@LINE-3]]:130 -> [[@LINE-3]]:135
+ // CHECK73: [[@LINE-4]]:55 -> [[@LINE-4]]:58, [[@LINE-3]]:2 -> [[@LINE-3]]:10, [[@LINE-3]]:27 -> [[@LINE-3]]:33
+ } a_200: "string"];
+ // CHECK74: [[@LINE-6]]:10 -> [[@LINE-6]]:16, [[@LINE-6]]:21 -> [[@LINE-6]]:27, [[@LINE-1]]:5 -> [[@LINE-1]]:10
+ if (12) {
+ [self world: "]" usingThing: @"string literal" + [] () { call() = [_undef_ivar part: "]" usingThing: 12
+];
+ // CHECK75: [[@LINE-2]]:89 -> [[@LINE-2]]:93, [[@LINE-2]]:99 -> [[@LINE-2]]:109
+ } foo: 12];
+ // CHECK76: [[@LINE-4]]:13 -> [[@LINE-4]]:18, [[@LINE-4]]:24 -> [[@LINE-4]]:34, [[@LINE-1]]:5 -> [[@LINE-1]]:8
+
+ }
+ [super test: (12) foo: "string" name: [] {
+ return @{ @1, @3 };
+
+ } object: "]"];
+ // CHECK77: [[@LINE-4]]:10 -> [[@LINE-4]]:14, [[@LINE-4]]:21 -> [[@LINE-4]]:24, [[@LINE-4]]:35 -> [[@LINE-4]]:39, [[@LINE-1]]:4 -> [[@LINE-1]]:10
+ return ']';
+}
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:foo:withSomething:onEntity:struct -new-name=world:bar:class:perform:z_Z_42 -indexed-file=%s -indexed-at=372:93 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK70 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:a_200:foo:piece -new-name=part:withSomething:name:foo -indexed-file=%s -indexed-at=372:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK71 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:method:test:test:world -new-name=withSomething:perform:usingThing:usingThing:method -indexed-file=%s -indexed-at=378:66 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK72 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:onEntity:method -new-name=foo:name:a_200 -indexed-file=%s -indexed-at=378:55 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK73 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:struct:a_200 -new-name=method:z_Z_42:struct -indexed-file=%s -indexed-at=378:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK74 %s
+// RUN: clang-refactor-test rename-indexed-file -name=part:usingThing -new-name=withSomething:name -indexed-file=%s -indexed-at=386:89 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK75 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:usingThing:foo -new-name=usingThing:method:class -indexed-file=%s -indexed-at=386:13 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK76 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:foo:name:object -new-name=z_Z_42:world:name:object -indexed-file=%s -indexed-at=393:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK77 %s
+
+-(BOOL) object {
+ [self part: @{ @1, @3 } foo: "]" method: ([self a_200: @"string literal" test: part: globalArray[i] object: "]"]) usingThing: @{ @1, @3 }];
+ // CHECK78: [[@LINE-1]]:51 -> [[@LINE-1]]:56, [[@LINE-1]]:76 -> [[@LINE-1]]:80, [[@LINE-1]]:83 -> [[@LINE-1]]:87, [[@LINE-1]]:104 -> [[@LINE-1]]:110
+ // CHECK79: [[@LINE-2]]:9 -> [[@LINE-2]]:13, [[@LINE-2]]:27 -> [[@LINE-2]]:30, [[@LINE-2]]:36 -> [[@LINE-2]]:42, [[@LINE-2]]:118 -> [[@LINE-2]]:128
+ [self onEntity: [] () { int bar = [self test: "string"/*comment*/ bar: globalArray[i] method: [] () {
+ }];
+ // CHECK80: [[@LINE-2]]:46 -> [[@LINE-2]]:50, [[@LINE-2]]:72 -> [[@LINE-2]]:75, [[@LINE-2]]:92 -> [[@LINE-2]]:98
+ } part: @"string literal" perform: [self.undef_property onEntity: globalArray[i] test: [] {
+ if ("]") {
+ [super foo: "string" z_Z_42: 12];
+ // CHECK81: [[@LINE-1]]:14 -> [[@LINE-1]]:17, [[@LINE-1]]:28 -> [[@LINE-1]]:34
+
+ }
+
+ } method: @"string literal" usingThing: ^ () {
+ Object * method = globalArray[i];
+
+ }]];
+ // CHECK82: [[@LINE-11]]:59 -> [[@LINE-11]]:67, [[@LINE-11]]:84 -> [[@LINE-11]]:88, [[@LINE-4]]:4 -> [[@LINE-4]]:10, [[@LINE-4]]:30 -> [[@LINE-4]]:40
+ // CHECK83: [[@LINE-15]]:9 -> [[@LINE-15]]:17, [[@LINE-12]]:5 -> [[@LINE-12]]:9, [[@LINE-12]]:29 -> [[@LINE-12]]:36
+ [self usingThing: ^ () { int bar = [self onEntity: [self.undef_property perform: "]"
+ struct: [self name: "string" withSomething: ^ () {
+
+
+ }]] foo: ']' object: [_undef_ivar world: [super withSomething: (@{ @1, @3 }) name: @"string literal"
+] onEntity: [super perform: "string" class: @"string literal"]] usingThing: 12 == @"string literal"
+];
+ // CHECK84: [[@LINE-6]]:16 -> [[@LINE-6]]:20, [[@LINE-6]]:31 -> [[@LINE-6]]:44
+ // CHECK85: [[@LINE-8]]:78 -> [[@LINE-8]]:85, [[@LINE-7]]:2 -> [[@LINE-7]]:8
+ // CHECK86: [[@LINE-5]]:50 -> [[@LINE-5]]:63, [[@LINE-5]]:79 -> [[@LINE-5]]:83
+ // CHECK87: [[@LINE-5]]:20 -> [[@LINE-5]]:27, [[@LINE-5]]:38 -> [[@LINE-5]]:43
+ // CHECK88: [[@LINE-7]]:36 -> [[@LINE-7]]:41, [[@LINE-6]]:3 -> [[@LINE-6]]:11
+ // CHECK89: [[@LINE-12]]:47 -> [[@LINE-12]]:55, [[@LINE-8]]:6 -> [[@LINE-8]]:9, [[@LINE-8]]:15 -> [[@LINE-8]]:21, [[@LINE-7]]:65 -> [[@LINE-7]]:75
+ } withSomething: "string" world: "string"];
+ // CHECK90: [[@LINE-14]]:9 -> [[@LINE-14]]:19, [[@LINE-1]]:5 -> [[@LINE-1]]:18, [[@LINE-1]]:29 -> [[@LINE-1]]:34
+ globalArray[12] = [self.undef_property bar: "string" z_Z_42: ^ {
+ [self class: ']' struct: @{ @1, @3 }
+ withSomething: ']' class: (']') world: @{ @1, @3 }];
+ // CHECK91: [[@LINE-2]]:10 -> [[@LINE-2]]:15, [[@LINE-2]]:21 -> [[@LINE-2]]:27, [[@LINE-1]]:2 -> [[@LINE-1]]:15, [[@LINE-1]]:21 -> [[@LINE-1]]:26, [[@LINE-1]]:34 -> [[@LINE-1]]:39
+
+ }
+ a_200: [] () { ; ;[_undef_ivar class: ']' struct: ^ {
+ } z_Z_42: [super withSomething: ^ {
+
+
+ } foo: globalArray[i] perform: (@"string literal") perform: 12]];
+ // CHECK92: [[@LINE-4]]:20 -> [[@LINE-4]]:33, [[@LINE-1]]:4 -> [[@LINE-1]]:7, [[@LINE-1]]:24 -> [[@LINE-1]]:31, [[@LINE-1]]:53 -> [[@LINE-1]]:60
+ // CHECK93: [[@LINE-6]]:36 -> [[@LINE-6]]:41, [[@LINE-6]]:47 -> [[@LINE-6]]:53, [[@LINE-5]]:5 -> [[@LINE-5]]:11
+ }
+];
+ // CHECK94: [[@LINE-15]]:42 -> [[@LINE-15]]:45, [[@LINE-15]]:56 -> [[@LINE-15]]:62, [[@LINE-9]]:2 -> [[@LINE-9]]:7
+}
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:test:part:object -new-name=object:a_200:object:object -indexed-file=%s -indexed-at=410:51 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK78 %s
+// RUN: clang-refactor-test rename-indexed-file -name=part:foo:method:usingThing -new-name=world:onEntity:foo:a_200 -indexed-file=%s -indexed-at=410:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK79 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:bar:method -new-name=object:method:withSomething -indexed-file=%s -indexed-at=413:46 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK80 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:z_Z_42 -new-name=foo:class -indexed-file=%s -indexed-at=418:14 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK81 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:test:method:usingThing -new-name=class:a_200:class:object -indexed-file=%s -indexed-at=416:59 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK82 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:part:perform -new-name=name:z_Z_42:class -indexed-file=%s -indexed-at=413:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK83 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:withSomething -new-name=withSomething:z_Z_42 -indexed-file=%s -indexed-at=430:16 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK84 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:struct -new-name=usingThing:struct -indexed-file=%s -indexed-at=429:78 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK85 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:name -new-name=name:foo -indexed-file=%s -indexed-at=433:50 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK86 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:class -new-name=test:z_Z_42 -indexed-file=%s -indexed-at=434:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK87 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:onEntity -new-name=foo:z_Z_42 -indexed-file=%s -indexed-at=433:36 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK88 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:foo:object:usingThing -new-name=test:part:struct:object -indexed-file=%s -indexed-at=429:47 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK89 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:withSomething:world -new-name=a_200:test:struct -indexed-file=%s -indexed-at=429:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK90 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:struct:withSomething:class:world -new-name=test:bar:foo:usingThing:usingThing -indexed-file=%s -indexed-at=445:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK91 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:foo:perform:perform -new-name=onEntity:struct:piece:withSomething -indexed-file=%s -indexed-at=451:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK92 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:struct:z_Z_42 -new-name=onEntity:method:a_200 -indexed-file=%s -indexed-at=450:36 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK93 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:z_Z_42:a_200 -new-name=withSomething:class:z_Z_42 -indexed-file=%s -indexed-at=444:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK94 %s
+
++(BOOL) world {
+ ; ;[self struct: [] { call() = [self part: 12 class: ^ {
+
+
+ } test: ^ () {
+
+
+ } a_200: "string"];
+ // CHECK95: [[@LINE-7]]:44 -> [[@LINE-7]]:48, [[@LINE-7]]:53 -> [[@LINE-7]]:58, [[@LINE-4]]:4 -> [[@LINE-4]]:8, [[@LINE-1]]:4 -> [[@LINE-1]]:9
+ } piece: "string" z_Z_42: [] { [self onEntity: struct: ^ () {
+ } world: [_undef_ivar withSomething: globalArray[i] piece: ^ {
+
+
+ } method:
+ part: @"string literal" world: @"string literal"
+]];
+ // CHECK96: [[@LINE-6]]:25 -> [[@LINE-6]]:38, [[@LINE-6]]:55 -> [[@LINE-6]]:60, [[@LINE-3]]:4 -> [[@LINE-3]]:10, [[@LINE-2]]:2 -> [[@LINE-2]]:6, [[@LINE-2]]:26 -> [[@LINE-2]]:31
+ // CHECK97: [[@LINE-8]]:44 -> [[@LINE-8]]:52, [[@LINE-8]]:55 -> [[@LINE-8]]:61, [[@LINE-7]]:5 -> [[@LINE-7]]:10
+ }];
+ // CHECK98: [[@LINE-18]]:12 -> [[@LINE-18]]:18, [[@LINE-10]]:5 -> [[@LINE-10]]:10, [[@LINE-10]]:21 -> [[@LINE-10]]:27
+ if (globalArray[i]) {
+ ([_undef_ivar world: ']' class: usingThing: 12]);
+ // CHECK99: [[@LINE-1]]:21 -> [[@LINE-1]]:26, [[@LINE-1]]:32 -> [[@LINE-1]]:37, [[@LINE-1]]:40 -> [[@LINE-1]]:50
+
+ }
+ some_type_t foo = [self.undef_property object: ']'
+ test: ^ () {
+ globalArray[12] = [_undef_ivar z_Z_42: 12 withSomething: [self a_200: ']' perform: globalArray[i] z_Z_42: [] {
+
+
+ } perform: @"string literal" z_Z_42: globalArray[i]]
+ name: 12];
+ // CHECK100: [[@LINE-5]]:67 -> [[@LINE-5]]:72, [[@LINE-5]]:78 -> [[@LINE-5]]:85, [[@LINE-5]]:102 -> [[@LINE-5]]:108, [[@LINE-2]]:4 -> [[@LINE-2]]:11, [[@LINE-2]]:31 -> [[@LINE-2]]:37
+ // CHECK101: [[@LINE-6]]:35 -> [[@LINE-6]]:41, [[@LINE-6]]:46 -> [[@LINE-6]]:59, [[@LINE-2]]:2 -> [[@LINE-2]]:6
+
+ } bar: "]"
+//comment
+ usingThing: [] {
+ return [self onEntity: 12 < 12 withSomething: [] {
+
+
+ }];
+ // CHECK102: [[@LINE-4]]:17 -> [[@LINE-4]]:25, [[@LINE-4]]:35 -> [[@LINE-4]]:48
+
+ } withSomething: ^ {
+ [super class: ([self.undef_property bar: [self withSomething: [self piece: "string" < globalArray[i] onEntity: [self method: 12
+ part: "string" usingThing: @{ @1, @3 }]] perform: globalArray[i] bar: [] {
+ } name: 12] withSomething: "]"]) world: [] () {
+ } usingThing: "]"];
+ // CHECK103: [[@LINE-4]]:121 -> [[@LINE-4]]:127, [[@LINE-3]]:2 -> [[@LINE-3]]:6, [[@LINE-3]]:17 -> [[@LINE-3]]:27
+ // CHECK104: [[@LINE-5]]:72 -> [[@LINE-5]]:77, [[@LINE-5]]:105 -> [[@LINE-5]]:113
+ // CHECK105: [[@LINE-6]]:51 -> [[@LINE-6]]:64, [[@LINE-5]]:43 -> [[@LINE-5]]:50, [[@LINE-5]]:67 -> [[@LINE-5]]:70, [[@LINE-4]]:5 -> [[@LINE-4]]:9
+ // CHECK106: [[@LINE-7]]:40 -> [[@LINE-7]]:43, [[@LINE-5]]:15 -> [[@LINE-5]]:28
+ // CHECK107: [[@LINE-8]]:11 -> [[@LINE-8]]:16, [[@LINE-6]]:36 -> [[@LINE-6]]:41, [[@LINE-5]]:5 -> [[@LINE-5]]:15
+
+ }];
+ // CHECK108: [[@LINE-31]]:42 -> [[@LINE-31]]:48, [[@LINE-30]]:2 -> [[@LINE-30]]:6, [[@LINE-21]]:4 -> [[@LINE-21]]:7, [[@LINE-19]]:2 -> [[@LINE-19]]:12, [[@LINE-12]]:4 -> [[@LINE-12]]:17
+}
+// RUN: clang-refactor-test rename-indexed-file -name=part:class:test:a_200 -new-name=perform:name:test:struct -indexed-file=%s -indexed-at=480:44 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK95 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:piece:method:part:world -new-name=object:piece:world:piece:world -indexed-file=%s -indexed-at=489:25 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK96 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:struct:world -new-name=onEntity:world:world -indexed-file=%s -indexed-at=488:44 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK97 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:piece:z_Z_42 -new-name=part:object:onEntity -indexed-file=%s -indexed-at=480:12 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK98 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:class:usingThing -new-name=class:struct:method -indexed-file=%s -indexed-at=500:21 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK99 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:perform:z_Z_42:perform:z_Z_42 -new-name=piece:part:piece:class:onEntity -indexed-file=%s -indexed-at=506:67 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK100 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:withSomething:name -new-name=usingThing:onEntity:a_200 -indexed-file=%s -indexed-at=506:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK101 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:withSomething -new-name=method:usingThing -indexed-file=%s -indexed-at=517:17 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK102 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:part:usingThing -new-name=object:piece:method -indexed-file=%s -indexed-at=524:121 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK103 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:onEntity -new-name=bar:struct -indexed-file=%s -indexed-at=524:72 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK104 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:perform:bar:name -new-name=usingThing:z_Z_42:class:part -indexed-file=%s -indexed-at=524:51 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK105 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:withSomething -new-name=world:perform -indexed-file=%s -indexed-at=524:40 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK106 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:world:usingThing -new-name=object:name:name -indexed-file=%s -indexed-at=524:11 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK107 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:test:bar:usingThing:withSomething -new-name=bar:piece:class:perform:class -indexed-file=%s -indexed-at=504:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK108 %s
+
++(Object *) test {
+ [super foo: globalArray[i] a_200: globalArray[i]
+//comment
+ foo: 12
+ struct: ^ () {
+ [_undef_ivar class: globalArray[i] struct: 12 object: @"string literal" foo: "string"];
+ // CHECK109: [[@LINE-1]]:17 -> [[@LINE-1]]:22, [[@LINE-1]]:39 -> [[@LINE-1]]:45, [[@LINE-1]]:50 -> [[@LINE-1]]:56, [[@LINE-1]]:76 -> [[@LINE-1]]:79
+
+ } * [super struct: [self world: ']' struct: [] {
+ [self method: [] {
+
+
+ } withSomething: "string" usingThing: [] () {
+
+
+ }];
+ // CHECK110: [[@LINE-7]]:10 -> [[@LINE-7]]:16, [[@LINE-4]]:4 -> [[@LINE-4]]:17, [[@LINE-4]]:28 -> [[@LINE-4]]:38
+
+ } struct: ^ () {
+ return [] () {
+ };
+
+ } class: [_undef_ivar part: "string" name: [] {
+ int bar = [self withSomething: "string" bar: @"string literal" usingThing: @"string literal"];
+ // CHECK111: [[@LINE-1]]:20 -> [[@LINE-1]]:33, [[@LINE-1]]:44 -> [[@LINE-1]]:47, [[@LINE-1]]:67 -> [[@LINE-1]]:77
+
+ }] class: "]"] < 12 foo: * "]"] + "string" + "]" == [] () {
+ some_type_t foo = [_undef_ivar part: 12 name: @"string literal" test: "]" bar: ^ {
+ }];
+ // CHECK112: [[@LINE-7]]:24 -> [[@LINE-7]]:28, [[@LINE-7]]:39 -> [[@LINE-7]]:43
+ // CHECK113: [[@LINE-22]]:27 -> [[@LINE-22]]:32, [[@LINE-22]]:38 -> [[@LINE-22]]:44, [[@LINE-12]]:4 -> [[@LINE-12]]:10, [[@LINE-8]]:4 -> [[@LINE-8]]:9, [[@LINE-4]]:5 -> [[@LINE-4]]:10
+ // CHECK114: [[@LINE-23]]:13 -> [[@LINE-23]]:19, [[@LINE-5]]:22 -> [[@LINE-5]]:25
+ // CHECK115: [[@LINE-5]]:35 -> [[@LINE-5]]:39, [[@LINE-5]]:44 -> [[@LINE-5]]:48, [[@LINE-5]]:68 -> [[@LINE-5]]:72, [[@LINE-5]]:78 -> [[@LINE-5]]:81
+
+ } < ^ {
+ int bar = [self struct: ^ () {
+
+
+ } a_200: @{ @1, @3 }];
+ // CHECK116: [[@LINE-4]]:20 -> [[@LINE-4]]:26, [[@LINE-1]]:4 -> [[@LINE-1]]:9
+
+ } == [super withSomething: [] { return [_undef_ivar usingThing: [] () {
+ }
+ world: [] () {
+
+
+ } foo: 12 perform: globalArray[i] name: @"string literal"];
+ // CHECK117: [[@LINE-6]]:58 -> [[@LINE-6]]:68, [[@LINE-4]]:2 -> [[@LINE-4]]:7, [[@LINE-1]]:4 -> [[@LINE-1]]:7, [[@LINE-1]]:12 -> [[@LINE-1]]:19, [[@LINE-1]]:36 -> [[@LINE-1]]:40
+ } world: ^ { // comment
+ } object: ^ { int method = "]";
+ }
+]];
+ // CHECK118: [[@LINE-11]]:14 -> [[@LINE-11]]:27, [[@LINE-4]]:5 -> [[@LINE-4]]:10, [[@LINE-3]]:5 -> [[@LINE-3]]:11
+ // CHECK119: [[@LINE-52]]:10 -> [[@LINE-52]]:13, [[@LINE-52]]:30 -> [[@LINE-52]]:35, [[@LINE-50]]:2 -> [[@LINE-50]]:5, [[@LINE-49]]:2 -> [[@LINE-49]]:8
+ return [] { return 12;
+ };
+ if ([] {
+ some_type_t foo = [_undef_ivar perform: @"string literal" name: globalArray[i] * ']'
+ z_Z_42: "]" perform: globalArray[i] class: globalArray[i]
+];
+ // CHECK120: [[@LINE-3]]:35 -> [[@LINE-3]]:42, [[@LINE-3]]:62 -> [[@LINE-3]]:66, [[@LINE-2]]:2 -> [[@LINE-2]]:8, [[@LINE-2]]:14 -> [[@LINE-2]]:21, [[@LINE-2]]:38 -> [[@LINE-2]]:43
+
+ }) {
+ [globalObject message] = [self.undef_property withSomething: ']' test: @"string literal" onEntity: ^ {
+ return @{ @1, @3 };
+
+ } onEntity: "string"];
+ // CHECK121: [[@LINE-4]]:53 -> [[@LINE-4]]:66, [[@LINE-4]]:72 -> [[@LINE-4]]:76, [[@LINE-4]]:96 -> [[@LINE-4]]:104, [[@LINE-1]]:4 -> [[@LINE-1]]:12
+
+ }
+ [globalObject message] = [self onEntity: "string" onEntity: ^ { call() = [self withSomething: ^ () {
+ } == ']' perform: ("string") bar: (']')];
+ // CHECK122: [[@LINE-2]]:86 -> [[@LINE-2]]:99, [[@LINE-1]]:12 -> [[@LINE-1]]:19, [[@LINE-1]]:32 -> [[@LINE-1]]:35
+ }
+];
+ // CHECK123: [[@LINE-5]]:34 -> [[@LINE-5]]:42, [[@LINE-5]]:53 -> [[@LINE-5]]:61
+ globalArray[12] = [self withSomething: @"string literal" piece: ^ {
+ call() = [_undef_ivar perform: (12) bar: ^ {
+
+
+ } test: "]" name: "string" bar: "]"];
+ // CHECK124: [[@LINE-4]]:26 -> [[@LINE-4]]:33, [[@LINE-4]]:40 -> [[@LINE-4]]:43, [[@LINE-1]]:4 -> [[@LINE-1]]:8, [[@LINE-1]]:14 -> [[@LINE-1]]:18, [[@LINE-1]]:29 -> [[@LINE-1]]:32
+
+ } z_Z_42: [] () { [self onEntity: [] () {
+
+
+ } piece: "string"];
+ // CHECK125: [[@LINE-4]]:29 -> [[@LINE-4]]:37, [[@LINE-1]]:4 -> [[@LINE-1]]:9
+ } + @"string literal" object: ^ {
+ [globalObject send: [self world: "]" onEntity: ']'
+ struct: [] () {
+ } perform: [self.undef_property piece: [] () {
+
+
+ } method: 12 foo: [] {
+
+
+ } onEntity: "string" * "]" method: "]"] < ']'] other: 42];
+ // CHECK126: [[@LINE-7]]:35 -> [[@LINE-7]]:40, [[@LINE-4]]:4 -> [[@LINE-4]]:10, [[@LINE-4]]:15 -> [[@LINE-4]]:18, [[@LINE-1]]:4 -> [[@LINE-1]]:12, [[@LINE-1]]:29 -> [[@LINE-1]]:35
+ // CHECK127: [[@LINE-10]]:30 -> [[@LINE-10]]:35, [[@LINE-10]]:41 -> [[@LINE-10]]:49, [[@LINE-9]]:2 -> [[@LINE-9]]:8, [[@LINE-8]]:5 -> [[@LINE-8]]:12
+
+ } method: ^ { // comment
+ }];
+ // CHECK128: [[@LINE-27]]:27 -> [[@LINE-27]]:40, [[@LINE-27]]:60 -> [[@LINE-27]]:65, [[@LINE-20]]:4 -> [[@LINE-20]]:10, [[@LINE-15]]:25 -> [[@LINE-15]]:31, [[@LINE-2]]:4 -> [[@LINE-2]]:10
+}
+// RUN: clang-refactor-test rename-indexed-file -name=class:struct:object:foo -new-name=onEntity:onEntity:struct:foo -indexed-file=%s -indexed-at=557:17 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK109 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:withSomething:usingThing -new-name=object:class:withSomething -indexed-file=%s -indexed-at=561:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK110 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:bar:usingThing -new-name=piece:class:test -indexed-file=%s -indexed-at=575:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK111 %s
+// RUN: clang-refactor-test rename-indexed-file -name=part:name -new-name=bar:object -indexed-file=%s -indexed-at=574:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK112 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:struct:struct:class:class -new-name=name:onEntity:z_Z_42:piece:foo -indexed-file=%s -indexed-at=560:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK113 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:foo -new-name=foo:test -indexed-file=%s -indexed-at=560:13 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK114 %s
+// RUN: clang-refactor-test rename-indexed-file -name=part:name:test:bar -new-name=class:z_Z_42:onEntity:usingThing -indexed-file=%s -indexed-at=579:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK115 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:a_200 -new-name=usingThing:bar -indexed-file=%s -indexed-at=587:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK116 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:world:foo:perform:name -new-name=withSomething:foo:world:test:test -indexed-file=%s -indexed-at=593:58 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK117 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:world:object -new-name=withSomething:class:part -indexed-file=%s -indexed-at=593:14 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK118 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:a_200:foo:struct -new-name=usingThing:foo:object:test -indexed-file=%s -indexed-at=553:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK119 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:name:z_Z_42:perform:class -new-name=method:usingThing:class:class:world -indexed-file=%s -indexed-at=609:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK120 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:test:onEntity:onEntity -new-name=onEntity:z_Z_42:a_200:piece -indexed-file=%s -indexed-at=615:53 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK121 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:perform:bar -new-name=foo:usingThing:world -indexed-file=%s -indexed-at=622:86 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK122 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:onEntity -new-name=foo:perform -indexed-file=%s -indexed-at=622:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK123 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:bar:test:name:bar -new-name=onEntity:withSomething:object:method:test -indexed-file=%s -indexed-at=629:26 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK124 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:piece -new-name=world:z_Z_42 -indexed-file=%s -indexed-at=635:29 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK125 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:method:foo:onEntity:method -new-name=foo:method:z_Z_42:piece:bar -indexed-file=%s -indexed-at=643:35 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK126 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:onEntity:struct:perform -new-name=test:object:z_Z_42:object -indexed-file=%s -indexed-at=641:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK127 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:piece:z_Z_42:object:method -new-name=test:test:withSomething:z_Z_42:name -indexed-file=%s -indexed-at=628:27 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK128 %s
+
++(int) object {
+ [self usingThing: [] () { ([self.undef_property object: ^ {
+ } usingThing: @"string literal"
+ test: "string" usingThing: globalArray[i]
+ foo: /*]*/ globalArray[i]]);
+ // CHECK129: [[@LINE-4]]:54 -> [[@LINE-4]]:60, [[@LINE-3]]:5 -> [[@LINE-3]]:15, [[@LINE-2]]:2 -> [[@LINE-2]]:6, [[@LINE-2]]:17 -> [[@LINE-2]]:27, [[@LINE-1]]:2 -> [[@LINE-1]]:5
+ } < ^ () {
+ [self z_Z_42: name: globalArray[i] test: globalArray[i]
+ perform: globalArray[i]];
+ // CHECK130: [[@LINE-2]]:10 -> [[@LINE-2]]:16, [[@LINE-2]]:19 -> [[@LINE-2]]:23, [[@LINE-2]]:40 -> [[@LINE-2]]:44, [[@LINE-1]]:2 -> [[@LINE-1]]:9
+
+ } perform: a_200: ([] () {
+ [super z_Z_42: "string" z_Z_42: [self perform: [] () {
+
+
+ } withSomething: ^ {
+
+
+ }
+] world: "]" onEntity: globalArray[i] foo: "]"];
+ // CHECK131: [[@LINE-8]]:42 -> [[@LINE-8]]:49, [[@LINE-5]]:4 -> [[@LINE-5]]:17
+ // CHECK132: [[@LINE-9]]:11 -> [[@LINE-9]]:17, [[@LINE-9]]:28 -> [[@LINE-9]]:34, [[@LINE-2]]:3 -> [[@LINE-2]]:8, [[@LINE-2]]:14 -> [[@LINE-2]]:22, [[@LINE-2]]:39 -> [[@LINE-2]]:42
+
+ } * "string") a_200: ']'];
+ // CHECK133: [[@LINE-23]]:9 -> [[@LINE-23]]:19, [[@LINE-13]]:4 -> [[@LINE-13]]:11, [[@LINE-13]]:14 -> [[@LINE-13]]:19, [[@LINE-1]]:16 -> [[@LINE-1]]:21
+ if (12) {
+ int bar = [super piece: 12 == globalArray[i] method: [self class: [] () { return "string";
+ }
+ class: @"string literal" test: ^ () {
+ globalArray[12] = [self class: globalArray[i] z_Z_42: [] () {
+ } method: [super test: [] () {
+ } struct: @"string literal"
+ withSomething: [] {
+
+
+ } == globalArray[i] class: "]" struct: @"string literal"]];
+ // CHECK134: [[@LINE-6]]:20 -> [[@LINE-6]]:24, [[@LINE-5]]:5 -> [[@LINE-5]]:11, [[@LINE-4]]:2 -> [[@LINE-4]]:15, [[@LINE-1]]:22 -> [[@LINE-1]]:27, [[@LINE-1]]:33 -> [[@LINE-1]]:39
+ // CHECK135: [[@LINE-8]]:28 -> [[@LINE-8]]:33, [[@LINE-8]]:50 -> [[@LINE-8]]:56, [[@LINE-7]]:5 -> [[@LINE-7]]:11
+
+ } struct: ']' * 12 + @"string literal"]];
+ // CHECK136: [[@LINE-14]]:66 -> [[@LINE-14]]:71, [[@LINE-12]]:2 -> [[@LINE-12]]:7, [[@LINE-12]]:27 -> [[@LINE-12]]:31, [[@LINE-1]]:4 -> [[@LINE-1]]:10
+ // CHECK137: [[@LINE-15]]:24 -> [[@LINE-15]]:29, [[@LINE-15]]:52 -> [[@LINE-15]]:58
+
+ }
+ [globalObject send: [super struct: ^ {
+ [globalObject message] = [self.undef_property test: "string" onEntity: "]"] other: 42];
+ // CHECK138: [[@LINE-1]]:50 -> [[@LINE-1]]:54, [[@LINE-1]]:65 -> [[@LINE-1]]:73
+
+ } z_Z_42: @"string literal" perform: @"string literal" part: globalArray[i] < 12 struct: [self onEntity: @"string literal" part: "]"]];
+ // CHECK139: [[@LINE-1]]:97 -> [[@LINE-1]]:105, [[@LINE-1]]:125 -> [[@LINE-1]]:129
+ // CHECK140: [[@LINE-6]]:30 -> [[@LINE-6]]:36, [[@LINE-2]]:4 -> [[@LINE-2]]:10, [[@LINE-2]]:30 -> [[@LINE-2]]:37, [[@LINE-2]]:57 -> [[@LINE-2]]:61, [[@LINE-2]]:83 -> [[@LINE-2]]:89
+ if ("]") {
+ [super onEntity: ^ { [self.undef_property withSomething: globalArray[i]
+ method: ^ () {
+ } onEntity: [self test: [_undef_ivar bar: @{ @1, @3 } < globalArray[i] < part: ] * "]" method: (([self struct: "string" piece: [] () {
+ } struct: "string"])) withSomething: [self a_200: "]" foo: ']' < [self a_200: @"string literal" object: ']' onEntity: "]"] part: 12 usingThing: globalArray[i] name: [self.undef_property perform: @"string literal" world: globalArray[i] method: (12) method: [self.undef_property foo: @"string literal" part: [] {
+
+
+ } * "string" withSomething: "string"
+] perform: @"string literal"]] bar: ']'
+]];
+ // CHECK141: [[@LINE-7]]:40 -> [[@LINE-7]]:43, [[@LINE-7]]:77 -> [[@LINE-7]]:81
+ // CHECK142: [[@LINE-8]]:107 -> [[@LINE-8]]:113, [[@LINE-8]]:124 -> [[@LINE-8]]:129, [[@LINE-7]]:5 -> [[@LINE-7]]:11
+ // CHECK143: [[@LINE-8]]:74 -> [[@LINE-8]]:79, [[@LINE-8]]:99 -> [[@LINE-8]]:105, [[@LINE-8]]:111 -> [[@LINE-8]]:119
+ // CHECK144: [[@LINE-9]]:280 -> [[@LINE-9]]:283, [[@LINE-9]]:303 -> [[@LINE-9]]:307, [[@LINE-6]]:15 -> [[@LINE-6]]:28
+ // CHECK145: [[@LINE-10]]:189 -> [[@LINE-10]]:196, [[@LINE-10]]:216 -> [[@LINE-10]]:221, [[@LINE-10]]:238 -> [[@LINE-10]]:244, [[@LINE-10]]:251 -> [[@LINE-10]]:257, [[@LINE-6]]:3 -> [[@LINE-6]]:10
+ // CHECK146: [[@LINE-11]]:46 -> [[@LINE-11]]:51, [[@LINE-11]]:57 -> [[@LINE-11]]:60, [[@LINE-11]]:126 -> [[@LINE-11]]:130, [[@LINE-11]]:135 -> [[@LINE-11]]:145, [[@LINE-11]]:162 -> [[@LINE-11]]:166
+ // CHECK147: [[@LINE-13]]:21 -> [[@LINE-13]]:25, [[@LINE-13]]:91 -> [[@LINE-13]]:97, [[@LINE-12]]:25 -> [[@LINE-12]]:38, [[@LINE-8]]:32 -> [[@LINE-8]]:35
+ // CHECK148: [[@LINE-16]]:53 -> [[@LINE-16]]:66, [[@LINE-15]]:2 -> [[@LINE-15]]:8, [[@LINE-14]]:5 -> [[@LINE-14]]:13
+ } usingThing:
+];
+ // CHECK149: [[@LINE-19]]:14 -> [[@LINE-19]]:22, [[@LINE-2]]:5 -> [[@LINE-2]]:15
+
+ }
+ [self.undef_property test: [] { if (@"string literal") {
+ call() = [self z_Z_42: "string" usingThing: @"string literal"
+];
+ // CHECK150: [[@LINE-2]]:22 -> [[@LINE-2]]:28, [[@LINE-2]]:39 -> [[@LINE-2]]:49
+
+ }
+ } object: globalArray[i]
+ test: ^ () { if ("string" == @"string literal" + ^ () {
+
+
+ }) {
+ int name = 12 * 12;
+
+ }
+ } a_200: "]"];
+ // CHECK151: [[@LINE-15]]:24 -> [[@LINE-15]]:28, [[@LINE-9]]:5 -> [[@LINE-9]]:11, [[@LINE-8]]:2 -> [[@LINE-8]]:6, [[@LINE-1]]:5 -> [[@LINE-1]]:10
+}
+// RUN: clang-refactor-test rename-indexed-file -name=object:usingThing:test:usingThing:foo -new-name=part:piece:object:a_200:name -indexed-file=%s -indexed-at=679:54 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK129 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:name:test:perform -new-name=a_200:usingThing:usingThing:withSomething -indexed-file=%s -indexed-at=685:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK130 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:withSomething -new-name=piece:struct -indexed-file=%s -indexed-at=690:42 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK131 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:z_Z_42:world:onEntity:foo -new-name=withSomething:method:perform:onEntity:bar -indexed-file=%s -indexed-at=690:11 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK132 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:perform:a_200:a_200 -new-name=a_200:world:usingThing:class -indexed-file=%s -indexed-at=679:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK133 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:struct:withSomething:class:struct -new-name=withSomething:part:a_200:method:perform -indexed-file=%s -indexed-at=708:20 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK134 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:z_Z_42:method -new-name=part:a_200:part -indexed-file=%s -indexed-at=707:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK135 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:class:test:struct -new-name=a_200:world:struct:world -indexed-file=%s -indexed-at=704:66 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK136 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:method -new-name=name:class -indexed-file=%s -indexed-at=704:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK137 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:onEntity -new-name=foo:piece -indexed-file=%s -indexed-at=723:50 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK138 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:part -new-name=class:name -indexed-file=%s -indexed-at=726:97 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK139 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:z_Z_42:perform:part:struct -new-name=onEntity:part:usingThing:struct:perform -indexed-file=%s -indexed-at=722:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK140 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:part -new-name=part:class -indexed-file=%s -indexed-at=732:40 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK141 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:piece:struct -new-name=part:onEntity:foo -indexed-file=%s -indexed-at=732:107 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK142 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:object:onEntity -new-name=world:bar:onEntity -indexed-file=%s -indexed-at=733:74 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK143 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:part:withSomething -new-name=usingThing:withSomething:perform -indexed-file=%s -indexed-at=733:280 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK144 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:world:method:method:perform -new-name=piece:bar:usingThing:class:piece -indexed-file=%s -indexed-at=733:189 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK145 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:foo:part:usingThing:name -new-name=z_Z_42:object:name:perform:foo -indexed-file=%s -indexed-at=733:46 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK146 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:method:withSomething:bar -new-name=piece:class:a_200:z_Z_42 -indexed-file=%s -indexed-at=732:21 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK147 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:method:onEntity -new-name=usingThing:foo:object -indexed-file=%s -indexed-at=730:53 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK148 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:usingThing -new-name=perform:piece -indexed-file=%s -indexed-at=730:14 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK149 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:usingThing -new-name=test:usingThing -indexed-file=%s -indexed-at=753:22 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK150 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:object:test:a_200 -new-name=foo:world:piece:z_Z_42 -indexed-file=%s -indexed-at=752:24 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK151 %s
+
++(void) onEntity {
+ int perform = [_undef_ivar onEntity: [] () {
+ /*comment*/([self z_Z_42: ']' + globalArray[i] a_200: method: "]" == "]" < ^ () {
+
+
+ }]);
+ // CHECK152: [[@LINE-4]]:22 -> [[@LINE-4]]:28, [[@LINE-4]]:51 -> [[@LINE-4]]:56, [[@LINE-4]]:59 -> [[@LINE-4]]:65
+
+ } method: "]" < globalArray[i] == @"string literal" struct: "string" == @{ @1, @3 } onEntity: @"string literal"
+ perform: ^ () { globalArray[12] = [self foo: ']' * @"string literal" piece: ^ {
+ } perform: "string" name: "]"];
+ // CHECK153: [[@LINE-2]]:45 -> [[@LINE-2]]:48, [[@LINE-2]]:74 -> [[@LINE-2]]:79, [[@LINE-1]]:5 -> [[@LINE-1]]:12, [[@LINE-1]]:23 -> [[@LINE-1]]:27
+ }];
+ // CHECK154: [[@LINE-12]]:30 -> [[@LINE-12]]:38, [[@LINE-5]]:4 -> [[@LINE-5]]:10, [[@LINE-5]]:54 -> [[@LINE-5]]:60, [[@LINE-5]]:86 -> [[@LINE-5]]:94, [[@LINE-4]]:2 -> [[@LINE-4]]:9
+ return [] {
+ [self part: (@"string literal") world: [self struct: [self a_200: "]" test: ']'] piece: "]" withSomething: @"string literal" struct: "]" object: ] bar: ']'];
+ // CHECK155: [[@LINE-1]]:63 -> [[@LINE-1]]:68, [[@LINE-1]]:74 -> [[@LINE-1]]:78
+ // CHECK156: [[@LINE-2]]:49 -> [[@LINE-2]]:55, [[@LINE-2]]:85 -> [[@LINE-2]]:90, [[@LINE-2]]:96 -> [[@LINE-2]]:109, [[@LINE-2]]:129 -> [[@LINE-2]]:135, [[@LINE-2]]:141 -> [[@LINE-2]]:147
+ // CHECK157: [[@LINE-3]]:10 -> [[@LINE-3]]:14, [[@LINE-3]]:36 -> [[@LINE-3]]:41, [[@LINE-3]]:151 -> [[@LINE-3]]:154
+
+ };
+}
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:a_200:method -new-name=name:world:part -indexed-file=%s -indexed-at=795:22 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK152 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:piece:perform:name -new-name=name:class:withSomething:method -indexed-file=%s -indexed-at=802:45 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK153 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:method:struct:onEntity:perform -new-name=withSomething:piece:bar:struct:piece -indexed-file=%s -indexed-at=794:30 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK154 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:test -new-name=part:z_Z_42 -indexed-file=%s -indexed-at=808:63 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK155 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:piece:withSomething:struct:object -new-name=foo:name:piece:z_Z_42:bar -indexed-file=%s -indexed-at=808:49 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK156 %s
+// RUN: clang-refactor-test rename-indexed-file -name=part:world:bar -new-name=test:a_200:z_Z_42 -indexed-file=%s -indexed-at=808:10 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK157 %s
+
++(const Object &) foo {
+ [self foo: "]" bar: ']' method: ^ () { ] } z_Z_42: [super usingThing: "string" world: [self world: globalArray[i] a_200: [] { [self z_Z_42: ']' usingThing: ("]")];
+ // CHECK158: [[@LINE-1]]:139 -> [[@LINE-1]]:145, [[@LINE-1]]:151 -> [[@LINE-1]]:161
+ } foo: (12)] usingThing: [] {
+ globalArray[12] = [self name: [] {
+
+
+ } foo: @"string literal"/*comment*/ withSomething: globalArray[i] test: [] {
+ }];
+ // CHECK159: [[@LINE-8]]:95 -> [[@LINE-8]]:100, [[@LINE-8]]:117 -> [[@LINE-8]]:122, [[@LINE-6]]:5 -> [[@LINE-6]]:8
+ // CHECK160: [[@LINE-6]]:28 -> [[@LINE-6]]:32, [[@LINE-3]]:4 -> [[@LINE-3]]:7, [[@LINE-3]]:38 -> [[@LINE-3]]:51, [[@LINE-3]]:68 -> [[@LINE-3]]:72
+
+ } foo: ^ { call() = [_undef_ivar world: @"string literal" usingThing: 12 onEntity: @"string literal" struct: ^ {
+ }];
+ // CHECK161: [[@LINE-2]]:39 -> [[@LINE-2]]:44, [[@LINE-2]]:64 -> [[@LINE-2]]:74, [[@LINE-2]]:79 -> [[@LINE-2]]:87, [[@LINE-2]]:107 -> [[@LINE-2]]:113
+ } < "]" test: ^ () { if ([] {
+ }) {
+ call() = [self withSomething: globalArray[i] z_Z_42: globalArray[i]
+ foo: 12 onEntity: @"string literal"];
+ // CHECK162: [[@LINE-2]]:22 -> [[@LINE-2]]:35, [[@LINE-2]]:52 -> [[@LINE-2]]:58, [[@LINE-1]]:2 -> [[@LINE-1]]:5, [[@LINE-1]]:10 -> [[@LINE-1]]:18
+
+ }
+ }]];
+ // CHECK163: [[@LINE-22]]:61 -> [[@LINE-22]]:71, [[@LINE-22]]:82 -> [[@LINE-22]]:87, [[@LINE-20]]:16 -> [[@LINE-20]]:26, [[@LINE-11]]:4 -> [[@LINE-11]]:7, [[@LINE-8]]:11 -> [[@LINE-8]]:15
+ // CHECK164: [[@LINE-23]]:9 -> [[@LINE-23]]:12, [[@LINE-23]]:18 -> [[@LINE-23]]:21, [[@LINE-23]]:27 -> [[@LINE-23]]:33, [[@LINE-23]]:46 -> [[@LINE-23]]:52
+ const Object & struct = ;
+ [globalObject message] = [self object: 12
+ name: 12 a_200: ^ () { if ([self object: ^ {
+ } foo: "string" * @"string literal"]) {
+ [self withSomething: [] {
+ }
+//comment
+ a_200: ^ {
+ } foo: "string" piece: [] () {
+
+
+ }];
+ // CHECK165: [[@LINE-10]]:38 -> [[@LINE-10]]:44, [[@LINE-9]]:5 -> [[@LINE-9]]:8
+ // CHECK166: [[@LINE-9]]:13 -> [[@LINE-9]]:26, [[@LINE-6]]:2 -> [[@LINE-6]]:7, [[@LINE-5]]:5 -> [[@LINE-5]]:8, [[@LINE-5]]:19 -> [[@LINE-5]]:24
+
+ }
+ }];
+ // CHECK167: [[@LINE-16]]:34 -> [[@LINE-16]]:40, [[@LINE-15]]:2 -> [[@LINE-15]]:6, [[@LINE-15]]:11 -> [[@LINE-15]]:16
+ [self world: @"string literal" withSomething: @{ @1, @3 }];
+ // CHECK168: [[@LINE-1]]:9 -> [[@LINE-1]]:14, [[@LINE-1]]:34 -> [[@LINE-1]]:47
+}
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:usingThing -new-name=method:onEntity -indexed-file=%s -indexed-at=823:139 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK158 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:a_200:foo -new-name=class:test:bar -indexed-file=%s -indexed-at=823:95 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK159 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:foo:withSomething:test -new-name=a_200:perform:piece:method -indexed-file=%s -indexed-at=826:28 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK160 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:usingThing:onEntity:struct -new-name=usingThing:name:onEntity:method -indexed-file=%s -indexed-at=834:39 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK161 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:z_Z_42:foo:onEntity -new-name=perform:struct:bar:object -indexed-file=%s -indexed-at=839:22 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK162 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:world:usingThing:foo:test -new-name=method:onEntity:part:part:bar -indexed-file=%s -indexed-at=823:61 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK163 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:bar:method:z_Z_42 -new-name=class:onEntity:method:name -indexed-file=%s -indexed-at=823:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK164 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:foo -new-name=object:usingThing -indexed-file=%s -indexed-at=849:38 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK165 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:a_200:foo:piece -new-name=class:struct:bar:onEntity -indexed-file=%s -indexed-at=851:13 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK166 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:name:a_200 -new-name=piece:withSomething:withSomething -indexed-file=%s -indexed-at=848:34 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK167 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:withSomething -new-name=part:withSomething -indexed-file=%s -indexed-at=865:9 -indexed-symbol-kind=objc-message %s | FileCheck --check-prefix=CHECK168 %s
+
++(void) object {
+ int test = globalArray[i];
+}
diff --git a/test/Refactor/Rename/IndexedObjCMethod.m b/test/Refactor/Rename/IndexedObjCMethod.m
new file mode 100644
index 0000000..f9182c6
--- /dev/null
+++ b/test/Refactor/Rename/IndexedObjCMethod.m
@@ -0,0 +1,128 @@
+@interface Test
+
+- (int)performAction:(int)action with:(int)value; // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:34 -> [[@LINE]]:38
+
+@end
+
+@implementation Test
+
+- (int)performAction:(int)action
+ with:(int)value { // CHECK: rename [[@LINE-1]]:8 -> [[@LINE-1]]:21, [[@LINE]]:8 -> [[@LINE]]:12
+ return action + value;
+}
+
++ (void)foo:(Test*)t {
+ [t performAction: 2 with: 4]; // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:25 -> [[@LINE]]:29
+ SEL s1 = @selector(performAction:
+ with:); // CHECK-NOT: selector [[@LINE-1]]:24 -> [[@LINE-1]]:37, [[@LINE]]:24 -> [[@LINE]]:28
+ SEL s2 = @selector(performAction:); // CHECK-NOT: selector [[@LINE]]
+ SEL s3 = @selector(performAction); // CHECK-NOT: selector [[@LINE]]
+ // Not indexed
+ [t performAction: 1 with: 2]; // CHECK-NOT: rename [[@LINE]]
+}
+
+@end
+
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=performAction:with -new-name=foo:bar -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-im %s | FileCheck %s
+
+@interface SemicolonIsExcluded
+-(void)/*A_propA4_set_decl*/setPropA4X:(int)value;
+@end
+// CHECK2: rename [[@LINE-2]]:29 -> [[@LINE-2]]:39
+
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=setPropA4X: -new-name=foo -indexed-file=%s -indexed-at=29:29 -indexed-symbol-kind=objc-im %s -x objective-c | FileCheck --check-prefix=CHECK2 %s
+
+// It should be possible to have the filename as one of the compilation arguments
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=performAction:with: -new-name=foo:bar: -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-cm %s -c %s -Wall | FileCheck %s
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=performAction:with: -new-name=foo:bar: -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-cm %s %s -fsyntax-only | FileCheck %s
+
+// -gmodules should be stripped to avoid -fmodule-format=obj in CC1 arguments:
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=performAction:with: -new-name=foo:bar: -indexed-file=%s -indexed-at=3:8 -indexed-at=9:8 -indexed-at=objc-message:15:8 -indexed-symbol-kind=objc-cm %s -fmodules -gmodules | FileCheck %s
+
+// These texual matches should be reported as comment or selector occurrences:
+// CHECK3: rename [[@LINE-40]]:8 -> [[@LINE-40]]:21, [[@LINE-40]]:34 -> [[@LINE-40]]:38
+// performAction
+/* performAction: with: 2 performAction */
+/*! performAction+1
+// performAction with
+!*/
+/// Hello performAction with World
+/// \c performAction.
+
+// CHECK3: comment [[@LINE-8]]:4 -> [[@LINE-8]]:17
+// CHECK3-NEXT: comment [[@LINE-8]]:4 -> [[@LINE-8]]:17
+// CHECK3-NEXT: comment [[@LINE-9]]:27 -> [[@LINE-9]]:40
+// CHECK3-NEXT: documentation [[@LINE-9]]:5 -> [[@LINE-9]]:18
+// CHECK3-NEXT: documentation [[@LINE-9]]:4 -> [[@LINE-9]]:17
+// CHECK3-NEXT: documentation [[@LINE-8]]:11 -> [[@LINE-8]]:24
+// CHECK3-NEXT: documentation [[@LINE-8]]:8 -> [[@LINE-8]]:21
+
+// "performAction:with:"
+// 'performAction:'with:
+// CHECK3-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:18
+// CHECK3-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:18
+
+// CHECK3-NEXT: selector [[@LINE+1]]:11 -> [[@LINE+1]]:24, [[@LINE+1]]:25 -> [[@LINE+1]]:29
+@selector(performAction:with:);
+// CHECK3-NEXT: selector [[@LINE+1]]:11 -> [[@LINE+1]]:24, [[@LINE+1]]:28 -> [[@LINE+1]]:32
+@selector(performAction : with );
+// CHECK3-NEXT: selector [[@LINE+2]]:19 -> [[@LINE+2]]:32, [[@LINE+2]]:46 -> [[@LINE+2]]:50
+SEL s = @selector(//comment
+ performAction: /*comment*/ with
+ );
+// CHECK3-NEXT: selector [[@LINE+1]]:33 -> [[@LINE+1]]:46, [[@LINE+2]]:33 -> [[@LINE+2]]:37
+void call = @selector(@selector(performAction:
+ with: ));
+
+// CHECK3-NEXT: comment [[@LINE+1]]:55
+// RUN: clang-refactor-test rename-indexed-file -name=performAction:with: -new-name=foo:bar -indexed-file=%s -indexed-at=objc-cm:3:8 %s | FileCheck --check-prefix=CHECK3 %s
+
+// These ones shouldn't:
+// performAction2 PERFORMACTION performActionWith
+const char *test = "performAction:with:";
+
+@selector(performAction: with ::)
+@selector(performAction:)
+@selector(performAction)
+@selector(performAction with)
+@selector(performAction:without:)
+@selector(performAction:with:somethingElse:)
+@selector(performAction:with "")
+@selector("performAction:with:")
+@selector(with: performAction:)
+selector(performAction:with)
+(performAction:with:)
+
+// CHECK3-NOT: comment
+// CHECK3-NOT: documentation
+// CHECK3-NOT: selector
+
+// It should be possible to find a selector in a file without any indexed occurrences:
+
+// CHECK4: selector [[@LINE+1]]:11
+@selector(nonIndexedSelector)
+// CHECK4-NEXT: comment
+// CHECK4-NOT: selector
+
+// RUN: clang-refactor-test rename-indexed-file -indexed-symbol-kind=objc-im -name=nonIndexedSelector -new-name=test -indexed-file=%s %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-indexed-file -indexed-symbol-kind=objc-cm -name=nonIndexedSelector -new-name=test -indexed-file=%s %s | FileCheck --check-prefix=CHECK4 %s
+
+#define MACRO doStuff
+#define MACRO2(x, y) doStuff:(x) with: (y)
+
+@interface I
+- (void)doStuff:(int)x with: y;
+@end
+
+@implementation I
+- (void)MACRO:(int)x with: y {
+ [self MACRO2(x, y)];
+}
+@end
+
+// CHECK-MACRO: macro [[@LINE-5]]:9 -> [[@LINE-5]]:9
+// CHECK-MACRO-NEXT: macro [[@LINE-5]]:9 -> [[@LINE-5]]:9
+// CHECK-MACRO-NEXT: rename [[@LINE-11]]:9 -> [[@LINE-11]]:16, [[@LINE-11]]:24 -> [[@LINE-11]]:28
+// CHECK-MACRO-NOT: macro
+
+// RUN: clang-refactor-test rename-indexed-file -indexed-symbol-kind=objc-im -name=doStuff:with: -new-name=foo:bar -indexed-file=%s -indexed-at=114:9 -indexed-at=118:9 -indexed-at=119:9 %s | FileCheck --check-prefix=CHECK-MACRO %s
diff --git a/test/Refactor/Rename/IndexedObjCMethodDecl.mm b/test/Refactor/Rename/IndexedObjCMethodDecl.mm
new file mode 100644
index 0000000..23e41ac
--- /dev/null
+++ b/test/Refactor/Rename/IndexedObjCMethodDecl.mm
@@ -0,0 +1,748 @@
+@implementation foo
++(some_type_t)a_200:(void)a_200 name:(int[1 + 2 - 3])z_Z_42 usingThing:(some_type_t)world method:(void)test
+class:(int[1 + 2 - 3])method __attribute__((eval { int x = 0 + 1; })) method:(({}))method {
+ const Object & piece = 12;
+}
+// CHECK1: [[@LINE-4]]:15 -> [[@LINE-4]]:20, [[@LINE-4]]:33 -> [[@LINE-4]]:37, [[@LINE-4]]:61 -> [[@LINE-4]]:71, [[@LINE-4]]:91 -> [[@LINE-4]]:97, [[@LINE-3]]:1 -> [[@LINE-3]]:6, [[@LINE-3]]:71 -> [[@LINE-3]]:77
++(some_type_t)world:(BOOL)withSomething class:(const Object &)name struct:(some_type_t)method __attribute__((test()))a_200:(int)name
+a_200:(({}))perform withSomething:(Object * (^)(BOOL, Object *))onEntity
+{
+ const Object & class = globalArray[i];
+}
+// CHECK2: [[@LINE-5]]:15 -> [[@LINE-5]]:20, [[@LINE-5]]:41 -> [[@LINE-5]]:46, [[@LINE-5]]:68 -> [[@LINE-5]]:74, [[@LINE-5]]:118 -> [[@LINE-5]]:123, [[@LINE-4]]:1 -> [[@LINE-4]]:6, [[@LINE-4]]:21 -> [[@LINE-4]]:34
+-(some_type_t)struct:(int[1 + 2 - 3])bar
+class:(int[1 + 2 - 3])foo
+part:(void)piece
+{
+ int onEntity = "]";
+}
+// CHECK3: [[@LINE-6]]:15 -> [[@LINE-6]]:21, [[@LINE-5]]:1 -> [[@LINE-5]]:6, [[@LINE-4]]:1 -> [[@LINE-4]]:5
+-(some_type_t)test:(^ { })a_200 world:(BOOL)onEntity a_200:(BOOL)perform
+withSomething:(({}))method
+{
+ [self part: [self z_Z_42: "]" world: ("]")] world: globalArray[i] class: "string" test: globalArray[i]];
+}
+// CHECK4: [[@LINE-5]]:15 -> [[@LINE-5]]:19, [[@LINE-5]]:33 -> [[@LINE-5]]:38, [[@LINE-5]]:54 -> [[@LINE-5]]:59, [[@LINE-4]]:1 -> [[@LINE-4]]:14
+-(int)a_200:(BOOL)bar piece:(Object *)perform class:(^ { })onEntity {
+ [globalObject send: [self perform: ']' object: [] () { ([self name: ']' a_200: ']']) other: 42];
+ } foo: ']'];
+}
+// CHECK5: [[@LINE-4]]:7 -> [[@LINE-4]]:12, [[@LINE-4]]:23 -> [[@LINE-4]]:28, [[@LINE-4]]:47 -> [[@LINE-4]]:52
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:name:usingThing:method:class:method -new-name=withSomething:struct:class:onEntity:withSomething:test -indexed-file=%s -indexed-at=2:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:class:struct:a_200:a_200:withSomething -new-name=onEntity:a_200:perform:onEntity:usingThing:onEntity -indexed-file=%s -indexed-at=7:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:class:part -new-name=z_Z_42:name:usingThing -indexed-file=%s -indexed-at=13:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:world:a_200:withSomething -new-name=part:withSomething:test:struct -indexed-file=%s -indexed-at=20:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:piece:class -new-name=a_200:a_200:piece -indexed-file=%s -indexed-at=26:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK5 %s
+
+@interface method
++(some_type_t)bar:(BOOL)bar bar:(Object *)usingThing struct:(int[1 + 2 - 3])usingThing
+perform:(BOOL)onEntity ;
+// CHECK6: [[@LINE-2]]:15 -> [[@LINE-2]]:18, [[@LINE-2]]:29 -> [[@LINE-2]]:32, [[@LINE-2]]:54 -> [[@LINE-2]]:60, [[@LINE-1]]:1 -> [[@LINE-1]]:8
++(void)struct:(^ { })part __attribute__((test()))foo:(^ { })part part:(void)class __attribute__((eval { int x = 0 + 1; })) onEntity:(Object *)method foo:(const Object &)class /*comment*/ ;
+// CHECK7: [[@LINE-1]]:8 -> [[@LINE-1]]:14, [[@LINE-1]]:50 -> [[@LINE-1]]:53, [[@LINE-1]]:66 -> [[@LINE-1]]:70, [[@LINE-1]]:124 -> [[@LINE-1]]:132, [[@LINE-1]]:150 -> [[@LINE-1]]:153
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=bar:bar:struct:perform -new-name=bar:withSomething:test:foo -indexed-file=%s -indexed-at=39:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:foo:part:onEntity:foo -new-name=z_Z_42:part:world:piece:perform -indexed-file=%s -indexed-at=42:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK7 %s
+
+@interface foo
++(BOOL)class:(Object *)method struct:(void)method
+perform:(Object *)class
+;
+// CHECK8: [[@LINE-3]]:8 -> [[@LINE-3]]:13, [[@LINE-3]]:31 -> [[@LINE-3]]:37, [[@LINE-2]]:1 -> [[@LINE-2]]:8
++(some_type_t)usingThing:(({}))struct withSomething:(const Object &)z_Z_42 method:(some_type_t)class
+a_200:(int)a_200 ;
+// CHECK9: [[@LINE-2]]:15 -> [[@LINE-2]]:25, [[@LINE-2]]:39 -> [[@LINE-2]]:52, [[@LINE-2]]:76 -> [[@LINE-2]]:82, [[@LINE-1]]:1 -> [[@LINE-1]]:6
++(Object *)a_200:(const Object &)class //comment
+class:(BOOL)bar usingThing:(void (*)(some_type_t, some_type_t))name
+bar:(BOOL)onEntity method:(void)method
+part:(BOOL)usingThing /*comment*/ ;
+// CHECK10: [[@LINE-4]]:12 -> [[@LINE-4]]:17, [[@LINE-3]]:1 -> [[@LINE-3]]:6, [[@LINE-3]]:17 -> [[@LINE-3]]:27, [[@LINE-2]]:1 -> [[@LINE-2]]:4, [[@LINE-2]]:20 -> [[@LINE-2]]:26, [[@LINE-1]]:1 -> [[@LINE-1]]:5
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=class:struct:perform -new-name=withSomething:foo:a_200 -indexed-file=%s -indexed-at=49:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:withSomething:method:a_200 -new-name=piece:bar:test:perform -indexed-file=%s -indexed-at=53:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:class:usingThing:bar:method:part -new-name=a_200:piece:class:z_Z_42:piece:world -indexed-file=%s -indexed-at=56:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK10 %s
+
+@implementation onEntity <object, onEntity, world>
+-(const Object &)test:(some_type_t (*)(some_type_t))z_Z_42 //comment
+perform:(Object *)name /*comment*/ method:(int)bar __attribute__((eval { int x = 0 + 1; })) part:(Object *)name z_Z_42:(BOOL)a_200 test:(^ { })usingThing __attribute__((test())){
+ call() = [self test: "]" z_Z_42: ^ { [_undef_ivar foo: "string" piece: @{ @1, @3 } perform: @"string literal" world: ']'];
+ } == "]" a_200: 12/*comment*/ object: 12 test: 12];
+}
+// CHECK11: [[@LINE-5]]:18 -> [[@LINE-5]]:22, [[@LINE-4]]:1 -> [[@LINE-4]]:8, [[@LINE-4]]:36 -> [[@LINE-4]]:42, [[@LINE-4]]:93 -> [[@LINE-4]]:97, [[@LINE-4]]:113 -> [[@LINE-4]]:119, [[@LINE-4]]:132 -> [[@LINE-4]]:136
+-(const Object &)z_Z_42:(({}))usingThing onEntity:(^ { })world
+a_200:(const Object &)withSomething __attribute__((test()))onEntity:(({}))onEntity {
+ [_undef_ivar object: globalArray[i] perform: ^ { globalArray[12] = [_undef_ivar class: "]" onEntity: [] {
+ } bar: "string" < ^ {
+ } foo: "string" piece: @{ @1, @3 }];
+ } test: /*]*/ method: globalArray[i]
+];
+}
+// CHECK12: [[@LINE-8]]:18 -> [[@LINE-8]]:24, [[@LINE-8]]:42 -> [[@LINE-8]]:50, [[@LINE-7]]:1 -> [[@LINE-7]]:6, [[@LINE-7]]:60 -> [[@LINE-7]]:68
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=test:perform:method:part:z_Z_42:test -new-name=a_200:piece:onEntity:withSomething:world:test -indexed-file=%s -indexed-at=67:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK11 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:onEntity:a_200:onEntity -new-name=part:world:z_Z_42:onEntity -indexed-file=%s -indexed-at=73:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK12 %s
+
+@implementation method
+-(BOOL)usingThing:(void)part part:(void)world
+class:(int)part perform:(some_type_t)withSomething
+z_Z_42:(const Object &)class //comment
+usingThing:(({}))part {
+ some_type_t foo = [self.undef_property onEntity: "string" withSomething: ']'];
+}
+// CHECK13: [[@LINE-6]]:8 -> [[@LINE-6]]:18, [[@LINE-6]]:30 -> [[@LINE-6]]:34, [[@LINE-5]]:1 -> [[@LINE-5]]:6, [[@LINE-5]]:17 -> [[@LINE-5]]:24, [[@LINE-4]]:1 -> [[@LINE-4]]:7, [[@LINE-3]]:1 -> [[@LINE-3]]:11
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:part:class:perform:z_Z_42:usingThing -new-name=class:method:withSomething:world:name:name -indexed-file=%s -indexed-at=87:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK13 %s
+
+@interface withSomething
+-(Object *)object:(^ { })onEntity __attribute__((test()))piece:(^ { })z_Z_42 /*comment*/ ;
+// CHECK14: [[@LINE-1]]:12 -> [[@LINE-1]]:18, [[@LINE-1]]:58 -> [[@LINE-1]]:63
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=object:piece -new-name=foo:bar -indexed-file=%s -indexed-at=98:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK14 %s
+
+@interface usingThing <piece, withSomething, perform>
++(void)part:(some_type_t)onEntity
+a_200:(int)bar perform:(some_type_t)struct
+;
+// CHECK15: [[@LINE-3]]:8 -> [[@LINE-3]]:12, [[@LINE-2]]:1 -> [[@LINE-2]]:6, [[@LINE-2]]:16 -> [[@LINE-2]]:23
+-(some_type_t)piece:(^ { })foo name:(({}))perform part:(^ { })name z_Z_42:(Object *)test
+;
+// CHECK16: [[@LINE-2]]:15 -> [[@LINE-2]]:20, [[@LINE-2]]:32 -> [[@LINE-2]]:36, [[@LINE-2]]:51 -> [[@LINE-2]]:55, [[@LINE-2]]:68 -> [[@LINE-2]]:74
++(Object *)method:(const Object &)method onEntity:(^ { })withSomething
+withSomething:(int (*)(const Object &, BOOL))piece ;
+// CHECK17: [[@LINE-2]]:12 -> [[@LINE-2]]:18, [[@LINE-2]]:42 -> [[@LINE-2]]:50, [[@LINE-1]]:1 -> [[@LINE-1]]:14
+-(BOOL)foo:(int[1 + 2 - 3])world
+foo:(int[1 + 2 - 3])struct method:(void (*)())piece foo:(BOOL)test __attribute__((eval { int x = 0 + 1; })) ;
+// CHECK18: [[@LINE-2]]:8 -> [[@LINE-2]]:11, [[@LINE-1]]:1 -> [[@LINE-1]]:4, [[@LINE-1]]:28 -> [[@LINE-1]]:34, [[@LINE-1]]:53 -> [[@LINE-1]]:56
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=part:a_200:perform -new-name=part:test:part -indexed-file=%s -indexed-at=104:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK15 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:name:part:z_Z_42 -new-name=usingThing:struct:onEntity:piece -indexed-file=%s -indexed-at=108:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK16 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:onEntity:withSomething -new-name=z_Z_42:method:usingThing -indexed-file=%s -indexed-at=111:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK17 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:foo:method:foo -new-name=piece:usingThing:class:foo -indexed-file=%s -indexed-at=114:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK18 %s
+
+@interface foo
+-(void)foo:(({}))part method:(({}))method ;
+// CHECK19: [[@LINE-1]]:8 -> [[@LINE-1]]:11, [[@LINE-1]]:23 -> [[@LINE-1]]:29
+-(const Object &)foo:(({}))usingThing test:(Object *)struct __attribute__((test()))name:(const Object &)z_Z_42 /*comment*/ name:(^ { })onEntity a_200:(Object * (^)())usingThing
+usingThing:(Object *)method /*comment*/ ;
+// CHECK20: [[@LINE-2]]:18 -> [[@LINE-2]]:21, [[@LINE-2]]:39 -> [[@LINE-2]]:43, [[@LINE-2]]:84 -> [[@LINE-2]]:88, [[@LINE-2]]:124 -> [[@LINE-2]]:128, [[@LINE-2]]:145 -> [[@LINE-2]]:150, [[@LINE-1]]:1 -> [[@LINE-1]]:11
++(BOOL)name:(Object *)a_200 part:(int)bar
+perform:(some_type_t)method ;
+// CHECK21: [[@LINE-2]]:8 -> [[@LINE-2]]:12, [[@LINE-2]]:29 -> [[@LINE-2]]:33, [[@LINE-1]]:1 -> [[@LINE-1]]:8
+-(some_type_t)usingThing:(void)test //comment
+foo:(void)world z_Z_42:(Object *)bar ;
+// CHECK22: [[@LINE-2]]:15 -> [[@LINE-2]]:25, [[@LINE-1]]:1 -> [[@LINE-1]]:4, [[@LINE-1]]:17 -> [[@LINE-1]]:23
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=foo:method -new-name=part:world -indexed-file=%s -indexed-at=124:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK19 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:test:name:name:a_200:usingThing -new-name=method:withSomething:struct:z_Z_42:bar:z_Z_42 -indexed-file=%s -indexed-at=126:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK20 %s
+// RUN: clang-refactor-test rename-indexed-file -name=name:part:perform -new-name=withSomething:part:object -indexed-file=%s -indexed-at=129:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK21 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:foo:z_Z_42 -new-name=perform:test:class -indexed-file=%s -indexed-at=132:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK22 %s
+
+@implementation struct
++(void)bar:(void)piece a_200:(some_type_t)class {
+ [self name: [] () {
+ [globalObject send: [super name: [] () {
+
+
+ } perform: globalArray[i]] other: 42];
+
+ } usingThing: @"string literal"];
+
+ call() = [self usingThing: @"string literal" perform: "string"];
+}
+// CHECK23: [[@LINE-11]]:8 -> [[@LINE-11]]:11, [[@LINE-11]]:24 -> [[@LINE-11]]:29
+-(void)perform:(BOOL)test
+withSomething:(const Object &)foo {
+ return [_undef_ivar piece: globalArray[i] + globalArray[i] object: globalArray[i]
+ a_200: [self withSomething: [] () { [self.undef_property usingThing: ']' struct: "]"
+ perform: "string"];
+ }
+ piece: ']' name: ']' a_200: "string" == ^ () {
+ globalArray[12] = [_undef_ivar foo: ']' foo: [] {
+ }
+ bar: ^ {
+ } + [] {
+
+
+ }
+];
+
+ }] z_Z_42: ^ () {
+ if (^ () {
+ }) {
+ return @"string literal" < globalArray[i] == [self world: [] {
+
+
+ } object: @"string literal" part: ']'];
+
+ }
+
+ } a_200: "string"];
+}
+// CHECK24: [[@LINE-28]]:8 -> [[@LINE-28]]:15, [[@LINE-27]]:1 -> [[@LINE-27]]:14
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=bar:a_200 -new-name=bar:name -indexed-file=%s -indexed-at=142:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK23 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:withSomething -new-name=withSomething:test -indexed-file=%s -indexed-at=154:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK24 %s
+
+@implementation object
+-(int)a_200:(Object *)perform usingThing:(int)bar //comment
+{
+ [self bar: ']' withSomething: "string"];
+
+ Object * part = ']';
+}
+// CHECK25: [[@LINE-6]]:7 -> [[@LINE-6]]:12, [[@LINE-6]]:31 -> [[@LINE-6]]:41
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=a_200:usingThing -new-name=object:bar -indexed-file=%s -indexed-at=188:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK25 %s
+
+@implementation test
++(void)method:(const Object &)z_Z_42 //comment
+piece:(some_type_t (*)(Object *))withSomething __attribute__((eval { int x = 0 + 1; })) perform:(void)piece method:(BOOL)object {
+ [self piece: [] () {
+ ; ;[self part: 12 a_200: @"string literal"];
+
+ } struct: globalArray[i] part: "]"];
+
+ ;
+}
+// CHECK26: [[@LINE-9]]:8 -> [[@LINE-9]]:14, [[@LINE-8]]:1 -> [[@LINE-8]]:6, [[@LINE-8]]:89 -> [[@LINE-8]]:96, [[@LINE-8]]:109 -> [[@LINE-8]]:115
+-(some_type_t)world:(some_type_t)world
+a_200:(void)bar //comment
+{
+ int piece = ;
+
+ if ("string") {
+ // comment
+
+ }
+}
+// CHECK27: [[@LINE-10]]:15 -> [[@LINE-10]]:20, [[@LINE-9]]:1 -> [[@LINE-9]]:6
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=method:piece:perform:method -new-name=perform:test:bar:object -indexed-file=%s -indexed-at=199:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK26 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:a_200 -new-name=z_Z_42:method -indexed-file=%s -indexed-at=209:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK27 %s
+
+@implementation perform
+-(Object *)class:(Object * (*)(BOOL))onEntity perform:(({}))class
+usingThing:(int (*)())object __attribute__((eval { int x = 0 + 1; })) piece:(BOOL (^)(BOOL, BOOL))class bar:(int)name withSomething:(const Object &)onEntity __attribute__((test())){
+ // comment
+}
+// CHECK28: [[@LINE-4]]:12 -> [[@LINE-4]]:17, [[@LINE-4]]:47 -> [[@LINE-4]]:54, [[@LINE-3]]:1 -> [[@LINE-3]]:11, [[@LINE-3]]:71 -> [[@LINE-3]]:76, [[@LINE-3]]:105 -> [[@LINE-3]]:108, [[@LINE-3]]:119 -> [[@LINE-3]]:132
+-(int)test:(({}))onEntity
+a_200:(BOOL)struct {
+ ([self name: ']' part: [] {
+ ; ;([_undef_ivar usingThing: globalArray[i] class: globalArray[i] method: globalArray[i] method: ']' class: 12]);
+
+ } bar: 12 withSomething: "]" == [] { [self test: [self world: [_undef_ivar struct: "]" bar: @"string literal" method: globalArray[i]] usingThing: @"string literal" class: [] () {
+ } bar: @"string literal"
+] withSomething: ([] {
+
+
+ } + "]" * ']') withSomething: @"string literal"];
+ } usingThing: "]"]);
+
+ call() = [self test: globalArray[i] name: @"string literal" perform: [self class: "]"
+ piece: globalArray[i] method: "string"
+]
+//comment
+ piece: "string"];
+}
+// CHECK29: [[@LINE-19]]:7 -> [[@LINE-19]]:11, [[@LINE-18]]:1 -> [[@LINE-18]]:6
+-(Object *)test:(^ { })z_Z_42 class:(void)usingThing
+{
+ if ([self.undef_property class: globalArray[i] a_200: ^ () { ] }
+]) {
+ if (globalArray[i]) {
+ some_type_t foo = [self foo: "]" perform: "string"
+];
+
+ }
+
+ }
+}
+// CHECK30: [[@LINE-12]]:12 -> [[@LINE-12]]:16, [[@LINE-12]]:31 -> [[@LINE-12]]:36
++(int)struct:(void (^)())method
+a_200:(BOOL)onEntity usingThing:(Object *)a_200 bar:(void)z_Z_42 z_Z_42:(Object *)struct perform:(BOOL (*)(Object *, Object *))method {
+ globalArray[12] = [self bar: ']' test: ^ () { ] } method: globalArray[i]];
+
+ if ([super test: "string" < globalArray[i] part: 12]) {
+ int bar = [self object: "]"
+ perform: [self a_200: "string" object: ("string")
+//comment
+]];
+
+ }
+}
+// CHECK31: [[@LINE-12]]:7 -> [[@LINE-12]]:13, [[@LINE-11]]:1 -> [[@LINE-11]]:6, [[@LINE-11]]:22 -> [[@LINE-11]]:32, [[@LINE-11]]:49 -> [[@LINE-11]]:52, [[@LINE-11]]:66 -> [[@LINE-11]]:72, [[@LINE-11]]:90 -> [[@LINE-11]]:97
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=class:perform:usingThing:piece:bar:withSomething -new-name=a_200:piece:foo:struct:onEntity:world -indexed-file=%s -indexed-at=225:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK28 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:a_200 -new-name=struct:world -indexed-file=%s -indexed-at=230:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK29 %s
+// RUN: clang-refactor-test rename-indexed-file -name=test:class -new-name=perform:name -indexed-file=%s -indexed-at=250:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK30 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:a_200:usingThing:bar:z_Z_42:perform -new-name=world:piece:test:world:class:struct -indexed-file=%s -indexed-at=263:7 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK31 %s
+
+@implementation usingThing <object>
+-(int)foo:(int)a_200 part:(Object *)name
+object:(BOOL)world __attribute__((test()))method:(const Object & (*)())test name:(some_type_t)z_Z_42 bar:(Object *)class /*comment*/ {
+ [self piece: ^ {
+ [self.undef_property name: [self z_Z_42: [self object: @"string literal" + [] {
+ }
+ z_Z_42: [] () {
+
+
+ }] class: [self method: globalArray[i] usingThing: globalArray[i] part: 12] onEntity: @{ @1, @3 } piece: "string"] object: ([self foo: @"string literal" < [self object: ']' usingThing: usingThing: globalArray[i] a_200: [] () {
+ }]
+ name: "string" foo: [] () {
+
+
+ }]) struct: "string" struct: "]" + [] {
+ } usingThing: ];
+
+ } usingThing: (@"string literal") usingThing: 12 a_200: ^ { [globalObject message] = [self onEntity: [] {
+ } == ^ {
+
+
+ } foo: @{ @1, @3 }];
+ } withSomething: "]" == ];
+}
+// CHECK32: [[@LINE-23]]:7 -> [[@LINE-23]]:10, [[@LINE-23]]:22 -> [[@LINE-23]]:26, [[@LINE-22]]:1 -> [[@LINE-22]]:7, [[@LINE-22]]:43 -> [[@LINE-22]]:49, [[@LINE-22]]:77 -> [[@LINE-22]]:81, [[@LINE-22]]:102 -> [[@LINE-22]]:105
+-(Object *)withSomething:(void)class __attribute__((test()))onEntity:(void)part struct:(some_type_t (*)(Object *))bar __attribute__((eval { int x = 0 + 1; })) perform:(Object *)world {
+ globalArray[12] = [self struct: globalArray[i] bar: ^ { if (']' + "string") {
+ return ^ () {
+ };
+
+ }
+ } class: ^ () { some_type_t foo = [self.undef_property withSomething: ']' bar: "]"];
+ } usingThing: ']'
+];
+}
+// CHECK33: [[@LINE-10]]:12 -> [[@LINE-10]]:25, [[@LINE-10]]:61 -> [[@LINE-10]]:69, [[@LINE-10]]:81 -> [[@LINE-10]]:87, [[@LINE-10]]:160 -> [[@LINE-10]]:167
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=foo:part:object:method:name:bar -new-name=z_Z_42:z_Z_42:part:part:usingThing:z_Z_42 -indexed-file=%s -indexed-at=283:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK32 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:onEntity:struct:perform -new-name=withSomething:name:foo:name -indexed-file=%s -indexed-at=307:12 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK33 %s
+
+@interface a_200 <foo>
++(void)bar:(int[1 + 2 - 3])a_200 object:(Object *)z_Z_42 world:(int[1 + 2 - 3])name name:(const Object &)name world:(Object *)world /*comment*/ ;
+// CHECK34: [[@LINE-1]]:8 -> [[@LINE-1]]:11, [[@LINE-1]]:34 -> [[@LINE-1]]:40, [[@LINE-1]]:58 -> [[@LINE-1]]:63, [[@LINE-1]]:85 -> [[@LINE-1]]:89, [[@LINE-1]]:111 -> [[@LINE-1]]:116
+-(int)foo:(void (^)())part usingThing:(int)usingThing object:(({}))withSomething class:(some_type_t (^)())perform /*comment*/ method:(int)foo ;
+// CHECK35: [[@LINE-1]]:7 -> [[@LINE-1]]:10, [[@LINE-1]]:28 -> [[@LINE-1]]:38, [[@LINE-1]]:55 -> [[@LINE-1]]:61, [[@LINE-1]]:82 -> [[@LINE-1]]:87, [[@LINE-1]]:127 -> [[@LINE-1]]:133
+-(void)z_Z_42:(({}))withSomething name:(void (*)())struct foo:(Object *)part ;
+// CHECK36: [[@LINE-1]]:8 -> [[@LINE-1]]:14, [[@LINE-1]]:35 -> [[@LINE-1]]:39, [[@LINE-1]]:59 -> [[@LINE-1]]:62
+-(BOOL)onEntity:(const Object &)z_Z_42
+piece:(const Object &)world
+onEntity:(BOOL)part ;
+// CHECK37: [[@LINE-3]]:8 -> [[@LINE-3]]:16, [[@LINE-2]]:1 -> [[@LINE-2]]:6, [[@LINE-1]]:1 -> [[@LINE-1]]:9
+-(BOOL)method:(some_type_t)name
+part:(BOOL)a_200 part:(void)test __attribute__((eval { int x = 0 + 1; })) ;
+// CHECK38: [[@LINE-2]]:8 -> [[@LINE-2]]:14, [[@LINE-1]]:1 -> [[@LINE-1]]:5, [[@LINE-1]]:18 -> [[@LINE-1]]:22
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=bar:object:world:name:world -new-name=withSomething:part:struct:part:withSomething -indexed-file=%s -indexed-at=323:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK34 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:usingThing:object:class:method -new-name=part:onEntity:foo:test:struct -indexed-file=%s -indexed-at=325:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK35 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:name:foo -new-name=piece:z_Z_42:world -indexed-file=%s -indexed-at=327:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK36 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:piece:onEntity -new-name=part:usingThing:foo -indexed-file=%s -indexed-at=329:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK37 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:part:part -new-name=perform:test:part -indexed-file=%s -indexed-at=333:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK38 %s
+
+@interface part <world, struct, part>
++(some_type_t)z_Z_42:(Object *)onEntity test:(int (*)())a_200 __attribute__((test()));
+// CHECK39: [[@LINE-1]]:15 -> [[@LINE-1]]:21, [[@LINE-1]]:41 -> [[@LINE-1]]:45
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:test -new-name=name:onEntity -indexed-file=%s -indexed-at=344:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK39 %s
+
+@interface piece
+-(some_type_t)part:(some_type_t)onEntity test:(Object *)world
+bar:(BOOL)foo
+method:(const Object & (^)(int, some_type_t))object
+withSomething:(BOOL)part
+;
+// CHECK40: [[@LINE-5]]:15 -> [[@LINE-5]]:19, [[@LINE-5]]:42 -> [[@LINE-5]]:46, [[@LINE-4]]:1 -> [[@LINE-4]]:4, [[@LINE-3]]:1 -> [[@LINE-3]]:7, [[@LINE-2]]:1 -> [[@LINE-2]]:14
+-(some_type_t)onEntity:(void)object test:(int[1 + 2 - 3])object ;
+// CHECK41: [[@LINE-1]]:15 -> [[@LINE-1]]:23, [[@LINE-1]]:37 -> [[@LINE-1]]:41
+-(some_type_t)struct:(void)name class:(some_type_t)foo name:(int (^)(BOOL, int))method
+foo:(void)usingThing usingThing:(int)z_Z_42 /*comment*/ a_200:(int[1 + 2 - 3])object
+;
+// CHECK42: [[@LINE-3]]:15 -> [[@LINE-3]]:21, [[@LINE-3]]:33 -> [[@LINE-3]]:38, [[@LINE-3]]:56 -> [[@LINE-3]]:60, [[@LINE-2]]:1 -> [[@LINE-2]]:4, [[@LINE-2]]:22 -> [[@LINE-2]]:32, [[@LINE-2]]:57 -> [[@LINE-2]]:62
+-(BOOL)bar:(some_type_t (*)(int))part /*comment*/ object:(int[1 + 2 - 3])onEntity perform:(BOOL)usingThing /*comment*/ ;
+// CHECK43: [[@LINE-1]]:8 -> [[@LINE-1]]:11, [[@LINE-1]]:51 -> [[@LINE-1]]:57, [[@LINE-1]]:83 -> [[@LINE-1]]:90
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=part:test:bar:method:withSomething -new-name=name:usingThing:perform:onEntity:bar -indexed-file=%s -indexed-at=350:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK40 %s
+// RUN: clang-refactor-test rename-indexed-file -name=onEntity:test -new-name=onEntity:class -indexed-file=%s -indexed-at=356:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK41 %s
+// RUN: clang-refactor-test rename-indexed-file -name=struct:class:name:foo:usingThing:a_200 -new-name=class:a_200:class:object:class:name -indexed-file=%s -indexed-at=358:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK42 %s
+// RUN: clang-refactor-test rename-indexed-file -name=bar:object:perform -new-name=a_200:test:object -indexed-file=%s -indexed-at=362:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK43 %s
+
+@implementation piece
++(BOOL)name:(int (^)(BOOL, const Object &))onEntity /*comment*/ withSomething:(void)foo name:(const Object &)withSomething a_200:(Object *)struct foo:(^ { })object {
+ if (12) {
+ return 12;
+
+ }
+}
+// CHECK44: [[@LINE-6]]:8 -> [[@LINE-6]]:12, [[@LINE-6]]:65 -> [[@LINE-6]]:78, [[@LINE-6]]:89 -> [[@LINE-6]]:93, [[@LINE-6]]:124 -> [[@LINE-6]]:129, [[@LINE-6]]:147 -> [[@LINE-6]]:150
++(int)class:(some_type_t (*)(BOOL))name
+name:(BOOL)onEntity __attribute__((test()))a_200:(^ { })withSomething __attribute__((eval { int x = 0 + 1; })) piece:(int)withSomething usingThing:(Object *)name {
+ ; ;[super method: part: ^ () {
+ // comment
+
+ }];
+
+ [self struct: "string" == "]" withSomething: ']' class: [] () {
+ [globalObject send: [self.undef_property part: ^ () {
+
+
+ } class: (']' < )
+ z_Z_42: ^ () {
+
+
+ } test: (12)] other: 42];
+
+ } object: 12 perform: ']'];
+}
+// CHECK45: [[@LINE-19]]:7 -> [[@LINE-19]]:12, [[@LINE-18]]:1 -> [[@LINE-18]]:5, [[@LINE-18]]:44 -> [[@LINE-18]]:49, [[@LINE-18]]:112 -> [[@LINE-18]]:117, [[@LINE-18]]:137 -> [[@LINE-18]]:147
++(some_type_t)perform:(int[1 + 2 - 3])world //comment
+withSomething:(const Object &)foo part:(int (^)(int))foo part:(int[1 + 2 - 3])perform //comment
+{
+ [globalObject send: [self object: ']' withSomething: 12 usingThing: ']' perform: @"string literal"] other: 42];
+}
+// CHECK46: [[@LINE-5]]:15 -> [[@LINE-5]]:22, [[@LINE-4]]:1 -> [[@LINE-4]]:14, [[@LINE-4]]:35 -> [[@LINE-4]]:39, [[@LINE-4]]:58 -> [[@LINE-4]]:62
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=name:withSomething:name:a_200:foo -new-name=object:onEntity:world:world:piece -indexed-file=%s -indexed-at=371:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK44 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:name:a_200:piece:usingThing -new-name=method:test:z_Z_42:part:foo -indexed-file=%s -indexed-at=378:7 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK45 %s
+// RUN: clang-refactor-test rename-indexed-file -name=perform:withSomething:part:part -new-name=a_200:object:perform:z_Z_42 -indexed-file=%s -indexed-at=398:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK46 %s
+
+@implementation z_Z_42 <struct>
++(BOOL)piece:(some_type_t (^)(int))class a_200:(BOOL (^)(some_type_t, BOOL))bar
+world:(const Object & (*)(const Object &, const Object &))struct piece:(^ { })test z_Z_42:(BOOL)name
+usingThing:(void (^)(Object *))struct {
+ [self object: ([self.undef_property struct: ^ {
+ /*comment*/[self.undef_property part: @"string literal" world: @"string literal"
+ object: "]" world: "string" world: @{ @1, @3 }];
+
+ } == ^ () { [super class: ^ () {
+
+
+ } * globalArray[i] piece: [] {
+ } bar: [self part: @"string literal" perform: "]" world: [] () {
+ }
+ world: [self.undef_property world: globalArray[i] perform: "string" struct: "]"]]];
+ }
+ name: 12 usingThing: "]" bar: "]"
+//comment
+]) withSomething: ^ () {
+ if ("string") {
+ [self withSomething: [] {
+
+
+ } method: 12 piece: [] () {
+
+
+ } a_200: ']' method: "]"];
+
+ }
+
+ } test: @{ @1, @3 }
+ class: 12 world: "string"];
+}
+// CHECK47: [[@LINE-32]]:8 -> [[@LINE-32]]:13, [[@LINE-32]]:42 -> [[@LINE-32]]:47, [[@LINE-31]]:1 -> [[@LINE-31]]:6, [[@LINE-31]]:66 -> [[@LINE-31]]:71, [[@LINE-31]]:84 -> [[@LINE-31]]:90, [[@LINE-30]]:1 -> [[@LINE-30]]:11
+-(int)method:(Object *)bar //comment
+part:(int)bar usingThing:(({}))name {
+ // comment
+
+ [_undef_ivar world: "]" onEntity: [] {
+ ; ;[self part: globalArray[i] world: @{ @1, @3 }];
+
+ } bar: "string" == [super name: 12 piece: /*]*/ @"string literal" part: "string" method: ^ {
+ some_type_t object = "string";
+
+ } piece: (^ {
+ return /*]*/ "]";
+
+ })] onEntity: [] {
+ int bar = [self name: "string"/*comment*/ method: globalArray[i]];
+
+ }];
+}
+// CHECK48: [[@LINE-18]]:7 -> [[@LINE-18]]:13, [[@LINE-17]]:1 -> [[@LINE-17]]:5, [[@LINE-17]]:15 -> [[@LINE-17]]:25
++(Object *)class:(BOOL)object __attribute__((test()))method:(some_type_t)method
+world:(Object *)struct
+{
+ [self method: ^ { call() = [self a_200: ']' a_200: "]"];
+ } struct: ^ () {
+ return [] () {
+ };
+
+ } class: [_undef_ivar part: "string" name: [] {
+ int bar = [self withSomething: "string" bar: @"string literal" usingThing: @"string literal"];
+
+ }]];
+
+ [super foo: 12 foo: * "]" foo: ("string") foo: "string" method: 12];
+}
+// CHECK49: [[@LINE-15]]:12 -> [[@LINE-15]]:17, [[@LINE-15]]:54 -> [[@LINE-15]]:60, [[@LINE-14]]:1 -> [[@LINE-14]]:6
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=piece:a_200:world:piece:z_Z_42:usingThing -new-name=struct:part:bar:struct:class:usingThing -indexed-file=%s -indexed-at=410:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK47 %s
+// RUN: clang-refactor-test rename-indexed-file -name=method:part:usingThing -new-name=object:piece:method -indexed-file=%s -indexed-at=443:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK48 %s
+// RUN: clang-refactor-test rename-indexed-file -name=class:method:world -new-name=struct:test:z_Z_42 -indexed-file=%s -indexed-at=462:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK49 %s
+
+@implementation a_200 <name, bar, a_200>
++(some_type_t)class:(void)z_Z_42
+object:(BOOL)perform
+z_Z_42:(const Object &)usingThing name:(const Object &)a_200 test:(({}))world perform:(int[1 + 2 - 3])z_Z_42
+{
+ if ("string") {
+ if ([super withSomething: [] { return [_undef_ivar usingThing: [] () {
+ }
+ world: [] () {
+
+
+ } foo: 12 perform: globalArray[i] name: @"string literal"];
+ } world: ^ { // comment
+ } object: ^ { int method = "]";
+ }
+]) {
+ [globalObject send: [self.undef_property method: globalArray[i] object: [] { [self method: ']' test: [self foo: ("string") a_200: @"string literal" piece: 12 < [self class: @"string literal" piece: "]" + ']']] test: [self onEntity: @"string literal"
+ z_Z_42: ']'] name: globalArray[i] test: 12] other: 42];
+ } class: ^ { ; ;[self perform: ("string") bar: (']') foo: "string"
+ foo: ^ {
+
+
+ }];
+ } piece: ^ {
+ call() = [_undef_ivar perform: (12) bar: ^ {
+
+
+ } test: "]" name: "string" bar: "]"];
+
+ }];
+
+ }
+
+ }
+
+ [globalObject send: [self bar: globalArray[i] == ']' perform: [_undef_ivar z_Z_42: @"string literal" object: ^ {
+ [globalObject send: [self world: "]" onEntity: ']'
+ struct: [] () {
+ } perform: [self.undef_property piece: [] () {
+
+
+ } method: 12 foo: [] {
+
+
+ } onEntity: "string" * "]" method: "]"] < ']'] other: 42] other: 42];
+
+ } method: ^ { // comment
+ } test: ^ () { return [] {
+ };
+ }] a_200: [] () { ([self.undef_property object: ^ {
+ } usingThing: @"string literal"
+ test: "string" usingThing: globalArray[i]
+ foo: /*]*/ globalArray[i]]);
+ } piece: 12];
+}
+// CHECK50: [[@LINE-54]]:15 -> [[@LINE-54]]:20, [[@LINE-53]]:1 -> [[@LINE-53]]:7, [[@LINE-52]]:1 -> [[@LINE-52]]:7, [[@LINE-52]]:35 -> [[@LINE-52]]:39, [[@LINE-52]]:62 -> [[@LINE-52]]:66, [[@LINE-52]]:79 -> [[@LINE-52]]:86
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=class:object:z_Z_42:name:test:perform -new-name=world:foo:part:a_200:name:piece -indexed-file=%s -indexed-at=484:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK50 %s
+
+@implementation piece (part)
++(BOOL)method:(^ { })foo foo:(({}))foo
+usingThing:(int)withSomething perform:(int[1 + 2 - 3])bar a_200:(some_type_t (^)())onEntity test:(const Object &)z_Z_42 __attribute__((eval { int x = 0 + 1; })) {
+ if ([self method: ']' struct: ^ { [globalObject send: [_undef_ivar world: "]" onEntity: globalArray[i] foo: "]" withSomething: < ']' onEntity: "string"] other: 42];
+ }
+]) {
+ call() = [self part: 12
+ struct: "string" perform: "string" class: [] () { return "string";
+ }
+];
+
+ }
+}
+// CHECK51: [[@LINE-12]]:8 -> [[@LINE-12]]:14, [[@LINE-12]]:26 -> [[@LINE-12]]:29, [[@LINE-11]]:1 -> [[@LINE-11]]:11, [[@LINE-11]]:31 -> [[@LINE-11]]:38, [[@LINE-11]]:59 -> [[@LINE-11]]:64, [[@LINE-11]]:93 -> [[@LINE-11]]:97
++(some_type_t)withSomething:(some_type_t (^)(int, Object *))z_Z_42 bar:(^ { })usingThing {
+ return 12;
+}
+// CHECK52: [[@LINE-3]]:15 -> [[@LINE-3]]:28, [[@LINE-3]]:68 -> [[@LINE-3]]:71
+-(some_type_t)object:(some_type_t (*)())bar
+foo:(some_type_t (*)(const Object &))class
+withSomething:(void)onEntity
+usingThing:(^ { })bar a_200:(some_type_t (^)())usingThing
+usingThing:(^ { })name {
+ call() = ([self part: "]" piece: "string" * "]" bar: globalArray[i] piece: [] () { int bar = [self test: "string" method: globalArray[i] method: "string" == /*]*/ "string" * @"string literal" object: @"string literal" usingThing: globalArray[i]];
+ } < "string"]);
+
+ if ("]" + [self part: @"string literal" class: "string"]) {
+ [super onEntity: "]" object: "]" piece: (12) test: ']' < ^ {
+ int bar = [self usingThing: [] () {
+
+
+ } z_Z_42: ^ {
+ } usingThing: "]" object: "]"];
+
+ }
+];
+
+ }
+}
+// CHECK53: [[@LINE-21]]:15 -> [[@LINE-21]]:21, [[@LINE-20]]:1 -> [[@LINE-20]]:4, [[@LINE-19]]:1 -> [[@LINE-19]]:14, [[@LINE-18]]:1 -> [[@LINE-18]]:11, [[@LINE-18]]:23 -> [[@LINE-18]]:28, [[@LINE-17]]:1 -> [[@LINE-17]]:11
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=method:foo:usingThing:perform:a_200:test -new-name=object:z_Z_42:test:struct:perform:z_Z_42 -indexed-file=%s -indexed-at=543:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK51 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:bar -new-name=world:foo -indexed-file=%s -indexed-at=556:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK52 %s
+// RUN: clang-refactor-test rename-indexed-file -name=object:foo:withSomething:usingThing:a_200:usingThing -new-name=part:a_200:method:perform:world:part -indexed-file=%s -indexed-at=560:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK53 %s
+
+@interface world
+-(const Object &)class:(int[1 + 2 - 3])struct onEntity:(void (^)(Object *, int))withSomething __attribute__((test()));
+// CHECK54: [[@LINE-1]]:18 -> [[@LINE-1]]:23, [[@LINE-1]]:47 -> [[@LINE-1]]:55
+-(int)world:(int)foo name:(const Object & (^)(int, Object *))piece a_200:(Object *)test object:(BOOL)onEntity __attribute__((eval { int x = 0 + 1; })) onEntity:(BOOL)z_Z_42 /*comment*/ ;
+// CHECK55: [[@LINE-1]]:7 -> [[@LINE-1]]:12, [[@LINE-1]]:22 -> [[@LINE-1]]:26, [[@LINE-1]]:68 -> [[@LINE-1]]:73, [[@LINE-1]]:89 -> [[@LINE-1]]:95, [[@LINE-1]]:152 -> [[@LINE-1]]:160
+-(int)usingThing:(^ { })part name:(const Object &)usingThing usingThing:(^ { })perform __attribute__((test()))struct:(^ { })world
+withSomething:(int[1 + 2 - 3])method //comment
+;
+// CHECK56: [[@LINE-3]]:7 -> [[@LINE-3]]:17, [[@LINE-3]]:30 -> [[@LINE-3]]:34, [[@LINE-3]]:62 -> [[@LINE-3]]:72, [[@LINE-3]]:111 -> [[@LINE-3]]:117, [[@LINE-2]]:1 -> [[@LINE-2]]:14
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=class:onEntity -new-name=usingThing:usingThing -indexed-file=%s -indexed-at=588:18 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK54 %s
+// RUN: clang-refactor-test rename-indexed-file -name=world:name:a_200:object:onEntity -new-name=world:bar:onEntity:name:part -indexed-file=%s -indexed-at=590:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK55 %s
+// RUN: clang-refactor-test rename-indexed-file -name=usingThing:name:usingThing:struct:withSomething -new-name=name:struct:method:method:usingThing -indexed-file=%s -indexed-at=592:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK56 %s
+
+@implementation onEntity
+-(BOOL)piece:(some_type_t (^)(int))usingThing perform:(int)foo onEntity:(({}))withSomething __attribute__((test()))bar:(Object *)usingThing usingThing:(const Object &)bar class:(const Object & (^)(int, const Object &))perform {
+ [self object: "string" foo: ([self onEntity: "string" * [] { if (@"string literal") {
+ call() = [self z_Z_42: "string" usingThing: @"string literal"
+];
+
+ }
+ } object: globalArray[i]
+ test: ^ () { if ("string" == @"string literal" + ^ () {
+
+
+ }) {
+ int name = 12 * 12;
+
+ }
+ }]) foo: @{ @1, @3 }];
+
+ call() = [super foo: ^ () { ] } part: ']' onEntity: [self piece: ']' piece: [] () { [super name: "]" perform: "string"];
+ }]
+ struct: "]" part: @"string literal"];
+}
+// CHECK57: [[@LINE-20]]:8 -> [[@LINE-20]]:13, [[@LINE-20]]:47 -> [[@LINE-20]]:54, [[@LINE-20]]:64 -> [[@LINE-20]]:72, [[@LINE-20]]:116 -> [[@LINE-20]]:119, [[@LINE-20]]:141 -> [[@LINE-20]]:151, [[@LINE-20]]:172 -> [[@LINE-20]]:177
+-(int)withSomething:(int[1 + 2 - 3])foo usingThing:(Object * (^)(some_type_t, BOOL))a_200 perform:(some_type_t)onEntity __attribute__((test())){
+ [globalObject message] = [self.undef_property struct: @"string literal" object: "]" < ']' withSomething: @"string literal" piece: [self.undef_property withSomething: 12 foo: @"string literal" withSomething: "]" object: [] () { Object * z_Z_42 = 12 + "string";
+ } part: ^ () {
+ const Object & z_Z_42 = globalArray[i];
+
+ }]];
+
+ some_type_t foo = [self.undef_property foo: 12 * globalArray[i] method: @"string literal" a_200: ^ () {
+ globalArray[12] = [self onEntity: @"string literal" class: "string" onEntity: "]"];
+
+ }/*comment*/];
+}
+// CHECK58: [[@LINE-12]]:7 -> [[@LINE-12]]:20, [[@LINE-12]]:41 -> [[@LINE-12]]:51, [[@LINE-12]]:91 -> [[@LINE-12]]:98
++(int)piece:(int)test
+onEntity:(int[1 + 2 - 3])test piece:(const Object &)piece {
+ [self z_Z_42: ']' usingThing: ("]")];
+
+ if ([self test: [] () {
+ // comment
+
+ } a_200: [] () { BOOL foo = [self world: [self.undef_property test: globalArray[i] foo: [self method: "]" onEntity: [] {
+
+
+ } withSomething: globalArray[i]] piece: 12 struct: ']'] usingThing: [super test: globalArray[i] world: [_undef_ivar bar: @{ @1, @3 } z_Z_42: [_undef_ivar bar: z_Z_42: [self foo: ("string") object: ^ () {
+
+
+ } bar: "string"] struct: (12) withSomething: @"string literal" name: [self.undef_property part: @"string literal" onEntity: [] {
+
+
+ }]] onEntity: ']' name: 12 object: @{ @1, @3 }]
+//comment
+] part: globalArray[i] test: 12];
+ } + ']' bar: [super piece: "]" world: @"string literal" object: globalArray[i] test: ']'
+ withSomething: @{ @1, @3 }] piece: [self a_200: ']' world: ']'] onEntity: @"string literal"] == ^ () { ] }) {
+ if (12) {
+ [self.undef_property withSomething: [self a_200: globalArray[i] withSomething: "string"
+ test: [] () { [self perform: "]" object: ']' foo: @{ @1, @3 } + [] () {
+ }];
+ } object: "string" struct: /*]*/ "]"] piece: @{ @1, @3 } a_200: 12 bar: globalArray[i]];
+
+ }
+
+ }
+}
+// CHECK59: [[@LINE-31]]:7 -> [[@LINE-31]]:12, [[@LINE-30]]:1 -> [[@LINE-30]]:9, [[@LINE-30]]:31 -> [[@LINE-30]]:36
++(some_type_t)withSomething:(BOOL)bar
+z_Z_42:(BOOL)struct {
+ [self usingThing: (globalArray[i]) world: @"string literal" name: [super test: globalArray[i] part: ^ () {
+ [super test: 12 z_Z_42: "string"];
+
+ } perform: ']' perform: "string"] object: "]" name: 12];
+
+ /*comment*/[self world: "string" bar: ^ {
+ [self.undef_property bar: @"string literal" name: ^ {
+
+
+ } z_Z_42: "string" + [_undef_ivar name: [self part: 12 struct: [] {
+ }
+] withSomething: @"string literal" class: ']' < "string" == "]" < "string" == "]"] onEntity: globalArray[i] object: "]"];
+
+ } == ']' test: @"string literal" world: "]" struct: "string"];
+}
+// CHECK60: [[@LINE-17]]:15 -> [[@LINE-17]]:28, [[@LINE-16]]:1 -> [[@LINE-16]]:7
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=piece:perform:onEntity:bar:usingThing:class -new-name=method:bar:piece:onEntity:foo:piece -indexed-file=%s -indexed-at=602:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK57 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:usingThing:perform -new-name=name:usingThing:foo -indexed-file=%s -indexed-at=623:7 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK58 %s
+// RUN: clang-refactor-test rename-indexed-file -name=piece:onEntity:piece -new-name=class:part:world -indexed-file=%s -indexed-at=636:7 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK59 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:z_Z_42 -new-name=object:object -indexed-file=%s -indexed-at=668:15 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK60 %s
+
+@interface test (z_Z_42)
+-(void)struct:(some_type_t)onEntity perform:(void)struct
+usingThing:(BOOL)foo onEntity:(some_type_t)onEntity /*comment*/ a_200:(Object *)piece ;
+// CHECK61: [[@LINE-2]]:8 -> [[@LINE-2]]:14, [[@LINE-2]]:37 -> [[@LINE-2]]:44, [[@LINE-1]]:1 -> [[@LINE-1]]:11, [[@LINE-1]]:22 -> [[@LINE-1]]:30, [[@LINE-1]]:65 -> [[@LINE-1]]:70
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=struct:perform:usingThing:onEntity:a_200 -new-name=method:world:foo:foo:perform -indexed-file=%s -indexed-at=693:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK61 %s
+
+@implementation part
+-(some_type_t)bar:(some_type_t (^)(some_type_t, Object *))class piece:(some_type_t)z_Z_42
+test:(BOOL)z_Z_42 /*comment*/ class:(void)class __attribute__((test())){
+ [globalObject send: [self onEntity: == ']' z_Z_42: [] { int bar = [_undef_ivar a_200: [super a_200: @"string literal"
+//comment
+ withSomething: @"string literal" + ^ {
+ }] foo: "]"] other: 42];
+ } test: ^ () { if ([] () {
+
+
+ }) {
+ int bar = [self part: ^ () {
+ }
+ class: [] () {
+ } object: @"string literal"];
+
+ }
+ }];
+
+ [globalObject message] = [_undef_ivar part: (@{ @1, @3 }) struct: "]" z_Z_42: [self perform: @"string literal" usingThing: ']' a_200: ^ { // comment
+ } struct: ^ () {
+ if ("string") {
+ int test = "string";
+
+ }
+
+ }]];
+}
+// CHECK62: [[@LINE-27]]:15 -> [[@LINE-27]]:18, [[@LINE-27]]:65 -> [[@LINE-27]]:70, [[@LINE-26]]:1 -> [[@LINE-26]]:5, [[@LINE-26]]:31 -> [[@LINE-26]]:36
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=bar:piece:test:class -new-name=perform:bar:struct:world -indexed-file=%s -indexed-at=700:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK62 %s
+
+@interface perform
++(void)piece:(BOOL)a_200 onEntity:(const Object & (^)())object bar:(int (*)())method //comment
+method:(some_type_t)onEntity
+;
+// CHECK63: [[@LINE-3]]:8 -> [[@LINE-3]]:13, [[@LINE-3]]:26 -> [[@LINE-3]]:34, [[@LINE-3]]:64 -> [[@LINE-3]]:67, [[@LINE-2]]:1 -> [[@LINE-2]]:7
+-(void)withSomething:(const Object &)piece //comment
+piece:(void)perform ;
+// CHECK64: [[@LINE-2]]:8 -> [[@LINE-2]]:21, [[@LINE-1]]:1 -> [[@LINE-1]]:6
+-(some_type_t)z_Z_42:(int)method piece:(^ { })struct struct:(BOOL)world /*comment*/ a_200:(const Object &)piece
+;
+// CHECK65: [[@LINE-2]]:15 -> [[@LINE-2]]:21, [[@LINE-2]]:34 -> [[@LINE-2]]:39, [[@LINE-2]]:54 -> [[@LINE-2]]:60, [[@LINE-2]]:85 -> [[@LINE-2]]:90
++(Object *)foo:(const Object &)part bar:(some_type_t)a_200 ;
+// CHECK66: [[@LINE-1]]:12 -> [[@LINE-1]]:15, [[@LINE-1]]:37 -> [[@LINE-1]]:40
+@end
+// RUN: clang-refactor-test rename-indexed-file -name=piece:onEntity:bar:method -new-name=onEntity:withSomething:class:piece -indexed-file=%s -indexed-at=732:8 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK63 %s
+// RUN: clang-refactor-test rename-indexed-file -name=withSomething:piece -new-name=perform:foo -indexed-file=%s -indexed-at=736:8 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK64 %s
+// RUN: clang-refactor-test rename-indexed-file -name=z_Z_42:piece:struct:a_200 -new-name=test:withSomething:piece:class -indexed-file=%s -indexed-at=739:15 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK65 %s
+// RUN: clang-refactor-test rename-indexed-file -name=foo:bar -new-name=usingThing:a_200 -indexed-file=%s -indexed-at=742:12 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK66 %s
diff --git a/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm b/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm
new file mode 100644
index 0000000..f533331
--- /dev/null
+++ b/test/Refactor/Rename/IndexedObjCMethodEmptySelector.mm
@@ -0,0 +1,49 @@
+
+@interface EmptySelectorsRule_Psych
+
+- (int):(int)_; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:8
+- (void)test: (int)x :(int)y; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:22 -> [[@LINE]]:22
+- (void):(int)_ :(int) m:(int)z; // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:9, [[@LINE]]:17 -> [[@LINE]]:17, [[@LINE]]:25 -> [[@LINE]]:25
+
+@end
+
+namespace g {
+ int x;
+}
+
+@implementation EmptySelectorsRule_Psych
+
+- (int):(int)_ { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:8
+ [self :2]; // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:11
+ SEL s0 = @selector(:); // CHECK1: selector [[@LINE]]:24 -> [[@LINE]]:24
+ // CHECK1-NOT: comment
+ // CHECK1-NOT: rename
+ // CHECK1-NOT: selector
+// RUN: clang-refactor-test rename-indexed-file -name=: -new-name=foo -indexed-file=%s -indexed-symbol-kind=objc-im -indexed-at=4:8 -indexed-at=16:8 -indexed-at=objc-message:17:11 %s | FileCheck --check-prefix=CHECK1 %s
+ return 0;
+}
+- (void)test: (int)x :(int)y { } // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:22 -> [[@LINE]]:22
+- (void) :(int)_ :(int)m :(int)z { // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:18 -> [[@LINE]]:18, [[@LINE]]:26 -> [[@LINE]]:26
+ [self test:0:1]; // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:15, [[@LINE]]:17 -> [[@LINE]]:17
+ SEL s1 = @selector(test::); // CHECK2: selector [[@LINE]]:24 -> [[@LINE]]:28, [[@LINE]]:29 -> [[@LINE]]:29
+ @selector(test: :); // CHECK2: selector [[@LINE]]:15 -> [[@LINE]]:19, [[@LINE]]:21 -> [[@LINE]]:21
+ // CHECK2-NOT: comment
+ // CHECK2-NOT: rename
+ // CHECK2-NOT: selector
+// RUN: clang-refactor-test rename-indexed-file -name=test:: -new-name=foo:bar: -indexed-file=%s -indexed-symbol-kind=objc-im -indexed-at=5:9 -indexed-at=25:9 -indexed-at=objc-message:27:11 %s | FileCheck --check-prefix=CHECK2 %s
+
+ [self: ::g::x + ([self: 0]):~0 :3]; // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:32 -> [[@LINE]]:32, [[@LINE]]:36 -> [[@LINE]]:36
+ SEL s2 = @selector(:::); // CHECK3: selector [[@LINE]]:24 -> [[@LINE]]:24, [[@LINE]]:25 -> [[@LINE]]:25, [[@LINE]]:26 -> [[@LINE]]:26
+ @selector(: ::); // CHECK3: selector [[@LINE]]:15 -> [[@LINE]]:15, [[@LINE]]:17 -> [[@LINE]]:17, [[@LINE]]:18 -> [[@LINE]]:18
+ @selector(::::); // not matching.
+ // CHECK3-NOT: comment
+ // CHECK3-NOT: rename
+ // CHECK3-NOT: selector
+// RUN: clang-refactor-test rename-indexed-file -name=::: -new-name=do:stuff:: -indexed-file=%s -indexed-symbol-kind=objc-im -indexed-at=6:9 -indexed-at=26:10 -indexed-at=objc-message:35:10 %s | FileCheck --check-prefix=CHECK3 %s
+
+ // NO Textual matches: text:
+ // :
+ // :::
+}
+
+@end
diff --git a/test/Refactor/Rename/IndexedObjCProperty.m b/test/Refactor/Rename/IndexedObjCProperty.m
new file mode 100644
index 0000000..003186d
--- /dev/null
+++ b/test/Refactor/Rename/IndexedObjCProperty.m
@@ -0,0 +1,45 @@
+@interface I1
+
+@property int p1;
+
+@end
+
+@implementation I1
+
+- (void)foo {
+ self.p1; // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:10
+ [self p1]; // CHECK: rename [[@LINE]]:9 -> [[@LINE]]:11
+ [self setP1: 2]; // CHECK: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14
+ _p1 = 3; // CHECK: rename "_foo" [[@LINE]]:3 -> [[@LINE]]:6
+}
+
+@end
+
+// RUN: clang-refactor-test rename-indexed-file -name=p1 -name=p1 -name=setP1 -name=_p1 -new-name=foo -new-name=foo -new-name=setFoo -new-name=_foo -indexed-file=%s -indexed-at=10:8 -indexed-at=objc-im:1:11:9 -indexed-at=objc-im:2:12:9 -indexed-at=3:13:3 %s | FileCheck %s
+
+// p1 _p1 setP1
+// CHECK: comment [[@LINE-1]]:4
+// CHECK: comment "_foo" [[@LINE-2]]:7
+// CHECK: comment "setFoo" [[@LINE-3]]:11
+
+@selector(p1)
+@selector(setP1:)
+@selector(_p1)
+// CHECK: selector [[@LINE-3]]:11
+// CHECK: selector "setFoo" [[@LINE-3]]:11
+// CHECK-NOT: selector
+
+@interface ImplicitProperty
+
+- (int)implicit; // IMPL_GET: rename [[@LINE]]:8 -> [[@LINE]]:16
+- (void)setImplicit:(int)x; // IMPL_SET: rename [[@LINE]]:9 -> [[@LINE]]:20
+
+@end
+
+void useImplicitProperty(ImplicitProperty *x) {
+ x.implicit; // IMPL_GET: rename [[@LINE]]:5 -> [[@LINE]]:13
+ x.implicit = 0; // IMPL_SET: implicit-property [[@LINE]]:5 -> [[@LINE]]:13
+}
+
+// RUN: clang-refactor-test rename-indexed-file -name=implicit -new-name=foo -indexed-file=%s -indexed-at=objc-im:34:8 -indexed-at=objc-message:40:5 %s | FileCheck --check-prefix=IMPL_GET %s
+// RUN: clang-refactor-test rename-indexed-file -name=setImplicit -new-name=setFoo -indexed-file=%s -indexed-at=objc-im:35:9 -indexed-at=objc-message:41:5 %s | FileCheck --check-prefix=IMPL_SET %s
diff --git a/test/Refactor/Rename/Inputs/MultiFileTUHeader.h b/test/Refactor/Rename/Inputs/MultiFileTUHeader.h
new file mode 100644
index 0000000..2e8d29f
--- /dev/null
+++ b/test/Refactor/Rename/Inputs/MultiFileTUHeader.h
@@ -0,0 +1,6 @@
+class Foo { // CHECK: rename "{{.*}}/Inputs/MultiFileTUHeader.h" [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ Foo(); // CHECK: rename "{{.*}}/Inputs/MultiFileTUHeader.h" [[@LINE]]:3 -> [[@LINE]]:6
+
+ void method();
+};
diff --git a/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m b/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m
new file mode 100644
index 0000000..249aa7f
--- /dev/null
+++ b/test/Refactor/Rename/Inputs/ObjCImplementationTURequestsImplementation.m
@@ -0,0 +1,3 @@
+@implementation ExplicitIVarsInInterface
+
+@end
diff --git a/test/Refactor/Rename/Inputs/TransparentEnum.h b/test/Refactor/Rename/Inputs/TransparentEnum.h
new file mode 100644
index 0000000..b21adae
--- /dev/null
+++ b/test/Refactor/Rename/Inputs/TransparentEnum.h
@@ -0,0 +1 @@
+#define TRANSPARENT_ENUM(_name, _type) enum _name:_type _name; enum _name : _type
diff --git a/test/Refactor/Rename/Inputs/objc-system-header.h b/test/Refactor/Rename/Inputs/objc-system-header.h
new file mode 100644
index 0000000..23b252a
--- /dev/null
+++ b/test/Refactor/Rename/Inputs/objc-system-header.h
@@ -0,0 +1,5 @@
+@interface MySystemClass
+
+- (void)someMethod:(int)x with:(int)y;
+
+@end
diff --git a/test/Refactor/Rename/Inputs/rename-indexed-file.cpp b/test/Refactor/Rename/Inputs/rename-indexed-file.cpp
new file mode 100644
index 0000000..ec6b78d
--- /dev/null
+++ b/test/Refactor/Rename/Inputs/rename-indexed-file.cpp
@@ -0,0 +1,4 @@
+void Test::otherFile() {
+ Test m;
+ m.~Test();
+}
diff --git a/test/Refactor/Rename/Inputs/system-header.h b/test/Refactor/Rename/Inputs/system-header.h
new file mode 100644
index 0000000..ae1af94
--- /dev/null
+++ b/test/Refactor/Rename/Inputs/system-header.h
@@ -0,0 +1,11 @@
+void systemFunction();
+
+struct SystemStruct {
+};
+typedef struct SystemStruct SystemTypedef;
+
+enum SystemEnum {
+ SystemEnumCase
+};
+
+extern int systemVariable;
diff --git a/test/Refactor/Rename/LocalBlockSymbol.m b/test/Refactor/Rename/LocalBlockSymbol.m
new file mode 100644
index 0000000..93235aa
--- /dev/null
+++ b/test/Refactor/Rename/LocalBlockSymbol.m
@@ -0,0 +1,58 @@
+__auto_type escaping1 = ^ {
+ struct Local { // LOCAL1: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:2:10 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL1 %s
+
+ int x;// ESCAPES1: rename [[@LINE]]
+ // NOESCAPE1: rename local [[@LINE-1]]
+// RUN: clang-refactor-test rename-initiate -at=%s:5:9 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=ESCAPES1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:5:9 -new-name=name %s -fblocks -x objective-c | FileCheck --check-prefix=NOESCAPE1 %s
+ };
+ struct Local result;
+ return result;
+};
+
+__auto_type escaping2 = ^ () { // no prototype,
+ struct Local { // LOCAL2: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:15:10 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL2 %s
+
+ int x;// ESCAPES2: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:18:9 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=ESCAPES2 %s
+ };
+ struct Local result;
+ return result;
+};
+
+__auto_type outer1 = ^ {
+ __auto_type escaping3 = ^ (int x) { // prototype with some arguments.
+ struct Local { // LOCAL3: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:27:12 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL3 %s
+
+ int x;// ESCAPES3: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:30:11 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=ESCAPES3 %s
+ };
+ struct Local result;
+ return result;
+ };
+ return escaping3(0);
+};
+
+void outer2() {
+ __auto_type escaping1 = ^ {
+ struct Local {
+ int x;// LOCAL4: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:42:11 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL4 %s
+ };
+ struct Local result;
+ return result;
+ };
+}
+
+__auto_type normalBlock = ^int (void) {
+ struct Local { // LOCAL5: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:51:10 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL5 %s
+
+ int x;// LOCAL6: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:54:9 -new-name=name %s -fblocks -x objective-c-header | FileCheck --check-prefix=LOCAL6 %s
+ };
+ return 0;
+};
diff --git a/test/Refactor/Rename/LocalBlockSymbolCpp.mm b/test/Refactor/Rename/LocalBlockSymbolCpp.mm
new file mode 100644
index 0000000..d48f3b5
--- /dev/null
+++ b/test/Refactor/Rename/LocalBlockSymbolCpp.mm
@@ -0,0 +1,24 @@
+auto escaping1 = ^ {
+ struct Global { // ESCAPES1: rename [[@LINE]]
+ // NOESCAPE1: rename local [[@LINE-1]]
+// RUN: clang-refactor-test rename-initiate -at=%s:2:10 -new-name=name %s -std=c++14 -fblocks -x objective-c++-header | FileCheck --check-prefix=ESCAPES1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:2:10 -new-name=name %s -std=c++14 -fblocks -x objective-c++ | FileCheck --check-prefix=NOESCAPE1 %s
+ };
+ return Global();
+};
+
+void outer1() {
+ auto escaping1 = ^ {
+ struct Local { // LOCAL1: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:12:12 -new-name=name %s -std=c++14 -fblocks -x objective-c++-header | FileCheck --check-prefix=LOCAL1 %s
+ };
+ return Local();
+ };
+}
+
+auto normalBlock = ^int () {
+ struct Local { // LOCAL2: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:20:10 -new-name=name %s -std=c++14 -fblocks -x objective-c++-header | FileCheck --check-prefix=LOCAL2 %s
+ };
+ return 0;
+};
diff --git a/test/Refactor/Rename/LocalSymbol.cpp b/test/Refactor/Rename/LocalSymbol.cpp
new file mode 100644
index 0000000..4bc82ed
--- /dev/null
+++ b/test/Refactor/Rename/LocalSymbol.cpp
@@ -0,0 +1,232 @@
+static int staticIsGlobalVar = 0; // CHECK1: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:1:12 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK1 %s
+
+int globalVar = 0; // CHECK2: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:4:5 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK2 %s
+
+struct GlobalFoo { // CHECK3: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:7:8 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK3 %s
+
+ struct GlobalBar { // CHECK4: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:10:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK4 %s
+ };
+
+ void foo() { // CHECK5: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK5 %s
+
+ struct LocalFoo { }; // CHECK6: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:17:12 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK6 %s
+ }
+
+ virtual void bar() { } // CHECK-BAR: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:21:16 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK-BAR %s
+
+ int globalField; // CHECK7: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:24:7 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK7 %s
+};
+
+enum GlobalEnum { // CHECK8: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:28:6 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK8 %s
+
+ GlobalEnumCase = 0 // CHECK9: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:31:3 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK9 %s
+};
+
+namespace {
+ struct AnonymousIsGlobalFoo { }; // CHECK10: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:36:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=CHECK10 %s
+
+ void globalFoo() { } // CHECK11: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:39:8 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=CHECK11 %s
+}
+
+namespace globalNamespace { // CHECK12: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:43:11 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK12 %s
+}
+
+void func(int localParam) { // CHECK13: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:47:15 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK13 %s
+
+ int localVar = 0; // CHECK14: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:50:7 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK14 %s
+
+ enum LocalEnum { // CHECK15: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:53:8 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK15 %s
+
+ LocalEnumCase = 0 // CHECK16: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:56:5 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK16 %s
+ };
+
+ struct LocalFoo: GlobalFoo { // CHECK17: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:60:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK17 %s
+
+ struct LocalBar { }; // CHECK18: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:63:12 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK18 %s
+
+ void foo() { } // CHECK19: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:66:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK19 %s
+
+ // Bar is global since it overrides GlobalFoo::bar
+ void bar() { } // CHECK-BAR: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:70:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK-BAR %s
+
+ int localField; // CHECK20: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:73:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK20 %s
+ };
+}
+
+auto escapable1 = []() -> auto {
+ struct Global { // ESCAPES1: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:79:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES1 %s
+
+ int field = 2; // ESCAPES2: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:82:9 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES2 %s
+
+ void foo() { } // ESCAPES3: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:85:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES3 %s
+ };
+ return Global();
+};
+
+auto escapable2() {
+ struct Global { // ESCAPES4: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:92:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES4 %s
+
+ int field = 2; // ESCAPES5: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:95:9 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES5 %s
+
+ void foo() { } // ESCAPES6: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:98:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES6 %s
+ };
+ return Global();
+}
+
+template<typename T>
+struct C {
+ T x;
+};
+
+decltype(auto) escapable4() {
+ struct Global { // ESCAPES7: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:110:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES7 %s
+ };
+ return C<Global>();
+}
+
+auto escapable5() -> decltype(auto) {
+ struct Foo {
+ struct Global { // ESCAPES8: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:118:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES8 %s
+ };
+ };
+ return C<Foo::Global>();
+}
+
+auto escapable6() {
+ struct Foo {
+ struct Global { // ESCAPES9: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:127:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES9 %s
+ };
+ };
+ return Foo::Global();
+}
+
+auto escapableOuter1() {
+ struct Foo {
+ auto escapableInner() {
+ struct Global { // ESCAPES10: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:137:14 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES10 %s
+ };
+ return Global();
+ }
+ }
+ return Foo().escapableInner();
+}
+
+auto escapableOuter2() {
+ auto escapableInner = []() -> auto {
+ struct Global { // ESCAPES11: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:148:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES11 %s
+ };
+ return Global();
+ }
+ return escapableInner();
+}
+
+void outer() {
+ auto escapableInner2 = []() -> auto {
+ struct Local { // ESCAPES12: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:158:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES12 %s
+ };
+ return Local();
+ }
+ struct Foo {
+ auto foo() {
+ struct Local { // ESCAPES13: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:165:14 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES13 %s
+ };
+ return Local();
+ }
+ };
+}
+
+struct Escapable {
+ auto escapable() {
+ struct Global { // ESCAPES14: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:175:12 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES14 %s
+ };
+ return Global();
+ }
+};
+
+auto escapableViaTypedef() {
+ struct Global { // ESCAPES15: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:183:10 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ESCAPES15 %s
+ };
+ typedef Foo Global;
+ return Foo();
+}
+
+auto nonescapable1 = []() -> auto {
+ struct Local { // NOESCAPE1: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:191:10 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=NOESCAPE1 %s
+
+ int field = 2; // NOESCAPE2: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:194:9 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=NOESCAPE2 %s
+
+ void foo() { } // NOESCAPE3: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:197:10 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=NOESCAPE3 %s
+ };
+ return Local();
+};
+
+static void localOrGlobal1() { }; // ISGLOBAL1: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:203:13 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=ISGLOBAL1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:203:13 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ISGLOBAL1 %s
+
+namespace {
+
+struct LocalOrGlobal { }; // ISGLOBAL2: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:209:13 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=ISGLOBAL2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:209:13 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ISGLOBAL2 %s
+
+
+void localOrGlobal2() { }; // ISGLOBAL3: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:214:13 -new-name=name %s -std=c++14 | FileCheck --check-prefix=ISGLOBAL3 %s
+}
+
+
+struct LocalOrGlobalWrapper1 {
+
+ static void foo() { } // ISGLOBAL4: rename [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:221:15 -new-name=name %s -std=c++14 -x c++ | FileCheck --check-prefix=ISGLOBAL4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:221:15 -new-name=name %s -std=c++14 -x c++-header | FileCheck --check-prefix=ISGLOBAL4 %s
+};
+
+void func2(int x) {
+ auto lambda1 = [x] // CHECK21: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:226:16 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK21 %s
+ (int z) { // CHECK22: rename local [[@LINE]]
+// RUN: clang-refactor-test rename-initiate -at=%s:229:10 -new-name=name %s -std=c++14 | FileCheck --check-prefix=CHECK22 %s
+ }
+}
diff --git a/test/Refactor/Rename/MemberExprMacro.cpp b/test/Refactor/Rename/MemberExprMacro.cpp
new file mode 100644
index 0000000..dfd7ec0
--- /dev/null
+++ b/test/Refactor/Rename/MemberExprMacro.cpp
@@ -0,0 +1,19 @@
+class Baz {
+public:
+ int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+};
+
+int qux(int x) { return 0; }
+#define MACRO(a) qux(a)
+
+int main() {
+ Baz baz;
+ baz.Foo = 1; /* Test 2 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+ MACRO(baz.Foo); // CHECK: rename [[@LINE]]:13 -> [[@LINE]]:16
+ int y = baz.Foo; // CHECK: rename [[@LINE]]:15 -> [[@LINE]]:18
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:3:7 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:11:7 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/MultiFileTU.cpp b/test/Refactor/Rename/MultiFileTU.cpp
new file mode 100644
index 0000000..14b0be2
--- /dev/null
+++ b/test/Refactor/Rename/MultiFileTU.cpp
@@ -0,0 +1,9 @@
+#include "Inputs/MultiFileTUHeader.h"
+
+Foo::Foo() {} // CHECK: rename "{{.*}}MultiFileTU.cpp" [[@LINE]]:1 -> [[@LINE]]:4
+// CHECK: rename "{{.*}}MultiFileTU.cpp" [[@LINE-1]]:6 -> [[@LINE-1]]:9
+
+void Foo::method() { } // CHECK: rename "{{.*}}MultiFileTU.cpp" [[@LINE]]:6 -> [[@LINE]]:9
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:6 -at=%s:6:6 -new-name=Bar %s | FileCheck %s
+// RUN: clang-refactor-test rename-initiate -at=%s:3:6 -at=%s:6:6 -new-name=Bar %s | FileCheck %S/Inputs/MultiFileTUHeader.h
diff --git a/test/Refactor/Rename/Namespace.cpp b/test/Refactor/Rename/Namespace.cpp
new file mode 100644
index 0000000..1a0900f
--- /dev/null
+++ b/test/Refactor/Rename/Namespace.cpp
@@ -0,0 +1,22 @@
+namespace gcc /* Test 1 */ { // CHECK: rename [[@LINE]]:11 -> [[@LINE]]:14
+ int x;
+}
+
+void boo() {
+ gcc::x = 42; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:1:11 -new-name=clang %s | FileCheck %s
+
+namespace ns1 {
+namespace ns2 { // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:14
+void f();
+}
+}
+
+void testVisitTwice() {
+ ns1::ns2::f(); // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:11
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:19:8 -new-name=clang %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Rename/NoNewName.cpp b/test/Refactor/Rename/NoNewName.cpp
new file mode 100644
index 0000000..0fe0069
--- /dev/null
+++ b/test/Refactor/Rename/NoNewName.cpp
@@ -0,0 +1,4 @@
+// Check for an error while -new-name argument has not been passed to
+// clang-rename.
+// RUN: not clang-refactor-test rename-initiate -at=%s:1:11 %s 2>&1 | FileCheck %s
+// CHECK: clang-refactor-test: for the -new-name option: must be specified at least once
diff --git a/test/Refactor/Rename/ObjCClass.m b/test/Refactor/Rename/ObjCClass.m
new file mode 100644
index 0000000..aacfc2c
--- /dev/null
+++ b/test/Refactor/Rename/ObjCClass.m
@@ -0,0 +1,95 @@
+@class I1, // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ I2; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+
+@interface I1 // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:2:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+@implementation I1 { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19
+ I1 *interfaceIVar; // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:5
+ // CHECK4: rename [[@LINE-1]]:7 -> [[@LINE-1]]:20
+ int ivar; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:11
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:11:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:12:3 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:21:20 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+-(void)foo: (const I1 *)bar { // CHECK1: rename [[@LINE]]:20 -> [[@LINE]]:22
+
+ ivar = 1; // CHECK3: rename [[@LINE]]:3 -> [[@LINE]]:7
+ self->ivar = 2; // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:13
+ print(bar->ivar);// CHECK3: rename [[@LINE]]:14 -> [[@LINE]]:18
+ interfaceIVar->ivar = 4; // CHECK4: rename [[@LINE]]:3 -> [[@LINE]]:16
+ // CHECK3: rename [[@LINE-1]]:18 -> [[@LINE-1]]:22
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:14:7 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:23:3 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:24:9 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:25:14 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:26:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:12:7 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:26:3 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+
+@end
+
+@interface I1 (Category) // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14
+@end // CHECK5: rename [[@LINE-1]]:16 -> [[@LINE-1]]:24
+
+@implementation I1 (Category) // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19
+@end // CHECK5: rename [[@LINE-1]]:21 -> [[@LINE-1]]:29
+
+// RUN: clang-refactor-test rename-initiate -at=%s:41:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:44:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:41:16 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:44:21 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+
+// Implementation only-category:
+
+@interface I3 // CHECK6: rename [[@LINE]]:12 -> [[@LINE]]:14
+@end
+
+@implementation I3 (DummyCategory) // CHECK6: rename [[@LINE]]:17 -> [[@LINE]]:19
+@end // CHECK7: rename [[@LINE-1]]:21 -> [[@LINE-1]]:34
+
+// RUN: clang-refactor-test rename-initiate -at=%s:55:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:58:17 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:58:21 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s
+
+// Class extension:
+
+@interface I3 () // CHECK6: rename [[@LINE]]:12 -> [[@LINE]]:14
+@end
+
+@implementation I3 // CHECK6: rename [[@LINE]]:17 -> [[@LINE]]:19
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:68:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:71:17 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+
+// Ivar declared in the interface:
+
+@interface I4 {
+ @public
+ int ivar1; // CHECK8: rename [[@LINE]]:7 -> [[@LINE]]:12
+}
+@end
+
+@implementation I4 {
+}
+
+- (void)foo {
+ ivar1 = 0; // CHECK8: rename [[@LINE]]:3 -> [[@LINE]]:8
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:81:7 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:89:3 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
diff --git a/test/Refactor/Rename/ObjCClassProperty.m b/test/Refactor/Rename/ObjCClassProperty.m
new file mode 100644
index 0000000..4c8c9e0
--- /dev/null
+++ b/test/Refactor/Rename/ObjCClassProperty.m
@@ -0,0 +1,100 @@
+@interface ExplicitClassProperty
+
+@property(class) int p1; // CHECK1: rename [[@LINE]]:22 -> [[@LINE]]:24
+@property(class, readonly) int p2; // CHECK2: rename [[@LINE]]:32 -> [[@LINE]]:34
+
++ (int)p1; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+
+@end
+
+@implementation ExplicitClassProperty
+
+@dynamic p1; // CHECK1: rename [[@LINE]]:10 -> [[@LINE]]:12
+
+@dynamic p2; // CHECK2: rename [[@LINE]]:10 -> [[@LINE]]:12
+
++ (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 1;
+}
+// TODO: remove
++ (void)setP1:(int)x { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:14
+}
+
++ (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 2;
+}
+
+- (void)foo {
+ ExplicitClassProperty.p1 = // CHECK1: rename [[@LINE]]:25 -> [[@LINE]]:27
+ ExplicitClassProperty.p2; // CHECK2: rename [[@LINE]]:27 -> [[@LINE]]:29
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:22 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:6:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:12:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:16:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:20:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:28:25 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:32 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:14:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:23:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:29:27 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+@interface ImplicitClassProperty
+
++(int)p3; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:9
++(void)setP3:(int)x; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:13
++(int)p4; // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:9
+
+@end
+
+@implementation ImplicitClassProperty
+
++ (int)p3 { // CHECK3: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 0;
+}
+
+- (void)foo {
+ ImplicitClassProperty.p3 = // CHECK3: implicit-property [[@LINE]]:25 -> [[@LINE]]:27
+ // CHECK4: implicit-property [[@LINE-1]]:25 -> [[@LINE-1]]:30
+ ImplicitClassProperty.p4; // CHECK5: implicit-property [[@LINE]]:31 -> [[@LINE]]:33
+ (void)ImplicitClassProperty.p3; // CHECK3: implicit-property [[@LINE]]:31 -> [[@LINE]]:33
+ // CHECK4: implicit-property [[@LINE-1]]:31 -> [[@LINE-1]]:36
+
+ int x = [ImplicitClassProperty p3]; // CHECK3: rename [[@LINE]]:34 -> [[@LINE]]:36
+ [ImplicitClassProperty setP3: x]; // CHECK4: rename [[@LINE]]:26 -> [[@LINE]]:31
+ x = [ImplicitClassProperty p4]; // CHECK5: rename [[@LINE]]:30 -> [[@LINE]]:32
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:48:7 -at=%s:64:31 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:49:8 -at=%s:61:25 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:50:7 -at=%s:63:31 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+
+
+
+
+
+
+
+
+@interface ClassReceivers // CHECK-RECEIVER: rename [[@LINE]]:12 -> [[@LINE]]:26
+
+@property(class) int p1;
++ (int)implicit;
++ (void)setImplicit:(int)x;
+
+@end
+
+void classReceivers() {
+ ClassReceivers.p1 = 0; // CHECK-RECEIVER: rename [[@LINE]]:3 -> [[@LINE]]:17
+ int y = ClassReceivers.p1; // CHECK-RECEIVER: rename [[@LINE]]:11 -> [[@LINE]]:25
+ ClassReceivers.implicit = 0; // CHECK-RECEIVER: rename [[@LINE]]:3 -> [[@LINE]]:17
+ int x = ClassReceivers.implicit; // CHECK-RECEIVER: rename [[@LINE]]:11 -> [[@LINE]]:25
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:94:3 -at=%s:95:11 -at=%s:96:3 -at=%s:97:11 -new-name=x %s | FileCheck --check-prefix=CHECK-RECEIVER %s
diff --git a/test/Refactor/Rename/ObjCCompatibilityAlias.m b/test/Refactor/Rename/ObjCCompatibilityAlias.m
new file mode 100644
index 0000000..da98da6
--- /dev/null
+++ b/test/Refactor/Rename/ObjCCompatibilityAlias.m
@@ -0,0 +1,44 @@
+@class I1, // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ I2; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+
+@interface I1 // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14
+@end
+
+@compatibility_alias I1Alias I1; // CHECK3: rename [[@LINE]]:22 -> [[@LINE]]:29
+ // CHECK1: rename [[@LINE-1]]:30 -> [[@LINE-1]]:32
+
+@compatibility_alias I2Alias I2; // CHECK4: rename [[@LINE]]:22 -> [[@LINE]]:29
+ // CHECK2: rename [[@LINE-1]]:30 -> [[@LINE-1]]:32
+
+// RUN: clang-refactor-test rename-initiate -at=%s:7:30 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:30 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:7:22 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:22 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+
+// TODO: Implement TypeLocs for @compatibility_alias (rdar://29245831)
+// XFAIL: *
+void foo(I1Alias *object) { // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:17
+}
+
+@implementation I1 { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19
+ I1Alias *object; // CHECK3: rename [[@LINE]]:3 -> [[@LINE]]:10
+}
+
+-(const I1Alias *)foo:(I2Alias *)object { // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:16
+ // CHECK4: rename [[@LINE-1]]:24 -> [[@LINE-1]]:31
+ return (const I1Alias *)self->object; // CHECK3: rename [[@LINE]]:17 -> [[@LINE]]:24
+}
+
+@end
+
+@interface I3: I1Alias // CHECK3: rename [[@LINE]]:16 -> [[@LINE]]:23
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:21:10 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:25:3 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:28:9 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:30:17 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:35:16 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:28:24 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
diff --git a/test/Refactor/Rename/ObjCImplementationTURequests.m b/test/Refactor/Rename/ObjCImplementationTURequests.m
new file mode 100644
index 0000000..9486a8a
--- /dev/null
+++ b/test/Refactor/Rename/ObjCImplementationTURequests.m
@@ -0,0 +1,32 @@
+@interface ExplicitIVarsInInterface {
+ int _requiresImplementationTU;
+}
+
+@property int requiresImplementationTU;
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/Inputs/ObjCImplementationTURequestsImplementation.m" -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s
+// CHECK1: Implementation TU USR: 'c:objc(cs)ExplicitIVarsInInterface@_requiresImplementationTU'
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo -implementation-tu="%S/MissingFile.m" -dump-symbols %s 2>&1 | FileCheck --check-prefix=CHECK-ERR1 %s
+// CHECK-ERR1: failed to load implementation TU
+
+@interface NoNeedForImplementationTUs {
+ int _p1;
+}
+
+@property int p1;
+@property int p2;
+
+@end
+
+@implementation NoNeedForImplementationTUs {
+ int _p2;
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:16:7 -new-name=foo %s | FileCheck --check-prefix=CHECK-NO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:25:7 -new-name=foo %s | FileCheck --check-prefix=CHECK-NO %s
+// CHECK-NO-NOT: Implementation TU USR
diff --git a/test/Refactor/Rename/ObjCImplicitProperty.m b/test/Refactor/Rename/ObjCImplicitProperty.m
new file mode 100644
index 0000000..b587f41
--- /dev/null
+++ b/test/Refactor/Rename/ObjCImplicitProperty.m
@@ -0,0 +1,31 @@
+@interface I1
+
+-(int)p1; // CHECK1: rename [[@LINE]]:7 -> [[@LINE]]:9
+-(void)setP1:(int)x; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:13
+-(int)p2; // CHECK3: rename [[@LINE]]:7 -> [[@LINE]]:9
+
+@end
+
+@implementation I1
+
+- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 0;
+}
+
+- (void)foo: (I1 *)other {
+ self.p1 = // CHECK1: implicit-property [[@LINE]]:8 -> [[@LINE]]:10
+ // CHECK2: implicit-property [[@LINE-1]]:8 -> [[@LINE-1]]:13
+ self.p2; // CHECK3: implicit-property [[@LINE]]:19 -> [[@LINE]]:21
+ (void)other.p1; // CHECK1: implicit-property [[@LINE]]:15 -> [[@LINE]]:17
+ // CHECK2: implicit-property [[@LINE-1]]:15 -> [[@LINE-1]]:20
+
+ int x = [self p1]; // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19
+ [self setP1: x]; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:14
+ x = [other p2]; // CHECK3: rename [[@LINE]]:14 -> [[@LINE]]:16
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:7 -at=%s:19:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -at=%s:16:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:5:7 -at=%s:18:19 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
diff --git a/test/Refactor/Rename/ObjCMethod.m b/test/Refactor/Rename/ObjCMethod.m
new file mode 100644
index 0000000..d2b1ad3
--- /dev/null
+++ b/test/Refactor/Rename/ObjCMethod.m
@@ -0,0 +1,150 @@
+@interface Test
+
+- (void)foo; // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:12
+- (int)performAction:(int)action with:(int)value; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:34 -> [[@LINE]]:38
+
+@end
+
+@implementation Test
+
+- (void)foo { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:12
+}
+
+- (int)performAction:(int)action with:(int)value { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:34 -> [[@LINE]]:38
+ return action + value;
+}
+
++ (void)foo:(Test*)t { // CHECK1-NOT: rename [[@LINE]]
+ [t foo]; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:11
+ SEL s = @selector(foo);
+ [Test foo:t]; // CHECK1-NOT: rename [[@LINE]]
+ [t performAction: 2 with: 4]; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:21, [[@LINE]]:25 -> [[@LINE]]:29
+ SEL s1 = @selector(foo:);
+ SEL s2 = @selector(performAction:
+ with:);
+ SEL s3 = @selector(performAction:);
+ SEL s4 = @selector(performAction);
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:18:8 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -new-name=doSomething:to %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:13:8 -new-name=doSomething:to: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:21:8 -new-name=doSomething:to: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK2 %s
+
+
+
+
+@interface SuperClass
+
+- (void)foo; // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12
+- (int)compareTo:(SuperClass *)other with:(int)options; // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE]]:38 -> [[@LINE]]:42
+
+@end
+
+@implementation SuperClass
+
+- (void)foo { // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12
+ return;
+}
+
+- (int)compareTo:(SuperClass *)other with:(int)options { // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE]]:38 -> [[@LINE]]:42
+ return 0;
+}
+
+@end
+
+@interface SubClass : SuperClass
+
+- (void)foo; // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12
+
+@end
+
+@implementation SubClass
+
+- (void)foo { // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12
+ [super foo]; // CHECK-OVERRIDEFOO: rename [[@LINE]]:10 -> [[@LINE]]:13
+}
+
+@end
+
+@interface SubClassTheSecond : SubClass
+
+- (void)foo; // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12
+- (int)compareTo:(SuperClass *)other with:(int)options; // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE]]:38 -> [[@LINE]]:42
+
+@end
+
+@implementation SubClassTheSecond
+
+- (void)foo { // CHECK-OVERRIDEFOO: rename [[@LINE]]:9 -> [[@LINE]]:12
+ return;
+}
+- (int)compareTo:(SuperClass *)other // CHECK-OVERRIDECOMP: rename [[@LINE]]:8 -> [[@LINE]]:17, [[@LINE+1]]:8 -> [[@LINE+1]]:12
+ with:(int)options {
+ [other foo]; // CHECK-OVERRIDEFOO: rename [[@LINE]]:10 -> [[@LINE]]:13
+ return [super compareTo: other with: options]; // CHECK-OVERRIDECOMP: rename [[@LINE]]:17 -> [[@LINE]]:26, [[@LINE]]:34 -> [[@LINE]]:38
+}
+
+@end
+
+@interface UnrelatedClass
+
+- (void)foo; // CHECK-OVERRIDEFOO-NOT: rename [[@LINE]]
+
+@end
+
+@interface UnrelatedSubClass : UnrelatedClass
+// This method doesn't override SuperClass.foo, so verify that this occurrence
+// isn't renamed even though its selector is the same.
+- (void)foo; // CHECK-OVERRIDEFOO-NOT: rename [[@LINE]]
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:44:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:51:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:63:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:69:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:70:10 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:77:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:84:9 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+// RUN: clang-refactor-test rename-initiate -at=%s:89:10 -new-name=bar %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDEFOO %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:45:8 -new-name=a:b: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:55:8 -new-name=a:b: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:78:8 -new-name=a:b %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:87:9 -new-name=a:b: %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:90:17 -new-name=a:b %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:90:34 -new-name=a:b %s -Wno-objc-root-class | FileCheck --check-prefix=CHECK-OVERRIDECOMP %s
+
+// Don't allow implicit parameters:
+@interface Foo
+- (void)foo;
+@end
+
+@implementation Foo
+- (void)foo {
+ self = 0;
+}
+@end
+// RUN: not clang-refactor-test rename-initiate -at=%s:130:3 -new-name=foo %s -Wno-objc-root-class 2>&1 | FileCheck --check-prefix=CHECK-NORENAME %s
+// CHECK-NORENAME: could not rename symbol at the given location
+
+@interface EmptySelectorsRule_Psych
+
+- (void):(int)_ :(int) m:(int)z; // EMPTY-SELECTOR: rename [[@LINE]]:9 -> [[@LINE]]:9, [[@LINE]]:17 -> [[@LINE]]:17, [[@LINE]]:25 -> [[@LINE]]:25
+
+@end
+
+@implementation EmptySelectorsRule_Psych
+
+- (void) :(int)_ :(int)m :(int)z { // EMPTY-SELECTOR: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:18 -> [[@LINE]]:18, [[@LINE]]:26 -> [[@LINE]]:26
+ [self: 15:0 :3]; // EMPTY-SELECTOR: rename [[@LINE]]:10 -> [[@LINE]]:10, [[@LINE]]:14 -> [[@LINE]]:14, [[@LINE]]:17 -> [[@LINE]]:17
+}
+// RUN: clang-refactor-test rename-initiate -at=%s:139:9 -new-name=test:a:: %s -Wno-objc-root-class | FileCheck --check-prefix=EMPTY-SELECTOR %s
+
+@end
diff --git a/test/Refactor/Rename/ObjCMethodMacro.m b/test/Refactor/Rename/ObjCMethodMacro.m
new file mode 100644
index 0000000..c6d6322
--- /dev/null
+++ b/test/Refactor/Rename/ObjCMethodMacro.m
@@ -0,0 +1,28 @@
+#define FOO foo // CHECK1-NOT: rename [[@LINE]]
+// CHECK1-NOT: macro [[@LINE-1]]
+
+@interface I
+
+- (void)FOO; // CHECK1: macro [[@LINE]]:9 -> [[@LINE]]:9
+- (void)foo: (int)x FOO: (int)y; // CHECK2: macro [[@LINE]]:21 -> [[@LINE]]:21
+
+@end
+
+@implementation I
+
+- (void)foo { // CHECK1-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:12
+ [self FOO: 1 FOO: 2]; // CHECK2-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9
+}
+
+- (void)foo: (int)x foo: (int)y { // CHECK2-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:12, [[@LINE]]:21 -> [[@LINE]]:24
+ [self FOO]; // CHECK1-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:13:9 -new-name=bar %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:17:9 -new-name=foo:bar %s | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:1:13 -new-name=foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:6:9 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// CHECK-ERROR: could not rename symbol at the given location
diff --git a/test/Refactor/Rename/ObjCProperty.m b/test/Refactor/Rename/ObjCProperty.m
new file mode 100644
index 0000000..f0ed852
--- /dev/null
+++ b/test/Refactor/Rename/ObjCProperty.m
@@ -0,0 +1,303 @@
+// XFAIL: *
+// TODO: Remove or cut it down to one symbol rename.
+
+@interface I1
+
+@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property (readwrite, nonatomic) int p2; // CHECK2: rename [[@LINE]]:38 -> [[@LINE]]:40
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:38 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+@implementation I1
+
+- (void)foo:(I1 *)other {
+ self.p2 = // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+ self.p1; // CHECK1: rename [[@LINE]]:18 -> [[@LINE]]:20
+ (void)other.p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:15:18 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:16:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+@implementation I1 (gettersAndSetters)
+
+- (void)foo2:(I1 *)other {
+ int x = [self p1]; // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19
+ [self setP1: x]; // CHECK1: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14
+ [other setP2: // CHECK2: rename "setFoo" [[@LINE]]:10 -> [[@LINE]]:15
+ [other p2]]; // CHECK2: rename [[@LINE]]:12 -> [[@LINE]]:14
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:28:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:29:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:30:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:31:12 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+@interface I2
+
+@property int noImplementation; // CHECK3: rename [[@LINE]]:15 -> [[@LINE]]:31
+
+@end
+
+void noImplementationGetterSetters(I2 *object) {
+ object.noImplementation = 2; // CHECK3: rename [[@LINE]]:10 -> [[@LINE]]:26
+ int x = object.noImplementation; // CHECK3: rename [[@LINE]]:18 -> [[@LINE]]:34
+ [object setNoImplementation: x]; // CHECK3: rename "setFoo" [[@LINE]]:11 -> [[@LINE]]:30
+ (void)[object noImplementation]; // CHECK3: rename [[@LINE]]:17 -> [[@LINE]]:33
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:43:15 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:48:10 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:49:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:50:11 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:51:17 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+
+@interface I3
+
+@property (readonly) int noSetter; // CHECK4: rename [[@LINE]]:26 -> [[@LINE]]:34
+
+@end
+
+void noPropertySetter(I3 *object) {
+ (void)object.noSetter; // CHECK4: rename [[@LINE]]:16 -> [[@LINE]]:24
+ object.noSetter = 2; // CHECK4-NOT: rename [[@LINE]]
+ (void)[object noSetter]; // CHECK4: rename [[@LINE]]:17 -> [[@LINE]]:25
+ [object setNoSetter: 2]; // CHECK4-NOT: rename "setFoo" [[@LINE]]
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:62:26 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:67:16 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:69:17 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+
+@interface PropertyOverrides1: I1
+
+- (int)p1; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+- (void)setP1:(int)x; // CHECK1: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14
+- (int)p2; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+
+@end
+
+@implementation PropertyOverrides1 {
+ I1 *object;
+}
+
+- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return [super p1]; // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:19
+}
+- (void)setP1:(int)x { // CHECK1: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14
+ [object setP1: x]; // CHECK1: rename "setFoo" [[@LINE]]:11 -> [[@LINE]]:16
+ [super setP1: x]; // CHECK1: rename "setFoo" [[@LINE]]:10 -> [[@LINE]]:15
+}
+- (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return super.p2; // CHECK2: rename [[@LINE]]:16 -> [[@LINE]]:18
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:79:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:80:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:89:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:90:17 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:92:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:93:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:94:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:81:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:96:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:97:16 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+@interface PropertyOverrides2: I3
+
+- (int)noSetter; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:16
+- (void)setNoSetter:(int)x; // CHECK4-NOT: rename "setFoo" [[@LINE]]
+
+@end
+
+void getterOnlyOverrideWithoutImplementation(PropertyOverrides2 *object) {
+ (void)object.noSetter; // CHECK4: rename [[@LINE]]:16 -> [[@LINE]]:24
+ int _ = [object noSetter]; // CHECK4: rename [[@LINE]]:19 -> [[@LINE]]:27
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:115:8 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:121:16 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:122:19 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+
+@interface MismatchedPropertyOverrides: I1
+
+- (void)p1; // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:11
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:131:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+@interface ExplicitlyNamedGetterSetters1
+
+@property (getter=getP3) int p3; // CHECK5: rename [[@LINE]]:30 -> [[@LINE]]:32
+ // CHECK5GET: rename [[@LINE-1]]:19 -> [[@LINE-1]]:24
+@property (getter=a, setter=b:) int p4; // CHECK6: rename [[@LINE]]:37 -> [[@LINE]]:39
+ // CHECK6GET: rename [[@LINE-1]]:19 -> [[@LINE-1]]:20
+ // CHECK6SET: rename [[@LINE-2]]:29 -> [[@LINE-2]]:30
+@property (readonly, getter=local) bool isLocal; // CHECK7: rename [[@LINE]]:41 -> [[@LINE]]:48
+ // CHECK7GET: rename [[@LINE-1]]:29 -> [[@LINE-1]]:34
+@end
+
+@implementation ExplicitlyNamedGetterSetters1
+
+- (void)foo:(ExplicitlyNamedGetterSetters *)other {
+ self.p3 = 2; // CHECK5: rename [[@LINE]]:8 -> [[@LINE]]:10
+ [self p3]; // CHECK5-NOT: rename [[@LINE]]
+ [self setP3: // CHECK5: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14
+ [other getP3]]; // CHECK5-NOT: rename [[@LINE]]
+ // CHECK5GET: rename [[@LINE-1]]:12 -> [[@LINE-1]]:17
+
+ self.p4 = 3; // CHECK6: rename [[@LINE]]:8 -> [[@LINE]]:10
+ [self p4]; // CHECK6-NOT: rename [[@LINE]]
+ [self setP4: 2]; // CHECK6-NOT: rename "setFoo" [[@LINE]]
+ [self b: // CHECK6-NOT: rename "setFoo" [[@LINE]]
+ // CHECK6SET: rename [[@LINE-1]]:9 -> [[@LINE-1]]:10
+ [other a]]; // CHECK6-NOT: rename [[@LINE]]
+ // CHECK6GET: rename [[@LINE-1]]:12 -> [[@LINE-1]]:13
+
+ (void)self.isLocal; // CHECK7: rename [[@LINE]]:14 -> [[@LINE]]:21
+ [self isLocal]; // CHECK7-NOT: rename [[@LINE]]
+ [other local]; // CHECK7-NOT: rename [[@LINE]]
+ // CHECK7GET: rename [[@LINE-1]]:10 -> [[@LINE-1]]:15
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:139:30 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:151:8 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:153:9 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:141:37 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:157:8 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:144:41 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:165:14 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:139:19 -new-name=foo %s | FileCheck --check-prefix=CHECK5GET %s
+// RUN: clang-refactor-test rename-initiate -at=%s:154:12 -new-name=foo %s | FileCheck --check-prefix=CHECK5GET %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:141:19 -new-name=foo %s | FileCheck --check-prefix=CHECK6GET %s
+// RUN: clang-refactor-test rename-initiate -at=%s:162:12 -new-name=foo %s | FileCheck --check-prefix=CHECK6GET %s
+// RUN: clang-refactor-test rename-initiate -at=%s:141:29 -new-name=foo %s | FileCheck --check-prefix=CHECK6SET %s
+// RUN: clang-refactor-test rename-initiate -at=%s:160:9 -new-name=foo %s | FileCheck --check-prefix=CHECK6SET %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:144:29 -new-name=foo %s | FileCheck --check-prefix=CHECK7GET %s
+// RUN: clang-refactor-test rename-initiate -at=%s:167:10 -new-name=foo %s | FileCheck --check-prefix=CHECK7GET %s
+
+void ivars1(I1 *object) {
+ object->_p1 = 2; // CHECK1: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14
+ object->_p2 = // CHECK2: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14
+ object->_p1; // CHECK1: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28
+}
+
+void ivars2(ExplicitlyNamedGetterSetters1 *object) {
+ object->_p3 = // CHECK5: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14
+ object->_p4; // CHECK6: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28
+ object->_isLocal = 0; // CHECK7: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:19
+}
+
+void ivarsNoImplementation(I2 *object) {
+ object->_noImplementation = 4; // CHECK2-NOT: rename "_foo" [[@LINE]]
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:195:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:197:25 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:196:11 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:201:11 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:202:25 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:203:11 -new-name=foo %s | FileCheck --check-prefix=CHECK7 %s
+
+@interface ExplicitIVars
+
+@property int p5; // CHECK8: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property(readonly) int p6; // CHECK9: rename [[@LINE]]:25 -> [[@LINE]]:27
+
+@end
+
+@implementation ExplicitIVars {
+ int _p5; // CHECK8: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+ int _p6; // CHECK9: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+}
+
+- (void)foo:(ExplicitIVars *)other {
+ _p5 = // CHECK8: rename "_foo" [[@LINE]]:3 -> [[@LINE]]:6
+ other->_p6; // CHECK9: rename "_foo" [[@LINE]]:16 -> [[@LINE]]:19
+ other->_p6 = // CHECK9: rename "_foo" [[@LINE]]:10 -> [[@LINE]]:13
+ _p5; // CHECK8: rename "_foo" [[@LINE]]:16 -> [[@LINE]]:19
+ self.p5 = // CHECK8: rename [[@LINE]]:8 -> [[@LINE]]:10
+ other.p6; // CHECK9: rename [[@LINE]]:18 -> [[@LINE]]:20
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:220:15 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:226:7 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:231:3 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:234:16 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:235:8 -new-name=foo %s | FileCheck --check-prefix=CHECK8 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:221:25 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:227:7 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:232:16 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:233:10 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:236:18 -new-name=foo %s | FileCheck --check-prefix=CHECK9 %s
+
+@interface ExplicitIVarsInInterface {
+ int _p7; // CHECK10: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+ @public
+ int _p8; // CHECK11: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+}
+
+@property int p7; // CHECK10: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property int p8; // CHECK11: rename [[@LINE]]:15 -> [[@LINE]]:17
+
+@end
+
+@implementation ExplicitIVarsInInterface
+@end
+
+void explicitIVarsInInterface(ExplicitIVarsInInterface* object) {
+ object->_p7 = // CHECK10: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14
+ object->_p8; // CHECK11: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:254:7 -new-name=foo %s | FileCheck --check-prefix=CHECK10 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:259:15 -new-name=foo %s | FileCheck --check-prefix=CHECK10 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:268:11 -new-name=foo %s | FileCheck --check-prefix=CHECK10 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:256:7 -new-name=foo %s | FileCheck --check-prefix=CHECK11 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:260:15 -new-name=foo %s | FileCheck --check-prefix=CHECK11 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:269:25 -new-name=foo %s | FileCheck --check-prefix=CHECK11 %s
+
+@interface GetterSetterDefinedInInterfaceOnly
+
+@property int p9; // CHECK12: rename [[@LINE]]:15 -> [[@LINE]]:17
+
+@end
+
+@implementation GetterSetterDefinedInInterfaceOnly
+
+- (int)p9 { return 0; } // CHECK12: rename [[@LINE]]:8 -> [[@LINE]]:10
+- (void)setP9:(int)x { } // CHECK12: rename "setFoo" [[@LINE]]:9 -> [[@LINE]]:14
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:288:8 -new-name=foo %s | FileCheck --check-prefix=CHECK12 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:289:9 -new-name=foo %s | FileCheck --check-prefix=CHECK12 %s
+
+void matchingGetterSetterSelector() {
+ @selector(p1); // CHECK1: selector [[@LINE]]:13 -> [[@LINE]]:15
+ @selector(setP1:); // CHECK1: selector "setFoo" [[@LINE]]:13 -> [[@LINE]]:18
+ @selector(setP1); // CHECK1-NOT: selector "setFoo" [[@LINE]]
+}
diff --git a/test/Refactor/Rename/ObjCPropertyDynamic.m b/test/Refactor/Rename/ObjCPropertyDynamic.m
new file mode 100644
index 0000000..8ac7aac
--- /dev/null
+++ b/test/Refactor/Rename/ObjCPropertyDynamic.m
@@ -0,0 +1,41 @@
+@interface DynamicProperty
+
+@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property(readonly) int p2; // CHECK2: rename [[@LINE]]:25 -> [[@LINE]]:27
+
+@end
+
+@implementation DynamicProperty
+
+@dynamic p1; // CHECK1: rename [[@LINE]]:10 -> [[@LINE]]:12
+
+@dynamic p2; // CHECK2: rename [[@LINE]]:10 -> [[@LINE]]:12
+
+- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 1;
+}
+// TODO: Remove
+- (void)setP1:(int)x { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:14
+}
+
+- (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 2;
+}
+
+- (void)foo:(DynamicProperty *)other {
+ self.p1 = // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ other.p2; // CHECK2: rename [[@LINE]]:19 -> [[@LINE]]:21
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:14:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:18:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:26:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:12:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:21:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:27:19 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m b/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m
new file mode 100644
index 0000000..fda9e82
--- /dev/null
+++ b/test/Refactor/Rename/ObjCPropertyIVarInInterfaceWithoutImplementation.m
@@ -0,0 +1,28 @@
+@interface ExplicitIVarsInInterface {
+ int _p1; // CHECK1: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+ @public
+ int _p2; // CHECK2: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+}
+
+@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property int p2; // CHECK2: rename [[@LINE]]:15 -> [[@LINE]]:17
+
+@end
+
+void explicitIVarsInInterface(ExplicitIVarsInInterface* object) {
+ object->_p7 = // CHECK1: rename "_foo" [[@LINE]]:11 -> [[@LINE]]:14
+ object->_p8; // CHECK2: rename "_foo" [[@LINE]]:25 -> [[@LINE]]:28
+}
+
+// XFAIL: *
+// This test is currently disabled as renaming can't initiate a property
+// renaming operation in a TU without @implementation.
+// rdar://29329980
+
+// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:7:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:13:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:7 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:8:15 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:14:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Rename/ObjCPropertyInCategory.m b/test/Refactor/Rename/ObjCPropertyInCategory.m
new file mode 100644
index 0000000..9c8bc79
--- /dev/null
+++ b/test/Refactor/Rename/ObjCPropertyInCategory.m
@@ -0,0 +1,45 @@
+@interface I1
+
+@end
+
+@interface I1 (Category)
+
+@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property(readonly) int p2; // CHECK2: rename [[@LINE]]:25 -> [[@LINE]]:27
+
+- (int)p1; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+
+@end
+
+@implementation I1 (Category)
+
+@dynamic p2; // CHECK2: rename [[@LINE]]:10 -> [[@LINE]]:12
+
+- (int)p1 { // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 1;
+}
+// TODO: Remove
+- (void)setP1:(int)x { // CHECK1: rename [[@LINE]]:9 -> [[@LINE]]:14
+}
+
+- (int)p2 { // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:10
+ return 2;
+}
+
+- (void)foo:(I1 *)other {
+ self.p1 = // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ other.p2; // CHECK2: rename [[@LINE]]:19 -> [[@LINE]]:21
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:7:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:18:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:22:9 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:30:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:8:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:16:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:25:8 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:31:19 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
diff --git a/test/Refactor/Rename/ObjCPropertyMacro.m b/test/Refactor/Rename/ObjCPropertyMacro.m
new file mode 100644
index 0000000..9d3ffa3
--- /dev/null
+++ b/test/Refactor/Rename/ObjCPropertyMacro.m
@@ -0,0 +1,21 @@
+#define IMPLICIT implicit
+#define SETIMPLICIT setImplicit
+
+@interface I
+
+- (int)IMPLICIT;
+- (void)setImplicit:(int)x; // CHECK1: rename [[@LINE]]
+
+@end
+
+@implementation I
+
+- (void)foo {
+ self.implicit; // CHECK1-NEXT: implicit-property [[@LINE]]
+ self.IMPLICIT; // CHECK1-NEXT: implicit-property in macro [[@LINE]]
+ self.IMPLICIT = 2; // CHECK1-NEXT: implicit-property in macro [[@LINE]]
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:7:9 -new-name=bar %s | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/Refactor/Rename/ObjCPropertySynthesize.m b/test/Refactor/Rename/ObjCPropertySynthesize.m
new file mode 100644
index 0000000..2b12b6f
--- /dev/null
+++ b/test/Refactor/Rename/ObjCPropertySynthesize.m
@@ -0,0 +1,112 @@
+// XFAIL: *
+// TODO: Remove or cut it down to one symbol rename.
+
+@interface SynthesizedIVars
+
+@property int p1; // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property(readonly) int p2; // CHECK2PROP: rename [[@LINE]]:25 -> [[@LINE]]:27
+
+@end
+
+@implementation SynthesizedIVars
+
+@synthesize p1 = _p1; // CHECK1: rename [[@LINE]]:13 -> [[@LINE]]:15
+ // CHECK1: rename "_foo" [[@LINE-1]]:18 -> [[@LINE-1]]:21
+
+// The rename of ivar 'p2_' shouldn't initiate the rename of property 'p2'
+// because it doesn't follow the default naming convention.
+@synthesize p2 = p2_; // CHECK2PROP: rename [[@LINE]]:13 -> [[@LINE]]:15
+ // CHECK2IVAR: rename [[@LINE-1]]:18 -> [[@LINE-1]]:21
+
+- (void)foo:(SynthesizedIVars *)other {
+ _p1 = // CHECK1: rename "_foo" [[@LINE]]:3 -> [[@LINE]]:6
+ other->p2_; // CHECK2IVAR: rename [[@LINE]]:16 -> [[@LINE]]:19
+ other->p2_ = // CHECK2IVAR: rename [[@LINE]]:10 -> [[@LINE]]:13
+ _p1; // CHECK1: rename "_foo" [[@LINE]]:16 -> [[@LINE]]:19
+ self.p1 = // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+ other.p2; // CHECK2PROP: rename [[@LINE]]:18 -> [[@LINE]]:20
+}
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:13 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:10:18 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:19:3 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:22:16 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:23:8 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:25 -new-name=foo %s | FileCheck --check-prefix=CHECK2PROP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:15:13 -new-name=foo %s | FileCheck --check-prefix=CHECK2PROP %s
+// RUN: clang-refactor-test rename-initiate -at=%s:24:18 -new-name=foo %s | FileCheck --check-prefix=CHECK2PROP %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:15:18 -new-name=foo %s | FileCheck --check-prefix=CHECK2IVAR %s
+// RUN: clang-refactor-test rename-initiate -at=%s:20:16 -new-name=foo %s | FileCheck --check-prefix=CHECK2IVAR %s
+// RUN: clang-refactor-test rename-initiate -at=%s:21:10 -new-name=foo %s | FileCheck --check-prefix=CHECK2IVAR %s
+
+@interface SynthesizedExplicitIVars {
+ int _p3; // CHECK3: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+}
+
+@property int p3; // CHECK3: rename [[@LINE]]:15 -> [[@LINE]]:17
+@property int p4; // CHECK4: rename [[@LINE]]:15 -> [[@LINE]]:17
+
+@end
+
+@implementation SynthesizedExplicitIVars {
+ int _p4; // CHECK4: rename "_foo" [[@LINE]]:7 -> [[@LINE]]:10
+}
+
+@synthesize p3 = _p3; // CHECK3: rename [[@LINE]]:13 -> [[@LINE]]:15
+ // CHECK3: rename "_foo" [[@LINE-1]]:18 -> [[@LINE-1]]:21
+@synthesize p4 = _p4; // CHECK4: rename [[@LINE]]:13 -> [[@LINE]]:15
+ // CHECK4: rename "_foo" [[@LINE-1]]:18 -> [[@LINE-1]]:21
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:45:7 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:48:15 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:57:13 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:57:18 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:49:15 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:54:7 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:59:13 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:59:18 -new-name=foo %s | FileCheck --check-prefix=CHECK4 %s
+
+@interface SynthesizedWithoutIVarName
+
+@property int p5; // CHECK5: rename [[@LINE]]:15 -> [[@LINE]]:17
+
+@end
+
+@implementation SynthesizedWithoutIVarName {
+ int _p5; // CHECK5-NOT: rename "" [[@LINE]]
+ // CHECK5-NOT: rename [[@LINE-1]]
+ int p5; // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:9
+}
+
+@synthesize p5; // CHECK5: rename [[@LINE]]:13 -> [[@LINE]]:15
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:76:15 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:83:7 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:86:13 -new-name=foo %s | FileCheck --check-prefix=CHECK5 %s
+
+@interface A
+@property int p6;
+@end
+
+@interface C : A
+@end
+
+@implementation C
+
+@synthesize p6;
+// CHECK6: Renaming 4 symbols
+// CHECK6-NEXT: 'c:objc(cs)A(py)p6'
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:103:13 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK6 %s
diff --git a/test/Refactor/Rename/ObjCProtocol.m b/test/Refactor/Rename/ObjCProtocol.m
new file mode 100644
index 0000000..661d83b
--- /dev/null
+++ b/test/Refactor/Rename/ObjCProtocol.m
@@ -0,0 +1,67 @@
+@protocol P1, // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:13
+ P2; // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:13
+
+@protocol P1 // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:13
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:1:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:11 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:2:11 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+void protocolExpressions(id foo) {
+ (void)@protocol(P1); // CHECK1: rename [[@LINE]]:19 -> [[@LINE]]:21
+ [foo p: @protocol(P2)]; // CHECK2: rename [[@LINE]]:21 -> [[@LINE]]:23
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:12:19 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:13:21 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+void qualifiedId(id<P1> foo) { // CHECK1: rename [[@LINE]]:21 -> [[@LINE]]:23
+ id<P2, P1> bar = // CHECK2: rename [[@LINE]]:6 -> [[@LINE]]:8
+ // CHECK1: rename [[@LINE-1]]:10 -> [[@LINE-1]]:12
+ (id<P1, P2, P1>)foo; // CHECK1: rename [[@LINE]]:24 -> [[@LINE]]:26
+ // CHECK2: rename [[@LINE-1]]:28 -> [[@LINE-1]]:30
+ // CHECK1: rename [[@LINE-2]]:32 -> [[@LINE-2]]:34
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:19:21 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:20:6 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:20:10 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:22:24 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:22:28 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:22:32 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+typedef id<P1> TypedefQualifiedID; // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14
+
+// RUN: clang-refactor-test rename-initiate -at=%s:34:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+
+@interface I1<P1> // CHECK1: rename [[@LINE]]:15 -> [[@LINE]]:17
+@end
+
+@protocol P3 < P1> // CHECK3: rename [[@LINE]]:11 -> [[@LINE]]:13
+ // CHECK1: rename [[@LINE-1]]:16 -> [[@LINE-1]]:18
+@end
+
+@interface I1 (Cat) <P2, P3> // CHECK2: rename [[@LINE]]:22 -> [[@LINE]]:24
+@end // CHECK3: rename [[@LINE-1]]:26 -> [[@LINE-1]]:28
+
+// RUN: clang-refactor-test rename-initiate -at=%s:38:15 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:41:16 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:45:22 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:41:11 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:45:26 -new-name=foo %s | FileCheck --check-prefix=CHECK3 %s
+
+typedef I1<P1, P2> * TypedefI1; // CHECK1: rename [[@LINE]]:12 -> [[@LINE]]:14
+ // CHECK2: rename [[@LINE-1]]:16 -> [[@LINE-1]]:18
+
+void qualifiedClassPointer(I1<P1> *x) { // CHECK1: rename [[@LINE]]:31 -> [[@LINE]]:33
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:54:12 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:57:31 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:54:16 -new-name=foo %s | FileCheck --check-prefix=CHECK2 %s
+
+void protocolTypeof(typeof(@protocol(P1)) *bar) { // CHECK1: rename [[@LINE]]:38 -> [[@LINE]]:40
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:64:38 -new-name=foo %s | FileCheck --check-prefix=CHECK1 %s
diff --git a/test/Refactor/Rename/ProhibitedDeclarations.cpp b/test/Refactor/Rename/ProhibitedDeclarations.cpp
new file mode 100644
index 0000000..b93f11d
--- /dev/null
+++ b/test/Refactor/Rename/ProhibitedDeclarations.cpp
@@ -0,0 +1,72 @@
+void dontRenameBuiltins(int x) {
+ __builtin_assume(x != 0);
+ __builtin_trap();
+}
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:2:3 -at=%s:3:3 -new-name=foo %s 2>&1 | FileCheck %s
+// CHECK: error: could not rename symbol at the given location
+
+// RUN: not clang-refactor-test list-actions -at=%s:2:3 %s 2>&1 | FileCheck --check-prefix=CHECK-BUILTIN %s
+// CHECK-BUILTIN: Failed to initiate 1 actions because:
+// CHECK-BUILTIN-NEXT: Rename: '__builtin_assume' is a builtin function that cannot be renamed
+// CHECK-BUILTIN-NEXT: No refactoring actions are available at the given location
+
+#include <system-header.h>
+
+void dontRenameSystemSymbols() {
+ systemFunction();
+}
+// RUN: not clang-refactor-test rename-initiate -at=%s:17:3 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s
+// CHECK-SYSTEM: 'systemFunction' cannot be renamed because it is declared in a system header
+
+struct External {
+ static void foo();
+} __attribute__((external_source_symbol(language="Swift")));
+
+void dontRenameExternalSourceSymbols() {
+ External::foo();
+}
+// RUN: not clang-refactor-test rename-initiate -at=%s:27:3 -new-name=foo %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-EXTERNAL1 %s
+// CHECK-EXTERNAL1: 'External' is declared in a Swift file; rename can be initiated in a Swift file only
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:27:13 -new-name=foo %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-EXTERNAL2 %s
+// CHECK-EXTERNAL2: 'foo' is declared in a Swift file; rename can be initiated in a Swift file only
+
+// Ensure that operators can't be renamed:
+struct Stream {
+};
+
+Stream &operator <<(Stream &, int);
+
+void renameArgsNotOperator(Stream x) { // CHECK-OP-X: rename local [[@LINE]]:35 -> [[@LINE]]:36
+ int y = 0; // CHECK-OP-Y: rename local [[@LINE]]:7 -> [[@LINE]]:8
+ x << // CHECK-OP-X: rename local [[@LINE]]:3 -> [[@LINE]]:4
+ y << // CHECK-OP-Y: rename local [[@LINE]]:3 -> [[@LINE]]:4
+ y; // CHECK-OP-Y: rename local [[@LINE]]:3 -> [[@LINE]]:4
+}
+// RUN: clang-refactor-test rename-initiate -at=%s:43:3 -new-name=foo %s | FileCheck --check-prefixes=CHECK-OP-X %s
+// RUN: clang-refactor-test rename-initiate -at=%s:44:3 -at=%s:45:3 -new-name=foo %s | FileCheck --check-prefixes=CHECK-OP-Y %s
+
+struct SystemStruct;
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:50:8 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM2 %s
+// CHECK-SYSTEM2: 'SystemStruct' cannot be renamed because it is declared in a system header
+
+typedef struct SystemStruct SystemTypedef;
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:55:29 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM3 %s
+// CHECK-SYSTEM3: 'SystemTypedef' cannot be renamed because it is declared in a system header
+
+enum SystemEnum;
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:60:6 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM4 %s
+// CHECK-SYSTEM4: 'SystemEnum' cannot be renamed because it is declared in a system header
+
+void systemFunction();
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:65:6 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s
+
+int systemVariable;
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:69:5 -new-name=foo %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM5 %s
+// CHECK-SYSTEM5: 'systemVariable' cannot be renamed because it is declared in a system header
diff --git a/test/Refactor/Rename/ProhibitedDeclarations.m b/test/Refactor/Rename/ProhibitedDeclarations.m
new file mode 100644
index 0000000..cb91a6b
--- /dev/null
+++ b/test/Refactor/Rename/ProhibitedDeclarations.m
@@ -0,0 +1,35 @@
+@protocol P1
+@end
+
+void dontRenameProtocol() {
+ Protocol *p = @protocol(P1);
+}
+// RUN: not clang-refactor-test rename-initiate -at=%s:5:3 -new-name=foo %s 2>&1 | FileCheck %s
+// CHECK: error: could not rename symbol at the given location
+
+#include <objc-system-header.h>
+
+@interface MyClass: MySystemClass
+
+- (void)someMethod:(int)x with:(int)y;
+
+@end
+
+@implementation MyClass
+
+- (void)someMethod:(int)x with:(int)y {
+}
+
+@end
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:14:9 -at=%s:20:9 -at=%s:28:9 -new-name=foo:bar %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s
+// CHECK-SYSTEM: method 'someMethod:with:' cannot be renamed because it overrides a method declared in a system framework
+
+@interface MySubClass: MyClass
+@end
+@implementation MySubClass
+- (void)someMethod:(int)x with:(int)y {
+}
+@end
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:31:9 -new-name=foo:bar %s -isystem %S/Inputs 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYSTEM %s
diff --git a/test/Refactor/Rename/TemplateClassInstantiation.cpp b/test/Refactor/Rename/TemplateClassInstantiation.cpp
new file mode 100644
index 0000000..48b7603
--- /dev/null
+++ b/test/Refactor/Rename/TemplateClassInstantiation.cpp
@@ -0,0 +1,39 @@
+template <typename T>
+class Foo { /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ T foo(T arg, T& ref, T* ptr) {
+ T value;
+ int number = 42;
+ value = (T)number;
+ value = static_cast<T>(number);
+ return value;
+ }
+ static void foo(T value) {}
+ T member;
+};
+
+template <typename T>
+void func() {
+ Foo<T> obj; /* Test 2 */ // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ obj.member = T();
+ Foo<T>::foo(); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+}
+
+int main() {
+ Foo<int> i; /* Test 3 */ // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ i.member = 0;
+ Foo<int>::foo(0); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+
+ Foo<bool> b; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ b.member = false;
+ Foo<bool>::foo(false); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:2:7 -new-name=Bar %s -fno-delayed-template-parsing | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:17:3 -new-name=Bar %s -fno-delayed-template-parsing | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:25:3 -new-name=Bar %s -fno-delayed-template-parsing | FileCheck %s
diff --git a/test/Refactor/Rename/TemplateParameters.cpp b/test/Refactor/Rename/TemplateParameters.cpp
new file mode 100644
index 0000000..2dbff69
--- /dev/null
+++ b/test/Refactor/Rename/TemplateParameters.cpp
@@ -0,0 +1,25 @@
+template <typename T, int x> // CHECK1: rename local [[@LINE]]:20 -> [[@LINE]]:21
+class Foo { // CHECK2: rename local [[@LINE-1]]:27 -> [[@LINE-1]]:28
+
+T func(); // CHECK1-NEXT: rename local [[@LINE]]:1 -> [[@LINE]]:2
+
+int array[x]; // CHECK2-NEXT: rename local [[@LINE]]:11 -> [[@LINE]]:12
+};
+
+// CHECK1-NOT: rename
+// CHECK2-NOT: rename
+
+// RUN: clang-refactor-test rename-initiate -at=%s:1:20 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:1:27 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK2 %s
+
+template<typename T, int x>
+T Foo<T, x>::func() { return T(); }
+
+template <template<typename> class H, typename S> // CHECK3: rename local [[@LINE]]:36 -> [[@LINE]]:37
+// CHECK4: rename local [[@LINE-1]]:48 -> [[@LINE-1]]:49
+void templateTemplateParam(const H<S> &value) { // CHECK3-NEXT: rename local [[@LINE]]:34 -> [[@LINE]]:35
+// CHECK4-NEXT: rename local [[@LINE-1]]:36 -> [[@LINE-1]]:37
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:18:36 -at=%s:20:34 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:18:48 -at=%s:20:36 -new-name=U %s -fno-delayed-template-parsing | FileCheck --check-prefix=CHECK4 %s
diff --git a/test/Refactor/Rename/TemplateTypename.cpp b/test/Refactor/Rename/TemplateTypename.cpp
new file mode 100644
index 0000000..da79f67
--- /dev/null
+++ b/test/Refactor/Rename/TemplateTypename.cpp
@@ -0,0 +1,24 @@
+template <typename T /* Test 1 */> // CHECK: rename local [[@LINE]]:20 -> [[@LINE]]:21
+class Foo {
+T foo(T arg, T& ref, T* /* Test 2 */ ptr) { // CHECK: rename local [[@LINE]]:1 -> [[@LINE]]:2
+ // CHECK: rename local [[@LINE-1]]:7 -> [[@LINE-1]]:8
+ // CHECK: rename local [[@LINE-2]]:14 -> [[@LINE-2]]:15
+ // CHECK: rename local [[@LINE-3]]:22 -> [[@LINE-3]]:23
+ T value; // CHECK: rename local [[@LINE]]:3 -> [[@LINE]]:4
+ int number = 42;
+ value = (T)number; // CHECK: rename local [[@LINE]]:12 -> [[@LINE]]:13
+ value = static_cast<T /* Test 3 */>(number); // CHECK: rename local [[@LINE]]:23 -> [[@LINE]]:24
+ return value;
+}
+
+static void foo(T value) {} // CHECK: rename local [[@LINE]]:17 -> [[@LINE]]:18
+
+T member; // CHECK: rename local [[@LINE]]:1 -> [[@LINE]]:2
+};
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:1:20 -new-name=U %s -fno-delayed-template-parsing | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:3:22 -new-name=U %s -fno-delayed-template-parsing | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:10:23 -new-name=U %s -fno-delayed-template-parsing | FileCheck %s
diff --git a/test/Refactor/Rename/TemplatedClassFunction.cpp b/test/Refactor/Rename/TemplatedClassFunction.cpp
new file mode 100644
index 0000000..7eb3e44
--- /dev/null
+++ b/test/Refactor/Rename/TemplatedClassFunction.cpp
@@ -0,0 +1,19 @@
+template <typename T>
+class A {
+public:
+ void foo() /* Test 1 */ {} // CHECK: rename [[@LINE]]:8 -> [[@LINE]]:11
+};
+
+int main(int argc, char **argv) {
+ A<int> a;
+ a.foo(); /* Test 2 */ // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ return 0;
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -new-name=bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:9:5 -new-name=bar %s | FileCheck %s
+//
+// Currently unsupported test.
+// XFAIL: *
diff --git a/test/Refactor/Rename/TransparentTypedef.m b/test/Refactor/Rename/TransparentTypedef.m
new file mode 100644
index 0000000..1a2dcb1
--- /dev/null
+++ b/test/Refactor/Rename/TransparentTypedef.m
@@ -0,0 +1,49 @@
+#define NS_ENUM(_name, _type) enum _name:_type _name; enum _name : _type
+// CHECK1: Renaming 1 symbols
+// CHECK1-NEXT: 'c:@E@AnotherEnum'
+typedef NS_ENUM(AnotherEnum, int) { // CHECK1: rename [[@LINE]]:17 -> [[@LINE]]:28
+ AnotherEnumFirst = 0,
+};
+AnotherEnum anotherT; // CHECK1: rename [[@LINE]]:1 -> [[@LINE]]:12
+enum AnotherEnum anotherE; // CHECK1: rename [[@LINE]]:6 -> [[@LINE]]:17
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:17 -at=%s:7:1 -at=%s:8:6 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK1 %s
+
+#define TRANSPARENT(_name) struct _name _name; struct _name
+#define OPAQUE(_name) struct _name *_name; struct _name
+
+// CHECK2: Renaming 1 symbols
+// CHECK2-NEXT: 'c:@S@AStruct'
+typedef TRANSPARENT(AStruct) { // CHECK2: rename [[@LINE]]:21 -> [[@LINE]]:28
+ int x;
+};
+
+AStruct aStructT; // CHECK2: rename [[@LINE]]:1 -> [[@LINE]]:8
+struct AStruct aStructS; // CHECK2: rename [[@LINE]]:8 -> [[@LINE]]:15
+
+// RUN: clang-refactor-test rename-initiate -at=%s:17:21 -at=%s:21:1 -at=%s:22:8 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK2 %s
+
+// CHECK3: Renaming 1 symbols
+// CHECK3-NEXT: 'c:{{.*}}TransparentTypedef.m@T@Separate'
+// CHECK4: Renaming 1 symbols
+// CHECK4-NEXT: 'c:@S@Separate'
+
+typedef OPAQUE(Separate) { // CHECK3: rename [[@LINE]]:16 -> [[@LINE]]:24
+ int x; // CHECK4: rename [[@LINE-1]]:16 -> [[@LINE-1]]:24
+};
+
+
+Separate separateT; // CHECK3: rename [[@LINE]]:1 -> [[@LINE]]:9
+struct Separate separateE; // CHECK4: rename [[@LINE]]:8 -> [[@LINE]]:16
+
+// RUN: clang-refactor-test rename-initiate -at=%s:36:1 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:31:16 -at=%s:37:8 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK4 %s
+
+#include "Inputs/TransparentEnum.h"
+
+// CHECK5: 'c:@E@AnotherEnum2'
+typedef TRANSPARENT_ENUM(AnotherEnum2, int) { // CHECK5: rename [[@LINE]]:26 -> [[@LINE]]:38
+ EnumThing = 0, // CHECK6: [[@LINE]]:3 -> [[@LINE]]:12
+};
+// RUN: clang-refactor-test rename-initiate -at=%s:45:26 -new-name=foo -dump-symbols %s | FileCheck --check-prefix=CHECK5 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:46:3 -new-name=foo %s | FileCheck --check-prefix=CHECK6 %s
diff --git a/test/Refactor/Rename/TypedefTag.cpp b/test/Refactor/Rename/TypedefTag.cpp
new file mode 100644
index 0000000..390350c
--- /dev/null
+++ b/test/Refactor/Rename/TypedefTag.cpp
@@ -0,0 +1,21 @@
+struct S1 { }; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:10
+
+typedef struct S2 { } S3; // CHECK2: rename [[@LINE]]:16 -> [[@LINE]]:18
+// CHECK3: rename [[@LINE-1]]:23 -> [[@LINE-1]]:25
+
+void func(struct S1, // CHECK1-NEXT: rename [[@LINE]]:18 -> [[@LINE]]:20
+ struct S2, // CHECK2-NEXT: rename [[@LINE]]:18 -> [[@LINE]]:20
+ S3) { // CHECK3-NEXT: rename [[@LINE]]:11 -> [[@LINE]]:13
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -at=%s:6:18 -new-name=Bar %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:3:16 -at=%s:7:18 -new-name=Bar %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:3:23 -at=%s:8:11 -new-name=Bar %s | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: clang-refactor-test rename-initiate -at=%s:1:8 -at=%s:6:18 -new-name=Bar %s -x c | FileCheck --check-prefix=CHECK1 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:3:16 -at=%s:7:18 -new-name=Bar %s -x c | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-initiate -at=%s:3:23 -at=%s:8:11 -new-name=Bar %s -x c | FileCheck --check-prefix=CHECK3 %s
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:3:9 -at=%s:6:11 -at=%s:7:11 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=ERROR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:3:9 -at=%s:6:11 -at=%s:7:11 -new-name=Bar %s -x c 2>&1 | FileCheck --check-prefix=ERROR %s
+// ERROR: could not rename symbol at the given location
diff --git a/test/Refactor/Rename/USRForSymbols.m b/test/Refactor/Rename/USRForSymbols.m
new file mode 100644
index 0000000..1f1efe8
--- /dev/null
+++ b/test/Refactor/Rename/USRForSymbols.m
@@ -0,0 +1,16 @@
+@interface I1
+
+@property int p1;
+
+@end
+
+@implementation I1
+
+@end
+
+// CHECK: 3 symbols
+// CHECK: 'c:objc(cs)I1(py)p1'
+// CHECK: 'c:objc(cs)I1(im)p1'
+// CHECK: 'c:objc(cs)I1(im)setP1:'
+
+// RUN: clang-refactor-test rename-initiate -at=%s:3:15 -new-name=foo -dump-symbols %s | FileCheck %s
diff --git a/test/Refactor/Rename/UserDefinedConversion.cpp b/test/Refactor/Rename/UserDefinedConversion.cpp
new file mode 100644
index 0000000..24614bf
--- /dev/null
+++ b/test/Refactor/Rename/UserDefinedConversion.cpp
@@ -0,0 +1,23 @@
+class Foo { /* Test 1 */ // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:10
+public:
+ Foo() {} // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+};
+
+class Baz {
+public:
+ operator Foo() /* Test 2 */ const { // CHECK: rename [[@LINE]]:12 -> [[@LINE]]:15
+ Foo foo; // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+ return foo;
+ }
+};
+
+int main() {
+ Baz boo;
+ Foo foo = static_cast<Foo>(boo); // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:6
+ return 0; // CHECK: rename [[@LINE-1]]:25 -> [[@LINE-1]]:28
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:1:7 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:8:12 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/UsingDecl.cpp b/test/Refactor/Rename/UsingDecl.cpp
new file mode 100644
index 0000000..99eb1f4
--- /dev/null
+++ b/test/Refactor/Rename/UsingDecl.cpp
@@ -0,0 +1,46 @@
+
+namespace ns { // CHECK4: rename [[@LINE]]:11 -> [[@LINE]]:13
+
+struct Struct { }; // CHECK1: rename [[@LINE]]:8 -> [[@LINE]]:14
+
+void func(); // CHECK2: rename [[@LINE]]:6 -> [[@LINE]]:10
+
+void overload1(int x);
+void overload1(double y); // CHECK3: rename [[@LINE]]:6 -> [[@LINE]]:15
+
+}
+
+using ns::Struct; // CHECK1: rename [[@LINE]]:11 -> [[@LINE]]:17
+
+// RUN: clang-refactor-test rename-initiate -at=%s:13:11 -new-name=x %s | FileCheck --check-prefix=CHECK1 %s
+
+using ns::func; // CHECK2: rename [[@LINE]]:11 -> [[@LINE]]:15
+
+// RUN: clang-refactor-test rename-initiate -at=%s:17:11 -new-name=x %s | FileCheck --check-prefix=CHECK2 %s
+
+using ns::overload1; // CHECK3: rename [[@LINE]]:11 -> [[@LINE]]:20
+
+// RUN: clang-refactor-test rename-initiate -at=%s:21:11 -new-name=x %s | FileCheck --check-prefix=CHECK3 %s
+
+using namespace ns; // CHECK4: rename [[@LINE]]:17 -> [[@LINE]]:19
+
+// RUN: clang-refactor-test rename-initiate -at=%s:25:17 -new-name=x %s | FileCheck --check-prefix=CHECK4 %s
+
+struct UsingConstructor {
+ template<class T>
+ UsingConstructor(T, typename T::Q);
+};
+struct UsingConstructorHere : UsingConstructor {
+using UsingConstructor::UsingConstructor; // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:23
+// CHECK5: rename [[@LINE-1]]:25 -> [[@LINE-1]]:41
+};
+// RUN: clang-refactor-test rename-initiate -at=%s:34:25 -new-name=x %s -std=c++14 | FileCheck --check-prefix=CHECK5 %s
+
+template<typename T>
+struct S : T {
+ using T::T; // CHECK6: rename local [[@LINE]]:12 -> [[@LINE]]:13
+// RUN: clang-refactor-test rename-initiate -at=%s:41:12 -new-name=x %s -std=c++14 | FileCheck --check-prefix=CHECK6 %s
+ using typename T::Foo;
+// RUN: not clang-refactor-test rename-initiate -at=%s:43:12 -new-name=x %s -std=c++14 2>&1 | FileCheck --check-prefix=CHECK-UNRESOLVED %s
+// CHECK-UNRESOLVED: error: could not rename symbol at the given location
+};
diff --git a/test/Refactor/Rename/Variable.cpp b/test/Refactor/Rename/Variable.cpp
new file mode 100644
index 0000000..7d5c41e
--- /dev/null
+++ b/test/Refactor/Rename/Variable.cpp
@@ -0,0 +1,29 @@
+namespace A {
+int Foo; /* Test 1 */ // CHECK: rename [[@LINE]]:5 -> [[@LINE]]:8
+}
+int Foo;
+int Qux = Foo;
+int Baz = A::Foo; /* Test 2 */ // CHECK-NEXT: rename [[@LINE]]:14 -> [[@LINE]]:17
+void fun() {
+ struct {
+ int Foo;
+ } b = {100};
+ int Foo = 100;
+ Baz = Foo;
+ {
+ extern int Foo;
+ Baz = Foo;
+ Foo = A::Foo /* Test 3 */ + Baz; // CHECK-NEXT: rename [[@LINE]]:14 -> [[@LINE]]:17
+ A::Foo /* Test 4 */ = b.Foo; // CHECK-NEXT: rename [[@LINE]]:8 -> [[@LINE]]:11
+ }
+ Foo = b.Foo; // CHECK-NOT: rename [[@LINE]]
+}
+
+// Test 1.
+// RUN: clang-refactor-test rename-initiate -at=%s:2:5 -new-name=Bar %s | FileCheck %s
+// Test 2.
+// RUN: clang-refactor-test rename-initiate -at=%s:6:14 -new-name=Bar %s | FileCheck %s
+// Test 3.
+// RUN: clang-refactor-test rename-initiate -at=%s:16:14 -new-name=Bar %s | FileCheck %s
+// Test 4.
+// RUN: clang-refactor-test rename-initiate -at=%s:17:8 -new-name=Bar %s | FileCheck %s
diff --git a/test/Refactor/Rename/VariableMacro.cpp b/test/Refactor/Rename/VariableMacro.cpp
new file mode 100644
index 0000000..312d459
--- /dev/null
+++ b/test/Refactor/Rename/VariableMacro.cpp
@@ -0,0 +1,69 @@
+#define Baz Foo // CHECK1-NOT: rename local [[@LINE]]
+// CHECK1-NOT: macro [[@LINE-1]]
+
+void foo(int value) {}
+
+void macro() {
+ int Foo; // CHECK1: rename local [[@LINE]]:7 -> [[@LINE]]:10
+ Foo = 42; // CHECK1-NEXT: rename local [[@LINE]]:3 -> [[@LINE]]:6
+ Baz -= 0; // CHECK1-NEXT: macro [[@LINE]]:3 -> [[@LINE]]:3
+ foo(Foo); // CHECK1-NEXT: rename local [[@LINE]]:7 -> [[@LINE]]:10
+ foo(Baz); // CHECK1-NEXT: macro [[@LINE]]:7 -> [[@LINE]]:7
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:7:7 -at=%s:8:3 -at=%s:10:7 -new-name=Bar %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:1:13 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:9:3 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// CHECK-ERROR: could not rename symbol at the given location
+
+#define M var
+#define MM M
+void macro2() {
+ int M = 2; // CHECK2: macro [[@LINE]]:7 -> [[@LINE]]:7
+ (void)var; // CHECK2-NEXT: rename local [[@LINE]]:9 -> [[@LINE]]:12
+ (void)M; // CHECK2-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9
+ (void)MM; // CHECK2-NEXT: macro [[@LINE]]:9 -> [[@LINE]]:9
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:24:9 -new-name=Bar %s | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:20:11 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:21:12 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:23:7 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:25:9 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+
+#define BAR(x) x
+#define FOO(x) BAR(x)
+int FOO(global) = 2; // CHECK3: rename [[@LINE]]:9 -> [[@LINE]]:15
+void macro3() {
+ (void)global; // CHECK3-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:15
+ BAR(global) = 0; // CHECK3-NEXT: rename [[@LINE]]:7 -> [[@LINE]]:13
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:40:9 -at=%s:41:7 -new-name=Bar %s | FileCheck --check-prefix=CHECK3 %s
+
+
+
+#define CONCAT(x, y) x##_##y
+int CONCAT(a, b) = 2; // CHECK4: macro [[@LINE]]:5 -> [[@LINE]]:5
+void macro3() {
+ (void)a_b; // CHECK4-NEXT: rename [[@LINE]]:9 -> [[@LINE]]:12
+ CONCAT(a, b) = 0; // CHECK4-NEXT: macro [[@LINE]]:3 -> [[@LINE]]:3
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:51:9 -new-name=Bar %s | FileCheck --check-prefix=CHECK4 %s
+
+void macroInFunc() {
+ #define VARNAME var
+ int VARNAME;
+}
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:58:19 -new-name=Bar %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR %s
+
+void localVarArg() {
+ int var; // CHECK5: rename local [[@LINE]]:7 -> [[@LINE]]:10
+ BAR(var) = 0; // CHECK5-NEXT: rename local [[@LINE]]:7 -> [[@LINE]]:10
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:65:7 -at=%s:66:7 -new-name=Bar %s | FileCheck --check-prefix=CHECK5 %s
diff --git a/test/Refactor/Rename/invalid-indexed-name.m b/test/Refactor/Rename/invalid-indexed-name.m
new file mode 100644
index 0000000..b682bb3
--- /dev/null
+++ b/test/Refactor/Rename/invalid-indexed-name.m
@@ -0,0 +1,37 @@
+// XFAIL: *
+// TODO: Remove if unused
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test
+
+int variable = 0; // CHECK1: rename [[@LINE]]:5 -> [[@LINE]]:13
+
+// RUN: clang-refactor-test rename-indexed-file -name=variable -new-name=class -indexed-file=%s -indexed-at=4:5 %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: not clang-refactor-test rename-indexed-file -name=variable -new-name=class -indexed-file=%s -indexed-at=4:5 %s -x objective-c++ 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+// CHECK-ERR: error: invalid new name
+
+// RUN: not clang-refactor-test rename-indexed-file -name=variable -new-name=int -indexed-file=%s -indexed-at=4:5 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+
+@interface I
+
+- (void)some:(int)x selector:(int)y; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:21 -> [[@LINE]]:29
+
+@end
+
+// RUN: clang-refactor-test rename-indexed-file -name=some:selector -new-name=struct:void -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s | FileCheck --check-prefix=CHECK2 %s
+// RUN: clang-refactor-test rename-indexed-file -name=some:selector: -new-name=struct:void -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-cm %s | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=struct:void -indexed-file=%s -indexed-at=14:9 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR-FAIL %s
+// CHECK-ERR-FAIL: failed to perform indexed file rename
+// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=hello:123 -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=+:test -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+
+// RUN: not clang-refactor-test rename-indexed-file -name=some:selector -new-name=justOnePiece -indexed-file=%s -indexed-at=14:9 -indexed-symbol-kind=objc-im %s 2>&1 | FileCheck --check-prefix=CHECK-ERR2 %s
+// CHECK-ERR2: error: the number of strings in the new name 'justOnePiece' doesn't match the the number of strings in the old name
+
+@interface I1
+
+- (void)singlePiece;
+
+@end
+
+// RUN: not clang-refactor-test rename-indexed-file -name=singlePiece -new-name=struct -indexed-file=%s -indexed-at=31:9 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
diff --git a/test/Refactor/Rename/invalid-name.cpp b/test/Refactor/Rename/invalid-name.cpp
new file mode 100644
index 0000000..703f818
--- /dev/null
+++ b/test/Refactor/Rename/invalid-name.cpp
@@ -0,0 +1,13 @@
+// XFAIL: *
+// TODO: Remove if unused
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test
+
+int variable = 0;
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=class %s 2>&1 | FileCheck %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=- %s 2>&1 | FileCheck %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=var+ %s 2>&1 | FileCheck %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name="var " %s 2>&1 | FileCheck %s
+
+// CHECK: error: invalid new name
diff --git a/test/Refactor/Rename/invalid-name.m b/test/Refactor/Rename/invalid-name.m
new file mode 100644
index 0000000..65014be
--- /dev/null
+++ b/test/Refactor/Rename/invalid-name.m
@@ -0,0 +1,23 @@
+// XFAIL: *
+// TODO: Remove if unused
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test
+
+int variable = 0; // CHECK1: rename [[@LINE]]:5 -> [[@LINE]]:13
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:5 -new-name=class %s | FileCheck --check-prefix=CHECK1 %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=class %s -x objective-c++ 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+// CHECK-ERR: error: invalid new name
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:4:5 -new-name=int %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+
+@interface I
+
+- (void)some:(int)x selector:(int)y; // CHECK2: rename [[@LINE]]:9 -> [[@LINE]]:13, [[@LINE]]:21 -> [[@LINE]]:29
+
+@end
+
+// RUN: clang-refactor-test rename-initiate -at=%s:14:9 -new-name=struct:void %s | FileCheck --check-prefix=CHECK2 %s
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:14:9 -new-name=hello:123 %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+// RUN: not clang-refactor-test rename-initiate -at=%s:14:9 -new-name=+:test %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
diff --git a/test/Refactor/Rename/rename-indexed-file.cpp b/test/Refactor/Rename/rename-indexed-file.cpp
new file mode 100644
index 0000000..4e64fdf
--- /dev/null
+++ b/test/Refactor/Rename/rename-indexed-file.cpp
@@ -0,0 +1,123 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test
+
+class Test { // CHECK1: rename [[@LINE]]:7 -> [[@LINE]]:11
+public:
+ Test() { } // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:7
+ ~Test() { } // CHECK1: rename [[@LINE]]:4 -> [[@LINE]]:8
+
+ void doSomething() { return; }
+ void otherFile();
+};
+
+void foo() {
+ Test test; // CHECK1: rename [[@LINE]]:3 -> [[@LINE]]:7
+ (test).doSomething();
+}
+
+Test notIndexed; // CHECK1-NOT: rename [[@LINE]]
+
+// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s | FileCheck --check-prefix=CHECK1 %s
+
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%S/Inputs/rename-indexed-file.cpp -indexed-at=1:6 -indexed-at=2:3 -indexed-at=3:6 %s | FileCheck --check-prefix=CHECK2 %s
+// CHECK2: rename 1:6 -> 1:10
+// CHECK2: rename 2:3 -> 2:7
+// CHECK2: rename 3:6 -> 3:10
+
+// A valid location with an non-identifier token shouldn't produce an occurence
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=15:3 %s | FileCheck --check-prefix=CHECK3 %s
+
+// A invalid location shouldn't produce an occurence
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=999:1 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=0:1 %s | FileCheck --check-prefix=CHECK3 %s
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=1:0 %s | FileCheck --check-prefix=CHECK3 %s
+
+
+// CHECK3: no replacements found
+// CHECK3-NOT: rename
+
+// RUN: not clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s
+
+// CHECK-ERROR1: for the -indexed-file option: must be specified at least once!
+
+// It should be possible to have the filename as one of the compilation arguments
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -ignore-filename-for-initiation-tu -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s -c %s -Wall | FileCheck --check-prefix=CHECK1 %s
+
+// -gmodules should be stripped to avoid -fmodule-format=obj in CC1 arguments:
+// RUN: clang-refactor-test rename-indexed-file -no-textual-matches -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 -indexed-at=6:3 -indexed-at=7:4 -indexed-at=14:3 %s -fmodules -gmodules | FileCheck --check-prefix=CHECK1 %s
+
+// These texual matches should be reported as comment occurrences:
+// CHECK4-INIT: rename [[@LINE-46]]:7 -> [[@LINE-46]]:11
+// Test
+/* Test 2 Test */
+/** Test+1
+// Test
+**/
+/// Hello Test World
+//! \c Test.
+
+// CHECK4: comment [[@LINE-8]]:4 -> [[@LINE-8]]:8
+// CHECK4-NEXT: comment [[@LINE-8]]:4 -> [[@LINE-8]]:8
+// CHECK4-NEXT: comment [[@LINE-9]]:11 -> [[@LINE-9]]:15
+// CHECK4-NEXT: documentation [[@LINE-9]]:5 -> [[@LINE-9]]:9
+// CHECK4-NEXT: documentation [[@LINE-9]]:4 -> [[@LINE-9]]:8
+// CHECK4-NEXT: documentation [[@LINE-8]]:11 -> [[@LINE-8]]:15
+// CHECK4-NEXT: documentation [[@LINE-8]]:8 -> [[@LINE-8]]:12
+
+// "Test"
+// 'Test'
+// CHECK4-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:9
+// CHECK4-NEXT: comment [[@LINE-2]]:5 -> [[@LINE-2]]:9
+
+// CHECK4-NEXT: comment [[@LINE+1]]:55
+// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=Foo -indexed-file=%s -indexed-at=4:7 %s | FileCheck --check-prefixes=CHECK4-INIT,CHECK4 %s
+// We should find textual occurrences even without indexed occurrences:
+// CHECK4-NEXT: comment [[@LINE+1]]:55
+// RUN: clang-refactor-test rename-indexed-file -name=Test -new-name=Foo -indexed-file=%s %s | FileCheck --check-prefix=CHECK4 %s
+
+// These ones shouldn't:
+// Test2 test Testable
+/// _Test
+/// ATest_
+const char *test = "Test";
+void Test20() { }
+
+// CHECK4-NOT: comment
+// CHECK4-NOT: documentation
+
+
+class MyInclude { // CHECK5: rename [[@LINE]]:7 -> [[@LINE]]:16
+};
+
+ /*comment*/ #include "MyInclude.h"
+#include <clang/myinclude.h>
+#import <MyInclude/ThisIsMyInclude>
+// CHECK5-NEXT: filename [[@LINE-3]]:24 -> [[@LINE-3]]:33
+// CHECK5-NEXT: filename [[@LINE-3]]:17 -> [[@LINE-3]]:26
+// CHECK5-NEXT: filename [[@LINE-3]]:26 -> [[@LINE-3]]:35
+
+// CHECK5-NOT: filename
+#include "My Include.h"
+"MyInclude.h"
+
+// RUN: clang-refactor-test rename-indexed-file -name=MyInclude -new-name=Foo -indexed-file=%s -indexed-at=89:7 -indexed-at=include:92:1 -indexed-at=include:93:1 -indexed-at=include:94:1 -indexed-at=include:100:1 %s | FileCheck --check-prefix=CHECK5 %s
+
+#define MACRO variable
+
+void macroOccurrence() {
+ variable;
+ MACRO;
+ 22;
+ MACRO;
+}
+// CHECK-MACRO: rename [[@LINE-5]]:3 -> [[@LINE-5]]:11
+// CHECK-MACRO-NEXT: macro [[@LINE-5]]:3 -> [[@LINE-5]]:3
+// CHECK-MACRO-NOT: macro
+
+// RUN: clang-refactor-test rename-indexed-file -name=variable -new-name=foo -indexed-file=%s -indexed-at=108:3 -indexed-at=109:3 -indexed-at=110:3 -indexed-at=111:2 %s | FileCheck --check-prefix=CHECK-MACRO %s
+
+struct MyType { // CHECK-MACRO-PREFIX: rename [[@LINE]]:8 -> [[@LINE]]:14
+};
+MyType MyTypePrefix; // CHECK-MACRO-PREFIX: macro [[@LINE]]:8 -> [[@LINE]]:8
+
+// RUN: clang-refactor-test rename-indexed-file -name=MyType -new-name=x -indexed-file=%s -indexed-at=119:8 -indexed-at=121:8 %s | FileCheck --check-prefix=CHECK-MACRO-PREFIX %s
diff --git a/test/Refactor/Rename/rename-initiate-usr.cpp b/test/Refactor/Rename/rename-initiate-usr.cpp
new file mode 100644
index 0000000..2f15465
--- /dev/null
+++ b/test/Refactor/Rename/rename-initiate-usr.cpp
@@ -0,0 +1,20 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test
+
+class Test { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:11
+public:
+ Test() { } // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7
+ ~Test() { } // CHECK: rename [[@LINE]]:4 -> [[@LINE]]:8
+};
+
+void foo() {
+ Test test; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7
+}
+
+// RUN: clang-refactor-test rename-initiate-usr -usr="c:@S@Test" -new-name=Foo %s | FileCheck %s
+
+// RUN: not clang-refactor-test rename-initiate-usr -usr="c:@S@Foo" -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s
+// CHECK-ERROR1: error: could not rename symbol with the given USR
+
+// RUN: not clang-refactor-test rename-initiate-usr -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR2 %s
+// CHECK-ERROR2: for the -usr option: must be specified at least once
diff --git a/test/Refactor/Rename/rename-initiate.cpp b/test/Refactor/Rename/rename-initiate.cpp
new file mode 100644
index 0000000..30c918c
--- /dev/null
+++ b/test/Refactor/Rename/rename-initiate.cpp
@@ -0,0 +1,27 @@
+// Note: the run lines follow their respective tests, since line/column
+// matter in this test
+
+class Test { // CHECK: rename [[@LINE]]:7 -> [[@LINE]]:11
+public:
+ Test() { } // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7
+ ~Test() { } // CHECK: rename [[@LINE]]:4 -> [[@LINE]]:8
+ void doSomething() {
+ return;
+ }
+};
+
+void foo() {
+ Test test; // CHECK: rename [[@LINE]]:3 -> [[@LINE]]:7
+ test.doSomething();
+}
+
+// RUN: clang-refactor-test rename-initiate -at=%s:4:7 -new-name=Foo %s | FileCheck %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:8 -new-name=Foo %s | FileCheck %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:9 -new-name=Foo %s | FileCheck %s
+// RUN: clang-refactor-test rename-initiate -at=%s:4:10 -new-name=Foo %s | FileCheck %s
+
+// RUN: not clang-refactor-test rename-initiate -at=%s:1:10 -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR1 %s
+// CHECK-ERROR1: error: could not rename symbol at the given location
+
+// RUN: not clang-refactor-test rename-initiate -at=%s -new-name=Foo %s 2>&1 | FileCheck --check-prefix=CHECK-ERROR2 %s
+// CHECK-ERROR2: error: The -at option must use the <file:line:column> format
diff --git a/test/Refactor/list-refactoring-actions.cpp b/test/Refactor/list-refactoring-actions.cpp
new file mode 100644
index 0000000..573a076
--- /dev/null
+++ b/test/Refactor/list-refactoring-actions.cpp
@@ -0,0 +1,39 @@
+int
+renamable = 0;
+
+// RUN: clang-refactor-test list-actions -at=%s:2:1 %s | FileCheck --check-prefix=CHECK-RENAME %s
+
+// CHECK-RENAME: Found {{[0-9]*}} actions:
+// CHECK-RENAME-NEXT: Rename
+
+// RUN: not clang-refactor-test list-actions -at=%s:2:13 %s 2>&1 | FileCheck --check-prefix=CHECK-NONE %s
+
+// CHECK-NONE: No refactoring actions are available at the given location
+// CHECK-NONE-NOT: Rename
+
+// RUN: not clang-refactor-test list-actions -at=%s %s 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+// CHECK-ERR: error: The -at option must use the <file:line:column> format
+
+void localVsGlobalRename(int renamable) { }
+
+// RUN: clang-refactor-test list-actions -dump-raw-action-type -at=%s:17:30 %s | FileCheck --check-prefix=CHECK-LOCAL-RENAME %s
+
+// CHECK-LOCAL-RENAME: Found {{[0-9]*}} actions:
+// CHECK-LOCAL-RENAME-NEXT: Rename(1)
+
+namespace nullDeclNamespace {
+
+template<template<typename T> class C> class NullNode {};
+
+struct AfterNull { };
+// RUN: clang-refactor-test list-actions -at=%s:28:8 %s | FileCheck --check-prefix=CHECK-RENAME %s
+
+}
+
+#define MACRO(X) (void)X;
+void macroArg() {
+ int variable = 0;
+ MACRO(variable);
+}
+// RUN: not clang-refactor-test list-actions -at=%s:26:9 -selected=%s:36:9-36:16 %s 2>&1 | FileCheck --check-prefix=CHECK-MACRO-ARG %s
+// CHECK-MACRO-ARG: No refactoring actions are available at the given location
diff --git a/test/Sema/attr-availability-app-extensions.c b/test/Sema/attr-availability-app-extensions.c
index 8f9dcbc..c66c14e 100644
--- a/test/Sema/attr-availability-app-extensions.c
+++ b/test/Sema/attr-availability-app-extensions.c
@@ -21,8 +21,19 @@
#endif
void f1(int); // expected-note {{'f1' has been explicitly marked unavailable here}}
+#if __has_feature(attribute_availability_app_extension)
+ __attribute__((availability(macOSApplicationExtension,unavailable)))
+#ifndef TVOS
+ __attribute__((availability(iOSApplicationExtension,unavailable)))
+#else
+ __attribute__((availability(tvOSApplicationExtension,unavailable)))
+#endif
+#endif
+void f2(int); // expected-note {{'f2' has been explicitly marked unavailable here}}
+
void test() {
f0(1); // expected-error {{'f0' is unavailable: not available on}}
f1(1); // expected-error {{'f1' is unavailable}}
+ f2(2); // expected-error {{'f2' is unavailable: not available on}}
}
diff --git a/test/Sema/attr-availability-ios.c b/test/Sema/attr-availability-ios.c
index 6462d58..3f901bb 100644
--- a/test/Sema/attr-availability-ios.c
+++ b/test/Sema/attr-availability-ios.c
@@ -2,13 +2,13 @@
void f0(int) __attribute__((availability(ios,introduced=2.0,deprecated=2.1))); // expected-note {{'f0' has been explicitly marked deprecated here}}
void f1(int) __attribute__((availability(ios,introduced=2.1)));
-void f2(int) __attribute__((availability(ios,introduced=2.0,deprecated=3.0))); // expected-note {{'f2' has been explicitly marked deprecated here}}
+void f2(int) __attribute__((availability(iOS,introduced=2.0,deprecated=3.0))); // expected-note {{'f2' has been explicitly marked deprecated here}}
void f3(int) __attribute__((availability(ios,introduced=3.0)));
void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=2.1,obsoleted=3.0))); // expected-note{{explicitly marked unavailable}}
void f5(int) __attribute__((availability(ios,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{'f5' has been explicitly marked deprecated here}}
void f6(int) __attribute__((availability(ios,deprecated=3.0)));
-void f6(int) __attribute__((availability(ios,introduced=2.0))); // expected-note {{'f6' has been explicitly marked deprecated here}}
+void f6(int) __attribute__((availability(iOS,introduced=2.0))); // expected-note {{'f6' has been explicitly marked deprecated here}}
void test() {
f0(0); // expected-warning{{'f0' is deprecated: first deprecated in iOS 2.1}}
diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c
index f422811..d390869 100644
--- a/test/Sema/attr-availability-macosx.c
+++ b/test/Sema/attr-availability-macosx.c
@@ -10,7 +10,7 @@
void f3(int) __attribute__((availability(macosx,introduced=10.6)));
void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}}
void f5(int) __attribute__((availability(ios,introduced=3.2), availability(macosx,unavailable))); // expected-note{{'f5' has been explicitly marked unavailable here}}
-void f6(int) __attribute__((availability(macosx,strict,introduced=10.6))); //expected-note{{'f6' has been explicitly marked unavailable here}}
+void f6(int) __attribute__((availability(macOS,strict,introduced=10.6))); //expected-note{{'f6' has been explicitly marked unavailable here}}
void test() {
f0(0);
@@ -47,7 +47,7 @@
};
// Make sure the note is on the declaration with the actual availability attributes.
-struct __attribute__((availability(macosx,strict,introduced=10.9))) type_info // \
+struct __attribute__((availability(macOS,strict,introduced=10.9))) type_info // \
expected-note{{'type_info' has been explicitly marked unavailable here}}
{
};
diff --git a/test/Sema/attr-availability-swift.c b/test/Sema/attr-availability-swift.c
new file mode 100644
index 0000000..42e7524
--- /dev/null
+++ b/test/Sema/attr-availability-swift.c
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -fblocks -verify %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -fsyntax-only -ast-dump %s | FileCheck %s
+//
+
+#if !__has_feature(attribute_availability_with_message)
+# error "Missing __has_feature"
+#endif
+
+#if __has_feature(attribute_availability_swift)
+# warning "okay"
+// expected-warning@-1{{okay}}
+#else
+# error "Missing __has_feature"
+#endif
+
+extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable)));
+// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "" ""
+extern int noSwiftGlobal1 __attribute__((availability(macosx, introduced=10.1))); // okay
+// CHECK: AvailabilityAttr {{.*}}macos 10.1 0 0 "" ""
+// CHECK: AvailabilityAttr {{.*}}Inherited swift 0 0 0 Unavailable "" ""
+extern int noSwiftGlobal1 __attribute__((availability(swift, unavailable, message="and this one has a message"))); // okay
+// CHECK: AvailabilityAttr {{.*}}swift 0 0 0 Unavailable "and this one has a message" ""
+// CHECK: AvailabilityAttr {{.*}}Inherited macos 10.1 0 0 "" ""
+extern int noSwiftGlobal2 __attribute__((availability(swift, introduced=5))); // expected-warning{{only 'unavailable' and 'deprecated' are supported for Swift availability}}
+// CHECK: VarDecl
+// CHECK-NOT: AvailabilityAttr
+extern int noSwiftGlobal3 __attribute__((availability(swift, deprecated, message="t")));
+// CHECK: VarDecl
+// CHECK: AvailabilityAttr {{.*}}swift 0 1 0 "t" ""
diff --git a/test/Sema/attr-availability-tvos.c b/test/Sema/attr-availability-tvos.c
index 6422464..d53d6ac 100644
--- a/test/Sema/attr-availability-tvos.c
+++ b/test/Sema/attr-availability-tvos.c
@@ -36,7 +36,7 @@
// Test tvOS specific attributes.
void f0_tvos(int) __attribute__((availability(tvos,introduced=2.0,deprecated=2.1))); // expected-note {{'f0_tvos' has been explicitly marked deprecated here}}
void f1_tvos(int) __attribute__((availability(tvos,introduced=2.1)));
-void f2_tvos(int) __attribute__((availability(tvos,introduced=2.0,deprecated=3.0))); // expected-note {{'f2_tvos' has been explicitly marked deprecated here}}
+void f2_tvos(int) __attribute__((availability(tvOS,introduced=2.0,deprecated=3.0))); // expected-note {{'f2_tvos' has been explicitly marked deprecated here}}
void f3_tvos(int) __attribute__((availability(tvos,introduced=3.0)));
void f4_tvos(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(tvos,introduced=2.0,deprecated=2.1,obsoleted=3.0))); // expected-note{{explicitly marked unavailable}}
void f5_tvos(int) __attribute__((availability(tvos,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0)));
@@ -44,7 +44,7 @@
void f5b_tvos(int) __attribute__((availability(tvos,introduced=2.0))) __attribute__((availability(tvos,deprecated=3.0))); // expected-note {{'f5b_tvos' has been explicitly marked deprecated here}}
void f5c_tvos(int) __attribute__((availability(ios,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{'f5c_tvos' has been explicitly marked deprecated here}}
void f6_tvos(int) __attribute__((availability(tvos,deprecated=3.0)));
-void f6_tvos(int) __attribute__((availability(tvos,introduced=2.0))); // expected-note {{'f6_tvos' has been explicitly marked deprecated here}}
+void f6_tvos(int) __attribute__((availability(tvOS,introduced=2.0))); // expected-note {{'f6_tvos' has been explicitly marked deprecated here}}
void test_tvos() {
f0_tvos(0); // expected-warning{{'f0_tvos' is deprecated: first deprecated in tvOS 2.1}}
diff --git a/test/Sema/attr-availability-watchos.c b/test/Sema/attr-availability-watchos.c
index cb9f968..ec7f3a7 100644
--- a/test/Sema/attr-availability-watchos.c
+++ b/test/Sema/attr-availability-watchos.c
@@ -25,7 +25,7 @@
// Test watchOS specific attributes.
void f0_watchos(int) __attribute__((availability(watchos,introduced=2.0,deprecated=2.1))); // expected-note {{'f0_watchos' has been explicitly marked deprecated here}}
void f1_watchos(int) __attribute__((availability(watchos,introduced=2.1)));
-void f2_watchos(int) __attribute__((availability(watchos,introduced=2.0,deprecated=3.0))); // expected-note {{'f2_watchos' has been explicitly marked deprecated here}}
+void f2_watchos(int) __attribute__((availability(watchOS,introduced=2.0,deprecated=3.0))); // expected-note {{'f2_watchos' has been explicitly marked deprecated here}}
void f3_watchos(int) __attribute__((availability(watchos,introduced=3.0)));
void f4_watchos(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(watchos,introduced=2.0,deprecated=2.1,obsoleted=3.0))); // expected-note{{explicitly marked unavailable}}
void f5_watchos(int) __attribute__((availability(watchos,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0)));
@@ -33,7 +33,7 @@
void f5b_watchos(int) __attribute__((availability(watchos,introduced=2.0))) __attribute__((availability(watchos,deprecated=3.0))); // expected-note {{'f5b_watchos' has been explicitly marked deprecated here}}
void f5c_watchos(int) __attribute__((availability(ios,introduced=2.0))) __attribute__((availability(ios,deprecated=3.0))); // expected-note {{'f5c_watchos' has been explicitly marked deprecated here}}
void f6_watchos(int) __attribute__((availability(watchos,deprecated=3.0)));
-void f6_watchos(int) __attribute__((availability(watchos,introduced=2.0))); // expected-note {{'f6_watchos' has been explicitly marked deprecated here}}
+void f6_watchos(int) __attribute__((availability(watchOS,introduced=2.0))); // expected-note {{'f6_watchos' has been explicitly marked deprecated here}}
void test_watchos() {
f0_watchos(0); // expected-warning{{'f0_watchos' is deprecated: first deprecated in watchOS 2.1}}
diff --git a/test/Sema/attr-availability.c b/test/Sema/attr-availability.c
index 1b8cbd2..b2b1d87 100644
--- a/test/Sema/attr-availability.c
+++ b/test/Sema/attr-availability.c
@@ -91,7 +91,6 @@
extern int x2 __attribute__((availability(macosx,introduced=10.2))); // expected-note {{previous attribute is here}}
extern int x2 __attribute__((availability(macosx,introduced=10.5))); // expected-warning {{availability does not match previous declaration}}
-
enum Original {
OriginalDeprecated __attribute__((availability(macosx, deprecated=10.2))), // expected-note + {{'OriginalDeprecated' has been explicitly marked deprecated here}}
OriginalUnavailable __attribute__((availability(macosx, unavailable))) // expected-note + {{'OriginalUnavailable' has been explicitly marked unavailable here}}
diff --git a/test/Sema/attr-noescape.c b/test/Sema/attr-noescape.c
new file mode 100644
index 0000000..ec367b6
--- /dev/null
+++ b/test/Sema/attr-noescape.c
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 %s -fblocks -verify -fsyntax-only
+
+#if !__has_attribute(noescape)
+# error "missing noescape attribute"
+#endif
+
+int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribute only applies to parameters}}
+
+void foo(__attribute__((noescape)) int *int_ptr,
+ __attribute__((noescape)) int (^block)(int),
+ __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute ignored on parameter of non-pointer type 'int'}}
+}
diff --git a/test/Sema/enum-attr.c b/test/Sema/enum-attr.c
index 933d8cc..bf0dcb0 100644
--- a/test/Sema/enum-attr.c
+++ b/test/Sema/enum-attr.c
@@ -44,7 +44,7 @@
enum Enum t0 = 100; // expected-warning{{integer constant not in range of enumerated type}}
t0 = 1;
- switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}}
+ switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}} expected-note {{add missing switch cases}}
case A0: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
}
@@ -58,7 +58,7 @@
enum EnumClosed t1 = 100; // expected-warning{{integer constant not in range of enumerated type}}
t1 = 1;
- switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}}
+ switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}} expected-note {{add missing switch cases}}
case B0: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
}
@@ -72,7 +72,7 @@
enum EnumOpen t2 = 100;
t2 = 1;
- switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}}
+ switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}} expected-note {{add missing switch cases}}
case C0: break;
case 16: break;
}
@@ -86,7 +86,7 @@
enum EnumFlag t3 = 5; // expected-warning{{integer constant not in range of enumerated type}}
t3 = 9;
- switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}}
+ switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}} expected-note {{add missing switch cases}}
case D0: break;
case 9: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
@@ -101,7 +101,7 @@
enum EnumFlagClosed t4 = 5; // expected-warning{{integer constant not in range of enumerated type}}
t4 = 9;
- switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}}
+ switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}} expected-note {{add missing switch cases}}
case E0: break;
case 9: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
@@ -116,7 +116,7 @@
enum EnumFlagOpen t5 = 5;
t5 = 9;
- switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}}
+ switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}} expected-note {{add missing switch cases}}
case F0: break;
case 9: break;
case 16: break;
diff --git a/test/Sema/enum.c b/test/Sema/enum.c
index 3546bfe..cf59ca1 100644
--- a/test/Sema/enum.c
+++ b/test/Sema/enum.c
@@ -123,3 +123,14 @@
// PR24610
enum Color { Red, Green, Blue }; // expected-note{{previous use is here}}
typedef struct Color NewColor; // expected-error {{use of 'Color' with tag type that does not match previous declaration}}
+
+// PR28903
+struct PR28903 {
+ enum {
+ PR28903_A = (enum { // expected-error-re {{'enum PR28903::(anonymous at {{.*}})' cannot be defined in an enumeration}}
+ PR28903_B,
+ PR28903_C = PR28903_B
+ })0
+ };
+ int makeStructNonEmpty;
+};
diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c
index 5465122..ca3132e 100644
--- a/test/Sema/format-strings.c
+++ b/test/Sema/format-strings.c
@@ -653,6 +653,33 @@
}
#pragma GCC diagnostic warning "-Wformat-nonliteral"
+void test_os_log_format(char c, const char *pc, int i, int *pi, void *p, void *buf) {
+ __builtin_os_log_format(buf, "");
+ __builtin_os_log_format(buf, "%d"); // expected-warning {{more '%' conversions than data arguments}}
+ __builtin_os_log_format(buf, "%d", i);
+ __builtin_os_log_format(buf, "%P", p); // expected-warning {{using '%P' format specifier without precision}}
+ __builtin_os_log_format(buf, "%.10P", p);
+ __builtin_os_log_format(buf, "%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
+ __builtin_os_log_format(buf, "%.*P", i, p);
+ __builtin_os_log_format(buf, "%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
+ __builtin_os_log_format(buf, pc); // expected-error {{os_log() format argument is not a string constant}}
+
+ printf("%{private}s", pc); // expected-warning {{using 'private' format specifier annotation outside of os_log()/os_trace()}}
+ __builtin_os_log_format(buf, "%{private}s", pc);
+
+ // <rdar://problem/23835805>
+ __builtin_os_log_format_buffer_size("no-args");
+ __builtin_os_log_format(buf, "%s", "hi");
+
+ // <rdar://problem/24828090>
+ wchar_t wc = 'a';
+ __builtin_os_log_format(buf, "%C", wc);
+ printf("%C", wc);
+ wchar_t wcs[] = {'a', 0};
+ __builtin_os_log_format(buf, "%S", wcs);
+ printf("%S", wcs);
+}
+
void test_char_pointer_arithmetic(int b) {
const char s1[] = "string";
const char s2[] = "%s string";
diff --git a/test/Sema/fp16vec-sema.c b/test/Sema/fp16vec-sema.c
new file mode 100644
index 0000000..aefb5f8
--- /dev/null
+++ b/test/Sema/fp16vec-sema.c
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+typedef __fp16 half4 __attribute__ ((vector_size (8)));
+typedef float float4 __attribute__ ((vector_size (16)));
+typedef short short4 __attribute__ ((vector_size (8)));
+typedef int int4 __attribute__ ((vector_size (16)));
+
+half4 hv0, hv1;
+float4 fv0, fv1;
+short4 sv0;
+int4 iv0;
+
+void testFP16Vec(int c) {
+ hv0 = hv0 + hv1;
+ hv0 = hv0 - hv1;
+ hv0 = hv0 * hv1;
+ hv0 = hv0 / hv1;
+ hv0 = c ? hv0 : hv1;
+ hv0 += hv1;
+ hv0 -= hv1;
+ hv0 *= hv1;
+ hv0 /= hv1;
+ sv0 = hv0 == hv1;
+ sv0 = hv0 != hv1;
+ sv0 = hv0 < hv1;
+ sv0 = hv0 > hv1;
+ sv0 = hv0 <= hv1;
+ sv0 = hv0 >= hv1;
+ sv0 = hv0 || hv1; // expected-error{{logical expression with vector types 'half4' (vector of 4 '__fp16' values) and 'half4' is only supported in C++}}
+ sv0 = hv0 && hv1; // expected-error{{logical expression with vector types 'half4' (vector of 4 '__fp16' values) and 'half4' is only supported in C++}}
+
+ // Implicit conversion between half vectors and float vectors are not allowed.
+ hv0 = fv0; // expected-error{{assigning to}}
+ fv0 = hv0; // expected-error{{assigning to}}
+ hv0 = (half4)fv0; // expected-error{{invalid conversion between}}
+ fv0 = (float4)hv0; // expected-error{{invalid conversion between}}
+ hv0 = fv0 + fv1; // expected-error{{assigning to}}
+ fv0 = hv0 + hv1; // expected-error{{assigning to}}
+ hv0 = hv0 + fv1; // expected-error{{cannot convert between vector}}
+ hv0 = c ? hv0 : fv1; // expected-error{{cannot convert between vector}}
+ sv0 = hv0 == fv1; // expected-error{{cannot convert between vector}}
+ sv0 = hv0 < fv1; // expected-error{{cannot convert between vector}}
+ sv0 = hv0 || fv1; // expected-error{{cannot convert between vector}} expected-error{{invalid operands to binary expression}}
+ iv0 = hv0 == hv1; // expected-error{{assigning to}}
+
+ // FIXME: clang currently disallows using these operators on vectors, which is
+ // allowed by gcc.
+ sv0 = !hv0; // expected-error{{invalid argument type}}
+ hv0++; // expected-error{{cannot increment value of type}}
+ ++hv0; // expected-error{{cannot increment value of type}}
+}
diff --git a/test/Sema/statements.c b/test/Sema/statements.c
index dbb4d56..a17e840 100644
--- a/test/Sema/statements.c
+++ b/test/Sema/statements.c
@@ -56,7 +56,7 @@
enum x { a, b, c, d, e, f, g };
void foo(enum x X) {
- switch (X) { // expected-warning {{enumeration value 'g' not handled in switch}}
+ switch (X) { // expected-warning {{enumeration value 'g' not handled in switch}} expected-note {{add missing switch cases}}
case a:
case b:
case c:
@@ -66,7 +66,7 @@
break;
}
- switch (X) { // expected-warning {{enumeration values 'f' and 'g' not handled in switch}}
+ switch (X) { // expected-warning {{enumeration values 'f' and 'g' not handled in switch}} expected-note {{add missing switch cases}}
case a:
case b:
case c:
@@ -75,7 +75,7 @@
break;
}
- switch (X) { // expected-warning {{enumeration values 'e', 'f', and 'g' not handled in switch}}
+ switch (X) { // expected-warning {{enumeration values 'e', 'f', and 'g' not handled in switch}} expected-note {{add missing switch cases}}
case a:
case b:
case c:
@@ -83,7 +83,7 @@
break;
}
- switch (X) { // expected-warning {{5 enumeration values not handled in switch: 'c', 'd', 'e'...}}
+ switch (X) { // expected-warning {{5 enumeration values not handled in switch: 'c', 'd', 'e'...}} expected-note {{add missing switch cases}}
case a:
case b:
break;
diff --git a/test/Sema/switch.c b/test/Sema/switch.c
index 7aa695d..5580209 100644
--- a/test/Sema/switch.c
+++ b/test/Sema/switch.c
@@ -97,7 +97,7 @@
A = 1,
B
} a;
- switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}}
+ switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} expected-note {{add missing switch cases}}
case A:
break;
}
@@ -155,7 +155,7 @@
case C:
break;
}
- switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}}
+ switch(a) { //expected-warning{{enumeration value 'B' not handled in switch}} expected-note {{add missing switch cases}}
case A:
break;
}
@@ -202,13 +202,13 @@
B,
C
} a;
- switch(a) { //expected-warning{{enumeration value 'A' not handled in switch}}
+ switch(a) { //expected-warning{{enumeration value 'A' not handled in switch}} expected-note {{add missing switch cases}}
case B:
case C:
break;
}
- switch(a) { //expected-warning{{enumeration value 'A' not explicitly handled in switch}}
+ switch(a) { //expected-warning{{enumeration value 'A' not explicitly handled in switch}} expected-note {{add missing switch cases}}
case B:
case C:
break;
@@ -238,7 +238,7 @@
} my_type_t;
int test13(my_type_t t) {
- switch(t) { // expected-warning{{enumeration value 'val3' not handled in switch}}
+ switch(t) { // expected-warning{{enumeration value 'val3' not handled in switch}} expected-note {{add missing switch cases}}
case val1:
return 1;
case val2:
diff --git a/test/SemaCXX/array-bounds.cpp b/test/SemaCXX/array-bounds.cpp
index 8ae92e7..92e8dd4 100644
--- a/test/SemaCXX/array-bounds.cpp
+++ b/test/SemaCXX/array-bounds.cpp
@@ -164,7 +164,7 @@
static enum enumB myVal = enumB_X;
void test_nested_switch() {
switch (enumA_E) { // expected-warning {{no case matching constant}}
- switch (myVal) { // expected-warning {{enumeration values 'enumB_X' and 'enumB_Z' not handled in switch}}
+ switch (myVal) { // expected-warning {{enumeration values 'enumB_X' and 'enumB_Z' not handled in switch}} expected-note {{add missing switch cases}}
case enumB_Y: ;
}
}
diff --git a/test/SemaCXX/cxx1y-init-captures.cpp b/test/SemaCXX/cxx1y-init-captures.cpp
index d681954..4b82452 100644
--- a/test/SemaCXX/cxx1y-init-captures.cpp
+++ b/test/SemaCXX/cxx1y-init-captures.cpp
@@ -206,3 +206,11 @@
find(weight); // expected-note {{in instantiation of function template specialization}}
}
}
+
+namespace init_capture_undeclared_identifier {
+ auto a = [x = y]{}; // expected-error{{use of undeclared identifier 'y'}}
+
+ int typo_foo; // expected-note 2 {{'typo_foo' declared here}}
+ auto b = [x = typo_boo]{}; // expected-error{{use of undeclared identifier 'typo_boo'; did you mean 'typo_foo'}}
+ auto c = [x(typo_boo)]{}; // expected-error{{use of undeclared identifier 'typo_boo'; did you mean 'typo_foo'}}
+}
diff --git a/test/SemaCXX/cxx1z-init-statement-template.cpp b/test/SemaCXX/cxx1z-init-statement-template.cpp
new file mode 100644
index 0000000..cedd2c7
--- /dev/null
+++ b/test/SemaCXX/cxx1z-init-statement-template.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -std=c++1z -verify -emit-llvm-only %s
+// expected-no-diagnostics
+
+// rdar://problem/33888545
+template <unsigned int BUFFER_SIZE> class Buffer {};
+
+class A {
+public:
+ int status;
+};
+
+template <unsigned int N> A parse(Buffer<N> buffer);
+
+template<unsigned int N>
+void init_in_if(Buffer<N> buffer) {
+ if (A a = parse(buffer); a.status > 0) {
+ }
+}
+
+template<unsigned int N>
+void init_in_switch(Buffer<N> buffer) {
+ switch (A a = parse(buffer); a.status) {
+ default:
+ break;
+ }
+}
+
+void test() {
+ Buffer<10> buffer;
+ init_in_if(buffer);
+ init_in_switch(buffer);
+}
diff --git a/test/SemaCXX/enum-attr.cpp b/test/SemaCXX/enum-attr.cpp
index 7726aff..92027ae 100644
--- a/test/SemaCXX/enum-attr.cpp
+++ b/test/SemaCXX/enum-attr.cpp
@@ -27,7 +27,7 @@
void test() {
enum Enum t0;
- switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}}
+ switch (t0) { // expected-warning{{enumeration value 'A1' not handled in switch}} expected-note {{add missing switch cases}}
case A0: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
}
@@ -40,7 +40,7 @@
enum EnumClosed t1;
- switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}}
+ switch (t1) { // expected-warning{{enumeration value 'B1' not handled in switch}} expected-note {{add missing switch cases}}
case B0: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
}
@@ -53,7 +53,7 @@
enum EnumOpen t2;
- switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}}
+ switch (t2) { // expected-warning{{enumeration value 'C1' not handled in switch}} expected-note {{add missing switch cases}}
case C0: break;
case 16: break;
}
@@ -66,7 +66,7 @@
enum EnumFlag t3;
- switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}}
+ switch (t3) { // expected-warning{{enumeration value 'D1' not handled in switch}} expected-note {{add missing switch cases}}
case D0: break;
case 9: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
@@ -80,7 +80,7 @@
enum EnumFlagClosed t4;
- switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}}
+ switch (t4) { // expected-warning{{enumeration value 'E1' not handled in switch}} expected-note {{add missing switch cases}}
case E0: break;
case 9: break;
case 16: break; // expected-warning{{case value not in enumerated type}}
@@ -94,7 +94,7 @@
enum EnumFlagOpen t5;
- switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}}
+ switch (t5) { // expected-warning{{enumeration value 'F1' not handled in switch}} expected-note {{add missing switch cases}}
case F0: break;
case 9: break;
case 16: break;
diff --git a/test/SemaCXX/enum.cpp b/test/SemaCXX/enum.cpp
index 6b0824b..cfe5760 100644
--- a/test/SemaCXX/enum.cpp
+++ b/test/SemaCXX/enum.cpp
@@ -110,3 +110,13 @@
// expected-warning@-2 {{not an integral constant expression}}
// expected-note@-3 {{value 28958703552 is outside the range of representable values}}
#endif
+
+// PR28903
+struct PR28903 {
+ enum {
+ PR28903_A = (enum { // expected-error-re {{'PR28903::(anonymous enum at {{.*}})' cannot be defined in an enumeration}}
+ PR28903_B,
+ PR28903_C = PR28903_B
+ })
+ };
+};
diff --git a/test/SemaCXX/microsoft-varargs.cpp b/test/SemaCXX/microsoft-varargs.cpp
index 35f31a9..5b0f90e 100644
--- a/test/SemaCXX/microsoft-varargs.cpp
+++ b/test/SemaCXX/microsoft-varargs.cpp
@@ -20,3 +20,8 @@
return __builtin_va_arg(ap, int);
}
+void test___va_start_ignore_const(const char *format, ...) {
+ va_list args;
+ ((void)(__va_start(&args, (&const_cast<char &>(reinterpret_cast<const volatile char &>(format))), ((sizeof(format) + 4 - 1) & ~(4 - 1)), (&const_cast<char &>(reinterpret_cast<const volatile char &>(format))))));
+}
+
diff --git a/test/SemaCXX/scope-check.cpp b/test/SemaCXX/scope-check.cpp
index 9e00332..256f01b 100644
--- a/test/SemaCXX/scope-check.cpp
+++ b/test/SemaCXX/scope-check.cpp
@@ -193,7 +193,7 @@
bool recurse() {
MyEnum K;
- switch (K) { // expected-warning {{enumeration value 'something_invalid' not handled in switch}}
+ switch (K) { // expected-warning {{enumeration value 'something_invalid' not handled in switch}} expected-note {{add missing switch cases}}
case something_valid:
case what_am_i_thinking: // expected-error {{use of undeclared identifier}}
int *X = 0;
diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp
index 2d78f06..910e2c1 100644
--- a/test/SemaCXX/typo-correction.cpp
+++ b/test/SemaCXX/typo-correction.cpp
@@ -411,7 +411,7 @@
void testAccess() {
Figure obj;
- switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}}
+ switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}} expected-note {{add missing switch cases}}
case SQUARE: // expected-error-re {{use of undeclared identifier 'SQUARE'{{$}}}}
case TRIANGLE: // expected-error-re {{use of undeclared identifier 'TRIANGLE'{{$}}}}
case CIRCE: // expected-error-re {{use of undeclared identifier 'CIRCE'{{$}}}}
diff --git a/test/SemaCXX/unavailable_aligned_allocation.cpp b/test/SemaCXX/unavailable_aligned_allocation.cpp
index 2ae5d2e..2000e0b 100644
--- a/test/SemaCXX/unavailable_aligned_allocation.cpp
+++ b/test/SemaCXX/unavailable_aligned_allocation.cpp
@@ -1,6 +1,12 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12.0 -fexceptions -faligned-alloc-unavailable -std=c++1z -verify %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12.0 -fexceptions -std=c++1z -verify -DNO_ERRORS %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.12.0 -fexceptions -faligned-allocation -faligned-alloc-unavailable -std=c++14 -verify %s
+// RUN: %clang_cc1 -triple arm64-apple-ios10.0.0 -fexceptions -faligned-alloc-unavailable -std=c++1z -verify -DIOS %s
+// RUN: %clang_cc1 -triple arm64-apple-ios10.0.0 -fexceptions -std=c++1z -verify -DNO_ERRORS %s
+// RUN: %clang_cc1 -triple arm64-apple-tvos10.0.0 -fexceptions -faligned-alloc-unavailable -std=c++1z -verify -DTVOS %s
+// RUN: %clang_cc1 -triple arm64-apple-tvos10.0.0 -fexceptions -std=c++1z -verify -DNO_ERRORS %s
+// RUN: %clang_cc1 -triple armv7k-apple-watchos3.0.0 -fexceptions -faligned-alloc-unavailable -std=c++1z -verify -DWATCHOS %s
+// RUN: %clang_cc1 -triple armv7k-apple-watchos3.0.0 -fexceptions -std=c++1z -verify -DNO_ERRORS %s
namespace std {
typedef decltype(sizeof(0)) size_t;
@@ -56,44 +62,68 @@
#ifdef NO_ERRORS
// expected-no-diagnostics
#else
-// expected-error@-16 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}}
+// expected-error@-16 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on}}
// expected-note@-17 {{if you supply your own aligned allocation functions}}
-// expected-error@-18 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}}
+// expected-error@-18 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on}}
// expected-note@-19 {{if you supply your own aligned allocation functions}}
-// expected-error@-20 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}}
+// expected-error@-20 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on}}
// expected-note@-21 {{if you supply your own aligned allocation functions}}
-// expected-error@-22 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}}
+// expected-error@-22 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on}}
// expected-note@-23 {{if you supply your own aligned allocation functions}}
-// expected-error@-24 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}}
+// expected-error@-24 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on}}
// expected-note@-25 {{if you supply your own aligned allocation functions}}
-// expected-error@-26 {{aligned allocation function of type 'void *(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}}
+// expected-error@-26 {{aligned allocation function of type 'void *(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept' is only available on}}
// expected-note@-27 {{if you supply your own aligned allocation functions}}
-// expected-error@-28 {{aligned deallocation function of type 'void (void *, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}}
+// expected-error@-28 {{aligned deallocation function of type 'void (void *, std::align_val_t, const std::nothrow_t &) noexcept' is only available on}}
// expected-note@-29 {{if you supply your own aligned allocation functions}}
-// expected-error@-29 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}}
+// expected-error@-29 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on}}
// expected-note@-30 {{if you supply your own aligned allocation functions}}
-// expected-error@-31 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}}
+// expected-error@-31 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on}}
// expected-note@-32 {{if you supply your own aligned allocation functions}}
-// expected-error@-33 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' possibly unavailable on}}
+// expected-error@-33 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on}}
// expected-note@-34 {{if you supply your own aligned allocation functions}}
-// expected-error@-35 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}}
+// expected-error@-35 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on}}
// expected-note@-36 {{if you supply your own aligned allocation functions}}
-// expected-error@-37 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' possibly unavailable on}}
+// expected-error@-37 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on}}
// expected-note@-38 {{if you supply your own aligned allocation functions}}
-// expected-error@-39 {{aligned allocation function of type 'void *(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}}
+// expected-error@-39 {{aligned allocation function of type 'void *(std::size_t, std::align_val_t, const std::nothrow_t &) noexcept' is only available on}}
// expected-note@-40 {{if you supply your own aligned allocation functions}}
-// expected-error@-41 {{aligned deallocation function of type 'void (void *, std::align_val_t, const std::nothrow_t &) noexcept' possibly unavailable on}}
+// expected-error@-41 {{aligned deallocation function of type 'void (void *, std::align_val_t, const std::nothrow_t &) noexcept' is only available on}}
// expected-note@-42 {{if you supply your own aligned allocation functions}}
#endif
+void testOveralignedCheckOS() {
+ auto *p = new OveralignedS;
+}
+
+#ifdef NO_ERRORS
+// expected-no-diagnostics
+#else
+#if defined(IOS)
+// expected-error@-7 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on iOS 11 or newer}}
+// expected-error@-8 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on iOS 11 or newer}}}
+#elif defined(TVOS)
+// expected-error@-10 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on tvOS 11 or newer}}}
+// expected-error@-11 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on tvOS 11 or newer}}}
+#elif defined(WATCHOS)
+// expected-error@-13 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on watchOS 4 or newer}}}
+// expected-error@-14 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on watchOS 4 or newer}}}
+#else
+// expected-error@-16 {{aligned allocation function of type 'void *(unsigned long, enum std::align_val_t)' is only available on macOS 10.13 or newer}}}
+// expected-error@-17 {{aligned deallocation function of type 'void (void *, enum std::align_val_t) noexcept' is only available on macOS 10.13 or newer}}}
+#endif
+
+// expected-note@-20 2 {{if you supply your own aligned allocation functions}}
+#endif
+
// No errors if user-defined aligned allocation functions are available.
void *operator new(std::size_t __sz, std::align_val_t) {
static char array[256];
diff --git a/test/SemaObjC/attr-swift.m b/test/SemaObjC/attr-swift.m
new file mode 100644
index 0000000..bdcbbd0
--- /dev/null
+++ b/test/SemaObjC/attr-swift.m
@@ -0,0 +1,217 @@
+// RUN: %clang_cc1 -verify -fsyntax-only -fobjc-arc -fblocks %s
+
+// --- swift_private ---
+
+__attribute__((swift_private))
+@protocol FooProto
+@end
+
+__attribute__((swift_private))
+@interface Foo
+@end
+
+@interface Bar
+@property id prop __attribute__((swift_private));
+- (void)instMethod __attribute__((swift_private));
++ (instancetype)bar __attribute__((swift_private));
+@end
+
+void function(id) __attribute__((swift_private));
+
+struct __attribute__((swift_private)) Point {
+ int x;
+ int y;
+};
+
+enum __attribute__((swift_private)) Colors {
+ Red, Green, Blue
+};
+
+typedef struct {
+ float x, y, z;
+} Point3D __attribute__((swift_private));
+
+
+// --- swift_name ---
+
+__attribute__((swift_name("SNFooType")))
+@protocol SNFoo
+@end
+
+__attribute__((swift_name("SNFooClass")))
+@interface SNFoo <SNFoo>
+- (instancetype)init __attribute__((swift_name("init()")));
+- (instancetype)initWithValue:(int)value __attribute__((swift_name("fooWithValue(_:)")));
+
++ (void)refresh __attribute__((swift_name("refresh()")));
+
++ (instancetype)foo __attribute__((swift_name("foo()")));
++ (SNFoo *)fooWithValue:(int)value __attribute__((swift_name("foo(value:)")));
++ (SNFoo *)fooWithValue:(int)value value:(int)value2 __attribute__((swift_name("foo(value:extra:)")));
++ (SNFoo *)fooWithConvertingValue:(int)value value:(int)value2 __attribute__((swift_name("init(_:extra:)")));
+
++ (SNFoo *)fooWithOtherValue:(int)value __attribute__((swift_name("init"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
++ (SNFoo *)fooWithAnotherValue:(int)value __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
++ (SNFoo *)fooWithYetAnotherValue:(int)value __attribute__((swift_name("foo(value:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 1; got 2)}}
+
++ (SNFoo *)fooAndReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning
++ (SNFoo *)fooWithValue:(int)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(value:)"))); // no-warning
++ (SNFoo *)fooFromErrorCode:(const int *)errorCode __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
++ (SNFoo *)fooWithValue:(int)value fromErrorCode:(const int *)errorCode __attribute__((swift_name("foo(value:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}}
++ (SNFoo *)fooWithPointerA:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo()"))); // no-warning
++ (SNFoo *)fooWithPointerB:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:)"))); // no-warning
++ (SNFoo *)fooWithPointerC:(int *)value andReturnErrorCode:(int *)errorCode __attribute__((swift_name("foo(pointer:errorCode:)"))); // no-warning
++ (SNFoo *)fooWithOtherFoo:(SNFoo *)other __attribute__((swift_name("foo()"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+
++ (instancetype)specialFoo __attribute__((swift_name("init(options:)")));
++ (instancetype)specialBar __attribute__((swift_name("init(options:extra:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 2)}}
++ (instancetype)specialBaz __attribute__((swift_name("init(_:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}}
++ (instancetype)specialGarply __attribute__((swift_name("foo(options:)"))); // expected-warning {{too many parameters in 'swift_name' attribute (expected 0; got 1)}}
+
++ (instancetype)trailingParen __attribute__((swift_name("foo("))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
++ (instancetype)trailingColon:(int)value __attribute__((swift_name("foo(value)"))); // expected-warning {{parameter of 'swift_name' attribute must be a Swift function name string}}
++ (instancetype)initialIgnore:(int)value __attribute__((swift_name("_(value:)"))); // expected-warning {{'swift_name' attribute has invalid identifier for base name}}
++ (instancetype)middleOmitted:(int)value __attribute__((swift_name("foo(:)"))); // expected-warning {{'swift_name' attribute has invalid identifier for parameter name}}
+
+@property(strong) id someProp __attribute__((swift_name("prop")));
+@end
+
+enum __attribute__((swift_name("MoreColors"))) MoreColors {
+ Cyan,
+ Magenta,
+ Yellow __attribute__((swift_name("RoseGold"))),
+ Black __attribute__((swift_name("SpaceGrey()"))) // expected-warning {{'swift_name' attribute has invalid identifier for base name}}
+};
+
+struct __attribute__((swift_name("FooStruct"))) BarStruct {
+ int x, y, z __attribute__((swift_name("zed")));
+};
+
+int global_int __attribute__((swift_name("GlobalInt")));
+
+void foo1(int i) __attribute__((swift_name("foo"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+void foo2(int i) __attribute__((swift_name("foo()"))); // expected-warning{{too few parameters in 'swift_name' attribute (expected 1; got 0)}}
+void foo2(int i) __attribute__((swift_name("foo(a:b:)"))); // expected-warning{{too many parameters in 'swift_name' attribute (expected 1; got 2)}}
+void foo3(int i, int j) __attribute__((swift_name("fooWithX(_:y:)"))); // okay
+void foo4(int i, int *error) __attribute__((swift_name("fooWithA(_:)"))); // okay
+
+typedef int some_int_type __attribute__((swift_name("SomeInt")));
+
+struct Point3D createPoint3D(float x, float y, float z) __attribute__((swift_name("Point3D.init(x:y:z:)")));
+struct Point3D rotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(self:radians:)")));
+struct Point3D badRotatePoint3D(Point3D point, float radians) __attribute__((swift_name("Point3D.rotate(radians:)"))); // expected-warning {{too few parameters in 'swift_name' attribute (expected 2; got 1)}}
+
+extern struct Point3D identityPoint __attribute__((swift_name("Point3D.identity")));
+
+// Getters and setters.
+float Point3DGetMagnitude(Point3D point) __attribute__((swift_name("getter:Point3D.magnitude(self:)")));
+
+float Point3DGetMagnitudeAndSomethingElse(Point3D point, float wat) __attribute__((swift_name("getter:Point3D.magnitude(self:wat:)"))); // expected-warning {{'swift_name' attribute for getter must not take any parameters besides 'self:'}}
+
+float Point3DGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.radius(self:)")));
+void Point3DSetRadius(Point3D point, float radius) __attribute__((swift_name("setter:Point3D.radius(self:newValue:)")));
+
+float Point3DPreGetRadius(Point3D point) __attribute__((swift_name("getter:Point3D.preRadius(self:)")));
+void Point3DPreSetRadius(float radius, Point3D point) __attribute__((swift_name("setter:Point3D.preRadius(newValue:self:)")));
+
+void Point3DSetRadiusAndSomethingElse(Point3D point, float radius, float wat) __attribute__((swift_name("setter:Point3D.radius(self:newValue:wat:)"))); // expected-warning {{'swift_name' attribute for setter must take one parameter for new value}}
+
+float Point3DGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)")));
+float Point3DSetComponent(Point3D point, unsigned index, float value) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:)")));
+
+float Point3DGetMatrixComponent(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("getter:Point3D.subscript(self:x:y:)")));
+void Point3DSetMatrixComponent(Point3D point, unsigned x, float value, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:newValue:y:)")));
+
+float Point3DSetWithoutNewValue(Point3D point, unsigned x, unsigned y) __attribute__((swift_name("setter:Point3D.subscript(self:x:y:)"))); // expected-warning {{'swift_name' attribute for 'subscript' setter must take a 'newValue:' parameter}}
+
+float Point3DSubscriptButNotGetterSetter(Point3D point, unsigned x) __attribute__((swift_name("Point3D.subscript(self:_:)"))); // expected-warning {{'swift_name' attribute for 'subscript' must be a getter or setter}}
+
+void Point3DSubscriptSetterTwoNewValues(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("setter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-warning {{'swift_name' attribute for 'subscript' setter cannot take multiple 'newValue:' parameters}}
+float Point3DSubscriptGetterNewValue(Point3D point, unsigned x, float a, float b) __attribute__((swift_name("getter:Point3D.subscript(self:_:newValue:newValue:)"))); // expected-warning {{'swift_name' attribute for 'subscript' getter cannot take a 'newValue:' parameter}}
+
+void Point3DMethodWithNewValue(Point3D point, float newValue) __attribute__((swift_name("Point3D.method(self:newValue:)")));
+void Point3DMethodWithNewValues(Point3D point, float newValue, float newValueB) __attribute__((swift_name("Point3D.method(self:newValue:newValue:)")));
+
+float Point3DStaticSubscript(unsigned x) __attribute__((swift_name("getter:Point3D.subscript(_:)"))); // expected-warning {{'swift_name' attribute for 'subscript' must take a 'self:' parameter}}
+float Point3DStaticSubscriptNoArgs(void) __attribute__((swift_name("getter:Point3D.subscript()"))); // expected-warning {{'swift_name' attribute for 'subscript' must take at least one parameter}}
+
+float Point3DPreGetComponent(Point3D point, unsigned index) __attribute__((swift_name("getter:Point3D.subscript(self:_:)")));
+
+Point3D getCurrentPoint3D(void) __attribute__((swift_name("getter:currentPoint3D()")));
+
+void setCurrentPoint3D(Point3D point) __attribute__((swift_name("setter:currentPoint3D(newValue:)")));
+
+Point3D getLastPoint3D(void) __attribute__((swift_name("getter:lastPoint3D()")));
+
+void setLastPoint3D(Point3D point) __attribute__((swift_name("setter:lastPoint3D(newValue:)")));
+
+Point3D getZeroPoint(void) __attribute__((swift_name("getter:Point3D.zero()")));
+Point3D getZeroPointNoPrototype() __attribute__((swift_name("getter:Point3D.zeroNoPrototype()"))); // expected-warning{{'swift_name' attribute can only be applied to function declarations with prototypes}}
+void setZeroPoint(Point3D point) __attribute__((swift_name("setter:Point3D.zero(newValue:)")));
+
+Point3D badGetter1(int x) __attribute__((swift_name("getter:bad1(_:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+void badSetter1(void) __attribute__((swift_name("getter:bad1())"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+
+Point3D badGetter2(Point3D point) __attribute__((swift_name("getter:bad2(_:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+
+void badSetter2(Point3D point) __attribute__((swift_name("setter:bad2(self:))"))); // expected-warning{{parameter of 'swift_name' attribute must be a Swift function name string}}
+
+// --- swift_error ---
+
+@class NSError;
+
+typedef struct __attribute__((objc_bridge(NSError))) __CFError *CFErrorRef;
+
+@interface Erroneous
+- (_Bool) tom0: (NSError**) err __attribute__((swift_error(none)));
+- (_Bool) tom1: (NSError**) err __attribute__((swift_error(nonnull_error)));
+- (_Bool) tom2: (NSError**) err __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute with 'null_result' convention can only be applied to a method returning a pointer}}
+- (_Bool) tom3: (NSError**) err __attribute__((swift_error(nonzero_result)));
+- (_Bool) tom4: (NSError**) err __attribute__((swift_error(zero_result)));
+
+- (Undeclared) richard0: (NSError**) err __attribute__((swift_error(none))); // expected-error {{expected a type}}
+- (Undeclared) richard1: (NSError**) err __attribute__((swift_error(nonnull_error))); // expected-error {{expected a type}}
+- (Undeclared) richard2: (NSError**) err __attribute__((swift_error(null_result))); // expected-error {{expected a type}}
+// FIXME: the follow-on warnings should really be suppressed, but apparently having an ill-formed return type doesn't mark anything as invalid
+- (Undeclared) richard3: (NSError**) err __attribute__((swift_error(nonzero_result))); // expected-error {{expected a type}} expected-error {{can only be applied}}
+- (Undeclared) richard4: (NSError**) err __attribute__((swift_error(zero_result))); // expected-error {{expected a type}} expected-error {{can only be applied}}
+
+- (instancetype) harry0: (NSError**) err __attribute__((swift_error(none)));
+- (instancetype) harry1: (NSError**) err __attribute__((swift_error(nonnull_error)));
+- (instancetype) harry2: (NSError**) err __attribute__((swift_error(null_result)));
+- (instancetype) harry3: (NSError**) err __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute with 'nonzero_result' convention can only be applied to a method returning an integral type}}
+- (instancetype) harry4: (NSError**) err __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute with 'zero_result' convention can only be applied to a method returning an integral type}}
+
+- (instancetype) harry0 __attribute__((swift_error(none)));
+- (instancetype) harry1 __attribute__((swift_error(nonnull_error))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+- (instancetype) harry2 __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+- (instancetype) harry3 __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+- (instancetype) harry4 __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a method with an error parameter}}
+@end
+
+extern _Bool tom0(CFErrorRef *) __attribute__((swift_error(none)));
+extern _Bool tom1(CFErrorRef *) __attribute__((swift_error(nonnull_error)));
+extern _Bool tom2(CFErrorRef *) __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute with 'null_result' convention can only be applied to a function returning a pointer}}
+extern _Bool tom3(CFErrorRef *) __attribute__((swift_error(nonzero_result)));
+extern _Bool tom4(CFErrorRef *) __attribute__((swift_error(zero_result)));
+
+extern Undeclared richard0(CFErrorRef *) __attribute__((swift_error(none))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard1(CFErrorRef *) __attribute__((swift_error(nonnull_error))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard2(CFErrorRef *) __attribute__((swift_error(null_result))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); // expected-error {{unknown type name 'Undeclared'}}
+extern Undeclared richard4(CFErrorRef *) __attribute__((swift_error(zero_result))); // expected-error {{unknown type name 'Undeclared'}}
+
+extern void *harry0(CFErrorRef *) __attribute__((swift_error(none)));
+extern void *harry1(CFErrorRef *) __attribute__((swift_error(nonnull_error)));
+extern void *harry2(CFErrorRef *) __attribute__((swift_error(null_result)));
+extern void *harry3(CFErrorRef *) __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute with 'nonzero_result' convention can only be applied to a function returning an integral type}}
+extern void *harry4(CFErrorRef *) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute with 'zero_result' convention can only be applied to a function returning an integral type}}
+
+extern void *wilma0(void) __attribute__((swift_error(none)));
+extern void *wilma1(void) __attribute__((swift_error(nonnull_error))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+extern void *wilma2(void) __attribute__((swift_error(null_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+extern void *wilma3(void) __attribute__((swift_error(nonzero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+extern void *wilma4(void) __attribute__((swift_error(zero_result))); // expected-error {{'swift_error' attribute can only be applied to a function with an error parameter}}
+
+
+extern _Bool suzanne __attribute__((swift_error(none))); // expected-error {{'swift_error' attribute only applies to functions and methods}}
diff --git a/test/SemaObjC/attr-swift_newtype.c b/test/SemaObjC/attr-swift_newtype.c
new file mode 100644
index 0000000..62a20d1
--- /dev/null
+++ b/test/SemaObjC/attr-swift_newtype.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+// RUN: not %clang_cc1 -ast-dump %s | FileCheck %s
+
+typedef int T1 __attribute__((swift_newtype(struct)));
+typedef int T2 __attribute__((swift_newtype(enum)));
+
+typedef int T3 __attribute__((swift_wrapper(struct)));
+typedef int T4 __attribute__((swift_wrapper(enum)));
+
+typedef int T5;
+typedef int T5 __attribute__((swift_wrapper(struct)));
+typedef int T5;
+// CHECK-LABEL: TypedefDecl {{.+}} T5 'int'
+// CHECK-NEXT: BuiltinType {{.+}} 'int'
+// CHECK-NEXT: TypedefDecl {{.+}} T5 'int'
+// CHECK-NEXT: BuiltinType {{.+}} 'int'
+// CHECK-NEXT: SwiftNewtypeAttr {{.+}} NK_Struct
+// CHECK-NEXT: TypedefDecl {{.+}} T5 'int'
+// CHECK-NEXT: BuiltinType {{.+}} 'int'
+// CHECK-NEXT: SwiftNewtypeAttr {{.+}} NK_Struct
+
+typedef int Bad1 __attribute__((swift_newtype(bad))); // expected-warning{{'swift_newtype' attribute argument not supported: 'bad'}}
+typedef int Bad2 __attribute__((swift_newtype())); // expected-error{{argument required after attribute}}
+typedef int Bad3 __attribute__((swift_newtype(bad, badder)));
+ // expected-error@-1{{expected ')'}}
+ // expected-note@-2{{to match this '('}}
+ // expected-warning@-3{{'swift_newtype' attribute argument not supported: 'bad'}}
+
+
+// TODO: better error message below
+// FIXME: why is this a parse error, rather than Sema error triggering?
+struct Bad4 __attribute__((swift_newtype(struct))) { }; // expected-error{{expected identifier or '('}}
diff --git a/test/SemaObjC/attr-swift_objc_members.m b/test/SemaObjC/attr-swift_objc_members.m
new file mode 100644
index 0000000..c8781f8
--- /dev/null
+++ b/test/SemaObjC/attr-swift_objc_members.m
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+
+#if !__has_attribute(swift_objc_members)
+# error Cannot query presence of swift_objc_members attribute.
+#endif
+
+__attribute__((swift_objc_members))
+__attribute__((objc_root_class))
+@interface A
+@end
+
+__attribute__((swift_objc_members)) // expected-error{{'swift_objc_members' attribute only applies to Objective-C interfaces}}
+@protocol P
+@end
+
+__attribute__((swift_objc_members)) // expected-error{{'swift_objc_members' attribute only applies to Objective-C interfaces}}
+extern void foo(void);
diff --git a/test/SemaObjC/default-synthesize-1.m b/test/SemaObjC/default-synthesize-1.m
index 731aa86..573434b 100644
--- a/test/SemaObjC/default-synthesize-1.m
+++ b/test/SemaObjC/default-synthesize-1.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -Wobjc-missing-property-synthesis -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -fsyntax-only -Wobjc-missing-property-synthesis -verify -Wno-objc-root-class -triple=x86_64-apple-macos10.10 %s
// rdar://11295716
@interface NSObject
@@ -141,3 +141,17 @@
return _description; // expected-error {{use of undeclared identifier '_description'}}
}
@end
+
+@interface DontWarnOnUnavailable
+
+// No warning expected:
+@property (nonatomic, readonly) int un1 __attribute__((unavailable));
+@property (readwrite) int un2 __attribute__((availability(macos, unavailable)));
+
+@property (readwrite) int un3 __attribute__((availability(ios, unavailable))); // expected-warning {{auto property synthesis is synthesizing property not explicitly synthesized}}
+
+@end
+
+@implementation DontWarnOnUnavailable // expected-note {{detected while default synthesizing properties in class implementation}}
+
+@end
diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m
index 767d5ac..e44bd3c 100644
--- a/test/SemaObjC/format-strings-objc.m
+++ b/test/SemaObjC/format-strings-objc.m
@@ -265,6 +265,21 @@
NSLog(@"%2$[tt]@ %1$[tt]s", @"Foo", @"Bar"); // expected-warning {{object format flags cannot be used with 's' conversion specifier}}
}
+// Test os_log_format primitive with ObjC string literal format argument.
+void test_os_log_format(char c, const char *pc, int i, int *pi, void *p, void *buf, NSString *nss) {
+ __builtin_os_log_format(buf, @"");
+ __builtin_os_log_format(buf, @"%d"); // expected-warning {{more '%' conversions than data arguments}}
+ __builtin_os_log_format(buf, @"%d", i);
+ __builtin_os_log_format(buf, @"%P", p); // expected-warning {{using '%P' format specifier without precision}}
+ __builtin_os_log_format(buf, @"%.10P", p);
+ __builtin_os_log_format(buf, @"%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
+ __builtin_os_log_format(buf, @"%.*P", i, p);
+ __builtin_os_log_format(buf, @"%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
+
+ __builtin_os_log_format(buf, @"%{private}s", pc);
+ __builtin_os_log_format(buf, @"%@", nss);
+}
+
// rdar://23622446
@interface RD23622446_Tester: NSObject
diff --git a/test/SemaObjC/kindof.m b/test/SemaObjC/kindof.m
index 9d758d3..63ba18f 100644
--- a/test/SemaObjC/kindof.m
+++ b/test/SemaObjC/kindof.m
@@ -385,7 +385,7 @@
@end
@interface NSGeneric<ObjectType> : NSObject
-- (void)test:(__kindof ObjectType)T; // expected-note{{passing argument to parameter 'T' here}}
+- (void)test:(__kindof ObjectType)T;
- (void)mapUsingBlock:(id (^)(__kindof ObjectType))block;
@end
@implementation NSGeneric
@@ -395,14 +395,6 @@
}
@end
-void testGeneric(NSGeneric<NSString*> *generic) {
- NSObject *NSObject_obj;
- // Assign from NSObject_obj to __kindof NSString*.
- [generic test:NSObject_obj]; // expected-warning{{incompatible pointer types sending 'NSObject *' to parameter of type '__kindof NSString *'}}
- NSString *NSString_str;
- [generic test:NSString_str];
-}
-
// Check that clang doesn't crash when a type parameter is illegal.
@interface Array1<T> : NSObject
@end
diff --git a/test/SemaObjC/parameterized_classes_subst.m b/test/SemaObjC/parameterized_classes_subst.m
index da2d56f..f90ee90 100644
--- a/test/SemaObjC/parameterized_classes_subst.m
+++ b/test/SemaObjC/parameterized_classes_subst.m
@@ -426,36 +426,3 @@
// warning about likely protocol/class name typos.
// --------------------------------------------------------------------------
typedef NSArray<NSObject> ArrayOfNSObjectWarning; // expected-warning{{parameterized class 'NSArray' already conforms to the protocols listed; did you forget a '*'?}}
-
-// rdar://25060179
-@interface MyMutableDictionary<KeyType, ObjectType> : NSObject
-- (void)setObject:(ObjectType)obj forKeyedSubscript:(KeyType <NSCopying>)key; // expected-note{{passing argument to parameter 'obj' here}} \
- // expected-note{{passing argument to parameter 'key' here}}
-@end
-
-void bar(MyMutableDictionary<NSString *, NSString *> *stringsByString,
- NSNumber *n1, NSNumber *n2) {
- // We warn here when the key types do not match.
- stringsByString[n1] = n2; // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'}} \
- // expected-warning{{incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString<NSCopying> *'}}
-}
-
-@interface MyTest<K, V> : NSObject <NSCopying>
-- (V)test:(K)key;
-- (V)test2:(K)key; // expected-note{{previous definition is here}}
-- (void)mapUsingBlock:(id (^)(V))block;
-- (void)mapUsingBlock2:(id (^)(V))block; // expected-note{{previous definition is here}}
-@end
-
-@implementation MyTest
-- (id)test:(id)key {
- return key;
-}
-- (int)test2:(id)key{ // expected-warning{{conflicting return type in implementation}}
- return 0;
-}
-- (void)mapUsingBlock:(id (^)(id))block {
-}
-- (void)mapUsingBlock2:(id)block { // expected-warning{{conflicting parameter types in implementation}}
-}
-@end
diff --git a/test/SemaObjC/typo-correction.m b/test/SemaObjC/typo-correction.m
index f19ec1a..47e0ab0 100644
--- a/test/SemaObjC/typo-correction.m
+++ b/test/SemaObjC/typo-correction.m
@@ -51,3 +51,23 @@
}
@end
+// rdar://problem/33102722
+// Typo correction for a property when it has as correction candidates
+// synthesized ivar and a class name, both at the same edit distance.
+@class TypoCandidate;
+
+__attribute__ (( __objc_root_class__ ))
+@interface PropertyType
+@property int x;
+@end
+
+__attribute__ (( __objc_root_class__ ))
+@interface InterfaceC
+@property(assign) PropertyType *typoCandidate; // expected-note {{'_typoCandidate' declared here}}
+@end
+
+@implementation InterfaceC
+-(void)method {
+ typoCandidate.x = 0; // expected-error {{use of undeclared identifier 'typoCandidate'; did you mean '_typoCandidate'?}}
+}
+@end
diff --git a/test/SemaObjCXX/typo-correction.mm b/test/SemaObjCXX/typo-correction.mm
index 5e33bfb..8dcda79 100644
--- a/test/SemaObjCXX/typo-correction.mm
+++ b/test/SemaObjCXX/typo-correction.mm
@@ -36,3 +36,22 @@
float a = ((InvalidNameInIvarAndPropertyBase*)node)->_a; // expected-error {{use of undeclared identifier 'node'}}
float b = ((InvalidNameInIvarAndPropertyBase*)node)._b; // expected-error {{use of undeclared identifier 'node'}}
}
+
+// rdar://problem/33102722
+// Typo correction for a property when it has as correction candidates
+// synthesized ivar and a class name, both at the same edit distance.
+@class TypoCandidate;
+
+@interface PropertyType : NSObject
+@property int x;
+@end
+
+@interface InterfaceC : NSObject
+@property(assign) PropertyType *typoCandidate; // expected-note {{'_typoCandidate' declared here}}
+@end
+
+@implementation InterfaceC
+-(void)method {
+ typoCandidate.x = 0; // expected-error {{use of undeclared identifier 'typoCandidate'; did you mean '_typoCandidate'?}}
+}
+@end
diff --git a/test/SemaTemplate/crash-unparsed-exception.cpp b/test/SemaTemplate/crash-unparsed-exception.cpp
index 1226b35..3137d3f 100644
--- a/test/SemaTemplate/crash-unparsed-exception.cpp
+++ b/test/SemaTemplate/crash-unparsed-exception.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -fcxx-exceptions -fexceptions %s
+// expected-no-diagnostics
struct A {
virtual ~A();
@@ -11,7 +12,7 @@
~D() throw();
};
struct E : A {
- D<int> d; //expected-error{{exception specification is not available until end of class definition}}
+ D<int> d;
};
- B<int> b; //expected-note{{in instantiation of template class 'B<int>' requested here}}
+ B<int> b;
};
diff --git a/test/SemaTemplate/default-arguments-cxx0x.cpp b/test/SemaTemplate/default-arguments-cxx0x.cpp
index d9fa2b4..c24ed12 100644
--- a/test/SemaTemplate/default-arguments-cxx0x.cpp
+++ b/test/SemaTemplate/default-arguments-cxx0x.cpp
@@ -87,3 +87,30 @@
A<1> m_target;
};
}
+
+// rdar://problem/34167492
+// Template B is instantiated during checking if defaulted A copy constructor
+// is constexpr. For this we check if S<int> copy constructor is constexpr. And
+// for this we check S constructor template with default argument that mentions
+// template B. In turn, template instantiation triggers checking defaulted
+// members exception spec. The problem is that it checks defaulted members not
+// for instantiated class only, but all defaulted members so far. In this case
+// we try to check exception spec for A default constructor which requires
+// initializer for the field _a. But initializers are added after constexpr
+// check so we reject the code because cannot find _a initializer.
+namespace rdar34167492 {
+ template <typename T> struct B { using type = bool; };
+
+ template <typename T> struct S {
+ S() noexcept;
+
+ template <typename U, typename B<U>::type = true>
+ S(const S<U>&) noexcept;
+ };
+
+ class A {
+ A() noexcept = default;
+ A(const A&) noexcept = default;
+ S<int> _a{};
+ };
+}
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index 4976332..662b853 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -9,6 +9,7 @@
add_clang_subdirectory(clang-offload-bundler)
add_clang_subdirectory(c-index-test)
+add_clang_subdirectory(IndexStore)
add_clang_subdirectory(clang-rename)
@@ -32,3 +33,6 @@
# libclang may require clang-tidy in clang-tools-extra.
add_clang_subdirectory(libclang)
+
+add_clang_subdirectory(clang-refactor-test)
+
diff --git a/tools/IndexStore/CMakeLists.txt b/tools/IndexStore/CMakeLists.txt
new file mode 100644
index 0000000..8ad6499
--- /dev/null
+++ b/tools/IndexStore/CMakeLists.txt
@@ -0,0 +1,94 @@
+include(CheckIncludeFiles)
+
+set(SOURCES
+ IndexStore.cpp
+
+ ADDITIONAL_HEADERS
+ ../../include/indexstore/indexstore.h
+ ../../include/indexstore/IndexStoreCXX.h
+ )
+
+set(LIBS
+ clangDirectoryWatcher
+ clangIndex
+)
+
+set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/IndexStore.exports)
+
+set(ENABLE_SHARED SHARED)
+
+if(WIN32)
+ set(output_name "libIndexStore")
+else()
+ set(output_name "IndexStore")
+endif()
+
+# FIXME: needs to be ported to non-Apple platforms.
+if(APPLE)
+
+add_clang_library(IndexStore ${ENABLE_SHARED} ${ENABLE_STATIC}
+ OUTPUT_NAME ${output_name}
+ ${SOURCES}
+
+ LINK_LIBS
+ ${LIBS}
+
+ LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ Core
+ Support
+ )
+
+set(INDEXSTORE_LIBRARY_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}")
+
+if(ENABLE_SHARED)
+ if(WIN32)
+ set_target_properties(IndexStore
+ PROPERTIES
+ VERSION ${INDEXSTORE_LIBRARY_VERSION}
+ DEFINE_SYMBOL _CINDEX_LIB_)
+ elseif(APPLE)
+ set(INDEXSTORE_LINK_FLAGS " -Wl,-compatibility_version -Wl,1")
+ set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -Wl,-current_version -Wl,${INDEXSTORE_LIBRARY_VERSION}")
+
+ check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H)
+ if(HAVE_CORESERVICES_H)
+ set(INDEXSTORE_LINK_FLAGS "${INDEXSTORE_LINK_FLAGS} -framework CoreServices")
+ endif()
+
+ set_property(TARGET IndexStore APPEND_STRING PROPERTY
+ LINK_FLAGS ${INDEXSTORE_LINK_FLAGS})
+ else()
+ set_target_properties(IndexStore
+ PROPERTIES
+ VERSION ${INDEXSTORE_LIBRARY_VERSION}
+ DEFINE_SYMBOL _CINDEX_LIB_)
+ endif()
+endif()
+
+if (LLVM_INSTALL_TOOLCHAIN_ONLY)
+ install(TARGETS IndexStore
+ COMPONENT IndexStore
+ LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
+ ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
+ RUNTIME DESTINATION bin)
+
+ if (NOT CMAKE_CONFIGURATION_TYPES)
+ add_custom_target(install-IndexStore
+ DEPENDS IndexStore
+ COMMAND "${CMAKE_COMMAND}"
+ -DCMAKE_INSTALL_COMPONENT=IndexStore
+ -P "${CMAKE_BINARY_DIR}/cmake_install.cmake")
+ endif()
+endif()
+
+set(INDEXSTORE_HEADERS_INSTALL_DESTINATION "local/include")
+
+install(DIRECTORY ../../include/indexstore
+ COMPONENT IndexStore
+ DESTINATION "${INDEXSTORE_HEADERS_INSTALL_DESTINATION}"
+ FILES_MATCHING
+ PATTERN "*.h"
+ PATTERN ".svn" EXCLUDE
+ )
+endif()
diff --git a/tools/IndexStore/IndexStore.cpp b/tools/IndexStore/IndexStore.cpp
new file mode 100644
index 0000000..4226524
--- /dev/null
+++ b/tools/IndexStore/IndexStore.cpp
@@ -0,0 +1,647 @@
+//===- IndexStore.cpp - Index store API -----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the API for the index store.
+//
+//===----------------------------------------------------------------------===//
+
+#include "indexstore/indexstore.h"
+#include "clang/Index/IndexDataStore.h"
+#include "clang/Index/IndexDataStoreSymbolUtils.h"
+#include "clang/Index/IndexRecordReader.h"
+#include "clang/Index/IndexUnitReader.h"
+#include "clang/Index/IndexUnitWriter.h"
+#include "clang/DirectoryWatcher/DirectoryWatcher.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Chrono.h"
+#include <Block.h>
+
+using namespace clang;
+using namespace clang::index;
+using namespace llvm;
+
+static indexstore_string_ref_t toIndexStoreString(StringRef str) {
+ return indexstore_string_ref_t{ str.data(), str.size() };
+}
+
+static timespec toTimeSpec(sys::TimePoint<> tp) {
+ std::chrono::seconds sec = std::chrono::time_point_cast<std::chrono::seconds>(
+ tp).time_since_epoch();
+ std::chrono::nanoseconds nsec =
+ std::chrono::time_point_cast<std::chrono::nanoseconds>(tp - sec)
+ .time_since_epoch();
+ timespec ts;
+ ts.tv_sec = sec.count();
+ ts.tv_nsec = nsec.count();
+ return ts;
+}
+
+namespace {
+
+struct IndexStoreError {
+ std::string Error;
+};
+
+} // anonymous namespace
+
+const char *
+indexstore_error_get_description(indexstore_error_t err) {
+ return static_cast<IndexStoreError*>(err)->Error.c_str();
+}
+
+void
+indexstore_error_dispose(indexstore_error_t err) {
+ delete static_cast<IndexStoreError*>(err);
+}
+
+unsigned
+indexstore_format_version(void) {
+ return IndexDataStore::getFormatVersion();
+}
+
+indexstore_t
+indexstore_store_create(const char *store_path, indexstore_error_t *c_error) {
+ std::unique_ptr<IndexDataStore> store;
+ std::string error;
+ store = IndexDataStore::create(store_path, error);
+ if (!store) {
+ if (c_error)
+ *c_error = new IndexStoreError{ error };
+ return nullptr;
+ }
+ return store.release();
+}
+
+void
+indexstore_store_dispose(indexstore_t store) {
+ delete static_cast<IndexDataStore*>(store);
+}
+
+#if INDEXSTORE_HAS_BLOCKS
+bool
+indexstore_store_units_apply(indexstore_t c_store, unsigned sorted,
+ bool(^applier)(indexstore_string_ref_t unit_name)) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ return store->foreachUnitName(sorted, [&](StringRef unitName) -> bool {
+ return applier(toIndexStoreString(unitName));
+ });
+}
+
+size_t
+indexstore_unit_event_notification_get_events_count(indexstore_unit_event_notification_t c_evtnote) {
+ auto *evtnote = static_cast<IndexDataStore::UnitEventNotification*>(c_evtnote);
+ return evtnote->Events.size();
+}
+
+indexstore_unit_event_t
+indexstore_unit_event_notification_get_event(indexstore_unit_event_notification_t c_evtnote, size_t index) {
+ auto *evtnote = static_cast<IndexDataStore::UnitEventNotification*>(c_evtnote);
+ return (indexstore_unit_event_t)&evtnote->Events[index];
+}
+
+bool
+indexstore_unit_event_notification_is_initial(indexstore_unit_event_notification_t c_evtnote) {
+ auto *evtnote = static_cast<IndexDataStore::UnitEventNotification*>(c_evtnote);
+ return evtnote->IsInitial;
+}
+
+indexstore_unit_event_kind_t
+indexstore_unit_event_get_kind(indexstore_unit_event_t c_evt) {
+ auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt);
+ indexstore_unit_event_kind_t k;
+ switch (evt->Kind) {
+ case IndexDataStore::UnitEventKind::Added:
+ k = INDEXSTORE_UNIT_EVENT_ADDED; break;
+ case IndexDataStore::UnitEventKind::Removed:
+ k = INDEXSTORE_UNIT_EVENT_REMOVED; break;
+ case IndexDataStore::UnitEventKind::Modified:
+ k = INDEXSTORE_UNIT_EVENT_MODIFIED; break;
+ case IndexDataStore::UnitEventKind::DirectoryDeleted:
+ k = INDEXSTORE_UNIT_EVENT_DIRECTORY_DELETED; break;
+ }
+ return k;
+}
+
+indexstore_string_ref_t
+indexstore_unit_event_get_unit_name(indexstore_unit_event_t c_evt) {
+ auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt);
+ return toIndexStoreString(evt->UnitName);
+}
+
+timespec
+indexstore_unit_event_get_modification_time(indexstore_unit_event_t c_evt) {
+ auto *evt = static_cast<IndexDataStore::UnitEvent*>(c_evt);
+ return evt->ModTime;
+}
+
+void
+indexstore_store_set_unit_event_handler(indexstore_t c_store,
+ indexstore_unit_event_handler_t blk_handler) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ if (!blk_handler) {
+ store->setUnitEventHandler(nullptr);
+ return;
+ }
+
+ class BlockWrapper {
+ indexstore_unit_event_handler_t blk_handler;
+ public:
+ BlockWrapper(indexstore_unit_event_handler_t handler) {
+ blk_handler = Block_copy(handler);
+ }
+ BlockWrapper(const BlockWrapper &other) {
+ blk_handler = Block_copy(other.blk_handler);
+ }
+ ~BlockWrapper() {
+ Block_release(blk_handler);
+ }
+
+ void operator()(indexstore_unit_event_notification_t evt_note) const {
+ blk_handler(evt_note);
+ }
+ };
+
+ BlockWrapper handler(blk_handler);
+
+ store->setUnitEventHandler([handler](IndexDataStore::UnitEventNotification evtNote) {
+ handler(&evtNote);
+ });
+}
+#endif
+
+bool
+indexstore_store_start_unit_event_listening(indexstore_t c_store,
+ indexstore_unit_event_listen_options_t *client_opts,
+ size_t listen_options_struct_size,
+ indexstore_error_t *c_error) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ indexstore_unit_event_listen_options_t listen_opts;
+ memset(&listen_opts, 0, sizeof(listen_opts));
+ unsigned clientOptSize = listen_options_struct_size < sizeof(listen_opts)
+ ? listen_options_struct_size : sizeof(listen_opts);
+ memcpy(&listen_opts, client_opts, clientOptSize);
+
+ std::string error;
+ auto createFn = [](StringRef Path, AbstractDirectoryWatcher::EventReceiver Receiver, bool waitInitialSync, std::string &Error)
+ -> std::unique_ptr<AbstractDirectoryWatcher> {
+ return DirectoryWatcher::create(Path, std::move(Receiver), waitInitialSync, Error);
+ };
+ bool err = store->startEventListening(createFn, listen_opts.wait_initial_sync, error);
+ if (err && c_error)
+ *c_error = new IndexStoreError{ error };
+ return err;
+}
+
+void
+indexstore_store_stop_unit_event_listening(indexstore_t c_store) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ store->stopEventListening();
+}
+
+void
+indexstore_store_discard_unit(indexstore_t c_store, const char *unit_name) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ store->discardUnit(unit_name);
+}
+
+void
+indexstore_store_discard_record(indexstore_t c_store, const char *record_name) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ store->discardRecord(record_name);
+}
+
+void
+indexstore_store_purge_stale_data(indexstore_t c_store) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ store->purgeStaleData();
+}
+
+indexstore_symbol_kind_t
+indexstore_symbol_get_kind(indexstore_symbol_t sym) {
+ return getIndexStoreKind(static_cast<IndexRecordDecl *>(sym)->SymInfo.Kind);
+}
+
+indexstore_symbol_subkind_t
+indexstore_symbol_get_subkind(indexstore_symbol_t sym) {
+ return getIndexStoreSubKind(static_cast<IndexRecordDecl *>(sym)->SymInfo.SubKind);
+}
+
+indexstore_symbol_language_t
+indexstore_symbol_get_language(indexstore_symbol_t sym) {
+ return getIndexStoreLang(static_cast<IndexRecordDecl *>(sym)->SymInfo.Lang);
+}
+
+uint64_t
+indexstore_symbol_get_properties(indexstore_symbol_t sym) {
+ return getIndexStoreProperties(static_cast<IndexRecordDecl *>(sym)->SymInfo.Properties);
+}
+
+uint64_t
+indexstore_symbol_get_roles(indexstore_symbol_t sym) {
+ return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->Roles);
+}
+
+uint64_t
+indexstore_symbol_get_related_roles(indexstore_symbol_t sym) {
+ return getIndexStoreRoles(static_cast<IndexRecordDecl *>(sym)->RelatedRoles);
+}
+
+indexstore_string_ref_t
+indexstore_symbol_get_name(indexstore_symbol_t sym) {
+ auto *D = static_cast<IndexRecordDecl*>(sym);
+ return toIndexStoreString(D->Name);
+}
+
+indexstore_string_ref_t
+indexstore_symbol_get_usr(indexstore_symbol_t sym) {
+ auto *D = static_cast<IndexRecordDecl*>(sym);
+ return toIndexStoreString(D->USR);
+}
+
+indexstore_string_ref_t
+indexstore_symbol_get_codegen_name(indexstore_symbol_t sym) {
+ auto *D = static_cast<IndexRecordDecl*>(sym);
+ return toIndexStoreString(D->CodeGenName);
+}
+
+uint64_t
+indexstore_symbol_relation_get_roles(indexstore_symbol_relation_t sym_rel) {
+ return getIndexStoreRoles(static_cast<IndexRecordRelation *>(sym_rel)->Roles);
+}
+
+indexstore_symbol_t
+indexstore_symbol_relation_get_symbol(indexstore_symbol_relation_t sym_rel) {
+ return (indexstore_symbol_t)static_cast<IndexRecordRelation*>(sym_rel)->Dcl;
+}
+
+indexstore_symbol_t
+indexstore_occurrence_get_symbol(indexstore_occurrence_t occur) {
+ return (indexstore_symbol_t)static_cast<IndexRecordOccurrence*>(occur)->Dcl;
+}
+
+#if INDEXSTORE_HAS_BLOCKS
+bool
+indexstore_occurrence_relations_apply(indexstore_occurrence_t occur,
+ bool(^applier)(indexstore_symbol_relation_t symbol_rel)) {
+ auto *recOccur = static_cast<IndexRecordOccurrence*>(occur);
+ for (auto &rel : recOccur->Relations) {
+ if (!applier(&rel))
+ return false;
+ }
+ return true;
+}
+#endif
+
+uint64_t
+indexstore_occurrence_get_roles(indexstore_occurrence_t occur) {
+ return static_cast<IndexRecordOccurrence*>(occur)->Roles;
+}
+
+void
+indexstore_occurrence_get_line_col(indexstore_occurrence_t occur,
+ unsigned *line, unsigned *column) {
+ auto *recOccur = static_cast<IndexRecordOccurrence*>(occur);
+ if (line)
+ *line = recOccur->Line;
+ if (column)
+ *column = recOccur->Column;
+}
+
+typedef void *indexstore_record_reader_t;
+
+indexstore_record_reader_t
+indexstore_record_reader_create(indexstore_t c_store, const char *record_name,
+ indexstore_error_t *c_error) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ std::unique_ptr<IndexRecordReader> reader;
+ std::string error;
+ reader = IndexRecordReader::createWithRecordFilename(record_name,
+ store->getFilePath(),
+ error);
+ if (!reader) {
+ if (c_error)
+ *c_error = new IndexStoreError{ error };
+ return nullptr;
+ }
+ return reader.release();
+}
+
+void
+indexstore_record_reader_dispose(indexstore_record_reader_t rdr) {
+ auto *reader = static_cast<IndexRecordReader *>(rdr);
+ delete reader;
+}
+
+#if INDEXSTORE_HAS_BLOCKS
+/// Goes through the symbol data and passes symbols to \c receiver, for the
+/// symbol data that \c filter returns true on.
+///
+/// This allows allocating memory only for the record symbols that the caller is
+/// interested in.
+bool
+indexstore_record_reader_search_symbols(indexstore_record_reader_t rdr,
+ bool(^filter)(indexstore_symbol_t symbol, bool *stop),
+ void(^receiver)(indexstore_symbol_t symbol)) {
+ auto *reader = static_cast<IndexRecordReader *>(rdr);
+
+ auto filterFn = [&](const IndexRecordDecl &D) -> IndexRecordReader::DeclSearchReturn {
+ bool stop = false;
+ bool accept = filter((indexstore_symbol_t)&D, &stop);
+ return { accept, !stop };
+ };
+ auto receiverFn = [&](const IndexRecordDecl *D) {
+ receiver((indexstore_symbol_t)D);
+ };
+
+ return reader->searchDecls(filterFn, receiverFn);
+}
+
+bool
+indexstore_record_reader_symbols_apply(indexstore_record_reader_t rdr,
+ bool nocache,
+ bool(^applier)(indexstore_symbol_t symbol)) {
+ auto *reader = static_cast<IndexRecordReader *>(rdr);
+ auto receiverFn = [&](const IndexRecordDecl *D) -> bool {
+ return applier((indexstore_symbol_t)D);
+ };
+ return reader->foreachDecl(nocache, receiverFn);
+}
+
+bool
+indexstore_record_reader_occurrences_apply(indexstore_record_reader_t rdr,
+ bool(^applier)(indexstore_occurrence_t occur)) {
+ auto *reader = static_cast<IndexRecordReader *>(rdr);
+ auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool {
+ return applier((indexstore_occurrence_t)&RO);
+ };
+ return reader->foreachOccurrence(receiverFn);
+}
+
+bool
+indexstore_record_reader_occurrences_in_line_range_apply(indexstore_record_reader_t rdr,
+ unsigned line_start,
+ unsigned line_count,
+ bool(^applier)(indexstore_occurrence_t occur)) {
+ auto *reader = static_cast<IndexRecordReader *>(rdr);
+ auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool {
+ return applier((indexstore_occurrence_t)&RO);
+ };
+ return reader->foreachOccurrenceInLineRange(line_start, line_count, receiverFn);
+}
+
+/// \param symbols if non-zero \c symbols_count, indicates the list of symbols
+/// that we want to get occurrences for. An empty array indicates that we want
+/// occurrences for all symbols.
+/// \param related_symbols Same as \c symbols but for related symbols.
+bool
+indexstore_record_reader_occurrences_of_symbols_apply(indexstore_record_reader_t rdr,
+ indexstore_symbol_t *symbols, size_t symbols_count,
+ indexstore_symbol_t *related_symbols, size_t related_symbols_count,
+ bool(^applier)(indexstore_occurrence_t occur)) {
+ auto *reader = static_cast<IndexRecordReader *>(rdr);
+ auto receiverFn = [&](const IndexRecordOccurrence &RO) -> bool {
+ return applier((indexstore_occurrence_t)&RO);
+ };
+ return reader->foreachOccurrence({(IndexRecordDecl**)symbols, symbols_count},
+ {(IndexRecordDecl**)related_symbols, related_symbols_count},
+ receiverFn);
+}
+#endif
+
+size_t
+indexstore_store_get_unit_name_from_output_path(indexstore_t store,
+ const char *output_path,
+ char *name_buf,
+ size_t buf_size) {
+ SmallString<256> unitName;
+ IndexUnitWriter::getUnitNameForAbsoluteOutputFile(output_path, unitName);
+ size_t nameLen = unitName.size();
+ strlcpy(name_buf, unitName.c_str(), buf_size);
+ return nameLen;
+}
+
+bool
+indexstore_store_get_unit_modification_time(indexstore_t c_store,
+ const char *unit_name,
+ int64_t *seconds,
+ int64_t *nanoseconds,
+ indexstore_error_t *c_error) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ std::string error;
+ // FIXME: This provides mod time with second-only accuracy.
+ auto optModTime = IndexUnitReader::getModificationTimeForUnit(unit_name,
+ store->getFilePath(), error);
+ if (!optModTime) {
+ if (c_error)
+ *c_error = new IndexStoreError{ error };
+ return true;
+ }
+
+ timespec ts = toTimeSpec(*optModTime);
+ if (seconds)
+ *seconds = ts.tv_sec;
+ if (nanoseconds)
+ *nanoseconds = ts.tv_nsec;
+
+ return false;
+}
+
+indexstore_unit_reader_t
+indexstore_unit_reader_create(indexstore_t c_store, const char *unit_name,
+ indexstore_error_t *c_error) {
+ IndexDataStore *store = static_cast<IndexDataStore*>(c_store);
+ std::unique_ptr<IndexUnitReader> reader;
+ std::string error;
+ reader = IndexUnitReader::createWithUnitFilename(unit_name,
+ store->getFilePath(), error);
+ if (!reader) {
+ if (c_error)
+ *c_error = new IndexStoreError{ error };
+ return nullptr;
+ }
+ return reader.release();
+}
+
+void
+indexstore_unit_reader_dispose(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ delete reader;
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_provider_identifier(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getProviderIdentifier());
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_provider_version(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getProviderVersion());
+}
+
+void
+indexstore_unit_reader_get_modification_time(indexstore_unit_reader_t rdr,
+ int64_t *seconds,
+ int64_t *nanoseconds) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ // FIXME: This provides mod time with second-only accuracy.
+ sys::TimePoint<> timeVal = reader->getModificationTime();
+ timespec ts = toTimeSpec(timeVal);
+ if (seconds)
+ *seconds = ts.tv_sec;
+ if (nanoseconds)
+ *nanoseconds = ts.tv_nsec;
+}
+
+bool
+indexstore_unit_reader_is_system_unit(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return reader->isSystemUnit();
+}
+
+bool
+indexstore_unit_reader_is_module_unit(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return reader->isModuleUnit();
+}
+
+bool
+indexstore_unit_reader_is_debug_compilation(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return reader->isDebugCompilation();
+}
+
+bool
+indexstore_unit_reader_has_main_file(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return reader->hasMainFile();
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_main_file(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getMainFilePath());
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_module_name(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getModuleName());
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_working_dir(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getWorkingDirectory());
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_output_file(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getOutputFile());
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_sysroot_path(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getSysrootPath());
+}
+
+indexstore_string_ref_t
+indexstore_unit_reader_get_target(indexstore_unit_reader_t rdr) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return toIndexStoreString(reader->getTarget());
+}
+
+indexstore_unit_dependency_kind_t
+indexstore_unit_dependency_get_kind(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ switch (dep->Kind) {
+ case IndexUnitReader::DependencyKind::Unit: return INDEXSTORE_UNIT_DEPENDENCY_UNIT;
+ case IndexUnitReader::DependencyKind::Record: return INDEXSTORE_UNIT_DEPENDENCY_RECORD;
+ case IndexUnitReader::DependencyKind::File: return INDEXSTORE_UNIT_DEPENDENCY_FILE;
+ }
+}
+
+bool
+indexstore_unit_dependency_is_system(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ return dep->IsSystem;
+}
+
+indexstore_string_ref_t
+indexstore_unit_dependency_get_filepath(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ return toIndexStoreString(dep->FilePath);
+}
+
+indexstore_string_ref_t
+indexstore_unit_dependency_get_modulename(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ return toIndexStoreString(dep->ModuleName);
+}
+
+indexstore_string_ref_t
+indexstore_unit_dependency_get_name(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ return toIndexStoreString(dep->UnitOrRecordName);
+}
+
+time_t
+indexstore_unit_dependency_get_modification_time(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ return dep->ModTime;
+}
+
+size_t
+indexstore_unit_dependency_get_file_size(indexstore_unit_dependency_t c_dep) {
+ auto dep = static_cast<const IndexUnitReader::DependencyInfo*>(c_dep);
+ return dep->FileSize;
+}
+
+indexstore_string_ref_t
+indexstore_unit_include_get_source_path(indexstore_unit_include_t c_inc) {
+ auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc);
+ return toIndexStoreString(inc->SourcePath);
+}
+
+indexstore_string_ref_t
+indexstore_unit_include_get_target_path(indexstore_unit_include_t c_inc) {
+ auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc);
+ return toIndexStoreString(inc->TargetPath);
+}
+
+unsigned
+indexstore_unit_include_get_source_line(indexstore_unit_include_t c_inc) {
+ auto inc = static_cast<const IndexUnitReader::IncludeInfo*>(c_inc);
+ return inc->SourceLine;
+}
+
+#if INDEXSTORE_HAS_BLOCKS
+bool
+indexstore_unit_reader_dependencies_apply(indexstore_unit_reader_t rdr,
+ bool(^applier)(indexstore_unit_dependency_t)) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &depInfo) -> bool {
+ return applier((void*)&depInfo);
+ });
+}
+
+bool
+indexstore_unit_reader_includes_apply(indexstore_unit_reader_t rdr,
+ bool(^applier)(indexstore_unit_include_t)) {
+ auto reader = static_cast<IndexUnitReader*>(rdr);
+ return reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &incInfo) -> bool {
+ return applier((void*)&incInfo);
+ });
+}
+#endif
diff --git a/tools/IndexStore/IndexStore.exports b/tools/IndexStore/IndexStore.exports
new file mode 100644
index 0000000..70c174f
--- /dev/null
+++ b/tools/IndexStore/IndexStore.exports
@@ -0,0 +1,69 @@
+indexstore_error_get_description
+indexstore_error_dispose
+indexstore_format_version
+indexstore_store_create
+indexstore_store_dispose
+indexstore_store_get_unit_modification_time
+indexstore_store_get_unit_name_from_output_path
+indexstore_store_units_apply
+indexstore_store_set_unit_event_handler
+indexstore_store_start_unit_event_listening
+indexstore_store_stop_unit_event_listening
+indexstore_store_discard_unit
+indexstore_store_discard_record
+indexstore_store_purge_stale_data
+indexstore_symbol_get_kind
+indexstore_symbol_get_language
+indexstore_symbol_get_properties
+indexstore_symbol_get_roles
+indexstore_symbol_get_related_roles
+indexstore_symbol_get_subkind
+indexstore_symbol_get_name
+indexstore_symbol_get_usr
+indexstore_symbol_get_codegen_name
+indexstore_symbol_relation_get_roles
+indexstore_symbol_relation_get_symbol
+indexstore_occurrence_get_symbol
+indexstore_occurrence_get_roles
+indexstore_occurrence_get_line_col
+indexstore_occurrence_relations_apply
+indexstore_record_reader_create
+indexstore_record_reader_dispose
+indexstore_record_reader_search_symbols
+indexstore_record_reader_symbols_apply
+indexstore_record_reader_occurrences_apply
+indexstore_record_reader_occurrences_in_line_range_apply
+indexstore_record_reader_occurrences_of_symbols_apply
+indexstore_unit_dependency_get_kind
+indexstore_unit_dependency_get_filepath
+indexstore_unit_dependency_get_file_size
+indexstore_unit_dependency_get_modification_time
+indexstore_unit_dependency_get_modulename
+indexstore_unit_dependency_get_name
+indexstore_unit_dependency_is_system
+indexstore_unit_event_get_kind
+indexstore_unit_event_get_modification_time
+indexstore_unit_event_get_unit_name
+indexstore_unit_event_notification_get_event
+indexstore_unit_event_notification_get_events_count
+indexstore_unit_event_notification_is_initial
+indexstore_unit_reader_create
+indexstore_unit_reader_dispose
+indexstore_unit_reader_get_main_file
+indexstore_unit_reader_get_modification_time
+indexstore_unit_reader_get_module_name
+indexstore_unit_reader_get_provider_identifier
+indexstore_unit_reader_get_provider_version
+indexstore_unit_reader_get_working_dir
+indexstore_unit_reader_get_output_file
+indexstore_unit_reader_get_sysroot_path
+indexstore_unit_reader_get_target
+indexstore_unit_reader_dependencies_apply
+indexstore_unit_reader_includes_apply
+indexstore_unit_reader_has_main_file
+indexstore_unit_reader_is_debug_compilation
+indexstore_unit_reader_is_module_unit
+indexstore_unit_reader_is_system_unit
+indexstore_unit_include_get_source_path
+indexstore_unit_include_get_target_path
+indexstore_unit_include_get_source_line
diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt
index ad990e0..c8d33b9 100644
--- a/tools/c-index-test/CMakeLists.txt
+++ b/tools/c-index-test/CMakeLists.txt
@@ -1,3 +1,5 @@
+include(CheckIncludeFiles)
+
set(LLVM_LINK_COMPONENTS
support
)
@@ -5,8 +7,15 @@
add_clang_executable(c-index-test
c-index-test.c
core_main.cpp
+ JSONAggregation.cpp
)
+set(INDEXSTORE_LIB)
+set(CINDEXTEST_LIBS)
+if(APPLE)
+ set(INDEXSTORE_LIB IndexStore)
+endif()
+
if(NOT MSVC)
set_property(
SOURCE c-index-test.c
@@ -19,16 +28,20 @@
libclang_static
clangCodeGen
clangIndex
+ ${CINDEXTEST_LIBS}
)
else()
target_link_libraries(c-index-test
libclang
+ ${INDEXSTORE_LIB}
clangAST
clangBasic
clangCodeGen
+ clangDirectoryWatcher
clangFrontend
clangIndex
clangSerialization
+ ${CINDEXTEST_LIBS}
)
endif()
@@ -42,6 +55,13 @@
target_link_libraries(c-index-test ${LIBXML2_LIBRARIES})
endif()
+if(APPLE)
+ check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES_H)
+ if(HAVE_CORESERVICES_H)
+ target_link_libraries(c-index-test "-framework CoreServices")
+ endif()
+endif()
+
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if(INTERNAL_INSTALL_PREFIX)
set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin")
diff --git a/tools/c-index-test/JSONAggregation.cpp b/tools/c-index-test/JSONAggregation.cpp
new file mode 100644
index 0000000..c7f4136
--- /dev/null
+++ b/tools/c-index-test/JSONAggregation.cpp
@@ -0,0 +1,409 @@
+//===--- JSONAggregation.cpp - Index data aggregation in JSON format ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "JSONAggregation.h"
+#include "indexstore/IndexStoreCXX.h"
+#include "clang/Index/IndexDataStoreSymbolUtils.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::index;
+using namespace indexstore;
+using namespace llvm;
+
+#if INDEXSTORE_HAS_BLOCKS
+
+namespace {
+
+typedef size_t FilePathIndex;
+typedef size_t RecordIndex;
+typedef size_t SymbolIndex;
+
+struct UnitSourceInfo {
+ FilePathIndex FilePath;
+ SmallVector<RecordIndex, 2> AssociatedRecords;
+};
+
+struct UnitInfo {
+ std::string Name;
+ SmallVector<UnitSourceInfo, 8> Sources;
+ SmallVector<std::string, 3> UnitDepends;
+ FilePathIndex OutFile;
+ StringRef Triple;
+};
+
+struct SymbolInfo {
+ SymbolKind Kind;
+ SymbolLanguage Lang;
+ StringRef USR;
+ StringRef Name;
+ StringRef CodegenName;
+ SymbolRoleSet Roles = 0;
+ SymbolRoleSet RelatedRoles = 0;
+};
+
+struct SymbolRelationInfo {
+ SymbolIndex RelatedSymbol;
+ SymbolRoleSet Roles;
+ SymbolRelationInfo(SymbolIndex relSymbol, SymbolRoleSet roles)
+ : RelatedSymbol(relSymbol), Roles(roles) {}
+};
+
+struct SymbolOccurrenceInfo {
+ SymbolIndex Symbol;
+ SymbolRoleSet Roles = 0;
+ std::vector<SymbolRelationInfo> Relations;
+ unsigned Line;
+ unsigned Column;
+};
+
+struct RecordInfo {
+ SmallVector<SymbolOccurrenceInfo, 8> Occurrences;
+};
+
+class Aggregator {
+ IndexStore Store;
+
+ BumpPtrAllocator Allocator;
+
+ StringMap<FilePathIndex, BumpPtrAllocator &> FilePathIndices;
+ std::vector<StringRef> FilePaths;
+ StringMap<char, BumpPtrAllocator &> Triples;
+
+ std::vector<std::unique_ptr<UnitInfo>> Units;
+
+ StringMap<RecordIndex, BumpPtrAllocator &> RecordIndices;
+ std::vector<std::unique_ptr<RecordInfo>> Records;
+
+ StringMap<SymbolIndex, BumpPtrAllocator &> SymbolIndices;
+ std::vector<SymbolInfo> Symbols;
+
+public:
+ explicit Aggregator(IndexStore store)
+ : Store(std::move(store)),
+ FilePathIndices(Allocator),
+ Triples(Allocator),
+ RecordIndices(Allocator),
+ SymbolIndices(Allocator) {}
+
+ bool process();
+ void processUnit(StringRef name, IndexUnitReader &UnitReader);
+ void dumpJSON(raw_ostream &OS);
+
+private:
+ StringRef copyStr(StringRef str) {
+ if (str.empty())
+ return StringRef();
+ char *buf = Allocator.Allocate<char>(str.size());
+ std::copy(str.begin(), str.end(), buf);
+ return StringRef(buf, str.size());
+ }
+
+ StringRef getTripleString(StringRef inputTriple) {
+ return Triples.insert(std::make_pair(inputTriple, 0)).first->first();
+ }
+
+ FilePathIndex getFilePathIndex(StringRef path, StringRef workingDir);
+ RecordIndex getRecordIndex(StringRef recordFile);
+ SymbolIndex getSymbolIndex(IndexRecordSymbol sym);
+ std::unique_ptr<RecordInfo> processRecord(StringRef recordFile);
+};
+
+} // anonymous namespace
+
+bool Aggregator::process() {
+ bool succ = Store.foreachUnit(/*sorted=*/true, [&](StringRef unitName) -> bool {
+ std::string error;
+ auto unitReader = IndexUnitReader(Store, unitName, error);
+ if (!unitReader) {
+ errs() << "error opening unit file '" << unitName << "': " << error << '\n';
+ return false;
+ }
+
+ processUnit(unitName, unitReader);
+ return true;
+ });
+
+ return !succ;
+}
+
+void Aggregator::processUnit(StringRef name, IndexUnitReader &UnitReader) {
+ auto workDir = UnitReader.getWorkingDirectory();
+ auto unit = llvm::make_unique<UnitInfo>();
+ unit->Name = name;
+ unit->Triple = getTripleString(UnitReader.getTarget());
+ unit->OutFile = getFilePathIndex(UnitReader.getOutputFile(), workDir);
+
+ struct DepInfo {
+ UnitSourceInfo source;
+ std::string unitName;
+ };
+ SmallVector<DepInfo, 32> Deps;
+ UnitReader.foreachDependency([&](IndexUnitDependency dep) -> bool {
+ Deps.resize(Deps.size()+1);
+ auto &depInfo = Deps.back();
+ switch (dep.getKind()) {
+ case IndexUnitDependency::DependencyKind::Unit: {
+ depInfo.unitName = dep.getName();
+ StringRef filePath = dep.getFilePath();
+ if (!filePath.empty())
+ depInfo.source.FilePath = getFilePathIndex(filePath, workDir);
+ break;
+ }
+ case IndexUnitDependency::DependencyKind::Record: {
+ depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir);
+ RecordIndex recIndex = getRecordIndex(dep.getName());
+ depInfo.source.AssociatedRecords.push_back(recIndex);
+ break;
+ }
+ case IndexUnitDependency::DependencyKind::File:
+ depInfo.source.FilePath = getFilePathIndex(dep.getFilePath(), workDir);
+ }
+ return true;
+ });
+
+ unit->Sources.reserve(Deps.size());
+ for (auto &dep : Deps) {
+ if (!dep.unitName.empty()) {
+ unit->UnitDepends.emplace_back(std::move(dep.unitName));
+ } else {
+ unit->Sources.push_back(std::move(dep.source));
+ }
+ }
+
+ Units.push_back(std::move(unit));
+}
+
+FilePathIndex Aggregator::getFilePathIndex(StringRef path, StringRef workingDir) {
+ StringRef absPath;
+ SmallString<128> absPathBuf;
+ if (sys::path::is_absolute(path) || workingDir.empty()) {
+ absPath = path;
+ } else {
+ absPathBuf = workingDir;
+ sys::path::append(absPathBuf, path);
+ absPath = absPathBuf.str();
+ }
+
+ auto pair = FilePathIndices.insert(std::make_pair(absPath, FilePaths.size()));
+ bool wasInserted = pair.second;
+ if (wasInserted) {
+ FilePaths.push_back(pair.first->first());
+ }
+ return pair.first->second;
+}
+
+RecordIndex Aggregator::getRecordIndex(StringRef recordFile) {
+ auto pair = RecordIndices.insert(std::make_pair(recordFile, Records.size()));
+ bool wasInserted = pair.second;
+ if (wasInserted) {
+ Records.push_back(processRecord(recordFile));
+ }
+ return pair.first->second;
+}
+
+std::unique_ptr<RecordInfo> Aggregator::processRecord(StringRef recordFile) {
+ std::string error;
+ auto recordReader = IndexRecordReader(Store, recordFile, error);
+ if (!recordReader) {
+ errs() << "failed reading record file: " << recordFile << '\n';
+ ::exit(1);
+ }
+ auto record = llvm::make_unique<RecordInfo>();
+ recordReader.foreachOccurrence([&](IndexRecordOccurrence idxOccur) -> bool {
+ SymbolIndex symIdx = getSymbolIndex(idxOccur.getSymbol());
+ SymbolInfo &symInfo = Symbols[symIdx];
+ symInfo.Roles |= idxOccur.getRoles();
+ SymbolOccurrenceInfo occurInfo;
+ occurInfo.Symbol = symIdx;
+ idxOccur.foreachRelation([&](IndexSymbolRelation rel) -> bool {
+ SymbolIndex relsymIdx = getSymbolIndex(rel.getSymbol());
+ SymbolInfo &relsymInfo = Symbols[relsymIdx];
+ relsymInfo.RelatedRoles |= rel.getRoles();
+ occurInfo.Relations.emplace_back(relsymIdx, rel.getRoles());
+ return true;
+ });
+ occurInfo.Roles = idxOccur.getRoles();
+ std::tie(occurInfo.Line, occurInfo.Column) = idxOccur.getLineCol();
+ record->Occurrences.push_back(std::move(occurInfo));
+ return true;
+ });
+ return record;
+}
+
+SymbolIndex Aggregator::getSymbolIndex(IndexRecordSymbol sym) {
+ auto pair = SymbolIndices.insert(std::make_pair(sym.getUSR(), Symbols.size()));
+ bool wasInserted = pair.second;
+ if (wasInserted) {
+ SymbolInfo symInfo;
+ symInfo.Kind = getSymbolKind(sym.getKind());
+ symInfo.Lang = getSymbolLanguage(sym.getLanguage());
+ symInfo.USR = pair.first->first();
+ symInfo.Name = copyStr(sym.getName());
+ symInfo.CodegenName = copyStr(sym.getCodegenName());
+ Symbols.push_back(std::move(symInfo));
+ }
+ return pair.first->second;
+}
+
+
+void Aggregator::dumpJSON(raw_ostream &OS) {
+ OS << "{\n";
+ OS.indent(2) << "\"files\": [\n";
+ for (unsigned i = 0, e = FilePaths.size(); i != e; ++i) {
+ OS.indent(4) << '\"' << FilePaths[i] << '\"';
+ if (i < e-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(2) << "],\n";
+
+ OS.indent(2) << "\"symbols\": [\n";
+ for (unsigned i = 0, e = Symbols.size(); i != e; ++i) {
+ OS.indent(4) << "{\n";
+ SymbolInfo &symInfo = Symbols[i];
+ OS.indent(6) << "\"kind\": \"" << getSymbolKindString(symInfo.Kind) << "\",\n";
+ OS.indent(6) << "\"lang\": \"" << getSymbolLanguageString(symInfo.Lang) << "\",\n";
+ OS.indent(6) << "\"usr\": \"" << symInfo.USR << "\",\n";
+ OS.indent(6) << "\"name\": \"" << symInfo.Name << "\",\n";
+ if (!symInfo.CodegenName.empty())
+ OS.indent(6) << "\"codegen\": \"" << symInfo.CodegenName << "\",\n";
+ OS.indent(6) << "\"roles\": \"";
+ printSymbolRoles(symInfo.Roles, OS);
+ OS << '\"';
+ if (symInfo.RelatedRoles != 0) {
+ OS << ",\n";
+ OS.indent(6) << "\"rel-roles\": \"";
+ printSymbolRoles(symInfo.RelatedRoles, OS);
+ OS << '\"';
+ }
+ OS << '\n';
+ OS.indent(4) << "}";
+ if (i < e-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(2) << "],\n";
+
+ OS.indent(2) << "\"records\": [\n";
+ for (unsigned i = 0, e = Records.size(); i != e; ++i) {
+ OS.indent(4) << "{\n";
+ RecordInfo &recInfo = *Records[i];
+ OS.indent(6) << "\"occurrences\": [\n";
+ for (unsigned oi = 0, oe = recInfo.Occurrences.size(); oi != oe; ++oi) {
+ OS.indent(8) << "{\n";
+ SymbolOccurrenceInfo &occurInfo = recInfo.Occurrences[oi];
+ OS.indent(10) << "\"symbol\": " << occurInfo.Symbol << ",\n";
+ OS.indent(10) << "\"line\": " << occurInfo.Line << ",\n";
+ OS.indent(10) << "\"col\": " << occurInfo.Column << ",\n";
+ OS.indent(10) << "\"roles\": \"";
+ printSymbolRoles(occurInfo.Roles, OS);
+ OS << '\"';
+ if (!occurInfo.Relations.empty()) {
+ OS << ",\n";
+ OS.indent(10) << "\"relations\": [\n";
+ for (unsigned ri = 0, re = occurInfo.Relations.size(); ri != re; ++ri) {
+ OS.indent(12) << "{\n";
+ SymbolRelationInfo &relInfo = occurInfo.Relations[ri];
+ OS.indent(14) << "\"symbol\": " << relInfo.RelatedSymbol << ",\n";
+ OS.indent(14) << "\"rel-roles\": \"";
+ printSymbolRoles(relInfo.Roles, OS);
+ OS << "\"\n";
+ OS.indent(12) << "}";
+ if (ri < re-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(10) << "]\n";
+ }
+ OS << '\n';
+ OS.indent(8) << "}";
+ if (oi < oe-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(6) << "]\n";
+ OS.indent(4) << "}";
+ if (i < e-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(2) << "],\n";
+
+ StringMap<size_t> UnitIndicesByName;
+ for (unsigned i = 0, e = Units.size(); i != e; ++i) {
+ UnitInfo &unit = *Units[i];
+ UnitIndicesByName[unit.Name] = i;
+ }
+
+ OS.indent(2) << "\"units\": [\n";
+ for (unsigned i = 0, e = Units.size(); i != e; ++i) {
+ OS.indent(4) << "{\n";
+ UnitInfo &unit = *Units[i];
+ OS.indent(6) << "\"triple\": \"" << unit.Triple << "\",\n";
+ OS.indent(6) << "\"out-file\": " << unit.OutFile << ",\n";
+ if (!unit.UnitDepends.empty()) {
+ OS.indent(6) << "\"unit-dependencies\": [";
+ for (unsigned ui = 0, ue = unit.UnitDepends.size(); ui != ue; ++ui) {
+ OS << UnitIndicesByName[unit.UnitDepends[ui]];
+ if (ui < ue-1) OS << ", ";
+ }
+ OS << "],\n";
+ }
+ OS.indent(6) << "\"sources\": [\n";
+ for (unsigned si = 0, se = unit.Sources.size(); si != se; ++si) {
+ OS.indent(8) << "{\n";
+ UnitSourceInfo &source = unit.Sources[si];
+ OS.indent(10) << "\"file\": " << source.FilePath;
+ if (!source.AssociatedRecords.empty()) {
+ OS << ",\n";
+ OS.indent(10) << "\"records\": [";
+ for (unsigned ri = 0, re = source.AssociatedRecords.size(); ri != re; ++ri) {
+ OS << source.AssociatedRecords[ri];
+ if (ri < re-1) OS << ", ";
+ }
+ OS << ']';
+ }
+ OS << '\n';
+ OS.indent(8) << "}";
+ if (si < se-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(6) << "]\n";
+ OS.indent(4) << "}";
+ if (i < e-1) OS << ',';
+ OS << '\n';
+ }
+ OS.indent(2) << "]\n";
+ OS << "}\n";
+}
+
+
+bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) {
+ std::string error;
+ auto dataStore = IndexStore(StorePath, error);
+ if (!dataStore) {
+ errs() << "error opening store path '" << StorePath << "': " << error << '\n';
+ return true;
+ }
+
+ // Explicitely avoid doing any memory cleanup for aggregator since the process
+ // is going to exit when we are done.
+ Aggregator *aggregator = new Aggregator(std::move(dataStore));
+ bool err = aggregator->process();
+ if (err)
+ return true;
+ aggregator->dumpJSON(OS);
+ return false;
+}
+
+#else
+
+bool index::aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS) {
+ return true;
+}
+#endif
diff --git a/tools/c-index-test/JSONAggregation.h b/tools/c-index-test/JSONAggregation.h
new file mode 100644
index 0000000..5224ce8
--- /dev/null
+++ b/tools/c-index-test/JSONAggregation.h
@@ -0,0 +1,24 @@
+//===--- JSONAggregation.h - Index data aggregation in JSON format --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H
+#define LLVM_CLANG_TOOLS_CINDEXTEST_JSONAGGREGATION_H
+
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+namespace index {
+
+/// Returns true if an error occurred, false otherwise.
+bool aggregateDataAsJSON(StringRef StorePath, raw_ostream &OS);
+
+} // end namespace index
+} // end namespace clang
+
+#endif
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index cf3581e..36b7703 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -110,6 +110,10 @@
case CXError_ASTReadError:
fprintf(stderr, "Failure: AST deserialization error occurred\n");
return;
+
+ default:
+ fprintf(stderr, "Failure (other)\n");
+ return;
}
}
diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp
index c255f54..298c55c 100644
--- a/tools/c-index-test/core_main.cpp
+++ b/tools/c-index-test/core_main.cpp
@@ -7,6 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#include "JSONAggregation.h"
+#include "indexstore/IndexStoreCXX.h"
+#include "clang/DirectoryWatcher/DirectoryWatcher.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -14,14 +17,31 @@
#include "clang/Frontend/FrontendAction.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Index/IndexDataConsumer.h"
+#include "clang/Index/IndexDataStoreSymbolUtils.h"
+#include "clang/Index/IndexRecordReader.h"
+#include "clang/Index/IndexUnitReader.h"
#include "clang/Index/USRGeneration.h"
#include "clang/Index/CodegenNameGenerator.h"
#include "clang/Serialization/ASTReader.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/PrettyStackTrace.h"
+#define HAVE_CORESERVICES 0
+
+#if defined(__has_include)
+#if __has_include(<CoreServices/CoreServices.h>)
+
+#include <CoreServices/CoreServices.h>
+#undef HAVE_CORESERVICES
+#define HAVE_CORESERVICES 1
+
+#endif
+#endif
+
using namespace clang;
using namespace clang::index;
using namespace llvm;
@@ -33,6 +53,11 @@
enum class ActionType {
None,
PrintSourceSymbols,
+ PrintRecord,
+ PrintUnit,
+ PrintStoreFormatVersion,
+ AggregateAsJSON,
+ WatchDir,
};
namespace options {
@@ -43,9 +68,26 @@
Action(cl::desc("Action:"), cl::init(ActionType::None),
cl::values(
clEnumValN(ActionType::PrintSourceSymbols,
- "print-source-symbols", "Print symbols from source")),
+ "print-source-symbols", "Print symbols from source"),
+ clEnumValN(ActionType::PrintRecord,
+ "print-record", "Print record info"),
+ clEnumValN(ActionType::PrintUnit,
+ "print-unit", "Print unit info"),
+ clEnumValN(ActionType::PrintStoreFormatVersion,
+ "print-store-format-version", "Print store format version"),
+ clEnumValN(ActionType::AggregateAsJSON,
+ "aggregate-json", "Aggregate index data in JSON format"),
+ clEnumValN(ActionType::WatchDir,
+ "watch-dir", "Watch directory for file events")),
cl::cat(IndexTestCoreCategory));
+static cl::opt<std::string>
+OutputFile("o", cl::desc("output file"),
+ cl::cat(IndexTestCoreCategory));
+
+static cl::list<std::string>
+InputFiles(cl::Positional, cl::desc("<filename>..."));
+
static cl::extrahelp MoreHelp(
"\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
"invocation\n"
@@ -65,6 +107,10 @@
ModuleFormat("fmodule-format", cl::init("raw"),
cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'"));
+static cl::opt<std::string>
+FilePathAndRange("filepath",
+ cl::desc("File path that can optionally include a line range"));
+
}
} // anonymous namespace
@@ -235,6 +281,305 @@
return false;
}
+#if INDEXSTORE_HAS_BLOCKS
+
+//===----------------------------------------------------------------------===//
+// Print Record
+//===----------------------------------------------------------------------===//
+
+static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS);
+static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS);
+
+static int printRecord(StringRef Filename, raw_ostream &OS) {
+ std::string Error;
+ auto Reader = IndexRecordReader::createWithFilePath(Filename, Error);
+ if (!Reader) {
+ errs() << Error << '\n';
+ return true;
+ }
+
+ Reader->foreachDecl(/*noCache=*/true, [&](const IndexRecordDecl *Rec)->bool {
+ printSymbol(*Rec, OS);
+ return true;
+ });
+ OS << "------------\n";
+ Reader->foreachOccurrence([&](const IndexRecordOccurrence &Rec)->bool {
+ printSymbol(Rec, OS);
+ return true;
+ });
+
+ return false;
+};
+
+//===----------------------------------------------------------------------===//
+// Print Store Records
+//===----------------------------------------------------------------------===//
+
+static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS);
+static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS);
+
+static bool printStoreRecord(indexstore::IndexStore &Store, StringRef RecName,
+ StringRef FilePath, raw_ostream &OS) {
+ std::string Error;
+ indexstore::IndexRecordReader Reader(Store, RecName, Error);
+ if (!Reader) {
+ errs() << "error loading record: " << Error << "\n";
+ return true;
+ }
+
+ StringRef Filename = sys::path::filename(FilePath);
+ OS << Filename << '\n';
+ OS << "------------\n";
+ Reader.foreachSymbol(/*noCache=*/true, [&](indexstore::IndexRecordSymbol Sym) -> bool {
+ printSymbol(Sym, OS);
+ return true;
+ });
+ OS << "------------\n";
+ Reader.foreachOccurrence([&](indexstore::IndexRecordOccurrence Occur)->bool {
+ printSymbol(Occur, OS);
+ return true;
+ });
+
+ return false;
+}
+
+static int printStoreRecords(StringRef StorePath, raw_ostream &OS) {
+ std::string Error;
+ indexstore::IndexStore Store(StorePath, Error);
+ if (!Store) {
+ errs() << "error loading store: " << Error << "\n";
+ return 1;
+ }
+
+ bool Success = Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool {
+ indexstore::IndexUnitReader Reader(Store, UnitName, Error);
+ if (!Reader) {
+ errs() << "error loading unit: " << Error << "\n";
+ return false;
+ }
+ return Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool {
+ if (Dep.getKind() == indexstore::IndexUnitDependency::DependencyKind::Record) {
+ bool Err = printStoreRecord(Store, Dep.getName(), Dep.getFilePath(), OS);
+ OS << '\n';
+ return !Err;
+ }
+ return true;
+ });
+ });
+
+ return !Success;
+}
+
+static std::string findRecordNameForFile(indexstore::IndexStore &store,
+ StringRef filePath) {
+ std::string recName;
+ store.foreachUnit(/*sorted=*/false, [&](StringRef unitName) -> bool {
+ std::string error;
+ indexstore::IndexUnitReader Reader(store, unitName, error);
+ if (!Reader) {
+ errs() << "error loading unit: " << error << "\n";
+ return false;
+ }
+ Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool {
+ if (Dep.getKind() == indexstore::IndexUnitDependency::DependencyKind::Record) {
+ if (Dep.getFilePath() == filePath) {
+ recName = Dep.getName();
+ return false;
+ }
+ return true;
+ }
+ return true;
+ });
+ return true;
+ });
+ return recName;
+}
+
+static int printStoreFileRecord(StringRef storePath, StringRef filePath,
+ Optional<unsigned> lineStart, unsigned lineCount,
+ raw_ostream &OS) {
+ std::string error;
+ indexstore::IndexStore store(storePath, error);
+ if (!store) {
+ errs() << "error loading store: " << error << "\n";
+ return 1;
+ }
+
+ std::string recName = findRecordNameForFile(store, filePath);
+ if (recName.empty()) {
+ errs() << "could not find record for '" << filePath << "'\n";
+ return 1;
+ }
+
+ if (!lineStart.hasValue())
+ return printStoreRecord(store, recName, filePath, OS);
+
+ indexstore::IndexRecordReader Reader(store, recName, error);
+ if (!Reader) {
+ errs() << "error loading record: " << error << "\n";
+ return 1;
+ }
+
+ Reader.foreachOccurrenceInLineRange(*lineStart, lineCount, [&](indexstore::IndexRecordOccurrence Occur)->bool {
+ printSymbol(Occur, OS);
+ return true;
+ });
+
+ return 0;
+}
+
+
+//===----------------------------------------------------------------------===//
+// Print Unit
+//===----------------------------------------------------------------------===//
+
+static int printUnit(StringRef Filename, raw_ostream &OS) {
+ std::string Error;
+ auto Reader = IndexUnitReader::createWithFilePath(Filename, Error);
+ if (!Reader) {
+ errs() << Error << '\n';
+ return true;
+ }
+
+ OS << "provider: " << Reader->getProviderIdentifier() << '-' << Reader->getProviderVersion() << '\n';
+ OS << "is-system: " << Reader->isSystemUnit() << '\n';
+ OS << "is-module: " << Reader->isModuleUnit() << '\n';
+ OS << "module-name: " << (Reader->getModuleName().empty() ? "<none>" : Reader->getModuleName()) << '\n';
+ OS << "has-main: " << Reader->hasMainFile() << '\n';
+ OS << "main-path: " << Reader->getMainFilePath() << '\n';
+ OS << "work-dir: " << Reader->getWorkingDirectory() << '\n';
+ OS << "out-file: " << Reader->getOutputFile() << '\n';
+ OS << "target: " << Reader->getTarget() << '\n';
+ OS << "is-debug: " << Reader->isDebugCompilation() << '\n';
+ OS << "DEPEND START\n";
+ unsigned NumDepends = 0;
+ Reader->foreachDependency([&](const IndexUnitReader::DependencyInfo &Dep) -> bool {
+ switch (Dep.Kind) {
+ case IndexUnitReader::DependencyKind::Unit:
+ OS << "Unit | "; break;
+ case IndexUnitReader::DependencyKind::Record:
+ OS << "Record | "; break;
+ case IndexUnitReader::DependencyKind::File:
+ OS << "File | "; break;
+ }
+ OS << (Dep.IsSystem ? "system" : "user");
+ OS << " | ";
+ if (!Dep.ModuleName.empty())
+ OS << Dep.ModuleName << " | ";
+ OS << Dep.FilePath << " | " << Dep.UnitOrRecordName << " | ";
+ OS << Dep.ModTime << " | " << Dep.FileSize << '\n';
+ ++NumDepends;
+ return true;
+ });
+ OS << "DEPEND END (" << NumDepends << ")\n";
+ OS << "INCLUDE START\n";
+ unsigned NumIncludes = 0;
+ Reader->foreachInclude([&](const IndexUnitReader::IncludeInfo &Inc) -> bool {
+ OS << Inc.SourcePath << ":" << Inc.SourceLine << " | ";
+ OS << Inc.TargetPath << '\n';
+ ++NumIncludes;
+ return true;
+ });
+ OS << "INCLUDE END (" << NumIncludes << ")\n";
+
+ return false;
+};
+
+//===----------------------------------------------------------------------===//
+// Print Store Units
+//===----------------------------------------------------------------------===//
+
+static bool printStoreUnit(indexstore::IndexStore &Store, StringRef UnitName,
+ raw_ostream &OS) {
+ std::string Error;
+ indexstore::IndexUnitReader Reader(Store, UnitName, Error);
+ if (!Reader) {
+ errs() << "error loading unit: " << Error << "\n";
+ return true;
+ }
+
+ OS << "provider: " << Reader.getProviderIdentifier() << '-' << Reader.getProviderVersion() << '\n';
+ OS << "is-system: " << Reader.isSystemUnit() << '\n';
+ OS << "is-module: " << Reader.isModuleUnit() << '\n';
+ OS << "module-name: " << (Reader.getModuleName().empty() ? "<none>" : Reader.getModuleName()) << '\n';
+ OS << "has-main: " << Reader.hasMainFile() << '\n';
+ OS << "main-path: " << Reader.getMainFilePath() << '\n';
+ OS << "work-dir: " << Reader.getWorkingDirectory() << '\n';
+ OS << "out-file: " << Reader.getOutputFile() << '\n';
+ OS << "target: " << Reader.getTarget() << '\n';
+ OS << "is-debug: " << Reader.isDebugCompilation() << '\n';
+ OS << "DEPEND START\n";
+ unsigned NumDepends = 0;
+ Reader.foreachDependency([&](indexstore::IndexUnitDependency Dep) -> bool {
+ switch (Dep.getKind()) {
+ case indexstore::IndexUnitDependency::DependencyKind::Unit:
+ OS << "Unit | "; break;
+ case indexstore::IndexUnitDependency::DependencyKind::Record:
+ OS << "Record | "; break;
+ case indexstore::IndexUnitDependency::DependencyKind::File:
+ OS << "File | "; break;
+ }
+ OS << (Dep.isSystem() ? "system" : "user");
+ OS << " | ";
+ if (!Dep.getModuleName().empty())
+ OS << Dep.getModuleName() << " | ";
+ OS << Dep.getFilePath() << " | " << Dep.getName() << " | ";
+ OS << Dep.getModificationTime() << '\n';
+ ++NumDepends;
+ return true;
+ });
+ OS << "DEPEND END (" << NumDepends << ")\n";
+ OS << "INCLUDE START\n";
+ unsigned NumIncludes = 0;
+ Reader.foreachInclude([&](indexstore::IndexUnitInclude Inc) -> bool {
+ OS << Inc.getSourcePath() << ":" << Inc.getSourceLine() << " | ";
+ OS << Inc.getTargetPath() << '\n';
+ ++NumIncludes;
+ return true;
+ });
+ OS << "INCLUDE END (" << NumIncludes << ")\n";
+
+ return false;
+}
+
+static int printStoreUnits(StringRef StorePath, raw_ostream &OS) {
+ std::string Error;
+ indexstore::IndexStore Store(StorePath, Error);
+ if (!Store) {
+ errs() << "error loading store: " << Error << "\n";
+ return 1;
+ }
+
+ bool Success = Store.foreachUnit(/*sorted=*/true, [&](StringRef UnitName) -> bool {
+ OS << UnitName << '\n';
+ OS << "--------\n";
+ bool err = printStoreUnit(Store, UnitName, OS);
+ OS << '\n';
+ return !err;
+ });
+
+ return !Success;
+}
+
+
+#else
+
+static int printUnit(StringRef Filename, raw_ostream &OS) {
+ return 1;
+}
+
+static int printStoreUnits(StringRef StorePath, raw_ostream &OS) {
+ return 1;
+}
+
+static int printStoreFileRecord(StringRef storePath, StringRef filePath,
+ Optional<unsigned> lineStart, unsigned lineCount,
+ raw_ostream &OS) {
+ return 1;
+}
+
+#endif
+
//===----------------------------------------------------------------------===//
// Helper Utils
//===----------------------------------------------------------------------===//
@@ -266,10 +611,210 @@
}
}
+#if INDEXSTORE_HAS_BLOCKS
+
+static void printSymbol(const IndexRecordDecl &Rec, raw_ostream &OS) {
+ printSymbolInfo(Rec.SymInfo, OS);
+ OS << " | ";
+
+ if (Rec.Name.empty())
+ OS << "<no-name>";
+ else
+ OS << Rec.Name;
+ OS << " | ";
+
+ if (Rec.USR.empty())
+ OS << "<no-usr>";
+ else
+ OS << Rec.USR;
+ OS << " | ";
+
+ if (Rec.CodeGenName.empty())
+ OS << "<no-cgname>";
+ else
+ OS << Rec.CodeGenName;
+ OS << " | ";
+
+ printSymbolRoles(Rec.Roles, OS);
+ OS << " - ";
+ printSymbolRoles(Rec.RelatedRoles, OS);
+ OS << '\n';
+}
+
+static void printSymbol(const IndexRecordOccurrence &Rec, raw_ostream &OS) {
+ OS << Rec.Line << ':' << Rec.Column << " | ";
+ printSymbolInfo(Rec.Dcl->SymInfo, OS);
+ OS << " | ";
+
+ if (Rec.Dcl->USR.empty())
+ OS << "<no-usr>";
+ else
+ OS << Rec.Dcl->USR;
+ OS << " | ";
+
+ printSymbolRoles(Rec.Roles, OS);
+ OS << " | ";
+ OS << "rel: " << Rec.Relations.size() << '\n';
+ for (auto &Rel : Rec.Relations) {
+ OS << '\t';
+ printSymbolRoles(Rel.Roles, OS);
+ OS << " | ";
+ if (Rel.Dcl->USR.empty())
+ OS << "<no-usr>";
+ else
+ OS << Rel.Dcl->USR;
+ OS << '\n';
+ }
+}
+
+static void printSymbol(indexstore::IndexRecordSymbol Sym, raw_ostream &OS) {
+ SymbolInfo SymInfo{getSymbolKind(Sym.getKind()),
+ getSymbolSubKind(Sym.getSubKind()),
+ SymbolPropertySet(Sym.getProperties()),
+ getSymbolLanguage(Sym.getLanguage())};
+
+ printSymbolInfo(SymInfo, OS);
+ OS << " | ";
+
+ if (Sym.getName().empty())
+ OS << "<no-name>";
+ else
+ OS << Sym.getName();
+ OS << " | ";
+
+ if (Sym.getUSR().empty())
+ OS << "<no-usr>";
+ else
+ OS << Sym.getUSR();
+ OS << " | ";
+
+ if (Sym.getCodegenName().empty())
+ OS << "<no-cgname>";
+ else
+ OS << Sym.getCodegenName();
+ OS << " | ";
+
+ printSymbolRoles(Sym.getRoles(), OS);
+ OS << " - ";
+ printSymbolRoles(Sym.getRelatedRoles(), OS);
+ OS << '\n';
+}
+
+static void printSymbol(indexstore::IndexRecordOccurrence Occur, raw_ostream &OS) {
+ OS << Occur.getLineCol().first << ':' << Occur.getLineCol().second << " | ";
+ auto Sym = Occur.getSymbol();
+ SymbolInfo SymInfo{getSymbolKind(Sym.getKind()),
+ getSymbolSubKind(Sym.getSubKind()),
+ SymbolPropertySet(Sym.getProperties()),
+ getSymbolLanguage(Sym.getLanguage())};
+
+ printSymbolInfo(SymInfo, OS);
+ OS << " | ";
+
+ if (Sym.getUSR().empty())
+ OS << "<no-usr>";
+ else
+ OS << Sym.getUSR();
+ OS << " | ";
+
+ unsigned NumRelations = 0;
+ Occur.foreachRelation([&](indexstore::IndexSymbolRelation) {
+ ++NumRelations;
+ return true;
+ });
+
+ printSymbolRoles(Occur.getRoles(), OS);
+ OS << " | ";
+ OS << "rel: " << NumRelations << '\n';
+ Occur.foreachRelation([&](indexstore::IndexSymbolRelation Rel) {
+ OS << '\t';
+ printSymbolRoles(Rel.getRoles(), OS);
+ OS << " | ";
+ auto Sym = Rel.getSymbol();
+ if (Sym.getUSR().empty())
+ OS << "<no-usr>";
+ else
+ OS << Sym.getUSR();
+ OS << '\n';
+ return true;
+ });
+}
+
+#else
+
+static int printRecord(StringRef Filename, raw_ostream &OS) {
+ return 1;
+}
+static int printStoreRecords(StringRef StorePath, raw_ostream &OS) {
+ return 1;
+}
+
+#endif
+
+static int watchDirectory(StringRef dirPath) {
+ raw_ostream &OS = outs();
+ auto receiver = [&](ArrayRef<DirectoryWatcher::Event> Events, bool isInitial) {
+ for (auto evt : Events) {
+ switch (evt.Kind) {
+ case DirectoryWatcher::EventKind::Added:
+ OS << "added: "; break;
+ case DirectoryWatcher::EventKind::Modified:
+ OS << "modified: "; break;
+ case DirectoryWatcher::EventKind::Removed:
+ OS << "removed: "; break;
+ case DirectoryWatcher::EventKind::DirectoryDeleted:
+ OS << "dir deleted: "; break;
+
+ }
+ OS << evt.Filename << '\n';
+ }
+ };
+ std::string Error;
+ auto watcher = DirectoryWatcher::create(dirPath, receiver,
+ /*waitInitialSync=*/true, Error);
+ if (!watcher) {
+ errs() << "failed creating directory watcher: " << Error << '\n';
+ return 1;
+ }
+#if HAVE_CORESERVICES
+ dispatch_main();
+#else
+ return 1;
+#endif
+}
+
//===----------------------------------------------------------------------===//
// Command line processing.
//===----------------------------------------------------------------------===//
+bool deconstructPathAndRange(StringRef input,
+ std::string &filepath,
+ Optional<unsigned> &lineStart,
+ unsigned &lineCount) {
+ StringRef path, range;
+ std::tie(path, range) = input.split(':');
+ StringRef start, end;
+ std::tie(start, end) = range.split(':');
+ filepath = path;
+ lineCount = 0;
+ if (start.empty())
+ return false;
+ unsigned num;
+ if (start.getAsInteger(10, num)) {
+ errs() << "couldn't convert to integer: " << start << '\n';
+ return true;
+ }
+ lineStart = num;
+ if (end.empty())
+ return false;
+ if (end.getAsInteger(10, num)) {
+ errs() << "couldn't convert to integer: " << end << '\n';
+ return true;
+ }
+ lineCount = num-lineStart.getValue();
+ return false;
+}
+
int indextest_core_main(int argc, const char **argv) {
sys::PrintStackTraceOnErrorSignal(argv[0]);
PrettyStackTraceProgram X(argc, argv);
@@ -305,5 +850,75 @@
return printSourceSymbols(CompArgs, options::DumpModuleImports, options::IncludeLocals);
}
+ if (options::Action == ActionType::PrintRecord) {
+ if (!options::FilePathAndRange.empty()) {
+ std::string filepath;
+ Optional<unsigned> lineStart;
+ unsigned lineCount;
+ if (deconstructPathAndRange(options::FilePathAndRange,
+ filepath, lineStart, lineCount))
+ return 1;
+
+ if (options::InputFiles.empty()) {
+ errs() << "error: missing index store path\n";
+ return 1;
+ }
+ return printStoreFileRecord(options::InputFiles[0], filepath, lineStart, lineCount, outs());
+ }
+
+ if (options::InputFiles.empty()) {
+ errs() << "error: missing input file or directory\n";
+ return 1;
+ }
+
+ if (sys::fs::is_directory(options::InputFiles[0]))
+ return printStoreRecords(options::InputFiles[0], outs());
+ else
+ return printRecord(options::InputFiles[0], outs());
+ }
+
+ if (options::Action == ActionType::PrintUnit) {
+ if (options::InputFiles.empty()) {
+ errs() << "error: missing input file or directory\n";
+ return 1;
+ }
+
+ if (sys::fs::is_directory(options::InputFiles[0]))
+ return printStoreUnits(options::InputFiles[0], outs());
+ else
+ return printUnit(options::InputFiles[0], outs());
+ }
+
+#if INDEXSTORE_HAS_BLOCKS
+ if (options::Action == ActionType::PrintStoreFormatVersion) {
+ outs() << indexstore::IndexStore::formatVersion() << '\n';
+ }
+#endif
+
+ if (options::Action == ActionType::AggregateAsJSON) {
+ if (options::InputFiles.empty()) {
+ errs() << "error: missing input data store directory\n";
+ return 1;
+ }
+ StringRef storePath = options::InputFiles[0];
+ if (options::OutputFile.empty())
+ return aggregateDataAsJSON(storePath, outs());
+ std::error_code EC;
+ raw_fd_ostream OS(options::OutputFile, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ errs() << "failed to open output file: " << EC.message() << '\n';
+ return 1;
+ }
+ return aggregateDataAsJSON(storePath, OS);
+ }
+
+ if (options::Action == ActionType::WatchDir) {
+ if (options::InputFiles.empty()) {
+ errs() << "error: missing directory path\n";
+ return 1;
+ }
+ return watchDirectory(options::InputFiles[0]);
+ }
+
return 0;
}
diff --git a/tools/clang-check/CMakeLists.txt b/tools/clang-check/CMakeLists.txt
index 04151a8..233f981 100644
--- a/tools/clang-check/CMakeLists.txt
+++ b/tools/clang-check/CMakeLists.txt
@@ -9,6 +9,7 @@
)
target_link_libraries(clang-check
+ clangAPINotes
clangAST
clangBasic
clangDriver
diff --git a/tools/clang-refactor-test/CMakeLists.txt b/tools/clang-refactor-test/CMakeLists.txt
new file mode 100644
index 0000000..f742327
--- /dev/null
+++ b/tools/clang-refactor-test/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(LLVM_LINK_COMPONENTS
+ support
+ )
+
+add_clang_executable(clang-refactor-test
+ ClangRefactorTest.cpp
+ )
+
+if (LLVM_BUILD_STATIC)
+ target_link_libraries(clang-refactor-test
+ libclang_static
+ )
+else()
+ target_link_libraries(clang-refactor-test
+ libclang
+ clangBasic
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
+endif()
diff --git a/tools/clang-refactor-test/ClangRefactorTest.cpp b/tools/clang-refactor-test/ClangRefactorTest.cpp
new file mode 100644
index 0000000..424d130
--- /dev/null
+++ b/tools/clang-refactor-test/ClangRefactorTest.cpp
@@ -0,0 +1,1388 @@
+//===--- ClangRefactorTest.cpp - ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a clang-refactor-test tool that is used to test the
+// refactoring library in Clang.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-c/Refactor.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Frontend/CommandLineSourceLoc.h"
+#include "clang/Tooling/Refactor/SymbolName.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace opts {
+
+static cl::OptionCategory
+ ClangRefactorTestOptions("clang-refactor-test common options");
+
+cl::SubCommand RenameInitiateSubcommand(
+ "rename-initiate", "Initiate renaming in an initial translation unit");
+
+cl::SubCommand RenameInitiateUSRSubcommand(
+ "rename-initiate-usr",
+ "Initiate renaming in an translation unit on a specific declaration");
+
+cl::SubCommand RenameIndexedFileSubcommand(
+ "rename-indexed-file",
+ "Initiate renaming and find occurrences in an indexed file");
+
+cl::SubCommand ListRefactoringActionsSubcommand("list-actions",
+ "Print the list of the "
+ "refactoring actions that can "
+ "be performed at the specified "
+ "location");
+
+cl::SubCommand InitiateActionSubcommand("initiate",
+ "Initiate a refactoring action");
+
+cl::SubCommand
+ PerformActionSubcommand("perform",
+ "Initiate and perform a refactoring action");
+
+const cl::desc
+ AtOptionDescription("The location at which the refactoring should be "
+ "initiated (<file>:<line>:<column>)");
+
+const cl::desc InRangeOptionDescription(
+ "The location(s) at which the refactoring should be "
+ "initiated (<file>:<line>:<column>-<last-column>)");
+
+const cl::desc SelectedRangeOptionDescription(
+ "The selected source range in which the refactoring should be "
+ "initiated (<file>:<line>:<column>-<line>:<column>)");
+
+static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
+
+namespace rename {
+static cl::list<std::string> AtLocation("at", AtOptionDescription, cl::Required,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameInitiateSubcommand),
+ cl::OneOrMore);
+
+static cl::opt<std::string>
+ USR("usr", cl::desc("The USR of the declaration that should be renamed"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateUSRSubcommand),
+ cl::Required);
+
+static cl::opt<std::string>
+ NewName("new-name", cl::desc("The new name to change the symbol to."),
+ cl::Required, cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameInitiateSubcommand),
+ cl::sub(RenameInitiateUSRSubcommand));
+
+static cl::list<std::string>
+ IndexedNames("name", cl::desc("The names of the renamed symbols"),
+ cl::Required, cl::OneOrMore, cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameIndexedFileSubcommand));
+
+static cl::list<std::string> IndexedNewNames(
+ "new-name", cl::desc("The new name to change the symbol to."), cl::Required,
+ cl::OneOrMore, cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameIndexedFileSubcommand));
+
+static cl::opt<std::string>
+ IndexedSymbolKind("indexed-symbol-kind",
+ cl::desc("The kind of the indexed symbol."), cl::Optional,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameIndexedFileSubcommand));
+
+static cl::opt<std::string>
+ IndexedFileName("indexed-file", cl::desc("The name of the indexed file"),
+ cl::Required, cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameIndexedFileSubcommand));
+
+static cl::list<std::string>
+ IndexedLocations("indexed-at",
+ cl::desc("The location of an indexed occurrence "
+ "([<kind>|<symbol-index>:]<line>:<column>)"),
+ cl::ZeroOrMore, cl::cat(ClangRefactorTestOptions),
+ cl::sub(RenameIndexedFileSubcommand));
+
+static cl::opt<bool> AvoidTextual(
+ "no-textual-matches", cl::desc("Avoid searching for textual matches"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameIndexedFileSubcommand));
+
+static cl::opt<bool> DumpSymbols(
+ "dump-symbols", cl::desc("Dump the information about the renamed symbols"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand),
+ cl::sub(RenameInitiateUSRSubcommand));
+}
+
+namespace listActions {
+cl::opt<std::string> AtLocation("at", AtOptionDescription, cl::Required,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(ListRefactoringActionsSubcommand));
+
+cl::opt<std::string> SelectedRange("selected", SelectedRangeOptionDescription,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(ListRefactoringActionsSubcommand));
+
+cl::opt<bool> DumpRawActionType(
+ "dump-raw-action-type",
+ cl::desc("Prints the action type integer value for each listed action"),
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(ListRefactoringActionsSubcommand));
+}
+
+namespace initiateAndPerform {
+cl::list<std::string> InLocationRanges("in", cl::ZeroOrMore,
+ InRangeOptionDescription,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(InitiateActionSubcommand));
+
+cl::list<std::string> AtLocations("at", cl::ZeroOrMore, AtOptionDescription,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(InitiateActionSubcommand),
+ cl::sub(PerformActionSubcommand));
+
+cl::list<std::string> SelectedRanges("selected", cl::ZeroOrMore,
+ SelectedRangeOptionDescription,
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(InitiateActionSubcommand),
+ cl::sub(PerformActionSubcommand));
+
+cl::opt<std::string> ActionName("action", cl::Required,
+ cl::desc("The name of the refactoring action"),
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(InitiateActionSubcommand),
+ cl::sub(PerformActionSubcommand));
+
+cl::opt<bool> LocationAgnostic(
+ "location-agnostic",
+ cl::desc(
+ "Ignore the location of initiation when verifying result consistency"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(InitiateActionSubcommand));
+
+cl::opt<unsigned> CandidateIndex(
+ "candidate",
+ cl::desc(
+ "The index of the refactoring candidate which should be performed"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand));
+
+cl::opt<std::string> ContinuationFile(
+ "continuation-file",
+ cl::desc("The source file in which the continuation should run"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand));
+
+cl::opt<std::string> QueryResults(
+ "query-results", cl::desc("The indexer query results that should be passed "
+ "into the continuation"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand));
+
+cl::opt<bool> EmitAssociatedInfo(
+ "emit-associated", cl::desc("Dump additional associated information"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand));
+}
+
+cl::opt<bool> Apply(
+ "apply",
+ cl::desc(
+ "Apply the changes and print the modified file to standard output"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand),
+ cl::sub(RenameInitiateSubcommand), cl::sub(RenameIndexedFileSubcommand));
+
+cl::opt<bool>
+ Diff("diff",
+ cl::desc("Display the replaced text in red when -apply is specified"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(PerformActionSubcommand),
+ cl::sub(RenameInitiateSubcommand),
+ cl::sub(RenameIndexedFileSubcommand));
+
+cl::opt<int> Context("context", cl::desc("How many lines of context should be "
+ "displayed when -apply is specified"),
+ cl::cat(ClangRefactorTestOptions),
+ cl::sub(PerformActionSubcommand),
+ cl::sub(RenameInitiateSubcommand),
+ cl::sub(RenameIndexedFileSubcommand));
+
+static cl::opt<std::string> FileName(
+ cl::Positional, cl::desc("<filename>"), cl::Required,
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand),
+ cl::sub(RenameInitiateUSRSubcommand), cl::sub(RenameIndexedFileSubcommand),
+ cl::sub(ListRefactoringActionsSubcommand),
+ cl::sub(InitiateActionSubcommand), cl::sub(PerformActionSubcommand));
+
+static cl::opt<bool> IgnoreFilenameForInitiationTU(
+ "ignore-filename-for-initiation-tu", cl::Optional,
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameIndexedFileSubcommand));
+
+static cl::list<std::string> CompilerArguments(
+ cl::ConsumeAfter, cl::desc("<arguments to be passed to the compiler>"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand),
+ cl::sub(RenameInitiateUSRSubcommand), cl::sub(RenameIndexedFileSubcommand),
+ cl::sub(ListRefactoringActionsSubcommand),
+ cl::sub(InitiateActionSubcommand), cl::sub(PerformActionSubcommand));
+
+static cl::opt<std::string> ImplementationTU(
+ "implementation-tu", cl::desc("The name of the implementation TU"),
+ cl::cat(ClangRefactorTestOptions), cl::sub(RenameInitiateSubcommand));
+}
+
+static const char *renameOccurrenceKindString(CXSymbolOccurrenceKind Kind,
+ bool IsLocal,
+ bool IsMacroExpansion) {
+ switch (Kind) {
+ case CXSymbolOccurrence_MatchingSymbol:
+ return IsMacroExpansion ? "macro" : IsLocal ? "rename local" : "rename";
+ case CXSymbolOccurrence_MatchingSelector:
+ assert(!IsLocal && "Objective-C selector renames must be global");
+ return IsMacroExpansion ? "selector in macro" : "selector";
+ case CXSymbolOccurrence_MatchingImplicitProperty:
+ assert(!IsLocal);
+ return IsMacroExpansion ? "implicit-property in macro"
+ : "implicit-property";
+ case CXSymbolOccurrence_MatchingCommentString:
+ return "comment";
+ case CXSymbolOccurrence_MatchingDocCommentString:
+ return "documentation";
+ case CXSymbolOccurrence_MatchingFilename:
+ return "filename";
+ case CXSymbolOccurrence_ExtractedDeclaration:
+ return "extracted-decl";
+ case CXSymbolOccurrence_ExtractedDeclaration_Reference:
+ return "extracted-decl-ref";
+ }
+}
+
+static int apply(ArrayRef<CXRefactoringReplacement> Replacements,
+ StringRef Filename) {
+ // Assume that the replacements are sorted.
+ auto Result = MemoryBuffer::getFile(Filename);
+ if (!Result) {
+ errs() << "Failed to open " << Filename << "\n";
+ return 1;
+ }
+
+ raw_ostream &OS = outs();
+
+ int Context = opts::Context;
+
+ MemoryBuffer &Buffer = **Result;
+ std::vector<std::pair<StringRef, std::vector<CXRefactoringReplacement>>>
+ Lines;
+ for (auto I = line_iterator(Buffer, /*SkipBlanks=*/false),
+ E = line_iterator();
+ I != E; ++I)
+ Lines.push_back(
+ std::make_pair(*I, std::vector<CXRefactoringReplacement>()));
+ unsigned FlushedLine = 1;
+ auto FlushUntil = [&](unsigned Line) {
+ // Adjust the first flushed line if needed when printing in context mode.
+ if (FlushedLine == 1 && Context)
+ FlushedLine = std::max(int(Line) - Context, 1);
+ for (; FlushedLine < Line; ++FlushedLine) {
+ const auto &Line = Lines[FlushedLine - 1];
+ if (Line.second.empty()) {
+ OS << Line.first << "\n";
+ continue;
+ }
+
+ unsigned I = 0;
+ for (const CXRefactoringReplacement &Replacement : Line.second) {
+ OS << Line.first.substr(I, Replacement.Range.Begin.Column - 1 - I);
+ if (opts::Diff) {
+ OS.changeColor(raw_ostream::RED, false, true);
+ OS << Line.first.substr(Replacement.Range.Begin.Column - 1,
+ Replacement.Range.End.Column - 1 -
+ (Replacement.Range.Begin.Column - 1));
+ }
+ OS.changeColor(raw_ostream::GREEN);
+ OS << clang_getCString(Replacement.ReplacementString);
+ OS.resetColor();
+ I = Replacement.Range.End.Column - 1;
+ }
+ OS << Line.first.substr(I);
+ if (I < Line.first.size() || opts::Diff)
+ OS << "\n";
+ }
+ };
+
+ int EndLineMax = 0;
+ for (const CXRefactoringReplacement &Replacement : Replacements) {
+ EndLineMax = std::max(int(Replacement.Range.End.Line), EndLineMax);
+ unsigned StartingLine = Replacement.Range.Begin.Line;
+ FlushUntil(StartingLine);
+ if (Replacement.Range.End.Line == StartingLine) {
+ Lines[StartingLine - 1].second.push_back(Replacement);
+ continue;
+ }
+ // Multi-line replacements have to be split
+ for (unsigned I = StartingLine; I <= Replacement.Range.End.Line; ++I) {
+ CXRefactoringReplacement NewReplacement;
+ if (I == Replacement.Range.End.Line)
+ NewReplacement.ReplacementString = Replacement.ReplacementString;
+ else
+ // FIXME: This is a hack to workaround the fact that the API doesn't
+ // provide a way to create a null string. This should be fixed when
+ // upstreaming.
+ NewReplacement.ReplacementString = {0, 0};
+ NewReplacement.Range.Begin.Line = I;
+ NewReplacement.Range.Begin.Column =
+ I == StartingLine ? Replacement.Range.Begin.Column : 1;
+ NewReplacement.Range.End.Line = I;
+ NewReplacement.Range.End.Column = I == Replacement.Range.End.Line
+ ? Replacement.Range.End.Column
+ : Lines[I - 1].first.size() + 1;
+ NewReplacement.AssociatedData = nullptr;
+ Lines[I - 1].second.push_back(NewReplacement);
+ }
+ }
+ FlushUntil(Context ? std::min(int(Lines.size()), EndLineMax + Context) + 1
+ : Lines.size() + 2);
+ // Print out a dividor when printing in the context mode.
+ if (Context) {
+ for (int I = 0; I < 80; ++I)
+ OS << '-';
+ OS << "\n";
+ }
+ return 0;
+}
+
+/// Converts the given renamed \p Occurrence into a string value that represents
+/// this occurrence.
+static std::string
+occurrenceToString(const CXSymbolOccurrence &Occurrence, bool IsLocal,
+ const tooling::SymbolName &NewName,
+ const tooling::SymbolName &ExpectedReplacementStrings,
+ StringRef Filename) {
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ OS << renameOccurrenceKindString(Occurrence.Kind, IsLocal,
+ Occurrence.IsMacroExpansion)
+ << ' ';
+ if (!Filename.empty())
+ OS << '"' << Filename << "\" ";
+
+ bool FirstRange = true;
+ assert(NewName.size() >= Occurrence.NumNamePieces &&
+ "new name doesn't match the number of pieces");
+ for (unsigned J = 0; J != Occurrence.NumNamePieces; ++J) {
+ if (!FirstRange) // TODO
+ OS << ", ";
+
+ // Print the replacement string if it doesn't match the expected string.
+ if (NewName[J] != ExpectedReplacementStrings[J])
+ OS << '"' << NewName[J] << "\" ";
+
+ CXFileRange Range = Occurrence.NamePieces[J];
+ OS << Range.Begin.Line << ":" << Range.Begin.Column << " -> "
+ << Range.End.Line << ":" << Range.End.Column;
+ FirstRange = false;
+ }
+ return OS.str();
+}
+
+static CXCursorKind
+renameIndexedOccurrenceKindStringToKind(StringRef Str, CXCursorKind Default) {
+ return llvm::StringSwitch<CXCursorKind>(Str)
+ .Case("objc-im", CXCursor_ObjCInstanceMethodDecl)
+ .Case("objc-cm", CXCursor_ObjCClassMethodDecl)
+ .Case("objc-message", CXCursor_ObjCMessageExpr)
+ .Case("include", CXCursor_InclusionDirective)
+ .Default(Default);
+}
+
+/// Parses the string passed as the -indexed-at argument.
+std::pair<CXRenamedIndexedSymbolLocation, unsigned>
+parseIndexedOccurrence(StringRef IndexedOccurrence,
+ CXCursorKind DefaultCursorKind) {
+ StringRef LineColumnLoc = IndexedOccurrence;
+ CXCursorKind Kind = DefaultCursorKind;
+ unsigned SymbolIndex = 0;
+ if (LineColumnLoc.count(':') > 1) {
+ std::pair<StringRef, StringRef> Split = LineColumnLoc.split(':');
+ // The first value is either the kind or the symbol index.
+ if (Split.first.getAsInteger(10, SymbolIndex)) {
+ if (Split.second.count(':') > 1) {
+ std::pair<StringRef, StringRef> SecondSplit = Split.second.split(':');
+ if (SecondSplit.first.getAsInteger(10, SymbolIndex))
+ assert(false && "expected symbol index");
+ Split.second = SecondSplit.second;
+ }
+ Kind = renameIndexedOccurrenceKindStringToKind(Split.first, Kind);
+ }
+ LineColumnLoc = Split.second;
+ }
+ auto Loc = std::string("-:") + LineColumnLoc.str();
+ auto Location = ParsedSourceLocation::FromString(Loc);
+ return std::make_pair(
+ CXRenamedIndexedSymbolLocation{{Location.Line, Location.Column}, Kind},
+ SymbolIndex);
+}
+
+/// Compare the produced occurrences to the expected occurrences that were
+/// gathered at the first location. Return true if the occurrences are
+/// different.
+static bool compareOccurrences(ArrayRef<std::string> ExpectedReplacements,
+ CXSymbolOccurrencesResult Occurrences,
+ bool IsLocal,
+ const tooling::SymbolName &NewSymbolName,
+ bool PrintFilenames) {
+ unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences);
+ size_t ExpectedReplacementIndex = 0;
+ for (unsigned FileIndex = 0; FileIndex < NumFiles; ++FileIndex) {
+ CXSymbolOccurrencesInFile FileResult;
+ clang_SymbolOccurrences_getOccurrencesForFile(Occurrences, FileIndex,
+ &FileResult);
+ StringRef Filename =
+ PrintFilenames ? clang_getCString(FileResult.Filename) : "";
+
+ for (unsigned I = 0; I != FileResult.NumOccurrences; ++I) {
+ std::string Replacement =
+ occurrenceToString(FileResult.Occurrences[I], IsLocal, NewSymbolName,
+ NewSymbolName, Filename);
+ if (ExpectedReplacementIndex >= ExpectedReplacements.size() ||
+ Replacement != ExpectedReplacements[ExpectedReplacementIndex])
+ return true;
+ ++ExpectedReplacementIndex;
+ }
+ }
+ // Verify that all of the expected replacements were checked.
+ return ExpectedReplacementIndex != ExpectedReplacements.size();
+}
+
+struct ImplementationTUWrapper {
+ CXTranslationUnit TU = nullptr;
+
+ ImplementationTUWrapper() {}
+ ~ImplementationTUWrapper() { clang_disposeTranslationUnit(TU); }
+
+ ImplementationTUWrapper(const ImplementationTUWrapper &) = delete;
+ ImplementationTUWrapper &operator=(const ImplementationTUWrapper &) = delete;
+
+ bool load(CXRefactoringAction Action, CXIndex CIdx,
+ ArrayRef<const char *> Args);
+};
+
+bool ImplementationTUWrapper::load(CXRefactoringAction Action, CXIndex CIdx,
+ ArrayRef<const char *> Args) {
+ if (!clang_RefactoringAction_requiresImplementationTU(Action))
+ return false;
+ CXString USR =
+ clang_RefactoringAction_getUSRThatRequiresImplementationTU(Action);
+ outs() << "Implementation TU USR: '" << clang_getCString(USR) << "'\n";
+ clang_disposeString(USR);
+ if (!TU) {
+ CXErrorCode Err = clang_parseTranslationUnit2(
+ CIdx, opts::ImplementationTU.c_str(), Args.data(), Args.size(), 0, 0,
+ CXTranslationUnit_KeepGoing, &TU);
+ if (Err != CXError_Success) {
+ errs() << "error: failed to load implementation TU '"
+ << opts::ImplementationTU << "'\n";
+ return true;
+ }
+ }
+ CXErrorCode Err = clang_RefactoringAction_addImplementationTU(Action, TU);
+ if (Err != CXError_Success) {
+ errs() << "error: failed to add implementation TU '"
+ << opts::ImplementationTU << "'\n";
+ return true;
+ }
+ return false;
+}
+
+static bool reportNewNameError(CXErrorCode Err) {
+ std::string NewName = opts::RenameIndexedFileSubcommand
+ ? opts::rename::IndexedNewNames[0]
+ : opts::rename::NewName;
+ if (Err == CXError_RefactoringNameSizeMismatch)
+ errs() << "error: the number of strings in the new name '" << NewName
+ << "' doesn't match the the number of strings in the old name\n";
+ else if (Err == CXError_RefactoringNameInvalid)
+ errs() << "error: invalid new name '" << NewName << "'\n";
+ else
+ return true;
+ return false;
+}
+
+int rename(CXTranslationUnit TU, CXIndex CIdx, ArrayRef<const char *> Args) {
+ assert(!opts::RenameIndexedFileSubcommand);
+ // Contains the renamed source replacements for the first location. It is
+ // compared to replacements from follow-up renames to ensure that all renames
+ // give the same result.
+ std::vector<std::string> ExpectedReplacements;
+ // Should we print out the filenames. False by default, but true when multiple
+ // files are modified.
+ bool PrintFilenames = false;
+ ImplementationTUWrapper ImplementationTU;
+
+ auto RenameAt = [&](const ParsedSourceLocation &Location,
+ const std::string &USR) -> int {
+ CXRefactoringAction RenamingAction;
+ CXErrorCode Err;
+ CXDiagnosticSet Diags = nullptr;
+ if (USR.empty()) {
+ CXSourceLocation Loc =
+ clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()),
+ Location.Line, Location.Column);
+ Err = clang_Refactoring_initiateAction(
+ TU, Loc, clang_getNullRange(), CXRefactor_Rename,
+ /*Options=*/nullptr, &RenamingAction, &Diags);
+ } else {
+ Err = clang_Refactoring_initiateActionOnDecl(
+ TU, USR.c_str(), CXRefactor_Rename, /*Options=*/nullptr,
+ &RenamingAction, nullptr);
+ }
+ if (Err != CXError_Success) {
+ errs() << "error: could not rename symbol "
+ << (USR.empty() ? "at the given location\n"
+ : "with the given USR\n");
+ if (USR.empty()) {
+ unsigned NumDiags = clang_getNumDiagnosticsInSet(Diags);
+ for (unsigned DiagID = 0; DiagID < NumDiags; ++DiagID) {
+ CXDiagnostic Diag = clang_getDiagnosticInSet(Diags, DiagID);
+ CXString Spelling = clang_getDiagnosticSpelling(Diag);
+ errs() << clang_getCString(Spelling) << "\n";
+ clang_disposeString(Spelling);
+ }
+ }
+ clang_disposeDiagnosticSet(Diags);
+ return 1;
+ }
+ clang_disposeDiagnosticSet(Diags);
+
+ if (ImplementationTU.load(RenamingAction, CIdx, Args))
+ return 1;
+
+ Err = clang_Refactoring_initiateRenamingOperation(RenamingAction);
+ if (Err != CXError_Success) {
+ errs() << "error: failed to initiate the renaming operation!\n";
+ return 1;
+ }
+
+ bool IsLocal = clang_RefactoringAction_getInitiatedActionType(
+ RenamingAction) == CXRefactor_Rename_Local;
+
+ unsigned NumSymbols = clang_RenamingOperation_getNumSymbols(RenamingAction);
+ if (opts::rename::DumpSymbols) {
+ outs() << "Renaming " << NumSymbols << " symbols\n";
+ for (unsigned I = 0; I < NumSymbols; ++I) {
+ CXString USR =
+ clang_RenamingOperation_getUSRForSymbol(RenamingAction, I);
+ outs() << "'" << clang_getCString(USR) << "'\n";
+ clang_disposeString(USR);
+ }
+ }
+
+ CXSymbolOccurrencesResult Occurrences;
+ Occurrences = clang_Refactoring_findSymbolOccurrencesInInitiationTU(
+ RenamingAction, Args.data(), Args.size(), 0, 0);
+
+ clang_RefactoringAction_dispose(RenamingAction);
+
+ // FIXME: This is a hack
+ LangOptions LangOpts;
+ LangOpts.ObjC1 = true;
+ tooling::SymbolName NewSymbolName(opts::rename::NewName, LangOpts);
+
+ if (ExpectedReplacements.empty()) {
+ if (opts::Apply) {
+ // FIXME: support --apply.
+ }
+
+ unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences);
+ if (NumFiles > 1)
+ PrintFilenames = true;
+ // Convert the occurrences to strings
+ for (unsigned FileIndex = 0; FileIndex < NumFiles; ++FileIndex) {
+ CXSymbolOccurrencesInFile FileResult;
+ clang_SymbolOccurrences_getOccurrencesForFile(Occurrences, FileIndex,
+ &FileResult);
+ StringRef Filename =
+ PrintFilenames ? clang_getCString(FileResult.Filename) : "";
+ for (unsigned I = 0; I != FileResult.NumOccurrences; ++I)
+ ExpectedReplacements.push_back(
+ occurrenceToString(FileResult.Occurrences[I], IsLocal,
+ NewSymbolName, NewSymbolName, Filename));
+ }
+ clang_SymbolOccurrences_dispose(Occurrences);
+ return 0;
+ }
+ // Compare the produced occurrences to the expected occurrences that were
+ // gathered at the first location.
+ bool AreOccurrencesDifferent =
+ compareOccurrences(ExpectedReplacements, Occurrences, IsLocal,
+ NewSymbolName, PrintFilenames);
+ clang_SymbolOccurrences_dispose(Occurrences);
+ if (!AreOccurrencesDifferent)
+ return 0;
+ errs() << "error: occurrences for a rename at " << Location.FileName << ":"
+ << Location.Line << ":" << Location.Column
+ << " differ to occurrences from the rename at the first location!\n";
+ return 1;
+ };
+
+ std::vector<ParsedSourceLocation> ParsedLocations;
+ for (const auto &I : enumerate(opts::rename::AtLocation)) {
+ auto Location = ParsedSourceLocation::FromString(I.value());
+ if (Location.FileName.empty()) {
+ errs()
+ << "error: The -at option must use the <file:line:column> format\n";
+ return 1;
+ }
+ ParsedLocations.push_back(Location);
+ }
+
+ if (opts::RenameInitiateUSRSubcommand) {
+ if (RenameAt(ParsedSourceLocation(), opts::rename::USR))
+ return 1;
+ } else {
+ assert(!ParsedLocations.empty() && "No -at locations");
+
+ for (const auto &Location : ParsedLocations) {
+ if (RenameAt(Location, ""))
+ return 1;
+ }
+ }
+
+ // Print the produced renamed replacements
+ if (opts::Apply)
+ return 0;
+ for (const auto &Replacement : ExpectedReplacements)
+ outs() << Replacement << "\n";
+ if (ExpectedReplacements.empty())
+ outs() << "no replacements found\n";
+ return 0;
+}
+
+int renameIndexedFile(CXIndex CIdx, ArrayRef<const char *> Args) {
+ assert(opts::RenameIndexedFileSubcommand);
+
+ // Compute the number of symbols.
+ unsigned NumSymbols = opts::rename::IndexedNames.size();
+
+ // Get the occurrences of a symbol.
+ CXCursorKind DefaultCursorKind = renameIndexedOccurrenceKindStringToKind(
+ opts::rename::IndexedSymbolKind, CXCursor_NotImplemented);
+ std::vector<std::vector<CXIndexedSymbolLocation>> IndexedOccurrences(
+ NumSymbols, std::vector<CXIndexedSymbolLocation>());
+ for (const auto &IndexedOccurrence : opts::rename::IndexedLocations) {
+ auto Occurrence =
+ parseIndexedOccurrence(IndexedOccurrence, DefaultCursorKind);
+ unsigned SymbolIndex = Occurrence.second;
+ assert(SymbolIndex < IndexedOccurrences.size() && "Invalid symbol index");
+ IndexedOccurrences[SymbolIndex].push_back(CXIndexedSymbolLocation{
+ Occurrence.first.Location, Occurrence.first.CursorKind});
+ }
+
+ // Create the indexed symbols.
+ std::vector<CXIndexedSymbol> IndexedSymbols;
+ for (const auto &I : llvm::enumerate(IndexedOccurrences)) {
+ const auto &Occurrences = I.value();
+ const char *Name =
+ opts::rename::IndexedNames[opts::rename::IndexedNames.size() < 2
+ ? 0
+ : I.index()]
+ .c_str();
+ IndexedSymbols.push_back({Occurrences.data(), (unsigned)Occurrences.size(),
+ DefaultCursorKind, Name});
+ }
+
+ CXRefactoringOptionSet Options = nullptr;
+ if (opts::rename::AvoidTextual) {
+ Options = clang_RefactoringOptionSet_create();
+ clang_RefactoringOptionSet_add(Options,
+ CXRefactorOption_AvoidTextualMatches);
+ }
+
+ CXSymbolOccurrencesResult Occurrences;
+ CXErrorCode Err = clang_Refactoring_findSymbolOccurrencesInIndexedFile(
+ IndexedSymbols.data(), IndexedSymbols.size(), CIdx,
+ opts::rename::IndexedFileName.c_str(), Args.data(), Args.size(), 0, 0,
+ Options, &Occurrences);
+ if (Err != CXError_Success) {
+ if (reportNewNameError(Err))
+ errs() << "error: failed to perform indexed file rename\n";
+ return 1;
+ }
+
+ if (Options)
+ clang_RefactoringOptionSet_dispose(Options);
+
+ // Should we print out the filenames. False by default, but true when multiple
+ // files are modified.
+ bool PrintFilenames = false;
+ unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Occurrences);
+ if (NumFiles > 1)
+ PrintFilenames = true;
+
+ LangOptions LangOpts;
+ LangOpts.ObjC1 = true;
+ tooling::SymbolName ExpectedReplacementStrings(
+ opts::rename::IndexedNewNames[0], LangOpts);
+
+ // Print the occurrences.
+ bool HasReplacements = false;
+ for (unsigned FileIndex = 0; FileIndex < NumFiles; ++FileIndex) {
+ CXSymbolOccurrencesInFile FileResult;
+ clang_SymbolOccurrences_getOccurrencesForFile(Occurrences, FileIndex,
+ &FileResult);
+ StringRef Filename =
+ PrintFilenames ? clang_getCString(FileResult.Filename) : "";
+ HasReplacements = FileResult.NumOccurrences;
+ for (unsigned I = 0; I != FileResult.NumOccurrences; ++I) {
+ unsigned SymbolIndex = FileResult.Occurrences[I].SymbolIndex;
+ const char *NewName =
+ opts::rename::IndexedNewNames[opts::rename::IndexedNewNames.size() < 2
+ ? 0
+ : SymbolIndex]
+ .c_str();
+ LangOptions LangOpts;
+ LangOpts.ObjC1 = true;
+ tooling::SymbolName NewSymbolName(NewName, LangOpts);
+
+ outs() << occurrenceToString(FileResult.Occurrences[I], /*IsLocal*/ false,
+ NewSymbolName, ExpectedReplacementStrings,
+ Filename)
+ << "\n";
+ }
+ }
+ if (!HasReplacements)
+ outs() << "no replacements found\n";
+ clang_SymbolOccurrences_dispose(Occurrences);
+ return 0;
+}
+
+/// Returns the last column number of a line in a file.
+static unsigned lastColumnForFile(StringRef Filename, unsigned LineNo) {
+ auto Buf = llvm::MemoryBuffer::getFile(Filename);
+ if (!Buf)
+ return 0;
+ unsigned LineCount = 1;
+ for (llvm::line_iterator Lines(**Buf, /*SkipBlanks=*/false);
+ !Lines.is_at_end(); ++Lines, ++LineCount) {
+ if (LineNo == LineCount)
+ return Lines->size() + 1;
+ }
+ return 0;
+}
+
+struct ParsedSourceLineRange : ParsedSourceLocation {
+ unsigned MaxColumn;
+
+ ParsedSourceLineRange() {}
+ ParsedSourceLineRange(const ParsedSourceLocation &Loc)
+ : ParsedSourceLocation(Loc), MaxColumn(Loc.Column) {}
+
+ static Optional<ParsedSourceLineRange> FromString(StringRef Str) {
+ std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-');
+ auto PSL = ParsedSourceLocation::FromString(RangeSplit.first);
+ ParsedSourceLineRange Result;
+ Result.FileName = std::move(PSL.FileName);
+ Result.Line = PSL.Line;
+ Result.Column = PSL.Column;
+ if (Result.FileName.empty())
+ return None;
+ if (RangeSplit.second == "end")
+ Result.MaxColumn = lastColumnForFile(Result.FileName, Result.Line);
+ else if (RangeSplit.second.getAsInteger(10, Result.MaxColumn))
+ return None;
+ if (Result.MaxColumn < Result.Column)
+ return None;
+ return Result;
+ }
+};
+
+struct ParsedSourceRange {
+ ParsedSourceLocation Begin, End;
+
+ ParsedSourceRange(const ParsedSourceLocation &Begin,
+ const ParsedSourceLocation &End)
+ : Begin(Begin), End(End) {}
+
+ static Optional<ParsedSourceRange> FromString(StringRef Str) {
+ std::pair<StringRef, StringRef> RangeSplit = Str.rsplit('-');
+ auto Begin = ParsedSourceLocation::FromString(RangeSplit.first);
+ if (Begin.FileName.empty())
+ return None;
+ std::string EndString = Begin.FileName + ":" + RangeSplit.second.str();
+ auto End = ParsedSourceLocation::FromString(EndString);
+ if (End.FileName.empty())
+ return None;
+ return ParsedSourceRange(Begin, End);
+ }
+};
+
+int listRefactoringActions(CXTranslationUnit TU) {
+ auto Location =
+ ParsedSourceLocation::FromString(opts::listActions::AtLocation);
+ if (Location.FileName.empty()) {
+ errs() << "error: The -at option must use the <file:line:column> format\n";
+ return 1;
+ }
+ CXSourceRange Range;
+ if (!opts::listActions::SelectedRange.empty()) {
+ auto SelectionRange =
+ ParsedSourceRange::FromString(opts::listActions::SelectedRange);
+ if (!SelectionRange) {
+ errs() << "error: The -selected option must use the "
+ "<file:line:column-line:column> format\n";
+ return 1;
+ }
+ auto Begin = SelectionRange.getValue().Begin;
+ auto End = SelectionRange.getValue().End;
+ CXFile File = clang_getFile(TU, Begin.FileName.c_str());
+ Range =
+ clang_getRange(clang_getLocation(TU, File, Begin.Line, Begin.Column),
+ clang_getLocation(TU, File, End.Line, End.Column));
+ } else
+ Range = clang_getNullRange();
+ CXSourceLocation Loc =
+ clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()),
+ Location.Line, Location.Column);
+ CXRefactoringActionSet ActionSet;
+ CXRefactoringActionSetWithDiagnostics FailedActionSet;
+ CXErrorCode Err =
+ clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt(
+ TU, Loc, Range, /*Options=*/nullptr, &ActionSet, &FailedActionSet);
+ if (FailedActionSet.NumActions) {
+ errs() << "Failed to initiate " << FailedActionSet.NumActions
+ << " actions because:\n";
+ for (unsigned I = 0; I < FailedActionSet.NumActions; ++I) {
+ errs() << clang_getCString(clang_RefactoringActionType_getName(
+ FailedActionSet.Actions[I].Action))
+ << ":";
+ CXDiagnosticSet Diags = FailedActionSet.Actions[I].Diagnostics;
+ unsigned NumDiags = clang_getNumDiagnosticsInSet(Diags);
+ for (unsigned DiagID = 0; DiagID < NumDiags; ++DiagID) {
+ CXDiagnostic Diag = clang_getDiagnosticInSet(Diags, DiagID);
+ CXString Spelling = clang_getDiagnosticSpelling(Diag);
+ errs() << ' ' << clang_getCString(Spelling);
+ clang_disposeString(Spelling);
+ }
+ errs() << "\n";
+ }
+ }
+ if (Err == CXError_RefactoringActionUnavailable)
+ errs() << "No refactoring actions are available at the given location\n";
+ if (Err != CXError_Success)
+ return 1;
+ // Print the list of refactoring actions.
+ outs() << "Found " << ActionSet.NumActions << " actions:\n";
+ for (unsigned I = 0; I < ActionSet.NumActions; ++I) {
+ outs() << clang_getCString(
+ clang_RefactoringActionType_getName(ActionSet.Actions[I]));
+ if (opts::listActions::DumpRawActionType)
+ outs() << "(" << ActionSet.Actions[I] << ")";
+ outs() << "\n";
+ }
+ clang_RefactoringActionSet_dispose(&ActionSet);
+ clang_RefactoringActionSetWithDiagnostics_dispose(&FailedActionSet);
+ return 0;
+}
+
+static std::string locationToString(CXSourceLocation Loc) {
+ unsigned Line, Column;
+ clang_getFileLocation(Loc, nullptr, &Line, &Column, nullptr);
+ std::string S;
+ llvm::raw_string_ostream OS(S);
+ OS << Line << ':' << Column;
+ return OS.str();
+}
+
+static std::string rangeToString(CXSourceRange Range) {
+ return locationToString(clang_getRangeStart(Range)) + " -> " +
+ locationToString(clang_getRangeEnd(Range));
+}
+
+static std::string
+refactoringCandidatesToString(CXRefactoringCandidateSet Candidates) {
+ std::string Results = "with multiple candidates:";
+ for (unsigned I = 0; I < Candidates.NumCandidates; ++I) {
+ Results += "\n";
+ Results += clang_getCString(Candidates.Candidates[I].Description);
+ }
+ return Results;
+}
+
+static void printEscaped(StringRef Str, raw_ostream &OS) {
+ size_t Pos = Str.find('\n');
+ OS << Str.substr(0, Pos);
+ if (Pos == StringRef::npos)
+ return;
+ OS << "\\n";
+ printEscaped(Str.substr(Pos + 1), OS);
+}
+
+bool printRefactoringReplacements(
+ CXRefactoringResult Result, CXRefactoringContinuation Continuation,
+ CXRefactoringContinuation CurrentContinuation) {
+ CXRefactoringReplacements Replacements =
+ clang_RefactoringResult_getSourceReplacements(Result);
+ if (Replacements.NumFileReplacementSets == 0) {
+ if (CurrentContinuation)
+ return false;
+ errs() << "error: no replacements produced!\n";
+ return true;
+ }
+ // Print out the produced results.
+ for (unsigned FileIndex = 0; FileIndex < Replacements.NumFileReplacementSets;
+ ++FileIndex) {
+ const CXRefactoringFileReplacementSet &FileSet =
+ Replacements.FileReplacementSets[FileIndex];
+ if (opts::Apply) {
+ apply(llvm::makeArrayRef(FileSet.Replacements, FileSet.NumReplacements),
+ clang_getCString(FileSet.Filename));
+ continue;
+ }
+ for (unsigned I = 0; I < FileSet.NumReplacements; ++I) {
+ const CXRefactoringReplacement &Replacement = FileSet.Replacements[I];
+
+ if (Continuation) {
+ // Always print the filenames in with continuations.
+ outs() << '"' << clang_getCString(FileSet.Filename) << "\" ";
+ }
+ outs() << '"';
+ printEscaped(clang_getCString(Replacement.ReplacementString), outs());
+ outs() << "\" ";
+ CXFileRange Range = Replacement.Range;
+ outs() << Range.Begin.Line << ":" << Range.Begin.Column << " -> "
+ << Range.End.Line << ":" << Range.End.Column;
+ if (opts::initiateAndPerform::EmitAssociatedInfo) {
+ CXRefactoringReplacementAssociatedSymbolOccurrences Info =
+ clang_RefactoringReplacement_getAssociatedSymbolOccurrences(
+ Replacement);
+ for (const CXSymbolOccurrence &SymbolOccurrence :
+ llvm::makeArrayRef(Info.AssociatedSymbolOccurrences,
+ Info.NumAssociatedSymbolOccurrences)) {
+ outs() << " [Symbol " << renameOccurrenceKindString(
+ SymbolOccurrence.Kind, /*IsLocal*/ false,
+ SymbolOccurrence.IsMacroExpansion)
+ << ' ' << SymbolOccurrence.SymbolIndex;
+ for (const auto &Piece :
+ llvm::makeArrayRef(SymbolOccurrence.NamePieces,
+ SymbolOccurrence.NumNamePieces)) {
+ outs() << ' ' << Piece.Begin.Line << ":" << Piece.Begin.Column
+ << " -> " << Piece.End.Line << ":" << Piece.End.Column;
+ }
+ outs() << ']';
+ }
+ }
+ outs() << "\n";
+ }
+ }
+ return false;
+}
+
+/// Returns the last column number of a line in a file.
+static std::string queryResultsForFile(StringRef Filename, StringRef Name,
+ StringRef FileSubstitution) {
+ auto Buf = llvm::MemoryBuffer::getFile(Filename);
+ if (!Buf)
+ return "<invalid>";
+ StringRef Buffer = (*Buf)->getBuffer();
+ std::string Label = Name.str() + ":";
+ size_t I = Buffer.find(Label);
+ if (I == StringRef::npos)
+ return "<invalid>";
+ I = I + Label.size();
+ auto Result = Buffer.substr(I, Buffer.find('\n', I) - I);
+ std::string Sub1 = llvm::Regex("%s").sub(FileSubstitution, Result);
+ return llvm::Regex("%S").sub(llvm::sys::path::parent_path(FileSubstitution),
+ Sub1);
+}
+
+static Optional<std::pair<unsigned, unsigned>>
+findSelectionLocInSource(StringRef Buffer, StringRef Label) {
+ size_t I = Buffer.find(Label);
+ if (I == StringRef::npos)
+ return None;
+ I = I + Label.size();
+ auto LocParts =
+ Buffer.substr(I, Buffer.find_first_of("\n/", I) - I).trim().split(":");
+ unsigned CurrentLine = Buffer.take_front(I).count('\n') + 1;
+ if (LocParts.second.empty())
+ return None;
+ StringRef LineString = LocParts.first;
+ unsigned Line, Column;
+ enum ExprKind { Literal, Add, Sub };
+ ExprKind Expr = LineString.startswith("+")
+ ? Add
+ : LineString.startswith("-") ? Sub : Literal;
+ if (LineString.drop_front(Expr != Literal ? 1 : 0).getAsInteger(10, Line))
+ return None;
+ if (Expr == Add)
+ Line += CurrentLine;
+ else if (Expr == Sub)
+ Line = CurrentLine - Line;
+ if (LocParts.second.getAsInteger(10, Column))
+ return None;
+ return std::make_pair(Line, Column);
+}
+
+static Optional<ParsedSourceLocation> selectionLocForFile(StringRef Filename,
+ StringRef Name) {
+ auto Buf = llvm::MemoryBuffer::getFile(Filename);
+ if (!Buf)
+ return None;
+
+ StringRef Buffer = (*Buf)->getBuffer();
+ std::string Label = Name.str() + ":";
+ auto Start = findSelectionLocInSource(Buffer, Label);
+ if (!Start)
+ return None;
+ // Create the resulting source location.
+ // FIXME: Parse can be avoided.
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ OS << Filename << ":" << Start->first << ":" << Start->second;
+ return ParsedSourceLocation::FromString(OS.str());
+}
+
+static Optional<ParsedSourceRange> selectionRangeForFile(StringRef Filename,
+ StringRef Name) {
+ auto Buf = llvm::MemoryBuffer::getFile(Filename);
+ if (!Buf)
+ return None;
+
+ StringRef Buffer = (*Buf)->getBuffer();
+ std::string BeginLabel = Name.str() + "-begin:";
+ std::string EndLabel = Name.str() + "-end:";
+ auto Start = findSelectionLocInSource(Buffer, BeginLabel);
+ auto End = findSelectionLocInSource(Buffer, EndLabel);
+ if (!Start || !End)
+ return None;
+ // Create the resulting source range.
+ // FIXME: Parse can be avoided.
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+ OS << Filename << ":" << Start->first << ":" << Start->second << "-"
+ << End->first << ":" << End->second;
+ return ParsedSourceRange::FromString(OS.str());
+}
+
+bool performOperation(CXRefactoringAction Action, ArrayRef<const char *> Args,
+ CXIndex CIdx) {
+ if (opts::initiateAndPerform::CandidateIndex.getNumOccurrences()) {
+ if (clang_RefactoringAction_selectRefactoringCandidate(
+ Action, opts::initiateAndPerform::CandidateIndex)) {
+ errs() << "error: failed to select the refactoring candidate!\n";
+ return true;
+ }
+ }
+ CXRefactoringOptionSet Options = nullptr;
+ CXString FailureReason;
+ CXRefactoringResult Result = clang_Refactoring_performOperation(
+ Action, Args.data(), Args.size(), nullptr, 0, Options, &FailureReason);
+ if (!Result) {
+ errs() << "error: failed to perform the refactoring operation";
+ if (const char *Reason = clang_getCString(FailureReason))
+ errs() << " (" << Reason << ')';
+ errs() << "!\n";
+ clang_disposeString(FailureReason);
+ return true;
+ }
+ CXRefactoringContinuation Continuation =
+ clang_RefactoringResult_getContinuation(Result);
+ bool AreReplacementsInvalid =
+ printRefactoringReplacements(Result, Continuation, Continuation);
+ clang_RefactoringResult_dispose(Result);
+ if (AreReplacementsInvalid) {
+ clang_RefactoringContinuation_dispose(Continuation);
+ return true;
+ }
+ if (!Continuation)
+ return false;
+ assert(clang_RefactoringContinuation_getNumIndexerQueries(Continuation) !=
+ 0 &&
+ "Missing indexer queries?");
+ std::string QueryResults = queryResultsForFile(
+ opts::FileName, opts::initiateAndPerform::QueryResults,
+ /*FileSubstitution=*/opts::initiateAndPerform::ContinuationFile);
+ clang_RefactoringContinuation_loadSerializedIndexerQueryResults(
+ Continuation, /*Source=*/QueryResults.c_str());
+ CXDiagnosticSet Diags =
+ clang_RefactoringContinuation_verifyBeforeFinalizing(Continuation);
+ if (Diags) {
+ llvm::errs() << "error: continuation failed: ";
+ for (unsigned I = 0, E = clang_getNumDiagnosticsInSet(Diags); I != E; ++I) {
+ CXDiagnostic Diag = clang_getDiagnosticInSet(Diags, I);
+ CXString Spelling = clang_getDiagnosticSpelling(Diag);
+ errs() << clang_getCString(Spelling) << "\n";
+ clang_disposeString(Spelling);
+ clang_disposeDiagnostic(Diag);
+ }
+ clang_RefactoringContinuation_dispose(Continuation);
+ clang_disposeDiagnosticSet(Diags);
+ return true;
+ }
+ clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation);
+ // Load the continuation TU.
+ CXTranslationUnit ContinuationTU;
+ CXErrorCode Err = clang_parseTranslationUnit2(
+ CIdx, opts::initiateAndPerform::ContinuationFile.c_str(), Args.data(),
+ Args.size(), 0, 0, CXTranslationUnit_KeepGoing, &ContinuationTU);
+ if (Err != CXError_Success) {
+ errs() << "error: failed to load '"
+ << opts::initiateAndPerform::ContinuationFile.c_str() << "'\n";
+ clang_RefactoringContinuation_dispose(Continuation);
+ return true;
+ }
+ Result = clang_RefactoringContinuation_continueOperationInTU(
+ Continuation, ContinuationTU, &FailureReason);
+ if (!Result) {
+ errs() << "error: failed to perform the refactoring continuation";
+ if (const char *Reason = clang_getCString(FailureReason))
+ errs() << " (" << Reason << ')';
+ errs() << "!\n";
+ clang_disposeString(FailureReason);
+ clang_disposeTranslationUnit(ContinuationTU);
+ clang_RefactoringContinuation_dispose(Continuation);
+ return true;
+ }
+ // FIXME: Continuations can be chained in the future.
+ AreReplacementsInvalid =
+ printRefactoringReplacements(Result, Continuation, nullptr);
+ clang_RefactoringResult_dispose(Result);
+ clang_disposeTranslationUnit(ContinuationTU);
+ clang_RefactoringContinuation_dispose(Continuation);
+ return AreReplacementsInvalid;
+}
+
+int initiateAndPerformAction(CXTranslationUnit TU, ArrayRef<const char *> Args,
+ CXIndex CIdx) {
+ std::vector<ParsedSourceLineRange> Ranges;
+ std::vector<ParsedSourceRange> SelectionRanges;
+ for (const auto &Range : opts::initiateAndPerform::InLocationRanges) {
+ auto ParsedLineRange = ParsedSourceLineRange::FromString(Range);
+ if (!ParsedLineRange) {
+ errs()
+ << "error: The -in option must use the <file:line:column[-column]> "
+ "format\n";
+ return 1;
+ }
+ Ranges.push_back(ParsedLineRange.getValue());
+ }
+ for (const auto &Range : opts::initiateAndPerform::AtLocations) {
+ if (!StringRef(Range).contains(':')) {
+ auto ParsedLocation = selectionLocForFile(opts::FileName, Range);
+ if (!ParsedLocation) {
+ errs() << "error: The -at option must use the <file:line:column> "
+ "format\n";
+ return 1;
+ }
+ Ranges.push_back(*ParsedLocation);
+ continue;
+ }
+ // TODO: Remove old location in arguments in favour of new testing
+ // locations.
+ auto ParsedLocation = ParsedSourceLocation::FromString(Range);
+ if (ParsedLocation.FileName.empty()) {
+ errs() << "error: The -at option must use the <file:line:column> "
+ "format\n";
+ return 1;
+ }
+ Ranges.push_back(ParsedLocation);
+ }
+ for (const auto &Range : opts::initiateAndPerform::SelectedRanges) {
+ auto ParsedRange = StringRef(Range).contains(':')
+ ? ParsedSourceRange::FromString(Range)
+ : selectionRangeForFile(opts::FileName, Range);
+ if (!ParsedRange) {
+ errs() << "error: The -selected option must use the "
+ "<file:line:column-line:column> format or refer to the name of "
+ "the selection specifier in the source\n";
+ return 1;
+ }
+ SelectionRanges.push_back(ParsedRange.getValue());
+ }
+ if (Ranges.empty() && SelectionRanges.empty()) {
+ errs() << "error: -in or -at options must be specified at least once!";
+ return 1;
+ }
+ if (!Ranges.empty() && !SelectionRanges.empty()) {
+ errs() << "error: -in or -at options can't be used with -selected!";
+ return 1;
+ }
+
+ auto ActionTypeOrNone = StringSwitch<Optional<CXRefactoringActionType>>(
+ opts::initiateAndPerform::ActionName)
+#define REFACTORING_OPERATION_ACTION(Name, Spelling, Command) \
+ .Case(Command, CXRefactor_##Name)
+#define REFACTORING_OPERATION_SUB_ACTION(Name, Parent, Spelling, Command) \
+ .Case(Command, CXRefactor_##Parent##_##Name)
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+ .Default(None);
+ if (!ActionTypeOrNone) {
+ errs() << "error: invalid action '" << opts::initiateAndPerform::ActionName
+ << "'\n";
+ return 1;
+ }
+ CXRefactoringActionType ActionType = *ActionTypeOrNone;
+
+ Optional<bool> Initiated;
+ Optional<std::string> InitiationFailureReason;
+ Optional<std::string> LocationCandidateInformation;
+ auto InitiateAndPerform =
+ [&](const ParsedSourceLocation &Location, unsigned Column,
+ Optional<ParsedSourceRange> SelectionRange = None) -> bool {
+ CXSourceLocation Loc =
+ clang_getLocation(TU, clang_getFile(TU, Location.FileName.c_str()),
+ Location.Line, Column);
+ CXSourceRange Range;
+ if (SelectionRange) {
+ auto Begin = SelectionRange.getValue().Begin;
+ auto End = SelectionRange.getValue().End;
+ CXFile File = clang_getFile(TU, Begin.FileName.c_str());
+ Range =
+ clang_getRange(clang_getLocation(TU, File, Begin.Line, Begin.Column),
+ clang_getLocation(TU, File, End.Line, End.Column));
+ } else
+ Range = clang_getNullRange();
+ CXRefactoringAction Action;
+ CXString FailureReason;
+ CXErrorCode Err = clang_Refactoring_initiateActionAt(
+ TU, Loc, Range, ActionType, /*Options=*/nullptr, &Action,
+ &FailureReason);
+ std::string ReasonString;
+ if (const char *Reason = clang_getCString(FailureReason))
+ ReasonString = Reason;
+ clang_disposeString(FailureReason);
+ if (InitiationFailureReason.hasValue() &&
+ InitiationFailureReason.getValue() != ReasonString) {
+ errs() << "error: inconsistent results in a single action range!\n";
+ return true;
+ }
+ InitiationFailureReason = std::move(ReasonString);
+ if (Err == CXError_RefactoringActionUnavailable) {
+ if (Initiated.hasValue() && Initiated.getValue()) {
+ errs() << "error: inconsistent results in a single action range!\n";
+ return true;
+ }
+ Initiated = false;
+ } else if (Err != CXError_Success)
+ return true;
+ else if (Initiated.hasValue() && !Initiated.getValue()) {
+ errs() << "error: inconsistent results in a single action range!\n";
+ return true;
+ } else
+ Initiated = true;
+
+ CXRefactoringCandidateSet Candidates;
+ if (clang_RefactoringAction_getRefactoringCandidates(Action, &Candidates) ==
+ CXError_Success &&
+ Candidates.NumCandidates > 1) {
+ std::string CandidateString = refactoringCandidatesToString(Candidates);
+ if (LocationCandidateInformation) {
+ if (*LocationCandidateInformation != CandidateString) {
+ errs() << "error: inconsistent results in a single action range!\n";
+ return true;
+ }
+ } else
+ LocationCandidateInformation = CandidateString;
+ } else if (opts::InitiateActionSubcommand &&
+ !opts::initiateAndPerform::LocationAgnostic) {
+ CXSourceRange Range =
+ clang_RefactoringAction_getSourceRangeOfInterest(Action);
+ std::string LocationString =
+ std::string("at ") +
+ (!clang_Range_isNull(Range)
+ ? SelectionRange ? rangeToString(Range)
+ : locationToString(clang_getRangeStart(Range))
+ : "<unknown>");
+ if (!LocationCandidateInformation.hasValue())
+ LocationCandidateInformation = LocationString;
+ else if (LocationCandidateInformation.getValue() != LocationString) {
+ errs() << "error: inconsistent results in a single action range!\n";
+ return true;
+ }
+ }
+
+ if (!*Initiated)
+ return false;
+
+ bool Failed = opts::PerformActionSubcommand
+ ? performOperation(Action, Args, CIdx)
+ : false;
+ clang_RefactoringAction_dispose(Action);
+ return Failed;
+ };
+
+ // Iterate over all of the possible locations and perform the initiation
+ // at each range.
+ for (const ParsedSourceLineRange &LineRange : Ranges) {
+ for (unsigned Column = LineRange.Column; Column <= LineRange.MaxColumn;
+ ++Column) {
+ if (InitiateAndPerform(LineRange, Column))
+ return 1;
+ }
+ }
+
+ for (const ParsedSourceRange &SelectionRange : SelectionRanges) {
+ if (InitiateAndPerform(SelectionRange.Begin, SelectionRange.Begin.Column,
+ SelectionRange))
+ return 1;
+ }
+
+ if (!Initiated.getValue()) {
+ errs() << "Failed to initiate the refactoring action";
+ if (InitiationFailureReason.hasValue() &&
+ !InitiationFailureReason.getValue().empty())
+ errs() << " (" << InitiationFailureReason.getValue() << ')';
+ errs() << "!\n";
+ return 1;
+ }
+ if (opts::InitiateActionSubcommand) {
+ outs() << "Initiated the '" << opts::initiateAndPerform::ActionName
+ << "' action";
+ if (!opts::initiateAndPerform::LocationAgnostic)
+ outs() << ' ' << LocationCandidateInformation.getValue();
+ outs() << "\n";
+ }
+ return 0;
+}
+
+int main(int argc, const char **argv) {
+ cl::HideUnrelatedOptions(opts::ClangRefactorTestOptions);
+
+ cl::ParseCommandLineOptions(argc, argv, "Clang refactoring test tool\n");
+ cl::PrintOptionValues();
+
+ CXIndex CIdx = clang_createIndex(0, 0);
+
+ std::vector<const char *> Args;
+ for (const auto &Arg : opts::CompilerArguments) {
+ Args.push_back(Arg.c_str());
+ }
+ CXTranslationUnit TU;
+ CXErrorCode Err = clang_parseTranslationUnit2(
+ CIdx,
+ opts::IgnoreFilenameForInitiationTU ? nullptr : opts::FileName.c_str(),
+ Args.data(), Args.size(), 0, 0, CXTranslationUnit_KeepGoing, &TU);
+ if (Err != CXError_Success) {
+ errs() << "error: failed to load '" << opts::FileName << "'\n";
+ return 1;
+ }
+
+ if (opts::RenameInitiateSubcommand || opts::RenameInitiateUSRSubcommand)
+ return rename(TU, CIdx, Args);
+ else if (opts::RenameIndexedFileSubcommand)
+ return renameIndexedFile(CIdx, Args);
+ else if (opts::ListRefactoringActionsSubcommand)
+ return listRefactoringActions(TU);
+ else if (opts::InitiateActionSubcommand || opts::PerformActionSubcommand)
+ return initiateAndPerformAction(TU, Args, CIdx);
+
+ clang_disposeTranslationUnit(TU);
+ clang_disposeIndex(CIdx);
+
+ return 0;
+}
diff --git a/tools/diagtool/DiagnosticNames.cpp b/tools/diagtool/DiagnosticNames.cpp
index a08da89..00af854 100644
--- a/tools/diagtool/DiagnosticNames.cpp
+++ b/tools/diagtool/DiagnosticNames.cpp
@@ -41,6 +41,7 @@
#include "clang/Basic/DiagnosticCommentKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
+#include "clang/Basic/DiagnosticRefactoringKinds.inc"
#undef DIAG
};
@@ -84,6 +85,11 @@
return nullptr;
}
+llvm::iterator_range<diagtool::GroupRecord::subgroup_iterator>
+GroupRecord::subgroups() const {
+ return llvm::make_range(subgroup_begin(), subgroup_end());
+}
+
GroupRecord::diagnostics_iterator GroupRecord::diagnostics_begin() const {
return DiagArrays + Members;
}
@@ -92,6 +98,11 @@
return nullptr;
}
+llvm::iterator_range<diagtool::GroupRecord::diagnostics_iterator>
+GroupRecord::diagnostics() const {
+ return llvm::make_range(diagnostics_begin(), diagnostics_end());
+}
+
llvm::ArrayRef<GroupRecord> diagtool::getDiagnosticGroups() {
return llvm::makeArrayRef(OptionTable);
}
diff --git a/tools/diagtool/DiagnosticNames.h b/tools/diagtool/DiagnosticNames.h
index ac1a098..598ae6a 100644
--- a/tools/diagtool/DiagnosticNames.h
+++ b/tools/diagtool/DiagnosticNames.h
@@ -20,7 +20,7 @@
const char *NameStr;
short DiagID;
uint8_t NameLen;
-
+
llvm::StringRef getName() const {
return llvm::StringRef(NameStr, NameLen);
}
@@ -80,7 +80,7 @@
bool operator==(group_iterator &Other) const {
return CurrentID == Other.CurrentID;
}
-
+
bool operator!=(group_iterator &Other) const {
return CurrentID != Other.CurrentID;
}
@@ -89,10 +89,12 @@
typedef group_iterator<GroupRecord> subgroup_iterator;
subgroup_iterator subgroup_begin() const;
subgroup_iterator subgroup_end() const;
+ llvm::iterator_range<subgroup_iterator> subgroups() const;
typedef group_iterator<DiagnosticRecord> diagnostics_iterator;
diagnostics_iterator diagnostics_begin() const;
diagnostics_iterator diagnostics_end() const;
+ llvm::iterator_range<diagnostics_iterator> diagnostics() const;
bool operator<(llvm::StringRef Other) const {
return getName() < Other;
diff --git a/tools/diagtool/ListWarnings.cpp b/tools/diagtool/ListWarnings.cpp
index 3e6e883..8bf9df9 100644
--- a/tools/diagtool/ListWarnings.cpp
+++ b/tools/diagtool/ListWarnings.cpp
@@ -23,7 +23,7 @@
DEF_DIAGTOOL("list-warnings",
"List warnings and their corresponding flags",
ListWarnings)
-
+
using namespace clang;
using namespace diagtool;
@@ -31,20 +31,19 @@
struct Entry {
llvm::StringRef DiagName;
llvm::StringRef Flag;
-
+
Entry(llvm::StringRef diagN, llvm::StringRef flag)
: DiagName(diagN), Flag(flag) {}
-
+
bool operator<(const Entry &x) const { return DiagName < x.DiagName; }
};
}
static void printEntries(std::vector<Entry> &entries, llvm::raw_ostream &out) {
- for (std::vector<Entry>::iterator it = entries.begin(), ei = entries.end();
- it != ei; ++it) {
- out << " " << it->DiagName;
- if (!it->Flag.empty())
- out << " [-W" << it->Flag << "]";
+ for (const Entry &E : entries) {
+ out << " " << E.DiagName;
+ if (!E.Flag.empty())
+ out << " [-W" << E.Flag << "]";
out << '\n';
}
}
@@ -52,23 +51,18 @@
int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
std::vector<Entry> Flagged, Unflagged;
llvm::StringMap<std::vector<unsigned> > flagHistogram;
-
- ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
- for (ArrayRef<DiagnosticRecord>::iterator di = AllDiagnostics.begin(),
- de = AllDiagnostics.end();
- di != de; ++di) {
- unsigned diagID = di->DiagID;
-
+ for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) {
+ const unsigned diagID = DR.DiagID;
+
if (DiagnosticIDs::isBuiltinNote(diagID))
continue;
-
+
if (!DiagnosticIDs::isBuiltinWarningOrExtension(diagID))
continue;
-
- Entry entry(di->getName(),
- DiagnosticIDs::getWarningOptionForDiag(diagID));
-
+
+ Entry entry(DR.getName(), DiagnosticIDs::getWarningOptionForDiag(diagID));
+
if (entry.Flag.empty())
Unflagged.push_back(entry);
else {
@@ -76,24 +70,24 @@
flagHistogram[entry.Flag].push_back(diagID);
}
}
-
+
out << "Warnings with flags (" << Flagged.size() << "):\n";
printEntries(Flagged, out);
-
+
out << "Warnings without flags (" << Unflagged.size() << "):\n";
printEntries(Unflagged, out);
out << "\nSTATISTICS:\n\n";
- double percentFlagged = ((double) Flagged.size())
- / (Flagged.size() + Unflagged.size()) * 100.0;
-
- out << " Percentage of warnings with flags: "
- << llvm::format("%.4g",percentFlagged) << "%\n";
-
+ double percentFlagged =
+ ((double)Flagged.size()) / (Flagged.size() + Unflagged.size()) * 100.0;
+
+ out << " Percentage of warnings with flags: "
+ << llvm::format("%.4g", percentFlagged) << "%\n";
+
out << " Number of unique flags: "
<< flagHistogram.size() << '\n';
-
+
double avgDiagsPerFlag = (double) Flagged.size() / flagHistogram.size();
out << " Average number of diagnostics per flag: "
<< llvm::format("%.4g", avgDiagsPerFlag) << '\n';
@@ -102,7 +96,7 @@
<< flagHistogram["pedantic"].size() << '\n';
out << '\n';
-
+
return 0;
}
diff --git a/tools/diagtool/ShowEnabledWarnings.cpp b/tools/diagtool/ShowEnabledWarnings.cpp
index e6ea786..513abc1 100644
--- a/tools/diagtool/ShowEnabledWarnings.cpp
+++ b/tools/diagtool/ShowEnabledWarnings.cpp
@@ -112,17 +112,14 @@
// which ones are turned on.
// FIXME: It would be very nice to print which flags are turning on which
// diagnostics, but this can be done with a diff.
- ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
std::vector<PrettyDiag> Active;
- for (ArrayRef<DiagnosticRecord>::iterator I = AllDiagnostics.begin(),
- E = AllDiagnostics.end();
- I != E; ++I) {
- unsigned DiagID = I->DiagID;
-
+ for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) {
+ unsigned DiagID = DR.DiagID;
+
if (DiagnosticIDs::isBuiltinNote(DiagID))
continue;
-
+
if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID))
continue;
@@ -132,17 +129,16 @@
continue;
StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID);
- Active.push_back(PrettyDiag(I->getName(), WarningOpt, DiagLevel));
+ Active.push_back(PrettyDiag(DR.getName(), WarningOpt, DiagLevel));
}
// Print them all out.
- for (std::vector<PrettyDiag>::const_iterator I = Active.begin(),
- E = Active.end(); I != E; ++I) {
+ for (const PrettyDiag &PD : Active) {
if (ShouldShowLevels)
- Out << getCharForLevel(I->Level) << " ";
- Out << I->Name;
- if (!I->Flag.empty())
- Out << " [-W" << I->Flag << "]";
+ Out << getCharForLevel(PD.Level) << " ";
+ Out << PD.Name;
+ if (!PD.Flag.empty())
+ Out << " [-W" << PD.Flag << "]";
Out << '\n';
}
diff --git a/tools/diagtool/TreeView.cpp b/tools/diagtool/TreeView.cpp
index 07af944..b4846b5 100644
--- a/tools/diagtool/TreeView.cpp
+++ b/tools/diagtool/TreeView.cpp
@@ -32,10 +32,10 @@
public:
llvm::raw_ostream &out;
const bool ShowColors;
- bool FlagsOnly;
+ bool Internal;
TreePrinter(llvm::raw_ostream &out)
- : out(out), ShowColors(hasColors(out)), FlagsOnly(false) {}
+ : out(out), ShowColors(hasColors(out)), Internal(false) {}
void setColor(llvm::raw_ostream::Colors Color) {
if (ShowColors)
@@ -54,28 +54,42 @@
return Diags.isIgnored(DiagID, SourceLocation());
}
+ static bool enabledByDefault(const GroupRecord &Group) {
+ for (const DiagnosticRecord &DR : Group.diagnostics()) {
+ if (isIgnored(DR.DiagID))
+ return false;
+ }
+
+ for (const GroupRecord &GR : Group.subgroups()) {
+ if (!enabledByDefault(GR))
+ return false;
+ }
+
+ return true;
+ }
+
void printGroup(const GroupRecord &Group, unsigned Indent = 0) {
out.indent(Indent * 2);
- setColor(llvm::raw_ostream::YELLOW);
+ if (enabledByDefault(Group))
+ setColor(llvm::raw_ostream::GREEN);
+ else
+ setColor(llvm::raw_ostream::YELLOW);
+
out << "-W" << Group.getName() << "\n";
resetColor();
++Indent;
- for (GroupRecord::subgroup_iterator I = Group.subgroup_begin(),
- E = Group.subgroup_end();
- I != E; ++I) {
- printGroup(*I, Indent);
+ for (const GroupRecord &GR : Group.subgroups()) {
+ printGroup(GR, Indent);
}
- if (!FlagsOnly) {
- for (GroupRecord::diagnostics_iterator I = Group.diagnostics_begin(),
- E = Group.diagnostics_end();
- I != E; ++I) {
- if (ShowColors && !isIgnored(I->DiagID))
+ if (Internal) {
+ for (const DiagnosticRecord &DR : Group.diagnostics()) {
+ if (ShowColors && !isIgnored(DR.DiagID))
setColor(llvm::raw_ostream::GREEN);
out.indent(Indent * 2);
- out << I->getName();
+ out << DR.getName();
resetColor();
out << "\n";
}
@@ -107,12 +121,9 @@
ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups();
llvm::DenseSet<unsigned> NonRootGroupIDs;
- for (ArrayRef<GroupRecord>::iterator I = AllGroups.begin(),
- E = AllGroups.end();
- I != E; ++I) {
- for (GroupRecord::subgroup_iterator SI = I->subgroup_begin(),
- SE = I->subgroup_end();
- SI != SE; ++SI) {
+ for (const GroupRecord &GR : AllGroups) {
+ for (auto SI = GR.subgroup_begin(), SE = GR.subgroup_end(); SI != SE;
+ ++SI) {
NonRootGroupIDs.insert((unsigned)SI.getID());
}
}
@@ -139,16 +150,16 @@
};
static void printUsage() {
- llvm::errs() << "Usage: diagtool tree [--flags-only] [<diagnostic-group>]\n";
+ llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n";
}
int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
// First check our one flag (--flags-only).
- bool FlagsOnly = false;
+ bool Internal = false;
if (argc > 0) {
StringRef FirstArg(*argv);
- if (FirstArg.equals("--flags-only")) {
- FlagsOnly = true;
+ if (FirstArg.equals("--internal")) {
+ Internal = true;
--argc;
++argv;
}
@@ -175,7 +186,7 @@
}
TreePrinter TP(out);
- TP.FlagsOnly = FlagsOnly;
+ TP.Internal = Internal;
TP.showKey();
return ShowAll ? TP.showAll() : TP.showGroup(RootGroup);
}
diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt
index 901b6d6..0e77fc2 100644
--- a/tools/driver/CMakeLists.txt
+++ b/tools/driver/CMakeLists.txt
@@ -32,6 +32,7 @@
driver.cpp
cc1_main.cpp
cc1as_main.cpp
+ apinotes_main.cpp
DEPENDS
${tablegen_deps}
@@ -39,6 +40,7 @@
target_link_libraries(clang
clangBasic
+ clangAPINotes
clangCodeGen
clangDriver
clangFrontend
diff --git a/tools/driver/apinotes_main.cpp b/tools/driver/apinotes_main.cpp
new file mode 100644
index 0000000..930dc39
--- /dev/null
+++ b/tools/driver/apinotes_main.cpp
@@ -0,0 +1,154 @@
+//===-- api_notes.cpp - API Notes Driver ----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides conversion between the YAML (source) and binary forms
+/// of API notes.
+///
+//===----------------------------------------------------------------------===//
+#include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/Triple.h"
+
+using namespace llvm;
+namespace api_notes = clang::api_notes;
+
+int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0,
+ void *MainAddr) {
+
+ // Mark all our options with this category, everything else (except for
+ // -version and -help) will be hidden.
+ static cl::OptionCategory APINotesCategory("API Notes options");
+
+ static cl::opt<api_notes::ActionType>
+ Action(cl::desc("Mode:"), cl::init(api_notes::ActionType::None),
+ cl::values(
+ clEnumValN(api_notes::ActionType::YAMLToBinary,
+ "yaml-to-binary",
+ "Convert YAML to binary format"),
+ clEnumValN(api_notes::ActionType::BinaryToYAML,
+ "binary-to-yaml",
+ "Convert binary format to YAML"),
+ clEnumValN(api_notes::ActionType::Dump,
+ "dump",
+ "Parse and dump the output")),
+ cl::cat(APINotesCategory));
+
+ static cl::opt<std::string>
+ InputFilename(cl::Positional, cl::desc("<input file>"),
+ cl::Required, cl::cat(APINotesCategory));
+
+ static cl::opt<std::string>
+ Target("target", cl::desc("Generate binary format for the given target"),
+ cl::cat(APINotesCategory));
+
+ static cl::opt<std::string>
+ OutputFilename("o", cl::desc("Output file name"), cl::cat(APINotesCategory));
+
+ cl::HideUnrelatedOptions(APINotesCategory);
+
+ SmallVector<const char *, 4> Args;
+ Args.push_back(Argv0);
+ Args.append(Argv.begin(), Argv.end());
+ cl::ParseCommandLineOptions(Args.size(),
+ Args.data(),
+ "Clang API Notes Tool\n");
+
+ if (Action == clang::api_notes::ActionType::None) {
+ errs() << "action required\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ auto fileBufOrErr = MemoryBuffer::getFile(InputFilename);
+ if (std::error_code EC = fileBufOrErr.getError()) {
+ llvm::errs() << "\n Could not open input file: " + EC.message() << '\n';
+ return true;
+ }
+ StringRef input = fileBufOrErr.get()->getBuffer();
+
+ switch (Action) {
+ case api_notes::ActionType::None:
+ llvm_unreachable("handled above");
+
+ case api_notes::ActionType::YAMLToBinary: {
+ if (OutputFilename.empty()) {
+ errs() << "output file is required\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ api_notes::OSType targetOS = api_notes::OSType::Absent;
+ // TODO: Check that we've specified the target.
+ if (!Target.empty()) {
+ llvm::Triple target(llvm::Triple::normalize(Target));
+ switch (target.getOS()) {
+ case llvm::Triple::Darwin:
+ case llvm::Triple::MacOSX:
+ targetOS = api_notes::OSType::OSX;
+ break;
+ case llvm::Triple::IOS:
+ targetOS = api_notes::OSType::IOS;
+ break;
+ case llvm::Triple::WatchOS:
+ targetOS = api_notes::OSType::WatchOS;
+ break;
+ case llvm::Triple::TvOS:
+ targetOS = api_notes::OSType::TvOS;
+ break;
+ default:
+ errs() << "target is not supported\n";
+ return 1;
+ }
+ }
+ std::error_code EC;
+ llvm::raw_fd_ostream os(OutputFilename, EC,
+ llvm::sys::fs::OpenFlags::F_None);
+
+ if (api_notes::compileAPINotes(input, /*sourceFile=*/nullptr, os, targetOS))
+ return 1;
+
+ os.flush();
+
+ return os.has_error();
+ }
+
+ case api_notes::ActionType::BinaryToYAML: {
+ if (OutputFilename.empty()) {
+ errs() << "output file required\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ std::error_code EC;
+ llvm::raw_fd_ostream os(OutputFilename, EC,
+ llvm::sys::fs::OpenFlags::F_None);
+
+ if (api_notes::decompileAPINotes(std::move(fileBufOrErr.get()), os))
+ return 1;
+
+ os.flush();
+
+ return os.has_error();
+ }
+
+ case api_notes::ActionType::Dump:
+ return api_notes::parseAndDumpAPINotes(input);
+ }
+
+ return 1;
+}
+
diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp
index 2fc2b50..4ede950 100644
--- a/tools/driver/cc1as_main.cpp
+++ b/tools/driver/cc1as_main.cpp
@@ -356,7 +356,7 @@
PIC = false;
}
- MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, CodeModel::Default, Ctx);
+ MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx);
if (Opts.SaveTemporaryLabels)
Ctx.setAllowTemporaryLabels(false);
if (Opts.GenDwarfForAssembly)
diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp
index 9f37c42..fd85a56 100644
--- a/tools/driver/driver.cpp
+++ b/tools/driver/driver.cpp
@@ -205,6 +205,8 @@
void *MainAddr);
extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
void *MainAddr);
+extern int cc1apinotes_main(ArrayRef<const char *> Argv, const char *Argv0,
+ void *MainAddr);
static void insertTargetAndModeArgs(StringRef Target, StringRef Mode,
SmallVectorImpl<const char *> &ArgVector,
@@ -306,6 +308,8 @@
return cc1_main(argv.slice(2), argv[0], GetExecutablePathVP);
if (Tool == "as")
return cc1as_main(argv.slice(2), argv[0], GetExecutablePathVP);
+ if (Tool == "apinotes")
+ return cc1apinotes_main(argv.slice(2), argv[0], GetExecutablePathVP);
// Reject unknown tools.
llvm::errs() << "error: unknown integrated tool '" << Tool << "'\n";
diff --git a/tools/libclang/CIndexDiagnostic.cpp b/tools/libclang/CIndexDiagnostic.cpp
index 4e47b25..9ac1819 100644
--- a/tools/libclang/CIndexDiagnostic.cpp
+++ b/tools/libclang/CIndexDiagnostic.cpp
@@ -146,7 +146,20 @@
CXDiagnosticSetImpl *CurrentSet;
CXDiagnosticSetImpl *MainSet;
-};
+};
+
+class CXStoredDiagnosticSet : public CXDiagnosticSetImpl {
+ llvm::SmallVector<StoredDiagnostic, 2> Diags;
+
+public:
+ CXStoredDiagnosticSet(ArrayRef<StoredDiagnostic> Diags,
+ const LangOptions &LangOpts)
+ : CXDiagnosticSetImpl(/*isManaged=*/true),
+ Diags(Diags.begin(), Diags.end()) {
+ for (const auto &Diag : this->Diags)
+ appendDiagnostic(llvm::make_unique<CXStoredDiagnostic>(Diag, LangOpts));
+ }
+};
}
CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU,
@@ -196,6 +209,11 @@
return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics);
}
+CXDiagnosticSetImpl *cxdiag::createStoredDiags(ArrayRef<StoredDiagnostic> Diags,
+ const LangOptions &LangOpts) {
+ return new CXStoredDiagnosticSet(Diags, LangOpts);
+}
+
//-----------------------------------------------------------------------------
// C Interface Routines
//-----------------------------------------------------------------------------
diff --git a/tools/libclang/CIndexDiagnostic.h b/tools/libclang/CIndexDiagnostic.h
index 9f40698..468f9bc 100644
--- a/tools/libclang/CIndexDiagnostic.h
+++ b/tools/libclang/CIndexDiagnostic.h
@@ -14,6 +14,7 @@
#define LLVM_CLANG_TOOLS_LIBCLANG_CINDEXDIAGNOSTIC_H
#include "clang-c/Index.h"
+#include "clang/Basic/LLVM.h"
#include <memory>
#include <vector>
#include <assert.h>
@@ -158,6 +159,9 @@
namespace cxdiag {
CXDiagnosticSetImpl *lazyCreateDiags(CXTranslationUnit TU,
bool checkIfChanged = false);
+
+CXDiagnosticSetImpl *createStoredDiags(ArrayRef<StoredDiagnostic> Diags,
+ const LangOptions &LangOpts);
} // end namespace cxdiag
} // end namespace clang
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index 2dd6703..dbad06a 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -9,6 +9,7 @@
CIndexInclusionStack.cpp
CIndexUSRs.cpp
CIndexer.cpp
+ CRefactor.cpp
CXComment.cpp
CXCursor.cpp
CXIndexDataConsumer.cpp
@@ -35,6 +36,7 @@
set(LIBS
clangAST
+ clangAPINotes
clangBasic
clangFrontend
clangIndex
diff --git a/tools/libclang/CRefactor.cpp b/tools/libclang/CRefactor.cpp
new file mode 100644
index 0000000..45f21ba
--- /dev/null
+++ b/tools/libclang/CRefactor.cpp
@@ -0,0 +1,1948 @@
+//===- CRefactor.cpp - Refactoring API hooks ------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the Clang-C refactoring library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIndexDiagnostic.h"
+#include "CIndexer.h"
+#include "CLog.h"
+#include "CXCursor.h"
+#include "CXSourceLocation.h"
+#include "CXString.h"
+#include "CXTranslationUnit.h"
+#include "clang-c/Refactor.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/Basic/DiagnosticCategories.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Tooling/Refactor/IndexerQuery.h"
+#include "clang/Tooling/Refactor/RefactoringActionFinder.h"
+#include "clang/Tooling/Refactor/RefactoringActions.h"
+#include "clang/Tooling/Refactor/RefactoringOperation.h"
+#include "clang/Tooling/Refactor/RefactoringOptions.h"
+#include "clang/Tooling/Refactor/RenameIndexedFile.h"
+#include "clang/Tooling/Refactor/RenamingOperation.h"
+#include "clang/Tooling/Refactor/SymbolOccurrenceFinder.h"
+#include "clang/Tooling/Refactor/USRFinder.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include <set>
+#include <vector>
+
+using namespace clang;
+using namespace clang::tooling;
+
+static RefactoringActionType
+translateRefactoringActionType(CXRefactoringActionType Action) {
+ switch (Action) {
+#define REFACTORING_ACTION(Name, Spelling) \
+ case CXRefactor_##Name: \
+ return RefactoringActionType::Name;
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+ }
+}
+
+static CXRefactoringActionType
+translateRefactoringActionType(RefactoringActionType Action) {
+ switch (Action) {
+#define REFACTORING_ACTION(Name, Spelling) \
+ case RefactoringActionType::Name: \
+ return CXRefactor_##Name;
+#include "clang/Tooling/Refactor/RefactoringActions.def"
+ }
+}
+
+static CXSymbolOccurrenceKind
+translateOccurrenceKind(rename::SymbolOccurrence::OccurrenceKind Kind) {
+ switch (Kind) {
+ case rename::SymbolOccurrence::MatchingSymbol:
+ return CXSymbolOccurrence_MatchingSymbol;
+ case rename::SymbolOccurrence::MatchingSelector:
+ return CXSymbolOccurrence_MatchingSelector;
+ case rename::SymbolOccurrence::MatchingImplicitProperty:
+ return CXSymbolOccurrence_MatchingImplicitProperty;
+ case rename::SymbolOccurrence::MatchingComment:
+ return CXSymbolOccurrence_MatchingCommentString;
+ case rename::SymbolOccurrence::MatchingDocComment:
+ return CXSymbolOccurrence_MatchingDocCommentString;
+ case rename::SymbolOccurrence::MatchingFilename:
+ return CXSymbolOccurrence_MatchingFilename;
+ }
+}
+
+namespace {
+
+// TODO: Remove
+class RenamingResult {
+ struct RenamedNameString {
+ CXString NewString;
+ unsigned OldLength;
+ };
+ typedef SmallVector<RenamedNameString, 4> SymbolNameInfo;
+ std::vector<SymbolNameInfo> NameInfo;
+
+ /// The set of files that have to be modified.
+ llvm::SmallVector<CXString, 2> Filenames;
+ llvm::SpecificBumpPtrAllocator<CXRefactoringReplacement_Old> Replacements;
+ std::vector<std::vector<CXRenamedSymbolOccurrence>> Occurrences;
+
+ void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ CXRefactoringReplacement_Old *OccurrenceReplacements =
+ Replacements.Allocate(RenamedOccurrence.locations().size());
+
+ unsigned I = 0;
+ const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex];
+ if (!RenamedOccurrence.IsMacroExpansion &&
+ RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment &&
+ RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment)
+ assert(RenamedOccurrence.locations().size() == SymbolNameInfo.size());
+ for (const auto &Location : RenamedOccurrence.locations()) {
+ CXSourceRange Range = cxloc::translateSourceRange(
+ SM, LangOpts,
+ CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange(
+ Location, SymbolNameInfo[I].OldLength)));
+ CXFileLocation Begin, End;
+ clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line,
+ &Begin.Column, nullptr);
+ clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line,
+ &End.Column, nullptr);
+
+ OccurrenceReplacements[I] = CXRefactoringReplacement_Old{
+ {Begin, End},
+ RenamedOccurrence.IsMacroExpansion ? cxstring::createNull()
+ : SymbolNameInfo[I].NewString};
+ ++I;
+ }
+
+ Occurrences.back().push_back(CXRenamedSymbolOccurrence{
+ OccurrenceReplacements, I,
+ translateOccurrenceKind(RenamedOccurrence.Kind),
+ RenamedOccurrence.IsMacroExpansion});
+ }
+
+public:
+ RenamingResult(ArrayRef<SymbolName> NewNames,
+ ArrayRef<rename::Symbol> Symbols) {
+ assert(NewNames.size() == Symbols.size());
+ for (size_t I = 0, E = NewNames.size(); I != E; ++I) {
+ const auto &NewName = NewNames[I];
+ const auto &OldName = Symbols[I].Name;
+
+ assert(NewName.size() == OldName.size());
+ SymbolNameInfo Info;
+ for (size_t I = 0, E = NewName.size(); I != E; ++I)
+ Info.push_back(RenamedNameString{cxstring::createDup(NewName[I]),
+ (unsigned)OldName[I].size()});
+ NameInfo.push_back(std::move(Info));
+ }
+ }
+
+ // FIXME: Don't duplicate code, Use just one constructor.
+ RenamingResult(ArrayRef<SymbolName> NewNames, ArrayRef<SymbolName> OldNames) {
+ assert(NewNames.size() == OldNames.size());
+ for (size_t I = 0, E = NewNames.size(); I != E; ++I) {
+ const auto &NewName = NewNames[I];
+ const auto &OldName = OldNames[I];
+
+ assert(NewName.size() == OldName.size());
+ SymbolNameInfo Info;
+ for (size_t I = 0, E = NewName.size(); I != E; ++I)
+ Info.push_back(RenamedNameString{cxstring::createDup(NewName[I]),
+ (unsigned)OldName[I].size()});
+ NameInfo.push_back(std::move(Info));
+ }
+ }
+
+ ~RenamingResult() {
+ for (const auto &SymbolInfo : NameInfo)
+ for (const auto &NameString : SymbolInfo)
+ clang_disposeString(NameString.NewString);
+ for (const auto &Filename : Filenames)
+ clang_disposeString(Filename);
+ }
+
+ void
+ handleTUResults(CXTranslationUnit TU,
+ llvm::MutableArrayRef<rename::SymbolOccurrence> Results) {
+ ASTUnit *Unit = cxtu::getASTUnit(TU);
+ assert(Unit && "Invalid TU");
+ auto &Ctx = Unit->getASTContext();
+
+ // Find the set of files that have to be modified and gather the indices of
+ // the occurrences for each file.
+ const SourceManager &SM = Ctx.getSourceManager();
+ typedef std::set<rename::SymbolOccurrence> OccurrenceSet;
+ llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences;
+ for (auto &Occurrence : Results) {
+ const std::pair<FileID, unsigned> DecomposedLocation =
+ SM.getDecomposedLoc(Occurrence.locations()[0]);
+ const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first);
+ assert(Entry && "Invalid file entry");
+ auto &FileOccurrences =
+ FilenamesToSymbolOccurrences
+ .try_emplace(Entry->getName(), OccurrenceSet())
+ .first->getValue();
+ FileOccurrences.insert(std::move(Occurrence));
+ }
+
+ // Create the filenames
+ for (const auto &FilenameCount : FilenamesToSymbolOccurrences)
+ Filenames.push_back(cxstring::createDup(FilenameCount.getKey()));
+
+ unsigned FileIndex = 0;
+ for (const auto &RenamedOccurrences : FilenamesToSymbolOccurrences) {
+ assert(clang_getCString(Filenames[FileIndex]) ==
+ RenamedOccurrences.getKey() &&
+ "Unstable iteration order");
+ Occurrences.push_back(std::vector<CXRenamedSymbolOccurrence>());
+ for (const auto &Occurrence : RenamedOccurrences.getValue())
+ addOccurrence(Occurrence, SM, Ctx.getLangOpts());
+ ++FileIndex;
+ }
+ }
+
+ void addMainFilename(const SourceManager &SM) {
+ assert(Filenames.empty() && "Main filename should be added only once");
+ Filenames.push_back(cxstring::createDup(
+ SM.getFileEntryForID(SM.getMainFileID())->getName()));
+ Occurrences.push_back(std::vector<CXRenamedSymbolOccurrence>());
+ }
+
+ void
+ handleSingleFileTUResults(const ASTContext &Ctx,
+ ArrayRef<rename::SymbolOccurrence> Occurrences) {
+ addMainFilename(Ctx.getSourceManager());
+ for (const auto &Occurrence : Occurrences)
+ addOccurrence(Occurrence, Ctx.getSourceManager(), Ctx.getLangOpts());
+ }
+
+ void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Filenames.empty()) {
+ addMainFilename(SM);
+ }
+ addOccurrence(Occurrence, SM, LangOpts);
+ }
+
+ ArrayRef<CXRenamedSymbolOccurrence> getOccurrences(unsigned FileIndex) const {
+ return Occurrences[FileIndex];
+ }
+
+ ArrayRef<CXString> getFilenames() const { return Filenames; }
+};
+
+class SymbolOccurrencesResult {
+ struct SymbolNamePiece {
+ unsigned OldLength;
+ };
+ typedef SmallVector<SymbolNamePiece, 4> SymbolNameInfo;
+ std::vector<SymbolNameInfo> NameInfo;
+
+ /// The set of files that have to be modified.
+ llvm::SmallVector<CXString, 2> Filenames;
+ llvm::SpecificBumpPtrAllocator<CXFileRange> Ranges;
+ std::vector<std::vector<CXSymbolOccurrence>> SymbolOccurrences;
+
+ void addOccurrence(const rename::SymbolOccurrence &RenamedOccurrence,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ ArrayRef<SourceLocation> Locations = RenamedOccurrence.locations();
+ CXFileRange *OccurrenceRanges = Ranges.Allocate(Locations.size());
+
+ unsigned I = 0;
+ const auto &SymbolNameInfo = NameInfo[RenamedOccurrence.SymbolIndex];
+ if (!RenamedOccurrence.IsMacroExpansion &&
+ RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingComment &&
+ RenamedOccurrence.Kind != rename::SymbolOccurrence::MatchingDocComment)
+ assert(Locations.size() == SymbolNameInfo.size());
+ for (const auto &Location : Locations) {
+ CXSourceRange Range = cxloc::translateSourceRange(
+ SM, LangOpts,
+ CharSourceRange::getCharRange(RenamedOccurrence.getLocationRange(
+ Location, SymbolNameInfo[I].OldLength)));
+ CXFileLocation Begin, End;
+ clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line,
+ &Begin.Column, nullptr);
+ clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line,
+ &End.Column, nullptr);
+ OccurrenceRanges[I] = CXFileRange{Begin, End};
+ ++I;
+ }
+
+ SymbolOccurrences.back().push_back(CXSymbolOccurrence{
+ OccurrenceRanges, /*NumNamePieces=*/I,
+ translateOccurrenceKind(RenamedOccurrence.Kind),
+ RenamedOccurrence.IsMacroExpansion, RenamedOccurrence.SymbolIndex});
+ }
+
+public:
+ SymbolOccurrencesResult(ArrayRef<rename::Symbol> Symbols) {
+ for (const auto &Symbol : Symbols) {
+ const SymbolName &Name = Symbol.Name;
+ SymbolNameInfo Info;
+ for (size_t I = 0, E = Name.size(); I != E; ++I)
+ Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()});
+ NameInfo.push_back(std::move(Info));
+ }
+ }
+
+ SymbolOccurrencesResult(ArrayRef<SymbolName> Names) {
+ for (const SymbolName &Name : Names) {
+ SymbolNameInfo Info;
+ for (size_t I = 0, E = Name.size(); I != E; ++I)
+ Info.push_back(SymbolNamePiece{(unsigned)Name[I].size()});
+ NameInfo.push_back(std::move(Info));
+ }
+ }
+
+ ~SymbolOccurrencesResult() {
+ for (const auto &Filename : Filenames)
+ clang_disposeString(Filename);
+ }
+
+ void
+ handleTUResults(CXTranslationUnit TU,
+ llvm::MutableArrayRef<rename::SymbolOccurrence> Results) {
+ ASTUnit *Unit = cxtu::getASTUnit(TU);
+ assert(Unit && "Invalid TU");
+ auto &Ctx = Unit->getASTContext();
+
+ // Find the set of files that have to be modified and gather the indices of
+ // the occurrences for each file.
+ const SourceManager &SM = Ctx.getSourceManager();
+ typedef std::set<rename::SymbolOccurrence> OccurrenceSet;
+ llvm::StringMap<OccurrenceSet> FilenamesToSymbolOccurrences;
+ for (auto &Occurrence : Results) {
+ const std::pair<FileID, unsigned> DecomposedLocation =
+ SM.getDecomposedLoc(Occurrence.locations()[0]);
+ const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first);
+ assert(Entry && "Invalid file entry");
+ auto &FileOccurrences =
+ FilenamesToSymbolOccurrences
+ .try_emplace(Entry->getName(), OccurrenceSet())
+ .first->getValue();
+ FileOccurrences.insert(std::move(Occurrence));
+ }
+
+ // Create the filenames
+ for (const auto &FilenameCount : FilenamesToSymbolOccurrences)
+ Filenames.push_back(cxstring::createDup(FilenameCount.getKey()));
+
+ unsigned FileIndex = 0;
+ for (const auto &RenamedOccurrences : FilenamesToSymbolOccurrences) {
+ assert(clang_getCString(Filenames[FileIndex]) ==
+ RenamedOccurrences.getKey() &&
+ "Unstable iteration order");
+ SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>());
+ for (const auto &Occurrence : RenamedOccurrences.getValue())
+ addOccurrence(Occurrence, SM, Ctx.getLangOpts());
+ ++FileIndex;
+ }
+ }
+
+ void addMainFilename(const SourceManager &SM) {
+ assert(Filenames.empty() && "Main filename should be added only once");
+ Filenames.push_back(cxstring::createDup(
+ SM.getFileEntryForID(SM.getMainFileID())->getName()));
+ SymbolOccurrences.push_back(std::vector<CXSymbolOccurrence>());
+ }
+
+ void handleIndexedFileOccurrence(const rename::SymbolOccurrence &Occurrence,
+ const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Filenames.empty()) {
+ addMainFilename(SM);
+ }
+ addOccurrence(Occurrence, SM, LangOpts);
+ }
+
+ ArrayRef<CXSymbolOccurrence> getOccurrences(unsigned FileIndex) const {
+ return SymbolOccurrences[FileIndex];
+ }
+
+ ArrayRef<CXString> getFilenames() const { return Filenames; }
+};
+
+class RenamingAction {
+public:
+ LangOptions LangOpts;
+ IdentifierTable IDs;
+ // TODO: Remove
+ SmallVector<SymbolName, 4> NewNames;
+ SymbolOperation Operation;
+
+ RenamingAction(const LangOptions &LangOpts, SymbolOperation Operation)
+ : LangOpts(LangOpts), IDs(LangOpts), Operation(std::move(Operation)) {}
+
+ /// \brief Sets the new renaming name and returns CXError_Success on success.
+ // TODO: Remove
+ CXErrorCode setNewName(StringRef Name) {
+ SymbolName NewSymbolName(Name, LangOpts);
+ if (NewSymbolName.size() != Operation.symbols()[0].Name.size())
+ return CXError_RefactoringNameSizeMismatch;
+ if (!rename::isNewNameValid(NewSymbolName, Operation, IDs, LangOpts))
+ return CXError_RefactoringNameInvalid;
+ rename::determineNewNames(std::move(NewSymbolName), Operation, NewNames,
+ LangOpts);
+ return CXError_Success;
+ }
+
+ // TODO: Remove
+ CXString usrForSymbolAt(unsigned Index) {
+ llvm::SmallVector<char, 128> Buff;
+ if (index::generateUSRForDecl(Operation.symbols()[Index].FoundDecl, Buff))
+ return cxstring::createNull();
+ return cxstring::createDup(StringRef(Buff.begin(), Buff.size()));
+ }
+
+ // TODO: Remove
+ CXString getUSRThatRequiresImplementationTU() {
+ llvm::SmallVector<char, 128> Buff;
+ if (!Operation.requiresImplementationTU() ||
+ index::generateUSRForDecl(Operation.declThatRequiresImplementationTU(),
+ Buff))
+ return cxstring::createNull();
+ return cxstring::createDup(StringRef(Buff.begin(), Buff.size()));
+ }
+
+ // TODO: Remove
+ RenamingResult *handlePrimaryTU(CXTranslationUnit TU, ASTUnit &Unit) {
+ // Perform the renaming.
+ if (NewNames.empty())
+ return nullptr;
+
+ const ASTContext &Context = Unit.getASTContext();
+ auto Occurrences = rename::findSymbolOccurrences(
+ Operation, Context.getTranslationUnitDecl());
+ auto *Result = new RenamingResult(NewNames, Operation.symbols());
+ Result->handleTUResults(TU, Occurrences);
+ return Result;
+ }
+
+ SymbolOccurrencesResult *findSymbolsInInitiationTU(CXTranslationUnit TU,
+ ASTUnit &Unit) {
+ const ASTContext &Context = Unit.getASTContext();
+ auto Occurrences = rename::findSymbolOccurrences(
+ Operation, Context.getTranslationUnitDecl());
+ auto *Result = new SymbolOccurrencesResult(Operation.symbols());
+ Result->handleTUResults(TU, Occurrences);
+ return Result;
+ }
+};
+
+static bool isObjCSelectorKind(CXCursorKind Kind) {
+ return Kind == CXCursor_ObjCInstanceMethodDecl ||
+ Kind == CXCursor_ObjCClassMethodDecl ||
+ Kind == CXCursor_ObjCMessageExpr;
+}
+
+// TODO: Remove
+static bool isObjCSelector(const CXRenamedIndexedSymbol &Symbol) {
+ if (isObjCSelectorKind(Symbol.CursorKind))
+ return true;
+ for (const auto &Occurrence : llvm::makeArrayRef(
+ Symbol.IndexedLocations, Symbol.IndexedLocationCount)) {
+ if (isObjCSelectorKind(Occurrence.CursorKind))
+ return true;
+ }
+ return false;
+}
+
+static bool isObjCSelector(const CXIndexedSymbol &Symbol) {
+ if (isObjCSelectorKind(Symbol.CursorKind))
+ return true;
+ for (const auto &Occurrence : llvm::makeArrayRef(
+ Symbol.IndexedLocations, Symbol.IndexedLocationCount)) {
+ if (isObjCSelectorKind(Occurrence.CursorKind))
+ return true;
+ }
+ return false;
+}
+
+// New names are initialized and verified after the LangOptions are created.
+CXErrorCode computeNewNames(ArrayRef<CXRenamedIndexedSymbol> Symbols,
+ ArrayRef<SymbolName> SymbolNames,
+ const LangOptions &LangOpts,
+ SmallVectorImpl<SymbolName> &NewNames) {
+ IdentifierTable IDs(LangOpts);
+ for (const auto &Symbol : Symbols) {
+ SymbolName NewSymbolName(Symbol.NewName, LangOpts);
+ if (NewSymbolName.size() != SymbolNames[0].size())
+ return CXError_RefactoringNameSizeMismatch;
+ if (!rename::isNewNameValid(NewSymbolName, isObjCSelector(Symbol), IDs,
+ LangOpts))
+ return CXError_RefactoringNameInvalid;
+ NewNames.push_back(std::move(NewSymbolName));
+ }
+ return CXError_Success;
+}
+
+static rename::IndexedOccurrence::OccurrenceKind
+translateIndexedOccurrenceKind(CXCursorKind Kind) {
+ switch (Kind) {
+ case CXCursor_ObjCMessageExpr:
+ return rename::IndexedOccurrence::IndexedObjCMessageSend;
+ case CXCursor_InclusionDirective:
+ return rename::IndexedOccurrence::InclusionDirective;
+ default:
+ return rename::IndexedOccurrence::IndexedSymbol;
+ }
+}
+
+// TODO: Remove
+CXErrorCode performIndexedFileRename(
+ ArrayRef<CXRenamedIndexedSymbol> Symbols, StringRef Filename,
+ ArrayRef<const char *> Arguments, CXIndex CIdx,
+ MutableArrayRef<CXUnsavedFile> UnsavedFiles,
+ const RefactoringOptionSet *Options, CXRenamingResult &Result) {
+ Result = nullptr;
+
+ // Adjust the given command line arguments to ensure that any positional
+ // arguments in them are stripped.
+ std::vector<const char *> ClangToolArguments;
+ ClangToolArguments.push_back("--");
+ for (const auto &Arg : Arguments) {
+ // Remove the '-gmodules' option, as the -fmodules-format=obj isn't
+ // supported without the linked object reader.
+ if (StringRef(Arg) == "-gmodules")
+ continue;
+ ClangToolArguments.push_back(Arg);
+ }
+ int Argc = ClangToolArguments.size();
+ std::string ErrorMessage;
+ std::unique_ptr<CompilationDatabase> Compilations =
+ FixedCompilationDatabase::loadFromCommandLine(
+ Argc, ClangToolArguments.data(), ErrorMessage);
+ if (!Compilations) {
+ llvm::errs() << "CRefactor: Failed to load command line: " << ErrorMessage
+ << "\n";
+ return CXError_Failure;
+ }
+
+ // Translate the symbols.
+ llvm::SmallVector<rename::IndexedSymbol, 4> IndexedSymbols;
+ for (const auto &Symbol : Symbols) {
+
+ // Parse the symbol name.
+ bool IsObjCSelector = false;
+ // Selectors have to be parsed.
+ if (isObjCSelector(Symbol))
+ IsObjCSelector = true;
+ // Ensure that we don't get selectors with incorrect symbol kind.
+ else if (StringRef(Symbol.Name).contains(':'))
+ return CXError_InvalidArguments;
+
+ std::vector<rename::IndexedOccurrence> IndexedOccurrences;
+ for (const auto &Loc : llvm::makeArrayRef(Symbol.IndexedLocations,
+ Symbol.IndexedLocationCount)) {
+ rename::IndexedOccurrence Result;
+ Result.Line = Loc.Location.Line;
+ Result.Column = Loc.Location.Column;
+ Result.Kind = translateIndexedOccurrenceKind(Loc.CursorKind);
+ IndexedOccurrences.push_back(Result);
+ }
+
+ IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector),
+ IndexedOccurrences,
+ /*IsObjCSelector=*/IsObjCSelector);
+ }
+
+ class ToolRunner final : public FrontendActionFactory,
+ public rename::IndexedFileOccurrenceConsumer {
+ ArrayRef<CXRenamedIndexedSymbol> Symbols;
+ ArrayRef<rename::IndexedSymbol> IndexedSymbols;
+ const RefactoringOptionSet *Options;
+
+ public:
+ RenamingResult *Result;
+ CXErrorCode Err;
+
+ ToolRunner(ArrayRef<CXRenamedIndexedSymbol> Symbols,
+ ArrayRef<rename::IndexedSymbol> IndexedSymbols,
+ const RefactoringOptionSet *Options)
+ : Symbols(Symbols), IndexedSymbols(IndexedSymbols), Options(Options),
+ Result(nullptr), Err(CXError_Success) {}
+
+ clang::FrontendAction *create() override {
+ return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this,
+ Options);
+ }
+
+ void handleOccurrence(const rename::SymbolOccurrence &Occurrence,
+ SourceManager &SM,
+ const LangOptions &LangOpts) override {
+ if (Err != CXError_Success)
+ return;
+ if (!Result) {
+ SmallVector<SymbolName, 4> SymbolNames;
+ for (const auto &Symbol : IndexedSymbols)
+ SymbolNames.push_back(Symbol.Name);
+ SmallVector<SymbolName, 4> NewNames;
+ Err = computeNewNames(Symbols, SymbolNames, LangOpts, NewNames);
+ if (Err != CXError_Success)
+ return;
+ Result = new RenamingResult(NewNames, SymbolNames);
+ }
+ Result->handleIndexedFileOccurrence(Occurrence, SM, LangOpts);
+ }
+ };
+
+ auto Runner = llvm::make_unique<ToolRunner>(Symbols, IndexedSymbols, Options);
+
+ // Run a clang tool on the input file.
+ std::string Name = Filename.str();
+ ClangTool Tool(*Compilations, Name);
+ Tool.run(Runner.get());
+ if (Runner->Err != CXError_Success)
+ return Runner->Err;
+ Result = Runner->Result;
+ return CXError_Success;
+}
+
+CXErrorCode performIndexedSymbolSearch(
+ ArrayRef<CXIndexedSymbol> Symbols, StringRef Filename,
+ ArrayRef<const char *> Arguments, CXIndex CIdx,
+ MutableArrayRef<CXUnsavedFile> UnsavedFiles,
+ const RefactoringOptionSet *Options, CXSymbolOccurrencesResult &Result) {
+ Result = nullptr;
+
+ // Adjust the given command line arguments to ensure that any positional
+ // arguments in them are stripped.
+ std::vector<const char *> ClangToolArguments;
+ ClangToolArguments.push_back("--");
+ for (const auto &Arg : Arguments) {
+ // Remove the '-gmodules' option, as the -fmodules-format=obj isn't
+ // supported without the linked object reader.
+ if (StringRef(Arg) == "-gmodules")
+ continue;
+ ClangToolArguments.push_back(Arg);
+ }
+ int Argc = ClangToolArguments.size();
+ std::string ErrorMessage;
+ std::unique_ptr<CompilationDatabase> Compilations =
+ FixedCompilationDatabase::loadFromCommandLine(
+ Argc, ClangToolArguments.data(), ErrorMessage);
+ if (!Compilations) {
+ llvm::errs() << "CRefactor: Failed to load command line: " << ErrorMessage
+ << "\n";
+ return CXError_Failure;
+ }
+
+ // Translate the symbols.
+ llvm::SmallVector<rename::IndexedSymbol, 4> IndexedSymbols;
+ for (const auto &Symbol : Symbols) {
+
+ // Parse the symbol name.
+ bool IsObjCSelector = false;
+ // Selectors have to be parsed.
+ if (isObjCSelector(Symbol))
+ IsObjCSelector = true;
+ // Ensure that we don't get selectors with incorrect symbol kind.
+ else if (StringRef(Symbol.Name).contains(':'))
+ return CXError_InvalidArguments;
+
+ std::vector<rename::IndexedOccurrence> IndexedOccurrences;
+ for (const auto &Loc : llvm::makeArrayRef(Symbol.IndexedLocations,
+ Symbol.IndexedLocationCount)) {
+ rename::IndexedOccurrence Result;
+ Result.Line = Loc.Location.Line;
+ Result.Column = Loc.Location.Column;
+ Result.Kind = translateIndexedOccurrenceKind(Loc.CursorKind);
+ IndexedOccurrences.push_back(Result);
+ }
+
+ IndexedSymbols.emplace_back(SymbolName(Symbol.Name, IsObjCSelector),
+ IndexedOccurrences,
+ /*IsObjCSelector=*/IsObjCSelector);
+ }
+
+ class ToolRunner final : public FrontendActionFactory,
+ public rename::IndexedFileOccurrenceConsumer {
+ ArrayRef<rename::IndexedSymbol> IndexedSymbols;
+ const RefactoringOptionSet *Options;
+
+ public:
+ SymbolOccurrencesResult *Result;
+
+ ToolRunner(ArrayRef<rename::IndexedSymbol> IndexedSymbols,
+ const RefactoringOptionSet *Options)
+ : IndexedSymbols(IndexedSymbols), Options(Options), Result(nullptr) {}
+
+ clang::FrontendAction *create() override {
+ return new rename::IndexedFileOccurrenceProducer(IndexedSymbols, *this,
+ Options);
+ }
+
+ void handleOccurrence(const rename::SymbolOccurrence &Occurrence,
+ SourceManager &SM,
+ const LangOptions &LangOpts) override {
+ if (!Result) {
+ SmallVector<SymbolName, 4> SymbolNames;
+ for (const auto &Symbol : IndexedSymbols)
+ SymbolNames.push_back(Symbol.Name);
+ Result = new SymbolOccurrencesResult(SymbolNames);
+ }
+ Result->handleIndexedFileOccurrence(Occurrence, SM, LangOpts);
+ }
+ };
+
+ auto Runner = llvm::make_unique<ToolRunner>(IndexedSymbols, Options);
+
+ // Run a clang tool on the input file.
+ std::string Name = Filename.str();
+ ClangTool Tool(*Compilations, Name);
+ for (const CXUnsavedFile &File : UnsavedFiles)
+ Tool.mapVirtualFile(File.Filename, StringRef(File.Contents, File.Length));
+ if (Tool.run(Runner.get()))
+ return CXError_Failure;
+ Result = Runner->Result;
+ return CXError_Success;
+}
+
+class RefactoringAction {
+ std::unique_ptr<RefactoringOperation> Operation;
+ std::unique_ptr<RenamingAction> Rename;
+
+ SmallVector<CXRefactoringCandidate, 2> RefactoringCandidates;
+ CXRefactoringCandidateSet CandidateSet = {nullptr, 0};
+ bool HasCandidateSet = false;
+
+public:
+ CXRefactoringActionType Type;
+ unsigned SelectedCandidate = 0;
+ CXTranslationUnit InitiationTU;
+ // TODO: Remove (no longer needed due to continuations).
+ CXTranslationUnit ImplementationTU;
+
+ RefactoringAction(std::unique_ptr<RefactoringOperation> Operation,
+ CXRefactoringActionType Type,
+ CXTranslationUnit InitiationTU)
+ : Operation(std::move(Operation)), Type(Type), InitiationTU(InitiationTU),
+ ImplementationTU(nullptr) {}
+
+ RefactoringAction(std::unique_ptr<RenamingAction> Rename,
+ CXTranslationUnit InitiationTU)
+ : Rename(std::move(Rename)),
+ Type(this->Rename->Operation.isLocal() ? CXRefactor_Rename_Local
+ : CXRefactor_Rename),
+ InitiationTU(InitiationTU), ImplementationTU(nullptr) {}
+
+ ~RefactoringAction() {
+ for (const auto &Candidate : RefactoringCandidates)
+ clang_disposeString(Candidate.Description);
+ }
+
+ RefactoringOperation *getOperation() const { return Operation.get(); }
+
+ RenamingAction *getRenamingAction() const { return Rename.get(); }
+
+ CXRefactoringCandidateSet getRefactoringCandidates() {
+ if (HasCandidateSet)
+ return CandidateSet;
+ HasCandidateSet = true;
+ RefactoringOperation *Operation = getOperation();
+ if (!Operation)
+ return CandidateSet;
+ auto Candidates = Operation->getRefactoringCandidates();
+ if (Candidates.empty())
+ return CandidateSet;
+ for (const auto &Candidate : Candidates)
+ RefactoringCandidates.push_back({cxstring::createDup(Candidate)});
+ CandidateSet = {RefactoringCandidates.data(),
+ (unsigned)RefactoringCandidates.size()};
+ return CandidateSet;
+ }
+
+ CXErrorCode selectCandidate(unsigned Index) {
+ RefactoringOperation *Operation = getOperation();
+ if (!Operation)
+ return CXError_InvalidArguments;
+ if (Index != 0 && Index >= getRefactoringCandidates().NumCandidates)
+ return CXError_InvalidArguments;
+ SelectedCandidate = Index;
+ return CXError_Success;
+ }
+};
+
+static bool operator==(const CXFileLocation &LHS, const CXFileLocation &RHS) {
+ return LHS.Line == RHS.Line && LHS.Column == RHS.Column;
+}
+
+static CXFileRange translateOffsetToRelativeRange(unsigned Offset,
+ unsigned Size,
+ StringRef Source) {
+ assert(Source.drop_front(Offset).take_front(Size).count('\n') == 0 &&
+ "Newlines in translated range?");
+ StringRef Prefix = Source.take_front(Offset);
+ unsigned StartLines = Prefix.count('\n') + 1;
+ if (StartLines > 1)
+ Offset -= Prefix.rfind('\n') + 1;
+ return CXFileRange{{StartLines, Offset + 1}, {StartLines, Offset + 1 + Size}};
+}
+
+class RefactoringResultWrapper {
+public:
+ CXRefactoringReplacements_Old Replacements; // TODO: Remove.
+ CXRefactoringReplacements SourceReplacements;
+ std::unique_ptr<RefactoringContinuation> Continuation;
+ llvm::BumpPtrAllocator Allocator;
+ CXTranslationUnit TU;
+
+ struct AssociatedReplacementInfo {
+ CXSymbolOccurrence *AssociatedSymbolOccurrences;
+ unsigned NumAssociatedSymbolOccurrences;
+ };
+
+ ~RefactoringResultWrapper() {
+ // TODO: Remove.
+ for (unsigned I = 0; I < Replacements.NumFileReplacementSets; ++I) {
+ const CXRefactoringFileReplacementSet_Old &FileSet =
+ Replacements.FileReplacementSets[I];
+ clang_disposeString(FileSet.Filename);
+ for (unsigned J = 0; J < FileSet.NumReplacements; ++J)
+ clang_disposeString(FileSet.Replacements[J].ReplacementString);
+ delete[] FileSet.Replacements;
+ }
+ delete[] Replacements.FileReplacementSets;
+
+ for (unsigned I = 0; I < SourceReplacements.NumFileReplacementSets; ++I) {
+ const CXRefactoringFileReplacementSet &FileSet =
+ SourceReplacements.FileReplacementSets[I];
+ clang_disposeString(FileSet.Filename);
+ for (unsigned J = 0; J < FileSet.NumReplacements; ++J)
+ clang_disposeString(FileSet.Replacements[J].ReplacementString);
+ }
+ }
+
+ RefactoringResultWrapper(
+ ArrayRef<RefactoringReplacement> Replacements,
+ ArrayRef<std::unique_ptr<RefactoringResultAssociatedSymbol>>
+ AssociatedSymbols,
+ std::unique_ptr<RefactoringContinuation> Continuation,
+ ASTContext &Context, CXTranslationUnit TU)
+ : Continuation(std::move(Continuation)), TU(TU) {
+ SourceManager &SM = Context.getSourceManager();
+
+ if (Replacements.empty()) {
+ assert(AssociatedSymbols.empty() && "Symbols without replacements??");
+ // TODO: Remove begin
+ this->Replacements.NumFileReplacementSets = 0;
+ this->Replacements.FileReplacementSets = nullptr;
+ // Remove end
+ this->SourceReplacements.NumFileReplacementSets = 0;
+ this->SourceReplacements.FileReplacementSets = nullptr;
+ return;
+ }
+ llvm::SmallDenseMap<const RefactoringResultAssociatedSymbol *, unsigned>
+ AssociatedSymbolToIndex;
+ for (const auto &Symbol : llvm::enumerate(AssociatedSymbols))
+ AssociatedSymbolToIndex[Symbol.value().get()] = Symbol.index();
+
+ // Find the set of files that have to be modified and gather the indices of
+ // the occurrences for each file.
+ llvm::DenseMap<const FileEntry *, std::vector<unsigned>>
+ FilesToReplacements;
+ for (const auto &Replacement : llvm::enumerate(Replacements)) {
+ SourceLocation Loc = Replacement.value().Range.getBegin();
+ const std::pair<FileID, unsigned> DecomposedLocation =
+ SM.getDecomposedLoc(Loc);
+ assert(DecomposedLocation.first.isValid() && "Invalid file!");
+ const FileEntry *Entry = SM.getFileEntryForID(DecomposedLocation.first);
+ FilesToReplacements.try_emplace(Entry, std::vector<unsigned>())
+ .first->second.push_back(Replacement.index());
+ }
+
+ // TODO: Remove
+ unsigned NumFiles = FilesToReplacements.size();
+ auto *FileReplacementSets =
+ new CXRefactoringFileReplacementSet_Old[NumFiles];
+
+ unsigned FileIndex = 0;
+ for (const auto &Entry : FilesToReplacements) {
+ CXRefactoringFileReplacementSet_Old &FileSet =
+ FileReplacementSets[FileIndex];
+ ++FileIndex;
+ ArrayRef<unsigned> ReplacementIndices = Entry.second;
+ FileSet.Filename = cxstring::createDup(Entry.first->getName());
+ FileSet.NumReplacements = ReplacementIndices.size();
+ auto *FileReplacements =
+ new CXRefactoringReplacement_Old[ReplacementIndices.size()];
+ FileSet.Replacements = FileReplacements;
+
+ unsigned NumRemoved = 0;
+ for (unsigned I = 0; I < FileSet.NumReplacements; ++I) {
+ const RefactoringReplacement &RefReplacement =
+ Replacements[ReplacementIndices[I]];
+ CXSourceRange Range = cxloc::translateSourceRange(
+ SM, Context.getLangOpts(),
+ CharSourceRange::getCharRange(RefReplacement.Range.getBegin(),
+ RefReplacement.Range.getEnd()));
+ CXFileLocation Begin, End;
+ clang_getFileLocation(clang_getRangeStart(Range), nullptr, &Begin.Line,
+ &Begin.Column, nullptr);
+ clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line,
+ &End.Column, nullptr);
+
+ if (I && FileReplacements[I - NumRemoved - 1].Range.End == Begin) {
+ // Merge the previous and the current replacement.
+ FileReplacements[I - NumRemoved - 1].Range.End = End;
+ std::string Replacement =
+ std::string(clang_getCString(
+ FileReplacements[I - NumRemoved - 1].ReplacementString)) +
+ RefReplacement.ReplacementString;
+ FileReplacements[I - NumRemoved - 1].ReplacementString =
+ cxstring::createDup(Replacement);
+ NumRemoved++;
+ continue;
+ }
+
+ CXRefactoringReplacement_Old &Replacement =
+ FileReplacements[I - NumRemoved];
+ Replacement.ReplacementString =
+ cxstring::createDup(RefReplacement.ReplacementString);
+ Replacement.Range.Begin = Begin;
+ Replacement.Range.End = End;
+ }
+ FileSet.NumReplacements -= NumRemoved;
+ }
+
+ this->Replacements.FileReplacementSets = FileReplacementSets;
+ this->Replacements.NumFileReplacementSets = NumFiles;
+
+ // TODO: Outdent.
+ {
+ unsigned NumFiles = FilesToReplacements.size();
+ auto *FileReplacementSets =
+ Allocator.Allocate<CXRefactoringFileReplacementSet>(NumFiles);
+ SourceReplacements.FileReplacementSets = FileReplacementSets;
+ SourceReplacements.NumFileReplacementSets = NumFiles;
+ unsigned FileIndex = 0;
+ for (const auto &Entry : FilesToReplacements) {
+ CXRefactoringFileReplacementSet &FileSet =
+ FileReplacementSets[FileIndex];
+ ++FileIndex;
+ ArrayRef<unsigned> ReplacementIndices = Entry.second;
+ FileSet.Filename = cxstring::createDup(Entry.first->getName());
+ FileSet.NumReplacements = ReplacementIndices.size();
+ auto *FileReplacements = Allocator.Allocate<CXRefactoringReplacement>(
+ ReplacementIndices.size());
+ FileSet.Replacements = FileReplacements;
+
+ unsigned NumRemoved = 0;
+ for (unsigned I = 0; I < FileSet.NumReplacements; ++I) {
+ const RefactoringReplacement &RefReplacement =
+ Replacements[ReplacementIndices[I]];
+ CXSourceRange Range = cxloc::translateSourceRange(
+ SM, Context.getLangOpts(),
+ CharSourceRange::getCharRange(RefReplacement.Range.getBegin(),
+ RefReplacement.Range.getEnd()));
+ CXFileLocation Begin, End;
+ clang_getFileLocation(clang_getRangeStart(Range), nullptr,
+ &Begin.Line, &Begin.Column, nullptr);
+ clang_getFileLocation(clang_getRangeEnd(Range), nullptr, &End.Line,
+ &End.Column, nullptr);
+
+ if (I && FileReplacements[I - NumRemoved - 1].Range.End == Begin) {
+ // Merge the previous and the current replacement.
+ FileReplacements[I - NumRemoved - 1].Range.End = End;
+ std::string Replacement =
+ std::string(clang_getCString(
+ FileReplacements[I - NumRemoved - 1].ReplacementString)) +
+ RefReplacement.ReplacementString;
+ FileReplacements[I - NumRemoved - 1].ReplacementString =
+ cxstring::createDup(Replacement);
+ NumRemoved++;
+ continue;
+ }
+
+ CXRefactoringReplacement &Replacement =
+ FileReplacements[I - NumRemoved];
+ Replacement.ReplacementString =
+ cxstring::createDup(RefReplacement.ReplacementString);
+ Replacement.Range.Begin = Begin;
+ Replacement.Range.End = End;
+ unsigned NumAssociatedSymbols = RefReplacement.SymbolLocations.size();
+ if (!NumAssociatedSymbols) {
+ Replacement.AssociatedData = nullptr;
+ continue;
+ }
+ AssociatedReplacementInfo *AssociatedData =
+ Allocator.Allocate<AssociatedReplacementInfo>();
+ Replacement.AssociatedData = AssociatedData;
+ AssociatedData->AssociatedSymbolOccurrences =
+ Allocator.Allocate<CXSymbolOccurrence>(NumAssociatedSymbols);
+ AssociatedData->NumAssociatedSymbolOccurrences = NumAssociatedSymbols;
+ unsigned SymbolIndex = 0;
+ for (const auto &AssociatedSymbol : RefReplacement.SymbolLocations) {
+ unsigned Index = AssociatedSymbolToIndex[AssociatedSymbol.first];
+ const RefactoringReplacement::AssociatedSymbolLocation &Loc =
+ AssociatedSymbol.second;
+ CXFileRange *NamePieces =
+ Allocator.Allocate<CXFileRange>(Loc.Offsets.size());
+ assert(AssociatedSymbol.first->getName().size() ==
+ Loc.Offsets.size() &&
+ "mismatching symbol name and offsets");
+ for (const auto &Offset : llvm::enumerate(Loc.Offsets)) {
+ StringRef NamePiece =
+ AssociatedSymbol.first->getName()[Offset.index()];
+ NamePieces[Offset.index()] = translateOffsetToRelativeRange(
+ Offset.value(), NamePiece.size(),
+ RefReplacement.ReplacementString);
+ }
+ AssociatedData->AssociatedSymbolOccurrences[SymbolIndex] =
+ CXSymbolOccurrence{
+ NamePieces, (unsigned)Loc.Offsets.size(),
+ Loc.IsDeclaration
+ ? CXSymbolOccurrence_ExtractedDeclaration
+ : CXSymbolOccurrence_ExtractedDeclaration_Reference,
+ /*IsMacroExpansion=*/0, Index};
+ ++SymbolIndex;
+ }
+ }
+ FileSet.NumReplacements -= NumRemoved;
+ }
+ }
+ }
+};
+
+class RefactoringContinuationWrapper {
+public:
+ std::unique_ptr<RefactoringContinuation> Continuation;
+ struct QueryWrapper {
+ indexer::IndexerQuery *Query;
+ CXTranslationUnit TU;
+ std::vector<indexer::Indexed<PersistentDeclRef<Decl>>> DeclResults;
+ unsigned ConsumedResults = 0;
+
+ QueryWrapper(indexer::IndexerQuery *Query, CXTranslationUnit TU)
+ : Query(Query), TU(TU) {}
+ };
+ SmallVector<QueryWrapper, 4> Queries;
+ bool IsInitiationTUAbandoned = false;
+
+ RefactoringContinuationWrapper(
+ std::unique_ptr<RefactoringContinuation> Continuation,
+ CXTranslationUnit TU)
+ : Continuation(std::move(Continuation)) {
+ Queries.emplace_back(this->Continuation->getASTUnitIndexerQuery(), TU);
+ assert(Queries.back().Query && "Invalid ast query");
+ std::vector<indexer::IndexerQuery *> AdditionalQueries =
+ this->Continuation->getAdditionalIndexerQueries();
+ for (indexer::IndexerQuery *IQ : AdditionalQueries)
+ Queries.emplace_back(IQ, TU);
+ }
+};
+
+class RefactoringDiagnosticConsumer : public DiagnosticConsumer {
+ const ASTContext &Context;
+ DiagnosticConsumer *PreviousClient;
+ std::unique_ptr<DiagnosticConsumer> PreviousClientPtr;
+ llvm::SmallVector<StoredDiagnostic, 2> RenameDiagnostics;
+ llvm::SmallVector<StoredDiagnostic, 1> ContinuationDiagnostics;
+
+public:
+ RefactoringDiagnosticConsumer(ASTContext &Context) : Context(Context) {
+ PreviousClient = Context.getDiagnostics().getClient();
+ PreviousClientPtr = Context.getDiagnostics().takeClient();
+ Context.getDiagnostics().setClient(this, /*ShouldOwnClient=*/false);
+ }
+
+ ~RefactoringDiagnosticConsumer() {
+ if (PreviousClientPtr)
+ Context.getDiagnostics().setClient(PreviousClientPtr.release());
+ else
+ Context.getDiagnostics().setClient(PreviousClient,
+ /*ShouldOwnClient=*/false);
+ }
+
+ void HandleDiagnostic(DiagnosticsEngine::Level Level,
+ const Diagnostic &Info) override {
+ unsigned Cat = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
+ if (Cat == diag::DiagCat_Rename_Issue)
+ RenameDiagnostics.push_back(StoredDiagnostic(Level, Info));
+ else if (Cat == diag::DiagCat_Refactoring_Continuation_Issue)
+ ContinuationDiagnostics.push_back(StoredDiagnostic(Level, Info));
+ else
+ assert(false && "Unhandled refactoring category");
+ }
+
+ CXDiagnosticSetImpl *createDiags() const {
+ if (RenameDiagnostics.empty() && ContinuationDiagnostics.empty())
+ return nullptr;
+ llvm::SmallVector<StoredDiagnostic, 2> AllDiagnostics;
+ for (const auto &D : RenameDiagnostics)
+ AllDiagnostics.push_back(D);
+ for (const auto &D : ContinuationDiagnostics)
+ AllDiagnostics.push_back(D);
+ return cxdiag::createStoredDiags(AllDiagnostics, Context.getLangOpts());
+ }
+
+ CXRefactoringActionSetWithDiagnostics createActionSet() const {
+ if (RenameDiagnostics.empty())
+ return {nullptr, 0};
+ CXRefactoringActionWithDiagnostics *Actions =
+ new CXRefactoringActionWithDiagnostics[1];
+ Actions[0].Action = CXRefactor_Rename;
+ Actions[0].Diagnostics =
+ cxdiag::createStoredDiags(RenameDiagnostics, Context.getLangOpts());
+ return {Actions, 1};
+ }
+};
+
+} // end anonymous namespace
+
+template <typename T>
+static T withRenamingAction(CXRefactoringAction Action, T DefaultValue,
+ llvm::function_ref<T(RenamingAction &)> Callback) {
+ if (!Action)
+ return DefaultValue;
+ RenamingAction *Rename =
+ static_cast<RefactoringAction *>(Action)->getRenamingAction();
+ if (!Rename)
+ return DefaultValue;
+ return Callback(*Rename);
+}
+
+static enum CXIndexerQueryKind
+translateDeclPredicate(const indexer::DeclPredicate &Predicate) {
+ indexer::DeclEntity Entity;
+ if (Predicate == Entity.isDefined().Predicate)
+ return CXIndexerQuery_Decl_IsDefined;
+ return CXIndexerQuery_Unknown;
+}
+
+extern "C" {
+
+CXString
+clang_RefactoringActionType_getName(enum CXRefactoringActionType Action) {
+ return cxstring::createRef(
+ getRefactoringActionTypeName(translateRefactoringActionType(Action)));
+}
+
+void clang_RefactoringActionSet_dispose(CXRefactoringActionSet *Set) {
+ if (Set && Set->Actions)
+ delete[] Set->Actions;
+}
+
+void clang_RefactoringActionSetWithDiagnostics_dispose(
+ CXRefactoringActionSetWithDiagnostics *Set) {
+ if (Set && Set->Actions) {
+ for (auto &S : llvm::makeArrayRef(Set->Actions, Set->NumActions))
+ clang_disposeDiagnosticSet(S.Diagnostics);
+ delete[] Set->Actions;
+ }
+}
+
+CXRefactoringOptionSet clang_RefactoringOptionSet_create() {
+ return new RefactoringOptionSet;
+}
+
+CXRefactoringOptionSet
+clang_RefactoringOptionSet_createFromString(const char *String) {
+ RefactoringOptionSet *Result = new RefactoringOptionSet;
+ auto Options = RefactoringOptionSet::parse(String);
+ if (Options) {
+ *Result = std::move(*Options);
+ return Result;
+ }
+ llvm::handleAllErrors(Options.takeError(),
+ [](const llvm::StringError &Error) {});
+ return clang_RefactoringOptionSet_create();
+}
+
+void clang_RefactoringOptionSet_add(CXRefactoringOptionSet Set,
+ enum CXRefactoringOption Option) {
+ if (!Set)
+ return;
+ switch (Option) {
+ case CXRefactorOption_AvoidTextualMatches:
+ static_cast<RefactoringOptionSet *>(Set)->add(
+ option::AvoidTextualMatches::getTrue());
+ break;
+ }
+}
+
+CXString clang_RefactoringOptionSet_toString(CXRefactoringOptionSet Set) {
+ if (!Set)
+ return cxstring::createNull();
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ static_cast<RefactoringOptionSet *>(Set)->print(OS);
+ return cxstring::createDup(OS.str());
+}
+
+void clang_RefactoringOptionSet_dispose(CXRefactoringOptionSet Set) {
+ if (Set)
+ delete static_cast<RefactoringOptionSet *>(Set);
+}
+
+enum CXErrorCode
+clang_Refactoring_findActionsAt(CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange,
+ CXRefactoringOptionSet Options,
+ CXRefactoringActionSet *OutSet) {
+ return clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt(
+ TU, Location, SelectionRange, Options, OutSet, /*OutFailureSet=*/nullptr);
+}
+
+enum CXErrorCode clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, CXRefactoringOptionSet Options,
+ CXRefactoringActionSet *OutSet,
+ CXRefactoringActionSetWithDiagnostics *OutFailureSet) {
+ LOG_FUNC_SECTION { *Log << TU << ' '; }
+ if (OutFailureSet) {
+ OutFailureSet->Actions = nullptr;
+ OutFailureSet->NumActions = 0;
+ }
+
+ if (!OutSet)
+ return CXError_InvalidArguments;
+
+ OutSet->Actions = nullptr;
+ OutSet->NumActions = 0;
+
+ if (cxtu::isNotUsableTU(TU)) {
+ LOG_BAD_TU(TU);
+ return CXError_InvalidArguments;
+ }
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
+ if (!CXXUnit)
+ return CXError_InvalidArguments;
+
+ SourceLocation Loc = cxloc::translateSourceLocation(Location);
+ if (Loc.isInvalid())
+ return CXError_InvalidArguments;
+
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+ (void)Options; // FIXME: handle options
+ ASTContext &Context = CXXUnit->getASTContext();
+ RefactoringDiagnosticConsumer DiagConsumer(Context);
+ RefactoringActionSet ActionSet = findActionSetAt(
+ Loc, cxloc::translateCXSourceRange(SelectionRange), Context);
+ if (OutFailureSet)
+ *OutFailureSet = DiagConsumer.createActionSet();
+ if (ActionSet.Actions.empty())
+ return CXError_RefactoringActionUnavailable;
+
+ CXRefactoringActionType *Actions =
+ new CXRefactoringActionType[ActionSet.Actions.size()];
+ OutSet->Actions = Actions;
+ OutSet->NumActions = ActionSet.Actions.size();
+ for (const auto &Action : llvm::enumerate(ActionSet.Actions))
+ Actions[Action.index()] = translateRefactoringActionType(Action.value());
+ return CXError_Success;
+}
+
+void clang_RefactoringAction_dispose(CXRefactoringAction Action) {
+ if (Action)
+ delete static_cast<RefactoringAction *>(Action);
+}
+
+CXSourceRange
+clang_RefactoringAction_getSourceRangeOfInterest(CXRefactoringAction Action) {
+ if (Action) {
+ RefactoringOperation *Operation =
+ static_cast<RefactoringAction *>(Action)->getOperation();
+ if (Operation) {
+ ASTUnit *CXXUnit = cxtu::getASTUnit(
+ static_cast<RefactoringAction *>(Action)->InitiationTU);
+ if (const Stmt *S = Operation->getTransformedStmt()) {
+ SourceRange Range = S->getSourceRange();
+ if (const Stmt *Last = Operation->getLastTransformedStmt())
+ Range.setEnd(Last->getLocEnd());
+ return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range);
+ } else if (const Decl *D = Operation->getTransformedDecl()) {
+ SourceRange Range = D->getSourceRange();
+ if (const Decl *Last = Operation->getLastTransformedDecl())
+ Range.setEnd(Last->getLocEnd());
+ return cxloc::translateSourceRange(CXXUnit->getASTContext(), Range);
+ }
+ }
+ }
+ return clang_getNullRange();
+}
+
+int clang_RefactoringAction_requiresImplementationTU(
+ CXRefactoringAction Action) {
+ return withRenamingAction<int>(Action, 0, [](RenamingAction &Action) {
+ return Action.Operation.requiresImplementationTU();
+ });
+}
+
+CXString clang_RefactoringAction_getUSRThatRequiresImplementationTU(
+ CXRefactoringAction Action) {
+ return withRenamingAction<CXString>(
+ Action, cxstring::createNull(), [](RenamingAction &Action) {
+ return Action.getUSRThatRequiresImplementationTU();
+ });
+}
+
+enum CXErrorCode
+clang_RefactoringAction_addImplementationTU(CXRefactoringAction Action,
+ CXTranslationUnit TU) {
+ if (!Action || !TU)
+ return CXError_InvalidArguments;
+ // Prohibit multiple additions of implementation TU.
+ if (static_cast<RefactoringAction *>(Action)->ImplementationTU)
+ return CXError_Failure;
+ static_cast<RefactoringAction *>(Action)->ImplementationTU = TU;
+ return CXError_Success;
+}
+
+enum CXErrorCode clang_RefactoringAction_getRefactoringCandidates(
+ CXRefactoringAction Action,
+ CXRefactoringCandidateSet *OutRefactoringCandidateSet) {
+ if (!Action || !OutRefactoringCandidateSet)
+ return CXError_InvalidArguments;
+ *OutRefactoringCandidateSet =
+ static_cast<RefactoringAction *>(Action)->getRefactoringCandidates();
+ return CXError_Success;
+}
+
+enum CXErrorCode
+clang_RefactoringAction_selectRefactoringCandidate(CXRefactoringAction Action,
+ unsigned Index) {
+ if (!Action)
+ return CXError_InvalidArguments;
+ return static_cast<RefactoringAction *>(Action)->selectCandidate(Index);
+}
+
+// TODO: Remove.
+enum CXErrorCode clang_Refactoring_initiateActionAt(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType,
+ CXRefactoringOptionSet Options, CXRefactoringAction *OutAction,
+ CXString *OutFailureReason) {
+ CXDiagnosticSet Diags;
+ CXErrorCode Result = clang_Refactoring_initiateAction(
+ TU, Location, SelectionRange, ActionType, Options, OutAction, &Diags);
+ if (OutFailureReason && Diags && clang_getNumDiagnosticsInSet(Diags) == 1) {
+ CXString Spelling =
+ clang_getDiagnosticSpelling(clang_getDiagnosticInSet(Diags, 0));
+ *OutFailureReason = cxstring::createDup(clang_getCString(Spelling));
+ clang_disposeString(Spelling);
+ } else if (OutFailureReason)
+ *OutFailureReason = cxstring::createEmpty();
+ clang_disposeDiagnosticSet(Diags);
+ return Result;
+}
+
+enum CXErrorCode clang_Refactoring_initiateAction(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, enum CXRefactoringActionType ActionType,
+ CXRefactoringOptionSet Options, CXRefactoringAction *OutAction,
+ CXDiagnosticSet *OutDiagnostics) {
+ if (!OutAction)
+ return CXError_InvalidArguments;
+ *OutAction = nullptr;
+ if (OutDiagnostics)
+ *OutDiagnostics = nullptr;
+
+ if (cxtu::isNotUsableTU(TU)) {
+ LOG_BAD_TU(TU);
+ return CXError_InvalidArguments;
+ }
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
+ if (!CXXUnit)
+ return CXError_InvalidArguments;
+
+ SourceLocation Loc = cxloc::translateSourceLocation(Location);
+ if (Loc.isInvalid())
+ return CXError_InvalidArguments;
+
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+
+ (void)Options; // FIXME: handle options
+ ASTContext &Context = CXXUnit->getASTContext();
+ RefactoringDiagnosticConsumer DiagConsumer(Context);
+ auto Operation = initiateRefactoringOperationAt(
+ Loc, cxloc::translateCXSourceRange(SelectionRange), Context,
+ translateRefactoringActionType(ActionType));
+ if (!Operation.Initiated) {
+ if (OutDiagnostics) {
+ if (!Operation.FailureReason.empty()) {
+ // TODO: Remove when other actions migrate to diagnostics.
+ StoredDiagnostic Diag(DiagnosticsEngine::Error, /*ID=*/0,
+ Operation.FailureReason);
+ *OutDiagnostics =
+ cxdiag::createStoredDiags(Diag, Context.getLangOpts());
+ } else
+ *OutDiagnostics = DiagConsumer.createDiags();
+ }
+ return CXError_RefactoringActionUnavailable;
+ }
+ if (Operation.RefactoringOp)
+ *OutAction = new RefactoringAction(std::move(Operation.RefactoringOp),
+ ActionType, TU);
+ else
+ *OutAction = new RefactoringAction(
+ llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(),
+ std::move(*Operation.SymbolOp)),
+ TU);
+ return CXError_Success;
+}
+
+enum CXErrorCode clang_Refactoring_initiateActionOnDecl(
+ CXTranslationUnit TU, const char *DeclUSR,
+ enum CXRefactoringActionType ActionType, CXRefactoringOptionSet Options,
+ CXRefactoringAction *OutAction, CXString *OutFailureReason) {
+ if (!OutAction)
+ return CXError_InvalidArguments;
+ *OutAction = nullptr;
+ if (OutFailureReason)
+ *OutFailureReason = cxstring::createNull();
+
+ if (cxtu::isNotUsableTU(TU)) {
+ LOG_BAD_TU(TU);
+ return CXError_InvalidArguments;
+ }
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
+ if (!CXXUnit)
+ return CXError_InvalidArguments;
+
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+
+ (void)Options; // FIXME: handle options
+ auto Operation = initiateRefactoringOperationOnDecl(
+ DeclUSR, CXXUnit->getASTContext(),
+ translateRefactoringActionType(ActionType));
+ if (!Operation.Initiated)
+ return CXError_RefactoringActionUnavailable;
+ // FIXME: Don't dupe with above
+ if (Operation.RefactoringOp)
+ *OutAction = new RefactoringAction(std::move(Operation.RefactoringOp),
+ ActionType, TU);
+ else
+ *OutAction = new RefactoringAction(
+ llvm::make_unique<RenamingAction>(CXXUnit->getLangOpts(),
+ std::move(*Operation.SymbolOp)),
+ TU);
+ return CXError_Success;
+}
+
+enum CXErrorCode
+clang_Refactoring_initiateRenamingOperation(CXRefactoringAction Action) {
+ if (!Action)
+ return CXError_InvalidArguments;
+ RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action);
+ RenamingAction *Rename = RefAction->getRenamingAction();
+ if (!Rename)
+ return CXError_InvalidArguments;
+ // TODO
+ return CXError_Success;
+}
+
+CINDEX_LINKAGE
+enum CXErrorCode clang_Refactoring_findRenamedCursor(
+ CXTranslationUnit TU, CXSourceLocation Location,
+ CXSourceRange SelectionRange, CXCursor *OutCursor) {
+ if (!OutCursor)
+ return CXError_InvalidArguments;
+
+ if (cxtu::isNotUsableTU(TU)) {
+ LOG_BAD_TU(TU);
+ return CXError_InvalidArguments;
+ }
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
+ if (!CXXUnit)
+ return CXError_InvalidArguments;
+ SourceLocation Loc = cxloc::translateSourceLocation(Location);
+ if (Loc.isInvalid())
+ return CXError_InvalidArguments;
+
+ const NamedDecl *ND = rename::getNamedDeclAt(CXXUnit->getASTContext(), Loc);
+ if (!ND) {
+ *OutCursor = cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound, TU);
+ return CXError_RefactoringActionUnavailable;
+ }
+
+ *OutCursor = cxcursor::MakeCXCursor(ND, TU);
+ return CXError_Success;
+}
+
+enum CXErrorCode clang_RenamingOperation_setNewName(CXRefactoringAction Action,
+ const char *NewName) {
+ return withRenamingAction<CXErrorCode>(
+ Action, CXError_InvalidArguments,
+ [=](RenamingAction &Action) -> CXErrorCode {
+ if (!NewName)
+ return CXError_InvalidArguments;
+ StringRef Name = NewName;
+ if (Name.empty())
+ return CXError_InvalidArguments;
+ return Action.setNewName(Name);
+ });
+}
+
+enum CXRefactoringActionType
+clang_RefactoringAction_getInitiatedActionType(CXRefactoringAction Action) {
+ return static_cast<RefactoringAction *>(Action)->Type;
+}
+
+unsigned clang_RenamingOperation_getNumSymbols(CXRefactoringAction Action) {
+ return withRenamingAction<unsigned>(Action, 0, [](RenamingAction &Action) {
+ return Action.Operation.symbols().size();
+ });
+}
+
+CXString clang_RenamingOperation_getUSRForSymbol(CXRefactoringAction Action,
+ unsigned Index) {
+ return withRenamingAction<CXString>(
+ Action, cxstring::createNull(),
+ [=](RenamingAction &Action) { return Action.usrForSymbolAt(Index); });
+}
+
+CXRenamingResult clang_Refactoring_findRenamedOccurrencesInPrimaryTUs(
+ CXRefactoringAction Action, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles) {
+ if (!Action)
+ return nullptr;
+ RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action);
+ RenamingAction *Rename = RefAction->getRenamingAction();
+ if (!Rename)
+ return nullptr;
+
+ // TODO: Handle implementation TU
+ if (cxtu::isNotUsableTU(RefAction->InitiationTU)) {
+ LOG_BAD_TU(RefAction->InitiationTU);
+ return nullptr;
+ }
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU);
+ if (!CXXUnit)
+ return nullptr;
+
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+
+ return Rename->handlePrimaryTU(RefAction->InitiationTU, *CXXUnit);
+}
+
+CXSymbolOccurrencesResult clang_Refactoring_findSymbolOccurrencesInInitiationTU(
+ CXRefactoringAction Action, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles) {
+ if (!Action)
+ return nullptr;
+ RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action);
+ RenamingAction *Rename = RefAction->getRenamingAction();
+ if (!Rename)
+ return nullptr;
+
+ if (cxtu::isNotUsableTU(RefAction->InitiationTU)) {
+ LOG_BAD_TU(RefAction->InitiationTU);
+ return nullptr;
+ }
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU);
+ if (!CXXUnit)
+ return nullptr;
+
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+
+ return Rename->findSymbolsInInitiationTU(RefAction->InitiationTU, *CXXUnit);
+}
+
+CXErrorCode clang_Refactoring_findRenamedOccurrencesInIndexedFile(
+ const CXRenamedIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx,
+ const char *Filename, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles, CXRefactoringOptionSet Options,
+ CXRenamingResult *OutResult) {
+ if (!OutResult)
+ return CXError_InvalidArguments;
+ if (!Symbols || !NumSymbols || !Filename)
+ return CXError_InvalidArguments;
+ return performIndexedFileRename(
+ llvm::makeArrayRef(Symbols, NumSymbols), StringRef(Filename),
+ llvm::makeArrayRef(CommandLineArgs, NumCommandLineArgs), CIdx,
+ MutableArrayRef<CXUnsavedFile>(UnsavedFiles, NumUnsavedFiles),
+ Options ? static_cast<RefactoringOptionSet *>(Options) : nullptr,
+ *OutResult);
+}
+
+CXErrorCode clang_Refactoring_findSymbolOccurrencesInIndexedFile(
+ const CXIndexedSymbol *Symbols, unsigned NumSymbols, CXIndex CIdx,
+ const char *Filename, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles, CXRefactoringOptionSet Options,
+ CXSymbolOccurrencesResult *OutResult) {
+ if (!OutResult)
+ return CXError_InvalidArguments;
+ if (!Symbols || !NumSymbols || !Filename)
+ return CXError_InvalidArguments;
+ return performIndexedSymbolSearch(
+ llvm::makeArrayRef(Symbols, NumSymbols), StringRef(Filename),
+ llvm::makeArrayRef(CommandLineArgs, NumCommandLineArgs), CIdx,
+ MutableArrayRef<CXUnsavedFile>(UnsavedFiles, NumUnsavedFiles),
+ Options ? static_cast<RefactoringOptionSet *>(Options) : nullptr,
+ *OutResult);
+}
+
+unsigned clang_RenamingResult_getNumModifiedFiles(CXRenamingResult Result) {
+ if (Result)
+ return static_cast<RenamingResult *>(Result)->getFilenames().size();
+ return 0;
+}
+
+void clang_RenamingResult_getResultForFile(CXRenamingResult Result,
+ unsigned FileIndex,
+ CXFileRenamingResult *OutResult) {
+ if (!Result ||
+ FileIndex >=
+ static_cast<RenamingResult *>(Result)->getFilenames().size()) {
+ OutResult->Filename = cxstring::createNull();
+ OutResult->NumOccurrences = 0;
+ OutResult->Occurrences = nullptr;
+ return;
+ }
+ auto &RenameResult = *static_cast<RenamingResult *>(Result);
+ OutResult->Filename = RenameResult.getFilenames()[FileIndex];
+ OutResult->NumOccurrences = RenameResult.getOccurrences(FileIndex).size();
+ OutResult->Occurrences = RenameResult.getOccurrences(FileIndex).data();
+}
+
+void clang_RenamingResult_dispose(CXRenamingResult Result) {
+ if (Result)
+ delete static_cast<RenamingResult *>(Result);
+}
+
+unsigned clang_SymbolOccurrences_getNumFiles(CXSymbolOccurrencesResult Result) {
+ if (Result)
+ return static_cast<SymbolOccurrencesResult *>(Result)
+ ->getFilenames()
+ .size();
+ return 0;
+}
+
+void clang_SymbolOccurrences_getOccurrencesForFile(
+ CXSymbolOccurrencesResult Result, unsigned FileIndex,
+ CXSymbolOccurrencesInFile *OutResult) {
+ if (!Result ||
+ FileIndex >= static_cast<SymbolOccurrencesResult *>(Result)
+ ->getFilenames()
+ .size()) {
+ OutResult->Filename = cxstring::createNull();
+ OutResult->NumOccurrences = 0;
+ OutResult->Occurrences = nullptr;
+ return;
+ }
+ auto &RenameResult = *static_cast<SymbolOccurrencesResult *>(Result);
+ OutResult->Filename = RenameResult.getFilenames()[FileIndex];
+ OutResult->NumOccurrences = RenameResult.getOccurrences(FileIndex).size();
+ OutResult->Occurrences = RenameResult.getOccurrences(FileIndex).data();
+}
+
+void clang_SymbolOccurrences_dispose(CXSymbolOccurrencesResult Result) {
+ if (Result)
+ delete static_cast<SymbolOccurrencesResult *>(Result);
+}
+
+CXRefactoringResult clang_Refactoring_performOperation(
+ CXRefactoringAction Action, const char *const *CommandLineArgs,
+ int NumCommandLineArgs, struct CXUnsavedFile *UnsavedFiles,
+ unsigned NumUnsavedFiles, CXRefactoringOptionSet Options,
+ CXString *OutFailureReason) {
+ if (OutFailureReason)
+ *OutFailureReason = cxstring::createNull();
+ if (!Action)
+ return nullptr;
+ RefactoringAction *RefAction = static_cast<RefactoringAction *>(Action);
+ if (!RefAction->getOperation())
+ return nullptr;
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(RefAction->InitiationTU);
+ if (!CXXUnit)
+ return nullptr;
+
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+
+ RefactoringOptionSet EmptyOptionSet;
+ const RefactoringOptionSet &OptionSet =
+ Options ? *static_cast<RefactoringOptionSet *>(Options) : EmptyOptionSet;
+ llvm::Expected<RefactoringResult> Result = RefAction->getOperation()->perform(
+ CXXUnit->getASTContext(), CXXUnit->getPreprocessor(), OptionSet,
+ RefAction->SelectedCandidate);
+ if (!Result) {
+ if (OutFailureReason) {
+ (void)!llvm::handleErrors(
+ Result.takeError(), [&](const RefactoringOperationError &Error) {
+ *OutFailureReason = cxstring::createDup(Error.FailureReason);
+ });
+ }
+ return nullptr;
+ }
+ return new RefactoringResultWrapper(
+ Result.get().Replacements, Result.get().AssociatedSymbols,
+ std::move(Result.get().Continuation), CXXUnit->getASTContext(),
+ RefAction->InitiationTU);
+}
+
+void clang_RefactoringResult_getReplacements(
+ CXRefactoringResult Result,
+ CXRefactoringReplacements_Old *OutReplacements) {
+ if (!OutReplacements)
+ return;
+ if (!Result) {
+ OutReplacements->FileReplacementSets = nullptr;
+ OutReplacements->NumFileReplacementSets = 0;
+ return;
+ }
+ *OutReplacements = static_cast<RefactoringResultWrapper *>(Result)->Replacements;
+}
+
+CXRefactoringReplacements
+clang_RefactoringResult_getSourceReplacements(CXRefactoringResult Result) {
+ if (!Result)
+ return CXRefactoringReplacements{nullptr, 0};
+ return static_cast<RefactoringResultWrapper *>(Result)->SourceReplacements;
+}
+
+CXRefactoringReplacementAssociatedSymbolOccurrences
+clang_RefactoringReplacement_getAssociatedSymbolOccurrences(
+ CXRefactoringReplacement Replacement) {
+ if (!Replacement.AssociatedData)
+ return CXRefactoringReplacementAssociatedSymbolOccurrences{nullptr, 0};
+ auto *Data =
+ static_cast<RefactoringResultWrapper::AssociatedReplacementInfo *>(
+ Replacement.AssociatedData);
+ return CXRefactoringReplacementAssociatedSymbolOccurrences{
+ Data->AssociatedSymbolOccurrences, Data->NumAssociatedSymbolOccurrences};
+}
+
+void clang_RefactoringResult_dispose(CXRefactoringResult Result) {
+ if (Result)
+ delete static_cast<RefactoringResultWrapper *>(Result);
+}
+
+CXRefactoringContinuation
+clang_RefactoringResult_getContinuation(CXRefactoringResult Result) {
+ if (!Result)
+ return nullptr;
+ auto *Wrapper = static_cast<RefactoringResultWrapper *>(Result);
+ if (!Wrapper->Continuation)
+ return nullptr;
+ return new RefactoringContinuationWrapper(std::move(Wrapper->Continuation),
+ Wrapper->TU);
+}
+
+enum CXErrorCode
+clang_RefactoringContinuation_loadSerializedIndexerQueryResults(
+ CXRefactoringContinuation Continuation, const char *Source) {
+ if (!Continuation)
+ return CXError_InvalidArguments;
+ auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation);
+ llvm::SmallVector<indexer::IndexerQuery *, 4> Queries;
+ for (const auto &Query : Wrapper->Queries)
+ Queries.push_back(Query.Query);
+ auto Err = indexer::IndexerQuery::loadResultsFromYAML(Source, Queries);
+ if (Err) {
+ consumeError(std::move(Err));
+ return CXError_Failure;
+ }
+ return CXError_Success;
+}
+
+unsigned clang_RefactoringContinuation_getNumIndexerQueries(
+ CXRefactoringContinuation Continuation) {
+ if (Continuation)
+ return static_cast<RefactoringContinuationWrapper *>(Continuation)
+ ->Queries.size();
+ return 0;
+}
+
+CXIndexerQuery clang_RefactoringContinuation_getIndexerQuery(
+ CXRefactoringContinuation Continuation, unsigned Index) {
+ if (!Continuation)
+ return nullptr;
+ auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation);
+ if (Index >= Wrapper->Queries.size())
+ return nullptr;
+ return &Wrapper->Queries[Index];
+}
+
+CXDiagnosticSet clang_RefactoringContinuation_verifyBeforeFinalizing(
+ CXRefactoringContinuation Continuation) {
+ if (!Continuation)
+ return nullptr;
+ auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation);
+ CXTranslationUnit TU = Wrapper->Queries[0].TU;
+ ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
+ if (!CXXUnit)
+ return nullptr;
+ ASTContext &Context = CXXUnit->getASTContext();
+ RefactoringDiagnosticConsumer DiagConsumer(Context);
+ for (const auto &Query : Wrapper->Queries) {
+ if (Query.Query->verify(Context))
+ break;
+ }
+ return DiagConsumer.createDiags();
+}
+
+void clang_RefactoringContinuation_finalizeEvaluationInInitationTU(
+ CXRefactoringContinuation Continuation) {
+ if (!Continuation)
+ return;
+ auto *Wrapper = static_cast<RefactoringContinuationWrapper *>(Continuation);
+ Wrapper->Queries.clear();
+ Wrapper->Continuation->persistTUSpecificState();
+ Wrapper->IsInitiationTUAbandoned = true;
+}
+
+CXRefactoringResult clang_RefactoringContinuation_continueOperationInTU(
+ CXRefactoringContinuation Continuation, CXTranslationUnit TU,
+ CXString *OutFailureReason) {
+ if (!Continuation || !TU)
+ return nullptr;
+ ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
+ if (!CXXUnit)
+ return nullptr;
+ ASTUnit::ConcurrencyCheck Check(*CXXUnit);
+ const auto *Wrapper =
+ static_cast<RefactoringContinuationWrapper *>(Continuation);
+ if (!Wrapper->IsInitiationTUAbandoned) {
+ // FIXME: We can avoid conversions of TU-specific state if the given TU is
+ // the same as the initiation TU.
+ clang_RefactoringContinuation_finalizeEvaluationInInitationTU(Continuation);
+ }
+ auto Result =
+ Wrapper->Continuation->runInExternalASTUnit(CXXUnit->getASTContext());
+ if (!Result) {
+ if (OutFailureReason) {
+ (void)!llvm::handleErrors(
+ Result.takeError(), [&](const RefactoringOperationError &Error) {
+ *OutFailureReason = cxstring::createDup(Error.FailureReason);
+ });
+ }
+ return nullptr;
+ }
+ return new RefactoringResultWrapper(
+ Result.get().Replacements, Result.get().AssociatedSymbols,
+ std::move(Result.get().Continuation), CXXUnit->getASTContext(), TU);
+}
+
+void clang_RefactoringContinuation_dispose(
+ CXRefactoringContinuation Continuation) {
+ if (Continuation)
+ delete static_cast<RefactoringContinuationWrapper *>(Continuation);
+}
+
+enum CXIndexerQueryKind clang_IndexerQuery_getKind(CXIndexerQuery Query) {
+ if (!Query)
+ return CXIndexerQuery_Unknown;
+ const auto *IQ =
+ static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query;
+ if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) {
+ const indexer::detail::DeclPredicateNode &Node = DQ->getPredicateNode();
+ if (const auto *NP =
+ dyn_cast<indexer::detail::DeclPredicateNotPredicate>(&Node))
+ return translateDeclPredicate(
+ cast<indexer::detail::DeclPredicateNodePredicate>(NP->getChild())
+ .getPredicate());
+ return translateDeclPredicate(
+ cast<indexer::detail::DeclPredicateNodePredicate>(Node).getPredicate());
+ } else if (isa<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ))
+ return CXIndexerQuery_Decl_FileThatShouldImplement;
+ return CXIndexerQuery_Unknown;
+}
+
+unsigned clang_IndexerQuery_getNumCursors(CXIndexerQuery Query) {
+ if (!Query)
+ return 0;
+ const auto *IQ =
+ static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query;
+ if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ))
+ return DQ->getInputs().size();
+ else if (isa<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ))
+ return 1;
+ return 0;
+}
+
+CXCursor clang_IndexerQuery_getCursor(CXIndexerQuery Query,
+ unsigned CursorIndex) {
+ if (Query) {
+ const auto *Wrapper =
+ static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query);
+ const indexer::IndexerQuery *IQ = Wrapper->Query;
+ CXTranslationUnit TU = Wrapper->TU;
+ if (const auto *DQ = dyn_cast<indexer::DeclarationsQuery>(IQ)) {
+ if (CursorIndex < DQ->getInputs().size())
+ return cxcursor::MakeCXCursor(DQ->getInputs()[CursorIndex], TU);
+ } else if (const auto *ASTQuery = dyn_cast<
+ indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) {
+ if (CursorIndex == 0)
+ return cxcursor::MakeCXCursor(ASTQuery->getDecl(), TU);
+ }
+ }
+ return cxcursor::MakeCXCursorInvalid(CXCursor_InvalidCode);
+}
+
+enum CXIndexerQueryAction
+clang_IndexerQuery_consumeIntResult(CXIndexerQuery Query, unsigned CursorIndex,
+ int Value) {
+ if (!Query)
+ return CXIndexerQueryAction_None;
+ auto *Wrapper =
+ static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query);
+ auto *DQ = dyn_cast<indexer::DeclarationsQuery>(Wrapper->Query);
+ if (!DQ)
+ return CXIndexerQueryAction_None;
+ if (CursorIndex >= DQ->getInputs().size() ||
+ Wrapper->ConsumedResults == DQ->getInputs().size())
+ return CXIndexerQueryAction_None;
+ if (Wrapper->DeclResults.empty())
+ Wrapper->DeclResults.resize(DQ->getInputs().size(),
+ indexer::Indexed<PersistentDeclRef<Decl>>(
+ PersistentDeclRef<Decl>::create(nullptr)));
+ // Filter the declarations!
+ bool IsNot = false;
+ if (isa<indexer::detail::DeclPredicateNotPredicate>(DQ->getPredicateNode()))
+ IsNot = true;
+ bool Result = IsNot ? !Value : !!Value;
+ Wrapper->DeclResults[CursorIndex] = indexer::Indexed<PersistentDeclRef<Decl>>(
+ PersistentDeclRef<Decl>::create(Result ? DQ->getInputs()[CursorIndex]
+ : nullptr),
+ Result ? indexer::QueryBoolResult::Yes : indexer::QueryBoolResult::No);
+ Wrapper->ConsumedResults++;
+ if (Wrapper->ConsumedResults == Wrapper->DeclResults.size()) {
+ // We've received all the results, pass them back to the query.
+ DQ->setOutput(std::move(Wrapper->DeclResults));
+ }
+ return CXIndexerQueryAction_None;
+}
+
+enum CXIndexerQueryAction
+clang_IndexerQuery_consumeFileResult(CXIndexerQuery Query, unsigned CursorIndex,
+ const char *Filename) {
+ if (!Query || !Filename)
+ return CXIndexerQueryAction_None;
+ auto *IQ =
+ static_cast<RefactoringContinuationWrapper::QueryWrapper *>(Query)->Query;
+ if (auto *ASTQuery =
+ dyn_cast<indexer::ASTUnitForImplementationOfDeclarationQuery>(IQ)) {
+ if (CursorIndex != 0)
+ return CXIndexerQueryAction_None;
+ ASTQuery->setResult(PersistentFileID(Filename));
+ return CXIndexerQueryAction_RunContinuationInTUThatHasThisFile;
+ }
+ return CXIndexerQueryAction_None;
+}
+}
diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp
index a2ef68b..2cfecf7 100644
--- a/tools/libclang/CXIndexDataConsumer.cpp
+++ b/tools/libclang/CXIndexDataConsumer.cpp
@@ -1258,6 +1258,8 @@
case SymbolKind::Module:
case SymbolKind::Macro:
case SymbolKind::ClassProperty:
+ case SymbolKind::Using:
+ case SymbolKind::CommentTag:
return CXIdxEntity_Unexposed;
case SymbolKind::Enum: return CXIdxEntity_Enum;
diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp
index 5312b7c..2a13624 100644
--- a/tools/libclang/Indexing.cpp
+++ b/tools/libclang/Indexing.cpp
@@ -272,7 +272,8 @@
/// SourceRangeSkipped - This hook is called when a source range is skipped.
/// \param Range The SourceRange that was skipped. The range begins at the
/// #if/#else directive and ends after the #endif/#else directive.
- void SourceRangeSkipped(SourceRange Range) override {}
+ void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override {
+ }
};
//===----------------------------------------------------------------------===//
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index e0d178a..e9a9b58 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -353,3 +353,57 @@
clang_EvalResult_getAsDouble
clang_EvalResult_getAsStr
clang_EvalResult_dispose
+clang_RefactoringActionType_getName
+clang_RefactoringActionSet_dispose
+clang_RefactoringActionSetWithDiagnostics_dispose
+clang_RefactoringOptionSet_create
+clang_RefactoringOptionSet_createFromString
+clang_RefactoringOptionSet_add
+clang_RefactoringOptionSet_toString
+clang_RefactoringOptionSet_dispose
+clang_Refactoring_findActionsAt
+clang_Refactoring_findActionsWithInitiationFailureDiagnosicsAt
+clang_RefactoringAction_dispose
+clang_RefactoringAction_getSourceRangeOfInterest
+clang_RefactoringAction_getInitiatedActionType
+clang_RefactoringAction_requiresImplementationTU
+clang_RefactoringAction_getUSRThatRequiresImplementationTU
+clang_RefactoringAction_addImplementationTU
+clang_RefactoringAction_getRefactoringCandidates
+clang_RefactoringAction_selectRefactoringCandidate
+clang_Refactoring_initiateActionAt
+clang_Refactoring_initiateAction
+clang_Refactoring_initiateActionOnDecl
+clang_Refactoring_initiateRenamingOperation
+clang_RenamingOperation_setNewName
+clang_RenamingOperation_getNumSymbols
+clang_RenamingOperation_getUSRForSymbol
+clang_Refactoring_findRenamedCursor
+clang_Refactoring_findRenamedOccurrencesInPrimaryTUs
+clang_Refactoring_findSymbolOccurrencesInInitiationTU
+clang_Refactoring_findRenamedOccurrencesInIndexedFile
+clang_Refactoring_findSymbolOccurrencesInIndexedFile
+clang_RenamingResult_getNumModifiedFiles
+clang_RenamingResult_getResultForFile
+clang_RenamingResult_dispose
+clang_SymbolOccurrences_getNumFiles
+clang_SymbolOccurrences_getOccurrencesForFile
+clang_SymbolOccurrences_dispose
+clang_Refactoring_performOperation
+clang_RefactoringResult_getReplacements
+clang_RefactoringResult_getSourceReplacements
+clang_RefactoringReplacement_getAssociatedSymbolOccurrences
+clang_RefactoringResult_getContinuation
+clang_RefactoringResult_dispose
+clang_RefactoringContinuation_loadSerializedIndexerQueryResults
+clang_RefactoringContinuation_getNumIndexerQueries
+clang_RefactoringContinuation_getIndexerQuery
+clang_RefactoringContinuation_verifyBeforeFinalizing
+clang_RefactoringContinuation_finalizeEvaluationInInitationTU
+clang_RefactoringContinuation_continueOperationInTU
+clang_RefactoringContinuation_dispose
+clang_IndexerQuery_getKind
+clang_IndexerQuery_getNumCursors
+clang_IndexerQuery_getCursor
+clang_IndexerQuery_consumeIntResult
+clang_IndexerQuery_consumeFileResult
diff --git a/unittests/AST/DeclTest.cpp b/unittests/AST/DeclTest.cpp
index 87aeef4..67b80ac 100644
--- a/unittests/AST/DeclTest.cpp
+++ b/unittests/AST/DeclTest.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "MatchVerifier.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
@@ -57,3 +58,53 @@
"constexpr _Complex __uint128_t c = 0xffffffffffffffff;",
Args));
}
+
+TEST(Decl, Availability) {
+ const char *CodeStr = "int x __attribute__((availability(macosx, "
+ "introduced=10.2, deprecated=10.8, obsoleted=10.10)));";
+ auto Matcher = varDecl(hasName("x"));
+ std::vector<std::string> Args = {"-target", "x86_64-apple-macosx10.9"};
+
+ class AvailabilityVerifier : public MatchVerifier<clang::VarDecl> {
+ public:
+ void verify(const MatchFinder::MatchResult &Result,
+ const clang::VarDecl &Node) override {
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 1)) !=
+ clang::AR_NotYetIntroduced) {
+ setFailure("failed introduced");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 2)) !=
+ clang::AR_Available) {
+ setFailure("failed available (exact)");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 3)) !=
+ clang::AR_Available) {
+ setFailure("failed available");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 8)) !=
+ clang::AR_Deprecated) {
+ setFailure("failed deprecated (exact)");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 9)) !=
+ clang::AR_Deprecated) {
+ setFailure("failed deprecated");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 10)) !=
+ clang::AR_Unavailable) {
+ setFailure("failed obsoleted (exact)");
+ }
+ if (Node.getAvailability(nullptr, clang::VersionTuple(10, 11)) !=
+ clang::AR_Unavailable) {
+ setFailure("failed obsoleted");
+ }
+
+ if (Node.getAvailability() != clang::AR_Deprecated)
+ setFailure("did not default to target OS version");
+
+ setSuccess();
+ }
+ };
+
+ AvailabilityVerifier Verifier;
+ EXPECT_TRUE(Verifier.match(CodeStr, Matcher, Args, Lang_C));
+}
diff --git a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 7bc8421..6037127 100644
--- a/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1108,35 +1108,26 @@
}
TEST(ConstructorDeclaration, Kinds) {
- EXPECT_TRUE(matches(
- "struct S { S(); };",
- cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit()))));
- EXPECT_TRUE(notMatches(
- "struct S { S(); };",
- cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))));
- EXPECT_TRUE(notMatches(
- "struct S { S(); };",
- cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))));
+ EXPECT_TRUE(matches("struct S { S(); };",
+ cxxConstructorDecl(isDefaultConstructor())));
+ EXPECT_TRUE(notMatches("struct S { S(); };",
+ cxxConstructorDecl(isCopyConstructor())));
+ EXPECT_TRUE(notMatches("struct S { S(); };",
+ cxxConstructorDecl(isMoveConstructor())));
- EXPECT_TRUE(notMatches(
- "struct S { S(const S&); };",
- cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit()))));
- EXPECT_TRUE(matches(
- "struct S { S(const S&); };",
- cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))));
- EXPECT_TRUE(notMatches(
- "struct S { S(const S&); };",
- cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))));
+ EXPECT_TRUE(notMatches("struct S { S(const S&); };",
+ cxxConstructorDecl(isDefaultConstructor())));
+ EXPECT_TRUE(matches("struct S { S(const S&); };",
+ cxxConstructorDecl(isCopyConstructor())));
+ EXPECT_TRUE(notMatches("struct S { S(const S&); };",
+ cxxConstructorDecl(isMoveConstructor())));
- EXPECT_TRUE(notMatches(
- "struct S { S(S&&); };",
- cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit()))));
- EXPECT_TRUE(notMatches(
- "struct S { S(S&&); };",
- cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))));
- EXPECT_TRUE(matches(
- "struct S { S(S&&); };",
- cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))));
+ EXPECT_TRUE(notMatches("struct S { S(S&&); };",
+ cxxConstructorDecl(isDefaultConstructor())));
+ EXPECT_TRUE(notMatches("struct S { S(S&&); };",
+ cxxConstructorDecl(isCopyConstructor())));
+ EXPECT_TRUE(matches("struct S { S(S&&); };",
+ cxxConstructorDecl(isMoveConstructor())));
}
TEST(ConstructorDeclaration, IsUserProvided) {
diff --git a/unittests/Frontend/ASTUnitTest.cpp b/unittests/Frontend/ASTUnitTest.cpp
new file mode 100644
index 0000000..a7d08a9
--- /dev/null
+++ b/unittests/Frontend/ASTUnitTest.cpp
@@ -0,0 +1,87 @@
+//===- unittests/Frontend/ASTUnitTest.cpp - ASTUnit tests -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <fstream>
+
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+TEST(ASTUnit, SaveLoadPreservesLangOptionsInPrintingPolicy) {
+ // Check that the printing policy is restored with the correct language
+ // options when loading an ASTUnit from a file. To this end, an ASTUnit
+ // for a C++ translation unit is set up and written to a temporary file.
+
+ // By default `UseVoidForZeroParams` is true for non-C++ language options,
+ // thus we can check this field after loading the ASTUnit to deduce whether
+ // the correct (C++) language options were used when setting up the printing
+ // policy.
+
+ {
+ PrintingPolicy PolicyWithDefaultLangOpt(LangOptions{});
+ EXPECT_TRUE(PolicyWithDefaultLangOpt.UseVoidForZeroParams);
+ }
+
+ int FD;
+ llvm::SmallString<256> InputFileName;
+ ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "cpp", FD, InputFileName));
+ tool_output_file input_file(InputFileName, FD);
+ input_file.os() << "";
+
+ const char* Args[] = {"clang", "-xc++", InputFileName.c_str()};
+
+ IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
+ CompilerInstance::createDiagnostics(new DiagnosticOptions());
+
+ std::shared_ptr<CompilerInvocation> CInvok =
+ createInvocationFromCommandLine(Args, Diags);
+
+ if (!CInvok)
+ FAIL() << "could not create compiler invocation";
+
+ FileManager *FileMgr =
+ new FileManager(FileSystemOptions(), vfs::getRealFileSystem());
+ auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
+
+ std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
+ CInvok, PCHContainerOps, Diags, FileMgr);
+
+ if (!AST)
+ FAIL() << "failed to create ASTUnit";
+
+ EXPECT_FALSE(AST->getASTContext().getPrintingPolicy().UseVoidForZeroParams);
+
+ llvm::SmallString<256> ASTFileName;
+ ASSERT_FALSE(llvm::sys::fs::createTemporaryFile("ast-unit", "ast", FD, ASTFileName));
+ tool_output_file ast_file(ASTFileName, FD);
+ AST->Save(ASTFileName.str());
+
+ EXPECT_TRUE(llvm::sys::fs::exists(ASTFileName));
+
+ std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
+ ASTFileName.str(), PCHContainerOps->getRawReader(), ASTUnit::LoadEverything, Diags,
+ FileSystemOptions(), /*UseDebugInfo=*/false);
+
+ if (!AU)
+ FAIL() << "failed to load ASTUnit";
+
+ EXPECT_FALSE(AU->getASTContext().getPrintingPolicy().UseVoidForZeroParams);
+}
+
+} // anonymous namespace
diff --git a/unittests/Frontend/CMakeLists.txt b/unittests/Frontend/CMakeLists.txt
index 674f77b..4312151 100644
--- a/unittests/Frontend/CMakeLists.txt
+++ b/unittests/Frontend/CMakeLists.txt
@@ -3,6 +3,7 @@
)
add_clang_unittest(FrontendTests
+ ASTUnitTest.cpp
FrontendActionTest.cpp
CodeGenActionTest.cpp
)
diff --git a/unittests/Lex/CMakeLists.txt b/unittests/Lex/CMakeLists.txt
index ef0f06c..2002fbf 100644
--- a/unittests/Lex/CMakeLists.txt
+++ b/unittests/Lex/CMakeLists.txt
@@ -13,6 +13,7 @@
clangAST
clangBasic
clangLex
+ clangAPINotes
clangParse
clangSema
)
diff --git a/unittests/Tooling/RefactoringTest.cpp b/unittests/Tooling/RefactoringTest.cpp
index 1590094..07aef25 100644
--- a/unittests/Tooling/RefactoringTest.cpp
+++ b/unittests/Tooling/RefactoringTest.cpp
@@ -25,6 +25,9 @@
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/Refactor/IndexerQuery.h"
+#include "clang/Tooling/Refactor/RefactoringOperation.h"
+#include "clang/Tooling/Refactor/RefactoringOptions.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Refactoring/AtomicChange.h"
#include "clang/Tooling/Tooling.h"
@@ -1090,6 +1093,51 @@
EXPECT_TRUE(FileToReplaces.empty());
}
+namespace {
+struct TestRefactoringValueOption final : RefactoringOption {
+ int Value;
+ TestRefactoringValueOption(int Value) : Value(Value) {}
+
+ static constexpr const char *Name = "test value option";
+};
+} // end anonymous namespace
+
+TEST(RefactoringOptionSet, AddGet) {
+ RefactoringOptionSet Options;
+ const TestRefactoringValueOption Kind(21);
+ const TestRefactoringValueOption DefaultKind(42);
+
+ EXPECT_EQ(Options.get<TestRefactoringValueOption>(), nullptr);
+ EXPECT_EQ(Options.get(DefaultKind).Value, DefaultKind.Value);
+
+ Options.add(Kind);
+
+ auto *Ptr = Options.get<TestRefactoringValueOption>();
+ ASSERT_TRUE(Ptr);
+ EXPECT_EQ(Ptr->Value, Kind.Value);
+ EXPECT_EQ(Options.get(DefaultKind).Value, Kind.Value);
+}
+
+namespace {
+struct TestRefactoringOption final : RefactoringOption {
+ int &Counter;
+ TestRefactoringOption(int &Counter) : Counter(Counter) {}
+ ~TestRefactoringOption() { ++Counter; }
+
+ static constexpr const char *Name = "test option";
+};
+} // end anonymous namespace
+
+TEST(RefactoringOptionSet, OptionDestroyed) {
+ int Counter = 0;
+ {
+ RefactoringOptionSet Options;
+ Options.add(TestRefactoringOption(Counter));
+ Options.add(TestRefactoringOption(Counter));
+ }
+ EXPECT_EQ(Counter, 3);
+}
+
class AtomicChangeTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -1295,5 +1343,127 @@
Replacement(Context.Sources, SourceLocation(), 0, "b")));
}
+namespace {
+
+class RefactoringOperationTest {
+ RefactoringActionType Type;
+ unsigned Line, Column;
+ bool Success = true;
+ std::function<void(const RefactoringResult &Result)> ResultHandler;
+
+public:
+ RefactoringOperationTest(
+ RefactoringActionType Type, unsigned Line, unsigned Column,
+ std::function<void(const RefactoringResult &Result)> ResultHandler)
+ : Type(Type), Line(Line), Column(Column),
+ ResultHandler(std::move(ResultHandler)) {}
+
+ bool runOver(StringRef Code) {
+ return runToolOnCode(new TestAction(this), Code);
+ }
+
+ bool succeeded() const { return Success; }
+
+ void run() {
+ assert(PP && Context && "Invalid state");
+ SourceLocation Loc = Context->getSourceManager().translateLineCol(
+ Context->getSourceManager().getMainFileID(), Line, Column);
+ if (Loc.isInvalid()) {
+ Success = false;
+ return;
+ }
+ RefactoringOperationResult Op =
+ initiateRefactoringOperationAt(Loc, SourceRange(), *Context, Type);
+ if (!Op.Initiated) {
+ Success = false;
+ return;
+ }
+ RefactoringOptionSet Options;
+ llvm::Expected<RefactoringResult> Result =
+ Op.RefactoringOp->perform(*Context, *PP, Options);
+ if (!Result) {
+ (void)!llvm::handleErrors(
+ Result.takeError(),
+ [&](const RefactoringOperationError &Error) { Success = false; });
+ return;
+ }
+ ResultHandler(Result.get());
+ }
+
+protected:
+ clang::Preprocessor *PP;
+ clang::ASTContext *Context;
+
+private:
+ class TestConsumer : public clang::ASTConsumer {
+ public:
+ TestConsumer(RefactoringOperationTest *Test) : Test(Test) {}
+
+ void HandleTranslationUnit(clang::ASTContext &Context) override {
+ Test->run();
+ }
+
+ private:
+ RefactoringOperationTest *Test;
+ };
+
+ class TestAction : public clang::ASTFrontendAction {
+ public:
+ TestAction(RefactoringOperationTest *Test) : Test(Test) {}
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ llvm::StringRef) override {
+ Test->PP = &Compiler.getPreprocessor();
+ Test->Context = &Compiler.getASTContext();
+ return llvm::make_unique<TestConsumer>(Test);
+ }
+
+ private:
+ RefactoringOperationTest *Test;
+ };
+};
+
+} // end anonymous namespace
+
+TEST(RefactoringContinuation, ContinuationAndQueriesExist) {
+ using namespace clang::tooling::indexer;
+ using namespace clang::tooling::indexer::detail;
+ RefactoringOperationTest Test(
+ RefactoringActionType::ImplementDeclaredMethods, 2, 1,
+ [](const RefactoringResult &Result) {
+ EXPECT_TRUE(Result.Replacements.empty());
+ ASSERT_NE(Result.Continuation, nullptr);
+ RefactoringContinuation &Continuation = *Result.Continuation;
+
+ ASTProducerQuery *ASTQuery = Continuation.getASTUnitIndexerQuery();
+ ASSERT_NE(ASTQuery, nullptr);
+ EXPECT_TRUE(isa<ASTProducerQuery>(ASTQuery));
+ EXPECT_TRUE(isa<ASTUnitForImplementationOfDeclarationQuery>(ASTQuery));
+ EXPECT_FALSE(isa<DeclarationsQuery>(ASTQuery));
+
+ auto AdditionalQueries = Continuation.getAdditionalIndexerQueries();
+ ASSERT_EQ(AdditionalQueries.size(), (size_t)1);
+ EXPECT_FALSE(isa<ASTProducerQuery>(AdditionalQueries[0]));
+ EXPECT_FALSE(isa<ASTUnitForImplementationOfDeclarationQuery>(
+ AdditionalQueries[0]));
+ ASSERT_TRUE(isa<DeclarationsQuery>(AdditionalQueries[0]));
+
+ const DeclPredicateNode &Predicate =
+ cast<DeclarationsQuery>(AdditionalQueries[0])->getPredicateNode();
+ ASSERT_TRUE(isa<DeclPredicateNotPredicate>(Predicate));
+ const DeclPredicateNode &SubPredicate =
+ cast<DeclPredicateNotPredicate>(Predicate).getChild();
+ ASSERT_TRUE(isa<DeclPredicateNodePredicate>(SubPredicate));
+ EXPECT_EQ(cast<DeclPredicateNodePredicate>(SubPredicate).getPredicate(),
+ DeclEntity().isDefined().Predicate);
+
+ ASTQuery->invalidateTUSpecificState();
+ AdditionalQueries[0]->invalidateTUSpecificState();
+ });
+ Test.runOver("class Foo {\nvoid method();\n};\n");
+ EXPECT_TRUE(Test.succeeded());
+}
+
} // end namespace tooling
} // end namespace clang
diff --git a/unittests/libclang/LibclangTest.cpp b/unittests/libclang/LibclangTest.cpp
index f2a96d6..d5a9cbf 100644
--- a/unittests/libclang/LibclangTest.cpp
+++ b/unittests/libclang/LibclangTest.cpp
@@ -8,6 +8,7 @@
//===----------------------------------------------------------------------===//
#include "clang-c/Index.h"
+#include "clang-c/Refactor.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
@@ -572,3 +573,71 @@
EXPECT_EQ(0U, clang_getNumDiagnostics(ClangTU));
DisplayDiagnostics();
}
+
+TEST(libclang, RefactoringAction) {
+ std::string Name =
+ clang_getCString(clang_RefactoringActionType_getName(CXRefactor_Rename));
+ EXPECT_EQ(Name, "Rename");
+}
+
+TEST_F(LibclangParseTest, RefactoringFindRenamedCursor) {
+ std::string Filename = "test.cpp";
+ WriteFile(Filename, "int renamable = 0;\n");
+
+ ClangTU = clang_parseTranslationUnit(Index, Filename.c_str(), nullptr, 0,
+ nullptr, 0, TUFlags);
+ CXSourceLocation Loc = clang_getLocation(
+ ClangTU, clang_getFile(ClangTU, Filename.c_str()), 1, 5);
+ CXSourceRange Range = clang_getRange(Loc, Loc);
+ CXCursor Cursor;
+ EXPECT_EQ(CXError_Success,
+ clang_Refactoring_findRenamedCursor(ClangTU, Loc, Range, &Cursor));
+ EXPECT_EQ(Cursor.kind, CXCursor_VarDecl);
+}
+
+TEST_F(LibclangParseTest, RefactoringRenameIndexedUnsavedFiles) {
+ std::string Filename = "test.cpp";
+ std::string PartialSource = "class Test { };\n";
+ WriteFile(Filename, PartialSource);
+ std::string FullSource = PartialSource + "Test t;\n";
+
+ CXIndexedSymbolLocation IndexedLocations[2] = {
+ {{1, 7}, CXCursor_DeclRefExpr}, {{2, 1}, CXCursor_DeclRefExpr}};
+ CXIndexedSymbol Symbols[1] = {
+ {IndexedLocations, 2, CXCursor_DeclRefExpr, /*Name=*/"Test"}};
+
+ CXIndex Idx = clang_createIndex(0, 0);
+
+ auto test = [&](CXUnsavedFile *File = nullptr) -> CXSymbolOccurrencesInFile {
+ CXSymbolOccurrencesResult Result;
+ CXErrorCode Err = clang_Refactoring_findSymbolOccurrencesInIndexedFile(
+ Symbols, 1, Idx, Filename.c_str(), nullptr, 0, File, File ? 1 : 0,
+ /*Options=*/nullptr, &Result);
+ EXPECT_EQ(CXError_Success, Err);
+ unsigned NumFiles = clang_SymbolOccurrences_getNumFiles(Result);
+ EXPECT_EQ(NumFiles, 1u);
+ CXSymbolOccurrencesInFile Occurrences;
+ clang_SymbolOccurrences_getOccurrencesForFile(Result, 0, &Occurrences);
+ return Occurrences;
+ };
+ CXSymbolOccurrencesInFile FileOccurrences = test();
+ EXPECT_EQ(FileOccurrences.NumOccurrences, 1u);
+ EXPECT_EQ(clang_getCString(FileOccurrences.Filename), Filename);
+ EXPECT_EQ(FileOccurrences.Occurrences[0].NumNamePieces, 1u);
+ EXPECT_EQ(FileOccurrences.Occurrences[0].NamePieces[0].Begin.Line, 1u);
+ EXPECT_EQ(FileOccurrences.Occurrences[0].NamePieces[0].Begin.Column, 7u);
+
+ CXUnsavedFile UnsavedFile = {Filename.c_str(), FullSource.c_str(),
+ FullSource.size()};
+ CXSymbolOccurrencesInFile UnsavedFileOccurrences = test(&UnsavedFile);
+ EXPECT_EQ(UnsavedFileOccurrences.NumOccurrences, 2u);
+ EXPECT_EQ(clang_getCString(UnsavedFileOccurrences.Filename), Filename);
+ EXPECT_EQ(UnsavedFileOccurrences.Occurrences[0].NumNamePieces, 1u);
+ EXPECT_EQ(UnsavedFileOccurrences.Occurrences[0].NamePieces[0].Begin.Line, 1u);
+ EXPECT_EQ(UnsavedFileOccurrences.Occurrences[0].NamePieces[0].Begin.Column,
+ 7u);
+ EXPECT_EQ(UnsavedFileOccurrences.Occurrences[1].NumNamePieces, 1u);
+ EXPECT_EQ(UnsavedFileOccurrences.Occurrences[1].NamePieces[0].Begin.Line, 2u);
+ EXPECT_EQ(UnsavedFileOccurrences.Occurrences[1].NamePieces[0].Begin.Column,
+ 1u);
+}
diff --git a/utils/TableGen/ClangAttrEmitter.cpp b/utils/TableGen/ClangAttrEmitter.cpp
index b6d2988..333bd13 100644
--- a/utils/TableGen/ClangAttrEmitter.cpp
+++ b/utils/TableGen/ClangAttrEmitter.cpp
@@ -1163,6 +1163,32 @@
}
};
+ class AttrArgument : public SimpleArgument {
+ public:
+ AttrArgument(const Record &Arg, StringRef Attr)
+ : SimpleArgument(Arg, Attr, "Attr *")
+ {}
+
+ void writePCHReadDecls(raw_ostream &OS) const override {
+ OS << " AttrVec vec;\n"
+ " ReadAttributes(Record, vec);\n"
+ " assert(vec.size() == 1);\n"
+ " Attr *" << getLowerName() << " = vec.front();";
+ }
+
+ void writePCHWrite(raw_ostream &OS) const override {
+ OS << " AddAttributes(SA->get" << getUpperName() << "());";
+ }
+
+ void writeDump(raw_ostream &OS) const override {}
+
+ void writeDumpChildren(raw_ostream &OS) const override {
+ OS << " dumpAttr(SA->get" << getUpperName() << "());\n";
+ }
+
+ void writeHasChildren(raw_ostream &OS) const override { OS << "true"; }
+ };
+
} // end anonymous namespace
static std::unique_ptr<Argument>
@@ -1212,6 +1238,8 @@
Ptr = llvm::make_unique<VariadicExprArgument>(Arg, Attr);
else if (ArgName == "VersionArgument")
Ptr = llvm::make_unique<VersionArgument>(Arg, Attr);
+ else if (ArgName == "AttrArgument")
+ Ptr = llvm::make_unique<AttrArgument>(Arg, Attr);
if (!Ptr) {
// Search in reverse order so that the most-derived type is handled first.
diff --git a/utils/perf-training/lit.cfg b/utils/perf-training/lit.cfg
index 671d44f..1deb080 100644
--- a/utils/perf-training/lit.cfg
+++ b/utils/perf-training/lit.cfg
@@ -8,7 +8,7 @@
def getSysrootFlagsOnDarwin(config, lit_config):
# On Darwin, support relocatable SDKs by providing Clang with a
# default system root path.
- if 'darwin' in config.target_triple:
+ if lit.util.isMacOSTriple(config.target_triple):
try:
out = subprocess.check_output(['xcrun', '--show-sdk-path']).strip()
res = 0
diff --git a/utils/perf-training/order-files.lit.cfg b/utils/perf-training/order-files.lit.cfg
index 93904ec..2e7acc7 100644
--- a/utils/perf-training/order-files.lit.cfg
+++ b/utils/perf-training/order-files.lit.cfg
@@ -9,7 +9,7 @@
def getSysrootFlagsOnDarwin(config, lit_config):
# On Darwin, support relocatable SDKs by providing Clang with a
# default system root path.
- if 'darwin' in config.target_triple:
+ if lit.util.isMacOSTriple(config.target_triple):
try:
out = subprocess.check_output(['xcrun', '--show-sdk-path']).strip()
res = 0