summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorDavid L. Jones <dlj@google.com>2017-11-10 01:07:01 +0000
committerDavid L. Jones <dlj@google.com>2017-11-10 01:07:01 +0000
commit41af1698c520ea38edf83e7c91f1e519d34f20c1 (patch)
tree05c516cb7514d80a5e8deccb07cd0f7c228b70d4 /tools
parentcd1b175aa96d9d675c09fc54dfd96ba41e3f2279 (diff)
parent4d085086c74a8fbce197f61548f488a63f300933 (diff)
Creating branches/google/testing and tags/google/testing/ from r317203
git-svn-id: https://llvm.org/svn/llvm-project/cfe/branches/google/testing@317856 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt5
-rw-r--r--tools/c-index-test/c-index-test.c169
-rw-r--r--tools/c-index-test/core_main.cpp2
-rw-r--r--tools/clang-check/ClangCheck.cpp3
-rw-r--r--tools/clang-diff/CMakeLists.txt14
-rw-r--r--tools/clang-diff/ClangDiff.cpp537
-rw-r--r--tools/clang-format-vs/.gitignore1
-rw-r--r--tools/clang-format-vs/CMakeLists.txt7
-rw-r--r--tools/clang-format-vs/ClangFormat.sln4
-rw-r--r--tools/clang-format-vs/ClangFormat/ClangFormat.csproj56
-rw-r--r--tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs198
-rw-r--r--tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs79
-rw-r--r--tools/clang-format-vs/ClangFormat/Vsix.cs96
-rw-r--r--tools/clang-format-vs/ClangFormat/license.txt39
-rw-r--r--tools/clang-format-vs/ClangFormat/packages.config1
-rw-r--r--tools/clang-format-vs/source.extension.vsixmanifest.in49
-rw-r--r--tools/clang-format/CMakeLists.txt2
-rw-r--r--tools/clang-format/ClangFormat.cpp56
-rw-r--r--tools/clang-format/clang-format.py19
-rw-r--r--tools/clang-format/fuzzer/CMakeLists.txt6
-rw-r--r--tools/clang-format/fuzzer/ClangFormatFuzzer.cpp7
-rwxr-xr-xtools/clang-format/git-clang-format87
-rw-r--r--tools/clang-func-mapping/CMakeLists.txt22
-rw-r--r--tools/clang-func-mapping/ClangFnMapGen.cpp124
-rw-r--r--tools/clang-fuzzer/CMakeLists.txt81
-rw-r--r--tools/clang-fuzzer/ClangFuzzer.cpp32
-rw-r--r--tools/clang-fuzzer/Dockerfile37
-rw-r--r--tools/clang-fuzzer/DummyClangFuzzer.cpp21
-rw-r--r--tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp44
-rw-r--r--tools/clang-fuzzer/README.txt82
-rw-r--r--tools/clang-fuzzer/cxx_proto.proto93
-rw-r--r--tools/clang-fuzzer/handle-cxx/CMakeLists.txt12
-rw-r--r--tools/clang-fuzzer/handle-cxx/handle_cxx.cpp58
-rw-r--r--tools/clang-fuzzer/handle-cxx/handle_cxx.h25
-rw-r--r--tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt14
-rw-r--r--tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp102
-rw-r--r--tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h22
-rw-r--r--tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp30
-rw-r--r--tools/clang-import-test/CMakeLists.txt1
-rw-r--r--tools/clang-import-test/clang-import-test.cpp235
-rw-r--r--tools/clang-offload-bundler/ClangOffloadBundler.cpp9
-rw-r--r--tools/clang-refactor/CMakeLists.txt23
-rw-r--r--tools/clang-refactor/ClangRefactor.cpp577
-rw-r--r--tools/clang-refactor/TestSupport.cpp392
-rw-r--r--tools/clang-refactor/TestSupport.h107
-rw-r--r--tools/clang-refactor/ToolRefactoringResultConsumer.h48
-rw-r--r--tools/clang-rename/CMakeLists.txt22
-rw-r--r--tools/clang-rename/ClangRename.cpp233
-rw-r--r--tools/clang-rename/clang-rename.el79
-rw-r--r--tools/clang-rename/clang-rename.py61
-rw-r--r--tools/diagtool/CMakeLists.txt1
-rw-r--r--tools/diagtool/DiagnosticNames.cpp12
-rw-r--r--tools/diagtool/DiagnosticNames.h6
-rw-r--r--tools/diagtool/FindDiagnosticID.cpp74
-rw-r--r--tools/diagtool/ListWarnings.cpp56
-rw-r--r--tools/diagtool/ShowEnabledWarnings.cpp24
-rw-r--r--tools/diagtool/TreeView.cpp59
-rw-r--r--tools/driver/CMakeLists.txt2
-rw-r--r--tools/driver/cc1as_main.cpp36
-rw-r--r--tools/driver/driver.cpp104
-rw-r--r--tools/libclang/ARCMigrate.cpp5
-rw-r--r--tools/libclang/CIndex.cpp310
-rw-r--r--tools/libclang/CIndexCodeCompletion.cpp9
-rw-r--r--tools/libclang/CIndexDiagnostic.cpp30
-rw-r--r--tools/libclang/CMakeLists.txt14
-rw-r--r--tools/libclang/CXCompilationDatabase.cpp31
-rw-r--r--tools/libclang/CXCursor.cpp26
-rw-r--r--tools/libclang/CXIndexDataConsumer.cpp6
-rw-r--r--tools/libclang/CXTranslationUnit.h4
-rw-r--r--tools/libclang/CXType.cpp74
-rw-r--r--tools/libclang/Indexing.cpp6
-rw-r--r--tools/libclang/libclang.exports14
-rw-r--r--tools/scan-build-py/libscanbuild/__init__.py6
-rw-r--r--tools/scan-build-py/libscanbuild/analyze.py313
-rw-r--r--tools/scan-build-py/libscanbuild/arguments.py5
-rw-r--r--tools/scan-build-py/libscanbuild/intercept.py4
-rw-r--r--tools/scan-build-py/libscanbuild/report.py37
-rw-r--r--tools/scan-build-py/libscanbuild/runner.py294
-rw-r--r--tools/scan-build-py/tests/unit/__init__.py2
-rw-r--r--tools/scan-build-py/tests/unit/test_analyze.py313
-rw-r--r--tools/scan-build-py/tests/unit/test_report.py2
-rw-r--r--tools/scan-build-py/tests/unit/test_runner.py321
-rw-r--r--tools/scan-build/CMakeLists.txt3
-rwxr-xr-xtools/scan-build/libexec/ccc-analyzer3
84 files changed, 4955 insertions, 1274 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index b0c97f0f1e..9f76d36dba 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -2,6 +2,7 @@ create_subdirectory_options(CLANG TOOL)
add_clang_subdirectory(diagtool)
add_clang_subdirectory(driver)
+add_clang_subdirectory(clang-diff)
add_clang_subdirectory(clang-format)
add_clang_subdirectory(clang-format-vs)
add_clang_subdirectory(clang-fuzzer)
@@ -10,6 +11,9 @@ add_clang_subdirectory(clang-offload-bundler)
add_clang_subdirectory(c-index-test)
+add_clang_subdirectory(clang-rename)
+add_clang_subdirectory(clang-refactor)
+
if(CLANG_ENABLE_ARCMT)
add_clang_subdirectory(arcmt-test)
add_clang_subdirectory(c-arcmt-test)
@@ -17,6 +21,7 @@ endif()
if(CLANG_ENABLE_STATIC_ANALYZER)
add_clang_subdirectory(clang-check)
+ add_clang_subdirectory(clang-func-mapping)
add_clang_subdirectory(scan-build)
add_clang_subdirectory(scan-view)
endif()
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index b6e6ded9cc..a11e03c846 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -710,6 +710,16 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) {
clang_getSpellingLocation(Loc, 0, &line, &column, 0);
printf(":%d:%d", line, column);
}
+
+ if (clang_getCursorKind(Referenced) == CXCursor_TypedefDecl) {
+ CXType T = clang_getCursorType(Referenced);
+ if (clang_Type_isTransparentTagTypedef(T)) {
+ CXType Underlying = clang_getTypedefDeclUnderlyingType(Referenced);
+ CXString S = clang_getTypeSpelling(Underlying);
+ printf(" (Transparent: %s)", clang_getCString(S));
+ clang_disposeString(S);
+ }
+ }
}
if (clang_isCursorDefinition(Cursor))
@@ -794,11 +804,57 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) {
printf(" (const)");
if (clang_CXXMethod_isPureVirtual(Cursor))
printf(" (pure)");
+ if (clang_EnumDecl_isScoped(Cursor))
+ printf(" (scoped)");
if (clang_Cursor_isVariadic(Cursor))
printf(" (variadic)");
if (clang_Cursor_isObjCOptional(Cursor))
printf(" (@optional)");
+ switch (clang_getCursorExceptionSpecificationType(Cursor))
+ {
+ case CXCursor_ExceptionSpecificationKind_None:
+ break;
+
+ case CXCursor_ExceptionSpecificationKind_DynamicNone:
+ printf(" (noexcept dynamic none)");
+ break;
+
+ case CXCursor_ExceptionSpecificationKind_Dynamic:
+ printf(" (noexcept dynamic)");
+ break;
+
+ case CXCursor_ExceptionSpecificationKind_MSAny:
+ printf(" (noexcept dynamic any)");
+ break;
+
+ case CXCursor_ExceptionSpecificationKind_BasicNoexcept:
+ printf(" (noexcept)");
+ break;
+
+ case CXCursor_ExceptionSpecificationKind_ComputedNoexcept:
+ printf(" (computed-noexcept)");
+ break;
+
+ case CXCursor_ExceptionSpecificationKind_Unevaluated:
+ case CXCursor_ExceptionSpecificationKind_Uninstantiated:
+ case CXCursor_ExceptionSpecificationKind_Unparsed:
+ break;
+ }
+
+ {
+ CXString language;
+ CXString definedIn;
+ unsigned generated;
+ if (clang_Cursor_isExternalSymbol(Cursor, &language, &definedIn,
+ &generated)) {
+ printf(" (external lang: %s, defined: %s, gen: %d)",
+ clang_getCString(language), clang_getCString(definedIn), generated);
+ clang_disposeString(language);
+ clang_disposeString(definedIn);
+ }
+ }
+
if (Cursor.kind == CXCursor_IBOutletCollectionAttr) {
CXType T =
clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor));
@@ -1507,10 +1563,19 @@ static enum CXChildVisitResult PrintManglings(CXCursor cursor, CXCursor p,
return CXChildVisit_Continue;
PrintCursor(cursor, NULL);
Manglings = clang_Cursor_getCXXManglings(cursor);
- for (I = 0, E = Manglings->Count; I < E; ++I)
- printf(" [mangled=%s]", clang_getCString(Manglings->Strings[I]));
- clang_disposeStringSet(Manglings);
- printf("\n");
+ if (Manglings) {
+ for (I = 0, E = Manglings->Count; I < E; ++I)
+ printf(" [mangled=%s]", clang_getCString(Manglings->Strings[I]));
+ clang_disposeStringSet(Manglings);
+ printf("\n");
+ }
+ Manglings = clang_Cursor_getObjCManglings(cursor);
+ if (Manglings) {
+ for (I = 0, E = Manglings->Count; I < E; ++I)
+ printf(" [mangled=%s]", clang_getCString(Manglings->Strings[I]));
+ clang_disposeStringSet(Manglings);
+ printf("\n");
+ }
return CXChildVisit_Recurse;
}
@@ -1550,6 +1615,51 @@ static enum CXChildVisitResult PrintTypeDeclaration(CXCursor cursor, CXCursor p,
}
/******************************************************************************/
+/* Target information testing. */
+/******************************************************************************/
+
+static int print_target_info(int argc, const char **argv) {
+ CXIndex Idx;
+ CXTranslationUnit TU;
+ CXTargetInfo TargetInfo;
+ CXString Triple;
+ const char *FileName;
+ enum CXErrorCode Err;
+ int PointerWidth;
+
+ if (argc == 0) {
+ fprintf(stderr, "No filename specified\n");
+ return 1;
+ }
+
+ FileName = argv[1];
+
+ Idx = clang_createIndex(0, 1);
+ Err = clang_parseTranslationUnit2(Idx, FileName, argv, argc, NULL, 0,
+ getDefaultParsingOptions(), &TU);
+ if (Err != CXError_Success) {
+ fprintf(stderr, "Couldn't parse translation unit!\n");
+ describeLibclangFailure(Err);
+ clang_disposeIndex(Idx);
+ return 1;
+ }
+
+ TargetInfo = clang_getTranslationUnitTargetInfo(TU);
+
+ Triple = clang_TargetInfo_getTriple(TargetInfo);
+ printf("TargetTriple: %s\n", clang_getCString(Triple));
+ clang_disposeString(Triple);
+
+ PointerWidth = clang_TargetInfo_getPointerWidth(TargetInfo);
+ printf("PointerWidth: %d\n", PointerWidth);
+
+ clang_TargetInfo_dispose(TargetInfo);
+ clang_disposeTranslationUnit(TU);
+ clang_disposeIndex(Idx);
+ return 0;
+}
+
+/******************************************************************************/
/* Loading ASTs/source. */
/******************************************************************************/
@@ -1674,6 +1784,8 @@ int perform_test_load_source(int argc, const char **argv,
return -1;
if (Repeats > 1) {
+ clang_suspendTranslationUnit(TU);
+
Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files,
clang_defaultReparseOptions(TU));
if (Err != CXError_Success) {
@@ -1780,6 +1892,34 @@ int perform_test_reparse_source(int argc, const char **argv, int trials,
return result;
}
+static int perform_single_file_parse(const char *filename) {
+ CXIndex Idx;
+ CXTranslationUnit TU;
+ enum CXErrorCode Err;
+ int result;
+
+ Idx = clang_createIndex(/* excludeDeclsFromPCH */1,
+ /* displayDiagnostics=*/1);
+
+ Err = clang_parseTranslationUnit2(Idx, filename,
+ /*command_line_args=*/NULL,
+ /*num_command_line_args=*/0,
+ /*unsaved_files=*/NULL,
+ /*num_unsaved_files=*/0,
+ CXTranslationUnit_SingleFileParse, &TU);
+ if (Err != CXError_Success) {
+ fprintf(stderr, "Unable to load translation unit!\n");
+ describeLibclangFailure(Err);
+ clang_disposeIndex(Idx);
+ return 1;
+ }
+
+ result = perform_test_load(Idx, TU, /*filter=*/"all", /*prefix=*/NULL, FilteredPrintingVisitor, /*PostVisit=*/NULL,
+ /*CommentSchemaFile=*/NULL);
+ clang_disposeIndex(Idx);
+ return result;
+}
+
/******************************************************************************/
/* Logic for testing clang_getCursor(). */
/******************************************************************************/
@@ -2427,11 +2567,14 @@ static void inspect_print_cursor(CXCursor Cursor) {
clang_Cursor_getObjCSelectorIndex(Cursor));
if (clang_Cursor_isDynamicCall(Cursor))
printf(" Dynamic-call");
- if (Cursor.kind == CXCursor_ObjCMessageExpr) {
+ if (Cursor.kind == CXCursor_ObjCMessageExpr ||
+ Cursor.kind == CXCursor_MemberRefExpr) {
CXType T = clang_Cursor_getReceiverType(Cursor);
- CXString S = clang_getTypeKindSpelling(T.kind);
- printf(" Receiver-type=%s", clang_getCString(S));
- clang_disposeString(S);
+ if (T.kind != CXType_Invalid) {
+ CXString S = clang_getTypeKindSpelling(T.kind);
+ printf(" Receiver-type=%s", clang_getCString(S));
+ clang_disposeString(S);
+ }
}
{
@@ -3004,6 +3147,7 @@ static const char *getEntityLanguageString(CXIdxEntityLanguage kind) {
case CXIdxEntityLang_C: return "C";
case CXIdxEntityLang_ObjC: return "ObjC";
case CXIdxEntityLang_CXX: return "C++";
+ case CXIdxEntityLang_Swift: return "Swift";
}
assert(0 && "Garbage language kind");
return 0;
@@ -4287,11 +4431,12 @@ static void print_usage(void) {
" c-index-test -test-print-type {<args>}*\n"
" c-index-test -test-print-type-size {<args>}*\n"
" c-index-test -test-print-bitwidth {<args>}*\n"
+ " c-index-test -test-print-target-info {<args>}*\n"
" c-index-test -test-print-type-declaration {<args>}*\n"
" c-index-test -print-usr [<CursorKind> {<args>}]*\n"
- " c-index-test -print-usr-file <file>\n"
- " c-index-test -write-pch <file> <compiler arguments>\n");
+ " c-index-test -print-usr-file <file>\n");
fprintf(stderr,
+ " c-index-test -write-pch <file> <compiler arguments>\n"
" c-index-test -compilation-db [lookup <filename>] database\n");
fprintf(stderr,
" c-index-test -print-build-session-timestamp\n");
@@ -4364,6 +4509,8 @@ int cindextest_main(int argc, const char **argv) {
return perform_test_load_source(argc - 3, argv + 3, argv[2], I,
postVisit);
}
+ else if (argc >= 3 && strcmp(argv[1], "-single-file-parse") == 0)
+ return perform_single_file_parse(argv[2]);
else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0)
return perform_file_scan(argv[2], argv[3],
argc >= 5 ? argv[4] : 0);
@@ -4397,6 +4544,8 @@ int cindextest_main(int argc, const char **argv) {
return perform_test_load_tu(argv[2], "all", NULL, PrintMangledName, NULL);
else if (argc > 2 && strcmp(argv[1], "-test-print-manglings") == 0)
return perform_test_load_tu(argv[2], "all", NULL, PrintManglings, NULL);
+ else if (argc > 2 && strcmp(argv[1], "-test-print-target-info") == 0)
+ return print_target_info(argc - 2, argv + 2);
else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) {
if (argc > 2)
return print_usrs(argv + 2, argv + argc);
diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp
index 4f2c3cb34a..c255f54ba6 100644
--- a/tools/c-index-test/core_main.cpp
+++ b/tools/c-index-test/core_main.cpp
@@ -217,7 +217,7 @@ static bool printSourceSymbolsFromModule(StringRef modulePath,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(new DiagnosticOptions());
std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
- modulePath, *pchRdr, Diags,
+ modulePath, *pchRdr, ASTUnit::LoadASTOnly, Diags,
FileSystemOpts, /*UseDebugInfo=*/false,
/*OnlyLocalDecls=*/true, None,
/*CaptureDiagnostics=*/false,
diff --git a/tools/clang-check/ClangCheck.cpp b/tools/clang-check/ClangCheck.cpp
index dac11ff077..e190c0721a 100644
--- a/tools/clang-check/ClangCheck.cpp
+++ b/tools/clang-check/ClangCheck.cpp
@@ -124,8 +124,7 @@ public:
/// \c FixItRewriter.
class FixItAction : public clang::FixItAction {
public:
- bool BeginSourceFileAction(clang::CompilerInstance& CI,
- StringRef Filename) override {
+ bool BeginSourceFileAction(clang::CompilerInstance& CI) override {
FixItOpts.reset(new FixItOptions);
Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(),
CI.getLangOpts(), FixItOpts.get()));
diff --git a/tools/clang-diff/CMakeLists.txt b/tools/clang-diff/CMakeLists.txt
new file mode 100644
index 0000000000..a1fc6275be
--- /dev/null
+++ b/tools/clang-diff/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS
+ Support
+ )
+
+add_clang_executable(clang-diff
+ ClangDiff.cpp
+ )
+
+target_link_libraries(clang-diff
+ clangBasic
+ clangFrontend
+ clangTooling
+ clangToolingASTDiff
+ )
diff --git a/tools/clang-diff/ClangDiff.cpp b/tools/clang-diff/ClangDiff.cpp
new file mode 100644
index 0000000000..55cef91170
--- /dev/null
+++ b/tools/clang-diff/ClangDiff.cpp
@@ -0,0 +1,537 @@
+//===- ClangDiff.cpp - compare source files by AST 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 implements a tool for syntax tree based comparison using
+// Tooling/ASTDiff.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/ASTDiff/ASTDiff.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::tooling;
+
+static cl::OptionCategory ClangDiffCategory("clang-diff options");
+
+static cl::opt<bool>
+ ASTDump("ast-dump",
+ cl::desc("Print the internal representation of the AST."),
+ cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt<bool> ASTDumpJson(
+ "ast-dump-json",
+ cl::desc("Print the internal representation of the AST as JSON."),
+ cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt<bool>
+ PrintMatches("dump-matches", cl::desc("Print the matched nodes."),
+ cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt<bool> HtmlDiff("html",
+ cl::desc("Output a side-by-side diff in HTML."),
+ cl::init(false), cl::cat(ClangDiffCategory));
+
+static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"),
+ cl::Required,
+ cl::cat(ClangDiffCategory));
+
+static cl::opt<std::string> DestinationPath(cl::Positional,
+ cl::desc("<destination>"),
+ cl::Optional,
+ cl::cat(ClangDiffCategory));
+
+static cl::opt<std::string> StopAfter("stop-diff-after",
+ cl::desc("<topdown|bottomup>"),
+ cl::Optional, cl::init(""),
+ cl::cat(ClangDiffCategory));
+
+static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional,
+ cl::init(-1), cl::cat(ClangDiffCategory));
+
+static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""),
+ cl::Optional, cl::cat(ClangDiffCategory));
+
+static cl::list<std::string> ArgsAfter(
+ "extra-arg",
+ cl::desc("Additional argument to append to the compiler command line"),
+ cl::cat(ClangDiffCategory));
+
+static cl::list<std::string> ArgsBefore(
+ "extra-arg-before",
+ cl::desc("Additional argument to prepend to the compiler command line"),
+ cl::cat(ClangDiffCategory));
+
+static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) {
+ if (!Compilations)
+ return;
+ auto AdjustingCompilations =
+ llvm::make_unique<ArgumentsAdjustingCompilations>(
+ std::move(Compilations));
+ AdjustingCompilations->appendArgumentsAdjuster(
+ getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN));
+ AdjustingCompilations->appendArgumentsAdjuster(
+ getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END));
+ Compilations = std::move(AdjustingCompilations);
+}
+
+static std::unique_ptr<ASTUnit>
+getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations,
+ const StringRef Filename) {
+ std::string ErrorMessage;
+ std::unique_ptr<CompilationDatabase> Compilations;
+ if (!CommonCompilations) {
+ Compilations = CompilationDatabase::autoDetectFromSource(
+ BuildPath.empty() ? Filename : BuildPath, ErrorMessage);
+ if (!Compilations) {
+ llvm::errs()
+ << "Error while trying to load a compilation database, running "
+ "without flags.\n"
+ << ErrorMessage;
+ Compilations =
+ llvm::make_unique<clang::tooling::FixedCompilationDatabase>(
+ ".", std::vector<std::string>());
+ }
+ }
+ addExtraArgs(Compilations);
+ std::array<std::string, 1> Files = {{Filename}};
+ ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files);
+ std::vector<std::unique_ptr<ASTUnit>> ASTs;
+ Tool.buildASTs(ASTs);
+ if (ASTs.size() != Files.size())
+ return nullptr;
+ return std::move(ASTs[0]);
+}
+
+static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); }
+
+static const char HtmlDiffHeader[] = R"(
+<html>
+<head>
+<meta charset='utf-8'/>
+<style>
+span.d { color: red; }
+span.u { color: #cc00cc; }
+span.i { color: green; }
+span.m { font-weight: bold; }
+span { font-weight: normal; color: black; }
+div.code {
+ width: 48%;
+ height: 98%;
+ overflow: scroll;
+ float: left;
+ padding: 0 0 0.5% 0.5%;
+ border: solid 2px LightGrey;
+ border-radius: 5px;
+}
+</style>
+</head>
+<script type='text/javascript'>
+highlightStack = []
+function clearHighlight() {
+ while (highlightStack.length) {
+ var [l, r] = highlightStack.pop()
+ document.getElementById(l).style.backgroundColor = 'inherit'
+ if (r[1] != '-')
+ document.getElementById(r).style.backgroundColor = 'inherit'
+ }
+}
+function highlight(event) {
+ var id = event.target['id']
+ doHighlight(id)
+}
+function doHighlight(id) {
+ clearHighlight()
+ source = document.getElementById(id)
+ if (!source.attributes['tid'])
+ return
+ var mapped = source
+ while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1')
+ mapped = mapped.parentElement
+ var tid = null, target = null
+ if (mapped) {
+ tid = mapped.attributes['tid'].value
+ target = document.getElementById(tid)
+ }
+ if (source.parentElement && source.parentElement.classList.contains('code'))
+ return
+ source.style.backgroundColor = 'lightgrey'
+ source.scrollIntoView()
+ if (target) {
+ if (mapped === source)
+ target.style.backgroundColor = 'lightgrey'
+ target.scrollIntoView()
+ }
+ highlightStack.push([id, tid])
+ location.hash = '#' + id
+}
+function scrollToBoth() {
+ doHighlight(location.hash.substr(1))
+}
+function changed(elem) {
+ return elem.classList.length == 0
+}
+function nextChangedNode(prefix, increment, number) {
+ do {
+ number += increment
+ var elem = document.getElementById(prefix + number)
+ } while(elem && !changed(elem))
+ return elem ? number : null
+}
+function handleKey(e) {
+ var down = e.code === "KeyJ"
+ var up = e.code === "KeyK"
+ if (!down && !up)
+ return
+ var id = highlightStack[0] ? highlightStack[0][0] : 'R0'
+ var oldelem = document.getElementById(id)
+ var number = parseInt(id.substr(1))
+ var increment = down ? 1 : -1
+ var lastnumber = number
+ var prefix = id[0]
+ do {
+ number = nextChangedNode(prefix, increment, number)
+ var elem = document.getElementById(prefix + number)
+ if (up && elem) {
+ while (elem.parentElement && changed(elem.parentElement))
+ elem = elem.parentElement
+ number = elem.id.substr(1)
+ }
+ } while ((down && id !== 'R0' && oldelem.contains(elem)))
+ if (!number)
+ number = lastnumber
+ elem = document.getElementById(prefix + number)
+ doHighlight(prefix + number)
+}
+window.onload = scrollToBoth
+window.onkeydown = handleKey
+</script>
+<body>
+<div onclick='highlight(event)'>
+)";
+
+static void printHtml(raw_ostream &OS, char C) {
+ switch (C) {
+ case '&':
+ OS << "&amp;";
+ break;
+ case '<':
+ OS << "&lt;";
+ break;
+ case '>':
+ OS << "&gt;";
+ break;
+ case '\'':
+ OS << "&#x27;";
+ break;
+ case '"':
+ OS << "&quot;";
+ break;
+ default:
+ OS << C;
+ }
+}
+
+static void printHtml(raw_ostream &OS, const StringRef Str) {
+ for (char C : Str)
+ printHtml(OS, C);
+}
+
+static std::string getChangeKindAbbr(diff::ChangeKind Kind) {
+ switch (Kind) {
+ case diff::None:
+ return "";
+ case diff::Delete:
+ return "d";
+ case diff::Update:
+ return "u";
+ case diff::Insert:
+ return "i";
+ case diff::Move:
+ return "m";
+ case diff::UpdateMove:
+ return "u m";
+ }
+ llvm_unreachable("Invalid enumeration value.");
+}
+
+static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff,
+ diff::SyntaxTree &Tree, bool IsLeft,
+ diff::NodeId Id, unsigned Offset) {
+ const diff::Node &Node = Tree.getNode(Id);
+ char MyTag, OtherTag;
+ diff::NodeId LeftId, RightId;
+ diff::NodeId TargetId = Diff.getMapped(Tree, Id);
+ if (IsLeft) {
+ MyTag = 'L';
+ OtherTag = 'R';
+ LeftId = Id;
+ RightId = TargetId;
+ } else {
+ MyTag = 'R';
+ OtherTag = 'L';
+ LeftId = TargetId;
+ RightId = Id;
+ }
+ unsigned Begin, End;
+ std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node);
+ const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager();
+ auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer();
+ for (; Offset < Begin; ++Offset)
+ printHtml(OS, Code[Offset]);
+ OS << "<span id='" << MyTag << Id << "' "
+ << "tid='" << OtherTag << TargetId << "' ";
+ OS << "title='";
+ printHtml(OS, Node.getTypeLabel());
+ OS << "\n" << LeftId << " -> " << RightId;
+ std::string Value = Tree.getNodeValue(Node);
+ if (!Value.empty()) {
+ OS << "\n";
+ printHtml(OS, Value);
+ }
+ OS << "'";
+ if (Node.Change != diff::None)
+ OS << " class='" << getChangeKindAbbr(Node.Change) << "'";
+ OS << ">";
+
+ for (diff::NodeId Child : Node.Children)
+ Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset);
+
+ for (; Offset < End; ++Offset)
+ printHtml(OS, Code[Offset]);
+ if (Id == Tree.getRootId()) {
+ End = Code.size();
+ for (; Offset < End; ++Offset)
+ printHtml(OS, Code[Offset]);
+ }
+ OS << "</span>";
+ return Offset;
+}
+
+static void printJsonString(raw_ostream &OS, const StringRef Str) {
+ for (signed char C : Str) {
+ switch (C) {
+ case '"':
+ OS << R"(\")";
+ break;
+ case '\\':
+ OS << R"(\\)";
+ break;
+ case '\n':
+ OS << R"(\n)";
+ break;
+ case '\t':
+ OS << R"(\t)";
+ break;
+ default:
+ if ('\x00' <= C && C <= '\x1f') {
+ OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C);
+ } else {
+ OS << C;
+ }
+ }
+ }
+}
+
+static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree,
+ diff::NodeId Id) {
+ const diff::Node &N = Tree.getNode(Id);
+ OS << R"("id":)" << int(Id);
+ OS << R"(,"type":")" << N.getTypeLabel() << '"';
+ auto Offsets = Tree.getSourceRangeOffsets(N);
+ OS << R"(,"begin":)" << Offsets.first;
+ OS << R"(,"end":)" << Offsets.second;
+ std::string Value = Tree.getNodeValue(N);
+ if (!Value.empty()) {
+ OS << R"(,"value":")";
+ printJsonString(OS, Value);
+ OS << '"';
+ }
+}
+
+static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree,
+ diff::NodeId Id) {
+ const diff::Node &N = Tree.getNode(Id);
+ OS << "{";
+ printNodeAttributes(OS, Tree, Id);
+ auto Identifier = N.getIdentifier();
+ auto QualifiedIdentifier = N.getQualifiedIdentifier();
+ if (Identifier) {
+ OS << R"(,"identifier":")";
+ printJsonString(OS, *Identifier);
+ OS << R"(")";
+ if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) {
+ OS << R"(,"qualified_identifier":")";
+ printJsonString(OS, *QualifiedIdentifier);
+ OS << R"(")";
+ }
+ }
+ OS << R"(,"children":[)";
+ if (N.Children.size() > 0) {
+ printNodeAsJson(OS, Tree, N.Children[0]);
+ for (size_t I = 1, E = N.Children.size(); I < E; ++I) {
+ OS << ",";
+ printNodeAsJson(OS, Tree, N.Children[I]);
+ }
+ }
+ OS << "]}";
+}
+
+static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree,
+ diff::NodeId Id) {
+ if (Id.isInvalid()) {
+ OS << "None";
+ return;
+ }
+ OS << Tree.getNode(Id).getTypeLabel();
+ std::string Value = Tree.getNodeValue(Id);
+ if (!Value.empty())
+ OS << ": " << Value;
+ OS << "(" << Id << ")";
+}
+
+static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) {
+ for (diff::NodeId Id : Tree) {
+ for (int I = 0; I < Tree.getNode(Id).Depth; ++I)
+ OS << " ";
+ printNode(OS, Tree, Id);
+ OS << "\n";
+ }
+}
+
+static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff,
+ diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree,
+ diff::NodeId Dst) {
+ const diff::Node &DstNode = DstTree.getNode(Dst);
+ diff::NodeId Src = Diff.getMapped(DstTree, Dst);
+ switch (DstNode.Change) {
+ case diff::None:
+ break;
+ case diff::Delete:
+ llvm_unreachable("The destination tree can't have deletions.");
+ case diff::Update:
+ OS << "Update ";
+ printNode(OS, SrcTree, Src);
+ OS << " to " << DstTree.getNodeValue(Dst) << "\n";
+ break;
+ case diff::Insert:
+ case diff::Move:
+ case diff::UpdateMove:
+ if (DstNode.Change == diff::Insert)
+ OS << "Insert";
+ else if (DstNode.Change == diff::Move)
+ OS << "Move";
+ else if (DstNode.Change == diff::UpdateMove)
+ OS << "Update and Move";
+ OS << " ";
+ printNode(OS, DstTree, Dst);
+ OS << " into ";
+ printNode(OS, DstTree, DstNode.Parent);
+ OS << " at " << DstTree.findPositionInParent(Dst) << "\n";
+ break;
+ }
+}
+
+int main(int argc, const char **argv) {
+ std::string ErrorMessage;
+ std::unique_ptr<CompilationDatabase> CommonCompilations =
+ FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage);
+ if (!CommonCompilations && !ErrorMessage.empty())
+ llvm::errs() << ErrorMessage;
+ cl::HideUnrelatedOptions(ClangDiffCategory);
+ if (!cl::ParseCommandLineOptions(argc, argv)) {
+ cl::PrintOptionValues();
+ return 1;
+ }
+
+ addExtraArgs(CommonCompilations);
+
+ if (ASTDump || ASTDumpJson) {
+ if (!DestinationPath.empty()) {
+ llvm::errs() << "Error: Please specify exactly one filename.\n";
+ return 1;
+ }
+ std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath);
+ if (!AST)
+ return 1;
+ diff::SyntaxTree Tree(AST->getASTContext());
+ if (ASTDump) {
+ printTree(llvm::outs(), Tree);
+ return 0;
+ }
+ llvm::outs() << R"({"filename":")";
+ printJsonString(llvm::outs(), SourcePath);
+ llvm::outs() << R"(","root":)";
+ printNodeAsJson(llvm::outs(), Tree, Tree.getRootId());
+ llvm::outs() << "}\n";
+ return 0;
+ }
+
+ if (DestinationPath.empty()) {
+ llvm::errs() << "Error: Exactly two paths are required.\n";
+ return 1;
+ }
+
+ std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath);
+ std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath);
+ if (!Src || !Dst)
+ return 1;
+
+ diff::ComparisonOptions Options;
+ if (MaxSize != -1)
+ Options.MaxSize = MaxSize;
+ if (!StopAfter.empty()) {
+ if (StopAfter == "topdown")
+ Options.StopAfterTopDown = true;
+ else if (StopAfter != "bottomup") {
+ llvm::errs() << "Error: Invalid argument for -stop-after\n";
+ return 1;
+ }
+ }
+ diff::SyntaxTree SrcTree(Src->getASTContext());
+ diff::SyntaxTree DstTree(Dst->getASTContext());
+ diff::ASTDiff Diff(SrcTree, DstTree, Options);
+
+ if (HtmlDiff) {
+ llvm::outs() << HtmlDiffHeader << "<pre>";
+ llvm::outs() << "<div id='L' class='code'>";
+ printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0);
+ llvm::outs() << "</div>";
+ llvm::outs() << "<div id='R' class='code'>";
+ printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(),
+ 0);
+ llvm::outs() << "</div>";
+ llvm::outs() << "</pre></div></body></html>\n";
+ return 0;
+ }
+
+ for (diff::NodeId Dst : DstTree) {
+ diff::NodeId Src = Diff.getMapped(DstTree, Dst);
+ if (PrintMatches && Src.isValid()) {
+ llvm::outs() << "Match ";
+ printNode(llvm::outs(), SrcTree, Src);
+ llvm::outs() << " to ";
+ printNode(llvm::outs(), DstTree, Dst);
+ llvm::outs() << "\n";
+ }
+ printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst);
+ }
+ for (diff::NodeId Src : SrcTree) {
+ if (Diff.getMapped(SrcTree, Src).isInvalid()) {
+ llvm::outs() << "Delete ";
+ printNode(llvm::outs(), SrcTree, Src);
+ llvm::outs() << "\n";
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/clang-format-vs/.gitignore b/tools/clang-format-vs/.gitignore
index c9d25d96c8..270d840cb6 100644
--- a/tools/clang-format-vs/.gitignore
+++ b/tools/clang-format-vs/.gitignore
@@ -7,6 +7,5 @@
# Generated and copied files
/ClangFormat/Key.snk
-/ClangFormat/license.txt
/ClangFormat/clang-format.exe
/ClangFormat/source.extension.vsixmanifest
diff --git a/tools/clang-format-vs/CMakeLists.txt b/tools/clang-format-vs/CMakeLists.txt
index d07a4f6ed5..1d44a47a31 100644
--- a/tools/clang-format-vs/CMakeLists.txt
+++ b/tools/clang-format-vs/CMakeLists.txt
@@ -6,11 +6,6 @@ if (BUILD_CLANG_FORMAT_VS_PLUGIN)
"${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/clang-format.exe"
DEPENDS clang-format)
- add_custom_target(clang_format_license
- ${CMAKE_COMMAND} -E copy_if_different
- "${CLANG_SOURCE_DIR}/LICENSE.TXT"
- "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/license.txt")
-
# Build number added to Clang version to ensure that new VSIX can be upgraded
string(TIMESTAMP CLANG_FORMAT_VSIX_BUILD %y%m%d%H%M UTC)
@@ -34,5 +29,5 @@ if (BUILD_CLANG_FORMAT_VS_PLUGIN)
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/bin/Release/ClangFormat.vsix"
"${LLVM_TOOLS_BINARY_DIR}/ClangFormat.vsix"
- DEPENDS clang_format_exe_for_vsix clang_format_license)
+ DEPENDS clang_format_exe_for_vsix)
endif()
diff --git a/tools/clang-format-vs/ClangFormat.sln b/tools/clang-format-vs/ClangFormat.sln
index 73bf84e028..46d742bce3 100644
--- a/tools/clang-format-vs/ClangFormat.sln
+++ b/tools/clang-format-vs/ClangFormat.sln
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25420.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.26228.12
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangFormat", "ClangFormat\ClangFormat.csproj", "{7FD1783E-2D31-4D05-BF23-6EBE1B42B608}"
EndProject
diff --git a/tools/clang-format-vs/ClangFormat/ClangFormat.csproj b/tools/clang-format-vs/ClangFormat/ClangFormat.csproj
index 5ce601d649..e5b7ec008a 100644
--- a/tools/clang-format-vs/ClangFormat/ClangFormat.csproj
+++ b/tools/clang-format-vs/ClangFormat/ClangFormat.csproj
@@ -14,7 +14,7 @@
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
- <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
+ <MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
@@ -59,8 +59,10 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
- <HintPath>..\packages\VSSDK.DTE.7.0.3\lib\net20\envdte.dll</HintPath>
- <EmbedInteropTypes>False</EmbedInteropTypes>
+ <EmbedInteropTypes>True</EmbedInteropTypes>
+ </Reference>
+ <Reference Include="envdte80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
@@ -116,10 +118,6 @@
<HintPath>..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.Wpf.dll</HintPath>
<Private>False</Private>
</Reference>
- <Reference Include="Microsoft.VisualStudio.TextManager.Interop">
- <HintPath>..\packages\VSSDK.TextManager.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.TextManager.Interop.dll</HintPath>
- <Private>True</Private>
- </Reference>
<Reference Include="Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>False</Private>
</Reference>
@@ -146,42 +144,6 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
- <COMReference Include="EnvDTE">
- <Guid>{80CC9F66-E7D8-4DDD-85B6-D9E6CD0E93E2}</Guid>
- <VersionMajor>8</VersionMajor>
- <VersionMinor>0</VersionMinor>
- <Lcid>0</Lcid>
- <WrapperTool>primary</WrapperTool>
- <Isolated>False</Isolated>
- <EmbedInteropTypes>False</EmbedInteropTypes>
- </COMReference>
- <COMReference Include="EnvDTE100">
- <Guid>{26AD1324-4B7C-44BC-84F8-B86AED45729F}</Guid>
- <VersionMajor>10</VersionMajor>
- <VersionMinor>0</VersionMinor>
- <Lcid>0</Lcid>
- <WrapperTool>primary</WrapperTool>
- <Isolated>False</Isolated>
- <EmbedInteropTypes>False</EmbedInteropTypes>
- </COMReference>
- <COMReference Include="EnvDTE80">
- <Guid>{1A31287A-4D7D-413E-8E32-3B374931BD89}</Guid>
- <VersionMajor>8</VersionMajor>
- <VersionMinor>0</VersionMinor>
- <Lcid>0</Lcid>
- <WrapperTool>primary</WrapperTool>
- <Isolated>False</Isolated>
- <EmbedInteropTypes>False</EmbedInteropTypes>
- </COMReference>
- <COMReference Include="EnvDTE90">
- <Guid>{2CE2370E-D744-4936-A090-3FFFE667B0E1}</Guid>
- <VersionMajor>9</VersionMajor>
- <VersionMinor>0</VersionMinor>
- <Lcid>0</Lcid>
- <WrapperTool>primary</WrapperTool>
- <Isolated>False</Isolated>
- <EmbedInteropTypes>False</EmbedInteropTypes>
- </COMReference>
<COMReference Include="Microsoft.VisualStudio.CommandBars">
<Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>
<VersionMajor>8</VersionMajor>
@@ -214,6 +176,8 @@
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PkgCmdID.cs" />
+ <Compile Include="RunningDocTableEventsDispatcher.cs" />
+ <Compile Include="Vsix.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
@@ -228,7 +192,9 @@
</ItemGroup>
<ItemGroup>
<None Include="Key.snk" />
- <None Include="packages.config" />
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
<None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
@@ -292,4 +258,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project> \ No newline at end of file
+</Project>
diff --git a/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
index c7eac42211..efb2147f2b 100644
--- a/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
+++ b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs
@@ -12,12 +12,11 @@
//
//===----------------------------------------------------------------------===//
-using Microsoft.VisualStudio.Editor;
+using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
-using Microsoft.VisualStudio.TextManager.Interop;
using System;
using System.Collections;
using System.ComponentModel;
@@ -25,6 +24,7 @@ using System.ComponentModel.Design;
using System.IO;
using System.Runtime.InteropServices;
using System.Xml.Linq;
+using System.Linq;
namespace LLVM.ClangFormat
{
@@ -36,6 +36,17 @@ namespace LLVM.ClangFormat
private string fallbackStyle = "LLVM";
private bool sortIncludes = false;
private string style = "file";
+ private bool formatOnSave = false;
+ private string formatOnSaveFileExtensions =
+ ".c;.cpp;.cxx;.cc;.tli;.tlh;.h;.hh;.hpp;.hxx;.hh;.inl" +
+ ".java;.js;.ts;.m;.mm;.proto;.protodevel;.td";
+
+ public OptionPageGrid Clone()
+ {
+ // Use MemberwiseClone to copy value types.
+ var clone = (OptionPageGrid)MemberwiseClone();
+ return clone;
+ }
public class StyleConverter : TypeConverter
{
@@ -74,7 +85,7 @@ namespace LLVM.ClangFormat
}
}
- [Category("LLVM/Clang")]
+ [Category("Format Options")]
[DisplayName("Style")]
[Description("Coding style, currently supports:\n" +
" - Predefined styles ('LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit').\n" +
@@ -121,7 +132,7 @@ namespace LLVM.ClangFormat
}
}
- [Category("LLVM/Clang")]
+ [Category("Format Options")]
[DisplayName("Assume Filename")]
[Description("When reading from stdin, clang-format assumes this " +
"filename to look for a style config file (with 'file' style) " +
@@ -142,7 +153,7 @@ namespace LLVM.ClangFormat
}
}
- [Category("LLVM/Clang")]
+ [Category("Format Options")]
[DisplayName("Fallback Style")]
[Description("The name of the predefined style used as a fallback in case clang-format " +
"is invoked with 'file' style, but can not find the configuration file.\n" +
@@ -154,7 +165,7 @@ namespace LLVM.ClangFormat
set { fallbackStyle = value; }
}
- [Category("LLVM/Clang")]
+ [Category("Format Options")]
[DisplayName("Sort includes")]
[Description("Sort touched include lines.\n\n" +
"See also: http://clang.llvm.org/docs/ClangFormat.html.")]
@@ -163,20 +174,48 @@ namespace LLVM.ClangFormat
get { return sortIncludes; }
set { sortIncludes = value; }
}
+
+ [Category("Format On Save")]
+ [DisplayName("Enable")]
+ [Description("Enable running clang-format when modified files are saved. " +
+ "Will only format if Style is found (ignores Fallback Style)."
+ )]
+ public bool FormatOnSave
+ {
+ get { return formatOnSave; }
+ set { formatOnSave = value; }
+ }
+
+ [Category("Format On Save")]
+ [DisplayName("File extensions")]
+ [Description("When formatting on save, clang-format will be applied only to " +
+ "files with these extensions.")]
+ public string FormatOnSaveFileExtensions
+ {
+ get { return formatOnSaveFileExtensions; }
+ set { formatOnSaveFileExtensions = value; }
+ }
}
[PackageRegistration(UseManagedResourcesOnly = true)]
[InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
[ProvideMenuResource("Menus.ctmenu", 1)]
+ [ProvideAutoLoad(UIContextGuids80.SolutionExists)] // Load package on solution load
[Guid(GuidList.guidClangFormatPkgString)]
[ProvideOptionPage(typeof(OptionPageGrid), "LLVM/Clang", "ClangFormat", 0, 0, true)]
public sealed class ClangFormatPackage : Package
{
#region Package Members
+
+ RunningDocTableEventsDispatcher _runningDocTableEventsDispatcher;
+
protected override void Initialize()
{
base.Initialize();
+ _runningDocTableEventsDispatcher = new RunningDocTableEventsDispatcher(this);
+ _runningDocTableEventsDispatcher.BeforeSave += OnBeforeSave;
+
var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
if (commandService != null)
{
@@ -195,6 +234,11 @@ namespace LLVM.ClangFormat
}
#endregion
+ OptionPageGrid GetUserOptions()
+ {
+ return (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
+ }
+
private void MenuItemCallback(object sender, EventArgs args)
{
var mc = sender as System.ComponentModel.Design.MenuCommand;
@@ -204,21 +248,45 @@ namespace LLVM.ClangFormat
switch (mc.CommandID.ID)
{
case (int)PkgCmdIDList.cmdidClangFormatSelection:
- FormatSelection();
+ FormatSelection(GetUserOptions());
break;
case (int)PkgCmdIDList.cmdidClangFormatDocument:
- FormatDocument();
+ FormatDocument(GetUserOptions());
break;
}
}
+ private static bool FileHasExtension(string filePath, string fileExtensions)
+ {
+ var extensions = fileExtensions.ToLower().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+ return extensions.Contains(Path.GetExtension(filePath).ToLower());
+ }
+
+ private void OnBeforeSave(object sender, Document document)
+ {
+ var options = GetUserOptions();
+
+ if (!options.FormatOnSave)
+ return;
+
+ if (!FileHasExtension(document.FullName, options.FormatOnSaveFileExtensions))
+ return;
+
+ if (!Vsix.IsDocumentDirty(document))
+ return;
+
+ var optionsWithNoFallbackStyle = GetUserOptions().Clone();
+ optionsWithNoFallbackStyle.FallbackStyle = "none";
+ FormatDocument(document, optionsWithNoFallbackStyle);
+ }
+
/// <summary>
/// Runs clang-format on the current selection
/// </summary>
- private void FormatSelection()
+ private void FormatSelection(OptionPageGrid options)
{
- IWpfTextView view = GetCurrentView();
+ IWpfTextView view = Vsix.GetCurrentView();
if (view == null)
// We're not in a text view.
return;
@@ -231,34 +299,49 @@ namespace LLVM.ClangFormat
// of the file.
if (start >= text.Length && text.Length > 0)
start = text.Length - 1;
- string path = GetDocumentParent(view);
- string filePath = GetDocumentPath(view);
+ string path = Vsix.GetDocumentParent(view);
+ string filePath = Vsix.GetDocumentPath(view);
- RunClangFormatAndApplyReplacements(text, start, length, path, filePath, view);
+ RunClangFormatAndApplyReplacements(text, start, length, path, filePath, options, view);
}
/// <summary>
/// Runs clang-format on the current document
/// </summary>
- private void FormatDocument()
+ private void FormatDocument(OptionPageGrid options)
+ {
+ FormatView(Vsix.GetCurrentView(), options);
+ }
+
+ private void FormatDocument(Document document, OptionPageGrid options)
+ {
+ FormatView(Vsix.GetDocumentView(document), options);
+ }
+
+ private void FormatView(IWpfTextView view, OptionPageGrid options)
{
- IWpfTextView view = GetCurrentView();
if (view == null)
// We're not in a text view.
return;
- string filePath = GetDocumentPath(view);
+ string filePath = Vsix.GetDocumentPath(view);
var path = Path.GetDirectoryName(filePath);
+
string text = view.TextBuffer.CurrentSnapshot.GetText();
+ if (!text.EndsWith(Environment.NewLine))
+ {
+ view.TextBuffer.Insert(view.TextBuffer.CurrentSnapshot.Length, Environment.NewLine);
+ text += Environment.NewLine;
+ }
- RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, view);
+ RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, options, view);
}
- private void RunClangFormatAndApplyReplacements(string text, int offset, int length, string path, string filePath, IWpfTextView view)
+ private void RunClangFormatAndApplyReplacements(string text, int offset, int length, string path, string filePath, OptionPageGrid options, IWpfTextView view)
{
try
{
- string replacements = RunClangFormat(text, offset, length, path, filePath);
+ string replacements = RunClangFormat(text, offset, length, path, filePath, options);
ApplyClangFormatReplacements(replacements, view);
}
catch (Exception e)
@@ -283,7 +366,7 @@ namespace LLVM.ClangFormat
///
/// Formats the text range starting at offset of the given length.
/// </summary>
- private string RunClangFormat(string text, int offset, int length, string path, string filePath)
+ private static string RunClangFormat(string text, int offset, int length, string path, string filePath, OptionPageGrid options)
{
string vsixPath = Path.GetDirectoryName(
typeof(ClangFormatPackage).Assembly.Location);
@@ -293,16 +376,16 @@ namespace LLVM.ClangFormat
process.StartInfo.FileName = vsixPath + "\\clang-format.exe";
// Poor man's escaping - this will not work when quotes are already escaped
// in the input (but we don't need more).
- string style = GetStyle().Replace("\"", "\\\"");
- string fallbackStyle = GetFallbackStyle().Replace("\"", "\\\"");
+ string style = options.Style.Replace("\"", "\\\"");
+ string fallbackStyle = options.FallbackStyle.Replace("\"", "\\\"");
process.StartInfo.Arguments = " -offset " + offset +
" -length " + length +
" -output-replacements-xml " +
" -style \"" + style + "\"" +
" -fallback-style \"" + fallbackStyle + "\"";
- if (GetSortIncludes())
+ if (options.SortIncludes)
process.StartInfo.Arguments += " -sort-includes ";
- string assumeFilename = GetAssumeFilename();
+ string assumeFilename = options.AssumeFilename;
if (string.IsNullOrEmpty(assumeFilename))
assumeFilename = filePath;
if (!string.IsNullOrEmpty(assumeFilename))
@@ -352,7 +435,7 @@ namespace LLVM.ClangFormat
/// <summary>
/// Applies the clang-format replacements (xml) to the current view
/// </summary>
- private void ApplyClangFormatReplacements(string replacements, IWpfTextView view)
+ private static void ApplyClangFormatReplacements(string replacements, IWpfTextView view)
{
// clang-format returns no replacements if input text is empty
if (replacements.Length == 0)
@@ -369,70 +452,5 @@ namespace LLVM.ClangFormat
}
edit.Apply();
}
-
- /// <summary>
- /// Returns the currently active view if it is a IWpfTextView.
- /// </summary>
- private IWpfTextView GetCurrentView()
- {
- // The SVsTextManager is a service through which we can get the active view.
- var textManager = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager));
- IVsTextView textView;
- textManager.GetActiveView(1, null, out textView);
-
- // Now we have the active view as IVsTextView, but the text interfaces we need
- // are in the IWpfTextView.
- var userData = (IVsUserData)textView;
- if (userData == null)
- return null;
- Guid guidWpfViewHost = DefGuidList.guidIWpfTextViewHost;
- object host;
- userData.GetData(ref guidWpfViewHost, out host);
- return ((IWpfTextViewHost)host).TextView;
- }
-
- private string GetStyle()
- {
- var page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
- return page.Style;
- }
-
- private string GetAssumeFilename()
- {
- var page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
- return page.AssumeFilename;
- }
-
- private string GetFallbackStyle()
- {
- var page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
- return page.FallbackStyle;
- }
-
- private bool GetSortIncludes()
- {
- var page = (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid));
- return page.SortIncludes;
- }
-
- private string GetDocumentParent(IWpfTextView view)
- {
- ITextDocument document;
- if (view.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document))
- {
- return Directory.GetParent(document.FilePath).ToString();
- }
- return null;
- }
-
- private string GetDocumentPath(IWpfTextView view)
- {
- ITextDocument document;
- if (view.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document))
- {
- return document.FilePath;
- }
- return null;
- }
}
}
diff --git a/tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs b/tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs
new file mode 100644
index 0000000000..163f68dbda
--- /dev/null
+++ b/tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs
@@ -0,0 +1,79 @@
+using EnvDTE;
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using System.Linq;
+
+namespace LLVM.ClangFormat
+{
+ // Exposes event sources for IVsRunningDocTableEvents3 events.
+ internal sealed class RunningDocTableEventsDispatcher : IVsRunningDocTableEvents3
+ {
+ private RunningDocumentTable _runningDocumentTable;
+ private DTE _dte;
+
+ public delegate void OnBeforeSaveHander(object sender, Document document);
+ public event OnBeforeSaveHander BeforeSave;
+
+ public RunningDocTableEventsDispatcher(Package package)
+ {
+ _runningDocumentTable = new RunningDocumentTable(package);
+ _runningDocumentTable.Advise(this);
+ _dte = (DTE)Package.GetGlobalService(typeof(DTE));
+ }
+
+ public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnAfterSave(uint docCookie)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
+ {
+ return VSConstants.S_OK;
+ }
+
+ public int OnBeforeSave(uint docCookie)
+ {
+ if (BeforeSave != null)
+ {
+ var document = FindDocumentByCookie(docCookie);
+ if (document != null) // Not sure why this happens sometimes
+ {
+ BeforeSave(this, FindDocumentByCookie(docCookie));
+ }
+ }
+ return VSConstants.S_OK;
+ }
+
+ private Document FindDocumentByCookie(uint docCookie)
+ {
+ var documentInfo = _runningDocumentTable.GetDocumentInfo(docCookie);
+ return _dte.Documents.Cast<Document>().FirstOrDefault(doc => doc.FullName == documentInfo.Moniker);
+ }
+ }
+}
diff --git a/tools/clang-format-vs/ClangFormat/Vsix.cs b/tools/clang-format-vs/ClangFormat/Vsix.cs
new file mode 100644
index 0000000000..0d86cb5982
--- /dev/null
+++ b/tools/clang-format-vs/ClangFormat/Vsix.cs
@@ -0,0 +1,96 @@
+using EnvDTE;
+using Microsoft.VisualStudio.Editor;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.TextManager.Interop;
+using System;
+using System.IO;
+
+namespace LLVM.ClangFormat
+{
+ internal sealed class Vsix
+ {
+ /// <summary>
+ /// Returns the currently active view if it is a IWpfTextView.
+ /// </summary>
+ public static IWpfTextView GetCurrentView()
+ {
+ // The SVsTextManager is a service through which we can get the active view.
+ var textManager = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager));
+ IVsTextView textView;
+ textManager.GetActiveView(1, null, out textView);
+
+ // Now we have the active view as IVsTextView, but the text interfaces we need
+ // are in the IWpfTextView.
+ return VsToWpfTextView(textView);
+ }
+
+ public static bool IsDocumentDirty(Document document)
+ {
+ var textView = GetDocumentView(document);
+ var textDocument = GetTextDocument(textView);
+ return textDocument?.IsDirty == true;
+ }
+
+ public static IWpfTextView GetDocumentView(Document document)
+ {
+ var textView = GetVsTextViewFrompPath(document.FullName);
+ return VsToWpfTextView(textView);
+ }
+
+ public static IWpfTextView VsToWpfTextView(IVsTextView textView)
+ {
+ var userData = (IVsUserData)textView;
+ if (userData == null)
+ return null;
+ Guid guidWpfViewHost = DefGuidList.guidIWpfTextViewHost;
+ object host;
+ userData.GetData(ref guidWpfViewHost, out host);
+ return ((IWpfTextViewHost)host).TextView;
+ }
+
+ public static IVsTextView GetVsTextViewFrompPath(string filePath)
+ {
+ // From http://stackoverflow.com/a/2427368/4039972
+ var dte2 = (EnvDTE80.DTE2)Package.GetGlobalService(typeof(SDTE));
+ var sp = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)dte2;
+ var serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(sp);
+
+ IVsUIHierarchy uiHierarchy;
+ uint itemID;
+ IVsWindowFrame windowFrame;
+ if (VsShellUtilities.IsDocumentOpen(serviceProvider, filePath, Guid.Empty,
+ out uiHierarchy, out itemID, out windowFrame))
+ {
+ // Get the IVsTextView from the windowFrame.
+ return VsShellUtilities.GetTextView(windowFrame);
+ }
+ return null;
+ }
+
+ public static ITextDocument GetTextDocument(IWpfTextView view)
+ {
+ ITextDocument document;
+ if (view != null && view.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document))
+ return document;
+ return null;
+ }
+
+ public static string GetDocumentParent(IWpfTextView view)
+ {
+ ITextDocument document = GetTextDocument(view);
+ if (document != null)
+ {
+ return Directory.GetParent(document.FilePath).ToString();
+ }
+ return null;
+ }
+
+ public static string GetDocumentPath(IWpfTextView view)
+ {
+ return GetTextDocument(view)?.FilePath;
+ }
+ }
+}
diff --git a/tools/clang-format-vs/ClangFormat/license.txt b/tools/clang-format-vs/ClangFormat/license.txt
new file mode 100644
index 0000000000..9966c8123f
--- /dev/null
+++ b/tools/clang-format-vs/ClangFormat/license.txt
@@ -0,0 +1,39 @@
+====================
+LLVM Release License
+====================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2007-2017 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+
+====================
+The LLVM software contains code written by third parties. Such software will have its own individual LICENSE.TXT file in the directory in which it appears. This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License applies to all code in the LLVM Distribution, and nothing in any of the other licenses gives permission to use the names of the LLVM Team or the University of Illinois to endorse or promote products derived from this Software.
+
+The following pieces of software have additional or alternate copyrights, licenses, and/or restrictions:
+
+Program Directory
+------- ---------
+<none yet>
+
diff --git a/tools/clang-format-vs/ClangFormat/packages.config b/tools/clang-format-vs/ClangFormat/packages.config
index 38f64ed3a6..07dc281178 100644
--- a/tools/clang-format-vs/ClangFormat/packages.config
+++ b/tools/clang-format-vs/ClangFormat/packages.config
@@ -2,7 +2,6 @@
<packages>
<package id="VSSDK.CoreUtility" version="10.0.4" targetFramework="net40" />
<package id="VSSDK.CoreUtility.10" version="10.0.4" targetFramework="net40" />
- <package id="VSSDK.DTE" version="7.0.3" targetFramework="net40" />
<package id="VSSDK.Editor" version="10.0.4" targetFramework="net40" />
<package id="VSSDK.Editor.10" version="10.0.4" targetFramework="net40" />
<package id="VSSDK.IDE" version="7.0.4" targetFramework="net40" />
diff --git a/tools/clang-format-vs/source.extension.vsixmanifest.in b/tools/clang-format-vs/source.extension.vsixmanifest.in
index 496fa40063..cf7186f73a 100644
--- a/tools/clang-format-vs/source.extension.vsixmanifest.in
+++ b/tools/clang-format-vs/source.extension.vsixmanifest.in
@@ -1,36 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
-<Vsix Version="1.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2010">
- <Identifier Id="20dbc914-1c7a-4992-b236-ef58b37850eb">
- <Name>ClangFormat</Name>
- <Author>LLVM</Author>
- <Version>@CLANG_FORMAT_VS_VERSION@</Version>
+<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
+ <Metadata>
+ <Identity Id="3cb18a5e-97e9-11e7-abc4-cec278b6b50a" Version="@CLANG_FORMAT_VS_VERSION@" Language="en-US" Publisher="LLVM"/>
+ <DisplayName>ClangFormat</DisplayName>
<Description xml:space="preserve">A tool to format C/C++/Obj-C code.</Description>
- <Locale>1033</Locale>
- <MoreInfoUrl>http://clang.llvm.org/docs/ClangFormat.html</MoreInfoUrl>
+ <MoreInfo>http://clang.llvm.org/docs/ClangFormat.html</MoreInfo>
<License>license.txt</License>
- <InstalledByMsi>false</InstalledByMsi>
- <SupportedProducts>
- <VisualStudio Version="10.0">
- <Edition>Pro</Edition>
- </VisualStudio>
- <VisualStudio Version="11.0">
- <Edition>Pro</Edition>
- </VisualStudio>
- <VisualStudio Version="12.0">
- <Edition>Pro</Edition>
- </VisualStudio>
- <VisualStudio Version="14.0">
- <Edition>Pro</Edition>
- </VisualStudio>
- </SupportedProducts>
- <SupportedFrameworkRuntimeEdition MinVersion="4.0" MaxVersion="4.0" />
- </Identifier>
- <References>
- <Reference Id="Microsoft.VisualStudio.MPF" MinVersion="10.0">
- <Name>Visual Studio MPF</Name>
- </Reference>
- </References>
- <Content>
- <VsPackage>|%CurrentProject%;PkgdefProjectOutputGroup|</VsPackage>
- </Content>
-</Vsix>
+ </Metadata>
+ <Installation InstalledByMsi="false">
+ <InstallationTarget Id="Microsoft.VisualStudio.Pro" Version="[11.0, 15.0]" />
+ </Installation>
+ <Dependencies>
+ <Dependency Id="Microsoft.VisualStudio.MPF" MinVersion="11.0" DisplayName="Visual Studio MPF" />
+ </Dependencies>
+ <Prerequisites>
+ <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[11.0,)" DisplayName="Visual Studio core editor" />
+ </Prerequisites>
+</PackageManifest>
diff --git a/tools/clang-format/CMakeLists.txt b/tools/clang-format/CMakeLists.txt
index a13633eaef..c695ba3442 100644
--- a/tools/clang-format/CMakeLists.txt
+++ b/tools/clang-format/CMakeLists.txt
@@ -15,7 +15,7 @@ target_link_libraries(clang-format
${CLANG_FORMAT_LIB_DEPS}
)
-if( LLVM_USE_SANITIZE_COVERAGE )
+if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE )
add_subdirectory(fuzzer)
endif()
diff --git a/tools/clang-format/ClangFormat.cpp b/tools/clang-format/ClangFormat.cpp
index ac0d0a8512..e169b9e585 100644
--- a/tools/clang-format/ClangFormat.cpp
+++ b/tools/clang-format/ClangFormat.cpp
@@ -102,6 +102,10 @@ static cl::opt<bool> SortIncludes(
"SortIncludes style flag"),
cl::cat(ClangFormatCategory));
+static cl::opt<bool>
+ Verbose("verbose", cl::desc("If set, shows the list of processed files"),
+ cl::cat(ClangFormatCategory));
+
static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
cl::cat(ClangFormatCategory));
@@ -276,14 +280,17 @@ static bool format(StringRef FileName) {
}
// Get new affected ranges after sorting `#includes`.
Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
- bool IncompleteFormat = false;
+ FormattingAttemptStatus Status;
Replacements FormatChanges = reformat(*FormatStyle, *ChangedCode, Ranges,
- AssumedFileName, &IncompleteFormat);
+ AssumedFileName, &Status);
Replaces = Replaces.merge(FormatChanges);
if (OutputXML) {
outs() << "<?xml version='1.0'?>\n<replacements "
"xml:space='preserve' incomplete_format='"
- << (IncompleteFormat ? "true" : "false") << "'>\n";
+ << (Status.FormatComplete ? "false" : "true") << "'";
+ if (!Status.FormatComplete)
+ outs() << " line=" << Status.Line;
+ outs() << ">\n";
if (Cursor.getNumOccurrences() != 0)
outs() << "<cursor>"
<< FormatChanges.getShiftedCodePosition(CursorPosition)
@@ -307,11 +314,15 @@ static bool format(StringRef FileName) {
if (Rewrite.overwriteChangedFiles())
return true;
} else {
- if (Cursor.getNumOccurrences() != 0)
+ if (Cursor.getNumOccurrences() != 0) {
outs() << "{ \"Cursor\": "
<< FormatChanges.getShiftedCodePosition(CursorPosition)
<< ", \"IncompleteFormat\": "
- << (IncompleteFormat ? "true" : "false") << " }\n";
+ << (Status.FormatComplete ? "false" : "true");
+ if (!Status.FormatComplete)
+ outs() << ", \"Line\": " << Status.Line;
+ outs() << " }\n";
+ }
Rewrite.getEditBuffer(ID).write(outs());
}
}
@@ -321,8 +332,7 @@ static bool format(StringRef FileName) {
} // namespace format
} // namespace clang
-static void PrintVersion() {
- raw_ostream &OS = outs();
+static void PrintVersion(raw_ostream &OS) {
OS << clang::getClangToolFullVersion("clang-format") << '\n';
}
@@ -341,8 +351,10 @@ int main(int argc, const char **argv) {
"together with <file>s, the files are edited in-place. Otherwise, the\n"
"result is written to the standard output.\n");
- if (Help)
+ if (Help) {
cl::PrintHelpMessage();
+ return 0;
+ }
if (DumpConfig) {
llvm::Expected<clang::format::FormatStyle> FormatStyle =
@@ -359,23 +371,19 @@ int main(int argc, const char **argv) {
}
bool Error = false;
- switch (FileNames.size()) {
- case 0:
+ if (FileNames.empty()) {
Error = clang::format::format("-");
- break;
- case 1:
- Error = clang::format::format(FileNames[0]);
- break;
- default:
- if (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty()) {
- errs() << "error: -offset, -length and -lines can only be used for "
- "single file.\n";
- return 1;
- }
- for (unsigned i = 0; i < FileNames.size(); ++i)
- Error |= clang::format::format(FileNames[i]);
- break;
+ return Error ? 1 : 0;
+ }
+ if (FileNames.size() != 1 && (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
+ errs() << "error: -offset, -length and -lines can only be used for "
+ "single file.\n";
+ return 1;
+ }
+ for (const auto &FileName : FileNames) {
+ if (Verbose)
+ errs() << "Formatting " << FileName << "\n";
+ Error |= clang::format::format(FileName);
}
return Error ? 1 : 0;
}
-
diff --git a/tools/clang-format/clang-format.py b/tools/clang-format/clang-format.py
index ae8a6ebf74..5fe592a920 100644
--- a/tools/clang-format/clang-format.py
+++ b/tools/clang-format/clang-format.py
@@ -62,9 +62,20 @@ def main():
# Determine range to format.
if vim.eval('exists("l:lines")') == '1':
- lines = vim.eval('l:lines')
+ lines = ['-lines', vim.eval('l:lines')]
+ elif vim.eval('exists("l:formatdiff")') == '1':
+ with open(vim.current.buffer.name, 'r') as f:
+ ondisk = f.read().splitlines();
+ sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer)
+ lines = []
+ for op in reversed(sequence.get_opcodes()):
+ if op[0] not in ['equal', 'delete']:
+ lines += ['-lines', '%s:%s' % (op[3] + 1, op[4])]
+ if lines == []:
+ return
else:
- lines = '%s:%s' % (vim.current.range.start + 1, vim.current.range.end + 1)
+ lines = ['-lines', '%s:%s' % (vim.current.range.start + 1,
+ vim.current.range.end + 1)]
# Determine the cursor position.
cursor = int(vim.eval('line2byte(line("."))+col(".")')) - 2
@@ -81,8 +92,8 @@ def main():
# Call formatter.
command = [binary, '-style', style, '-cursor', str(cursor)]
- if lines != 'all':
- command.extend(['-lines', lines])
+ if lines != ['-lines', 'all']:
+ command += lines
if fallback_style:
command.extend(['-fallback-style', fallback_style])
if vim.current.buffer.name:
diff --git a/tools/clang-format/fuzzer/CMakeLists.txt b/tools/clang-format/fuzzer/CMakeLists.txt
index c7772fcb2f..8f777324af 100644
--- a/tools/clang-format/fuzzer/CMakeLists.txt
+++ b/tools/clang-format/fuzzer/CMakeLists.txt
@@ -1,5 +1,9 @@
set(LLVM_LINK_COMPONENTS support)
+if(LLVM_USE_SANITIZE_COVERAGE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer")
+endif()
+
add_clang_executable(clang-format-fuzzer
EXCLUDE_FROM_ALL
ClangFormatFuzzer.cpp
@@ -7,5 +11,5 @@ add_clang_executable(clang-format-fuzzer
target_link_libraries(clang-format-fuzzer
${CLANG_FORMAT_LIB_DEPS}
- LLVMFuzzer
+ ${LLVM_LIB_FUZZING_ENGINE}
)
diff --git a/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp b/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp
index 5334ce873e..d440a6124b 100644
--- a/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp
+++ b/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp
@@ -20,7 +20,10 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
std::string s((const char *)data, size);
auto Style = getGoogleStyle(clang::format::FormatStyle::LK_Cpp);
Style.ColumnLimit = 60;
- applyAllReplacements(s, clang::format::reformat(
- Style, s, {clang::tooling::Range(0, s.size())}));
+ auto Replaces = reformat(Style, s, clang::tooling::Range(0, s.size()));
+ auto Result = applyAllReplacements(s, Replaces);
+
+ // Output must be checked, as otherwise we crash.
+ if (!Result) {}
return 0;
}
diff --git a/tools/clang-format/git-clang-format b/tools/clang-format/git-clang-format
index 74fd451a84..60cd4fb25b 100755
--- a/tools/clang-format/git-clang-format
+++ b/tools/clang-format/git-clang-format
@@ -20,9 +20,10 @@ clang-format on the changes in current files or a specific commit.
For further details, run:
git clang-format -h
-Requires Python 2.7
+Requires Python 2.7 or Python 3
"""
+from __future__ import print_function
import argparse
import collections
import contextlib
@@ -138,15 +139,15 @@ def main():
if opts.verbose >= 1:
ignored_files.difference_update(changed_lines)
if ignored_files:
- print 'Ignoring changes in the following files (wrong extension):'
+ print('Ignoring changes in the following files (wrong extension):')
for filename in ignored_files:
- print ' ', filename
+ print(' %s' % filename)
if changed_lines:
- print 'Running clang-format on the following files:'
+ print('Running clang-format on the following files:')
for filename in changed_lines:
- print ' ', filename
+ print(' %s' % filename)
if not changed_lines:
- print 'no modified files to format'
+ print('no modified files to format')
return
# The computed diff outputs absolute paths, so we must cd before accessing
# those files.
@@ -163,20 +164,20 @@ def main():
binary=opts.binary,
style=opts.style)
if opts.verbose >= 1:
- print 'old tree:', old_tree
- print 'new tree:', new_tree
+ print('old tree: %s' % old_tree)
+ print('new tree: %s' % new_tree)
if old_tree == new_tree:
if opts.verbose >= 0:
- print 'clang-format did not modify any files'
+ print('clang-format did not modify any files')
elif opts.diff:
print_diff(old_tree, new_tree)
else:
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
patch_mode=opts.patch)
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
- print 'changed files:'
+ print('changed files:')
for filename in changed_files:
- print ' ', filename
+ print(' %s' % filename)
def load_git_config(non_string_options=None):
@@ -257,7 +258,7 @@ def get_object_type(value):
stdout, stderr = p.communicate()
if p.returncode != 0:
return None
- return stdout.strip()
+ return convert_string(stdout.strip())
def compute_diff_and_extract_lines(commits, files):
@@ -300,6 +301,7 @@ def extract_lines(patch_file):
list of line `Range`s."""
matches = {}
for line in patch_file:
+ line = convert_string(line)
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
if match:
filename = match.group(1).rstrip('\r\n')
@@ -320,8 +322,10 @@ def filter_by_extension(dictionary, allowed_extensions):
`allowed_extensions` must be a collection of lowercase file extensions,
excluding the period."""
allowed_extensions = frozenset(allowed_extensions)
- for filename in dictionary.keys():
+ for filename in list(dictionary.keys()):
base_ext = filename.rsplit('.', 1)
+ if len(base_ext) == 1 and '' in allowed_extensions:
+ continue
if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
del dictionary[filename]
@@ -344,8 +348,13 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
"""Run clang-format on each file and save the result to a git tree.
Returns the object ID (SHA-1) of the created tree."""
+ def iteritems(container):
+ try:
+ return container.iteritems() # Python 2
+ except AttributeError:
+ return container.items() # Python 3
def index_info_generator():
- for filename, line_ranges in changed_lines.iteritems():
+ for filename, line_ranges in iteritems(changed_lines):
if revision:
git_metadata_cmd = ['git', 'ls-tree',
'%s:%s' % (revision, os.path.dirname(filename)),
@@ -356,6 +365,9 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
mode = oct(int(stdout.split()[0], 8))
else:
mode = oct(os.stat(filename).st_mode)
+ # Adjust python3 octal format so that it matches what git expects
+ if mode.startswith('0o'):
+ mode = '0' + mode[2:]
blob_id = clang_format_to_blob(filename, line_ranges,
revision=revision,
binary=binary,
@@ -376,7 +388,7 @@ def create_tree(input_lines, mode):
with temporary_index_file():
p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
for line in input_lines:
- p.stdin.write('%s\0' % line)
+ p.stdin.write(to_bytes('%s\0' % line))
p.stdin.close()
if p.wait() != 0:
die('`%s` failed' % ' '.join(cmd))
@@ -431,7 +443,7 @@ def clang_format_to_blob(filename, line_ranges, revision=None,
die('`%s` failed' % ' '.join(clang_format_cmd))
if git_show and git_show.wait() != 0:
die('`%s` failed' % ' '.join(git_show_cmd))
- return stdout.rstrip('\r\n')
+ return convert_string(stdout).rstrip('\r\n')
@contextlib.contextmanager
@@ -488,10 +500,10 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
if not force:
unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
if unstaged_files:
- print >>sys.stderr, ('The following files would be modified but '
- 'have unstaged changes:')
- print >>sys.stderr, unstaged_files
- print >>sys.stderr, 'Please commit, stage, or stash them first.'
+ print('The following files would be modified but '
+ 'have unstaged changes:', file=sys.stderr)
+ print(unstaged_files, file=sys.stderr)
+ print('Please commit, stage, or stash them first.', file=sys.stderr)
sys.exit(2)
if patch_mode:
# In patch mode, we could just as well create an index from the new tree
@@ -518,25 +530,50 @@ def run(*args, **kwargs):
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
stdout, stderr = p.communicate(input=stdin)
+
+ stdout = convert_string(stdout)
+ stderr = convert_string(stderr)
+
if p.returncode == 0:
if stderr:
if verbose:
- print >>sys.stderr, '`%s` printed to stderr:' % ' '.join(args)
- print >>sys.stderr, stderr.rstrip()
+ print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr)
+ print(stderr.rstrip(), file=sys.stderr)
if strip:
stdout = stdout.rstrip('\r\n')
return stdout
if verbose:
- print >>sys.stderr, '`%s` returned %s' % (' '.join(args), p.returncode)
+ print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr)
if stderr:
- print >>sys.stderr, stderr.rstrip()
+ print(stderr.rstrip(), file=sys.stderr)
sys.exit(2)
def die(message):
- print >>sys.stderr, 'error:', message
+ print('error:', message, file=sys.stderr)
sys.exit(2)
+def to_bytes(str_input):
+ # Encode to UTF-8 to get binary data.
+ if isinstance(str_input, bytes):
+ return str_input
+ return str_input.encode('utf-8')
+
+
+def to_string(bytes_input):
+ if isinstance(bytes_input, str):
+ return bytes_input
+ return bytes_input.encode('utf-8')
+
+
+def convert_string(bytes_input):
+ try:
+ return to_string(bytes_input.decode('utf-8'))
+ except AttributeError: # 'str' object has no attribute 'decode'.
+ return str(bytes_input)
+ except UnicodeError:
+ return str(bytes_input)
+
if __name__ == '__main__':
main()
diff --git a/tools/clang-func-mapping/CMakeLists.txt b/tools/clang-func-mapping/CMakeLists.txt
new file mode 100644
index 0000000000..8c10fcd757
--- /dev/null
+++ b/tools/clang-func-mapping/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ asmparser
+ support
+ mc
+ )
+
+add_clang_executable(clang-func-mapping
+ ClangFnMapGen.cpp
+ )
+
+target_link_libraries(clang-func-mapping
+ clangAST
+ clangBasic
+ clangCrossTU
+ clangFrontend
+ clangIndex
+ clangTooling
+ )
+
+install(TARGETS clang-func-mapping
+ RUNTIME DESTINATION bin)
diff --git a/tools/clang-func-mapping/ClangFnMapGen.cpp b/tools/clang-func-mapping/ClangFnMapGen.cpp
new file mode 100644
index 0000000000..4bf812fffe
--- /dev/null
+++ b/tools/clang-func-mapping/ClangFnMapGen.cpp
@@ -0,0 +1,124 @@
+//===- ClangFnMapGen.cpp -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// Clang tool which creates a list of defined functions and the files in which
+// they are defined.
+//
+//===--------------------------------------------------------------------===//
+
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/GlobalDecl.h"
+#include "clang/AST/Mangle.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Signals.h"
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace clang;
+using namespace clang::cross_tu;
+using namespace clang::tooling;
+
+static cl::OptionCategory ClangFnMapGenCategory("clang-fnmapgen options");
+
+class MapFunctionNamesConsumer : public ASTConsumer {
+public:
+ MapFunctionNamesConsumer(ASTContext &Context) : Ctx(Context) {}
+
+ ~MapFunctionNamesConsumer() {
+ // Flush results to standard output.
+ llvm::outs() << createCrossTUIndexString(Index);
+ }
+
+ virtual void HandleTranslationUnit(ASTContext &Ctx) {
+ handleDecl(Ctx.getTranslationUnitDecl());
+ }
+
+private:
+ void handleDecl(const Decl *D);
+
+ ASTContext &Ctx;
+ llvm::StringMap<std::string> Index;
+ std::string CurrentFileName;
+};
+
+void MapFunctionNamesConsumer::handleDecl(const Decl *D) {
+ if (!D)
+ return;
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (FD->isThisDeclarationADefinition()) {
+ if (const Stmt *Body = FD->getBody()) {
+ std::string LookupName = CrossTranslationUnitContext::getLookupName(FD);
+ const SourceManager &SM = Ctx.getSourceManager();
+ if (CurrentFileName.empty()) {
+ CurrentFileName =
+ SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName();
+ if (CurrentFileName.empty())
+ CurrentFileName = "invalid_file";
+ }
+
+ switch (FD->getLinkageInternal()) {
+ case ExternalLinkage:
+ case VisibleNoLinkage:
+ case UniqueExternalLinkage:
+ if (SM.isInMainFile(Body->getLocStart()))
+ Index[LookupName] = CurrentFileName;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if (const auto *DC = dyn_cast<DeclContext>(D))
+ for (const Decl *D : DC->decls())
+ handleDecl(D);
+}
+
+class MapFunctionNamesAction : public ASTFrontendAction {
+protected:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
+ llvm::StringRef) {
+ std::unique_ptr<ASTConsumer> PFC(
+ new MapFunctionNamesConsumer(CI.getASTContext()));
+ return PFC;
+ }
+};
+
+static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
+
+int main(int argc, const char **argv) {
+ // Print a stack trace if we signal out.
+ sys::PrintStackTraceOnErrorSignal(argv[0], false);
+ PrettyStackTraceProgram X(argc, argv);
+
+ const char *Overview = "\nThis tool collects the USR name and location "
+ "of all functions definitions in the source files "
+ "(excluding headers).\n";
+ CommonOptionsParser OptionsParser(argc, argv, ClangFnMapGenCategory,
+ cl::ZeroOrMore, Overview);
+
+ ClangTool Tool(OptionsParser.getCompilations(),
+ OptionsParser.getSourcePathList());
+ Tool.run(newFrontendActionFactory<MapFunctionNamesAction>().get());
+ return 0;
+}
diff --git a/tools/clang-fuzzer/CMakeLists.txt b/tools/clang-fuzzer/CMakeLists.txt
index 87d21c6bf1..abc501511f 100644
--- a/tools/clang-fuzzer/CMakeLists.txt
+++ b/tools/clang-fuzzer/CMakeLists.txt
@@ -1,20 +1,71 @@
-if( LLVM_USE_SANITIZE_COVERAGE )
- set(LLVM_LINK_COMPONENTS support)
+set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} FuzzMutate)
+set(CXX_FLAGS_NOFUZZ ${CMAKE_CXX_FLAGS})
+set(DUMMY_MAIN DummyClangFuzzer.cpp)
+if(LLVM_LIB_FUZZING_ENGINE)
+ unset(DUMMY_MAIN)
+elseif(LLVM_USE_SANITIZE_COVERAGE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer")
+ set(CXX_FLAGS_NOFUZZ "${CXX_FLAGS_NOFUZZ} -fsanitize=fuzzer-no-link")
+ unset(DUMMY_MAIN)
+endif()
+
+# Hack to bypass LLVM's cmake sources check and allow multiple libraries and
+# executables from this directory.
+set(LLVM_OPTIONAL_SOURCES
+ ClangFuzzer.cpp
+ DummyClangFuzzer.cpp
+ ExampleClangProtoFuzzer.cpp
+ )
+
+if(CLANG_ENABLE_PROTO_FUZZER)
+ # Create protobuf .h and .cc files, and put them in a library for use by
+ # clang-proto-fuzzer components.
+ find_package(Protobuf REQUIRED)
+ add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI)
+ include_directories(${PROTOBUF_INCLUDE_DIRS})
+ include_directories(${CMAKE_CURRENT_BINARY_DIR})
+ protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS cxx_proto.proto)
+ set(LLVM_OPTIONAL_SOURCES ${LLVM_OPTIONAL_SOURCES} ${PROTO_SRCS})
+ add_clang_library(clangCXXProto
+ ${PROTO_SRCS}
+ ${PROTO_HDRS}
+
+ LINK_LIBS
+ ${PROTOBUF_LIBRARIES}
+ )
- add_clang_executable(clang-fuzzer
- EXCLUDE_FROM_ALL
- ClangFuzzer.cpp
+ # Build and include libprotobuf-mutator
+ include(ProtobufMutator)
+ include_directories(${ProtobufMutator_INCLUDE_DIRS})
+
+ # Build the protobuf->C++ translation library and driver.
+ add_clang_subdirectory(proto-to-cxx)
+
+ # Build the protobuf fuzzer
+ add_clang_executable(clang-proto-fuzzer
+ ${DUMMY_MAIN}
+ ExampleClangProtoFuzzer.cpp
)
- target_link_libraries(clang-fuzzer
- ${CLANG_FORMAT_LIB_DEPS}
- clangAST
- clangBasic
- clangDriver
- clangFrontend
- clangRewriteFrontend
- clangStaticAnalyzerFrontend
- clangTooling
- LLVMFuzzer
+ target_link_libraries(clang-proto-fuzzer
+ ${ProtobufMutator_LIBRARIES}
+ ${PROTOBUF_LIBRARIES}
+ ${LLVM_LIB_FUZZING_ENGINE}
+ clangCXXProto
+ clangHandleCXX
+ clangProtoToCXX
)
endif()
+
+add_clang_subdirectory(handle-cxx)
+
+add_clang_executable(clang-fuzzer
+ EXCLUDE_FROM_ALL
+ ${DUMMY_MAIN}
+ ClangFuzzer.cpp
+ )
+
+target_link_libraries(clang-fuzzer
+ ${LLVM_LIB_FUZZING_ENGINE}
+ clangHandleCXX
+ )
diff --git a/tools/clang-fuzzer/ClangFuzzer.cpp b/tools/clang-fuzzer/ClangFuzzer.cpp
index 1692882c0b..2d35fb7735 100644
--- a/tools/clang-fuzzer/ClangFuzzer.cpp
+++ b/tools/clang-fuzzer/ClangFuzzer.cpp
@@ -13,36 +13,14 @@
///
//===----------------------------------------------------------------------===//
-#include "clang/Tooling/Tooling.h"
-#include "clang/Frontend/FrontendActions.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Lex/PreprocessorOptions.h"
-#include "llvm/Option/Option.h"
+#include "handle-cxx/handle_cxx.h"
-using namespace clang;
+using namespace clang_fuzzer;
+
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { return 0; }
extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
std::string s((const char *)data, size);
- llvm::opt::ArgStringList CC1Args;
- CC1Args.push_back("-cc1");
- CC1Args.push_back("./test.cc");
- llvm::IntrusiveRefCntPtr<FileManager> Files(
- new FileManager(FileSystemOptions()));
- IgnoringDiagConsumer Diags;
- IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
- DiagnosticsEngine Diagnostics(
- IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
- &Diags, false);
- std::unique_ptr<clang::CompilerInvocation> Invocation(
- tooling::newInvocation(&Diagnostics, CC1Args));
- std::unique_ptr<llvm::MemoryBuffer> Input =
- llvm::MemoryBuffer::getMemBuffer(s);
- Invocation->getPreprocessorOpts().addRemappedFile("./test.cc", Input.release());
- std::unique_ptr<tooling::ToolAction> action(
- tooling::newFrontendActionFactory<clang::SyntaxOnlyAction>());
- std::shared_ptr<PCHContainerOperations> PCHContainerOps =
- std::make_shared<PCHContainerOperations>();
- action->runInvocation(std::move(Invocation), Files.get(), PCHContainerOps,
- &Diags);
+ HandleCXX(s, {"-O2"});
return 0;
}
diff --git a/tools/clang-fuzzer/Dockerfile b/tools/clang-fuzzer/Dockerfile
new file mode 100644
index 0000000000..1946b8bf88
--- /dev/null
+++ b/tools/clang-fuzzer/Dockerfile
@@ -0,0 +1,37 @@
+#===- llvm/tools/clang/tools/clang-fuzzer ---------------------------------===//
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===----------------------------------------------------------------------===//
+# Produces an image that builds clang-proto-fuzzer
+FROM ubuntu:16.04
+RUN apt-get update -y
+RUN apt-get install -y autoconf automake libtool curl make g++ unzip wget git \
+ binutils liblzma-dev libz-dev python-all cmake ninja-build subversion \
+ pkg-config docbook2x
+
+WORKDIR /root
+
+# Get protobuf
+RUN wget -qO- https://github.com/google/protobuf/releases/download/v3.3.0/protobuf-cpp-3.3.0.tar.gz | tar zxf -
+RUN cd protobuf-3.3.0 && ./autogen.sh && ./configure && make -j $(nproc) && make check -j $(nproc) && make install && ldconfig
+# Get LLVM
+RUN svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
+RUN cd llvm/tools && svn co http://llvm.org/svn/llvm-project/cfe/trunk clang -r $(cd ../ && svn info | grep Revision | awk '{print $2}')
+RUN cd llvm/projects && svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt -r $(cd ../ && svn info | grep Revision | awk '{print $2}')
+# Build plain LLVM (stage 0)
+RUN mkdir build0 && cd build0 && cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../llvm && ninja
+# Configure instrumented LLVM (stage 1)
+RUN mkdir build1 && cd build1 && cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../llvm \
+ -DLLVM_ENABLE_ASSERTIONS=ON \
+ -DCMAKE_C_COMPILER=`pwd`/../build0/bin/clang \
+ -DCMAKE_CXX_COMPILER=`pwd`/../build0/bin/clang++ \
+ -DLLVM_USE_SANITIZE_COVERAGE=YES \
+ -DLLVM_USE_SANITIZER=Address -DCLANG_ENABLE_PROTO_FUZZER=ON
+# Build the fuzzers
+RUN cd build1 && ninja clang-fuzzer
+RUN cd build1 && ninja clang-proto-fuzzer
+RUN cd build1 && ninja clang-proto-to-cxx
diff --git a/tools/clang-fuzzer/DummyClangFuzzer.cpp b/tools/clang-fuzzer/DummyClangFuzzer.cpp
new file mode 100644
index 0000000000..382c161307
--- /dev/null
+++ b/tools/clang-fuzzer/DummyClangFuzzer.cpp
@@ -0,0 +1,21 @@
+//===-- DummyClangFuzzer.cpp - Entry point to sanity check fuzzers --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides a main() to build without linking libFuzzer.
+//
+//===----------------------------------------------------------------------===//
+#include "llvm/FuzzMutate/FuzzerCLI.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv);
+
+int main(int argc, char *argv[]) {
+ return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput,
+ LLVMFuzzerInitialize);
+}
diff --git a/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp b/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp
new file mode 100644
index 0000000000..ab734e85b8
--- /dev/null
+++ b/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp
@@ -0,0 +1,44 @@
+//===-- ExampleClangProtoFuzzer.cpp - Fuzz Clang --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements a function that runs Clang on a single
+/// input and uses libprotobuf-mutator to find new inputs. This function is
+/// then linked into the Fuzzer library.
+///
+//===----------------------------------------------------------------------===//
+
+#include "cxx_proto.pb.h"
+#include "handle-cxx/handle_cxx.h"
+#include "proto-to-cxx/proto_to_cxx.h"
+
+#include "src/libfuzzer/libfuzzer_macro.h"
+
+#include <cstring>
+
+using namespace clang_fuzzer;
+
+static std::vector<const char *> CLArgs;
+
+extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
+ CLArgs.push_back("-O2");
+ for (int I = 1; I < *argc; I++) {
+ if (strcmp((*argv)[I], "-ignore_remaining_args=1") == 0) {
+ for (I++; I < *argc; I++)
+ CLArgs.push_back((*argv)[I]);
+ break;
+ }
+ }
+ return 0;
+}
+
+DEFINE_BINARY_PROTO_FUZZER(const Function& input) {
+ auto S = FunctionToString(input);
+ HandleCXX(S, CLArgs);
+}
diff --git a/tools/clang-fuzzer/README.txt b/tools/clang-fuzzer/README.txt
new file mode 100644
index 0000000000..66a6a6332c
--- /dev/null
+++ b/tools/clang-fuzzer/README.txt
@@ -0,0 +1,82 @@
+This directory contains two utilities for fuzzing Clang: clang-fuzzer and
+clang-proto-fuzzer. Both use libFuzzer to generate inputs to clang via
+coverage-guided mutation.
+
+The two utilities differ, however, in how they structure inputs to Clang.
+clang-fuzzer makes no attempt to generate valid C++ programs and is therefore
+primarily useful for stressing the surface layers of Clang (i.e. lexer, parser).
+clang-proto-fuzzer uses a protobuf class to describe a subset of the C++
+language and then uses libprotobuf-mutator to mutate instantiations of that
+class, producing valid C++ programs in the process. As a result,
+clang-proto-fuzzer is better at stressing deeper layers of Clang and LLVM.
+
+===================================
+ Building clang-fuzzer
+===================================
+Within your LLVM build directory, run CMake with the following variable
+definitions:
+- CMAKE_C_COMPILER=clang
+- CMAKE_CXX_COMPILER=clang++
+- LLVM_USE_SANITIZE_COVERAGE=YES
+- LLVM_USE_SANITIZER=Address
+
+Then build the clang-fuzzer target.
+
+Example:
+ cd $LLVM_SOURCE_DIR
+ mkdir build && cd build
+ cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DLLVM_USE_SANITIZE_COVERAGE=YES -DLLVM_USE_SANITIZER=Address
+ ninja clang-fuzzer
+
+======================
+ Running clang-fuzzer
+======================
+ bin/clang-fuzzer CORPUS_DIR
+
+
+=======================================================
+ Building clang-proto-fuzzer (Linux-only instructions)
+=======================================================
+Install the necessary dependencies:
+- binutils // needed for libprotobuf-mutator
+- liblzma-dev // needed for libprotobuf-mutator
+- libz-dev // needed for libprotobuf-mutator
+- docbook2x // needed for libprotobuf-mutator
+- Recent version of protobuf [3.3.0 is known to work]
+
+Within your LLVM build directory, run CMake with the following variable
+definitions:
+- CMAKE_C_COMPILER=clang
+- CMAKE_CXX_COMPILER=clang++
+- LLVM_USE_SANITIZE_COVERAGE=YES
+- LLVM_USE_SANITIZER=Address
+- CLANG_ENABLE_PROTO_FUZZER=ON
+
+Then build the clang-proto-fuzzer and clang-proto-to-cxx targets. Optionally,
+you may also build clang-fuzzer with this setup.
+
+Example:
+ cd $LLVM_SOURCE_DIR
+ mkdir build && cd build
+ cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \
+ -DLLVM_USE_SANITIZE_COVERAGE=YES -DLLVM_USE_SANITIZER=Address \
+ -DCLANG_ENABLE_PROTO_FUZZER=ON
+ ninja clang-proto-fuzzer clang-proto-to-cxx
+
+This directory also contains a Dockerfile which sets up all required
+dependencies and builds the fuzzers.
+
+============================
+ Running clang-proto-fuzzer
+============================
+ bin/clang-proto-fuzzer CORPUS_DIR
+
+Arguments can be specified after -ignore_remaining_args=1 to modify the compiler
+invocation. For example, the following command line will fuzz LLVM with a
+custom optimization level and target triple:
+ bin/clang-proto-fuzzer CORPUS_DIR -ignore_remaining_args=1 -O3 -triple \
+ arm64apple-ios9
+
+To translate a clang-proto-fuzzer corpus output to C++:
+ bin/clang-proto-to-cxx CORPUS_OUTPUT_FILE
diff --git a/tools/clang-fuzzer/cxx_proto.proto b/tools/clang-fuzzer/cxx_proto.proto
new file mode 100644
index 0000000000..714a29861e
--- /dev/null
+++ b/tools/clang-fuzzer/cxx_proto.proto
@@ -0,0 +1,93 @@
+//===-- cxx_proto.proto - Protobuf description of C++ ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file describes a subset of C++ as a protobuf. It is used to
+/// more easily find interesting inputs for fuzzing Clang.
+///
+//===----------------------------------------------------------------------===//
+
+syntax = "proto2";
+
+message VarRef {
+ required int32 varnum = 1;
+}
+
+message Lvalue {
+ required VarRef varref = 1;
+}
+
+message Const {
+ required int32 val = 1;
+}
+
+message BinaryOp {
+ enum Op {
+ PLUS = 0;
+ MINUS = 1;
+ MUL = 2;
+ DIV = 3;
+ MOD = 4;
+ XOR = 5;
+ AND = 6;
+ OR = 7;
+ EQ = 8;
+ NE = 9;
+ LE = 10;
+ GE = 11;
+ LT = 12;
+ GT = 13;
+ };
+ required Op op = 1;
+ required Rvalue left = 2;
+ required Rvalue right = 3;
+}
+
+message Rvalue {
+ oneof rvalue_oneof {
+ VarRef varref = 1;
+ Const cons = 2;
+ BinaryOp binop = 3;
+ }
+}
+
+message AssignmentStatement {
+ required Lvalue lvalue = 1;
+ required Rvalue rvalue = 2;
+}
+
+
+message IfElse {
+ required Rvalue cond = 1;
+ required StatementSeq if_body = 2;
+ required StatementSeq else_body = 3;
+}
+
+message While {
+ required Rvalue cond = 1;
+ required StatementSeq body = 2;
+}
+
+message Statement {
+ oneof stmt_oneof {
+ AssignmentStatement assignment = 1;
+ IfElse ifelse = 2;
+ While while_loop = 3;
+ }
+}
+
+message StatementSeq {
+ repeated Statement statements = 1;
+}
+
+message Function {
+ required StatementSeq statements = 1;
+}
+
+package clang_fuzzer;
diff --git a/tools/clang-fuzzer/handle-cxx/CMakeLists.txt b/tools/clang-fuzzer/handle-cxx/CMakeLists.txt
new file mode 100644
index 0000000000..caf1dba7af
--- /dev/null
+++ b/tools/clang-fuzzer/handle-cxx/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support)
+
+add_clang_library(clangHandleCXX
+ handle_cxx.cpp
+
+ LINK_LIBS
+ clangBasic
+ clangCodeGen
+ clangFrontend
+ clangLex
+ clangTooling
+ )
diff --git a/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp b/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp
new file mode 100644
index 0000000000..312ab91e5f
--- /dev/null
+++ b/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp
@@ -0,0 +1,58 @@
+//==-- handle_cxx.cpp - Helper function for Clang fuzzers ------------------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements HandleCXX for use by the Clang fuzzers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "handle_cxx.h"
+
+#include "clang/CodeGen/CodeGenAction.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/PreprocessorOptions.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/TargetSelect.h"
+
+using namespace clang;
+
+void clang_fuzzer::HandleCXX(const std::string &S,
+ const std::vector<const char *> &ExtraArgs) {
+ llvm::InitializeAllTargets();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmPrinters();
+ llvm::InitializeAllAsmParsers();
+
+ llvm::opt::ArgStringList CC1Args;
+ CC1Args.push_back("-cc1");
+ for (auto &A : ExtraArgs)
+ CC1Args.push_back(A);
+ CC1Args.push_back("./test.cc");
+
+ llvm::IntrusiveRefCntPtr<FileManager> Files(
+ new FileManager(FileSystemOptions()));
+ IgnoringDiagConsumer Diags;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &Diags, false);
+ std::unique_ptr<clang::CompilerInvocation> Invocation(
+ tooling::newInvocation(&Diagnostics, CC1Args));
+ std::unique_ptr<llvm::MemoryBuffer> Input =
+ llvm::MemoryBuffer::getMemBuffer(S);
+ Invocation->getPreprocessorOpts().addRemappedFile("./test.cc",
+ Input.release());
+ std::unique_ptr<tooling::ToolAction> action(
+ tooling::newFrontendActionFactory<clang::EmitObjAction>());
+ std::shared_ptr<PCHContainerOperations> PCHContainerOps =
+ std::make_shared<PCHContainerOperations>();
+ action->runInvocation(std::move(Invocation), Files.get(), PCHContainerOps,
+ &Diags);
+}
+
diff --git a/tools/clang-fuzzer/handle-cxx/handle_cxx.h b/tools/clang-fuzzer/handle-cxx/handle_cxx.h
new file mode 100644
index 0000000000..e76311ff39
--- /dev/null
+++ b/tools/clang-fuzzer/handle-cxx/handle_cxx.h
@@ -0,0 +1,25 @@
+//==-- handle_cxx.h - Helper function for Clang fuzzers --------------------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines HandleCXX for use by the Clang fuzzers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_CXX_HANDLECXX_H
+#define LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_CXX_HANDLECXX_H
+
+#include <string>
+#include <vector>
+
+namespace clang_fuzzer {
+void HandleCXX(const std::string &S,
+ const std::vector<const char *> &ExtraArgs);
+} // namespace clang_fuzzer
+
+#endif
diff --git a/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt b/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt
new file mode 100644
index 0000000000..2b0ade4455
--- /dev/null
+++ b/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD})
+set(CMAKE_CXX_FLAGS ${CXX_FLAGS_NOFUZZ})
+
+# Hack to bypass LLVM's CMake source checks so we can have both a library and
+# an executable built from this directory.
+set(LLVM_OPTIONAL_SOURCES proto_to_cxx.cpp proto_to_cxx_main.cpp)
+
+add_clang_library(clangProtoToCXX proto_to_cxx.cpp
+ DEPENDS clangCXXProto
+ LINK_LIBS clangCXXProto ${PROTOBUF_LIBRARIES}
+ )
+
+add_clang_executable(clang-proto-to-cxx proto_to_cxx_main.cpp)
+target_link_libraries(clang-proto-to-cxx clangProtoToCXX)
diff --git a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp
new file mode 100644
index 0000000000..cd75e0c99c
--- /dev/null
+++ b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp
@@ -0,0 +1,102 @@
+//==-- proto_to_cxx.cpp - Protobuf-C++ conversion --------------------------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements functions for converting between protobufs and C++.
+//
+//===----------------------------------------------------------------------===//
+
+#include "proto_to_cxx.h"
+#include "cxx_proto.pb.h"
+
+#include <ostream>
+#include <sstream>
+
+namespace clang_fuzzer {
+
+// Forward decls.
+std::ostream &operator<<(std::ostream &os, const BinaryOp &x);
+std::ostream &operator<<(std::ostream &os, const StatementSeq &x);
+
+// Proto to C++.
+std::ostream &operator<<(std::ostream &os, const Const &x) {
+ return os << "(" << x.val() << ")";
+}
+std::ostream &operator<<(std::ostream &os, const VarRef &x) {
+ return os << "a[" << (static_cast<uint32_t>(x.varnum()) % 100) << "]";
+}
+std::ostream &operator<<(std::ostream &os, const Lvalue &x) {
+ return os << x.varref();
+}
+std::ostream &operator<<(std::ostream &os, const Rvalue &x) {
+ if (x.has_varref()) return os << x.varref();
+ if (x.has_cons()) return os << x.cons();
+ if (x.has_binop()) return os << x.binop();
+ return os << "1";
+}
+std::ostream &operator<<(std::ostream &os, const BinaryOp &x) {
+ os << "(" << x.left();
+ switch (x.op()) {
+ case BinaryOp::PLUS: os << "+"; break;
+ case BinaryOp::MINUS: os << "-"; break;
+ case BinaryOp::MUL: os << "*"; break;
+ case BinaryOp::DIV: os << "/"; break;
+ case BinaryOp::MOD: os << "%"; break;
+ case BinaryOp::XOR: os << "^"; break;
+ case BinaryOp::AND: os << "&"; break;
+ case BinaryOp::OR: os << "|"; break;
+ case BinaryOp::EQ: os << "=="; break;
+ case BinaryOp::NE: os << "!="; break;
+ case BinaryOp::LE: os << "<="; break;
+ case BinaryOp::GE: os << ">="; break;
+ case BinaryOp::LT: os << "<"; break;
+ case BinaryOp::GT: os << ">"; break;
+ }
+ return os << x.right() << ")";
+}
+std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) {
+ return os << x.lvalue() << "=" << x.rvalue() << ";\n";
+}
+std::ostream &operator<<(std::ostream &os, const IfElse &x) {
+ return os << "if (" << x.cond() << "){\n"
+ << x.if_body() << "} else { \n"
+ << x.else_body() << "}\n";
+}
+std::ostream &operator<<(std::ostream &os, const While &x) {
+ return os << "while (" << x.cond() << "){\n" << x.body() << "}\n";
+}
+std::ostream &operator<<(std::ostream &os, const Statement &x) {
+ if (x.has_assignment()) return os << x.assignment();
+ if (x.has_ifelse()) return os << x.ifelse();
+ if (x.has_while_loop()) return os << x.while_loop();
+ return os << "(void)0;\n";
+}
+std::ostream &operator<<(std::ostream &os, const StatementSeq &x) {
+ for (auto &st : x.statements()) os << st;
+ return os;
+}
+std::ostream &operator<<(std::ostream &os, const Function &x) {
+ return os << "void foo(int *a) {\n" << x.statements() << "}\n";
+}
+
+// ---------------------------------
+
+std::string FunctionToString(const Function &input) {
+ std::ostringstream os;
+ os << input;
+ return os.str();
+
+}
+std::string ProtoToCxx(const uint8_t *data, size_t size) {
+ Function message;
+ if (!message.ParseFromArray(data, size))
+ return "#error invalid proto\n";
+ return FunctionToString(message);
+}
+
+} // namespace clang_fuzzer
diff --git a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h
new file mode 100644
index 0000000000..1985e91ba2
--- /dev/null
+++ b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h
@@ -0,0 +1,22 @@
+//==-- proto_to_cxx.h - Protobuf-C++ conversion ----------------------------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines functions for converting between protobufs and C++.
+//
+//===----------------------------------------------------------------------===//
+
+#include <cstdint>
+#include <cstddef>
+#include <string>
+
+namespace clang_fuzzer {
+class Function;
+std::string FunctionToString(const Function &input);
+std::string ProtoToCxx(const uint8_t *data, size_t size);
+}
diff --git a/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp
new file mode 100644
index 0000000000..73ef14b755
--- /dev/null
+++ b/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp
@@ -0,0 +1,30 @@
+//==-- proto_to_cxx_main.cpp - Driver for protobuf-C++ conversion ----------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements a simple driver to print a C++ program from a protobuf.
+//
+//===----------------------------------------------------------------------===//
+#include <fstream>
+#include <iostream>
+#include <streambuf>
+#include <string>
+
+#include "proto_to_cxx.h"
+
+int main(int argc, char **argv) {
+ for (int i = 1; i < argc; i++) {
+ std::fstream in(argv[i]);
+ std::string str((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+ std::cout << "// " << argv[i] << std::endl;
+ std::cout << clang_fuzzer::ProtoToCxx(
+ reinterpret_cast<const uint8_t *>(str.data()), str.size());
+ }
+}
+
diff --git a/tools/clang-import-test/CMakeLists.txt b/tools/clang-import-test/CMakeLists.txt
index bd4919694a..85e833d37b 100644
--- a/tools/clang-import-test/CMakeLists.txt
+++ b/tools/clang-import-test/CMakeLists.txt
@@ -17,6 +17,7 @@ set(CLANG_IMPORT_TEST_LIB_DEPS
clangAST
clangBasic
clangCodeGen
+ clangDriver
clangFrontend
clangLex
clangParse
diff --git a/tools/clang-import-test/clang-import-test.cpp b/tools/clang-import-test/clang-import-test.cpp
index 33190af4bf..1ea7ee3611 100644
--- a/tools/clang-import-test/clang-import-test.cpp
+++ b/tools/clang-import-test/clang-import-test.cpp
@@ -9,19 +9,25 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTImporter.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ExternalASTMerger.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TargetOptions.h"
#include "clang/CodeGen/ModuleBuilder.h"
+#include "clang/Driver/Types.h"
+#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/ParseAST.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Host.h"
@@ -40,11 +46,30 @@ static llvm::cl::list<std::string>
Imports("import", llvm::cl::ZeroOrMore,
llvm::cl::desc("Path to a file containing declarations to import"));
+static llvm::cl::opt<bool>
+ Direct("direct", llvm::cl::Optional,
+ llvm::cl::desc("Use the parsed declarations without indirection"));
+
+static llvm::cl::opt<bool>
+ UseOrigins("use-origins", llvm::cl::Optional,
+ llvm::cl::desc("Use DeclContext origin information for more accurate lookups"));
+
static llvm::cl::list<std::string>
ClangArgs("Xcc", llvm::cl::ZeroOrMore,
llvm::cl::desc("Argument to pass to the CompilerInvocation"),
llvm::cl::CommaSeparated);
+static llvm::cl::opt<std::string>
+ Input("x", llvm::cl::Optional,
+ llvm::cl::desc("The language to parse (default: c++)"),
+ llvm::cl::init("c++"));
+
+static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
+ llvm::cl::desc("Dump combined AST"));
+
+static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false),
+ llvm::cl::desc("Dump IR from final parse"));
+
namespace init_convenience {
class TestDiagnosticConsumer : public DiagnosticConsumer {
private:
@@ -98,6 +123,7 @@ private:
llvm::errs() << LineString << '\n';
llvm::errs().indent(LocColumn);
llvm::errs() << '^';
+ llvm::errs() << '\n';
}
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
@@ -130,8 +156,7 @@ private:
}
};
-std::unique_ptr<CompilerInstance>
-BuildCompilerInstance(ArrayRef<const char *> ClangArgv) {
+std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
auto Ins = llvm::make_unique<CompilerInstance>();
auto DC = llvm::make_unique<TestDiagnosticConsumer>();
const bool ShouldOwnClient = true;
@@ -139,13 +164,27 @@ BuildCompilerInstance(ArrayRef<const char *> ClangArgv) {
auto Inv = llvm::make_unique<CompilerInvocation>();
+ std::vector<const char *> ClangArgv(ClangArgs.size());
+ std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
+ [](const std::string &s) -> const char * { return s.data(); });
CompilerInvocation::CreateFromArgs(*Inv, ClangArgv.data(),
&ClangArgv.data()[ClangArgv.size()],
Ins->getDiagnostics());
- Inv->getLangOpts()->CPlusPlus = true;
- Inv->getLangOpts()->CPlusPlus11 = true;
- Inv->getHeaderSearchOpts().UseLibcxx = true;
+ {
+ using namespace driver::types;
+ ID Id = lookupTypeForTypeSpecifier(Input.c_str());
+ assert(Id != TY_INVALID);
+ if (isCXX(Id)) {
+ Inv->getLangOpts()->CPlusPlus = true;
+ Inv->getLangOpts()->CPlusPlus11 = true;
+ Inv->getHeaderSearchOpts().UseLibcxx = true;
+ }
+ if (isObjC(Id)) {
+ Inv->getLangOpts()->ObjC1 = 1;
+ Inv->getLangOpts()->ObjC2 = 1;
+ }
+ }
Inv->getLangOpts()->Bool = true;
Inv->getLangOpts()->WChar = true;
Inv->getLangOpts()->Blocks = true;
@@ -189,65 +228,59 @@ std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
} // end namespace
namespace {
-class TestExternalASTSource : public ExternalASTSource {
-private:
- llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs;
- std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ForwardImporters;
- std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ReverseImporters;
-public:
- TestExternalASTSource(
- CompilerInstance &ExpressionCI,
- llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs)
- : ImportCIs(ImportCIs) {
- for (const std::unique_ptr<CompilerInstance> &ImportCI : ImportCIs) {
- ForwardImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>(
- ExpressionCI.getASTContext(), ExpressionCI.getFileManager(),
- ImportCI->getASTContext(), ImportCI->getFileManager(),
- /*MinimalImport=*/true);
- ReverseImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>(
- ImportCI->getASTContext(), ImportCI->getFileManager(),
- ExpressionCI.getASTContext(), ExpressionCI.getFileManager(),
- /*MinimalImport=*/true);
- }
+/// A container for a CompilerInstance (possibly with an ExternalASTMerger
+/// attached to its ASTContext).
+///
+/// Provides an accessor for the DeclContext origins associated with the
+/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is
+/// attached).
+///
+/// This is the main unit of parsed source code maintained by clang-import-test.
+struct CIAndOrigins {
+ using OriginMap = clang::ExternalASTMerger::OriginMap;
+ std::unique_ptr<CompilerInstance> CI;
+
+ ASTContext &getASTContext() { return CI->getASTContext(); }
+ FileManager &getFileManager() { return CI->getFileManager(); }
+ const OriginMap &getOriginMap() {
+ static const OriginMap EmptyOriginMap;
+ if (ExternalASTSource *Source = CI->getASTContext().getExternalSource())
+ return static_cast<ExternalASTMerger *>(Source)->GetOrigins();
+ return EmptyOriginMap;
}
-
- bool FindExternalVisibleDeclsByName(const DeclContext *DC,
- DeclarationName Name) override {
- llvm::SmallVector<NamedDecl *, 1> Decls;
-
- if (isa<TranslationUnitDecl>(DC)) {
- for (const std::unique_ptr<CompilerInstance> &I : ImportCIs) {
- DeclarationName FromName = ReverseImporters[I.get()]->Import(Name);
- DeclContextLookupResult Result =
- I->getASTContext().getTranslationUnitDecl()->lookup(FromName);
- for (NamedDecl *FromD : Result) {
- NamedDecl *D =
- llvm::cast<NamedDecl>(ForwardImporters[I.get()]->Import(FromD));
- Decls.push_back(D);
- }
- }
- }
- if (Decls.empty()) {
- return false;
- } else {
- SetExternalVisibleDeclsForName(DC, Name, Decls);
- return true;
- }
+ DiagnosticConsumer &getDiagnosticClient() {
+ return CI->getDiagnosticClient();
}
+ CompilerInstance &getCompilerInstance() { return *CI; }
};
-void AddExternalSource(
- CompilerInstance &CI,
- llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
- ASTContext &AST = CI.getASTContext();
- auto ES = llvm::make_unique<TestExternalASTSource>(CI, Imports);
- AST.setExternalSource(ES.release());
- AST.getTranslationUnitDecl()->setHasExternalVisibleStorage();
+void AddExternalSource(CIAndOrigins &CI,
+ llvm::MutableArrayRef<CIAndOrigins> Imports) {
+ ExternalASTMerger::ImporterTarget Target(
+ {CI.getASTContext(), CI.getFileManager()});
+ llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
+ for (CIAndOrigins &Import : Imports)
+ Sources.push_back(
+ {Import.getASTContext(), Import.getFileManager(), Import.getOriginMap()});
+ auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
+ CI.getASTContext().setExternalSource(ES.release());
+ CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
+}
+
+CIAndOrigins BuildIndirect(CIAndOrigins &CI) {
+ CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()};
+ auto ST = llvm::make_unique<SelectorTable>();
+ auto BC = llvm::make_unique<Builtin::Context>();
+ std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext(
+ IndirectCI.getCompilerInstance(), *ST, *BC);
+ IndirectCI.getCompilerInstance().setASTContext(AST.release());
+ AddExternalSource(IndirectCI, CI);
+ return IndirectCI;
}
llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
- CodeGenerator &CG) {
+ ASTConsumer &Consumer) {
SourceManager &SM = CI.getSourceManager();
const FileEntry *FE = CI.getFileManager().getFile(Path);
if (!FE) {
@@ -255,65 +288,91 @@ llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
llvm::Twine("Couldn't open ", Path), std::error_code());
}
SM.setMainFileID(SM.createFileID(FE, SourceLocation(), SrcMgr::C_User));
- ParseAST(CI.getPreprocessor(), &CG, CI.getASTContext());
+ ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext());
return llvm::Error::success();
}
-llvm::Expected<std::unique_ptr<CompilerInstance>>
-Parse(const std::string &Path,
- llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
- std::vector<const char *> ClangArgv(ClangArgs.size());
- std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
- [](const std::string &s) -> const char * { return s.data(); });
- std::unique_ptr<CompilerInstance> CI =
- init_convenience::BuildCompilerInstance(ClangArgv);
+llvm::Expected<CIAndOrigins> Parse(const std::string &Path,
+ llvm::MutableArrayRef<CIAndOrigins> Imports,
+ bool ShouldDumpAST, bool ShouldDumpIR) {
+ CIAndOrigins CI{init_convenience::BuildCompilerInstance()};
auto ST = llvm::make_unique<SelectorTable>();
auto BC = llvm::make_unique<Builtin::Context>();
std::unique_ptr<ASTContext> AST =
- init_convenience::BuildASTContext(*CI, *ST, *BC);
- CI->setASTContext(AST.release());
- AddExternalSource(*CI, Imports);
+ init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC);
+ CI.getCompilerInstance().setASTContext(AST.release());
+ if (Imports.size())
+ AddExternalSource(CI, Imports);
+
+ std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers;
auto LLVMCtx = llvm::make_unique<llvm::LLVMContext>();
- std::unique_ptr<CodeGenerator> CG =
- init_convenience::BuildCodeGen(*CI, *LLVMCtx);
- CG->Initialize(CI->getASTContext());
+ ASTConsumers.push_back(
+ init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx));
+ auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get());
- CI->getDiagnosticClient().BeginSourceFile(CI->getLangOpts(),
- &CI->getPreprocessor());
- if (llvm::Error PE = ParseSource(Path, *CI, *CG)) {
+ if (ShouldDumpAST)
+ ASTConsumers.push_back(CreateASTDumper("", true, false, false));
+
+ CI.getDiagnosticClient().BeginSourceFile(
+ CI.getCompilerInstance().getLangOpts(),
+ &CI.getCompilerInstance().getPreprocessor());
+ MultiplexConsumer Consumers(std::move(ASTConsumers));
+ Consumers.Initialize(CI.getASTContext());
+
+ if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers))
return std::move(PE);
- }
- CI->getDiagnosticClient().EndSourceFile();
- if (CI->getDiagnosticClient().getNumErrors()) {
+ CI.getDiagnosticClient().EndSourceFile();
+ if (ShouldDumpIR)
+ CG.GetModule()->print(llvm::outs(), nullptr);
+ if (CI.getDiagnosticClient().getNumErrors())
return llvm::make_error<llvm::StringError>(
"Errors occured while parsing the expression.", std::error_code());
- } else {
- return std::move(CI);
- }
+ return std::move(CI);
}
+
+void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) {
+ llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources;
+ for (CIAndOrigins &Import : Imports)
+ Sources.push_back(
+ {Import.getASTContext(), Import.getFileManager(), Import.getOriginMap()});
+ ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource();
+ auto *Merger = static_cast<ExternalASTMerger *>(Source);
+ Merger->RemoveSources(Sources);
+}
+
} // end namespace
int main(int argc, const char **argv) {
const bool DisableCrashReporting = true;
llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
llvm::cl::ParseCommandLineOptions(argc, argv);
- std::vector<std::unique_ptr<CompilerInstance>> ImportCIs;
+ std::vector<CIAndOrigins> ImportCIs;
for (auto I : Imports) {
- llvm::Expected<std::unique_ptr<CompilerInstance>> ImportCI = Parse(I, {});
+ llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false);
if (auto E = ImportCI.takeError()) {
llvm::errs() << llvm::toString(std::move(E));
exit(-1);
- } else {
- ImportCIs.push_back(std::move(*ImportCI));
+ }
+ ImportCIs.push_back(std::move(*ImportCI));
+ }
+ std::vector<CIAndOrigins> IndirectCIs;
+ if (!Direct || UseOrigins) {
+ for (auto &ImportCI : ImportCIs) {
+ CIAndOrigins IndirectCI = BuildIndirect(ImportCI);
+ IndirectCIs.push_back(std::move(IndirectCI));
}
}
- llvm::Expected<std::unique_ptr<CompilerInstance>> ExpressionCI =
- Parse(Expression, ImportCIs);
+ if (UseOrigins)
+ for (auto &ImportCI : ImportCIs)
+ IndirectCIs.push_back(std::move(ImportCI));
+ llvm::Expected<CIAndOrigins> ExpressionCI =
+ Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs,
+ DumpAST, DumpIR);
if (auto E = ExpressionCI.takeError()) {
llvm::errs() << llvm::toString(std::move(E));
exit(-1);
- } else {
- return 0;
}
+ Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs);
+ return 0;
}
diff --git a/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/tools/clang-offload-bundler/ClangOffloadBundler.cpp
index f37868fb61..6ff4becb50 100644
--- a/tools/clang-offload-bundler/ClangOffloadBundler.cpp
+++ b/tools/clang-offload-bundler/ClangOffloadBundler.cpp
@@ -412,7 +412,7 @@ class ObjectFileHandler final : public FileHandler {
/// read from the buffers.
unsigned NumberOfProcessedInputs = 0;
- /// LLVM context used to to create the auxiliar modules.
+ /// LLVM context used to to create the auxiliary modules.
LLVMContext VMContext;
/// LLVM module used to create an object with all the bundle
@@ -915,8 +915,7 @@ static bool UnbundleFiles() {
return false;
}
-static void PrintVersion() {
- raw_ostream &OS = outs();
+static void PrintVersion(raw_ostream &OS) {
OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n';
}
@@ -932,8 +931,10 @@ int main(int argc, const char **argv) {
"one. The resulting file can also be unbundled into different files by \n"
"this tool if -unbundle is provided.\n");
- if (Help)
+ if (Help) {
cl::PrintHelpMessage();
+ return 0;
+ }
bool Error = false;
if (Unbundle) {
diff --git a/tools/clang-refactor/CMakeLists.txt b/tools/clang-refactor/CMakeLists.txt
new file mode 100644
index 0000000000..c20e83bacf
--- /dev/null
+++ b/tools/clang-refactor/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+add_clang_tool(clang-refactor
+ ClangRefactor.cpp
+ TestSupport.cpp
+ )
+
+target_link_libraries(clang-refactor
+ clangAST
+ clangBasic
+ clangFormat
+ clangFrontend
+ clangLex
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
+
+install(TARGETS clang-refactor RUNTIME DESTINATION bin)
diff --git a/tools/clang-refactor/ClangRefactor.cpp b/tools/clang-refactor/ClangRefactor.cpp
new file mode 100644
index 0000000000..523c3a6220
--- /dev/null
+++ b/tools/clang-refactor/ClangRefactor.cpp
@@ -0,0 +1,577 @@
+//===--- ClangRefactor.cpp - Clang-based refactoring tool -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements a clang-refactor tool that performs various
+/// source transformations.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TestSupport.h"
+#include "clang/Frontend/CommandLineSourceLoc.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/RefactoringAction.h"
+#include "clang/Tooling/Refactoring/RefactoringOptions.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace clang;
+using namespace tooling;
+using namespace refactor;
+namespace cl = llvm::cl;
+
+namespace opts {
+
+static cl::OptionCategory CommonRefactorOptions("Refactoring options");
+
+static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"),
+ cl::cat(cl::GeneralCategory),
+ cl::sub(*cl::AllSubCommands));
+
+static cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s"),
+ cl::cat(cl::GeneralCategory),
+ cl::sub(*cl::AllSubCommands));
+
+} // end namespace opts
+
+namespace {
+
+/// Stores the parsed `-selection` argument.
+class SourceSelectionArgument {
+public:
+ virtual ~SourceSelectionArgument() {}
+
+ /// Parse the `-selection` argument.
+ ///
+ /// \returns A valid argument when the parse succedeed, null otherwise.
+ static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value);
+
+ /// Prints any additional state associated with the selection argument to
+ /// the given output stream.
+ virtual void print(raw_ostream &OS) {}
+
+ /// Returns a replacement refactoring result consumer (if any) that should
+ /// consume the results of a refactoring operation.
+ ///
+ /// The replacement refactoring result consumer is used by \c
+ /// TestSourceSelectionArgument to inject a test-specific result handling
+ /// logic into the refactoring operation. The test-specific consumer
+ /// ensures that the individual results in a particular test group are
+ /// identical.
+ virtual std::unique_ptr<ClangRefactorToolConsumerInterface>
+ createCustomConsumer() {
+ return nullptr;
+ }
+
+ /// Runs the give refactoring function for each specified selection.
+ ///
+ /// \returns true if an error occurred, false otherwise.
+ virtual bool
+ forAllRanges(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange R)> Callback) = 0;
+};
+
+/// Stores the parsed -selection=test:<filename> option.
+class TestSourceSelectionArgument final : public SourceSelectionArgument {
+public:
+ TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections)
+ : TestSelections(std::move(TestSelections)) {}
+
+ void print(raw_ostream &OS) override { TestSelections.dump(OS); }
+
+ std::unique_ptr<ClangRefactorToolConsumerInterface>
+ createCustomConsumer() override {
+ return TestSelections.createConsumer();
+ }
+
+ /// Testing support: invokes the selection action for each selection range in
+ /// the test file.
+ bool forAllRanges(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange R)> Callback) override {
+ return TestSelections.foreachRange(SM, Callback);
+ }
+
+private:
+ TestSelectionRangesInFile TestSelections;
+};
+
+/// Stores the parsed -selection=filename:line:column[-line:column] option.
+class SourceRangeSelectionArgument final : public SourceSelectionArgument {
+public:
+ SourceRangeSelectionArgument(ParsedSourceRange Range)
+ : Range(std::move(Range)) {}
+
+ bool forAllRanges(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange R)> Callback) override {
+ const FileEntry *FE = SM.getFileManager().getFile(Range.FileName);
+ FileID FID = FE ? SM.translateFile(FE) : FileID();
+ if (!FE || FID.isInvalid()) {
+ llvm::errs() << "error: -selection=" << Range.FileName
+ << ":... : given file is not in the target TU\n";
+ return true;
+ }
+
+ SourceLocation Start = SM.getMacroArgExpandedLocation(
+ SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second));
+ SourceLocation End = SM.getMacroArgExpandedLocation(
+ SM.translateLineCol(FID, Range.End.first, Range.End.second));
+ if (Start.isInvalid() || End.isInvalid()) {
+ llvm::errs() << "error: -selection=" << Range.FileName << ':'
+ << Range.Begin.first << ':' << Range.Begin.second << '-'
+ << Range.End.first << ':' << Range.End.second
+ << " : invalid source location\n";
+ return true;
+ }
+ Callback(SourceRange(Start, End));
+ return false;
+ }
+
+private:
+ ParsedSourceRange Range;
+};
+
+std::unique_ptr<SourceSelectionArgument>
+SourceSelectionArgument::fromString(StringRef Value) {
+ if (Value.startswith("test:")) {
+ StringRef Filename = Value.drop_front(strlen("test:"));
+ Optional<TestSelectionRangesInFile> ParsedTestSelection =
+ findTestSelectionRanges(Filename);
+ if (!ParsedTestSelection)
+ return nullptr; // A parsing error was already reported.
+ return llvm::make_unique<TestSourceSelectionArgument>(
+ std::move(*ParsedTestSelection));
+ }
+ Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value);
+ if (Range)
+ return llvm::make_unique<SourceRangeSelectionArgument>(std::move(*Range));
+ llvm::errs() << "error: '-selection' option must be specified using "
+ "<file>:<line>:<column> or "
+ "<file>:<line>:<column>-<line>:<column> format\n";
+ return nullptr;
+}
+
+/// A container that stores the command-line options used by a single
+/// refactoring option.
+class RefactoringActionCommandLineOptions {
+public:
+ void addStringOption(const RefactoringOption &Option,
+ std::unique_ptr<cl::opt<std::string>> CLOption) {
+ StringOptions[&Option] = std::move(CLOption);
+ }
+
+ const cl::opt<std::string> &
+ getStringOption(const RefactoringOption &Opt) const {
+ auto It = StringOptions.find(&Opt);
+ return *It->second;
+ }
+
+private:
+ llvm::DenseMap<const RefactoringOption *,
+ std::unique_ptr<cl::opt<std::string>>>
+ StringOptions;
+};
+
+/// Passes the command-line option values to the options used by a single
+/// refactoring action rule.
+class CommandLineRefactoringOptionVisitor final
+ : public RefactoringOptionVisitor {
+public:
+ CommandLineRefactoringOptionVisitor(
+ const RefactoringActionCommandLineOptions &Options)
+ : Options(Options) {}
+
+ void visit(const RefactoringOption &Opt,
+ Optional<std::string> &Value) override {
+ const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt);
+ if (!CLOpt.getValue().empty()) {
+ Value = CLOpt.getValue();
+ return;
+ }
+ Value = None;
+ if (Opt.isRequired())
+ MissingRequiredOptions.push_back(&Opt);
+ }
+
+ ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const {
+ return MissingRequiredOptions;
+ }
+
+private:
+ llvm::SmallVector<const RefactoringOption *, 4> MissingRequiredOptions;
+ const RefactoringActionCommandLineOptions &Options;
+};
+
+/// Creates the refactoring options used by all the rules in a single
+/// refactoring action.
+class CommandLineRefactoringOptionCreator final
+ : public RefactoringOptionVisitor {
+public:
+ CommandLineRefactoringOptionCreator(
+ cl::OptionCategory &Category, cl::SubCommand &Subcommand,
+ RefactoringActionCommandLineOptions &Options)
+ : Category(Category), Subcommand(Subcommand), Options(Options) {}
+
+ void visit(const RefactoringOption &Opt, Optional<std::string> &) override {
+ if (Visited.insert(&Opt).second)
+ Options.addStringOption(Opt, create<std::string>(Opt));
+ }
+
+private:
+ template <typename T>
+ std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) {
+ if (!OptionNames.insert(Opt.getName()).second)
+ llvm::report_fatal_error("Multiple identical refactoring options "
+ "specified for one refactoring action");
+ // FIXME: cl::Required can be specified when this option is present
+ // in all rules in an action.
+ return llvm::make_unique<cl::opt<T>>(
+ Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional,
+ cl::cat(Category), cl::sub(Subcommand));
+ }
+
+ llvm::SmallPtrSet<const RefactoringOption *, 8> Visited;
+ llvm::StringSet<> OptionNames;
+ cl::OptionCategory &Category;
+ cl::SubCommand &Subcommand;
+ RefactoringActionCommandLineOptions &Options;
+};
+
+/// A subcommand that corresponds to individual refactoring action.
+class RefactoringActionSubcommand : public cl::SubCommand {
+public:
+ RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action,
+ RefactoringActionRules ActionRules,
+ cl::OptionCategory &Category)
+ : SubCommand(Action->getCommand(), Action->getDescription()),
+ Action(std::move(Action)), ActionRules(std::move(ActionRules)) {
+ // Check if the selection option is supported.
+ bool HasSelection = false;
+ for (const auto &Rule : this->ActionRules) {
+ if ((HasSelection = Rule->hasSelectionRequirement()))
+ break;
+ }
+ if (HasSelection) {
+ Selection = llvm::make_unique<cl::opt<std::string>>(
+ "selection",
+ cl::desc("The selected source range in which the refactoring should "
+ "be initiated (<file>:<line>:<column>-<line>:<column> or "
+ "<file>:<line>:<column>)"),
+ cl::cat(Category), cl::sub(*this));
+ }
+ // Create the refactoring options.
+ for (const auto &Rule : this->ActionRules) {
+ CommandLineRefactoringOptionCreator OptionCreator(Category, *this,
+ Options);
+ Rule->visitRefactoringOptions(OptionCreator);
+ }
+ }
+
+ ~RefactoringActionSubcommand() { unregisterSubCommand(); }
+
+ const RefactoringActionRules &getActionRules() const { return ActionRules; }
+
+ /// Parses the command-line arguments that are specific to this rule.
+ ///
+ /// \returns true on error, false otherwise.
+ bool parseArguments() {
+ if (Selection) {
+ ParsedSelection = SourceSelectionArgument::fromString(*Selection);
+ if (!ParsedSelection)
+ return true;
+ }
+ return false;
+ }
+
+ SourceSelectionArgument *getSelection() const {
+ assert(Selection && "selection not supported!");
+ return ParsedSelection.get();
+ }
+
+ const RefactoringActionCommandLineOptions &getOptions() const {
+ return Options;
+ }
+
+private:
+ std::unique_ptr<RefactoringAction> Action;
+ RefactoringActionRules ActionRules;
+ std::unique_ptr<cl::opt<std::string>> Selection;
+ std::unique_ptr<SourceSelectionArgument> ParsedSelection;
+ RefactoringActionCommandLineOptions Options;
+};
+
+class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface {
+public:
+ ClangRefactorConsumer() {}
+
+ void handleError(llvm::Error Err) override {
+ Optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err);
+ if (!Diag) {
+ llvm::errs() << llvm::toString(std::move(Err)) << "\n";
+ return;
+ }
+ llvm::cantFail(std::move(Err)); // This is a success.
+ DiagnosticBuilder DB(
+ getDiags().Report(Diag->first, Diag->second.getDiagID()));
+ Diag->second.Emit(DB);
+ }
+
+ void handle(AtomicChanges Changes) override {
+ SourceChanges.insert(SourceChanges.begin(), Changes.begin(), Changes.end());
+ }
+
+ void handle(SymbolOccurrences Occurrences) override {
+ llvm_unreachable("symbol occurrence results are not handled yet");
+ }
+
+ const AtomicChanges &getSourceChanges() const { return SourceChanges; }
+
+private:
+ AtomicChanges SourceChanges;
+};
+
+class ClangRefactorTool {
+public:
+ std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands;
+
+ ClangRefactorTool() {
+ std::vector<std::unique_ptr<RefactoringAction>> Actions =
+ createRefactoringActions();
+
+ // Actions must have unique command names so that we can map them to one
+ // subcommand.
+ llvm::StringSet<> CommandNames;
+ for (const auto &Action : Actions) {
+ if (!CommandNames.insert(Action->getCommand()).second) {
+ llvm::errs() << "duplicate refactoring action command '"
+ << Action->getCommand() << "'!";
+ exit(1);
+ }
+ }
+
+ // Create subcommands and command-line options.
+ for (auto &Action : Actions) {
+ SubCommands.push_back(llvm::make_unique<RefactoringActionSubcommand>(
+ std::move(Action), Action->createActiveActionRules(),
+ opts::CommonRefactorOptions));
+ }
+ }
+
+ using TUCallbackType = llvm::function_ref<void(ASTContext &)>;
+
+ /// Parses the translation units that were given to the subcommand using
+ /// the 'sources' option and invokes the callback for each parsed
+ /// translation unit.
+ bool foreachTranslationUnit(const CompilationDatabase &DB,
+ ArrayRef<std::string> Sources,
+ TUCallbackType Callback) {
+ class ToolASTConsumer : public ASTConsumer {
+ public:
+ TUCallbackType Callback;
+ ToolASTConsumer(TUCallbackType Callback) : Callback(Callback) {}
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ Callback(Context);
+ }
+ };
+ class ActionWrapper {
+ public:
+ TUCallbackType Callback;
+ ActionWrapper(TUCallbackType Callback) : Callback(Callback) {}
+
+ std::unique_ptr<ASTConsumer> newASTConsumer() {
+ return llvm::make_unique<ToolASTConsumer>(Callback);
+ }
+ };
+
+ ClangTool Tool(DB, Sources);
+ ActionWrapper ToolAction(Callback);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory =
+ tooling::newFrontendActionFactory(&ToolAction);
+ return Tool.run(Factory.get());
+ }
+
+ /// Logs an individual refactoring action invocation to STDOUT.
+ void logInvocation(RefactoringActionSubcommand &Subcommand,
+ const RefactoringRuleContext &Context) {
+ if (!opts::Verbose)
+ return;
+ llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n";
+ if (Context.getSelectionRange().isValid()) {
+ SourceRange R = Context.getSelectionRange();
+ llvm::outs() << " -selection=";
+ R.getBegin().print(llvm::outs(), Context.getSources());
+ llvm::outs() << " -> ";
+ R.getEnd().print(llvm::outs(), Context.getSources());
+ llvm::outs() << "\n";
+ }
+ }
+
+ bool applySourceChanges(const AtomicChanges &Replacements) {
+ std::set<std::string> Files;
+ for (const auto &Change : Replacements)
+ Files.insert(Change.getFilePath());
+ // FIXME: Add automatic formatting support as well.
+ tooling::ApplyChangesSpec Spec;
+ // FIXME: We should probably cleanup the result by default as well.
+ Spec.Cleanup = false;
+ for (const auto &File : Files) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
+ llvm::MemoryBuffer::getFile(File);
+ if (!BufferErr) {
+ llvm::errs() << "error: failed to open " << File << " for rewriting\n";
+ return true;
+ }
+ auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
+ Replacements, Spec);
+ if (!Result) {
+ llvm::errs() << toString(Result.takeError());
+ return true;
+ }
+
+ if (opts::Inplace) {
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ llvm::errs() << EC.message() << "\n";
+ return true;
+ }
+ OS << *Result;
+ continue;
+ }
+
+ llvm::outs() << *Result;
+ }
+ return false;
+ }
+
+ bool invokeAction(RefactoringActionSubcommand &Subcommand,
+ const CompilationDatabase &DB,
+ ArrayRef<std::string> Sources) {
+ // Find a set of matching rules.
+ SmallVector<RefactoringActionRule *, 4> MatchingRules;
+ llvm::StringSet<> MissingOptions;
+
+ bool HasSelection = false;
+ for (const auto &Rule : Subcommand.getActionRules()) {
+ bool SelectionMatches = true;
+ if (Rule->hasSelectionRequirement()) {
+ HasSelection = true;
+ if (!Subcommand.getSelection()) {
+ MissingOptions.insert("selection");
+ SelectionMatches = false;
+ }
+ }
+ CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions());
+ Rule->visitRefactoringOptions(Visitor);
+ if (SelectionMatches && Visitor.getMissingRequiredOptions().empty()) {
+ MatchingRules.push_back(Rule.get());
+ continue;
+ }
+ for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions())
+ MissingOptions.insert(Opt->getName());
+ }
+ if (MatchingRules.empty()) {
+ llvm::errs() << "error: '" << Subcommand.getName()
+ << "' can't be invoked with the given arguments:\n";
+ for (const auto &Opt : MissingOptions)
+ llvm::errs() << " missing '-" << Opt.getKey() << "' option\n";
+ return true;
+ }
+
+ ClangRefactorConsumer Consumer;
+ bool HasFailed = false;
+ if (foreachTranslationUnit(DB, Sources, [&](ASTContext &AST) {
+ RefactoringRuleContext Context(AST.getSourceManager());
+ Context.setASTContext(AST);
+
+ auto InvokeRule = [&](RefactoringResultConsumer &Consumer) {
+ logInvocation(Subcommand, Context);
+ for (RefactoringActionRule *Rule : MatchingRules) {
+ if (!Rule->hasSelectionRequirement())
+ continue;
+ Rule->invoke(Consumer, Context);
+ return;
+ }
+ // FIXME (Alex L): If more than one initiation succeeded, then the
+ // rules are ambiguous.
+ llvm_unreachable(
+ "The action must have at least one selection rule");
+ };
+
+ std::unique_ptr<ClangRefactorToolConsumerInterface> CustomConsumer;
+ if (HasSelection)
+ CustomConsumer = Subcommand.getSelection()->createCustomConsumer();
+ ClangRefactorToolConsumerInterface &ActiveConsumer =
+ CustomConsumer ? *CustomConsumer : Consumer;
+ ActiveConsumer.beginTU(AST);
+ if (HasSelection) {
+ assert(Subcommand.getSelection() && "Missing selection argument?");
+ if (opts::Verbose)
+ Subcommand.getSelection()->print(llvm::outs());
+ if (Subcommand.getSelection()->forAllRanges(
+ Context.getSources(), [&](SourceRange R) {
+ Context.setSelectionRange(R);
+ InvokeRule(ActiveConsumer);
+ }))
+ HasFailed = true;
+ ActiveConsumer.endTU();
+ return;
+ }
+ // FIXME (Alex L): Implement non-selection based invocation path.
+ ActiveConsumer.endTU();
+ }))
+ return true;
+ return HasFailed || applySourceChanges(Consumer.getSourceChanges());
+ }
+};
+
+} // end anonymous namespace
+
+int main(int argc, const char **argv) {
+ ClangRefactorTool Tool;
+
+ CommonOptionsParser Options(
+ argc, argv, cl::GeneralCategory, cl::ZeroOrMore,
+ "Clang-based refactoring tool for C, C++ and Objective-C");
+
+ // Figure out which action is specified by the user. The user must specify
+ // the action using a command-line subcommand, e.g. the invocation
+ // `clang-refactor local-rename` corresponds to the `LocalRename` refactoring
+ // action. All subcommands must have a unique names. This allows us to figure
+ // out which refactoring action should be invoked by looking at the first
+ // subcommand that's enabled by LLVM's command-line parser.
+ auto It = llvm::find_if(
+ Tool.SubCommands,
+ [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) {
+ return !!(*SubCommand);
+ });
+ if (It == Tool.SubCommands.end()) {
+ llvm::errs() << "error: no refactoring action given\n";
+ llvm::errs() << "note: the following actions are supported:\n";
+ for (const auto &Subcommand : Tool.SubCommands)
+ llvm::errs().indent(2) << Subcommand->getName() << "\n";
+ return 1;
+ }
+ RefactoringActionSubcommand &ActionCommand = **It;
+
+ if (ActionCommand.parseArguments())
+ return 1;
+ if (Tool.invokeAction(ActionCommand, Options.getCompilations(),
+ Options.getSourcePathList()))
+ return 1;
+
+ return 0;
+}
diff --git a/tools/clang-refactor/TestSupport.cpp b/tools/clang-refactor/TestSupport.cpp
new file mode 100644
index 0000000000..9331dfd92e
--- /dev/null
+++ b/tools/clang-refactor/TestSupport.cpp
@@ -0,0 +1,392 @@
+//===--- TestSupport.cpp - Clang-based refactoring tool -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements routines that provide refactoring testing
+/// utilities.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TestSupport.h"
+#include "clang/Basic/DiagnosticError.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace refactor {
+
+void TestSelectionRangesInFile::dump(raw_ostream &OS) const {
+ for (const auto &Group : GroupedRanges) {
+ OS << "Test selection group '" << Group.Name << "':\n";
+ for (const auto &Range : Group.Ranges) {
+ OS << " " << Range.Begin << "-" << Range.End << "\n";
+ }
+ }
+}
+
+bool TestSelectionRangesInFile::foreachRange(
+ const SourceManager &SM,
+ llvm::function_ref<void(SourceRange)> Callback) const {
+ const FileEntry *FE = SM.getFileManager().getFile(Filename);
+ FileID FID = FE ? SM.translateFile(FE) : FileID();
+ if (!FE || FID.isInvalid()) {
+ llvm::errs() << "error: -selection=test:" << Filename
+ << " : given file is not in the target TU";
+ return true;
+ }
+ SourceLocation FileLoc = SM.getLocForStartOfFile(FID);
+ for (const auto &Group : GroupedRanges) {
+ for (const TestSelectionRange &Range : Group.Ranges) {
+ // Translate the offset pair to a true source range.
+ SourceLocation Start =
+ SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Range.Begin));
+ SourceLocation End =
+ SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Range.End));
+ assert(Start.isValid() && End.isValid() && "unexpected invalid range");
+ Callback(SourceRange(Start, End));
+ }
+ }
+ return false;
+}
+
+namespace {
+
+void dumpChanges(const tooling::AtomicChanges &Changes, raw_ostream &OS) {
+ for (const auto &Change : Changes)
+ OS << const_cast<tooling::AtomicChange &>(Change).toYAMLString() << "\n";
+}
+
+bool areChangesSame(const tooling::AtomicChanges &LHS,
+ const tooling::AtomicChanges &RHS) {
+ if (LHS.size() != RHS.size())
+ return false;
+ for (const auto &I : llvm::zip(LHS, RHS)) {
+ if (!(std::get<0>(I) == std::get<1>(I)))
+ return false;
+ }
+ return true;
+}
+
+bool printRewrittenSources(const tooling::AtomicChanges &Changes,
+ raw_ostream &OS) {
+ std::set<std::string> Files;
+ for (const auto &Change : Changes)
+ Files.insert(Change.getFilePath());
+ tooling::ApplyChangesSpec Spec;
+ Spec.Cleanup = false;
+ for (const auto &File : Files) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr =
+ llvm::MemoryBuffer::getFile(File);
+ if (!BufferErr) {
+ llvm::errs() << "failed to open" << File << "\n";
+ return true;
+ }
+ auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(),
+ Changes, Spec);
+ if (!Result) {
+ llvm::errs() << toString(Result.takeError());
+ return true;
+ }
+ OS << *Result;
+ }
+ return false;
+}
+
+class TestRefactoringResultConsumer final
+ : public ClangRefactorToolConsumerInterface {
+public:
+ TestRefactoringResultConsumer(const TestSelectionRangesInFile &TestRanges)
+ : TestRanges(TestRanges) {
+ Results.push_back({});
+ }
+
+ ~TestRefactoringResultConsumer() {
+ // Ensure all results are checked.
+ for (auto &Group : Results) {
+ for (auto &Result : Group) {
+ if (!Result) {
+ (void)llvm::toString(Result.takeError());
+ }
+ }
+ }
+ }
+
+ void handleError(llvm::Error Err) override { handleResult(std::move(Err)); }
+
+ void handle(tooling::AtomicChanges Changes) override {
+ handleResult(std::move(Changes));
+ }
+
+ void handle(tooling::SymbolOccurrences Occurrences) override {
+ tooling::RefactoringResultConsumer::handle(std::move(Occurrences));
+ }
+
+private:
+ bool handleAllResults();
+
+ void handleResult(Expected<tooling::AtomicChanges> Result) {
+ Results.back().push_back(std::move(Result));
+ size_t GroupIndex = Results.size() - 1;
+ if (Results.back().size() >=
+ TestRanges.GroupedRanges[GroupIndex].Ranges.size()) {
+ ++GroupIndex;
+ if (GroupIndex >= TestRanges.GroupedRanges.size()) {
+ if (handleAllResults())
+ exit(1); // error has occurred.
+ return;
+ }
+ Results.push_back({});
+ }
+ }
+
+ const TestSelectionRangesInFile &TestRanges;
+ std::vector<std::vector<Expected<tooling::AtomicChanges>>> Results;
+};
+
+std::pair<unsigned, unsigned> getLineColumn(StringRef Filename,
+ unsigned Offset) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile =
+ MemoryBuffer::getFile(Filename);
+ if (!ErrOrFile)
+ return {0, 0};
+ StringRef Source = ErrOrFile.get()->getBuffer();
+ Source = Source.take_front(Offset);
+ size_t LastLine = Source.find_last_of("\r\n");
+ return {Source.count('\n') + 1,
+ (LastLine == StringRef::npos ? Offset : Offset - LastLine) + 1};
+}
+
+} // end anonymous namespace
+
+bool TestRefactoringResultConsumer::handleAllResults() {
+ bool Failed = false;
+ for (auto &Group : llvm::enumerate(Results)) {
+ // All ranges in the group must produce the same result.
+ Optional<tooling::AtomicChanges> CanonicalResult;
+ Optional<std::string> CanonicalErrorMessage;
+ for (auto &I : llvm::enumerate(Group.value())) {
+ Expected<tooling::AtomicChanges> &Result = I.value();
+ std::string ErrorMessage;
+ bool HasResult = !!Result;
+ if (!HasResult) {
+ handleAllErrors(
+ Result.takeError(),
+ [&](StringError &Err) { ErrorMessage = Err.getMessage(); },
+ [&](DiagnosticError &Err) {
+ const PartialDiagnosticAt &Diag = Err.getDiagnostic();
+ llvm::SmallString<100> DiagText;
+ Diag.second.EmitToString(getDiags(), DiagText);
+ ErrorMessage = DiagText.str().str();
+ });
+ }
+ if (!CanonicalResult && !CanonicalErrorMessage) {
+ if (HasResult)
+ CanonicalResult = std::move(*Result);
+ else
+ CanonicalErrorMessage = std::move(ErrorMessage);
+ continue;
+ }
+
+ // Verify that this result corresponds to the canonical result.
+ if (CanonicalErrorMessage) {
+ // The error messages must match.
+ if (!HasResult && ErrorMessage == *CanonicalErrorMessage)
+ continue;
+ } else {
+ assert(CanonicalResult && "missing canonical result");
+ // The results must match.
+ if (HasResult && areChangesSame(*Result, *CanonicalResult))
+ continue;
+ }
+ Failed = true;
+ // Report the mismatch.
+ std::pair<unsigned, unsigned> LineColumn = getLineColumn(
+ TestRanges.Filename,
+ TestRanges.GroupedRanges[Group.index()].Ranges[I.index()].Begin);
+ llvm::errs()
+ << "error: unexpected refactoring result for range starting at "
+ << LineColumn.first << ':' << LineColumn.second << " in group '"
+ << TestRanges.GroupedRanges[Group.index()].Name << "':\n ";
+ if (HasResult)
+ llvm::errs() << "valid result";
+ else
+ llvm::errs() << "error '" << ErrorMessage << "'";
+ llvm::errs() << " does not match initial ";
+ if (CanonicalErrorMessage)
+ llvm::errs() << "error '" << *CanonicalErrorMessage << "'\n";
+ else
+ llvm::errs() << "valid result\n";
+ if (HasResult && !CanonicalErrorMessage) {
+ llvm::errs() << " Expected to Produce:\n";
+ dumpChanges(*CanonicalResult, llvm::errs());
+ llvm::errs() << " Produced:\n";
+ dumpChanges(*Result, llvm::errs());
+ }
+ }
+
+ // Dump the results:
+ const auto &TestGroup = TestRanges.GroupedRanges[Group.index()];
+ if (!CanonicalResult) {
+ llvm::outs() << TestGroup.Ranges.size() << " '" << TestGroup.Name
+ << "' results:\n";
+ llvm::outs() << *CanonicalErrorMessage << "\n";
+ } else {
+ llvm::outs() << TestGroup.Ranges.size() << " '" << TestGroup.Name
+ << "' results:\n";
+ if (printRewrittenSources(*CanonicalResult, llvm::outs()))
+ return true;
+ }
+ }
+ return Failed;
+}
+
+std::unique_ptr<ClangRefactorToolConsumerInterface>
+TestSelectionRangesInFile::createConsumer() const {
+ return llvm::make_unique<TestRefactoringResultConsumer>(*this);
+}
+
+/// Adds the \p ColumnOffset to file offset \p Offset, without going past a
+/// newline.
+static unsigned addColumnOffset(StringRef Source, unsigned Offset,
+ unsigned ColumnOffset) {
+ if (!ColumnOffset)
+ return Offset;
+ StringRef Substr = Source.drop_front(Offset).take_front(ColumnOffset);
+ size_t NewlinePos = Substr.find_first_of("\r\n");
+ return Offset +
+ (NewlinePos == StringRef::npos ? ColumnOffset : (unsigned)NewlinePos);
+}
+
+static unsigned addEndLineOffsetAndEndColumn(StringRef Source, unsigned Offset,
+ unsigned LineNumberOffset,
+ unsigned Column) {
+ StringRef Line = Source.drop_front(Offset);
+ unsigned LineOffset = 0;
+ for (; LineNumberOffset != 0; --LineNumberOffset) {
+ size_t NewlinePos = Line.find_first_of("\r\n");
+ // Line offset goes out of bounds.
+ if (NewlinePos == StringRef::npos)
+ break;
+ LineOffset += NewlinePos + 1;
+ Line = Line.drop_front(NewlinePos + 1);
+ }
+ // Source now points to the line at +lineOffset;
+ size_t LineStart = Source.find_last_of("\r\n", /*From=*/Offset + LineOffset);
+ return addColumnOffset(
+ Source, LineStart == StringRef::npos ? 0 : LineStart + 1, Column - 1);
+}
+
+Optional<TestSelectionRangesInFile>
+findTestSelectionRanges(StringRef Filename) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile =
+ MemoryBuffer::getFile(Filename);
+ if (!ErrOrFile) {
+ llvm::errs() << "error: -selection=test:" << Filename
+ << " : could not open the given file";
+ return None;
+ }
+ StringRef Source = ErrOrFile.get()->getBuffer();
+
+ // See the doc comment for this function for the explanation of this
+ // syntax.
+ static Regex RangeRegex("range[[:blank:]]*([[:alpha:]_]*)?[[:blank:]]*=[[:"
+ "blank:]]*(\\+[[:digit:]]+)?[[:blank:]]*(->[[:blank:]"
+ "]*[\\+\\:[:digit:]]+)?");
+
+ std::map<std::string, SmallVector<TestSelectionRange, 8>> GroupedRanges;
+
+ LangOptions LangOpts;
+ LangOpts.CPlusPlus = 1;
+ LangOpts.CPlusPlus11 = 1;
+ Lexer Lex(SourceLocation::getFromRawEncoding(0), LangOpts, Source.begin(),
+ Source.begin(), Source.end());
+ Lex.SetCommentRetentionState(true);
+ Token Tok;
+ for (Lex.LexFromRawLexer(Tok); Tok.isNot(tok::eof);
+ Lex.LexFromRawLexer(Tok)) {
+ if (Tok.isNot(tok::comment))
+ continue;
+ StringRef Comment =
+ Source.substr(Tok.getLocation().getRawEncoding(), Tok.getLength());
+ SmallVector<StringRef, 4> Matches;
+ // Try to detect mistyped 'range:' comments to ensure tests don't miss
+ // anything.
+ auto DetectMistypedCommand = [&]() -> bool {
+ if (Comment.contains_lower("range") && Comment.contains("=") &&
+ !Comment.contains_lower("run") && !Comment.contains("CHECK")) {
+ llvm::errs() << "error: suspicious comment '" << Comment
+ << "' that "
+ "resembles the range command found\n";
+ llvm::errs() << "note: please reword if this isn't a range command\n";
+ }
+ return false;
+ };
+ // Allow CHECK: comments to contain range= commands.
+ if (!RangeRegex.match(Comment, &Matches) || Comment.contains("CHECK")) {
+ if (DetectMistypedCommand())
+ return None;
+ continue;
+ }
+ unsigned Offset = Tok.getEndLoc().getRawEncoding();
+ unsigned ColumnOffset = 0;
+ if (!Matches[2].empty()) {
+ // Don't forget to drop the '+'!
+ if (Matches[2].drop_front().getAsInteger(10, ColumnOffset))
+ assert(false && "regex should have produced a number");
+ }
+ Offset = addColumnOffset(Source, Offset, ColumnOffset);
+ unsigned EndOffset;
+
+ if (!Matches[3].empty()) {
+ static Regex EndLocRegex(
+ "->[[:blank:]]*(\\+[[:digit:]]+):([[:digit:]]+)");
+ SmallVector<StringRef, 4> EndLocMatches;
+ if (!EndLocRegex.match(Matches[3], &EndLocMatches)) {
+ if (DetectMistypedCommand())
+ return None;
+ continue;
+ }
+ unsigned EndLineOffset = 0, EndColumn = 0;
+ if (EndLocMatches[1].drop_front().getAsInteger(10, EndLineOffset) ||
+ EndLocMatches[2].getAsInteger(10, EndColumn))
+ assert(false && "regex should have produced a number");
+ EndOffset = addEndLineOffsetAndEndColumn(Source, Offset, EndLineOffset,
+ EndColumn);
+ } else {
+ EndOffset = Offset;
+ }
+ TestSelectionRange Range = {Offset, EndOffset};
+ auto It = GroupedRanges.insert(std::make_pair(
+ Matches[1].str(), SmallVector<TestSelectionRange, 8>{Range}));
+ if (!It.second)
+ It.first->second.push_back(Range);
+ }
+ if (GroupedRanges.empty()) {
+ llvm::errs() << "error: -selection=test:" << Filename
+ << ": no 'range' commands";
+ return None;
+ }
+
+ TestSelectionRangesInFile TestRanges = {Filename.str(), {}};
+ for (auto &Group : GroupedRanges)
+ TestRanges.GroupedRanges.push_back({Group.first, std::move(Group.second)});
+ return std::move(TestRanges);
+}
+
+} // end namespace refactor
+} // end namespace clang
diff --git a/tools/clang-refactor/TestSupport.h b/tools/clang-refactor/TestSupport.h
new file mode 100644
index 0000000000..101f35bd94
--- /dev/null
+++ b/tools/clang-refactor/TestSupport.h
@@ -0,0 +1,107 @@
+//===--- TestSupport.h - Clang-based refactoring tool -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Declares datatypes and routines that are used by test-specific code
+/// in clang-refactor.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H
+#define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H
+
+#include "ToolRefactoringResultConsumer.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
+#include <map>
+#include <string>
+
+namespace clang {
+
+class SourceManager;
+
+namespace refactor {
+
+/// A source selection range that's specified in a test file using an inline
+/// command in the comment. These commands can take the following forms:
+///
+/// - /*range=*/ will create an empty selection range in the default group
+/// right after the comment.
+/// - /*range a=*/ will create an empty selection range in the 'a' group right
+/// after the comment.
+/// - /*range = +1*/ will create an empty selection range at a location that's
+/// right after the comment with one offset to the column.
+/// - /*range= -> +2:3*/ will create a selection range that starts at the
+/// location right after the comment, and ends at column 3 of the 2nd line
+/// after the line of the starting location.
+///
+/// Clang-refactor will expected all ranges in one test group to produce
+/// identical results.
+struct TestSelectionRange {
+ unsigned Begin, End;
+};
+
+/// A set of test selection ranges specified in one file.
+struct TestSelectionRangesInFile {
+ std::string Filename;
+ struct RangeGroup {
+ std::string Name;
+ SmallVector<TestSelectionRange, 8> Ranges;
+ };
+ std::vector<RangeGroup> GroupedRanges;
+
+ TestSelectionRangesInFile(TestSelectionRangesInFile &&) = default;
+ TestSelectionRangesInFile &operator=(TestSelectionRangesInFile &&) = default;
+
+ bool foreachRange(const SourceManager &SM,
+ llvm::function_ref<void(SourceRange)> Callback) const;
+
+ std::unique_ptr<ClangRefactorToolConsumerInterface> createConsumer() const;
+
+ void dump(llvm::raw_ostream &OS) const;
+};
+
+/// Extracts the grouped selection ranges from the file that's specified in
+/// the -selection=test:<filename> option.
+///
+/// The grouped ranges are specified in comments using the following syntax:
+/// "range" [ group-name ] "=" [ "+" starting-column-offset ] [ "->"
+/// "+" ending-line-offset ":"
+/// ending-column-position ]
+///
+/// The selection range is then computed from this command by taking the ending
+/// location of the comment, and adding 'starting-column-offset' to the column
+/// for that location. That location in turns becomes the whole selection range,
+/// unless 'ending-line-offset' and 'ending-column-position' are specified. If
+/// they are specified, then the ending location of the selection range is
+/// the starting location's line + 'ending-line-offset' and the
+/// 'ending-column-position' column.
+///
+/// All selection ranges in one group are expected to produce the same
+/// refactoring result.
+///
+/// When testing, zero is returned from clang-refactor even when a group
+/// produces an initiation error, which is different from normal invocation
+/// that returns a non-zero value. This is done on purpose, to ensure that group
+/// consistency checks can return non-zero, but still print the output of
+/// the group. So even if a test matches the output of group, it will still fail
+/// because clang-refactor should return zero on exit when the group results are
+/// consistent.
+///
+/// \returns None on failure (errors are emitted to stderr), or a set of
+/// grouped source ranges in the given file otherwise.
+Optional<TestSelectionRangesInFile> findTestSelectionRanges(StringRef Filename);
+
+} // end namespace refactor
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H
diff --git a/tools/clang-refactor/ToolRefactoringResultConsumer.h b/tools/clang-refactor/ToolRefactoringResultConsumer.h
new file mode 100644
index 0000000000..64a994d88b
--- /dev/null
+++ b/tools/clang-refactor/ToolRefactoringResultConsumer.h
@@ -0,0 +1,48 @@
+//===--- ToolRefactoringResultConsumer.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_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H
+#define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"
+
+namespace clang {
+namespace refactor {
+
+/// An interface that subclasses the \c RefactoringResultConsumer interface
+/// that stores the reference to the TU-specific diagnostics engine.
+class ClangRefactorToolConsumerInterface
+ : public tooling::RefactoringResultConsumer {
+public:
+ /// Called when a TU is entered.
+ void beginTU(ASTContext &Context) {
+ assert(!Diags && "Diags has been set");
+ Diags = &Context.getDiagnostics();
+ }
+
+ /// Called when the tool is done with a TU.
+ void endTU() {
+ assert(Diags && "Diags unset");
+ Diags = nullptr;
+ }
+
+ DiagnosticsEngine &getDiags() const {
+ assert(Diags && "no diags");
+ return *Diags;
+ }
+
+private:
+ DiagnosticsEngine *Diags = nullptr;
+};
+
+} // end namespace refactor
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H
diff --git a/tools/clang-rename/CMakeLists.txt b/tools/clang-rename/CMakeLists.txt
new file mode 100644
index 0000000000..e74f05d821
--- /dev/null
+++ b/tools/clang-rename/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+add_clang_tool(clang-rename ClangRename.cpp)
+
+target_link_libraries(clang-rename
+ clangBasic
+ clangFrontend
+ clangRewrite
+ clangTooling
+ clangToolingCore
+ clangToolingRefactor
+ )
+
+install(PROGRAMS clang-rename.py
+ DESTINATION share/clang
+ COMPONENT clang-rename)
+install(PROGRAMS clang-rename.el
+ DESTINATION share/clang
+ COMPONENT clang-rename)
diff --git a/tools/clang-rename/ClangRename.cpp b/tools/clang-rename/ClangRename.cpp
new file mode 100644
index 0000000000..fca12ca60c
--- /dev/null
+++ b/tools/clang-rename/ClangRename.cpp
@@ -0,0 +1,233 @@
+//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file implements a clang-rename tool that automatically finds and
+/// renames symbols in C++ code.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
+#include "clang/Frontend/TextDiagnosticPrinter.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+#include <system_error>
+
+using namespace llvm;
+using namespace clang;
+
+/// \brief An oldname -> newname rename.
+struct RenameAllInfo {
+ unsigned Offset = 0;
+ std::string QualifiedName;
+ std::string NewName;
+};
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo)
+
+namespace llvm {
+namespace yaml {
+
+/// \brief Specialized MappingTraits to describe how a RenameAllInfo is
+/// (de)serialized.
+template <> struct MappingTraits<RenameAllInfo> {
+ static void mapping(IO &IO, RenameAllInfo &Info) {
+ IO.mapOptional("Offset", Info.Offset);
+ IO.mapOptional("QualifiedName", Info.QualifiedName);
+ IO.mapRequired("NewName", Info.NewName);
+ }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+static cl::OptionCategory ClangRenameOptions("clang-rename common options");
+
+static cl::list<unsigned> SymbolOffsets(
+ "offset",
+ cl::desc("Locates the symbol by offset as opposed to <line>:<column>."),
+ cl::ZeroOrMore, cl::cat(ClangRenameOptions));
+static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."),
+ cl::cat(ClangRenameOptions));
+static cl::list<std::string>
+ QualifiedNames("qualified-name",
+ cl::desc("The fully qualified name of the symbol."),
+ cl::ZeroOrMore, cl::cat(ClangRenameOptions));
+
+static cl::list<std::string>
+ NewNames("new-name", cl::desc("The new name to change the symbol to."),
+ cl::ZeroOrMore, cl::cat(ClangRenameOptions));
+static cl::opt<bool> PrintName(
+ "pn",
+ cl::desc("Print the found symbol's name prior to renaming to stderr."),
+ cl::cat(ClangRenameOptions));
+static cl::opt<bool> PrintLocations(
+ "pl", cl::desc("Print the locations affected by renaming to stderr."),
+ cl::cat(ClangRenameOptions));
+static cl::opt<std::string>
+ ExportFixes("export-fixes",
+ cl::desc("YAML file to store suggested fixes in."),
+ cl::value_desc("filename"), cl::cat(ClangRenameOptions));
+static cl::opt<std::string>
+ Input("input", cl::desc("YAML file to load oldname-newname pairs from."),
+ cl::Optional, cl::cat(ClangRenameOptions));
+static cl::opt<bool> Force("force",
+ cl::desc("Ignore nonexistent qualified names."),
+ cl::cat(ClangRenameOptions));
+
+int main(int argc, const char **argv) {
+ tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions);
+
+ if (!Input.empty()) {
+ // Populate QualifiedNames and NewNames from a YAML file.
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
+ llvm::MemoryBuffer::getFile(Input);
+ if (!Buffer) {
+ errs() << "clang-rename: failed to read " << Input << ": "
+ << Buffer.getError().message() << "\n";
+ return 1;
+ }
+
+ std::vector<RenameAllInfo> Infos;
+ llvm::yaml::Input YAML(Buffer.get()->getBuffer());
+ YAML >> Infos;
+ for (const auto &Info : Infos) {
+ if (!Info.QualifiedName.empty())
+ QualifiedNames.push_back(Info.QualifiedName);
+ else
+ SymbolOffsets.push_back(Info.Offset);
+ NewNames.push_back(Info.NewName);
+ }
+ }
+
+ // Check the arguments for correctness.
+ if (NewNames.empty()) {
+ errs() << "clang-rename: -new-name must be specified.\n\n";
+ return 1;
+ }
+
+ if (SymbolOffsets.empty() == QualifiedNames.empty()) {
+ errs() << "clang-rename: -offset and -qualified-name can't be present at "
+ "the same time.\n";
+ return 1;
+ }
+
+ // Check if NewNames is a valid identifier in C++17.
+ LangOptions Options;
+ Options.CPlusPlus = true;
+ Options.CPlusPlus1z = true;
+ IdentifierTable Table(Options);
+ for (const auto &NewName : NewNames) {
+ auto NewNameTokKind = Table.get(NewName).getTokenID();
+ if (!tok::isAnyIdentifier(NewNameTokKind)) {
+ errs() << "ERROR: new name is not a valid identifier in C++17.\n\n";
+ return 1;
+ }
+ }
+
+ if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) {
+ errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size()
+ << ") + number of qualified names (" << QualifiedNames.size()
+ << ") must be equal to number of new names(" << NewNames.size()
+ << ").\n\n";
+ cl::PrintHelpMessage();
+ return 1;
+ }
+
+ auto Files = OP.getSourcePathList();
+ tooling::RefactoringTool Tool(OP.getCompilations(), Files);
+ tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
+ Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
+ const std::vector<std::vector<std::string>> &USRList =
+ FindingAction.getUSRList();
+ const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings();
+ if (PrintName) {
+ for (const auto &PrevName : PrevNames) {
+ outs() << "clang-rename found name: " << PrevName << '\n';
+ }
+ }
+
+ if (FindingAction.errorOccurred()) {
+ // Diagnostics are already issued at this point.
+ return 1;
+ }
+
+ // Perform the renaming.
+ tooling::RenamingAction RenameAction(NewNames, PrevNames, USRList,
+ Tool.getReplacements(), PrintLocations);
+ std::unique_ptr<tooling::FrontendActionFactory> Factory =
+ tooling::newFrontendActionFactory(&RenameAction);
+ int ExitCode;
+
+ if (Inplace) {
+ ExitCode = Tool.runAndSave(Factory.get());
+ } else {
+ ExitCode = Tool.run(Factory.get());
+
+ if (!ExportFixes.empty()) {
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::F_None);
+ if (EC) {
+ llvm::errs() << "Error opening output file: " << EC.message() << '\n';
+ return 1;
+ }
+
+ // Export replacements.
+ tooling::TranslationUnitReplacements TUR;
+ const auto &FileToReplacements = Tool.getReplacements();
+ for (const auto &Entry : FileToReplacements)
+ TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(),
+ Entry.second.end());
+
+ yaml::Output YAML(OS);
+ YAML << TUR;
+ OS.close();
+ return 0;
+ }
+
+ // Write every file to stdout. Right now we just barf the files without any
+ // indication of which files start where, other than that we print the files
+ // in the same order we see them.
+ LangOptions DefaultLangOptions;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
+ TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts);
+ DiagnosticsEngine Diagnostics(
+ IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
+ &DiagnosticPrinter, false);
+ auto &FileMgr = Tool.getFiles();
+ SourceManager Sources(Diagnostics, FileMgr);
+ Rewriter Rewrite(Sources, DefaultLangOptions);
+
+ Tool.applyAllReplacements(Rewrite);
+ for (const auto &File : Files) {
+ const auto *Entry = FileMgr.getFile(File);
+ const auto ID = Sources.getOrCreateFileID(Entry, SrcMgr::C_User);
+ Rewrite.getEditBuffer(ID).write(outs());
+ }
+ }
+
+ return ExitCode;
+}
diff --git a/tools/clang-rename/clang-rename.el b/tools/clang-rename/clang-rename.el
new file mode 100644
index 0000000000..b6c3ed4c68
--- /dev/null
+++ b/tools/clang-rename/clang-rename.el
@@ -0,0 +1,79 @@
+;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>. -*- lexical-binding: t; -*-
+
+;; Keywords: tools, c
+
+;;; Commentary:
+
+;; To install clang-rename.el make sure the directory of this file is in your
+;; `load-path' and add
+;;
+;; (require 'clang-rename)
+;;
+;; to your .emacs configuration.
+
+;;; Code:
+
+(defgroup clang-rename nil
+ "Integration with clang-rename"
+ :group 'c)
+
+(defcustom clang-rename-binary "clang-rename"
+ "Path to clang-rename executable."
+ :type '(file :must-match t)
+ :group 'clang-rename)
+
+;;;###autoload
+(defun clang-rename (new-name)
+ "Rename all instances of the symbol at point to NEW-NAME using clang-rename."
+ (interactive "sEnter a new name: ")
+ (save-some-buffers :all)
+ ;; clang-rename should not be combined with other operations when undoing.
+ (undo-boundary)
+ (let ((output-buffer (get-buffer-create "*clang-rename*")))
+ (with-current-buffer output-buffer (erase-buffer))
+ (let ((exit-code (call-process
+ clang-rename-binary nil output-buffer nil
+ (format "-offset=%d"
+ ;; clang-rename wants file (byte) offsets, not
+ ;; buffer (character) positions.
+ (clang-rename--bufferpos-to-filepos
+ ;; Emacs treats one character after a symbol as
+ ;; part of the symbol, but clang-rename doesn’t.
+ ;; Use the beginning of the current symbol, if
+ ;; available, to resolve the inconsistency.
+ (or (car (bounds-of-thing-at-point 'symbol))
+ (point))
+ 'exact))
+ (format "-new-name=%s" new-name)
+ "-i" (buffer-file-name))))
+ (if (and (integerp exit-code) (zerop exit-code))
+ ;; Success; revert current buffer so it gets the modifications.
+ (progn
+ (kill-buffer output-buffer)
+ (revert-buffer :ignore-auto :noconfirm :preserve-modes))
+ ;; Failure; append exit code to output buffer and display it.
+ (let ((message (clang-rename--format-message
+ "clang-rename failed with %s %s"
+ (if (integerp exit-code) "exit status" "signal")
+ exit-code)))
+ (with-current-buffer output-buffer
+ (insert ?\n message ?\n))
+ (message "%s" message)
+ (display-buffer output-buffer))))))
+
+(defalias 'clang-rename--bufferpos-to-filepos
+ (if (fboundp 'bufferpos-to-filepos)
+ 'bufferpos-to-filepos
+ ;; Emacs 24 doesn’t have ‘bufferpos-to-filepos’, simulate it using
+ ;; ‘position-bytes’.
+ (lambda (position &optional _quality _coding-system)
+ (1- (position-bytes position)))))
+
+;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older
+;; versions.
+(defalias 'clang-rename--format-message
+ (if (fboundp 'format-message) 'format-message 'format))
+
+(provide 'clang-rename)
+
+;;; clang-rename.el ends here
diff --git a/tools/clang-rename/clang-rename.py b/tools/clang-rename/clang-rename.py
new file mode 100644
index 0000000000..3cc6644ff8
--- /dev/null
+++ b/tools/clang-rename/clang-rename.py
@@ -0,0 +1,61 @@
+'''
+Minimal clang-rename integration with Vim.
+
+Before installing make sure one of the following is satisfied:
+
+* clang-rename is in your PATH
+* `g:clang_rename_path` in ~/.vimrc points to valid clang-rename executable
+* `binary` in clang-rename.py points to valid to clang-rename executable
+
+To install, simply put this into your ~/.vimrc
+
+ noremap <leader>cr :pyf <path-to>/clang-rename.py<cr>
+
+IMPORTANT NOTE: Before running the tool, make sure you saved the file.
+
+All you have to do now is to place a cursor on a variable/function/class which
+you would like to rename and press '<leader>cr'. You will be prompted for a new
+name if the cursor points to a valid symbol.
+'''
+
+import vim
+import subprocess
+import sys
+
+def main():
+ binary = 'clang-rename'
+ if vim.eval('exists("g:clang_rename_path")') == "1":
+ binary = vim.eval('g:clang_rename_path')
+
+ # Get arguments for clang-rename binary.
+ offset = int(vim.eval('line2byte(line("."))+col(".")')) - 2
+ if offset < 0:
+ print >> sys.stderr, '''Couldn\'t determine cursor position.
+ Is your file empty?'''
+ return
+ filename = vim.current.buffer.name
+
+ new_name_request_message = 'type new name:'
+ new_name = vim.eval("input('{}\n')".format(new_name_request_message))
+
+ # Call clang-rename.
+ command = [binary,
+ filename,
+ '-i',
+ '-offset', str(offset),
+ '-new-name', str(new_name)]
+ # FIXME: make it possible to run the tool on unsaved file.
+ p = subprocess.Popen(command,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+
+ if stderr:
+ print stderr
+
+ # Reload all buffers in Vim.
+ vim.command("checktime")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/diagtool/CMakeLists.txt b/tools/diagtool/CMakeLists.txt
index e88c2ab6e8..3f7d80385a 100644
--- a/tools/diagtool/CMakeLists.txt
+++ b/tools/diagtool/CMakeLists.txt
@@ -6,6 +6,7 @@ add_clang_executable(diagtool
diagtool_main.cpp
DiagTool.cpp
DiagnosticNames.cpp
+ FindDiagnosticID.cpp
ListWarnings.cpp
ShowEnabledWarnings.cpp
TreeView.cpp
diff --git a/tools/diagtool/DiagnosticNames.cpp b/tools/diagtool/DiagnosticNames.cpp
index a08da89577..b0ca7f9806 100644
--- a/tools/diagtool/DiagnosticNames.cpp
+++ b/tools/diagtool/DiagnosticNames.cpp
@@ -32,6 +32,7 @@ static const DiagnosticRecord BuiltinDiagnosticsByID[] = {
SFINAE,NOWERROR,SHOWINSYSHEADER,CATEGORY) \
{ #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) },
#include "clang/Basic/DiagnosticCommonKinds.inc"
+#include "clang/Basic/DiagnosticCrossTUKinds.inc"
#include "clang/Basic/DiagnosticDriverKinds.inc"
#include "clang/Basic/DiagnosticFrontendKinds.inc"
#include "clang/Basic/DiagnosticSerializationKinds.inc"
@@ -41,6 +42,7 @@ static const DiagnosticRecord BuiltinDiagnosticsByID[] = {
#include "clang/Basic/DiagnosticCommentKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
+#include "clang/Basic/DiagnosticRefactoringKinds.inc"
#undef DIAG
};
@@ -84,6 +86,11 @@ GroupRecord::subgroup_iterator GroupRecord::subgroup_end() const {
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 +99,11 @@ GroupRecord::diagnostics_iterator GroupRecord::diagnostics_end() const {
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 ac1a098579..598ae6a0ba 100644
--- a/tools/diagtool/DiagnosticNames.h
+++ b/tools/diagtool/DiagnosticNames.h
@@ -20,7 +20,7 @@ namespace diagtool {
const char *NameStr;
short DiagID;
uint8_t NameLen;
-
+
llvm::StringRef getName() const {
return llvm::StringRef(NameStr, NameLen);
}
@@ -80,7 +80,7 @@ namespace diagtool {
bool operator==(group_iterator &Other) const {
return CurrentID == Other.CurrentID;
}
-
+
bool operator!=(group_iterator &Other) const {
return CurrentID != Other.CurrentID;
}
@@ -89,10 +89,12 @@ namespace diagtool {
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/FindDiagnosticID.cpp b/tools/diagtool/FindDiagnosticID.cpp
new file mode 100644
index 0000000000..db6fe5e472
--- /dev/null
+++ b/tools/diagtool/FindDiagnosticID.cpp
@@ -0,0 +1,74 @@
+//===- FindDiagnosticID.cpp - diagtool tool for finding diagnostic id -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagTool.h"
+#include "DiagnosticNames.h"
+#include "clang/Basic/AllDiagnostics.h"
+#include "llvm/Support/CommandLine.h"
+
+DEF_DIAGTOOL("find-diagnostic-id", "Print the id of the given diagnostic",
+ FindDiagnosticID)
+
+using namespace clang;
+using namespace diagtool;
+
+static StringRef getNameFromID(StringRef Name) {
+ int DiagID;
+ if(!Name.getAsInteger(0, DiagID)) {
+ const DiagnosticRecord &Diag = getDiagnosticForID(DiagID);
+ return Diag.getName();
+ }
+ return StringRef();
+}
+
+static Optional<DiagnosticRecord>
+findDiagnostic(ArrayRef<DiagnosticRecord> Diagnostics, StringRef Name) {
+ for (const auto &Diag : Diagnostics) {
+ StringRef DiagName = Diag.getName();
+ if (DiagName == Name)
+ return Diag;
+ }
+ return None;
+}
+
+int FindDiagnosticID::run(unsigned int argc, char **argv,
+ llvm::raw_ostream &OS) {
+ static llvm::cl::OptionCategory FindDiagnosticIDOptions(
+ "diagtool find-diagnostic-id options");
+
+ static llvm::cl::opt<std::string> DiagnosticName(
+ llvm::cl::Positional, llvm::cl::desc("<diagnostic-name>"),
+ llvm::cl::Required, llvm::cl::cat(FindDiagnosticIDOptions));
+
+ std::vector<const char *> Args;
+ Args.push_back("diagtool find-diagnostic-id");
+ for (const char *A : llvm::makeArrayRef(argv, argc))
+ Args.push_back(A);
+
+ llvm::cl::HideUnrelatedOptions(FindDiagnosticIDOptions);
+ llvm::cl::ParseCommandLineOptions((int)Args.size(), Args.data(),
+ "Diagnostic ID mapping utility");
+
+ ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName();
+ Optional<DiagnosticRecord> Diag =
+ findDiagnostic(AllDiagnostics, DiagnosticName);
+ if (!Diag) {
+ // Name to id failed, so try id to name.
+ auto Name = getNameFromID(DiagnosticName);
+ if (!Name.empty()) {
+ OS << Name << '\n';
+ return 0;
+ }
+
+ llvm::errs() << "error: invalid diagnostic '" << DiagnosticName << "'\n";
+ return 1;
+ }
+ OS << Diag->DiagID << "\n";
+ return 0;
+}
diff --git a/tools/diagtool/ListWarnings.cpp b/tools/diagtool/ListWarnings.cpp
index 3e6e88306e..8bf9df9401 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 @@ namespace {
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 @@ static void printEntries(std::vector<Entry> &entries, llvm::raw_ostream &out) {
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 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
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 @@ int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
<< flagHistogram["pedantic"].size() << '\n';
out << '\n';
-
+
return 0;
}
diff --git a/tools/diagtool/ShowEnabledWarnings.cpp b/tools/diagtool/ShowEnabledWarnings.cpp
index e6ea786a9a..513abc15b2 100644
--- a/tools/diagtool/ShowEnabledWarnings.cpp
+++ b/tools/diagtool/ShowEnabledWarnings.cpp
@@ -112,17 +112,14 @@ int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
// 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 @@ int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) {
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 07af944ffc..b4846b5574 100644
--- a/tools/diagtool/TreeView.cpp
+++ b/tools/diagtool/TreeView.cpp
@@ -32,10 +32,10 @@ class TreePrinter {
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 @@ public:
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 @@ public:
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 @@ public:
};
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 @@ int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) {
}
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 f6e26fa11f..901b6d62e4 100644
--- a/tools/driver/CMakeLists.txt
+++ b/tools/driver/CMakeLists.txt
@@ -61,7 +61,7 @@ add_dependencies(clang clang-headers)
if(NOT CLANG_LINKS_TO_CREATE)
set(CLANG_LINKS_TO_CREATE clang++ clang-cl clang-cpp)
- if (WIN32)
+ if (MSVC)
list(APPEND CLANG_LINKS_TO_CREATE ../msbuild-bin/cl)
endif()
endif()
diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp
index 2fa8edb81a..9b90562af9 100644
--- a/tools/driver/cc1as_main.cpp
+++ b/tools/driver/cc1as_main.cpp
@@ -88,12 +88,13 @@ struct AssemblerInvocation {
unsigned NoInitialTextSection : 1;
unsigned SaveTemporaryLabels : 1;
unsigned GenDwarfForAssembly : 1;
- unsigned CompressDebugSections : 1;
unsigned RelaxELFRelocations : 1;
unsigned DwarfVersion;
std::string DwarfDebugFlags;
std::string DwarfDebugProducer;
std::string DebugCompilationDir;
+ llvm::DebugCompressionType CompressDebugSections =
+ llvm::DebugCompressionType::None;
std::string MainFileName;
/// @}
@@ -201,7 +202,22 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts,
Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels);
// Any DebugInfoKind implies GenDwarfForAssembly.
Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ);
- Opts.CompressDebugSections = Args.hasArg(OPT_compress_debug_sections);
+
+ if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections,
+ OPT_compress_debug_sections_EQ)) {
+ if (A->getOption().getID() == OPT_compress_debug_sections) {
+ // TODO: be more clever about the compression type auto-detection
+ Opts.CompressDebugSections = llvm::DebugCompressionType::GNU;
+ } else {
+ Opts.CompressDebugSections =
+ llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue())
+ .Case("none", llvm::DebugCompressionType::None)
+ .Case("zlib", llvm::DebugCompressionType::Z)
+ .Case("zlib-gnu", llvm::DebugCompressionType::GNU)
+ .Default(llvm::DebugCompressionType::None);
+ }
+ }
+
Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations);
Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags);
Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
@@ -314,8 +330,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
// Ensure MCAsmInfo initialization occurs before any use, otherwise sections
// may be created with a combination of default and explicit settings.
- if (Opts.CompressDebugSections)
- MAI->setCompressDebugSections(DebugCompressionType::DCT_ZlibGnu);
+ MAI->setCompressDebugSections(Opts.CompressDebugSections);
MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations);
@@ -341,7 +356,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
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)
@@ -404,8 +419,8 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts,
Opts.CPU, Options);
Triple T(Opts.Triple);
Str.reset(TheTarget->createMCObjectStreamer(
- T, Ctx, *MAB, *Out, CE, *STI, Opts.RelaxAll,
- Opts.IncrementalLinkerCompatible,
+ T, Ctx, std::unique_ptr<MCAsmBackend>(MAB), *Out, std::unique_ptr<MCCodeEmitter>(CE), *STI,
+ Opts.RelaxAll, Opts.IncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ true));
Str.get()->InitSections(Opts.NoExecStack);
}
@@ -489,7 +504,8 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
if (Asm.ShowHelp) {
std::unique_ptr<OptTable> Opts(driver::createDriverOptTable());
Opts->PrintHelp(llvm::outs(), "clang -cc1as", "Clang Integrated Assembler",
- /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0);
+ /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0,
+ /*ShowAllAliases=*/false);
return 0;
}
@@ -506,12 +522,12 @@ int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
// FIXME: Remove this, one day.
if (!Asm.LLVMArgs.empty()) {
unsigned NumArgs = Asm.LLVMArgs.size();
- const char **Args = new const char*[NumArgs + 2];
+ auto Args = llvm::make_unique<const char*[]>(NumArgs + 2);
Args[0] = "clang (LLVM option parsing)";
for (unsigned i = 0; i != NumArgs; ++i)
Args[i + 1] = Asm.LLVMArgs[i].c_str();
Args[NumArgs + 1] = nullptr;
- llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args);
+ llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get());
}
// Execute the invocation, unless there were parsing errors.
diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp
index 6161302862..fa757da953 100644
--- a/tools/driver/driver.cpp
+++ b/tools/driver/driver.cpp
@@ -53,8 +53,15 @@ using namespace clang::driver;
using namespace llvm::opt;
std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) {
- if (!CanonicalPrefixes)
- return Argv0;
+ if (!CanonicalPrefixes) {
+ SmallString<128> ExecutablePath(Argv0);
+ // Do a PATH lookup if Argv0 isn't a valid path.
+ if (!llvm::sys::fs::exists(ExecutablePath))
+ if (llvm::ErrorOr<std::string> P =
+ llvm::sys::findProgramByName(ExecutablePath))
+ ExecutablePath = *P;
+ return ExecutablePath.str();
+ }
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
@@ -199,23 +206,26 @@ extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
void *MainAddr);
-static void insertTargetAndModeArgs(StringRef Target, StringRef Mode,
+static void insertTargetAndModeArgs(const ParsedClangName &NameParts,
SmallVectorImpl<const char *> &ArgVector,
std::set<std::string> &SavedStrings) {
- if (!Mode.empty()) {
+ // Put target and mode arguments at the start of argument list so that
+ // arguments specified in command line could override them. Avoid putting
+ // them at index 0, as an option like '-cc1' must remain the first.
+ auto InsertionPoint = ArgVector.begin();
+ if (InsertionPoint != ArgVector.end())
+ ++InsertionPoint;
+
+ if (NameParts.DriverMode) {
// Add the mode flag to the arguments.
- auto it = ArgVector.begin();
- if (it != ArgVector.end())
- ++it;
- ArgVector.insert(it, GetStableCStr(SavedStrings, Mode));
+ ArgVector.insert(InsertionPoint,
+ GetStableCStr(SavedStrings, NameParts.DriverMode));
}
- if (!Target.empty()) {
- auto it = ArgVector.begin();
- if (it != ArgVector.end())
- ++it;
- const char *arr[] = {"-target", GetStableCStr(SavedStrings, Target)};
- ArgVector.insert(it, std::begin(arr), std::end(arr));
+ if (NameParts.TargetIsValid) {
+ const char *arr[] = {"-target", GetStableCStr(SavedStrings,
+ NameParts.TargetPrefix)};
+ ArgVector.insert(InsertionPoint, std::begin(arr), std::end(arr));
}
}
@@ -323,9 +333,7 @@ int main(int argc_, const char **argv_) {
}
llvm::InitializeAllTargets();
- std::string ProgName = argv[0];
- std::pair<std::string, std::string> TargetAndMode =
- ToolChain::getTargetAndModeFromProgramName(ProgName);
+ auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]);
llvm::BumpPtrAllocator A;
llvm::StringSaver Saver(A);
@@ -338,7 +346,7 @@ int main(int argc_, const char **argv_) {
// Finally, our -cc1 tools don't care which tokenization mode we use because
// response files written by clang will tokenize the same way in either mode.
bool ClangCLMode = false;
- if (TargetAndMode.second == "--driver-mode=cl" ||
+ if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") ||
std::find_if(argv.begin(), argv.end(), [](const char *F) {
return F && strcmp(F, "--driver-mode=cl") == 0;
}) != argv.end()) {
@@ -447,46 +455,48 @@ int main(int argc_, const char **argv_) {
Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
SetInstallDir(argv, TheDriver, CanonicalPrefixes);
+ TheDriver.setTargetAndMode(TargetAndMode);
- insertTargetAndModeArgs(TargetAndMode.first, TargetAndMode.second, argv,
- SavedStrings);
+ insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings);
SetBackdoorDriverOutputsFromEnvVars(TheDriver);
std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv));
- int Res = 0;
- SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
- if (C.get())
+ int Res = 1;
+ if (C && !C->containsError()) {
+ SmallVector<std::pair<int, const Command *>, 4> FailingCommands;
Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
- // Force a crash to test the diagnostics.
- if (::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH")) {
- Diags.Report(diag::err_drv_force_crash) << "FORCE_CLANG_DIAGNOSTICS_CRASH";
+ // Force a crash to test the diagnostics.
+ if (TheDriver.GenReproducer) {
+ Diags.Report(diag::err_drv_force_crash)
+ << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH");
- // Pretend that every command failed.
- FailingCommands.clear();
- for (const auto &J : C->getJobs())
- if (const Command *C = dyn_cast<Command>(&J))
- FailingCommands.push_back(std::make_pair(-1, C));
- }
+ // Pretend that every command failed.
+ FailingCommands.clear();
+ for (const auto &J : C->getJobs())
+ if (const Command *C = dyn_cast<Command>(&J))
+ FailingCommands.push_back(std::make_pair(-1, C));
+ }
- for (const auto &P : FailingCommands) {
- int CommandRes = P.first;
- const Command *FailingCommand = P.second;
- if (!Res)
- Res = CommandRes;
-
- // If result status is < 0, then the driver command signalled an error.
- // If result status is 70, then the driver command reported a fatal error.
- // On Windows, abort will return an exit code of 3. In these cases,
- // generate additional diagnostic information if possible.
- bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70;
+ for (const auto &P : FailingCommands) {
+ int CommandRes = P.first;
+ const Command *FailingCommand = P.second;
+ if (!Res)
+ Res = CommandRes;
+
+ // If result status is < 0, then the driver command signalled an error.
+ // If result status is 70, then the driver command reported a fatal error.
+ // On Windows, abort will return an exit code of 3. In these cases,
+ // generate additional diagnostic information if possible.
+ bool DiagnoseCrash = CommandRes < 0 || CommandRes == 70;
#ifdef LLVM_ON_WIN32
- DiagnoseCrash |= CommandRes == 3;
+ DiagnoseCrash |= CommandRes == 3;
#endif
- if (DiagnoseCrash) {
- TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
- break;
+ if (DiagnoseCrash) {
+ TheDriver.generateCompilationDiagnostics(*C, *FailingCommand);
+ break;
+ }
}
}
diff --git a/tools/libclang/ARCMigrate.cpp b/tools/libclang/ARCMigrate.cpp
index 44a60c4e3e..ed2ecdb29e 100644
--- a/tools/libclang/ARCMigrate.cpp
+++ b/tools/libclang/ARCMigrate.cpp
@@ -14,6 +14,7 @@
#include "clang-c/Index.h"
#include "CXString.h"
#include "clang/ARCMigrate/ARCMT.h"
+#include "clang/Config/config.h"
#include "clang/Frontend/TextDiagnosticBuffer.h"
#include "llvm/Support/FileSystem.h"
@@ -33,7 +34,7 @@ struct Remap {
//===----------------------------------------------------------------------===//
CXRemapping clang_getRemappings(const char *migrate_dir_path) {
-#ifndef CLANG_ENABLE_ARCMT
+#if !CLANG_ENABLE_ARCMT
llvm::errs() << "error: feature not enabled in this build\n";
return nullptr;
#else
@@ -76,7 +77,7 @@ CXRemapping clang_getRemappings(const char *migrate_dir_path) {
CXRemapping clang_getRemappingsFromFileList(const char **filePaths,
unsigned numFiles) {
-#ifndef CLANG_ENABLE_ARCMT
+#if !CLANG_ENABLE_ARCMT
llvm::errs() << "error: feature not enabled in this build\n";
return nullptr;
#else
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 216322b9f9..fa3b970855 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -26,6 +26,7 @@
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticCategories.h"
#include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/CompilerInstance.h"
@@ -906,7 +907,8 @@ bool CursorVisitor::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
if (VisitTemplateParameters(D->getTemplateParameters()))
return true;
- return VisitFunctionDecl(D->getTemplatedDecl());
+ auto* FD = D->getTemplatedDecl();
+ return VisitAttributes(FD) || VisitFunctionDecl(FD);
}
bool CursorVisitor::VisitClassTemplateDecl(ClassTemplateDecl *D) {
@@ -915,7 +917,8 @@ bool CursorVisitor::VisitClassTemplateDecl(ClassTemplateDecl *D) {
if (VisitTemplateParameters(D->getTemplateParameters()))
return true;
- return VisitCXXRecordDecl(D->getTemplatedDecl());
+ auto* CD = D->getTemplatedDecl();
+ return VisitAttributes(CD) || VisitCXXRecordDecl(CD);
}
bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
@@ -1741,6 +1744,7 @@ DEFAULT_TYPELOC_IMPL(ConstantArray, ArrayType)
DEFAULT_TYPELOC_IMPL(IncompleteArray, ArrayType)
DEFAULT_TYPELOC_IMPL(VariableArray, ArrayType)
DEFAULT_TYPELOC_IMPL(DependentSizedArray, ArrayType)
+DEFAULT_TYPELOC_IMPL(DependentAddressSpace, Type)
DEFAULT_TYPELOC_IMPL(DependentSizedExtVector, Type)
DEFAULT_TYPELOC_IMPL(Vector, Type)
DEFAULT_TYPELOC_IMPL(ExtVector, VectorType)
@@ -2263,6 +2267,42 @@ void OMPClauseEnqueue::VisitOMPReductionClause(const OMPReductionClause *C) {
Visitor->AddStmt(E);
}
}
+void OMPClauseEnqueue::VisitOMPTaskReductionClause(
+ const OMPTaskReductionClause *C) {
+ VisitOMPClauseList(C);
+ VisitOMPClauseWithPostUpdate(C);
+ for (auto *E : C->privates()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->lhs_exprs()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->rhs_exprs()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->reduction_ops()) {
+ Visitor->AddStmt(E);
+ }
+}
+void OMPClauseEnqueue::VisitOMPInReductionClause(
+ const OMPInReductionClause *C) {
+ VisitOMPClauseList(C);
+ VisitOMPClauseWithPostUpdate(C);
+ for (auto *E : C->privates()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->lhs_exprs()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->rhs_exprs()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->reduction_ops()) {
+ Visitor->AddStmt(E);
+ }
+ for (auto *E : C->taskgroup_descriptors())
+ Visitor->AddStmt(E);
+}
void OMPClauseEnqueue::VisitOMPLinearClause(const OMPLinearClause *C) {
VisitOMPClauseList(C);
VisitOMPClauseWithPostUpdate(C);
@@ -2720,6 +2760,8 @@ void EnqueueVisitor::VisitOMPTaskwaitDirective(const OMPTaskwaitDirective *D) {
void EnqueueVisitor::VisitOMPTaskgroupDirective(
const OMPTaskgroupDirective *D) {
VisitOMPExecutableDirective(D);
+ if (const Expr *E = D->getReductionRef())
+ VisitStmt(E);
}
void EnqueueVisitor::VisitOMPFlushDirective(const OMPFlushDirective *D) {
@@ -3246,7 +3288,8 @@ enum CXErrorCode clang_createTranslationUnit2(CXIndex CIdx,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
CompilerInstance::createDiagnostics(new DiagnosticOptions());
std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile(
- ast_filename, CXXIdx->getPCHContainerOperations()->getRawReader(), Diags,
+ ast_filename, CXXIdx->getPCHContainerOperations()->getRawReader(),
+ ASTUnit::LoadEverything, Diags,
FileSystemOpts, /*UseDebugInfo=*/false,
CXXIdx->getOnlyLocalDecls(), None,
/*CaptureDiagnostics=*/true,
@@ -3299,12 +3342,14 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename,
options & CXTranslationUnit_CreatePreambleOnFirstParse;
// FIXME: Add a flag for modules.
TranslationUnitKind TUKind
- = (options & CXTranslationUnit_Incomplete)? TU_Prefix : TU_Complete;
+ = (options & (CXTranslationUnit_Incomplete |
+ CXTranslationUnit_SingleFileParse))? TU_Prefix : TU_Complete;
bool CacheCodeCompletionResults
= options & CXTranslationUnit_CacheCompletionResults;
bool IncludeBriefCommentsInCodeCompletion
= options & CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;
bool SkipFunctionBodies = options & CXTranslationUnit_SkipFunctionBodies;
+ bool SingleFileParse = options & CXTranslationUnit_SingleFileParse;
bool ForSerialization = options & CXTranslationUnit_ForSerialization;
// Configure the diagnostics.
@@ -3312,7 +3357,7 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename,
Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
if (options & CXTranslationUnit_KeepGoing)
- Diags->setFatalsAsError(true);
+ Diags->setSuppressAfterFatalError(false);
// Recover resources if we crash before exiting this function.
llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine,
@@ -3371,7 +3416,10 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename,
Args->push_back("-Xclang");
Args->push_back("-detailed-preprocessing-record");
}
-
+
+ // Suppress any editor placeholder diagnostics.
+ Args->push_back("-fallow-editor-placeholders");
+
unsigned NumErrors = Diags->getClient()->getNumErrors();
std::unique_ptr<ASTUnit> ErrUnit;
// Unless the user specified that they want the preamble on the first parse
@@ -3386,7 +3434,7 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename,
/*CaptureDiagnostics=*/true, *RemappedFiles.get(),
/*RemappedFilesKeepOriginalName=*/true, PrecompilePreambleAfterNParses,
TUKind, CacheCodeCompletionResults, IncludeBriefCommentsInCodeCompletion,
- /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies,
+ /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies, SingleFileParse,
/*UserFilesAreVolatile=*/true, ForSerialization,
CXXIdx->getPCHContainerOperations()->getRawReader().getFormat(),
&ErrUnit));
@@ -3459,6 +3507,12 @@ enum CXErrorCode clang_parseTranslationUnit2FullArgv(
CIdx, source_filename, command_line_args, num_command_line_args,
llvm::makeArrayRef(unsaved_files, num_unsaved_files), options, out_TU);
};
+
+ if (getenv("LIBCLANG_NOTHREADS")) {
+ ParseTranslationUnitImpl();
+ return result;
+ }
+
llvm::CrashRecoveryContext CRC;
if (!RunSafely(CRC, ParseTranslationUnitImpl)) {
@@ -3914,6 +3968,20 @@ void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) {
}
}
+unsigned clang_suspendTranslationUnit(CXTranslationUnit CTUnit) {
+ if (CTUnit) {
+ ASTUnit *Unit = cxtu::getASTUnit(CTUnit);
+
+ if (Unit && Unit->isUnsafeToFree())
+ return false;
+
+ Unit->ResetForParse();
+ return true;
+ }
+
+ return false;
+}
+
unsigned clang_defaultReparseOptions(CXTranslationUnit TU) {
return CXReparse_None;
}
@@ -4015,6 +4083,50 @@ CXCursor clang_getTranslationUnitCursor(CXTranslationUnit TU) {
return MakeCXCursor(CXXUnit->getASTContext().getTranslationUnitDecl(), TU);
}
+CXTargetInfo clang_getTranslationUnitTargetInfo(CXTranslationUnit CTUnit) {
+ if (isNotUsableTU(CTUnit)) {
+ LOG_BAD_TU(CTUnit);
+ return nullptr;
+ }
+
+ CXTargetInfoImpl* impl = new CXTargetInfoImpl();
+ impl->TranslationUnit = CTUnit;
+ return impl;
+}
+
+CXString clang_TargetInfo_getTriple(CXTargetInfo TargetInfo) {
+ if (!TargetInfo)
+ return cxstring::createEmpty();
+
+ CXTranslationUnit CTUnit = TargetInfo->TranslationUnit;
+ assert(!isNotUsableTU(CTUnit) &&
+ "Unexpected unusable translation unit in TargetInfo");
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(CTUnit);
+ std::string Triple =
+ CXXUnit->getASTContext().getTargetInfo().getTriple().normalize();
+ return cxstring::createDup(Triple);
+}
+
+int clang_TargetInfo_getPointerWidth(CXTargetInfo TargetInfo) {
+ if (!TargetInfo)
+ return -1;
+
+ CXTranslationUnit CTUnit = TargetInfo->TranslationUnit;
+ assert(!isNotUsableTU(CTUnit) &&
+ "Unexpected unusable translation unit in TargetInfo");
+
+ ASTUnit *CXXUnit = cxtu::getASTUnit(CTUnit);
+ return CXXUnit->getASTContext().getTargetInfo().getMaxPointerWidth();
+}
+
+void clang_TargetInfo_dispose(CXTargetInfo TargetInfo) {
+ if (!TargetInfo)
+ return;
+
+ delete TargetInfo;
+}
+
//===----------------------------------------------------------------------===//
// CXFile Operations.
//===----------------------------------------------------------------------===//
@@ -4530,6 +4642,20 @@ CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) {
return cxstring::createSet(Manglings);
}
+CXStringSet *clang_Cursor_getObjCManglings(CXCursor C) {
+ if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind))
+ return nullptr;
+
+ const Decl *D = getCursorDecl(C);
+ if (!(isa<ObjCInterfaceDecl>(D) || isa<ObjCImplementationDecl>(D)))
+ return nullptr;
+
+ ASTContext &Ctx = D->getASTContext();
+ index::CodegenNameGenerator CGNameGen(Ctx);
+ std::vector<std::string> Manglings = CGNameGen.getAllManglings(D);
+ return cxstring::createSet(Manglings);
+}
+
CXString clang_getCursorDisplayName(CXCursor C) {
if (!clang_isDeclaration(C.kind))
return clang_getCursorSpelling(C);
@@ -7021,8 +7147,10 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) {
switch (ND->getLinkageInternal()) {
case NoLinkage:
case VisibleNoLinkage: return CXLinkage_NoLinkage;
+ case ModuleInternalLinkage:
case InternalLinkage: return CXLinkage_Internal;
case UniqueExternalLinkage: return CXLinkage_UniqueExternal;
+ case ModuleLinkage:
case ExternalLinkage: return CXLinkage_External;
};
@@ -7152,15 +7280,11 @@ static CXVersion convertVersion(VersionTuple In) {
return Out;
}
-static int getCursorPlatformAvailabilityForDecl(const Decl *D,
- int *always_deprecated,
- CXString *deprecated_message,
- int *always_unavailable,
- CXString *unavailable_message,
- CXPlatformAvailability *availability,
- int availability_size) {
+static void getCursorPlatformAvailabilityForDecl(
+ const Decl *D, int *always_deprecated, CXString *deprecated_message,
+ int *always_unavailable, CXString *unavailable_message,
+ SmallVectorImpl<AvailabilityAttr *> &AvailabilityAttrs) {
bool HadAvailAttr = false;
- int N = 0;
for (auto A : D->attrs()) {
if (DeprecatedAttr *Deprecated = dyn_cast<DeprecatedAttr>(A)) {
HadAvailAttr = true;
@@ -7172,7 +7296,7 @@ static int getCursorPlatformAvailabilityForDecl(const Decl *D,
}
continue;
}
-
+
if (UnavailableAttr *Unavailable = dyn_cast<UnavailableAttr>(A)) {
HadAvailAttr = true;
if (always_unavailable)
@@ -7183,38 +7307,72 @@ static int getCursorPlatformAvailabilityForDecl(const Decl *D,
}
continue;
}
-
+
if (AvailabilityAttr *Avail = dyn_cast<AvailabilityAttr>(A)) {
+ AvailabilityAttrs.push_back(Avail);
HadAvailAttr = true;
- if (N < availability_size) {
- availability[N].Platform
- = cxstring::createDup(Avail->getPlatform()->getName());
- availability[N].Introduced = convertVersion(Avail->getIntroduced());
- availability[N].Deprecated = convertVersion(Avail->getDeprecated());
- availability[N].Obsoleted = convertVersion(Avail->getObsoleted());
- availability[N].Unavailable = Avail->getUnavailable();
- availability[N].Message = cxstring::createDup(Avail->getMessage());
- }
- ++N;
}
}
if (!HadAvailAttr)
if (const EnumConstantDecl *EnumConst = dyn_cast<EnumConstantDecl>(D))
return getCursorPlatformAvailabilityForDecl(
- cast<Decl>(EnumConst->getDeclContext()),
- always_deprecated,
- deprecated_message,
- always_unavailable,
- unavailable_message,
- availability,
- availability_size);
-
- return N;
+ cast<Decl>(EnumConst->getDeclContext()), always_deprecated,
+ deprecated_message, always_unavailable, unavailable_message,
+ AvailabilityAttrs);
+
+ if (AvailabilityAttrs.empty())
+ return;
+
+ std::sort(AvailabilityAttrs.begin(), AvailabilityAttrs.end(),
+ [](AvailabilityAttr *LHS, AvailabilityAttr *RHS) {
+ return LHS->getPlatform()->getName() <
+ RHS->getPlatform()->getName();
+ });
+ ASTContext &Ctx = D->getASTContext();
+ auto It = std::unique(
+ AvailabilityAttrs.begin(), AvailabilityAttrs.end(),
+ [&Ctx](AvailabilityAttr *LHS, AvailabilityAttr *RHS) {
+ if (LHS->getPlatform() != RHS->getPlatform())
+ return false;
+
+ if (LHS->getIntroduced() == RHS->getIntroduced() &&
+ LHS->getDeprecated() == RHS->getDeprecated() &&
+ LHS->getObsoleted() == RHS->getObsoleted() &&
+ LHS->getMessage() == RHS->getMessage() &&
+ LHS->getReplacement() == RHS->getReplacement())
+ return true;
+
+ if ((!LHS->getIntroduced().empty() && !RHS->getIntroduced().empty()) ||
+ (!LHS->getDeprecated().empty() && !RHS->getDeprecated().empty()) ||
+ (!LHS->getObsoleted().empty() && !RHS->getObsoleted().empty()))
+ return false;
+
+ if (LHS->getIntroduced().empty() && !RHS->getIntroduced().empty())
+ LHS->setIntroduced(Ctx, RHS->getIntroduced());
+
+ if (LHS->getDeprecated().empty() && !RHS->getDeprecated().empty()) {
+ LHS->setDeprecated(Ctx, RHS->getDeprecated());
+ if (LHS->getMessage().empty())
+ LHS->setMessage(Ctx, RHS->getMessage());
+ if (LHS->getReplacement().empty())
+ LHS->setReplacement(Ctx, RHS->getReplacement());
+ }
+
+ if (LHS->getObsoleted().empty() && !RHS->getObsoleted().empty()) {
+ LHS->setObsoleted(Ctx, RHS->getObsoleted());
+ if (LHS->getMessage().empty())
+ LHS->setMessage(Ctx, RHS->getMessage());
+ if (LHS->getReplacement().empty())
+ LHS->setReplacement(Ctx, RHS->getReplacement());
+ }
+
+ return true;
+ });
+ AvailabilityAttrs.erase(It, AvailabilityAttrs.end());
}
-int clang_getCursorPlatformAvailability(CXCursor cursor,
- int *always_deprecated,
+int clang_getCursorPlatformAvailability(CXCursor cursor, int *always_deprecated,
CXString *deprecated_message,
int *always_unavailable,
CXString *unavailable_message,
@@ -7236,14 +7394,29 @@ int clang_getCursorPlatformAvailability(CXCursor cursor,
if (!D)
return 0;
- return getCursorPlatformAvailabilityForDecl(D, always_deprecated,
- deprecated_message,
- always_unavailable,
- unavailable_message,
- availability,
- availability_size);
+ SmallVector<AvailabilityAttr *, 8> AvailabilityAttrs;
+ getCursorPlatformAvailabilityForDecl(D, always_deprecated, deprecated_message,
+ always_unavailable, unavailable_message,
+ AvailabilityAttrs);
+ for (const auto &Avail :
+ llvm::enumerate(llvm::makeArrayRef(AvailabilityAttrs)
+ .take_front(availability_size))) {
+ availability[Avail.index()].Platform =
+ cxstring::createDup(Avail.value()->getPlatform()->getName());
+ availability[Avail.index()].Introduced =
+ convertVersion(Avail.value()->getIntroduced());
+ availability[Avail.index()].Deprecated =
+ convertVersion(Avail.value()->getDeprecated());
+ availability[Avail.index()].Obsoleted =
+ convertVersion(Avail.value()->getObsoleted());
+ availability[Avail.index()].Unavailable = Avail.value()->getUnavailable();
+ availability[Avail.index()].Message =
+ cxstring::createDup(Avail.value()->getMessage());
+ }
+
+ return AvailabilityAttrs.size();
}
-
+
void clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability) {
clang_disposeString(availability->Platform);
clang_disposeString(availability->Message);
@@ -7256,6 +7429,22 @@ CXLanguageKind clang_getCursorLanguage(CXCursor cursor) {
return CXLanguage_Invalid;
}
+CXTLSKind clang_getCursorTLSKind(CXCursor cursor) {
+ const Decl *D = cxcursor::getCursorDecl(cursor);
+ if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ switch (VD->getTLSKind()) {
+ case VarDecl::TLS_None:
+ return CXTLS_None;
+ case VarDecl::TLS_Dynamic:
+ return CXTLS_Dynamic;
+ case VarDecl::TLS_Static:
+ return CXTLS_Static;
+ }
+ }
+
+ return CXTLS_None;
+}
+
/// \brief If the given cursor is the "templated" declaration
/// descibing a class or function template, return the class or
/// function template.
@@ -7431,6 +7620,26 @@ unsigned clang_Cursor_isVariadic(CXCursor C) {
return 0;
}
+unsigned clang_Cursor_isExternalSymbol(CXCursor C,
+ CXString *language, CXString *definedIn,
+ unsigned *isGenerated) {
+ if (!clang_isDeclaration(C.kind))
+ return 0;
+
+ const Decl *D = getCursorDecl(C);
+
+ if (auto *attr = D->getExternalSourceSymbolAttr()) {
+ if (language)
+ *language = cxstring::createDup(attr->getLanguage());
+ if (definedIn)
+ *definedIn = cxstring::createDup(attr->getDefinedIn());
+ if (isGenerated)
+ *isGenerated = attr->getGeneratedDeclaration();
+ return 1;
+ }
+ return 0;
+}
+
CXSourceRange clang_Cursor_getCommentRange(CXCursor C) {
if (!clang_isDeclaration(C.kind))
return clang_getNullRange();
@@ -7676,6 +7885,15 @@ unsigned clang_CXXMethod_isVirtual(CXCursor C) {
return (Method && Method->isVirtual()) ? 1 : 0;
}
+unsigned clang_EnumDecl_isScoped(CXCursor C) {
+ if (!clang_isDeclaration(C.kind))
+ return 0;
+
+ const Decl *D = cxcursor::getCursorDecl(C);
+ auto *Enum = dyn_cast_or_null<EnumDecl>(D);
+ return (Enum && Enum->isScoped()) ? 1 : 0;
+}
+
//===----------------------------------------------------------------------===//
// Attribute introspection.
//===----------------------------------------------------------------------===//
@@ -8055,7 +8273,7 @@ cxindex::checkForMacroInMacroDefinition(const MacroInfo *MI, const Token &Tok,
return nullptr;
// Check that the identifier is not one of the macro arguments.
- if (std::find(MI->arg_begin(), MI->arg_end(), &II) != MI->arg_end())
+ if (std::find(MI->param_begin(), MI->param_end(), &II) != MI->param_end())
return nullptr;
MacroDirective *InnerMD = PP.getLocalMacroDirectiveHistory(&II);
diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp
index ca68bc1cd2..c2b4c0bcb0 100644
--- a/tools/libclang/CIndexCodeCompletion.cpp
+++ b/tools/libclang/CIndexCodeCompletion.cpp
@@ -270,10 +270,6 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults {
/// \brief Source manager, used for diagnostics.
IntrusiveRefCntPtr<SourceManager> SourceMgr;
- /// \brief Temporary files that should be removed once we have finished
- /// with the code-completion results.
- std::vector<std::string> TemporaryFiles;
-
/// \brief Temporary buffers that will be deleted once we have finished with
/// the code-completion results.
SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers;
@@ -320,7 +316,8 @@ AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults(
: CXCodeCompleteResults(), DiagOpts(new DiagnosticOptions),
Diag(new DiagnosticsEngine(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts)),
- FileMgr(FileMgr), SourceMgr(new SourceManager(*Diag, *FileMgr)),
+ FileMgr(std::move(FileMgr)),
+ SourceMgr(new SourceManager(*Diag, *this->FileMgr)),
CodeCompletionAllocator(
std::make_shared<clang::GlobalCodeCompletionAllocator>()),
Contexts(CXCompletionContext_Unknown),
@@ -334,8 +331,6 @@ AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
llvm::DeleteContainerPointers(DiagnosticsWrappers);
delete [] Results;
- for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I)
- llvm::sys::fs::remove(TemporaryFiles[I]);
for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I)
delete TemporaryBuffers[I];
diff --git a/tools/libclang/CIndexDiagnostic.cpp b/tools/libclang/CIndexDiagnostic.cpp
index de223d3043..4e47b25a4b 100644
--- a/tools/libclang/CIndexDiagnostic.cpp
+++ b/tools/libclang/CIndexDiagnostic.cpp
@@ -110,40 +110,34 @@ public:
CurrentSet = &CD.getChildDiagnostics();
}
- void emitDiagnosticMessage(SourceLocation Loc, PresumedLoc PLoc,
- DiagnosticsEngine::Level Level,
- StringRef Message,
+ void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
+ DiagnosticsEngine::Level Level, StringRef Message,
ArrayRef<CharSourceRange> Ranges,
- const SourceManager *SM,
DiagOrStoredDiag D) override {
if (!D.isNull())
return;
CXSourceLocation L;
- if (SM)
- L = translateSourceLocation(*SM, LangOpts, Loc);
+ if (Loc.hasManager())
+ L = translateSourceLocation(Loc.getManager(), LangOpts, Loc);
else
L = clang_getNullLocation();
CurrentSet->appendDiagnostic(
llvm::make_unique<CXDiagnosticCustomNoteImpl>(Message, L));
}
- void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc,
+ void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc,
DiagnosticsEngine::Level Level,
- ArrayRef<CharSourceRange> Ranges,
- const SourceManager &SM) override {}
+ ArrayRef<CharSourceRange> Ranges) override {}
- void emitCodeContext(SourceLocation Loc,
- DiagnosticsEngine::Level Level,
- SmallVectorImpl<CharSourceRange>& Ranges,
- ArrayRef<FixItHint> Hints,
- const SourceManager &SM) override {}
+ void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
+ SmallVectorImpl<CharSourceRange> &Ranges,
+ ArrayRef<FixItHint> Hints) override {}
- void emitNote(SourceLocation Loc, StringRef Message,
- const SourceManager *SM) override {
+ void emitNote(FullSourceLoc Loc, StringRef Message) override {
CXSourceLocation L;
- if (SM)
- L = translateSourceLocation(*SM, LangOpts, Loc);
+ if (Loc.hasManager())
+ L = translateSourceLocation(Loc.getManager(), LangOpts, Loc);
else
L = clang_getNullLocation();
CurrentSet->appendDiagnostic(
diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt
index 2dd6703076..da2beca2ac 100644
--- a/tools/libclang/CMakeLists.txt
+++ b/tools/libclang/CMakeLists.txt
@@ -51,6 +51,9 @@ if (TARGET clangTidyPlugin)
add_definitions(-DCLANG_TOOL_EXTRA_BUILD)
list(APPEND LIBS clangTidyPlugin)
list(APPEND LIBS clangIncludeFixerPlugin)
+ if(LLVM_ENABLE_MODULES)
+ list(APPEND LLVM_COMPILE_FLAGS "-fmodules-ignore-macro=CLANG_TOOL_EXTRA_BUILD")
+ endif()
endif ()
find_library(DL_LIBRARY_PATH dl)
@@ -115,6 +118,12 @@ if(ENABLE_SHARED)
PROPERTIES
VERSION ${LIBCLANG_LIBRARY_VERSION}
DEFINE_SYMBOL _CINDEX_LIB_)
+ # FIXME: _CINDEX_LIB_ affects dllexport/dllimport on Win32.
+ if(LLVM_ENABLE_MODULES AND NOT WIN32)
+ target_compile_options(libclang PRIVATE
+ "-fmodules-ignore-macro=_CINDEX_LIB_"
+ )
+ endif()
endif()
endif()
@@ -132,6 +141,11 @@ install(DIRECTORY ../../include/clang-c
PATTERN ".svn" EXCLUDE
)
+# LLVM_DISTRIBUTION_COMPONENTS requires that each component have both a
+# component and an install-component target, so add a dummy libclang-headers
+# target to allow using it in LLVM_DISTRIBUTION_COMPONENTS.
+add_custom_target(libclang-headers)
+
if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's.
add_custom_target(install-libclang-headers
DEPENDS
diff --git a/tools/libclang/CXCompilationDatabase.cpp b/tools/libclang/CXCompilationDatabase.cpp
index c122ec8a6d..2ca532659d 100644
--- a/tools/libclang/CXCompilationDatabase.cpp
+++ b/tools/libclang/CXCompilationDatabase.cpp
@@ -145,36 +145,23 @@ clang_CompileCommand_getArg(CXCompileCommand CCmd, unsigned Arg)
unsigned
clang_CompileCommand_getNumMappedSources(CXCompileCommand CCmd)
{
- if (!CCmd)
- return 0;
-
- return static_cast<CompileCommand *>(CCmd)->MappedSources.size();
+ // Left here for backward compatibility. No mapped sources exists in the C++
+ // backend anymore.
+ return 0;
}
CXString
clang_CompileCommand_getMappedSourcePath(CXCompileCommand CCmd, unsigned I)
{
- if (!CCmd)
- return cxstring::createNull();
-
- CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd);
-
- if (I >= Cmd->MappedSources.size())
- return cxstring::createNull();
-
- return cxstring::createRef(Cmd->MappedSources[I].first.c_str());
+ // Left here for backward compatibility. No mapped sources exists in the C++
+ // backend anymore.
+ return cxstring::createNull();
}
CXString
clang_CompileCommand_getMappedSourceContent(CXCompileCommand CCmd, unsigned I)
{
- if (!CCmd)
- return cxstring::createNull();
-
- CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd);
-
- if (I >= Cmd->MappedSources.size())
- return cxstring::createNull();
-
- return cxstring::createRef(Cmd->MappedSources[I].second.c_str());
+ // Left here for backward compatibility. No mapped sources exists in the C++
+ // backend anymore.
+ return cxstring::createNull();
}
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index c19aa65ac6..fb61249a77 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -1523,6 +1523,10 @@ int clang_Cursor_isDynamicCall(CXCursor C) {
return true;
}
+ if (auto *PropRefE = dyn_cast<ObjCPropertyRefExpr>(E)) {
+ return !PropRefE->isSuperReceiver();
+ }
+
const MemberExpr *ME = nullptr;
if (isa<MemberExpr>(E))
ME = cast<MemberExpr>(E);
@@ -1532,7 +1536,9 @@ int clang_Cursor_isDynamicCall(CXCursor C) {
if (ME) {
if (const CXXMethodDecl *
MD = dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl()))
- return MD->isVirtual() && !ME->hasQualifier();
+ return MD->isVirtual() &&
+ ME->performsVirtualDispatch(
+ cxcursor::getCursorContext(C).getLangOpts());
}
return 0;
@@ -1547,5 +1553,23 @@ CXType clang_Cursor_getReceiverType(CXCursor C) {
if (const ObjCMessageExpr *MsgE = dyn_cast_or_null<ObjCMessageExpr>(E))
return cxtype::MakeCXType(MsgE->getReceiverType(), TU);
+ if (auto *PropRefE = dyn_cast<ObjCPropertyRefExpr>(E)) {
+ return cxtype::MakeCXType(
+ PropRefE->getReceiverType(cxcursor::getCursorContext(C)), TU);
+ }
+
+ const MemberExpr *ME = nullptr;
+ if (isa<MemberExpr>(E))
+ ME = cast<MemberExpr>(E);
+ else if (const CallExpr *CE = dyn_cast<CallExpr>(E))
+ ME = dyn_cast_or_null<MemberExpr>(CE->getCallee());
+
+ if (ME) {
+ if (dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl())) {
+ auto receiverTy = ME->getBase()->IgnoreImpCasts()->getType();
+ return cxtype::MakeCXType(receiverTy, TU);
+ }
+ }
+
return cxtype::MakeCXType(QualType(), TU);
}
diff --git a/tools/libclang/CXIndexDataConsumer.cpp b/tools/libclang/CXIndexDataConsumer.cpp
index 5d9776be3b..ffe5c486dd 100644
--- a/tools/libclang/CXIndexDataConsumer.cpp
+++ b/tools/libclang/CXIndexDataConsumer.cpp
@@ -423,11 +423,13 @@ bool CXIndexDataConsumer::isFunctionLocalDecl(const Decl *D) {
if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) {
switch (ND->getFormalLinkage()) {
case NoLinkage:
- case VisibleNoLinkage:
case InternalLinkage:
return true;
+ case VisibleNoLinkage:
+ case ModuleInternalLinkage:
case UniqueExternalLinkage:
llvm_unreachable("Not a sema linkage");
+ case ModuleLinkage:
case ExternalLinkage:
return false;
}
@@ -1256,6 +1258,7 @@ static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage
case SymbolKind::Module:
case SymbolKind::Macro:
case SymbolKind::ClassProperty:
+ case SymbolKind::Using:
return CXIdxEntity_Unexposed;
case SymbolKind::Enum: return CXIdxEntity_Enum;
@@ -1315,6 +1318,7 @@ static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) {
case SymbolLanguage::C: return CXIdxEntityLang_C;
case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC;
case SymbolLanguage::CXX: return CXIdxEntityLang_CXX;
+ case SymbolLanguage::Swift: return CXIdxEntityLang_Swift;
}
llvm_unreachable("invalid symbol language");
}
diff --git a/tools/libclang/CXTranslationUnit.h b/tools/libclang/CXTranslationUnit.h
index 67c31d2dba..ce8469b501 100644
--- a/tools/libclang/CXTranslationUnit.h
+++ b/tools/libclang/CXTranslationUnit.h
@@ -35,6 +35,10 @@ struct CXTranslationUnitImpl {
clang::index::CommentToXMLConverter *CommentToXML;
};
+struct CXTargetInfoImpl {
+ CXTranslationUnit TranslationUnit;
+};
+
namespace clang {
namespace cxtu {
diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp
index 54549ef1e3..dfc0152477 100644
--- a/tools/libclang/CXType.cpp
+++ b/tools/libclang/CXType.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Type.h"
+#include "clang/Basic/AddressSpaces.h"
#include "clang/Frontend/ASTUnit.h"
using namespace clang;
@@ -52,6 +53,7 @@ static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) {
BTCASE(Float);
BTCASE(Double);
BTCASE(LongDouble);
+ BTCASE(Float16);
BTCASE(Float128);
BTCASE(NullPtr);
BTCASE(Overload);
@@ -59,6 +61,13 @@ static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) {
BTCASE(ObjCId);
BTCASE(ObjCClass);
BTCASE(ObjCSel);
+#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) BTCASE(Id);
+#include "clang/Basic/OpenCLImageTypes.def"
+#undef IMAGE_TYPE
+ BTCASE(OCLSampler);
+ BTCASE(OCLEvent);
+ BTCASE(OCLQueue);
+ BTCASE(OCLReserveID);
default:
return CXType_Unexposed;
}
@@ -94,6 +103,7 @@ static CXTypeKind GetTypeKind(QualType T) {
TKCASE(MemberPointer);
TKCASE(Auto);
TKCASE(Elaborated);
+ TKCASE(Pipe);
default:
return CXType_Unexposed;
}
@@ -386,6 +396,30 @@ unsigned clang_isRestrictQualifiedType(CXType CT) {
return T.isLocalRestrictQualified();
}
+unsigned clang_getAddressSpace(CXType CT) {
+ QualType T = GetQualType(CT);
+
+ // For non language-specific address space, use separate helper function.
+ if (T.getAddressSpace() >= LangAS::FirstTargetAddressSpace) {
+ return T.getQualifiers().getAddressSpaceAttributePrintValue();
+ }
+ // FIXME: this function returns either a LangAS or a target AS
+ // Those values can overlap which makes this function rather unpredictable
+ // for any caller
+ return (unsigned)T.getAddressSpace();
+}
+
+CXString clang_getTypedefName(CXType CT) {
+ QualType T = GetQualType(CT);
+ const TypedefType *TT = T->getAs<TypedefType>();
+ if (TT) {
+ TypedefNameDecl *TD = TT->getDecl();
+ if (TD)
+ return cxstring::createDup(TD->getNameAsString().c_str());
+ }
+ return cxstring::createEmpty();
+}
+
CXType clang_getPointeeType(CXType CT) {
QualType T = GetQualType(CT);
const Type *TP = T.getTypePtrOrNull();
@@ -490,7 +524,7 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) {
TKIND(Char_U);
TKIND(UChar);
TKIND(Char16);
- TKIND(Char32);
+ TKIND(Char32);
TKIND(UShort);
TKIND(UInt);
TKIND(ULong);
@@ -508,6 +542,7 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) {
TKIND(Float);
TKIND(Double);
TKIND(LongDouble);
+ TKIND(Float16);
TKIND(Float128);
TKIND(NullPtr);
TKIND(Overload);
@@ -535,6 +570,14 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) {
TKIND(MemberPointer);
TKIND(Auto);
TKIND(Elaborated);
+ TKIND(Pipe);
+#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) TKIND(Id);
+#include "clang/Basic/OpenCLImageTypes.def"
+#undef IMAGE_TYPE
+ TKIND(OCLSampler);
+ TKIND(OCLEvent);
+ TKIND(OCLQueue);
+ TKIND(OCLReserveID);
}
#undef TKIND
return cxstring::createRef(s);
@@ -573,7 +616,7 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) {
TCALLINGCONV(X86Pascal);
TCALLINGCONV(X86RegCall);
TCALLINGCONV(X86VectorCall);
- TCALLINGCONV(X86_64Win64);
+ TCALLINGCONV(Win64);
TCALLINGCONV(X86_64SysV);
TCALLINGCONV(AAPCS);
TCALLINGCONV(AAPCS_VFP);
@@ -646,6 +689,24 @@ CXType clang_getCursorResultType(CXCursor C) {
return MakeCXType(QualType(), cxcursor::getCursorTU(C));
}
+int clang_getExceptionSpecificationType(CXType X) {
+ QualType T = GetQualType(X);
+ if (T.isNull())
+ return -1;
+
+ if (const auto *FD = T->getAs<FunctionProtoType>())
+ return static_cast<int>(FD->getExceptionSpecType());
+
+ return -1;
+}
+
+int clang_getCursorExceptionSpecificationType(CXCursor C) {
+ if (clang_isDeclaration(C.kind))
+ return clang_getExceptionSpecificationType(clang_getCursorType(C));
+
+ return -1;
+}
+
unsigned clang_isPODType(CXType X) {
QualType T = GetQualType(X);
if (T.isNull())
@@ -1039,3 +1100,12 @@ CXType clang_Type_getNamedType(CXType CT){
return MakeCXType(QualType(), GetTU(CT));
}
+
+unsigned clang_Type_isTransparentTagTypedef(CXType TT){
+ QualType T = GetQualType(TT);
+ if (auto *TT = dyn_cast_or_null<TypedefType>(T.getTypePtrOrNull())) {
+ if (auto *D = TT->getDecl())
+ return D->isTransparentTag();
+ }
+ return false;
+}
diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp
index f98b258879..2a136242ef 100644
--- a/tools/libclang/Indexing.cpp
+++ b/tools/libclang/Indexing.cpp
@@ -262,7 +262,8 @@ public:
/// MacroUndefined - This hook is called whenever a macro #undef is seen.
/// MI is released immediately following this callback.
void MacroUndefined(const Token &MacroNameTok,
- const MacroDefinition &MD) override {}
+ const MacroDefinition &MD,
+ const MacroDirective *UD) override {}
/// MacroExpands - This is called by when a macro invocation is found.
void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
@@ -271,7 +272,8 @@ public:
/// 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 222cb67839..fb4547a867 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -12,6 +12,7 @@ clang_CXXMethod_isConst
clang_CXXMethod_isPureVirtual
clang_CXXMethod_isStatic
clang_CXXMethod_isVirtual
+clang_EnumDecl_isScoped
clang_Cursor_getArgument
clang_Cursor_getNumTemplateArguments
clang_Cursor_getTemplateArgumentKind
@@ -22,6 +23,7 @@ clang_Cursor_getBriefCommentText
clang_Cursor_getCommentRange
clang_Cursor_getCXXManglings
clang_Cursor_getMangling
+clang_Cursor_getObjCManglings
clang_Cursor_getParsedComment
clang_Cursor_getRawCommentText
clang_Cursor_getNumArguments
@@ -35,6 +37,7 @@ clang_Cursor_getReceiverType
clang_Cursor_isAnonymous
clang_Cursor_isBitField
clang_Cursor_isDynamicCall
+clang_Cursor_isExternalSymbol
clang_Cursor_isNull
clang_Cursor_isObjCOptional
clang_Cursor_isVariadic
@@ -79,6 +82,9 @@ clang_TParamCommandComment_getParamName
clang_TParamCommandComment_isParamPositionValid
clang_TParamCommandComment_getDepth
clang_TParamCommandComment_getIndex
+clang_TargetInfo_dispose
+clang_TargetInfo_getPointerWidth
+clang_TargetInfo_getTriple
clang_Type_getAlignOf
clang_Type_getClassType
clang_Type_getSizeOf
@@ -88,6 +94,7 @@ clang_Type_getTemplateArgumentAsType
clang_Type_getCXXRefQualifier
clang_Type_visitFields
clang_Type_getNamedType
+clang_Type_isTransparentTagTypedef
clang_VerbatimBlockLineComment_getText
clang_VerbatimLineComment_getText
clang_HTMLTagComment_getAsString
@@ -142,6 +149,7 @@ clang_findReferencesInFile
clang_findReferencesInFileWithBlock
clang_formatDiagnostic
clang_free
+clang_getAddressSpace
clang_getAllSkippedRanges
clang_getArgType
clang_getArrayElementType
@@ -169,6 +177,7 @@ clang_getCursorCompletionString
clang_getCursorDefinition
clang_getCursorDisplayName
clang_getCursorExtent
+clang_getCursorExceptionSpecificationType
clang_getCursorKind
clang_getCursorKindSpelling
clang_getCursorLanguage
@@ -181,6 +190,7 @@ clang_getCursorReferenced
clang_getCursorResultType
clang_getCursorSemanticParent
clang_getCursorSpelling
+clang_getCursorTLSKind
clang_getCursorType
clang_getCursorUSR
clang_getCursorVisibility
@@ -204,6 +214,7 @@ clang_getElementType
clang_getEnumConstantDeclUnsignedValue
clang_getEnumConstantDeclValue
clang_getEnumDeclIntegerType
+clang_getExceptionSpecificationType
clang_getFieldDeclBitWidth
clang_getExpansionLocation
clang_getFile
@@ -249,10 +260,12 @@ clang_getTokenLocation
clang_getTokenSpelling
clang_getTranslationUnitCursor
clang_getTranslationUnitSpelling
+clang_getTranslationUnitTargetInfo
clang_getTypeDeclaration
clang_getTypeKindSpelling
clang_getTypeSpelling
clang_getTypedefDeclUnderlyingType
+clang_getTypedefName
clang_hashCursor
clang_indexLoc_getCXSourceLocation
clang_indexLoc_getFileLocation
@@ -299,6 +312,7 @@ clang_remap_getFilenames
clang_remap_getNumFiles
clang_reparseTranslationUnit
clang_saveTranslationUnit
+clang_suspendTranslationUnit
clang_sortCodeCompletionResults
clang_toggleCrashRecovery
clang_tokenize
diff --git a/tools/scan-build-py/libscanbuild/__init__.py b/tools/scan-build-py/libscanbuild/__init__.py
index ca75174912..800926ebb6 100644
--- a/tools/scan-build-py/libscanbuild/__init__.py
+++ b/tools/scan-build-py/libscanbuild/__init__.py
@@ -41,12 +41,6 @@ def duplicate_check(method):
return predicate
-def tempdir():
- """ Return the default temorary directory. """
-
- return os.getenv('TMPDIR', os.getenv('TEMP', os.getenv('TMP', '/tmp')))
-
-
def run_build(command, *args, **kwargs):
""" Run and report build command execution
diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py
index 0485ca85f6..b5614b5b6d 100644
--- a/tools/scan-build-py/libscanbuild/analyze.py
+++ b/tools/scan-build-py/libscanbuild/analyze.py
@@ -16,18 +16,23 @@ import os
import os.path
import json
import logging
-import tempfile
import multiprocessing
+import tempfile
+import functools
+import subprocess
import contextlib
import datetime
+
from libscanbuild import command_entry_point, compiler_wrapper, \
- wrapper_environment, run_build
+ wrapper_environment, run_build, run_command
from libscanbuild.arguments import parse_args_for_scan_build, \
parse_args_for_analyze_build
-from libscanbuild.runner import run
from libscanbuild.intercept import capture
from libscanbuild.report import document
-from libscanbuild.compilation import split_command
+from libscanbuild.compilation import split_command, classify_source, \
+ compiler_language
+from libscanbuild.clang import get_version, get_arguments
+from libscanbuild.shell import decode
__all__ = ['scan_build', 'analyze_build', 'analyze_compiler_wrapper']
@@ -40,7 +45,8 @@ def scan_build():
""" Entry point for scan-build command. """
args = parse_args_for_scan_build()
- with report_directory(args.output, args.keep_empty) as target_dir:
+ # will re-assign the report directory as new output
+ with report_directory(args.output, args.keep_empty) as args.output:
# Run against a build command. there are cases, when analyzer run
# is not required. But we need to set up everything for the
# wrappers, because 'configure' needs to capture the CC/CXX values
@@ -50,13 +56,13 @@ def scan_build():
exit_code = capture(args)
# Run the analyzer against the captured commands.
if need_analyzer(args.build):
- run_analyzer(args, target_dir)
+ run_analyzer_parallel(args)
else:
# Run build command and analyzer with compiler wrappers.
- environment = setup_environment(args, target_dir)
+ environment = setup_environment(args)
exit_code = run_build(args.build, env=environment)
# Cover report generation and bug counting.
- number_of_bugs = document(args, target_dir, False)
+ number_of_bugs = document(args)
# Set exit status as it was requested.
return number_of_bugs if args.status_bugs else exit_code
@@ -66,11 +72,12 @@ def analyze_build():
""" Entry point for analyze-build command. """
args = parse_args_for_analyze_build()
- with report_directory(args.output, args.keep_empty) as target_dir:
+ # will re-assign the report directory as new output
+ with report_directory(args.output, args.keep_empty) as args.output:
# Run the analyzer against a compilation db.
- run_analyzer(args, target_dir)
+ run_analyzer_parallel(args)
# Cover report generation and bug counting.
- number_of_bugs = document(args, target_dir, True)
+ number_of_bugs = document(args)
# Set exit status as it was requested.
return number_of_bugs if args.status_bugs else 0
@@ -88,7 +95,7 @@ def need_analyzer(args):
return len(args) and not re.search('configure|autogen', args[0])
-def run_analyzer(args, output_dir):
+def run_analyzer_parallel(args):
""" Runs the analyzer against the given compilation database. """
def exclude(filename):
@@ -98,7 +105,7 @@ def run_analyzer(args, output_dir):
consts = {
'clang': args.clang,
- 'output_dir': output_dir,
+ 'output_dir': args.output,
'output_format': args.output_format,
'output_failures': args.output_failures,
'direct_args': analyzer_params(args),
@@ -120,7 +127,7 @@ def run_analyzer(args, output_dir):
pool.join()
-def setup_environment(args, destination):
+def setup_environment(args):
""" Set up environment for build command to interpose compiler wrapper. """
environment = dict(os.environ)
@@ -129,7 +136,7 @@ def setup_environment(args, destination):
'CC': COMPILER_WRAPPER_CC,
'CXX': COMPILER_WRAPPER_CXX,
'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '',
- 'ANALYZE_BUILD_REPORT_DIR': destination,
+ 'ANALYZE_BUILD_REPORT_DIR': args.output,
'ANALYZE_BUILD_REPORT_FORMAT': args.output_format,
'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '',
'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)),
@@ -242,7 +249,7 @@ def analyzer_params(args):
if args.output_format:
result.append('-analyzer-output={0}'.format(args.output_format))
if args.analyzer_config:
- result.append(args.analyzer_config)
+ result.extend(['-analyzer-config', args.analyzer_config])
if args.verbose >= 4:
result.append('-analyzer-display-progress')
if args.plugins:
@@ -257,3 +264,277 @@ def analyzer_params(args):
result.append('-analyzer-viz-egraph-ubigraph')
return prefix_with('-Xclang', result)
+
+
+def require(required):
+ """ Decorator for checking the required values in state.
+
+ It checks the required attributes in the passed state and stop when
+ any of those is missing. """
+
+ def decorator(function):
+ @functools.wraps(function)
+ def wrapper(*args, **kwargs):
+ for key in required:
+ if key not in args[0]:
+ raise KeyError('{0} not passed to {1}'.format(
+ key, function.__name__))
+
+ return function(*args, **kwargs)
+
+ return wrapper
+
+ return decorator
+
+
+@require(['command', # entry from compilation database
+ 'directory', # entry from compilation database
+ 'file', # entry from compilation database
+ 'clang', # clang executable name (and path)
+ 'direct_args', # arguments from command line
+ 'force_debug', # kill non debug macros
+ 'output_dir', # where generated report files shall go
+ 'output_format', # it's 'plist' or 'html' or both
+ 'output_failures']) # generate crash reports or not
+def run(opts):
+ """ Entry point to run (or not) static analyzer against a single entry
+ of the compilation database.
+
+ This complex task is decomposed into smaller methods which are calling
+ each other in chain. If the analyzis is not possibe the given method
+ just return and break the chain.
+
+ The passed parameter is a python dictionary. Each method first check
+ that the needed parameters received. (This is done by the 'require'
+ decorator. It's like an 'assert' to check the contract between the
+ caller and the called method.) """
+
+ try:
+ command = opts.pop('command')
+ command = command if isinstance(command, list) else decode(command)
+ logging.debug("Run analyzer against '%s'", command)
+ opts.update(classify_parameters(command))
+
+ return arch_check(opts)
+ except Exception:
+ logging.error("Problem occured during analyzis.", exc_info=1)
+ return None
+
+
+@require(['clang', 'directory', 'flags', 'file', 'output_dir', 'language',
+ 'error_output', 'exit_code'])
+def report_failure(opts):
+ """ Create report when analyzer failed.
+
+ The major report is the preprocessor output. The output filename generated
+ randomly. The compiler output also captured into '.stderr.txt' file.
+ And some more execution context also saved into '.info.txt' file. """
+
+ def extension():
+ """ Generate preprocessor file extension. """
+
+ mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
+ return mapping.get(opts['language'], '.i')
+
+ def destination():
+ """ Creates failures directory if not exits yet. """
+
+ failures_dir = os.path.join(opts['output_dir'], 'failures')
+ if not os.path.isdir(failures_dir):
+ os.makedirs(failures_dir)
+ return failures_dir
+
+ # Classify error type: when Clang terminated by a signal it's a 'Crash'.
+ # (python subprocess Popen.returncode is negative when child terminated
+ # by signal.) Everything else is 'Other Error'.
+ error = 'crash' if opts['exit_code'] < 0 else 'other_error'
+ # Create preprocessor output file name. (This is blindly following the
+ # Perl implementation.)
+ (handle, name) = tempfile.mkstemp(suffix=extension(),
+ prefix='clang_' + error + '_',
+ dir=destination())
+ os.close(handle)
+ # Execute Clang again, but run the syntax check only.
+ cwd = opts['directory']
+ cmd = get_arguments(
+ [opts['clang'], '-fsyntax-only', '-E'
+ ] + opts['flags'] + [opts['file'], '-o', name], cwd)
+ run_command(cmd, cwd=cwd)
+ # write general information about the crash
+ with open(name + '.info.txt', 'w') as handle:
+ handle.write(opts['file'] + os.linesep)
+ handle.write(error.title().replace('_', ' ') + os.linesep)
+ handle.write(' '.join(cmd) + os.linesep)
+ handle.write(' '.join(os.uname()) + os.linesep)
+ handle.write(get_version(opts['clang']))
+ handle.close()
+ # write the captured output too
+ with open(name + '.stderr.txt', 'w') as handle:
+ handle.writelines(opts['error_output'])
+ handle.close()
+
+
+@require(['clang', 'directory', 'flags', 'direct_args', 'file', 'output_dir',
+ 'output_format'])
+def run_analyzer(opts, continuation=report_failure):
+ """ It assembles the analysis command line and executes it. Capture the
+ output of the analysis and returns with it. If failure reports are
+ requested, it calls the continuation to generate it. """
+
+ def target():
+ """ Creates output file name for reports. """
+ if opts['output_format'] in {'plist', 'plist-html'}:
+ (handle, name) = tempfile.mkstemp(prefix='report-',
+ suffix='.plist',
+ dir=opts['output_dir'])
+ os.close(handle)
+ return name
+ return opts['output_dir']
+
+ try:
+ cwd = opts['directory']
+ cmd = get_arguments([opts['clang'], '--analyze'] +
+ opts['direct_args'] + opts['flags'] +
+ [opts['file'], '-o', target()],
+ cwd)
+ output = run_command(cmd, cwd=cwd)
+ return {'error_output': output, 'exit_code': 0}
+ except subprocess.CalledProcessError as ex:
+ result = {'error_output': ex.output, 'exit_code': ex.returncode}
+ if opts.get('output_failures', False):
+ opts.update(result)
+ continuation(opts)
+ return result
+
+
+@require(['flags', 'force_debug'])
+def filter_debug_flags(opts, continuation=run_analyzer):
+ """ Filter out nondebug macros when requested. """
+
+ if opts.pop('force_debug'):
+ # lazy implementation just append an undefine macro at the end
+ opts.update({'flags': opts['flags'] + ['-UNDEBUG']})
+
+ return continuation(opts)
+
+
+@require(['language', 'compiler', 'file', 'flags'])
+def language_check(opts, continuation=filter_debug_flags):
+ """ Find out the language from command line parameters or file name
+ extension. The decision also influenced by the compiler invocation. """
+
+ accepted = frozenset({
+ 'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output',
+ 'c++-cpp-output', 'objective-c-cpp-output'
+ })
+
+ # language can be given as a parameter...
+ language = opts.pop('language')
+ compiler = opts.pop('compiler')
+ # ... or find out from source file extension
+ if language is None and compiler is not None:
+ language = classify_source(opts['file'], compiler == 'c')
+
+ if language is None:
+ logging.debug('skip analysis, language not known')
+ return None
+ elif language not in accepted:
+ logging.debug('skip analysis, language not supported')
+ return None
+ else:
+ logging.debug('analysis, language: %s', language)
+ opts.update({'language': language,
+ 'flags': ['-x', language] + opts['flags']})
+ return continuation(opts)
+
+
+@require(['arch_list', 'flags'])
+def arch_check(opts, continuation=language_check):
+ """ Do run analyzer through one of the given architectures. """
+
+ disabled = frozenset({'ppc', 'ppc64'})
+
+ received_list = opts.pop('arch_list')
+ if received_list:
+ # filter out disabled architectures and -arch switches
+ filtered_list = [a for a in received_list if a not in disabled]
+ if filtered_list:
+ # There should be only one arch given (or the same multiple
+ # times). If there are multiple arch are given and are not
+ # the same, those should not change the pre-processing step.
+ # But that's the only pass we have before run the analyzer.
+ current = filtered_list.pop()
+ logging.debug('analysis, on arch: %s', current)
+
+ opts.update({'flags': ['-arch', current] + opts['flags']})
+ return continuation(opts)
+ else:
+ logging.debug('skip analysis, found not supported arch')
+ return None
+ else:
+ logging.debug('analysis, on default arch')
+ return continuation(opts)
+
+# To have good results from static analyzer certain compiler options shall be
+# omitted. The compiler flag filtering only affects the static analyzer run.
+#
+# Keys are the option name, value number of options to skip
+IGNORED_FLAGS = {
+ '-c': 0, # compile option will be overwritten
+ '-fsyntax-only': 0, # static analyzer option will be overwritten
+ '-o': 1, # will set up own output file
+ # flags below are inherited from the perl implementation.
+ '-g': 0,
+ '-save-temps': 0,
+ '-install_name': 1,
+ '-exported_symbols_list': 1,
+ '-current_version': 1,
+ '-compatibility_version': 1,
+ '-init': 1,
+ '-e': 1,
+ '-seg1addr': 1,
+ '-bundle_loader': 1,
+ '-multiply_defined': 1,
+ '-sectorder': 3,
+ '--param': 1,
+ '--serialize-diagnostics': 1
+}
+
+
+def classify_parameters(command):
+ """ Prepare compiler flags (filters some and add others) and take out
+ language (-x) and architecture (-arch) flags for future processing. """
+
+ result = {
+ 'flags': [], # the filtered compiler flags
+ 'arch_list': [], # list of architecture flags
+ 'language': None, # compilation language, None, if not specified
+ 'compiler': compiler_language(command) # 'c' or 'c++'
+ }
+
+ # iterate on the compile options
+ args = iter(command[1:])
+ for arg in args:
+ # take arch flags into a separate basket
+ if arg == '-arch':
+ result['arch_list'].append(next(args))
+ # take language
+ elif arg == '-x':
+ result['language'] = next(args)
+ # parameters which looks source file are not flags
+ elif re.match(r'^[^-].+', arg) and classify_source(arg):
+ pass
+ # ignore some flags
+ elif arg in IGNORED_FLAGS:
+ count = IGNORED_FLAGS[arg]
+ for _ in range(count):
+ next(args)
+ # we don't care about extra warnings, but we should suppress ones
+ # that we don't want to see.
+ elif re.match(r'^-W.+', arg) and not re.match(r'^-Wno-.+', arg):
+ pass
+ # and consider everything else as compilation flag.
+ else:
+ result['flags'].append(arg)
+
+ return result
diff --git a/tools/scan-build-py/libscanbuild/arguments.py b/tools/scan-build-py/libscanbuild/arguments.py
index fe5725dcdf..2735123f9f 100644
--- a/tools/scan-build-py/libscanbuild/arguments.py
+++ b/tools/scan-build-py/libscanbuild/arguments.py
@@ -17,7 +17,8 @@ import os
import sys
import argparse
import logging
-from libscanbuild import reconfigure_logging, tempdir
+import tempfile
+from libscanbuild import reconfigure_logging
from libscanbuild.clang import get_checkers
__all__ = ['parse_args_for_intercept_build', 'parse_args_for_analyze_build',
@@ -187,7 +188,7 @@ def create_analyze_parser(from_build_command):
'--output',
'-o',
metavar='<path>',
- default=tempdir(),
+ default=tempfile.gettempdir(),
help="""Specifies the output directory for analyzer reports.
Subdirectory will be created if default directory is targeted.""")
output.add_argument(
diff --git a/tools/scan-build-py/libscanbuild/intercept.py b/tools/scan-build-py/libscanbuild/intercept.py
index 71b957d83a..b9bf9e9175 100644
--- a/tools/scan-build-py/libscanbuild/intercept.py
+++ b/tools/scan-build-py/libscanbuild/intercept.py
@@ -31,7 +31,7 @@ import logging
from libear import build_libear, TemporaryDirectory
from libscanbuild import command_entry_point, compiler_wrapper, \
wrapper_environment, run_command, run_build
-from libscanbuild import duplicate_check, tempdir
+from libscanbuild import duplicate_check
from libscanbuild.compilation import split_command
from libscanbuild.arguments import parse_args_for_intercept_build
from libscanbuild.shell import encode, decode
@@ -84,7 +84,7 @@ def capture(args):
for entry in itertools.chain(previous, current)
if os.path.exists(entry['file']) and not duplicate(entry))
- with TemporaryDirectory(prefix='intercept-', dir=tempdir()) as tmp_dir:
+ with TemporaryDirectory(prefix='intercept-') as tmp_dir:
# run the build command
environment = setup_environment(args, tmp_dir)
exit_code = run_build(args.build, env=environment)
diff --git a/tools/scan-build-py/libscanbuild/report.py b/tools/scan-build-py/libscanbuild/report.py
index 08170093f7..54b9695d92 100644
--- a/tools/scan-build-py/libscanbuild/report.py
+++ b/tools/scan-build-py/libscanbuild/report.py
@@ -18,58 +18,60 @@ import plistlib
import glob
import json
import logging
+import datetime
from libscanbuild import duplicate_check
from libscanbuild.clang import get_version
__all__ = ['document']
-def document(args, output_dir, use_cdb):
+def document(args):
""" Generates cover report and returns the number of bugs/crashes. """
html_reports_available = args.output_format in {'html', 'plist-html'}
logging.debug('count crashes and bugs')
- crash_count = sum(1 for _ in read_crashes(output_dir))
+ crash_count = sum(1 for _ in read_crashes(args.output))
bug_counter = create_counters()
- for bug in read_bugs(output_dir, html_reports_available):
+ for bug in read_bugs(args.output, html_reports_available):
bug_counter(bug)
result = crash_count + bug_counter.total
if html_reports_available and result:
+ use_cdb = os.path.exists(args.cdb)
+
logging.debug('generate index.html file')
- # common prefix for source files to have sort filenames
+ # common prefix for source files to have sorter path
prefix = commonprefix_from(args.cdb) if use_cdb else os.getcwd()
# assemble the cover from multiple fragments
+ fragments = []
try:
- fragments = []
if bug_counter.total:
- fragments.append(bug_summary(output_dir, bug_counter))
- fragments.append(bug_report(output_dir, prefix))
+ fragments.append(bug_summary(args.output, bug_counter))
+ fragments.append(bug_report(args.output, prefix))
if crash_count:
- fragments.append(crash_report(output_dir, prefix))
- assemble_cover(output_dir, prefix, args, fragments)
- # copy additinal files to the report
- copy_resource_files(output_dir)
+ fragments.append(crash_report(args.output, prefix))
+ assemble_cover(args, prefix, fragments)
+ # copy additional files to the report
+ copy_resource_files(args.output)
if use_cdb:
- shutil.copy(args.cdb, output_dir)
+ shutil.copy(args.cdb, args.output)
finally:
for fragment in fragments:
os.remove(fragment)
return result
-def assemble_cover(output_dir, prefix, args, fragments):
+def assemble_cover(args, prefix, fragments):
""" Put together the fragments into a final report. """
import getpass
import socket
- import datetime
if args.html_title is None:
args.html_title = os.path.basename(prefix) + ' - analyzer results'
- with open(os.path.join(output_dir, 'index.html'), 'w') as handle:
+ with open(os.path.join(args.output, 'index.html'), 'w') as handle:
indent = 0
handle.write(reindent("""
|<!DOCTYPE html>
@@ -481,9 +483,10 @@ def commonprefix_from(filename):
def commonprefix(files):
- """ Fixed version of os.path.commonprefix. Return the longest path prefix
- that is a prefix of all paths in filenames. """
+ """ Fixed version of os.path.commonprefix.
+ :param files: list of file names.
+ :return: the longest path prefix that is a prefix of all files. """
result = None
for current in files:
if result is not None:
diff --git a/tools/scan-build-py/libscanbuild/runner.py b/tools/scan-build-py/libscanbuild/runner.py
deleted file mode 100644
index 014b872479..0000000000
--- a/tools/scan-build-py/libscanbuild/runner.py
+++ /dev/null
@@ -1,294 +0,0 @@
-# -*- coding: utf-8 -*-
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-""" This module is responsible to run the analyzer commands. """
-
-import re
-import os
-import os.path
-import tempfile
-import functools
-import subprocess
-import logging
-from libscanbuild import run_command
-from libscanbuild.compilation import classify_source, compiler_language
-from libscanbuild.clang import get_version, get_arguments
-from libscanbuild.shell import decode
-
-__all__ = ['run']
-
-# To have good results from static analyzer certain compiler options shall be
-# omitted. The compiler flag filtering only affects the static analyzer run.
-#
-# Keys are the option name, value number of options to skip
-IGNORED_FLAGS = {
- '-c': 0, # compile option will be overwritten
- '-fsyntax-only': 0, # static analyzer option will be overwritten
- '-o': 1, # will set up own output file
- # flags below are inherited from the perl implementation.
- '-g': 0,
- '-save-temps': 0,
- '-install_name': 1,
- '-exported_symbols_list': 1,
- '-current_version': 1,
- '-compatibility_version': 1,
- '-init': 1,
- '-e': 1,
- '-seg1addr': 1,
- '-bundle_loader': 1,
- '-multiply_defined': 1,
- '-sectorder': 3,
- '--param': 1,
- '--serialize-diagnostics': 1
-}
-
-
-def require(required):
- """ Decorator for checking the required values in state.
-
- It checks the required attributes in the passed state and stop when
- any of those is missing. """
-
- def decorator(function):
- @functools.wraps(function)
- def wrapper(*args, **kwargs):
- for key in required:
- if key not in args[0]:
- raise KeyError('{0} not passed to {1}'.format(
- key, function.__name__))
-
- return function(*args, **kwargs)
-
- return wrapper
-
- return decorator
-
-
-@require(['command', # entry from compilation database
- 'directory', # entry from compilation database
- 'file', # entry from compilation database
- 'clang', # clang executable name (and path)
- 'direct_args', # arguments from command line
- 'force_debug', # kill non debug macros
- 'output_dir', # where generated report files shall go
- 'output_format', # it's 'plist' or 'html' or both
- 'output_failures']) # generate crash reports or not
-def run(opts):
- """ Entry point to run (or not) static analyzer against a single entry
- of the compilation database.
-
- This complex task is decomposed into smaller methods which are calling
- each other in chain. If the analyzis is not possibe the given method
- just return and break the chain.
-
- The passed parameter is a python dictionary. Each method first check
- that the needed parameters received. (This is done by the 'require'
- decorator. It's like an 'assert' to check the contract between the
- caller and the called method.) """
-
- try:
- command = opts.pop('command')
- command = command if isinstance(command, list) else decode(command)
- logging.debug("Run analyzer against '%s'", command)
- opts.update(classify_parameters(command))
-
- return arch_check(opts)
- except Exception:
- logging.error("Problem occured during analyzis.", exc_info=1)
- return None
-
-
-@require(['clang', 'directory', 'flags', 'file', 'output_dir', 'language',
- 'error_output', 'exit_code'])
-def report_failure(opts):
- """ Create report when analyzer failed.
-
- The major report is the preprocessor output. The output filename generated
- randomly. The compiler output also captured into '.stderr.txt' file.
- And some more execution context also saved into '.info.txt' file. """
-
- def extension():
- """ Generate preprocessor file extension. """
-
- mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'}
- return mapping.get(opts['language'], '.i')
-
- def destination():
- """ Creates failures directory if not exits yet. """
-
- failures_dir = os.path.join(opts['output_dir'], 'failures')
- if not os.path.isdir(failures_dir):
- os.makedirs(failures_dir)
- return failures_dir
-
- # Classify error type: when Clang terminated by a signal it's a 'Crash'.
- # (python subprocess Popen.returncode is negative when child terminated
- # by signal.) Everything else is 'Other Error'.
- error = 'crash' if opts['exit_code'] < 0 else 'other_error'
- # Create preprocessor output file name. (This is blindly following the
- # Perl implementation.)
- (handle, name) = tempfile.mkstemp(suffix=extension(),
- prefix='clang_' + error + '_',
- dir=destination())
- os.close(handle)
- # Execute Clang again, but run the syntax check only.
- cwd = opts['directory']
- cmd = get_arguments(
- [opts['clang'], '-fsyntax-only', '-E'
- ] + opts['flags'] + [opts['file'], '-o', name], cwd)
- run_command(cmd, cwd=cwd)
- # write general information about the crash
- with open(name + '.info.txt', 'w') as handle:
- handle.write(opts['file'] + os.linesep)
- handle.write(error.title().replace('_', ' ') + os.linesep)
- handle.write(' '.join(cmd) + os.linesep)
- handle.write(' '.join(os.uname()) + os.linesep)
- handle.write(get_version(opts['clang']))
- handle.close()
- # write the captured output too
- with open(name + '.stderr.txt', 'w') as handle:
- handle.writelines(opts['error_output'])
- handle.close()
-
-
-@require(['clang', 'directory', 'flags', 'direct_args', 'file', 'output_dir',
- 'output_format'])
-def run_analyzer(opts, continuation=report_failure):
- """ It assembles the analysis command line and executes it. Capture the
- output of the analysis and returns with it. If failure reports are
- requested, it calls the continuation to generate it. """
-
- def target():
- """ Creates output file name for reports. """
- if opts['output_format'] in {'plist', 'plist-html'}:
- (handle, name) = tempfile.mkstemp(prefix='report-',
- suffix='.plist',
- dir=opts['output_dir'])
- os.close(handle)
- return name
- return opts['output_dir']
-
- try:
- cwd = opts['directory']
- cmd = get_arguments([opts['clang'], '--analyze'] +
- opts['direct_args'] + opts['flags'] +
- [opts['file'], '-o', target()],
- cwd)
- output = run_command(cmd, cwd=cwd)
- return {'error_output': output, 'exit_code': 0}
- except subprocess.CalledProcessError as ex:
- result = {'error_output': ex.output, 'exit_code': ex.returncode}
- if opts.get('output_failures', False):
- opts.update(result)
- continuation(opts)
- return result
-
-
-@require(['flags', 'force_debug'])
-def filter_debug_flags(opts, continuation=run_analyzer):
- """ Filter out nondebug macros when requested. """
-
- if opts.pop('force_debug'):
- # lazy implementation just append an undefine macro at the end
- opts.update({'flags': opts['flags'] + ['-UNDEBUG']})
-
- return continuation(opts)
-
-
-@require(['language', 'compiler', 'file', 'flags'])
-def language_check(opts, continuation=filter_debug_flags):
- """ Find out the language from command line parameters or file name
- extension. The decision also influenced by the compiler invocation. """
-
- accepted = frozenset({
- 'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output',
- 'c++-cpp-output', 'objective-c-cpp-output'
- })
-
- # language can be given as a parameter...
- language = opts.pop('language')
- compiler = opts.pop('compiler')
- # ... or find out from source file extension
- if language is None and compiler is not None:
- language = classify_source(opts['file'], compiler == 'c')
-
- if language is None:
- logging.debug('skip analysis, language not known')
- return None
- elif language not in accepted:
- logging.debug('skip analysis, language not supported')
- return None
- else:
- logging.debug('analysis, language: %s', language)
- opts.update({'language': language,
- 'flags': ['-x', language] + opts['flags']})
- return continuation(opts)
-
-
-@require(['arch_list', 'flags'])
-def arch_check(opts, continuation=language_check):
- """ Do run analyzer through one of the given architectures. """
-
- disabled = frozenset({'ppc', 'ppc64'})
-
- received_list = opts.pop('arch_list')
- if received_list:
- # filter out disabled architectures and -arch switches
- filtered_list = [a for a in received_list if a not in disabled]
- if filtered_list:
- # There should be only one arch given (or the same multiple
- # times). If there are multiple arch are given and are not
- # the same, those should not change the pre-processing step.
- # But that's the only pass we have before run the analyzer.
- current = filtered_list.pop()
- logging.debug('analysis, on arch: %s', current)
-
- opts.update({'flags': ['-arch', current] + opts['flags']})
- return continuation(opts)
- else:
- logging.debug('skip analysis, found not supported arch')
- return None
- else:
- logging.debug('analysis, on default arch')
- return continuation(opts)
-
-
-def classify_parameters(command):
- """ Prepare compiler flags (filters some and add others) and take out
- language (-x) and architecture (-arch) flags for future processing. """
-
- result = {
- 'flags': [], # the filtered compiler flags
- 'arch_list': [], # list of architecture flags
- 'language': None, # compilation language, None, if not specified
- 'compiler': compiler_language(command) # 'c' or 'c++'
- }
-
- # iterate on the compile options
- args = iter(command[1:])
- for arg in args:
- # take arch flags into a separate basket
- if arg == '-arch':
- result['arch_list'].append(next(args))
- # take language
- elif arg == '-x':
- result['language'] = next(args)
- # parameters which looks source file are not flags
- elif re.match(r'^[^-].+', arg) and classify_source(arg):
- pass
- # ignore some flags
- elif arg in IGNORED_FLAGS:
- count = IGNORED_FLAGS[arg]
- for _ in range(count):
- next(args)
- # we don't care about extra warnings, but we should suppress ones
- # that we don't want to see.
- elif re.match(r'^-W.+', arg) and not re.match(r'^-Wno-.+', arg):
- pass
- # and consider everything else as compilation flag.
- else:
- result['flags'].append(arg)
-
- return result
diff --git a/tools/scan-build-py/tests/unit/__init__.py b/tools/scan-build-py/tests/unit/__init__.py
index dc8bf12eb4..6b7fd9fa0d 100644
--- a/tools/scan-build-py/tests/unit/__init__.py
+++ b/tools/scan-build-py/tests/unit/__init__.py
@@ -7,7 +7,6 @@
from . import test_libear
from . import test_compilation
from . import test_clang
-from . import test_runner
from . import test_report
from . import test_analyze
from . import test_intercept
@@ -18,7 +17,6 @@ def load_tests(loader, suite, _):
suite.addTests(loader.loadTestsFromModule(test_libear))
suite.addTests(loader.loadTestsFromModule(test_compilation))
suite.addTests(loader.loadTestsFromModule(test_clang))
- suite.addTests(loader.loadTestsFromModule(test_runner))
suite.addTests(loader.loadTestsFromModule(test_report))
suite.addTests(loader.loadTestsFromModule(test_analyze))
suite.addTests(loader.loadTestsFromModule(test_intercept))
diff --git a/tools/scan-build-py/tests/unit/test_analyze.py b/tools/scan-build-py/tests/unit/test_analyze.py
index 5cfc121266..a250ff2213 100644
--- a/tools/scan-build-py/tests/unit/test_analyze.py
+++ b/tools/scan-build-py/tests/unit/test_analyze.py
@@ -7,6 +7,10 @@
import libear
import libscanbuild.analyze as sut
import unittest
+import re
+import os
+import os.path
+
class ReportDirectoryTest(unittest.TestCase):
@@ -20,3 +24,312 @@ class ReportDirectoryTest(unittest.TestCase):
sut.report_directory(tmpdir, False) as report_dir3:
self.assertLess(report_dir1, report_dir2)
self.assertLess(report_dir2, report_dir3)
+
+
+class FilteringFlagsTest(unittest.TestCase):
+
+ def test_language_captured(self):
+ def test(flags):
+ cmd = ['clang', '-c', 'source.c'] + flags
+ opts = sut.classify_parameters(cmd)
+ return opts['language']
+
+ self.assertEqual(None, test([]))
+ self.assertEqual('c', test(['-x', 'c']))
+ self.assertEqual('cpp', test(['-x', 'cpp']))
+
+ def test_arch(self):
+ def test(flags):
+ cmd = ['clang', '-c', 'source.c'] + flags
+ opts = sut.classify_parameters(cmd)
+ return opts['arch_list']
+
+ self.assertEqual([], test([]))
+ self.assertEqual(['mips'], test(['-arch', 'mips']))
+ self.assertEqual(['mips', 'i386'],
+ test(['-arch', 'mips', '-arch', 'i386']))
+
+ def assertFlagsChanged(self, expected, flags):
+ cmd = ['clang', '-c', 'source.c'] + flags
+ opts = sut.classify_parameters(cmd)
+ self.assertEqual(expected, opts['flags'])
+
+ def assertFlagsUnchanged(self, flags):
+ self.assertFlagsChanged(flags, flags)
+
+ def assertFlagsFiltered(self, flags):
+ self.assertFlagsChanged([], flags)
+
+ def test_optimalizations_pass(self):
+ self.assertFlagsUnchanged(['-O'])
+ self.assertFlagsUnchanged(['-O1'])
+ self.assertFlagsUnchanged(['-Os'])
+ self.assertFlagsUnchanged(['-O2'])
+ self.assertFlagsUnchanged(['-O3'])
+
+ def test_include_pass(self):
+ self.assertFlagsUnchanged([])
+ self.assertFlagsUnchanged(['-include', '/usr/local/include'])
+ self.assertFlagsUnchanged(['-I.'])
+ self.assertFlagsUnchanged(['-I', '.'])
+ self.assertFlagsUnchanged(['-I/usr/local/include'])
+ self.assertFlagsUnchanged(['-I', '/usr/local/include'])
+ self.assertFlagsUnchanged(['-I/opt', '-I', '/opt/otp/include'])
+ self.assertFlagsUnchanged(['-isystem', '/path'])
+ self.assertFlagsUnchanged(['-isystem=/path'])
+
+ def test_define_pass(self):
+ self.assertFlagsUnchanged(['-DNDEBUG'])
+ self.assertFlagsUnchanged(['-UNDEBUG'])
+ self.assertFlagsUnchanged(['-Dvar1=val1', '-Dvar2=val2'])
+ self.assertFlagsUnchanged(['-Dvar="val ues"'])
+
+ def test_output_filtered(self):
+ self.assertFlagsFiltered(['-o', 'source.o'])
+
+ def test_some_warning_filtered(self):
+ self.assertFlagsFiltered(['-Wall'])
+ self.assertFlagsFiltered(['-Wnoexcept'])
+ self.assertFlagsFiltered(['-Wreorder', '-Wunused', '-Wundef'])
+ self.assertFlagsUnchanged(['-Wno-reorder', '-Wno-unused'])
+
+ def test_compile_only_flags_pass(self):
+ self.assertFlagsUnchanged(['-std=C99'])
+ self.assertFlagsUnchanged(['-nostdinc'])
+ self.assertFlagsUnchanged(['-isystem', '/image/debian'])
+ self.assertFlagsUnchanged(['-iprefix', '/usr/local'])
+ self.assertFlagsUnchanged(['-iquote=me'])
+ self.assertFlagsUnchanged(['-iquote', 'me'])
+
+ def test_compile_and_link_flags_pass(self):
+ self.assertFlagsUnchanged(['-fsinged-char'])
+ self.assertFlagsUnchanged(['-fPIC'])
+ self.assertFlagsUnchanged(['-stdlib=libc++'])
+ self.assertFlagsUnchanged(['--sysroot', '/'])
+ self.assertFlagsUnchanged(['-isysroot', '/'])
+
+ def test_some_flags_filtered(self):
+ self.assertFlagsFiltered(['-g'])
+ self.assertFlagsFiltered(['-fsyntax-only'])
+ self.assertFlagsFiltered(['-save-temps'])
+ self.assertFlagsFiltered(['-init', 'my_init'])
+ self.assertFlagsFiltered(['-sectorder', 'a', 'b', 'c'])
+
+
+class Spy(object):
+ def __init__(self):
+ self.arg = None
+ self.success = 0
+
+ def call(self, params):
+ self.arg = params
+ return self.success
+
+
+class RunAnalyzerTest(unittest.TestCase):
+
+ @staticmethod
+ def run_analyzer(content, failures_report):
+ with libear.TemporaryDirectory() as tmpdir:
+ filename = os.path.join(tmpdir, 'test.cpp')
+ with open(filename, 'w') as handle:
+ handle.write(content)
+
+ opts = {
+ 'clang': 'clang',
+ 'directory': os.getcwd(),
+ 'flags': [],
+ 'direct_args': [],
+ 'file': filename,
+ 'output_dir': tmpdir,
+ 'output_format': 'plist',
+ 'output_failures': failures_report
+ }
+ spy = Spy()
+ result = sut.run_analyzer(opts, spy.call)
+ return (result, spy.arg)
+
+ def test_run_analyzer(self):
+ content = "int div(int n, int d) { return n / d; }"
+ (result, fwds) = RunAnalyzerTest.run_analyzer(content, False)
+ self.assertEqual(None, fwds)
+ self.assertEqual(0, result['exit_code'])
+
+ def test_run_analyzer_crash(self):
+ content = "int div(int n, int d) { return n / d }"
+ (result, fwds) = RunAnalyzerTest.run_analyzer(content, False)
+ self.assertEqual(None, fwds)
+ self.assertEqual(1, result['exit_code'])
+
+ def test_run_analyzer_crash_and_forwarded(self):
+ content = "int div(int n, int d) { return n / d }"
+ (_, fwds) = RunAnalyzerTest.run_analyzer(content, True)
+ self.assertEqual(1, fwds['exit_code'])
+ self.assertTrue(len(fwds['error_output']) > 0)
+
+
+class ReportFailureTest(unittest.TestCase):
+
+ def assertUnderFailures(self, path):
+ self.assertEqual('failures', os.path.basename(os.path.dirname(path)))
+
+ def test_report_failure_create_files(self):
+ with libear.TemporaryDirectory() as tmpdir:
+ # create input file
+ filename = os.path.join(tmpdir, 'test.c')
+ with open(filename, 'w') as handle:
+ handle.write('int main() { return 0')
+ uname_msg = ' '.join(os.uname()) + os.linesep
+ error_msg = 'this is my error output'
+ # execute test
+ opts = {
+ 'clang': 'clang',
+ 'directory': os.getcwd(),
+ 'flags': [],
+ 'file': filename,
+ 'output_dir': tmpdir,
+ 'language': 'c',
+ 'error_type': 'other_error',
+ 'error_output': error_msg,
+ 'exit_code': 13
+ }
+ sut.report_failure(opts)
+ # verify the result
+ result = dict()
+ pp_file = None
+ for root, _, files in os.walk(tmpdir):
+ keys = [os.path.join(root, name) for name in files]
+ for key in keys:
+ with open(key, 'r') as handle:
+ result[key] = handle.readlines()
+ if re.match(r'^(.*/)+clang(.*)\.i$', key):
+ pp_file = key
+
+ # prepocessor file generated
+ self.assertUnderFailures(pp_file)
+ # info file generated and content dumped
+ info_file = pp_file + '.info.txt'
+ self.assertTrue(info_file in result)
+ self.assertEqual('Other Error\n', result[info_file][1])
+ self.assertEqual(uname_msg, result[info_file][3])
+ # error file generated and content dumped
+ error_file = pp_file + '.stderr.txt'
+ self.assertTrue(error_file in result)
+ self.assertEqual([error_msg], result[error_file])
+
+
+class AnalyzerTest(unittest.TestCase):
+
+ def test_nodebug_macros_appended(self):
+ def test(flags):
+ spy = Spy()
+ opts = {'flags': flags, 'force_debug': True}
+ self.assertEqual(spy.success,
+ sut.filter_debug_flags(opts, spy.call))
+ return spy.arg['flags']
+
+ self.assertEqual(['-UNDEBUG'], test([]))
+ self.assertEqual(['-DNDEBUG', '-UNDEBUG'], test(['-DNDEBUG']))
+ self.assertEqual(['-DSomething', '-UNDEBUG'], test(['-DSomething']))
+
+ def test_set_language_fall_through(self):
+ def language(expected, input):
+ spy = Spy()
+ input.update({'compiler': 'c', 'file': 'test.c'})
+ self.assertEqual(spy.success, sut.language_check(input, spy.call))
+ self.assertEqual(expected, spy.arg['language'])
+
+ language('c', {'language': 'c', 'flags': []})
+ language('c++', {'language': 'c++', 'flags': []})
+
+ def test_set_language_stops_on_not_supported(self):
+ spy = Spy()
+ input = {
+ 'compiler': 'c',
+ 'flags': [],
+ 'file': 'test.java',
+ 'language': 'java'
+ }
+ self.assertIsNone(sut.language_check(input, spy.call))
+ self.assertIsNone(spy.arg)
+
+ def test_set_language_sets_flags(self):
+ def flags(expected, input):
+ spy = Spy()
+ input.update({'compiler': 'c', 'file': 'test.c'})
+ self.assertEqual(spy.success, sut.language_check(input, spy.call))
+ self.assertEqual(expected, spy.arg['flags'])
+
+ flags(['-x', 'c'], {'language': 'c', 'flags': []})
+ flags(['-x', 'c++'], {'language': 'c++', 'flags': []})
+
+ def test_set_language_from_filename(self):
+ def language(expected, input):
+ spy = Spy()
+ input.update({'language': None, 'flags': []})
+ self.assertEqual(spy.success, sut.language_check(input, spy.call))
+ self.assertEqual(expected, spy.arg['language'])
+
+ language('c', {'file': 'file.c', 'compiler': 'c'})
+ language('c++', {'file': 'file.c', 'compiler': 'c++'})
+ language('c++', {'file': 'file.cxx', 'compiler': 'c'})
+ language('c++', {'file': 'file.cxx', 'compiler': 'c++'})
+ language('c++', {'file': 'file.cpp', 'compiler': 'c++'})
+ language('c-cpp-output', {'file': 'file.i', 'compiler': 'c'})
+ language('c++-cpp-output', {'file': 'file.i', 'compiler': 'c++'})
+
+ def test_arch_loop_sets_flags(self):
+ def flags(archs):
+ spy = Spy()
+ input = {'flags': [], 'arch_list': archs}
+ sut.arch_check(input, spy.call)
+ return spy.arg['flags']
+
+ self.assertEqual([], flags([]))
+ self.assertEqual(['-arch', 'i386'], flags(['i386']))
+ self.assertEqual(['-arch', 'i386'], flags(['i386', 'ppc']))
+ self.assertEqual(['-arch', 'sparc'], flags(['i386', 'sparc']))
+
+ def test_arch_loop_stops_on_not_supported(self):
+ def stop(archs):
+ spy = Spy()
+ input = {'flags': [], 'arch_list': archs}
+ self.assertIsNone(sut.arch_check(input, spy.call))
+ self.assertIsNone(spy.arg)
+
+ stop(['ppc'])
+ stop(['ppc64'])
+
+
+@sut.require([])
+def method_without_expecteds(opts):
+ return 0
+
+
+@sut.require(['this', 'that'])
+def method_with_expecteds(opts):
+ return 0
+
+
+@sut.require([])
+def method_exception_from_inside(opts):
+ raise Exception('here is one')
+
+
+class RequireDecoratorTest(unittest.TestCase):
+
+ def test_method_without_expecteds(self):
+ self.assertEqual(method_without_expecteds(dict()), 0)
+ self.assertEqual(method_without_expecteds({}), 0)
+ self.assertEqual(method_without_expecteds({'this': 2}), 0)
+ self.assertEqual(method_without_expecteds({'that': 3}), 0)
+
+ def test_method_with_expecteds(self):
+ self.assertRaises(KeyError, method_with_expecteds, dict())
+ self.assertRaises(KeyError, method_with_expecteds, {})
+ self.assertRaises(KeyError, method_with_expecteds, {'this': 2})
+ self.assertRaises(KeyError, method_with_expecteds, {'that': 3})
+ self.assertEqual(method_with_expecteds({'this': 0, 'that': 3}), 0)
+
+ def test_method_exception_not_caught(self):
+ self.assertRaises(Exception, method_exception_from_inside, dict())
diff --git a/tools/scan-build-py/tests/unit/test_report.py b/tools/scan-build-py/tests/unit/test_report.py
index 3f249ce2aa..c943699156 100644
--- a/tools/scan-build-py/tests/unit/test_report.py
+++ b/tools/scan-build-py/tests/unit/test_report.py
@@ -75,7 +75,7 @@ class ParseFileTest(unittest.TestCase):
'file.i.stderr.txt')
def test_parse_real_crash(self):
- import libscanbuild.runner as sut2
+ import libscanbuild.analyze as sut2
import re
with libear.TemporaryDirectory() as tmpdir:
filename = os.path.join(tmpdir, 'test.c')
diff --git a/tools/scan-build-py/tests/unit/test_runner.py b/tools/scan-build-py/tests/unit/test_runner.py
deleted file mode 100644
index 4b51e65284..0000000000
--- a/tools/scan-build-py/tests/unit/test_runner.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# -*- coding: utf-8 -*-
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-
-import libear
-import libscanbuild.runner as sut
-import unittest
-import re
-import os
-import os.path
-
-
-class FilteringFlagsTest(unittest.TestCase):
-
- def test_language_captured(self):
- def test(flags):
- cmd = ['clang', '-c', 'source.c'] + flags
- opts = sut.classify_parameters(cmd)
- return opts['language']
-
- self.assertEqual(None, test([]))
- self.assertEqual('c', test(['-x', 'c']))
- self.assertEqual('cpp', test(['-x', 'cpp']))
-
- def test_arch(self):
- def test(flags):
- cmd = ['clang', '-c', 'source.c'] + flags
- opts = sut.classify_parameters(cmd)
- return opts['arch_list']
-
- self.assertEqual([], test([]))
- self.assertEqual(['mips'], test(['-arch', 'mips']))
- self.assertEqual(['mips', 'i386'],
- test(['-arch', 'mips', '-arch', 'i386']))
-
- def assertFlagsChanged(self, expected, flags):
- cmd = ['clang', '-c', 'source.c'] + flags
- opts = sut.classify_parameters(cmd)
- self.assertEqual(expected, opts['flags'])
-
- def assertFlagsUnchanged(self, flags):
- self.assertFlagsChanged(flags, flags)
-
- def assertFlagsFiltered(self, flags):
- self.assertFlagsChanged([], flags)
-
- def test_optimalizations_pass(self):
- self.assertFlagsUnchanged(['-O'])
- self.assertFlagsUnchanged(['-O1'])
- self.assertFlagsUnchanged(['-Os'])
- self.assertFlagsUnchanged(['-O2'])
- self.assertFlagsUnchanged(['-O3'])
-
- def test_include_pass(self):
- self.assertFlagsUnchanged([])
- self.assertFlagsUnchanged(['-include', '/usr/local/include'])
- self.assertFlagsUnchanged(['-I.'])
- self.assertFlagsUnchanged(['-I', '.'])
- self.assertFlagsUnchanged(['-I/usr/local/include'])
- self.assertFlagsUnchanged(['-I', '/usr/local/include'])
- self.assertFlagsUnchanged(['-I/opt', '-I', '/opt/otp/include'])
- self.assertFlagsUnchanged(['-isystem', '/path'])
- self.assertFlagsUnchanged(['-isystem=/path'])
-
- def test_define_pass(self):
- self.assertFlagsUnchanged(['-DNDEBUG'])
- self.assertFlagsUnchanged(['-UNDEBUG'])
- self.assertFlagsUnchanged(['-Dvar1=val1', '-Dvar2=val2'])
- self.assertFlagsUnchanged(['-Dvar="val ues"'])
-
- def test_output_filtered(self):
- self.assertFlagsFiltered(['-o', 'source.o'])
-
- def test_some_warning_filtered(self):
- self.assertFlagsFiltered(['-Wall'])
- self.assertFlagsFiltered(['-Wnoexcept'])
- self.assertFlagsFiltered(['-Wreorder', '-Wunused', '-Wundef'])
- self.assertFlagsUnchanged(['-Wno-reorder', '-Wno-unused'])
-
- def test_compile_only_flags_pass(self):
- self.assertFlagsUnchanged(['-std=C99'])
- self.assertFlagsUnchanged(['-nostdinc'])
- self.assertFlagsUnchanged(['-isystem', '/image/debian'])
- self.assertFlagsUnchanged(['-iprefix', '/usr/local'])
- self.assertFlagsUnchanged(['-iquote=me'])
- self.assertFlagsUnchanged(['-iquote', 'me'])
-
- def test_compile_and_link_flags_pass(self):
- self.assertFlagsUnchanged(['-fsinged-char'])
- self.assertFlagsUnchanged(['-fPIC'])
- self.assertFlagsUnchanged(['-stdlib=libc++'])
- self.assertFlagsUnchanged(['--sysroot', '/'])
- self.assertFlagsUnchanged(['-isysroot', '/'])
-
- def test_some_flags_filtered(self):
- self.assertFlagsFiltered(['-g'])
- self.assertFlagsFiltered(['-fsyntax-only'])
- self.assertFlagsFiltered(['-save-temps'])
- self.assertFlagsFiltered(['-init', 'my_init'])
- self.assertFlagsFiltered(['-sectorder', 'a', 'b', 'c'])
-
-
-class Spy(object):
- def __init__(self):
- self.arg = None
- self.success = 0
-
- def call(self, params):
- self.arg = params
- return self.success
-
-
-class RunAnalyzerTest(unittest.TestCase):
-
- @staticmethod
- def run_analyzer(content, failures_report):
- with libear.TemporaryDirectory() as tmpdir:
- filename = os.path.join(tmpdir, 'test.cpp')
- with open(filename, 'w') as handle:
- handle.write(content)
-
- opts = {
- 'clang': 'clang',
- 'directory': os.getcwd(),
- 'flags': [],
- 'direct_args': [],
- 'file': filename,
- 'output_dir': tmpdir,
- 'output_format': 'plist',
- 'output_failures': failures_report
- }
- spy = Spy()
- result = sut.run_analyzer(opts, spy.call)
- return (result, spy.arg)
-
- def test_run_analyzer(self):
- content = "int div(int n, int d) { return n / d; }"
- (result, fwds) = RunAnalyzerTest.run_analyzer(content, False)
- self.assertEqual(None, fwds)
- self.assertEqual(0, result['exit_code'])
-
- def test_run_analyzer_crash(self):
- content = "int div(int n, int d) { return n / d }"
- (result, fwds) = RunAnalyzerTest.run_analyzer(content, False)
- self.assertEqual(None, fwds)
- self.assertEqual(1, result['exit_code'])
-
- def test_run_analyzer_crash_and_forwarded(self):
- content = "int div(int n, int d) { return n / d }"
- (_, fwds) = RunAnalyzerTest.run_analyzer(content, True)
- self.assertEqual(1, fwds['exit_code'])
- self.assertTrue(len(fwds['error_output']) > 0)
-
-
-class ReportFailureTest(unittest.TestCase):
-
- def assertUnderFailures(self, path):
- self.assertEqual('failures', os.path.basename(os.path.dirname(path)))
-
- def test_report_failure_create_files(self):
- with libear.TemporaryDirectory() as tmpdir:
- # create input file
- filename = os.path.join(tmpdir, 'test.c')
- with open(filename, 'w') as handle:
- handle.write('int main() { return 0')
- uname_msg = ' '.join(os.uname()) + os.linesep
- error_msg = 'this is my error output'
- # execute test
- opts = {
- 'clang': 'clang',
- 'directory': os.getcwd(),
- 'flags': [],
- 'file': filename,
- 'output_dir': tmpdir,
- 'language': 'c',
- 'error_type': 'other_error',
- 'error_output': error_msg,
- 'exit_code': 13
- }
- sut.report_failure(opts)
- # verify the result
- result = dict()
- pp_file = None
- for root, _, files in os.walk(tmpdir):
- keys = [os.path.join(root, name) for name in files]
- for key in keys:
- with open(key, 'r') as handle:
- result[key] = handle.readlines()
- if re.match(r'^(.*/)+clang(.*)\.i$', key):
- pp_file = key
-
- # prepocessor file generated
- self.assertUnderFailures(pp_file)
- # info file generated and content dumped
- info_file = pp_file + '.info.txt'
- self.assertTrue(info_file in result)
- self.assertEqual('Other Error\n', result[info_file][1])
- self.assertEqual(uname_msg, result[info_file][3])
- # error file generated and content dumped
- error_file = pp_file + '.stderr.txt'
- self.assertTrue(error_file in result)
- self.assertEqual([error_msg], result[error_file])
-
-
-class AnalyzerTest(unittest.TestCase):
-
- def test_nodebug_macros_appended(self):
- def test(flags):
- spy = Spy()
- opts = {'flags': flags, 'force_debug': True}
- self.assertEqual(spy.success,
- sut.filter_debug_flags(opts, spy.call))
- return spy.arg['flags']
-
- self.assertEqual(['-UNDEBUG'], test([]))
- self.assertEqual(['-DNDEBUG', '-UNDEBUG'], test(['-DNDEBUG']))
- self.assertEqual(['-DSomething', '-UNDEBUG'], test(['-DSomething']))
-
- def test_set_language_fall_through(self):
- def language(expected, input):
- spy = Spy()
- input.update({'compiler': 'c', 'file': 'test.c'})
- self.assertEqual(spy.success, sut.language_check(input, spy.call))
- self.assertEqual(expected, spy.arg['language'])
-
- language('c', {'language': 'c', 'flags': []})
- language('c++', {'language': 'c++', 'flags': []})
-
- def test_set_language_stops_on_not_supported(self):
- spy = Spy()
- input = {
- 'compiler': 'c',
- 'flags': [],
- 'file': 'test.java',
- 'language': 'java'
- }
- self.assertIsNone(sut.language_check(input, spy.call))
- self.assertIsNone(spy.arg)
-
- def test_set_language_sets_flags(self):
- def flags(expected, input):
- spy = Spy()
- input.update({'compiler': 'c', 'file': 'test.c'})
- self.assertEqual(spy.success, sut.language_check(input, spy.call))
- self.assertEqual(expected, spy.arg['flags'])
-
- flags(['-x', 'c'], {'language': 'c', 'flags': []})
- flags(['-x', 'c++'], {'language': 'c++', 'flags': []})
-
- def test_set_language_from_filename(self):
- def language(expected, input):
- spy = Spy()
- input.update({'language': None, 'flags': []})
- self.assertEqual(spy.success, sut.language_check(input, spy.call))
- self.assertEqual(expected, spy.arg['language'])
-
- language('c', {'file': 'file.c', 'compiler': 'c'})
- language('c++', {'file': 'file.c', 'compiler': 'c++'})
- language('c++', {'file': 'file.cxx', 'compiler': 'c'})
- language('c++', {'file': 'file.cxx', 'compiler': 'c++'})
- language('c++', {'file': 'file.cpp', 'compiler': 'c++'})
- language('c-cpp-output', {'file': 'file.i', 'compiler': 'c'})
- language('c++-cpp-output', {'file': 'file.i', 'compiler': 'c++'})
-
- def test_arch_loop_sets_flags(self):
- def flags(archs):
- spy = Spy()
- input = {'flags': [], 'arch_list': archs}
- sut.arch_check(input, spy.call)
- return spy.arg['flags']
-
- self.assertEqual([], flags([]))
- self.assertEqual(['-arch', 'i386'], flags(['i386']))
- self.assertEqual(['-arch', 'i386'], flags(['i386', 'ppc']))
- self.assertEqual(['-arch', 'sparc'], flags(['i386', 'sparc']))
-
- def test_arch_loop_stops_on_not_supported(self):
- def stop(archs):
- spy = Spy()
- input = {'flags': [], 'arch_list': archs}
- self.assertIsNone(sut.arch_check(input, spy.call))
- self.assertIsNone(spy.arg)
-
- stop(['ppc'])
- stop(['ppc64'])
-
-
-@sut.require([])
-def method_without_expecteds(opts):
- return 0
-
-
-@sut.require(['this', 'that'])
-def method_with_expecteds(opts):
- return 0
-
-
-@sut.require([])
-def method_exception_from_inside(opts):
- raise Exception('here is one')
-
-
-class RequireDecoratorTest(unittest.TestCase):
-
- def test_method_without_expecteds(self):
- self.assertEqual(method_without_expecteds(dict()), 0)
- self.assertEqual(method_without_expecteds({}), 0)
- self.assertEqual(method_without_expecteds({'this': 2}), 0)
- self.assertEqual(method_without_expecteds({'that': 3}), 0)
-
- def test_method_with_expecteds(self):
- self.assertRaises(KeyError, method_with_expecteds, dict())
- self.assertRaises(KeyError, method_with_expecteds, {})
- self.assertRaises(KeyError, method_with_expecteds, {'this': 2})
- self.assertRaises(KeyError, method_with_expecteds, {'that': 3})
- self.assertEqual(method_with_expecteds({'this': 0, 'that': 3}), 0)
-
- def test_method_exception_not_caught(self):
- self.assertRaises(Exception, method_exception_from_inside, dict())
diff --git a/tools/scan-build/CMakeLists.txt b/tools/scan-build/CMakeLists.txt
index 78c243d8e0..380379300b 100644
--- a/tools/scan-build/CMakeLists.txt
+++ b/tools/scan-build/CMakeLists.txt
@@ -4,8 +4,11 @@ include(GNUInstallDirs)
if (WIN32 AND NOT CYGWIN)
set(BinFiles
+ scan-build
scan-build.bat)
set(LibexecFiles
+ ccc-analyzer
+ c++-analyzer
ccc-analyzer.bat
c++-analyzer.bat)
else()
diff --git a/tools/scan-build/libexec/ccc-analyzer b/tools/scan-build/libexec/ccc-analyzer
index bfda1d326f..b0ec7e7e74 100755
--- a/tools/scan-build/libexec/ccc-analyzer
+++ b/tools/scan-build/libexec/ccc-analyzer
@@ -385,7 +385,8 @@ my %CompilerLinkerOptionMap = (
'-target' => 1,
'-v' => 0,
'-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '='
- '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '='
+ '-miphoneos-version-min' => 0, # This is really a 1 argument, but always has '='
+ '--target' => 0
);
my %IgnoredOptionMap = (