diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
| commit | 2b6b257f4e5503a7a2675bdb8735693db769f75c (patch) | |
| tree | e85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /tools | |
| parent | b4348ed0b7e90c0831b925fbee00b5f179a99796 (diff) | |
Notes
Diffstat (limited to 'tools')
70 files changed, 2538 insertions, 2594 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 510bc44f40cb..d734493c619e 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -7,7 +7,6 @@ add_clang_subdirectory(clang-format-vs) add_clang_subdirectory(clang-fuzzer) add_clang_subdirectory(c-index-test) -add_clang_subdirectory(libclang) if(CLANG_ENABLE_ARCMT) add_clang_subdirectory(arcmt-test) @@ -26,3 +25,6 @@ endif() # to keep the primary Clang repository small and focused. # It also may be included by LLVM_EXTERNAL_CLANG_TOOLS_EXTRA_SOURCE_DIR. add_llvm_external_project(clang-tools-extra extra) + +# libclang may require clang-tidy in clang-tools-extra. +add_clang_subdirectory(libclang) diff --git a/tools/Makefile b/tools/Makefile deleted file mode 100644 index 5c362bfc9aad..000000000000 --- a/tools/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -##===- tools/Makefile --------------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := .. - -include $(CLANG_LEVEL)/../../Makefile.config - -DIRS := -PARALLEL_DIRS := clang-format driver diagtool - -ifeq ($(ENABLE_CLANG_STATIC_ANALYZER), 1) - PARALLEL_DIRS += clang-check scan-build scan-view -endif - -ifeq ($(ENABLE_CLANG_ARCMT), 1) - DIRS += libclang c-index-test c-arcmt-test - PARALLEL_DIRS += arcmt-test -endif - -# Recurse into the extra repository of tools if present. -OPTIONAL_PARALLEL_DIRS := extra - -ifeq ($(BUILD_CLANG_ONLY),YES) - DIRS := libclang c-index-test - PARALLEL_DIRS := driver - OPTIONAL_PARALLEL_DIRS := -endif - -include $(CLANG_LEVEL)/Makefile diff --git a/tools/arcmt-test/Makefile b/tools/arcmt-test/Makefile deleted file mode 100644 index d9d44bb05bd2..000000000000 --- a/tools/arcmt-test/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -##===- tools/arcmt-test/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -CLANG_LEVEL := ../.. - -TOOLNAME = arcmt-test - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# Don't install this. It is used for tests. -NO_INSTALL = 1 - -include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option -USEDLIBS = clangARCMigrate.a clangRewrite.a \ - clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ - clangSema.a clangEdit.a clangAnalysis.a clangAST.a clangLex.a \ - clangBasic.a - -include $(CLANG_LEVEL)/Makefile diff --git a/tools/arcmt-test/arcmt-test.cpp b/tools/arcmt-test/arcmt-test.cpp index 7c8e46aee067..900358ec1fe9 100644 --- a/tools/arcmt-test/arcmt-test.cpp +++ b/tools/arcmt-test/arcmt-test.cpp @@ -341,7 +341,7 @@ static void printSourceRange(CharSourceRange range, ASTContext &Ctx, int main(int argc, const char **argv) { void *MainAddr = (void*) (intptr_t) GetExecutablePath; - llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); std::string resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr); diff --git a/tools/c-arcmt-test/Makefile b/tools/c-arcmt-test/Makefile deleted file mode 100644 index 03e0c9e58c4d..000000000000 --- a/tools/c-arcmt-test/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -##===- tools/c-arcmt-test/Makefile -------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -CLANG_LEVEL := ../.. - -TOOLNAME = c-arcmt-test - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# Don't install this. It is used for tests. -NO_INSTALL = 1 - -# Include this here so we can get the configuration of the targets that have -# been configured for construction. We have to do this early so we can set up -# LINK_COMPONENTS before including Makefile.rules -include $(CLANG_LEVEL)/../../Makefile.config - -LINK_COMPONENTS := $(TARGETS_TO_BUILD) \ - AsmParser \ - BitReader \ - BitWriter \ - IPO \ - MC \ - ObjCARCOpts \ - Option \ - Support - -# Note that 'USEDLIBS' must include all of the core clang libraries -# when -static is given to linker on cygming. -USEDLIBS = clang.a \ - clangCodeGen.a \ - clangARCMigrate.a \ - clangIndex.a \ - clangFormat.a \ - clangTooling.a \ - clangToolingCore.a \ - clangRewriteFrontend.a \ - clangRewrite.a \ - clangFrontend.a clangDriver.a \ - clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ - clangSerialization.a clangParse.a clangSema.a \ - clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a - -include $(CLANG_LEVEL)/Makefile diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index c78a42ffe8eb..e0df5031d3e7 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -1,5 +1,10 @@ +set(LLVM_LINK_COMPONENTS + support +) + add_clang_executable(c-index-test c-index-test.c + core_main.cpp ) if(NOT MSVC) @@ -12,10 +17,15 @@ endif() if (LLVM_BUILD_STATIC) target_link_libraries(c-index-test libclang_static + clangIndex ) else() target_link_libraries(c-index-test libclang + clangAST + clangBasic + clangFrontend + clangIndex ) endif() @@ -32,6 +42,8 @@ endif() if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) if(INTERNAL_INSTALL_PREFIX) set(INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/bin") + set_property(TARGET c-index-test APPEND PROPERTY INSTALL_RPATH + "@executable_path/../../lib") else() set(INSTALL_DESTINATION bin) endif() diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile deleted file mode 100644 index b757b477958e..000000000000 --- a/tools/c-index-test/Makefile +++ /dev/null @@ -1,58 +0,0 @@ -##===- tools/index-test/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -CLANG_LEVEL := ../.. - -TOOLNAME = c-index-test - -# If a separate install prefix was specified for internal tools, use it -# when installing c-index-test. -INTERNAL_TOOL = 1 - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -# Include this here so we can get the configuration of the targets that have -# been configured for construction. We have to do this early so we can set up -# LINK_COMPONENTS before including Makefile.rules -include $(CLANG_LEVEL)/../../Makefile.config - -LINK_COMPONENTS := $(TARGETS_TO_BUILD) \ - AsmParser \ - BitReader \ - BitWriter \ - IPO \ - MC \ - ObjCARCOpts \ - Option \ - Support - -# Note that 'USEDLIBS' must include all of the core clang libraries -# when -static is given to linker on cygming. -USEDLIBS = clang.a \ - clangCodeGen.a \ - clangIndex.a clangFormat.a clangRewrite.a \ - clangFrontend.a clangDriver.a \ - clangTooling.a \ - clangToolingCore.a \ - clangSerialization.a clangParse.a clangSema.a \ - clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a - -include $(CLANG_LEVEL)/Makefile - -LIBS += $(LIBXML2_LIBS) - -# Headers in $(LIBXML2_INC) should not be checked with clang's -Wdocumentation. -# Use -isystem instead of -I then. -# FIXME: Could autoconf detect clang or availability of -isystem? -ifneq ($(findstring -Wdocumentation,$(OPTIMIZE_OPTION)),) -CPPFLAGS += $(subst -I,-isystem ,$(LIBXML2_INC)) -else -CPPFLAGS += $(LIBXML2_INC) -endif diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 2a6002537ec5..007af9e252a8 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -23,6 +23,8 @@ # include <unistd.h> #endif +extern int indextest_core_main(int argc, const char **argv); + /******************************************************************************/ /* Utility functions. */ /******************************************************************************/ @@ -78,6 +80,8 @@ static unsigned getDefaultParsingOptions() { options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; if (getenv("CINDEXTEST_CREATE_PREAMBLE_ON_FIRST_PARSE")) options |= CXTranslationUnit_CreatePreambleOnFirstParse; + if (getenv("CINDEXTEST_KEEP_GOING")) + options |= CXTranslationUnit_KeepGoing; return options; } @@ -768,9 +772,20 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { clang_disposeString(DeprecatedMessage); clang_disposeString(UnavailableMessage); - + + if (clang_CXXConstructor_isDefaultConstructor(Cursor)) + printf(" (default constructor)"); + + if (clang_CXXConstructor_isMoveConstructor(Cursor)) + printf(" (move constructor)"); + if (clang_CXXConstructor_isCopyConstructor(Cursor)) + printf(" (copy constructor)"); + if (clang_CXXConstructor_isConvertingConstructor(Cursor)) + printf(" (converting constructor)"); if (clang_CXXField_isMutable(Cursor)) printf(" (mutable)"); + if (clang_CXXMethod_isDefaulted(Cursor)) + printf(" (defaulted)"); if (clang_CXXMethod_isStatic(Cursor)) printf(" (static)"); if (clang_CXXMethod_isVirtual(Cursor)) @@ -922,6 +937,7 @@ static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { PRINT_PROP_ATTR(weak); PRINT_PROP_ATTR(strong); PRINT_PROP_ATTR(unsafe_unretained); + PRINT_PROP_ATTR(class); printf("]"); } } @@ -1417,10 +1433,10 @@ static enum CXChildVisitResult PrintTypeSize(CXCursor cursor, CXCursor p, CXString FieldSpelling = clang_getCursorSpelling(cursor); const char *FieldName = clang_getCString(FieldSpelling); /* recurse to get the first parent record that is not anonymous. */ - CXCursor Parent, Record; unsigned RecordIsAnonymous = 0; if (clang_getCursorKind(cursor) == CXCursor_FieldDecl) { - Record = Parent = p; + CXCursor Record; + CXCursor Parent = p; do { Record = Parent; Parent = clang_getCursorSemanticParent(Record); @@ -1993,6 +2009,7 @@ static void print_completion_result(CXCompletionResult *completion_result, enum CXCursorKind ParentKind; CXString ParentName; CXString BriefComment; + CXString Annotation; const char *BriefCommentCString; fprintf(file, "%s:", clang_getCString(ks)); @@ -2026,9 +2043,10 @@ static void print_completion_result(CXCompletionResult *completion_result, for (i = 0; i < annotationCount; ++i) { if (i != 0) fprintf(file, ", "); - fprintf(file, "\"%s\"", - clang_getCString(clang_getCompletionAnnotation( - completion_result->CompletionString, i))); + Annotation = + clang_getCompletionAnnotation(completion_result->CompletionString, i); + fprintf(file, "\"%s\"", clang_getCString(Annotation)); + clang_disposeString(Annotation); } fprintf(file, ")"); } @@ -2130,25 +2148,6 @@ void print_completion_contexts(unsigned long long contexts, FILE *file) { } } -int my_stricmp(const char *s1, const char *s2) { - while (*s1 && *s2) { - int c1 = tolower((unsigned char)*s1), c2 = tolower((unsigned char)*s2); - if (c1 < c2) - return -1; - else if (c1 > c2) - return 1; - - ++s1; - ++s2; - } - - if (*s1) - return 1; - else if (*s2) - return -1; - return 0; -} - int perform_code_completion(int argc, const char **argv, int timing_only) { const char *input = argv[1]; char *filename = 0; @@ -2287,7 +2286,11 @@ typedef struct { unsigned column; } CursorSourceLocation; -static int inspect_cursor_at(int argc, const char **argv) { +typedef void (*cursor_handler_t)(CXCursor cursor); + +static int inspect_cursor_at(int argc, const char **argv, + const char *locations_flag, + cursor_handler_t handler) { CXIndex CIdx; int errorCode; struct CXUnsavedFile *unsaved_files = 0; @@ -2301,7 +2304,7 @@ static int inspect_cursor_at(int argc, const char **argv) { unsigned I; /* Count the number of locations. */ - while (strstr(argv[NumLocations+1], "-cursor-at=") == argv[NumLocations+1]) + while (strstr(argv[NumLocations+1], locations_flag) == argv[NumLocations+1]) ++NumLocations; /* Parse the locations. */ @@ -2309,7 +2312,7 @@ static int inspect_cursor_at(int argc, const char **argv) { Locations = (CursorSourceLocation *)malloc( NumLocations * sizeof(CursorSourceLocation)); for (Loc = 0; Loc < NumLocations; ++Loc) { - const char *input = argv[Loc + 1] + strlen("-cursor-at="); + const char *input = argv[Loc + 1] + strlen(locations_flag); if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, &Locations[Loc].line, &Locations[Loc].column, 0, 0))) @@ -2368,72 +2371,7 @@ static int inspect_cursor_at(int argc, const char **argv) { return -1; if (I + 1 == Repeats) { - CXCompletionString completionString = clang_getCursorCompletionString( - Cursor); - CXSourceLocation CursorLoc = clang_getCursorLocation(Cursor); - CXString Spelling; - const char *cspell; - unsigned line, column; - clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0); - printf("%d:%d ", line, column); - PrintCursor(Cursor, NULL); - PrintCursorExtent(Cursor); - Spelling = clang_getCursorSpelling(Cursor); - cspell = clang_getCString(Spelling); - if (cspell && strlen(cspell) != 0) { - unsigned pieceIndex; - printf(" Spelling=%s (", cspell); - for (pieceIndex = 0; ; ++pieceIndex) { - CXSourceRange range = - clang_Cursor_getSpellingNameRange(Cursor, pieceIndex, 0); - if (clang_Range_isNull(range)) - break; - PrintRange(range, 0); - } - printf(")"); - } - clang_disposeString(Spelling); - if (clang_Cursor_getObjCSelectorIndex(Cursor) != -1) - printf(" Selector index=%d", - clang_Cursor_getObjCSelectorIndex(Cursor)); - if (clang_Cursor_isDynamicCall(Cursor)) - printf(" Dynamic-call"); - if (Cursor.kind == CXCursor_ObjCMessageExpr) { - CXType T = clang_Cursor_getReceiverType(Cursor); - CXString S = clang_getTypeKindSpelling(T.kind); - printf(" Receiver-type=%s", clang_getCString(S)); - clang_disposeString(S); - } - - { - CXModule mod = clang_Cursor_getModule(Cursor); - CXFile astFile; - CXString name, astFilename; - unsigned i, numHeaders; - if (mod) { - astFile = clang_Module_getASTFile(mod); - astFilename = clang_getFileName(astFile); - name = clang_Module_getFullName(mod); - numHeaders = clang_Module_getNumTopLevelHeaders(TU, mod); - printf(" ModuleName=%s (%s) system=%d Headers(%d):", - clang_getCString(name), clang_getCString(astFilename), - clang_Module_isSystem(mod), numHeaders); - clang_disposeString(name); - clang_disposeString(astFilename); - for (i = 0; i < numHeaders; ++i) { - CXFile file = clang_Module_getTopLevelHeader(TU, mod, i); - CXString filename = clang_getFileName(file); - printf("\n%s", clang_getCString(filename)); - clang_disposeString(filename); - } - } - } - - if (completionString != NULL) { - printf("\nCompletion string: "); - print_completion_string(completionString, stdout); - } - printf("\n"); + handler(Cursor); free(Locations[Loc].filename); } } @@ -2447,6 +2385,184 @@ static int inspect_cursor_at(int argc, const char **argv) { return 0; } +static void inspect_print_cursor(CXCursor Cursor) { + CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); + CXCompletionString completionString = clang_getCursorCompletionString( + Cursor); + CXSourceLocation CursorLoc = clang_getCursorLocation(Cursor); + CXString Spelling; + const char *cspell; + unsigned line, column; + clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0); + printf("%d:%d ", line, column); + PrintCursor(Cursor, NULL); + PrintCursorExtent(Cursor); + Spelling = clang_getCursorSpelling(Cursor); + cspell = clang_getCString(Spelling); + if (cspell && strlen(cspell) != 0) { + unsigned pieceIndex; + printf(" Spelling=%s (", cspell); + for (pieceIndex = 0; ; ++pieceIndex) { + CXSourceRange range = + clang_Cursor_getSpellingNameRange(Cursor, pieceIndex, 0); + if (clang_Range_isNull(range)) + break; + PrintRange(range, 0); + } + printf(")"); + } + clang_disposeString(Spelling); + if (clang_Cursor_getObjCSelectorIndex(Cursor) != -1) + printf(" Selector index=%d", + clang_Cursor_getObjCSelectorIndex(Cursor)); + if (clang_Cursor_isDynamicCall(Cursor)) + printf(" Dynamic-call"); + if (Cursor.kind == CXCursor_ObjCMessageExpr) { + CXType T = clang_Cursor_getReceiverType(Cursor); + CXString S = clang_getTypeKindSpelling(T.kind); + printf(" Receiver-type=%s", clang_getCString(S)); + clang_disposeString(S); + } + + { + CXModule mod = clang_Cursor_getModule(Cursor); + CXFile astFile; + CXString name, astFilename; + unsigned i, numHeaders; + if (mod) { + astFile = clang_Module_getASTFile(mod); + astFilename = clang_getFileName(astFile); + name = clang_Module_getFullName(mod); + numHeaders = clang_Module_getNumTopLevelHeaders(TU, mod); + printf(" ModuleName=%s (%s) system=%d Headers(%d):", + clang_getCString(name), clang_getCString(astFilename), + clang_Module_isSystem(mod), numHeaders); + clang_disposeString(name); + clang_disposeString(astFilename); + for (i = 0; i < numHeaders; ++i) { + CXFile file = clang_Module_getTopLevelHeader(TU, mod, i); + CXString filename = clang_getFileName(file); + printf("\n%s", clang_getCString(filename)); + clang_disposeString(filename); + } + } + } + + if (completionString != NULL) { + printf("\nCompletion string: "); + print_completion_string(completionString, stdout); + } + printf("\n"); +} + +static void display_evaluate_results(CXEvalResult result) { + switch (clang_EvalResult_getKind(result)) { + case CXEval_Int: + { + int val = clang_EvalResult_getAsInt(result); + printf("Kind: Int , Value: %d", val); + break; + } + case CXEval_Float: + { + double val = clang_EvalResult_getAsDouble(result); + printf("Kind: Float , Value: %f", val); + break; + } + case CXEval_ObjCStrLiteral: + { + const char* str = clang_EvalResult_getAsStr(result); + printf("Kind: ObjCString , Value: %s", str); + break; + } + case CXEval_StrLiteral: + { + const char* str = clang_EvalResult_getAsStr(result); + printf("Kind: CString , Value: %s", str); + break; + } + case CXEval_CFStr: + { + const char* str = clang_EvalResult_getAsStr(result); + printf("Kind: CFString , Value: %s", str); + break; + } + default: + printf("Unexposed"); + break; + } +} + +static void inspect_evaluate_cursor(CXCursor Cursor) { + CXSourceLocation CursorLoc = clang_getCursorLocation(Cursor); + CXString Spelling; + const char *cspell; + unsigned line, column; + CXEvalResult ER; + + clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0); + printf("%d:%d ", line, column); + PrintCursor(Cursor, NULL); + PrintCursorExtent(Cursor); + Spelling = clang_getCursorSpelling(Cursor); + cspell = clang_getCString(Spelling); + if (cspell && strlen(cspell) != 0) { + unsigned pieceIndex; + printf(" Spelling=%s (", cspell); + for (pieceIndex = 0; ; ++pieceIndex) { + CXSourceRange range = + clang_Cursor_getSpellingNameRange(Cursor, pieceIndex, 0); + if (clang_Range_isNull(range)) + break; + PrintRange(range, 0); + } + printf(")"); + } + clang_disposeString(Spelling); + + ER = clang_Cursor_Evaluate(Cursor); + if (!ER) { + printf("Not Evaluatable"); + } else { + display_evaluate_results(ER); + clang_EvalResult_dispose(ER); + } + printf("\n"); +} + +static void inspect_macroinfo_cursor(CXCursor Cursor) { + CXSourceLocation CursorLoc = clang_getCursorLocation(Cursor); + CXString Spelling; + const char *cspell; + unsigned line, column; + clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0); + printf("%d:%d ", line, column); + PrintCursor(Cursor, NULL); + PrintCursorExtent(Cursor); + Spelling = clang_getCursorSpelling(Cursor); + cspell = clang_getCString(Spelling); + if (cspell && strlen(cspell) != 0) { + unsigned pieceIndex; + printf(" Spelling=%s (", cspell); + for (pieceIndex = 0; ; ++pieceIndex) { + CXSourceRange range = + clang_Cursor_getSpellingNameRange(Cursor, pieceIndex, 0); + if (clang_Range_isNull(range)) + break; + PrintRange(range, 0); + } + printf(")"); + } + clang_disposeString(Spelling); + + if (clang_Cursor_isMacroBuiltin(Cursor)) { + printf("[builtin macro]"); + } else if (clang_Cursor_isMacroFunctionLike(Cursor)) { + printf("[function macro]"); + } + printf("\n"); +} + static enum CXVisitorResult findFileRefsVisit(void *context, CXCursor cursor, CXSourceRange range) { if (clang_Range_isNull(range)) @@ -4121,6 +4237,8 @@ static void print_usage(void) { "usage: c-index-test -code-completion-at=<site> <compiler arguments>\n" " c-index-test -code-completion-timing=<site> <compiler arguments>\n" " c-index-test -cursor-at=<site> <compiler arguments>\n" + " c-index-test -evaluate-cursor-at=<site> <compiler arguments>\n" + " c-index-test -get-macro-info-cursor-at=<site> <compiler arguments>\n" " c-index-test -file-refs-at=<site> <compiler arguments>\n" " c-index-test -file-includes-in=<filename> <compiler arguments>\n"); fprintf(stderr, @@ -4186,7 +4304,13 @@ int cindextest_main(int argc, const char **argv) { if (argc > 2 && strstr(argv[1], "-code-completion-timing=") == argv[1]) return perform_code_completion(argc, argv, 1); if (argc > 2 && strstr(argv[1], "-cursor-at=") == argv[1]) - return inspect_cursor_at(argc, argv); + return inspect_cursor_at(argc, argv, "-cursor-at=", inspect_print_cursor); + if (argc > 2 && strstr(argv[1], "-evaluate-cursor-at=") == argv[1]) + return inspect_cursor_at(argc, argv, "-evaluate-cursor-at=", + inspect_evaluate_cursor); + if (argc > 2 && strstr(argv[1], "-get-macro-info-cursor-at=") == argv[1]) + return inspect_cursor_at(argc, argv, "-get-macro-info-cursor-at=", + inspect_macroinfo_cursor); if (argc > 2 && strstr(argv[1], "-file-refs-at=") == argv[1]) return find_file_refs_at(argc, argv); if (argc > 2 && strstr(argv[1], "-file-includes-in=") == argv[1]) @@ -4285,13 +4409,15 @@ int cindextest_main(int argc, const char **argv) { * size). */ typedef struct thread_info { + int (*main_func)(int argc, const char **argv); int argc; const char **argv; int result; } thread_info; void thread_runner(void *client_data_v) { thread_info *client_data = client_data_v; - client_data->result = cindextest_main(client_data->argc, client_data->argv); + client_data->result = client_data->main_func(client_data->argc, + client_data->argv); } static void flush_atexit(void) { @@ -4310,11 +4436,16 @@ int main(int argc, const char **argv) { LIBXML_TEST_VERSION #endif - if (getenv("CINDEXTEST_NOTHREADS")) - return cindextest_main(argc, argv); - + client_data.main_func = cindextest_main; client_data.argc = argc; client_data.argv = argv; + + if (argc > 1 && strcmp(argv[1], "core") == 0) + client_data.main_func = indextest_core_main; + + if (getenv("CINDEXTEST_NOTHREADS")) + return client_data.main_func(client_data.argc, client_data.argv); + clang_executeOnThread(thread_runner, &client_data, 0); return client_data.result; } diff --git a/tools/c-index-test/core_main.cpp b/tools/c-index-test/core_main.cpp new file mode 100644 index 000000000000..e64dae726fe3 --- /dev/null +++ b/tools/c-index-test/core_main.cpp @@ -0,0 +1,230 @@ +//===-- core_main.cpp - Core Index Tool testbed ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Index/IndexingAction.h" +#include "clang/Index/IndexDataConsumer.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Index/CodegenNameGenerator.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/PrettyStackTrace.h" + +using namespace clang; +using namespace clang::index; +using namespace llvm; + +extern "C" int indextest_core_main(int argc, const char **argv); + +namespace { + +enum class ActionType { + None, + PrintSourceSymbols, +}; + +namespace options { + +static cl::OptionCategory IndexTestCoreCategory("index-test-core options"); + +static cl::opt<ActionType> +Action(cl::desc("Action:"), cl::init(ActionType::None), + cl::values( + clEnumValN(ActionType::PrintSourceSymbols, + "print-source-symbols", "Print symbols from source"), + clEnumValEnd), + cl::cat(IndexTestCoreCategory)); + +static cl::extrahelp MoreHelp( + "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " + "invocation\n" +); + +} +} // anonymous namespace + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS); + +namespace { + +class PrintIndexDataConsumer : public IndexDataConsumer { + raw_ostream &OS; + std::unique_ptr<CodegenNameGenerator> CGNameGen; + +public: + PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { + } + + void initialize(ASTContext &Ctx) override { + CGNameGen.reset(new CodegenNameGenerator(Ctx)); + } + + bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override { + ASTContext &Ctx = D->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + unsigned Line = SM.getLineNumber(FID, Offset); + unsigned Col = SM.getColumnNumber(FID, Offset); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(D), OS); + OS << " | "; + + printSymbolNameAndUSR(D, Ctx, OS); + OS << " | "; + + if (CGNameGen->writeName(D, OS)) + OS << "<no-cgname>"; + OS << " | "; + + printSymbolRoles(Roles, OS); + OS << " | "; + + OS << "rel: " << Relations.size() << '\n'; + + for (auto &SymRel : Relations) { + OS << '\t'; + printSymbolRoles(SymRel.Roles, OS); + OS << " | "; + printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS); + OS << '\n'; + } + + return true; + } + + bool handleModuleOccurence(const ImportDecl *ImportD, SymbolRoleSet Roles, + FileID FID, unsigned Offset) override { + ASTContext &Ctx = ImportD->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + unsigned Line = SM.getLineNumber(FID, Offset); + unsigned Col = SM.getColumnNumber(FID, Offset); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(ImportD), OS); + OS << " | "; + + OS << ImportD->getImportedModule()->getFullModuleName() << " | "; + + printSymbolRoles(Roles, OS); + OS << " |\n"; + + return true; + } +}; + +} // anonymous namespace + +//===----------------------------------------------------------------------===// +// Print Source Symbols +//===----------------------------------------------------------------------===// + +static bool printSourceSymbols(ArrayRef<const char *> Args) { + SmallVector<const char *, 4> ArgsWithProgName; + ArgsWithProgName.push_back("clang"); + ArgsWithProgName.append(Args.begin(), Args.end()); + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + IntrusiveRefCntPtr<CompilerInvocation> + CInvok(createInvocationFromCommandLine(ArgsWithProgName, Diags)); + if (!CInvok) + return true; + + auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(outs()); + IndexingOptions IndexOpts; + std::unique_ptr<FrontendAction> IndexAction; + IndexAction = createIndexingAction(DataConsumer, IndexOpts, + /*WrappedAction=*/nullptr); + + auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); + std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( + CInvok.get(), PCHContainerOps, Diags, IndexAction.get())); + + if (!Unit) + return true; + + return false; +} + +//===----------------------------------------------------------------------===// +// Helper Utils +//===----------------------------------------------------------------------===// + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { + OS << getSymbolKindString(SymInfo.Kind); + if (SymInfo.SubKinds) { + OS << '('; + printSymbolSubKinds(SymInfo.SubKinds, OS); + OS << ')'; + } + OS << '/' << getSymbolLanguageString(SymInfo.Lang); +} + +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS) { + if (printSymbolName(D, Ctx.getLangOpts(), OS)) { + OS << "<no-name>"; + } + OS << " | "; + + SmallString<256> USRBuf; + if (generateUSRForDecl(D, USRBuf)) { + OS << "<no-usr>"; + } else { + OS << USRBuf; + } +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int indextest_core_main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + PrettyStackTraceProgram X(argc, argv); + + assert(argv[1] == StringRef("core")); + ++argv; + --argc; + + std::vector<const char *> CompArgs; + const char **DoubleDash = std::find(argv, argv + argc, StringRef("--")); + if (DoubleDash != argv + argc) { + CompArgs = std::vector<const char *>(DoubleDash + 1, argv + argc); + argc = DoubleDash - argv; + } + + cl::HideUnrelatedOptions(options::IndexTestCoreCategory); + cl::ParseCommandLineOptions(argc, argv, "index-test-core"); + + if (options::Action == ActionType::None) { + errs() << "error: action required; pass '-help' for options\n"; + return 1; + } + + if (options::Action == ActionType::PrintSourceSymbols) { + if (CompArgs.empty()) { + errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n"; + return 1; + } + return printSourceSymbols(CompArgs); + } + + return 0; +} diff --git a/tools/clang-check/ClangCheck.cpp b/tools/clang-check/ClangCheck.cpp index a9934c978d30..b4177d4a86e2 100644 --- a/tools/clang-check/ClangCheck.cpp +++ b/tools/clang-check/ClangCheck.cpp @@ -142,7 +142,7 @@ public: return clang::CreateASTDumper(ASTDumpFilter, /*DumpDecls=*/true, /*DumpLookups=*/false); if (ASTPrint) - return clang::CreateASTPrinter(&llvm::outs(), ASTDumpFilter); + return clang::CreateASTPrinter(nullptr, ASTDumpFilter); return llvm::make_unique<clang::ASTConsumer>(); } }; @@ -150,7 +150,7 @@ public: } // namespace int main(int argc, const char **argv) { - llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); // Initialize targets for clang module support. llvm::InitializeAllTargets(); diff --git a/tools/clang-check/Makefile b/tools/clang-check/Makefile deleted file mode 100644 index da010ab1f32a..000000000000 --- a/tools/clang-check/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -##===- tools/clang-check/Makefile --------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. - -TOOLNAME = clang-check - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader ipo objcarcopts \ - instrumentation bitwriter support mc option -USEDLIBS = clangFrontend.a clangCodeGen.a clangIndex.a \ - clangSerialization.a clangDriver.a \ - clangTooling.a clangParse.a clangSema.a \ - clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ - clangStaticAnalyzerCore.a clangAnalysis.a clangRewriteFrontend.a \ - clangRewrite.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a - -include $(CLANG_LEVEL)/Makefile diff --git a/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs index df872b2e2198..6af2fd177f0f 100644 --- a/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs +++ b/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs @@ -202,9 +202,10 @@ namespace LLVM.ClangFormat if (start >= text.Length && text.Length > 0)
start = text.Length - 1;
string path = GetDocumentParent(view);
+ string filePath = GetDocumentPath(view);
try
{
- var root = XElement.Parse(RunClangFormat(text, start, length, path));
+ var root = XElement.Parse(RunClangFormat(text, start, length, path, filePath));
var edit = view.TextBuffer.CreateEdit();
foreach (XElement replacement in root.Descendants("replacement"))
{
@@ -237,7 +238,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)
+ private string RunClangFormat(string text, int offset, int length, string path, string filePath)
{
string vsixPath = Path.GetDirectoryName(
typeof(ClangFormatPackage).Assembly.Location);
@@ -257,6 +258,8 @@ namespace LLVM.ClangFormat if (GetSortIncludes())
process.StartInfo.Arguments += " -sort-includes ";
string assumeFilename = GetAssumeFilename();
+ if (string.IsNullOrEmpty(assumeFilename))
+ assumeFilename = filePath;
if (!string.IsNullOrEmpty(assumeFilename))
process.StartInfo.Arguments += " -assume-filename \"" + assumeFilename + "\"";
process.StartInfo.CreateNoWindow = true;
@@ -355,5 +358,15 @@ namespace LLVM.ClangFormat }
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/CMakeLists.txt b/tools/clang-format/CMakeLists.txt index 6ef0c2280f4e..a13633eaefc4 100644 --- a/tools/clang-format/CMakeLists.txt +++ b/tools/clang-format/CMakeLists.txt @@ -1,6 +1,6 @@ set(LLVM_LINK_COMPONENTS support) -add_clang_executable(clang-format +add_clang_tool(clang-format ClangFormat.cpp ) @@ -19,10 +19,21 @@ if( LLVM_USE_SANITIZE_COVERAGE ) add_subdirectory(fuzzer) endif() -install(TARGETS clang-format RUNTIME DESTINATION bin) -install(PROGRAMS clang-format-bbedit.applescript DESTINATION share/clang) -install(PROGRAMS clang-format-diff.py DESTINATION share/clang) -install(PROGRAMS clang-format-sublime.py DESTINATION share/clang) -install(PROGRAMS clang-format.el DESTINATION share/clang) -install(PROGRAMS clang-format.py DESTINATION share/clang) -install(PROGRAMS git-clang-format DESTINATION bin) +install(PROGRAMS clang-format-bbedit.applescript + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format-diff.py + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format-sublime.py + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format.el + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS clang-format.py + DESTINATION share/clang + COMPONENT clang-format) +install(PROGRAMS git-clang-format + DESTINATION bin + COMPONENT clang-format) diff --git a/tools/clang-format/ClangFormat.cpp b/tools/clang-format/ClangFormat.cpp index 36f237fc750c..27577a5a336a 100644 --- a/tools/clang-format/ClangFormat.cpp +++ b/tools/clang-format/ClangFormat.cpp @@ -257,13 +257,16 @@ static bool format(StringRef FileName) { unsigned CursorPosition = Cursor; Replacements Replaces = sortIncludes(FormatStyle, Code->getBuffer(), Ranges, AssumedFileName, &CursorPosition); - std::string ChangedCode = - tooling::applyAllReplacements(Code->getBuffer(), Replaces); + auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); + if (!ChangedCode) { + llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; + return true; + } for (const auto &R : Replaces) Ranges.push_back({R.getOffset(), R.getLength()}); bool IncompleteFormat = false; - Replacements FormatChanges = reformat(FormatStyle, ChangedCode, Ranges, + Replacements FormatChanges = reformat(FormatStyle, *ChangedCode, Ranges, AssumedFileName, &IncompleteFormat); Replaces = tooling::mergeReplacements(Replaces, FormatChanges); if (OutputXML) { @@ -315,7 +318,7 @@ static void PrintVersion() { } int main(int argc, const char **argv) { - llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); cl::HideUnrelatedOptions(ClangFormatCategory); diff --git a/tools/clang-format/Makefile b/tools/clang-format/Makefile deleted file mode 100644 index 76e31cc1a072..000000000000 --- a/tools/clang-format/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -##===- clang-format/Makefile -------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. - -TOOLNAME = clang-format - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option -USEDLIBS = clangFormat.a clangToolingCore.a clangDriver.a clangRewrite.a \ - clangLex.a clangBasic.a - -include $(CLANG_LEVEL)/Makefile diff --git a/tools/clang-format/clang-format-diff.py b/tools/clang-format/clang-format-diff.py index 9e02bb09387f..5e728f547169 100755 --- a/tools/clang-format/clang-format-diff.py +++ b/tools/clang-format/clang-format-diff.py @@ -31,10 +31,6 @@ import StringIO import sys -# Change this to the full path if clang-format is not on the path. -binary = 'clang-format' - - def main(): parser = argparse.ArgumentParser(description= 'Reformat changed lines in diff. Without -i ' @@ -56,10 +52,11 @@ def main(): help='let clang-format sort include blocks') parser.add_argument('-v', '--verbose', action='store_true', help='be more verbose, ineffective without -i') - parser.add_argument( - '-style', - help= - 'formatting style to apply (LLVM, Google, Chromium, Mozilla, WebKit)') + parser.add_argument('-style', + help='formatting style to apply (LLVM, Google, Chromium, ' + 'Mozilla, WebKit)') + parser.add_argument('-binary', default='clang-format', + help='location of binary to use for clang-format') args = parser.parse_args() # Extract changed lines for each file. @@ -95,7 +92,7 @@ def main(): for filename, lines in lines_by_file.iteritems(): if args.i and args.verbose: print 'Formatting', filename - command = [binary, filename] + command = [args.binary, filename] if args.i: command.append('-i') if args.sort_includes: diff --git a/tools/diagtool/Makefile b/tools/diagtool/Makefile deleted file mode 100644 index d49e976e6428..000000000000 --- a/tools/diagtool/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -##===- tools/diagtool/Makefile -----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -CLANG_LEVEL := ../.. - -TOOLNAME = diagtool - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS := 1 - -# Don't install this. -NO_INSTALL = 1 - -include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option -USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ - clangSema.a clangAnalysis.a clangEdit.a clangAST.a clangLex.a \ - clangBasic.a - -include $(CLANG_LEVEL)/Makefile - diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index fca9b616751b..e03b3fa3951e 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -24,7 +24,7 @@ if(CLANG_PLUGIN_SUPPORT) set(LLVM_NO_DEAD_STRIP 1) endif() -add_clang_executable(clang +add_clang_tool(clang driver.cpp cc1_main.cpp cc1as_main.cpp @@ -51,18 +51,6 @@ endif() add_dependencies(clang clang-headers) -install(TARGETS clang - RUNTIME DESTINATION bin - COMPONENT clang) - -if(NOT CMAKE_CONFIGURATION_TYPES) - add_custom_target(install-clang - DEPENDS clang - COMMAND "${CMAKE_COMMAND}" - -DCMAKE_INSTALL_COMPONENT=clang - -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") -endif() - if(NOT CLANG_LINKS_TO_CREATE) set(CLANG_LINKS_TO_CREATE clang++ clang-cl) @@ -99,8 +87,25 @@ if (APPLE) set(TOOL_INFO_BUILD_VERSION) endif() -if(CLANG_ORDER_FILE) - target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") +# the linker -order_file flag is only supported by ld64 +if(LD64_EXECUTABLE AND CLANG_ORDER_FILE) + include(CMakePushCheckState) + + function(check_linker_flag flag out_var) + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag}") + check_cxx_compiler_flag("" ${out_var}) + cmake_pop_check_state() + endfunction() + + # This is a test to ensure the actual order file works with the linker. + check_linker_flag("-Wl,-order_file,${CLANG_ORDER_FILE}" + LINKER_ORDER_FILE_WORKS) + + if(LINKER_ORDER_FILE_WORKS) + target_link_libraries(clang "-Wl,-order_file,${CLANG_ORDER_FILE}") + set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) + endif() endif() if(WITH_POLLY AND LINK_POLLY_INTO_TOOLS) diff --git a/tools/driver/Makefile b/tools/driver/Makefile deleted file mode 100644 index 347702eb9611..000000000000 --- a/tools/driver/Makefile +++ /dev/null @@ -1,75 +0,0 @@ -##===- tools/driver/Makefile -------------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -CLANG_LEVEL := ../.. - -TOOLNAME = clang -TOOLALIAS = clang++ - -ifdef CLANG_ORDER_FILE -TOOL_ORDER_FILE := $(CLANG_ORDER_FILE) -endif - -# Include tool version information on OS X. -TOOL_INFO_PLIST := Info.plist - -# Include this here so we can get the configuration of the targets that have -# been configured for construction. We have to do this early so we can set up -# LINK_COMPONENTS before including Makefile.rules -include $(CLANG_LEVEL)/../../Makefile.config - -# Have the option of not supporting plugins. This is important for startup -# performance. -ifeq ($(CLANG_PLUGIN_SUPPORT), 1) -NO_DEAD_STRIP := 1 -else -TOOL_NO_EXPORTS := 1 -endif - -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ - instrumentation ipo irreader linker objcarcopts option \ - profiledata selectiondag -USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ - clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ - clangRewriteFrontend.a clangRewrite.a - -ifeq ($(ENABLE_CLANG_STATIC_ANALYZER),1) -USEDLIBS += clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ - clangStaticAnalyzerCore.a -endif - -ifeq ($(ENABLE_CLANG_ARCMT),1) -USEDLIBS += clangARCMigrate.a -endif - -USEDLIBS += clangAnalysis.a clangEdit.a clangAST.a clangLex.a clangBasic.a - -include $(CLANG_LEVEL)/Makefile - -# Set the tool version information values. -ifeq ($(HOST_OS),Darwin) -ifdef CLANG_VENDOR -TOOL_INFO_NAME := $(CLANG_VENDOR) clang -else -TOOL_INFO_NAME := clang -endif - -ifdef CLANG_VENDOR_UTI -TOOL_INFO_UTI := $(CLANG_VENDOR_UTI) -else -TOOL_INFO_UTI := org.llvm.clang -endif - -TOOL_INFO_VERSION := $(word 3,$(shell grep "CLANG_VERSION " \ - $(PROJ_OBJ_DIR)/$(CLANG_LEVEL)/include/clang/Basic/Version.inc)) -ifdef LLVM_SUBMIT_VERSION -TOOL_INFO_BUILD_VERSION := $(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) -else -TOOL_INFO_BUILD_VERSION := -endif -endif diff --git a/tools/driver/cc1_main.cpp b/tools/driver/cc1_main.cpp index 8240561236b2..d78a31e67a2f 100644 --- a/tools/driver/cc1_main.cpp +++ b/tools/driver/cc1_main.cpp @@ -126,15 +126,9 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { // When running with -disable-free, don't do any destruction or shutdown. if (Clang->getFrontendOpts().DisableFree) { - if (llvm::AreStatisticsEnabled() || Clang->getFrontendOpts().ShowStats) - llvm::PrintStatistics(); BuryPointer(std::move(Clang)); return !Success; } - // Managed static deconstruction. Useful for making things like - // -time-passes usable. - llvm::llvm_shutdown(); - return !Success; } diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp index 59b7af521743..2d17be99e2d5 100644 --- a/tools/driver/cc1as_main.cpp +++ b/tools/driver/cc1as_main.cpp @@ -30,10 +30,10 @@ #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetAsmParser.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -43,10 +43,8 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/Host.h" -#include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetRegistry.h" @@ -88,6 +86,7 @@ struct AssemblerInvocation { unsigned SaveTemporaryLabels : 1; unsigned GenDwarfForAssembly : 1; unsigned CompressDebugSections : 1; + unsigned RelaxELFRelocations : 1; unsigned DwarfVersion; std::string DwarfDebugFlags; std::string DwarfDebugProducer; @@ -200,7 +199,8 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, // Any DebugInfoKind implies GenDwarfForAssembly. Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ); Opts.CompressDebugSections = Args.hasArg(OPT_compress_debug_sections); - Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 0, Diags); + 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); Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer); Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); @@ -313,7 +313,9 @@ 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(true); + MAI->setCompressDebugSections(DebugCompressionType::DCT_ZlibGnu); + + MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations); bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; std::unique_ptr<raw_fd_ostream> FDOS = getOutputStream(Opts, Diags, IsBinary); @@ -326,19 +328,18 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr); - llvm::Reloc::Model RM = llvm::Reloc::Default; + bool PIC = false; if (Opts.RelocationModel == "static") { - RM = llvm::Reloc::Static; + PIC = false; } else if (Opts.RelocationModel == "pic") { - RM = llvm::Reloc::PIC_; + PIC = true; } else { assert(Opts.RelocationModel == "dynamic-no-pic" && "Invalid PIC model!"); - RM = llvm::Reloc::DynamicNoPIC; + PIC = false; } - MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), RM, - CodeModel::Default, Ctx); + MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, CodeModel::Default, Ctx); if (Opts.SaveTemporaryLabels) Ctx.setAllowTemporaryLabels(false); if (Opts.GenDwarfForAssembly) @@ -447,11 +448,6 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message, } int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { - // Print a stack trace if we signal out. - sys::PrintStackTraceOnErrorSignal(); - PrettyStackTraceProgram X(Argv.size(), Argv.data()); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. - // Initialize targets and assembly printers/parsers. InitializeAllTargetInfos(); InitializeAllTargetMCs(); diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index de14425d3616..4d69aaffba23 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -130,7 +130,7 @@ static void ApplyOneQAOverride(raw_ostream &OS, } } } else if (Edit[0] == 'x' || Edit[0] == 'X') { - std::string Option = Edit.substr(1, std::string::npos); + auto Option = Edit.substr(1); for (unsigned i = 1; i < Args.size();) { if (Option == Args[i]) { OS << "### Deleting argument " << Args[i] << '\n'; @@ -308,8 +308,9 @@ static int ExecuteCC1Tool(ArrayRef<const char *> argv, StringRef Tool) { } int main(int argc_, const char **argv_) { - llvm::sys::PrintStackTraceOnErrorSignal(); + llvm::sys::PrintStackTraceOnErrorSignal(argv_[0]); llvm::PrettyStackTraceProgram X(argc_, argv_); + llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. if (llvm::sys::Process::FixupStandardFileDescriptors()) return 1; @@ -338,18 +339,33 @@ int main(int argc_, const char **argv_) { // have to manually search for a --driver-mode=cl argument the hard way. // 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. - llvm::cl::TokenizerCallback Tokenizer = &llvm::cl::TokenizeGNUCommandLine; + bool ClangCLMode = false; if (TargetAndMode.second == "--driver-mode=cl" || std::find_if(argv.begin(), argv.end(), [](const char *F) { return F && strcmp(F, "--driver-mode=cl") == 0; }) != argv.end()) { - Tokenizer = &llvm::cl::TokenizeWindowsCommandLine; + ClangCLMode = true; + } + enum { Default, POSIX, Windows } RSPQuoting = Default; + for (const char *F : argv) { + if (strcmp(F, "--rsp-quoting=posix") == 0) + RSPQuoting = POSIX; + else if (strcmp(F, "--rsp-quoting=windows") == 0) + RSPQuoting = Windows; } // Determines whether we want nullptr markers in argv to indicate response - // files end-of-lines. We only use this for the /LINK driver argument. - bool MarkEOLs = true; - if (argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) + // files end-of-lines. We only use this for the /LINK driver argument with + // clang-cl.exe on Windows. + bool MarkEOLs = ClangCLMode; + + llvm::cl::TokenizerCallback Tokenizer; + if (RSPQuoting == Windows || (RSPQuoting == Default && ClangCLMode)) + Tokenizer = &llvm::cl::TokenizeWindowsCommandLine; + else + Tokenizer = &llvm::cl::TokenizeGNUCommandLine; + + if (MarkEOLs && argv.size() > 1 && StringRef(argv[1]).startswith("-cc1")) MarkEOLs = false; llvm::cl::ExpandResponseFiles(Saver, Tokenizer, argv, MarkEOLs); @@ -482,8 +498,6 @@ int main(int argc_, const char **argv_) { // results now. This happens in -disable-free mode. llvm::TimerGroup::printAll(llvm::errs()); - llvm::llvm_shutdown(); - #ifdef LLVM_ON_WIN32 // Exit status should not be negative on Win32, unless abnormal termination. // Once abnormal termiation was caught, negative status should not be diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 9086c60e18be..027bf95b660b 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -22,16 +22,15 @@ #include "CXType.h" #include "CursorVisitor.h" #include "clang/AST/Attr.h" -#include "clang/AST/Mangle.h" #include "clang/AST/StmtVisitor.h" #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" #include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Index/CodegenNameGenerator.h" #include "clang/Index/CommentToXML.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" @@ -42,8 +41,6 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Config/llvm-config.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/Mangler.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Format.h" @@ -526,8 +523,10 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) { for (ASTUnit::top_level_iterator TL = CXXUnit->top_level_begin(), TLEnd = CXXUnit->top_level_end(); TL != TLEnd; ++TL) { - if (Visit(MakeCXCursor(*TL, TU, RegionOfInterest), true)) - return true; + const Optional<bool> V = handleDeclForVisitation(*TL); + if (!V.hasValue()) + continue; + return V.getValue(); } } else if (VisitDeclContext( CXXUnit->getASTContext().getTranslationUnitDecl())) @@ -624,40 +623,48 @@ bool CursorVisitor::VisitDeclContext(DeclContext *DC) { Decl *D = *I; if (D->getLexicalDeclContext() != DC) continue; - CXCursor Cursor = MakeCXCursor(D, TU, RegionOfInterest); + const Optional<bool> V = handleDeclForVisitation(D); + if (!V.hasValue()) + continue; + return V.getValue(); + } + return false; +} - // Ignore synthesized ivars here, otherwise if we have something like: - // @synthesize prop = _prop; - // and '_prop' is not declared, we will encounter a '_prop' ivar before - // encountering the 'prop' synthesize declaration and we will think that - // we passed the region-of-interest. - if (ObjCIvarDecl *ivarD = dyn_cast<ObjCIvarDecl>(D)) { - if (ivarD->getSynthesize()) - continue; - } +Optional<bool> CursorVisitor::handleDeclForVisitation(const Decl *D) { + CXCursor Cursor = MakeCXCursor(D, TU, RegionOfInterest); - // FIXME: ObjCClassRef/ObjCProtocolRef for forward class/protocol - // declarations is a mismatch with the compiler semantics. - if (Cursor.kind == CXCursor_ObjCInterfaceDecl) { - ObjCInterfaceDecl *ID = cast<ObjCInterfaceDecl>(D); - if (!ID->isThisDeclarationADefinition()) - Cursor = MakeCursorObjCClassRef(ID, ID->getLocation(), TU); + // Ignore synthesized ivars here, otherwise if we have something like: + // @synthesize prop = _prop; + // and '_prop' is not declared, we will encounter a '_prop' ivar before + // encountering the 'prop' synthesize declaration and we will think that + // we passed the region-of-interest. + if (auto *ivarD = dyn_cast<ObjCIvarDecl>(D)) { + if (ivarD->getSynthesize()) + return None; + } - } else if (Cursor.kind == CXCursor_ObjCProtocolDecl) { - ObjCProtocolDecl *PD = cast<ObjCProtocolDecl>(D); - if (!PD->isThisDeclarationADefinition()) - Cursor = MakeCursorObjCProtocolRef(PD, PD->getLocation(), TU); - } + // FIXME: ObjCClassRef/ObjCProtocolRef for forward class/protocol + // declarations is a mismatch with the compiler semantics. + if (Cursor.kind == CXCursor_ObjCInterfaceDecl) { + auto *ID = cast<ObjCInterfaceDecl>(D); + if (!ID->isThisDeclarationADefinition()) + Cursor = MakeCursorObjCClassRef(ID, ID->getLocation(), TU); - const Optional<bool> &V = shouldVisitCursor(Cursor); - if (!V.hasValue()) - continue; - if (!V.getValue()) - return false; - if (Visit(Cursor, true)) - return true; + } else if (Cursor.kind == CXCursor_ObjCProtocolDecl) { + auto *PD = cast<ObjCProtocolDecl>(D); + if (!PD->isThisDeclarationADefinition()) + Cursor = MakeCursorObjCProtocolRef(PD, PD->getLocation(), TU); } - return false; + + const Optional<bool> V = shouldVisitCursor(Cursor); + if (!V.hasValue()) + return None; + if (!V.getValue()) + return false; + if (Visit(Cursor, true)) + return true; + return None; } bool CursorVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) { @@ -938,7 +945,7 @@ bool CursorVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) { if (Visit(TSInfo->getTypeLoc())) return true; - for (const auto *P : ND->params()) { + for (const auto *P : ND->parameters()) { if (Visit(MakeCXCursor(P, TU, RegionOfInterest))) return true; } @@ -1077,7 +1084,8 @@ bool CursorVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *PD) { IdentifierInfo *PropertyId = PD->getIdentifier(); ObjCPropertyDecl *prevDecl = - ObjCPropertyDecl::findPropertyDecl(cast<DeclContext>(ID), PropertyId); + ObjCPropertyDecl::findPropertyDecl(cast<DeclContext>(ID), PropertyId, + PD->getQueryKind()); if (!prevDecl) return false; @@ -1232,6 +1240,14 @@ bool CursorVisitor::VisitUnresolvedUsingTypenameDecl( return false; } +bool CursorVisitor::VisitStaticAssertDecl(StaticAssertDecl *D) { + if (Visit(MakeCXCursor(D->getAssertExpr(), StmtParent, TU, RegionOfInterest))) + return true; + if (Visit(MakeCXCursor(D->getMessage(), StmtParent, TU, RegionOfInterest))) + return true; + return false; +} + bool CursorVisitor::VisitDeclarationNameInfo(DeclarationNameInfo Name) { switch (Name.getName().getNameKind()) { case clang::DeclarationName::Identifier: @@ -1456,18 +1472,9 @@ bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) { case BuiltinType::Void: case BuiltinType::NullPtr: case BuiltinType::Dependent: - case BuiltinType::OCLImage1d: - case BuiltinType::OCLImage1dArray: - case BuiltinType::OCLImage1dBuffer: - case BuiltinType::OCLImage2d: - case BuiltinType::OCLImage2dArray: - case BuiltinType::OCLImage2dDepth: - case BuiltinType::OCLImage2dArrayDepth: - case BuiltinType::OCLImage2dMSAA: - case BuiltinType::OCLImage2dArrayMSAA: - case BuiltinType::OCLImage2dMSAADepth: - case BuiltinType::OCLImage2dArrayMSAADepth: - case BuiltinType::OCLImage3d: +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLImageTypes.def" case BuiltinType::OCLSampler: case BuiltinType::OCLEvent: case BuiltinType::OCLClkEvent: @@ -1953,10 +1960,22 @@ public: void VisitOMPAtomicDirective(const OMPAtomicDirective *D); void VisitOMPTargetDirective(const OMPTargetDirective *D); void VisitOMPTargetDataDirective(const OMPTargetDataDirective *D); + void VisitOMPTargetEnterDataDirective(const OMPTargetEnterDataDirective *D); + void VisitOMPTargetExitDataDirective(const OMPTargetExitDataDirective *D); + void VisitOMPTargetParallelDirective(const OMPTargetParallelDirective *D); + void + VisitOMPTargetParallelForDirective(const OMPTargetParallelForDirective *D); void VisitOMPTeamsDirective(const OMPTeamsDirective *D); void VisitOMPTaskLoopDirective(const OMPTaskLoopDirective *D); void VisitOMPTaskLoopSimdDirective(const OMPTaskLoopSimdDirective *D); void VisitOMPDistributeDirective(const OMPDistributeDirective *D); + void VisitOMPDistributeParallelForDirective( + const OMPDistributeParallelForDirective *D); + void VisitOMPDistributeParallelForSimdDirective( + const OMPDistributeParallelForSimdDirective *D); + void VisitOMPDistributeSimdDirective(const OMPDistributeSimdDirective *D); + void VisitOMPTargetParallelForSimdDirective( + const OMPTargetParallelForSimdDirective *D); private: void AddDeclarationNameInfo(const Stmt *S); @@ -2027,8 +2046,21 @@ public: #define OPENMP_CLAUSE(Name, Class) \ void Visit##Class(const Class *C); #include "clang/Basic/OpenMPKinds.def" + void VisitOMPClauseWithPreInit(const OMPClauseWithPreInit *C); + void VisitOMPClauseWithPostUpdate(const OMPClauseWithPostUpdate *C); }; +void OMPClauseEnqueue::VisitOMPClauseWithPreInit( + const OMPClauseWithPreInit *C) { + Visitor->AddStmt(C->getPreInitStmt()); +} + +void OMPClauseEnqueue::VisitOMPClauseWithPostUpdate( + const OMPClauseWithPostUpdate *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getPostUpdateExpr()); +} + void OMPClauseEnqueue::VisitOMPIfClause(const OMPIfClause *C) { Visitor->AddStmt(C->getCondition()); } @@ -2058,8 +2090,8 @@ void OMPClauseEnqueue::VisitOMPDefaultClause(const OMPDefaultClause *C) { } void OMPClauseEnqueue::VisitOMPProcBindClause(const OMPProcBindClause *C) { } void OMPClauseEnqueue::VisitOMPScheduleClause(const OMPScheduleClause *C) { + VisitOMPClauseWithPreInit(C); Visitor->AddStmt(C->getChunkSize()); - Visitor->AddStmt(C->getHelperChunkSize()); } void OMPClauseEnqueue::VisitOMPOrderedClause(const OMPOrderedClause *C) { @@ -2132,10 +2164,18 @@ void OMPClauseEnqueue::VisitOMPPrivateClause(const OMPPrivateClause *C) { void OMPClauseEnqueue::VisitOMPFirstprivateClause( const OMPFirstprivateClause *C) { VisitOMPClauseList(C); + VisitOMPClauseWithPreInit(C); + for (const auto *E : C->private_copies()) { + Visitor->AddStmt(E); + } + for (const auto *E : C->inits()) { + Visitor->AddStmt(E); + } } void OMPClauseEnqueue::VisitOMPLastprivateClause( const OMPLastprivateClause *C) { VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); for (auto *E : C->private_copies()) { Visitor->AddStmt(E); } @@ -2154,6 +2194,7 @@ void OMPClauseEnqueue::VisitOMPSharedClause(const OMPSharedClause *C) { } void OMPClauseEnqueue::VisitOMPReductionClause(const OMPReductionClause *C) { VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); for (auto *E : C->privates()) { Visitor->AddStmt(E); } @@ -2169,6 +2210,7 @@ void OMPClauseEnqueue::VisitOMPReductionClause(const OMPReductionClause *C) { } void OMPClauseEnqueue::VisitOMPLinearClause(const OMPLinearClause *C) { VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); for (const auto *E : C->privates()) { Visitor->AddStmt(E); } @@ -2222,6 +2264,25 @@ void OMPClauseEnqueue::VisitOMPDependClause(const OMPDependClause *C) { void OMPClauseEnqueue::VisitOMPMapClause(const OMPMapClause *C) { VisitOMPClauseList(C); } +void OMPClauseEnqueue::VisitOMPDistScheduleClause( + const OMPDistScheduleClause *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getChunkSize()); +} +void OMPClauseEnqueue::VisitOMPDefaultmapClause( + const OMPDefaultmapClause * /*C*/) {} +void OMPClauseEnqueue::VisitOMPToClause(const OMPToClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPFromClause(const OMPFromClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPUseDevicePtrClause(const OMPUseDevicePtrClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPIsDevicePtrClause(const OMPIsDevicePtrClause *C) { + VisitOMPClauseList(C); +} } void EnqueueVisitor::EnqueueChildren(const OMPClause *S) { @@ -2362,21 +2423,20 @@ void EnqueueVisitor::VisitDeclStmt(const DeclStmt *S) { } void EnqueueVisitor::VisitDesignatedInitExpr(const DesignatedInitExpr *E) { AddStmt(E->getInit()); - for (DesignatedInitExpr::const_reverse_designators_iterator - D = E->designators_rbegin(), DEnd = E->designators_rend(); - D != DEnd; ++D) { - if (D->isFieldDesignator()) { - if (FieldDecl *Field = D->getField()) - AddMemberRef(Field, D->getFieldLoc()); + for (const DesignatedInitExpr::Designator &D : + llvm::reverse(E->designators())) { + if (D.isFieldDesignator()) { + if (FieldDecl *Field = D.getField()) + AddMemberRef(Field, D.getFieldLoc()); continue; } - if (D->isArrayDesignator()) { - AddStmt(E->getArrayIndex(*D)); + if (D.isArrayDesignator()) { + AddStmt(E->getArrayIndex(D)); continue; } - assert(D->isArrayRangeDesignator() && "Unknown designator kind"); - AddStmt(E->getArrayRangeEnd(*D)); - AddStmt(E->getArrayRangeStart(*D)); + assert(D.isArrayRangeDesignator() && "Unknown designator kind"); + AddStmt(E->getArrayRangeEnd(D)); + AddStmt(E->getArrayRangeStart(D)); } } void EnqueueVisitor::VisitExplicitCastExpr(const ExplicitCastExpr *E) { @@ -2628,6 +2688,26 @@ void EnqueueVisitor::VisitOMPTargetDataDirective(const VisitOMPExecutableDirective(D); } +void EnqueueVisitor::VisitOMPTargetEnterDataDirective( + const OMPTargetEnterDataDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetExitDataDirective( + const OMPTargetExitDataDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetParallelDirective( + const OMPTargetParallelDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetParallelForDirective( + const OMPTargetParallelForDirective *D) { + VisitOMPLoopDirective(D); +} + void EnqueueVisitor::VisitOMPTeamsDirective(const OMPTeamsDirective *D) { VisitOMPExecutableDirective(D); } @@ -2655,6 +2735,26 @@ void EnqueueVisitor::VisitOMPDistributeDirective( VisitOMPLoopDirective(D); } +void EnqueueVisitor::VisitOMPDistributeParallelForDirective( + const OMPDistributeParallelForDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPDistributeParallelForSimdDirective( + const OMPDistributeParallelForSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPDistributeSimdDirective( + const OMPDistributeSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetParallelForSimdDirective( + const OMPTargetParallelForSimdDirective *D) { + VisitOMPLoopDirective(D); +} + void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, const Stmt *S) { EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S); } @@ -3107,6 +3207,9 @@ clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, IntrusiveRefCntPtr<DiagnosticsEngine> Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + if (options & CXTranslationUnit_KeepGoing) + Diags->setFatalsAsError(true); + // Recover resources if we crash before exiting this function. llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > @@ -3284,6 +3387,313 @@ enum CXErrorCode clang_parseTranslationUnit2FullArgv( return result; } +CXString clang_Type_getObjCEncoding(CXType CT) { + CXTranslationUnit tu = static_cast<CXTranslationUnit>(CT.data[1]); + ASTContext &Ctx = getASTUnit(tu)->getASTContext(); + std::string encoding; + Ctx.getObjCEncodingForType(QualType::getFromOpaquePtr(CT.data[0]), + encoding); + + return cxstring::createDup(encoding); +} + +static const IdentifierInfo *getMacroIdentifier(CXCursor C) { + if (C.kind == CXCursor_MacroDefinition) { + if (const MacroDefinitionRecord *MDR = getCursorMacroDefinition(C)) + return MDR->getName(); + } else if (C.kind == CXCursor_MacroExpansion) { + MacroExpansionCursor ME = getCursorMacroExpansion(C); + return ME.getName(); + } + return nullptr; +} + +unsigned clang_Cursor_isMacroFunctionLike(CXCursor C) { + const IdentifierInfo *II = getMacroIdentifier(C); + if (!II) { + return false; + } + ASTUnit *ASTU = getCursorASTUnit(C); + Preprocessor &PP = ASTU->getPreprocessor(); + if (const MacroInfo *MI = PP.getMacroInfo(II)) + return MI->isFunctionLike(); + return false; +} + +unsigned clang_Cursor_isMacroBuiltin(CXCursor C) { + const IdentifierInfo *II = getMacroIdentifier(C); + if (!II) { + return false; + } + ASTUnit *ASTU = getCursorASTUnit(C); + Preprocessor &PP = ASTU->getPreprocessor(); + if (const MacroInfo *MI = PP.getMacroInfo(II)) + return MI->isBuiltinMacro(); + return false; +} + +unsigned clang_Cursor_isFunctionInlined(CXCursor C) { + const Decl *D = getCursorDecl(C); + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D); + if (!FD) { + return false; + } + return FD->isInlined(); +} + +static StringLiteral* getCFSTR_value(CallExpr *callExpr) { + if (callExpr->getNumArgs() != 1) { + return nullptr; + } + + StringLiteral *S = nullptr; + auto *arg = callExpr->getArg(0); + if (arg->getStmtClass() == Stmt::ImplicitCastExprClass) { + ImplicitCastExpr *I = static_cast<ImplicitCastExpr *>(arg); + auto *subExpr = I->getSubExprAsWritten(); + + if(subExpr->getStmtClass() != Stmt::StringLiteralClass){ + return nullptr; + } + + S = static_cast<StringLiteral *>(I->getSubExprAsWritten()); + } else if (arg->getStmtClass() == Stmt::StringLiteralClass) { + S = static_cast<StringLiteral *>(callExpr->getArg(0)); + } else { + return nullptr; + } + return S; +} + +struct ExprEvalResult { + CXEvalResultKind EvalType; + union { + int intVal; + double floatVal; + char *stringVal; + } EvalData; + ~ExprEvalResult() { + if (EvalType != CXEval_UnExposed && EvalType != CXEval_Float && + EvalType != CXEval_Int) { + delete EvalData.stringVal; + } + } +}; + +void clang_EvalResult_dispose(CXEvalResult E) { + delete static_cast<ExprEvalResult *>(E); +} + +CXEvalResultKind clang_EvalResult_getKind(CXEvalResult E) { + if (!E) { + return CXEval_UnExposed; + } + return ((ExprEvalResult *)E)->EvalType; +} + +int clang_EvalResult_getAsInt(CXEvalResult E) { + if (!E) { + return 0; + } + return ((ExprEvalResult *)E)->EvalData.intVal; +} + +double clang_EvalResult_getAsDouble(CXEvalResult E) { + if (!E) { + return 0; + } + return ((ExprEvalResult *)E)->EvalData.floatVal; +} + +const char* clang_EvalResult_getAsStr(CXEvalResult E) { + if (!E) { + return nullptr; + } + return ((ExprEvalResult *)E)->EvalData.stringVal; +} + +static const ExprEvalResult* evaluateExpr(Expr *expr, CXCursor C) { + Expr::EvalResult ER; + ASTContext &ctx = getCursorContext(C); + if (!expr) + return nullptr; + + expr = expr->IgnoreParens(); + if (!expr->EvaluateAsRValue(ER, ctx)) + return nullptr; + + QualType rettype; + CallExpr *callExpr; + auto result = llvm::make_unique<ExprEvalResult>(); + result->EvalType = CXEval_UnExposed; + + if (ER.Val.isInt()) { + result->EvalType = CXEval_Int; + result->EvalData.intVal = ER.Val.getInt().getExtValue(); + return result.release(); + } + + if (ER.Val.isFloat()) { + llvm::SmallVector<char, 100> Buffer; + ER.Val.getFloat().toString(Buffer); + std::string floatStr(Buffer.data(), Buffer.size()); + result->EvalType = CXEval_Float; + bool ignored; + llvm::APFloat apFloat = ER.Val.getFloat(); + apFloat.convert(llvm::APFloat::IEEEdouble, + llvm::APFloat::rmNearestTiesToEven, &ignored); + result->EvalData.floatVal = apFloat.convertToDouble(); + return result.release(); + } + + if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { + const ImplicitCastExpr *I = dyn_cast<ImplicitCastExpr>(expr); + auto *subExpr = I->getSubExprAsWritten(); + if (subExpr->getStmtClass() == Stmt::StringLiteralClass || + subExpr->getStmtClass() == Stmt::ObjCStringLiteralClass) { + const StringLiteral *StrE = nullptr; + const ObjCStringLiteral *ObjCExpr; + ObjCExpr = dyn_cast<ObjCStringLiteral>(subExpr); + + if (ObjCExpr) { + StrE = ObjCExpr->getString(); + result->EvalType = CXEval_ObjCStrLiteral; + } else { + StrE = cast<StringLiteral>(I->getSubExprAsWritten()); + result->EvalType = CXEval_StrLiteral; + } + + std::string strRef(StrE->getString().str()); + result->EvalData.stringVal = new char[strRef.size() + 1]; + strncpy((char *)result->EvalData.stringVal, strRef.c_str(), + strRef.size()); + result->EvalData.stringVal[strRef.size()] = '\0'; + return result.release(); + } + } else if (expr->getStmtClass() == Stmt::ObjCStringLiteralClass || + expr->getStmtClass() == Stmt::StringLiteralClass) { + const StringLiteral *StrE = nullptr; + const ObjCStringLiteral *ObjCExpr; + ObjCExpr = dyn_cast<ObjCStringLiteral>(expr); + + if (ObjCExpr) { + StrE = ObjCExpr->getString(); + result->EvalType = CXEval_ObjCStrLiteral; + } else { + StrE = cast<StringLiteral>(expr); + result->EvalType = CXEval_StrLiteral; + } + + std::string strRef(StrE->getString().str()); + result->EvalData.stringVal = new char[strRef.size() + 1]; + strncpy((char *)result->EvalData.stringVal, strRef.c_str(), strRef.size()); + result->EvalData.stringVal[strRef.size()] = '\0'; + return result.release(); + } + + if (expr->getStmtClass() == Stmt::CStyleCastExprClass) { + CStyleCastExpr *CC = static_cast<CStyleCastExpr *>(expr); + + rettype = CC->getType(); + if (rettype.getAsString() == "CFStringRef" && + CC->getSubExpr()->getStmtClass() == Stmt::CallExprClass) { + + callExpr = static_cast<CallExpr *>(CC->getSubExpr()); + StringLiteral *S = getCFSTR_value(callExpr); + if (S) { + std::string strLiteral(S->getString().str()); + result->EvalType = CXEval_CFStr; + + result->EvalData.stringVal = new char[strLiteral.size() + 1]; + strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(), + strLiteral.size()); + result->EvalData.stringVal[strLiteral.size()] = '\0'; + return result.release(); + } + } + + } else if (expr->getStmtClass() == Stmt::CallExprClass) { + callExpr = static_cast<CallExpr *>(expr); + rettype = callExpr->getCallReturnType(ctx); + + if (rettype->isVectorType() || callExpr->getNumArgs() > 1) + return nullptr; + + if (rettype->isIntegralType(ctx) || rettype->isRealFloatingType()) { + if (callExpr->getNumArgs() == 1 && + !callExpr->getArg(0)->getType()->isIntegralType(ctx)) + return nullptr; + } else if (rettype.getAsString() == "CFStringRef") { + + StringLiteral *S = getCFSTR_value(callExpr); + if (S) { + std::string strLiteral(S->getString().str()); + result->EvalType = CXEval_CFStr; + result->EvalData.stringVal = new char[strLiteral.size() + 1]; + strncpy((char *)result->EvalData.stringVal, strLiteral.c_str(), + strLiteral.size()); + result->EvalData.stringVal[strLiteral.size()] = '\0'; + return result.release(); + } + } + } else if (expr->getStmtClass() == Stmt::DeclRefExprClass) { + DeclRefExpr *D = static_cast<DeclRefExpr *>(expr); + ValueDecl *V = D->getDecl(); + if (V->getKind() == Decl::Function) { + std::string strName = V->getNameAsString(); + result->EvalType = CXEval_Other; + result->EvalData.stringVal = new char[strName.size() + 1]; + strncpy(result->EvalData.stringVal, strName.c_str(), strName.size()); + result->EvalData.stringVal[strName.size()] = '\0'; + return result.release(); + } + } + + return nullptr; +} + +CXEvalResult clang_Cursor_Evaluate(CXCursor C) { + const Decl *D = getCursorDecl(C); + if (D) { + const Expr *expr = nullptr; + if (auto *Var = dyn_cast<VarDecl>(D)) { + expr = Var->getInit(); + } else if (auto *Field = dyn_cast<FieldDecl>(D)) { + expr = Field->getInClassInitializer(); + } + if (expr) + return const_cast<CXEvalResult>(reinterpret_cast<const void *>( + evaluateExpr(const_cast<Expr *>(expr), C))); + return nullptr; + } + + const CompoundStmt *compoundStmt = dyn_cast_or_null<CompoundStmt>(getCursorStmt(C)); + if (compoundStmt) { + Expr *expr = nullptr; + for (auto *bodyIterator : compoundStmt->body()) { + if ((expr = dyn_cast<Expr>(bodyIterator))) { + break; + } + } + if (expr) + return const_cast<CXEvalResult>( + reinterpret_cast<const void *>(evaluateExpr(expr, C))); + } + return nullptr; +} + +unsigned clang_Cursor_hasAttrs(CXCursor C) { + const Decl *D = getCursorDecl(C); + if (!D) { + return 0; + } + + if (D->hasAttrs()) { + return 1; + } + + return 0; +} unsigned clang_defaultSaveOptions(CXTranslationUnit TU) { return CXSaveTranslationUnit_None; } @@ -3581,6 +3991,9 @@ static const Decl *getDeclFromExpr(const Stmt *E) { if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) if (!CE->isElidable()) return CE->getConstructor(); + if (const CXXInheritedCtorInitExpr *CE = + dyn_cast<CXXInheritedCtorInitExpr>(E)) + return CE->getConstructor(); if (const ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>(E)) return OME->getMethodDecl(); @@ -3617,26 +4030,6 @@ static SourceLocation getLocationFromExpr(const Expr *E) { return E->getLocStart(); } -static std::string getMangledStructor(std::unique_ptr<MangleContext> &M, - std::unique_ptr<llvm::DataLayout> &DL, - const NamedDecl *ND, - unsigned StructorType) { - std::string FrontendBuf; - llvm::raw_string_ostream FOS(FrontendBuf); - - if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(ND)) - M->mangleCXXCtor(CD, static_cast<CXXCtorType>(StructorType), FOS); - else if (const auto *DD = dyn_cast_or_null<CXXDestructorDecl>(ND)) - M->mangleCXXDtor(DD, static_cast<CXXDtorType>(StructorType), FOS); - - std::string BackendBuf; - llvm::raw_string_ostream BOS(BackendBuf); - - llvm::Mangler::getNameWithPrefix(BOS, llvm::Twine(FOS.str()), *DL); - - return BOS.str(); -} - extern "C" { unsigned clang_visitChildren(CXCursor parent, @@ -3985,29 +4378,9 @@ CXString clang_Cursor_getMangling(CXCursor C) { if (!D || !(isa<FunctionDecl>(D) || isa<VarDecl>(D))) return cxstring::createEmpty(); - // First apply frontend mangling. - const NamedDecl *ND = cast<NamedDecl>(D); - ASTContext &Ctx = ND->getASTContext(); - std::unique_ptr<MangleContext> MC(Ctx.createMangleContext()); - - std::string FrontendBuf; - llvm::raw_string_ostream FrontendBufOS(FrontendBuf); - if (MC->shouldMangleDeclName(ND)) { - MC->mangleName(ND, FrontendBufOS); - } else { - ND->printName(FrontendBufOS); - } - - // Now apply backend mangling. - std::unique_ptr<llvm::DataLayout> DL( - new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); - - std::string FinalBuf; - llvm::raw_string_ostream FinalBufOS(FinalBuf); - llvm::Mangler::getNameWithPrefix(FinalBufOS, llvm::Twine(FrontendBufOS.str()), - *DL); - - return cxstring::createDup(FinalBufOS.str()); + ASTContext &Ctx = D->getASTContext(); + index::CodegenNameGenerator CGNameGen(Ctx); + return cxstring::createDup(CGNameGen.getName(D)); } CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { @@ -4018,43 +4391,9 @@ CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { if (!(isa<CXXRecordDecl>(D) || isa<CXXMethodDecl>(D))) return nullptr; - const NamedDecl *ND = cast<NamedDecl>(D); - - ASTContext &Ctx = ND->getASTContext(); - std::unique_ptr<MangleContext> M(Ctx.createMangleContext()); - std::unique_ptr<llvm::DataLayout> DL( - new llvm::DataLayout(Ctx.getTargetInfo().getDataLayoutString())); - - std::vector<std::string> Manglings; - - auto hasDefaultCXXMethodCC = [](ASTContext &C, const CXXMethodDecl *MD) { - auto DefaultCC = C.getDefaultCallingConvention(/*IsVariadic=*/false, - /*IsCSSMethod=*/true); - auto CC = MD->getType()->getAs<FunctionProtoType>()->getCallConv(); - return CC == DefaultCC; - }; - - if (const auto *CD = dyn_cast_or_null<CXXConstructorDecl>(ND)) { - Manglings.emplace_back(getMangledStructor(M, DL, CD, Ctor_Base)); - - if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) - if (!CD->getParent()->isAbstract()) - Manglings.emplace_back(getMangledStructor(M, DL, CD, Ctor_Complete)); - - if (Ctx.getTargetInfo().getCXXABI().isMicrosoft()) - if (CD->hasAttr<DLLExportAttr>() && CD->isDefaultConstructor()) - if (!(hasDefaultCXXMethodCC(Ctx, CD) && CD->getNumParams() == 0)) - Manglings.emplace_back(getMangledStructor(M, DL, CD, - Ctor_DefaultClosure)); - } else if (const auto *DD = dyn_cast_or_null<CXXDestructorDecl>(ND)) { - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Base)); - if (Ctx.getTargetInfo().getCXXABI().isItaniumFamily()) { - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Complete)); - if (DD->isVirtual()) - Manglings.emplace_back(getMangledStructor(M, DL, DD, Dtor_Deleting)); - } - } - + ASTContext &Ctx = D->getASTContext(); + index::CodegenNameGenerator CGNameGen(Ctx); + std::vector<std::string> Manglings = CGNameGen.getAllManglings(D); return cxstring::createSet(Manglings); } @@ -4132,10 +4471,8 @@ CXString clang_getCursorDisplayName(CXCursor C) { SmallString<128> Str; llvm::raw_svector_ostream OS(Str); OS << *ClassSpec; - TemplateSpecializationType::PrintTemplateArgumentList(OS, - ClassSpec->getTemplateArgs().data(), - ClassSpec->getTemplateArgs().size(), - Policy); + TemplateSpecializationType::PrintTemplateArgumentList( + OS, ClassSpec->getTemplateArgs().asArray(), Policy); return cxstring::createDup(OS.str()); } @@ -4274,6 +4611,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("ObjCStringLiteral"); case CXCursor_ObjCBoolLiteralExpr: return cxstring::createRef("ObjCBoolLiteralExpr"); + case CXCursor_ObjCAvailabilityCheckExpr: + return cxstring::createRef("ObjCAvailabilityCheckExpr"); case CXCursor_ObjCSelfExpr: return cxstring::createRef("ObjCSelfExpr"); case CXCursor_ObjCEncodeExpr: @@ -4510,6 +4849,16 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPTargetDirective"); case CXCursor_OMPTargetDataDirective: return cxstring::createRef("OMPTargetDataDirective"); + case CXCursor_OMPTargetEnterDataDirective: + return cxstring::createRef("OMPTargetEnterDataDirective"); + case CXCursor_OMPTargetExitDataDirective: + return cxstring::createRef("OMPTargetExitDataDirective"); + case CXCursor_OMPTargetParallelDirective: + return cxstring::createRef("OMPTargetParallelDirective"); + case CXCursor_OMPTargetParallelForDirective: + return cxstring::createRef("OMPTargetParallelForDirective"); + case CXCursor_OMPTargetUpdateDirective: + return cxstring::createRef("OMPTargetUpdateDirective"); case CXCursor_OMPTeamsDirective: return cxstring::createRef("OMPTeamsDirective"); case CXCursor_OMPCancellationPointDirective: @@ -4522,10 +4871,20 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("OMPTaskLoopSimdDirective"); case CXCursor_OMPDistributeDirective: return cxstring::createRef("OMPDistributeDirective"); + case CXCursor_OMPDistributeParallelForDirective: + return cxstring::createRef("OMPDistributeParallelForDirective"); + case CXCursor_OMPDistributeParallelForSimdDirective: + return cxstring::createRef("OMPDistributeParallelForSimdDirective"); + case CXCursor_OMPDistributeSimdDirective: + return cxstring::createRef("OMPDistributeSimdDirective"); + case CXCursor_OMPTargetParallelForSimdDirective: + return cxstring::createRef("OMPTargetParallelForSimdDirective"); case CXCursor_OverloadCandidate: return cxstring::createRef("OverloadCandidate"); case CXCursor_TypeAliasTemplateDecl: return cxstring::createRef("TypeAliasTemplateDecl"); + case CXCursor_StaticAssert: + return cxstring::createRef("StaticAssert"); } llvm_unreachable("Unhandled CXCursorKind"); @@ -5264,12 +5623,16 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::StaticAssert: case Decl::Block: case Decl::Captured: + case Decl::OMPCapturedExpr: case Decl::Label: // FIXME: Is this right?? case Decl::ClassScopeFunctionSpecialization: case Decl::Import: case Decl::OMPThreadPrivate: + case Decl::OMPDeclareReduction: case Decl::ObjCTypeParam: case Decl::BuiltinTemplate: + case Decl::PragmaComment: + case Decl::PragmaDetectMismatch: return C; // Declaration kinds that don't make any sense here, but are @@ -5347,6 +5710,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { D->getLocation(), TU); case Decl::UsingShadow: + case Decl::ConstructorUsingShadow: return clang_getCursorDefinition( MakeCXCursor(cast<UsingShadowDecl>(D)->getTargetDecl(), TU)); @@ -5570,7 +5934,8 @@ CXSourceRange clang_getCursorReferenceNameRange(CXCursor C, unsigned NameFlags, } void clang_enableStackTraces(void) { - llvm::sys::PrintStackTraceOnErrorSignal(); + // FIXME: Provide an argv0 here so we can find llvm-symbolizer. + llvm::sys::PrintStackTraceOnErrorSignal(StringRef()); } void clang_executeOnThread(void (*fn)(void*), void *user_data, @@ -5968,7 +6333,7 @@ AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) { if (Method->getObjCDeclQualifier()) HasContextSensitiveKeywords = true; else { - for (const auto *P : Method->params()) { + for (const auto *P : Method->parameters()) { if (P->getObjCDeclQualifier()) { HasContextSensitiveKeywords = true; break; @@ -6420,6 +6785,7 @@ static void clang_annotateTokensImpl(CXTranslationUnit TU, ASTUnit *CXXUnit, .Case("setter", true) .Case("strong", true) .Case("weak", true) + .Case("class", true) .Default(false)) Tokens[I].int_data[0] = CXToken_Keyword; } @@ -6866,6 +7232,7 @@ unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C, unsigned reserved) { SET_CXOBJCPROP_ATTR(weak); SET_CXOBJCPROP_ATTR(strong); SET_CXOBJCPROP_ATTR(unsafe_unretained); + SET_CXOBJCPROP_ATTR(class); #undef SET_CXOBJCPROP_ATTR return Result; @@ -7068,6 +7435,48 @@ CXFile clang_Module_getTopLevelHeader(CXTranslationUnit TU, //===----------------------------------------------------------------------===// extern "C" { + +unsigned clang_CXXConstructor_isDefaultConstructor(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXConstructorDecl *Constructor = + D ? dyn_cast_or_null<CXXConstructorDecl>(D->getAsFunction()) : nullptr; + return (Constructor && Constructor->isDefaultConstructor()) ? 1 : 0; +} + +unsigned clang_CXXConstructor_isCopyConstructor(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXConstructorDecl *Constructor = + D ? dyn_cast_or_null<CXXConstructorDecl>(D->getAsFunction()) : nullptr; + return (Constructor && Constructor->isCopyConstructor()) ? 1 : 0; +} + +unsigned clang_CXXConstructor_isMoveConstructor(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXConstructorDecl *Constructor = + D ? dyn_cast_or_null<CXXConstructorDecl>(D->getAsFunction()) : nullptr; + return (Constructor && Constructor->isMoveConstructor()) ? 1 : 0; +} + +unsigned clang_CXXConstructor_isConvertingConstructor(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXConstructorDecl *Constructor = + D ? dyn_cast_or_null<CXXConstructorDecl>(D->getAsFunction()) : nullptr; + // Passing 'false' excludes constructors marked 'explicit'. + return (Constructor && Constructor->isConvertingConstructor(false)) ? 1 : 0; +} + unsigned clang_CXXField_isMutable(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; @@ -7098,6 +7507,16 @@ unsigned clang_CXXMethod_isConst(CXCursor C) { return (Method && (Method->getTypeQualifiers() & Qualifiers::Const)) ? 1 : 0; } +unsigned clang_CXXMethod_isDefaulted(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_or_null<CXXMethodDecl>(D->getAsFunction()) : nullptr; + return (Method && Method->isDefaulted()) ? 1 : 0; +} + unsigned clang_CXXMethod_isStatic(CXCursor C) { if (!clang_isDeclaration(C.kind)) return 0; @@ -7614,3 +8033,10 @@ cxindex::Logger::~Logger() { OS << "--------------------------------------------------\n"; } } + +#ifdef CLANG_TOOL_EXTRA_BUILD +// This anchor is used to force the linker to link the clang-tidy plugin. +extern volatile int ClangTidyPluginAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED ClangTidyPluginAnchorDestination = + ClangTidyPluginAnchorSource; +#endif diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp index d7b65852844e..69d60c9d44f2 100644 --- a/tools/libclang/CIndexUSRs.cpp +++ b/tools/libclang/CIndexUSRs.cpp @@ -137,7 +137,7 @@ CXString clang_constructUSR_ObjCProperty(const char *property, SmallString<128> Buf(getUSRSpacePrefix()); llvm::raw_svector_ostream OS(Buf); OS << extractUSRSuffix(clang_getCString(classUSR)); - generateUSRForObjCProperty(property, OS); + generateUSRForObjCProperty(property, /*isClassProp=*/false, OS); return cxstring::createDup(OS.str()); } diff --git a/tools/libclang/CIndexer.h b/tools/libclang/CIndexer.h index 94c27a05600f..47e30c30ba11 100644 --- a/tools/libclang/CIndexer.h +++ b/tools/libclang/CIndexer.h @@ -20,6 +20,7 @@ #include "clang/Lex/ModuleLoader.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" +#include <utility> #include <vector> namespace llvm { @@ -46,7 +47,8 @@ public: CIndexer(std::shared_ptr<PCHContainerOperations> PCHContainerOps = std::make_shared<PCHContainerOperations>()) : OnlyLocalDecls(false), DisplayDiagnostics(false), - Options(CXGlobalOpt_None), PCHContainerOps(PCHContainerOps) {} + Options(CXGlobalOpt_None), PCHContainerOps(std::move(PCHContainerOps)) { + } /// \brief Whether we only want to see "local" declarations (that did not /// come from a previous precompiled header). If false, we want to see all diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 5267c02db5f4..630be124660d 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -11,17 +11,14 @@ set(SOURCES CIndexer.cpp CXComment.cpp CXCursor.cpp + CXIndexDataConsumer.cpp CXCompilationDatabase.cpp CXLoadedDiagnostic.cpp CXSourceLocation.cpp CXStoredDiagnostic.cpp CXString.cpp CXType.cpp - IndexBody.cpp - IndexDecl.cpp - IndexTypeSourceInfo.cpp Indexing.cpp - IndexingContext.cpp ADDITIONAL_HEADERS CIndexDiagnostic.h @@ -33,7 +30,6 @@ set(SOURCES CXTranslationUnit.h CXType.h Index_Internal.h - IndexingContext.h ../../include/clang-c/Index.h ) @@ -51,6 +47,11 @@ if (CLANG_ENABLE_ARCMT) list(APPEND LIBS clangARCMigrate) endif () +if (TARGET clangTidyPlugin) + add_definitions(-DCLANG_TOOL_EXTRA_BUILD) + list(APPEND LIBS clangTidyPlugin) +endif () + find_library(DL_LIBRARY_PATH dl) if (DL_LIBRARY_PATH) list(APPEND LIBS dl) @@ -115,3 +116,25 @@ if(ENABLE_SHARED) DEFINE_SYMBOL _CINDEX_LIB_) endif() endif() + +if(INTERNAL_INSTALL_PREFIX) + set(LIBCLANG_HEADERS_INSTALL_DESTINATION "${INTERNAL_INSTALL_PREFIX}/include") +else() + set(LIBCLANG_HEADERS_INSTALL_DESTINATION include) +endif() + +install(DIRECTORY ../../include/clang-c + COMPONENT libclang-headers + DESTINATION "${LIBCLANG_HEADERS_INSTALL_DESTINATION}" + FILES_MATCHING + PATTERN "*.h" + PATTERN ".svn" EXCLUDE + ) + +if (NOT CMAKE_CONFIGURATION_TYPES) # don't add this for IDE's. + add_custom_target(install-libclang-headers + DEPENDS + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=libclang-headers + -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") +endif() diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index c766d2d69fa9..19ab0f929c85 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -256,7 +256,6 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::PredefinedExprClass: case Stmt::ShuffleVectorExprClass: case Stmt::ConvertVectorExprClass: - case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::VAArgExprClass: case Stmt::ObjCArrayLiteralClass: case Stmt::ObjCDictionaryLiteralClass: @@ -327,6 +326,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_UnaryOperator; break; + case Stmt::UnaryExprOrTypeTraitExprClass: case Stmt::CXXNoexceptExprClass: K = CXCursor_UnaryExpr; break; @@ -447,7 +447,11 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::ObjCBoolLiteralExprClass: K = CXCursor_ObjCBoolLiteralExpr; break; - + + case Stmt::ObjCAvailabilityCheckExprClass: + K = CXCursor_ObjCAvailabilityCheckExpr; + break; + case Stmt::ObjCBridgedCastExprClass: K = CXCursor_ObjCBridgedCastExpr; break; @@ -504,6 +508,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::CXXMemberCallExprClass: case Stmt::CUDAKernelCallExprClass: case Stmt::CXXConstructExprClass: + case Stmt::CXXInheritedCtorInitExprClass: case Stmt::CXXTemporaryObjectExprClass: case Stmt::CXXUnresolvedConstructExprClass: case Stmt::UserDefinedLiteralClass: @@ -600,6 +605,21 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPTargetDataDirectiveClass: K = CXCursor_OMPTargetDataDirective; break; + case Stmt::OMPTargetEnterDataDirectiveClass: + K = CXCursor_OMPTargetEnterDataDirective; + break; + case Stmt::OMPTargetExitDataDirectiveClass: + K = CXCursor_OMPTargetExitDataDirective; + break; + case Stmt::OMPTargetParallelDirectiveClass: + K = CXCursor_OMPTargetParallelDirective; + break; + case Stmt::OMPTargetParallelForDirectiveClass: + K = CXCursor_OMPTargetParallelForDirective; + break; + case Stmt::OMPTargetUpdateDirectiveClass: + K = CXCursor_OMPTargetUpdateDirective; + break; case Stmt::OMPTeamsDirectiveClass: K = CXCursor_OMPTeamsDirective; break; @@ -618,6 +638,18 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, case Stmt::OMPDistributeDirectiveClass: K = CXCursor_OMPDistributeDirective; break; + case Stmt::OMPDistributeParallelForDirectiveClass: + K = CXCursor_OMPDistributeParallelForDirective; + break; + case Stmt::OMPDistributeParallelForSimdDirectiveClass: + K = CXCursor_OMPDistributeParallelForSimdDirective; + break; + case Stmt::OMPDistributeSimdDirectiveClass: + K = CXCursor_OMPDistributeSimdDirective; + break; + case Stmt::OMPTargetParallelForSimdDirectiveClass: + K = CXCursor_OMPTargetParallelForSimdDirective; + break; } CXCursor C = { K, 0, { Parent, S, TU } }; diff --git a/tools/libclang/IndexingContext.cpp b/tools/libclang/CXIndexDataConsumer.cpp index f7640c63e05c..59fa92bb21e2 100644 --- a/tools/libclang/IndexingContext.cpp +++ b/tools/libclang/CXIndexDataConsumer.cpp @@ -1,4 +1,4 @@ -//===- IndexingContext.cpp - Higher level API functions -------------------===// +//===- CXIndexDataConsumer.cpp - Index data consumer for libclang----------===// // // The LLVM Compiler Infrastructure // @@ -7,21 +7,233 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" +#include "CXIndexDataConsumer.h" #include "CIndexDiagnostic.h" #include "CXTranslationUnit.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" using namespace clang; +using namespace clang::index; using namespace cxindex; using namespace cxcursor; -IndexingContext::ObjCProtocolListInfo::ObjCProtocolListInfo( +namespace { +class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> { + CXIndexDataConsumer &DataConsumer; + SourceLocation DeclLoc; + const DeclContext *LexicalDC; + +public: + IndexingDeclVisitor(CXIndexDataConsumer &dataConsumer, SourceLocation Loc, + const DeclContext *lexicalDC) + : DataConsumer(dataConsumer), DeclLoc(Loc), LexicalDC(lexicalDC) { } + + bool VisitFunctionDecl(const FunctionDecl *D) { + DataConsumer.handleFunction(D); + return true; + } + + bool VisitVarDecl(const VarDecl *D) { + DataConsumer.handleVar(D); + return true; + } + + bool VisitFieldDecl(const FieldDecl *D) { + DataConsumer.handleField(D); + return true; + } + + bool VisitMSPropertyDecl(const MSPropertyDecl *D) { + return true; + } + + bool VisitEnumConstantDecl(const EnumConstantDecl *D) { + DataConsumer.handleEnumerator(D); + return true; + } + + bool VisitTypedefNameDecl(const TypedefNameDecl *D) { + DataConsumer.handleTypedefName(D); + return true; + } + + bool VisitTagDecl(const TagDecl *D) { + DataConsumer.handleTagDecl(D); + return true; + } + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { + DataConsumer.handleObjCInterface(D); + return true; + } + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { + DataConsumer.handleObjCProtocol(D); + return true; + } + + bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { + DataConsumer.handleObjCImplementation(D); + return true; + } + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { + DataConsumer.handleObjCCategory(D); + return true; + } + + bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { + DataConsumer.handleObjCCategoryImpl(D); + return true; + } + + bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { + if (isa<ObjCImplDecl>(LexicalDC) && !D->isThisDeclarationADefinition()) + DataConsumer.handleSynthesizedObjCMethod(D, DeclLoc, LexicalDC); + else + DataConsumer.handleObjCMethod(D); + return true; + } + + bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { + DataConsumer.handleObjCProperty(D); + return true; + } + + bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { + DataConsumer.handleSynthesizedObjCProperty(D); + return true; + } + + bool VisitNamespaceDecl(const NamespaceDecl *D) { + DataConsumer.handleNamespace(D); + return true; + } + + bool VisitUsingDecl(const UsingDecl *D) { + return true; + } + + bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { + return true; + } + + bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { + DataConsumer.handleClassTemplate(D); + return true; + } + + bool VisitClassTemplateSpecializationDecl(const + ClassTemplateSpecializationDecl *D) { + DataConsumer.handleTagDecl(D); + return true; + } + + bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { + DataConsumer.handleFunctionTemplate(D); + return true; + } + + bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { + DataConsumer.handleTypeAliasTemplate(D); + return true; + } + + bool VisitImportDecl(const ImportDecl *D) { + DataConsumer.importedModule(D); + return true; + } +}; +} + +bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D, + SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) { + SourceLocation Loc = getASTContext().getSourceManager() + .getLocForStartOfFile(FID).getLocWithOffset(Offset); + + if (Roles & (unsigned)SymbolRole::Reference) { + const NamedDecl *ND = dyn_cast<NamedDecl>(D); + if (!ND) + return true; + + if (auto *ObjCID = dyn_cast_or_null<ObjCInterfaceDecl>(ASTNode.OrigD)) { + if (!ObjCID->isThisDeclarationADefinition() && + ObjCID->getLocation() == Loc) { + // The libclang API treats this as ObjCClassRef declaration. + IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCID); + return true; + } + } + if (auto *ObjCPD = dyn_cast_or_null<ObjCProtocolDecl>(ASTNode.OrigD)) { + if (!ObjCPD->isThisDeclarationADefinition() && + ObjCPD->getLocation() == Loc) { + // The libclang API treats this as ObjCProtocolRef declaration. + IndexingDeclVisitor(*this, Loc, nullptr).Visit(ObjCPD); + return true; + } + } + + CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct; + if (Roles & (unsigned)SymbolRole::Implicit) { + Kind = CXIdxEntityRef_Implicit; + } + + CXCursor Cursor; + if (ASTNode.OrigE) { + Cursor = cxcursor::MakeCXCursor(ASTNode.OrigE, + cast<Decl>(ASTNode.ContainerDC), + getCXTU()); + } else { + if (ASTNode.OrigD) { + if (auto *OrigND = dyn_cast<NamedDecl>(ASTNode.OrigD)) + Cursor = getRefCursor(OrigND, Loc); + else + Cursor = MakeCXCursor(ASTNode.OrigD, CXTU); + } else { + Cursor = getRefCursor(ND, Loc); + } + } + handleReference(ND, Loc, Cursor, + dyn_cast_or_null<NamedDecl>(ASTNode.Parent), + ASTNode.ContainerDC, ASTNode.OrigE, Kind); + + } else { + const DeclContext *LexicalDC = ASTNode.ContainerDC; + if (!LexicalDC) { + for (const auto &SymRel : Relations) { + if (SymRel.Roles & (unsigned)SymbolRole::RelationChildOf) + LexicalDC = dyn_cast<DeclContext>(SymRel.RelatedSymbol); + } + } + IndexingDeclVisitor(*this, Loc, LexicalDC).Visit(ASTNode.OrigD); + } + + return !shouldAbort(); +} + +bool CXIndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD, + SymbolRoleSet Roles, + FileID FID, + unsigned Offset) { + IndexingDeclVisitor(*this, SourceLocation(), nullptr).Visit(ImportD); + return !shouldAbort(); +} + +void CXIndexDataConsumer::finish() { + indexDiagnostics(); +} + + +CXIndexDataConsumer::ObjCProtocolListInfo::ObjCProtocolListInfo( const ObjCProtocolList &ProtList, - IndexingContext &IdxCtx, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); for (ObjCInterfaceDecl::protocol_iterator @@ -61,7 +273,7 @@ IBOutletCollectionInfo::IBOutletCollectionInfo( IBCollInfo.objcClass = nullptr; } -AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) +AttrListInfo::AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx) : SA(IdxCtx), ref_cnt(0) { if (!D->hasAttrs()) @@ -114,14 +326,14 @@ AttrListInfo::AttrListInfo(const Decl *D, IndexingContext &IdxCtx) } IntrusiveRefCntPtr<AttrListInfo> -AttrListInfo::create(const Decl *D, IndexingContext &IdxCtx) { +AttrListInfo::create(const Decl *D, CXIndexDataConsumer &IdxCtx) { ScratchAlloc SA(IdxCtx); AttrListInfo *attrs = SA.allocate<AttrListInfo>(); return new (attrs) AttrListInfo(D, IdxCtx); } -IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, - IndexingContext &IdxCtx, +CXIndexDataConsumer::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA) { for (const auto &Base : D->bases()) { BaseEntities.push_back(EntityInfo()); @@ -155,7 +367,7 @@ IndexingContext::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, CXBases.push_back(&BaseInfos[i]); } -SourceLocation IndexingContext::CXXBasesListInfo::getBaseLoc( +SourceLocation CXIndexDataConsumer::CXXBasesListInfo::getBaseLoc( const CXXBaseSpecifier &Base) const { SourceLocation Loc = Base.getSourceRange().getBegin(); TypeLoc TL; @@ -193,16 +405,16 @@ const char *ScratchAlloc::copyCStr(StringRef Str) { return buf; } -void IndexingContext::setASTContext(ASTContext &ctx) { +void CXIndexDataConsumer::setASTContext(ASTContext &ctx) { Ctx = &ctx; cxtu::getASTUnit(CXTU)->setASTContext(&ctx); } -void IndexingContext::setPreprocessor(Preprocessor &PP) { +void CXIndexDataConsumer::setPreprocessor(Preprocessor &PP) { cxtu::getASTUnit(CXTU)->setPreprocessor(&PP); } -bool IndexingContext::isFunctionLocalDecl(const Decl *D) { +bool CXIndexDataConsumer::isFunctionLocalDecl(const Decl *D) { assert(D); if (!D->getParentFunctionOrMethod()) @@ -224,13 +436,13 @@ bool IndexingContext::isFunctionLocalDecl(const Decl *D) { return true; } -bool IndexingContext::shouldAbort() { +bool CXIndexDataConsumer::shouldAbort() { if (!CB.abortQuery) return false; return CB.abortQuery(ClientData, nullptr); } -void IndexingContext::enteredMainFile(const FileEntry *File) { +void CXIndexDataConsumer::enteredMainFile(const FileEntry *File) { if (File && CB.enteredMainFile) { CXIdxClientFile idxFile = CB.enteredMainFile(ClientData, @@ -240,7 +452,7 @@ void IndexingContext::enteredMainFile(const FileEntry *File) { } } -void IndexingContext::ppIncludedFile(SourceLocation hashLoc, +void CXIndexDataConsumer::ppIncludedFile(SourceLocation hashLoc, StringRef filename, const FileEntry *File, bool isImport, bool isAngled, @@ -258,7 +470,7 @@ void IndexingContext::ppIncludedFile(SourceLocation hashLoc, FileMap[File] = idxFile; } -void IndexingContext::importedModule(const ImportDecl *ImportD) { +void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) { if (!CB.importedASTFile) return; @@ -277,7 +489,7 @@ void IndexingContext::importedModule(const ImportDecl *ImportD) { (void)astFile; } -void IndexingContext::importedPCH(const FileEntry *File) { +void CXIndexDataConsumer::importedPCH(const FileEntry *File) { if (!CB.importedASTFile) return; @@ -292,24 +504,33 @@ void IndexingContext::importedPCH(const FileEntry *File) { (void)astFile; } -void IndexingContext::startedTranslationUnit() { +void CXIndexDataConsumer::startedTranslationUnit() { CXIdxClientContainer idxCont = nullptr; if (CB.startedTranslationUnit) idxCont = CB.startedTranslationUnit(ClientData, nullptr); addContainerInMap(Ctx->getTranslationUnitDecl(), idxCont); } -void IndexingContext::handleDiagnosticSet(CXDiagnostic CXDiagSet) { +void CXIndexDataConsumer::indexDiagnostics() { + if (!hasDiagnosticCallback()) + return; + + CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(getCXTU()); + handleDiagnosticSet(DiagSet); +} + +void CXIndexDataConsumer::handleDiagnosticSet(CXDiagnostic CXDiagSet) { if (!CB.diagnostic) return; CB.diagnostic(ClientData, CXDiagSet, nullptr); } -bool IndexingContext::handleDecl(const NamedDecl *D, +bool CXIndexDataConsumer::handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, - const DeclContext *LexicalDC) { + const DeclContext *LexicalDC, + const DeclContext *SemaDC) { if (!CB.indexDeclaration || !D) return false; if (D->isImplicit() && shouldIgnoreIfImplicit(D)) @@ -335,10 +556,12 @@ bool IndexingContext::handleDecl(const NamedDecl *D, DInfo.attributes = DInfo.EntInfo.attributes; DInfo.numAttributes = DInfo.EntInfo.numAttributes; - getContainerInfo(D->getDeclContext(), DInfo.SemanticContainer); + if (!SemaDC) + SemaDC = D->getDeclContext(); + getContainerInfo(SemaDC, DInfo.SemanticContainer); DInfo.semanticContainer = &DInfo.SemanticContainer; - if (LexicalDC == D->getDeclContext()) { + if (LexicalDC == SemaDC) { DInfo.lexicalContainer = &DInfo.SemanticContainer; } else if (isTemplateImplicitInstantiation(D)) { // Implicit instantiations have the lexical context of where they were @@ -362,14 +585,14 @@ bool IndexingContext::handleDecl(const NamedDecl *D, return true; } -bool IndexingContext::handleObjCContainer(const ObjCContainerDecl *D, +bool CXIndexDataConsumer::handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, ObjCContainerDeclInfo &ContDInfo) { ContDInfo.ObjCContDeclInfo.declInfo = &ContDInfo; return handleDecl(D, Loc, Cursor, ContDInfo); } -bool IndexingContext::handleFunction(const FunctionDecl *D) { +bool CXIndexDataConsumer::handleFunction(const FunctionDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; @@ -385,31 +608,31 @@ bool IndexingContext::handleFunction(const FunctionDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleVar(const VarDecl *D) { +bool CXIndexDataConsumer::handleVar(const VarDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleField(const FieldDecl *D) { +bool CXIndexDataConsumer::handleField(const FieldDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleMSProperty(const MSPropertyDecl *D) { +bool CXIndexDataConsumer::handleMSProperty(const MSPropertyDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleEnumerator(const EnumConstantDecl *D) { +bool CXIndexDataConsumer::handleEnumerator(const EnumConstantDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTagDecl(const TagDecl *D) { +bool CXIndexDataConsumer::handleTagDecl(const TagDecl *D) { if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(D)) return handleCXXRecordDecl(CXXRD, D); @@ -418,13 +641,13 @@ bool IndexingContext::handleTagDecl(const TagDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTypedefName(const TypedefNameDecl *D) { +bool CXIndexDataConsumer::handleTypedefName(const TypedefNameDecl *D) { DeclInfo DInfo(!D->isFirstDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { +bool CXIndexDataConsumer::handleObjCInterface(const ObjCInterfaceDecl *D) { // For @class forward declarations, suppress them the same way as references. if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) @@ -472,7 +695,7 @@ bool IndexingContext::handleObjCInterface(const ObjCInterfaceDecl *D) { return handleObjCContainer(D, D->getLocation(), getCursor(D), InterInfo); } -bool IndexingContext::handleObjCImplementation( +bool CXIndexDataConsumer::handleObjCImplementation( const ObjCImplementationDecl *D) { ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/false, /*isRedeclaration=*/true, @@ -480,7 +703,7 @@ bool IndexingContext::handleObjCImplementation( return handleObjCContainer(D, D->getLocation(), getCursor(D), ContDInfo); } -bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { +bool CXIndexDataConsumer::handleObjCProtocol(const ObjCProtocolDecl *D) { if (!D->isThisDeclarationADefinition()) { if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) return false; // already occurred. @@ -509,7 +732,7 @@ bool IndexingContext::handleObjCProtocol(const ObjCProtocolDecl *D) { return handleObjCContainer(D, D->getLocation(), getCursor(D), ProtInfo); } -bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { +bool CXIndexDataConsumer::handleObjCCategory(const ObjCCategoryDecl *D) { ScratchAlloc SA(*this); ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/false); @@ -541,7 +764,7 @@ bool IndexingContext::handleObjCCategory(const ObjCCategoryDecl *D) { return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } -bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { +bool CXIndexDataConsumer::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { ScratchAlloc SA(*this); const ObjCCategoryDecl *CatD = D->getCategoryDecl(); @@ -570,7 +793,7 @@ bool IndexingContext::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); } -bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { +bool CXIndexDataConsumer::handleObjCMethod(const ObjCMethodDecl *D) { bool isDef = D->isThisDeclarationADefinition(); bool isContainer = isDef; bool isSkipped = false; @@ -586,22 +809,23 @@ bool IndexingContext::handleObjCMethod(const ObjCMethodDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleSynthesizedObjCProperty( +bool CXIndexDataConsumer::handleSynthesizedObjCProperty( const ObjCPropertyImplDecl *D) { ObjCPropertyDecl *PD = D->getPropertyDecl(); - return handleReference(PD, D->getLocation(), getCursor(D), nullptr, - D->getDeclContext()); + auto *DC = D->getDeclContext(); + return handleReference(PD, D->getLocation(), getCursor(D), + dyn_cast<NamedDecl>(DC), DC); } -bool IndexingContext::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, +bool CXIndexDataConsumer::handleSynthesizedObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc, const DeclContext *LexicalDC) { DeclInfo DInfo(/*isRedeclaration=*/true, /*isDefinition=*/true, /*isContainer=*/false); - return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC); + return handleDecl(D, Loc, getCursor(D), DInfo, LexicalDC, D->getDeclContext()); } -bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { +bool CXIndexDataConsumer::handleObjCProperty(const ObjCPropertyDecl *D) { ScratchAlloc SA(*this); ObjCPropertyDeclInfo DInfo; @@ -626,31 +850,31 @@ bool IndexingContext::handleObjCProperty(const ObjCPropertyDecl *D) { return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleNamespace(const NamespaceDecl *D) { +bool CXIndexDataConsumer::handleNamespace(const NamespaceDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isOriginalNamespace(), /*isDefinition=*/true, /*isContainer=*/true); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleClassTemplate(const ClassTemplateDecl *D) { +bool CXIndexDataConsumer::handleClassTemplate(const ClassTemplateDecl *D) { return handleCXXRecordDecl(D->getTemplatedDecl(), D); } -bool IndexingContext::handleFunctionTemplate(const FunctionTemplateDecl *D) { +bool CXIndexDataConsumer::handleFunctionTemplate(const FunctionTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/D->isThisDeclarationADefinition(), /*isContainer=*/D->isThisDeclarationADefinition()); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { +bool CXIndexDataConsumer::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), /*isDefinition=*/true, /*isContainer=*/false); return handleDecl(D, D->getLocation(), getCursor(D), DInfo); } -bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, const NamedDecl *Parent, const DeclContext *DC, const Expr *E, @@ -663,7 +887,7 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, return handleReference(D, Loc, Cursor, Parent, DC, E, Kind); } -bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, const DeclContext *DC, @@ -709,7 +933,7 @@ bool IndexingContext::handleReference(const NamedDecl *D, SourceLocation Loc, return true; } -bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { +bool CXIndexDataConsumer::isNotFromSourceFile(SourceLocation Loc) const { if (Loc.isInvalid()) return true; SourceManager &SM = Ctx->getSourceManager(); @@ -718,7 +942,7 @@ bool IndexingContext::isNotFromSourceFile(SourceLocation Loc) const { return SM.getFileEntryForID(FID) == nullptr; } -void IndexingContext::addContainerInMap(const DeclContext *DC, +void CXIndexDataConsumer::addContainerInMap(const DeclContext *DC, CXIdxClientContainer container) { if (!DC) return; @@ -737,7 +961,7 @@ void IndexingContext::addContainerInMap(const DeclContext *DC, ContainerMap.erase(I); } -CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { +CXIdxClientEntity CXIndexDataConsumer::getClientEntity(const Decl *D) const { if (!D) return nullptr; EntityMapTy::const_iterator I = EntityMap.find(D); @@ -746,13 +970,13 @@ CXIdxClientEntity IndexingContext::getClientEntity(const Decl *D) const { return I->second; } -void IndexingContext::setClientEntity(const Decl *D, CXIdxClientEntity client) { +void CXIndexDataConsumer::setClientEntity(const Decl *D, CXIdxClientEntity client) { if (!D) return; EntityMap[D] = client; } -bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, +bool CXIndexDataConsumer::handleCXXRecordDecl(const CXXRecordDecl *RD, const NamedDecl *OrigD) { if (RD->isThisDeclarationADefinition()) { ScratchAlloc SA(*this); @@ -785,7 +1009,7 @@ bool IndexingContext::handleCXXRecordDecl(const CXXRecordDecl *RD, return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), DInfo); } -bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, +bool CXIndexDataConsumer::markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc) { if (!D || Loc.isInvalid()) return true; @@ -807,7 +1031,7 @@ bool IndexingContext::markEntityOccurrenceInFile(const NamedDecl *D, return !res.second; // already in map } -const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { +const NamedDecl *CXIndexDataConsumer::getEntityDecl(const NamedDecl *D) const { assert(D); D = cast<NamedDecl>(D->getCanonicalDecl()); @@ -830,7 +1054,7 @@ const NamedDecl *IndexingContext::getEntityDecl(const NamedDecl *D) const { } const DeclContext * -IndexingContext::getEntityContainer(const Decl *D) const { +CXIndexDataConsumer::getEntityContainer(const Decl *D) const { const DeclContext *DC = dyn_cast<DeclContext>(D); if (DC) return DC; @@ -846,7 +1070,7 @@ IndexingContext::getEntityContainer(const Decl *D) const { } CXIdxClientContainer -IndexingContext::getClientContainerForDC(const DeclContext *DC) const { +CXIndexDataConsumer::getClientContainerForDC(const DeclContext *DC) const { if (!DC) return nullptr; @@ -857,7 +1081,7 @@ IndexingContext::getClientContainerForDC(const DeclContext *DC) const { return I->second; } -CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { +CXIdxClientFile CXIndexDataConsumer::getIndexFile(const FileEntry *File) { if (!File) return nullptr; @@ -868,17 +1092,17 @@ CXIdxClientFile IndexingContext::getIndexFile(const FileEntry *File) { return nullptr; } -CXIdxLoc IndexingContext::getIndexLoc(SourceLocation Loc) const { +CXIdxLoc CXIndexDataConsumer::getIndexLoc(SourceLocation Loc) const { CXIdxLoc idxLoc = { {nullptr, nullptr}, 0 }; if (Loc.isInvalid()) return idxLoc; - idxLoc.ptr_data[0] = const_cast<IndexingContext *>(this); + idxLoc.ptr_data[0] = const_cast<CXIndexDataConsumer *>(this); idxLoc.int_data = Loc.getRawEncoding(); return idxLoc; } -void IndexingContext::translateLoc(SourceLocation Loc, +void CXIndexDataConsumer::translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset) { @@ -908,7 +1132,12 @@ void IndexingContext::translateLoc(SourceLocation Loc, *offset = FileOffset; } -void IndexingContext::getEntityInfo(const NamedDecl *D, +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolSubKinds(SymbolSubKindSet K); +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); + +void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, EntityInfo &EntityInfo, ScratchAlloc &SA) { if (!D) @@ -918,9 +1147,11 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, EntityInfo.cursor = getCursor(D); EntityInfo.Dcl = D; EntityInfo.IndexCtx = this; - EntityInfo.kind = CXIdxEntity_Unexposed; - EntityInfo.templateKind = CXIdxEntity_NonTemplate; - EntityInfo.lang = CXIdxEntityLang_C; + + SymbolInfo SymInfo = getSymbolInfo(D); + EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); + EntityInfo.templateKind = getEntityKindFromSymbolSubKinds(SymInfo.SubKinds); + EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); if (D->hasAttrs()) { EntityInfo.AttrList = AttrListInfo::create(D, *this); @@ -928,167 +1159,9 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, EntityInfo.numAttributes = EntityInfo.AttrList->getNumAttrs(); } - if (const TagDecl *TD = dyn_cast<TagDecl>(D)) { - switch (TD->getTagKind()) { - case TTK_Struct: - EntityInfo.kind = CXIdxEntity_Struct; break; - case TTK_Union: - EntityInfo.kind = CXIdxEntity_Union; break; - case TTK_Class: - EntityInfo.kind = CXIdxEntity_CXXClass; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case TTK_Interface: - EntityInfo.kind = CXIdxEntity_CXXInterface; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case TTK_Enum: - EntityInfo.kind = CXIdxEntity_Enum; break; - } - - if (const CXXRecordDecl *CXXRec = dyn_cast<CXXRecordDecl>(D)) - if (!CXXRec->isCLike()) - EntityInfo.lang = CXIdxEntityLang_CXX; - - if (isa<ClassTemplatePartialSpecializationDecl>(D)) { - EntityInfo.templateKind = CXIdxEntity_TemplatePartialSpecialization; - } else if (isa<ClassTemplateSpecializationDecl>(D)) { - EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; - } - - } else { - switch (D->getKind()) { - case Decl::Typedef: - EntityInfo.kind = CXIdxEntity_Typedef; break; - case Decl::Function: - EntityInfo.kind = CXIdxEntity_Function; - break; - case Decl::ParmVar: - EntityInfo.kind = CXIdxEntity_Variable; - break; - case Decl::Var: - EntityInfo.kind = CXIdxEntity_Variable; - if (isa<CXXRecordDecl>(D->getDeclContext())) { - EntityInfo.kind = CXIdxEntity_CXXStaticVariable; - EntityInfo.lang = CXIdxEntityLang_CXX; - } - break; - case Decl::Field: - EntityInfo.kind = CXIdxEntity_Field; - if (const CXXRecordDecl * - CXXRec = dyn_cast<CXXRecordDecl>(D->getDeclContext())) { - // FIXME: isPOD check is not sufficient, a POD can contain methods, - // we want a isCStructLike check. - if (!CXXRec->isPOD()) - EntityInfo.lang = CXIdxEntityLang_CXX; - } - break; - case Decl::EnumConstant: - EntityInfo.kind = CXIdxEntity_EnumConstant; break; - case Decl::ObjCInterface: - EntityInfo.kind = CXIdxEntity_ObjCClass; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCProtocol: - EntityInfo.kind = CXIdxEntity_ObjCProtocol; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCCategory: - EntityInfo.kind = CXIdxEntity_ObjCCategory; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCMethod: - if (cast<ObjCMethodDecl>(D)->isInstanceMethod()) - EntityInfo.kind = CXIdxEntity_ObjCInstanceMethod; - else - EntityInfo.kind = CXIdxEntity_ObjCClassMethod; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCProperty: - EntityInfo.kind = CXIdxEntity_ObjCProperty; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::ObjCIvar: - EntityInfo.kind = CXIdxEntity_ObjCIvar; - EntityInfo.lang = CXIdxEntityLang_ObjC; - break; - case Decl::Namespace: - EntityInfo.kind = CXIdxEntity_CXXNamespace; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::NamespaceAlias: - EntityInfo.kind = CXIdxEntity_CXXNamespaceAlias; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXConstructor: - EntityInfo.kind = CXIdxEntity_CXXConstructor; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXDestructor: - EntityInfo.kind = CXIdxEntity_CXXDestructor; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXConversion: - EntityInfo.kind = CXIdxEntity_CXXConversionFunction; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - case Decl::CXXMethod: { - const CXXMethodDecl *MD = cast<CXXMethodDecl>(D); - if (MD->isStatic()) - EntityInfo.kind = CXIdxEntity_CXXStaticMethod; - else - EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - } - case Decl::ClassTemplate: - EntityInfo.kind = CXIdxEntity_CXXClass; - EntityInfo.templateKind = CXIdxEntity_Template; - break; - case Decl::FunctionTemplate: - EntityInfo.kind = CXIdxEntity_Function; - EntityInfo.templateKind = CXIdxEntity_Template; - if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>( - cast<FunctionTemplateDecl>(D)->getTemplatedDecl())) { - if (isa<CXXConstructorDecl>(MD)) - EntityInfo.kind = CXIdxEntity_CXXConstructor; - else if (isa<CXXDestructorDecl>(MD)) - EntityInfo.kind = CXIdxEntity_CXXDestructor; - else if (isa<CXXConversionDecl>(MD)) - EntityInfo.kind = CXIdxEntity_CXXConversionFunction; - else { - if (MD->isStatic()) - EntityInfo.kind = CXIdxEntity_CXXStaticMethod; - else - EntityInfo.kind = CXIdxEntity_CXXInstanceMethod; - } - } - break; - case Decl::TypeAliasTemplate: - EntityInfo.kind = CXIdxEntity_CXXTypeAlias; - EntityInfo.templateKind = CXIdxEntity_Template; - break; - case Decl::TypeAlias: - EntityInfo.kind = CXIdxEntity_CXXTypeAlias; - EntityInfo.lang = CXIdxEntityLang_CXX; - break; - default: - break; - } - } - if (EntityInfo.kind == CXIdxEntity_Unexposed) return; - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - if (FD->getTemplatedKind() == - FunctionDecl::TK_FunctionTemplateSpecialization) - EntityInfo.templateKind = CXIdxEntity_TemplateSpecialization; - } - - if (EntityInfo.templateKind != CXIdxEntity_NonTemplate) - EntityInfo.lang = CXIdxEntityLang_CXX; - if (IdentifierInfo *II = D->getIdentifier()) { EntityInfo.name = SA.toCStr(II->getName()); @@ -1115,14 +1188,14 @@ void IndexingContext::getEntityInfo(const NamedDecl *D, } } -void IndexingContext::getContainerInfo(const DeclContext *DC, +void CXIndexDataConsumer::getContainerInfo(const DeclContext *DC, ContainerInfo &ContInfo) { ContInfo.cursor = getCursor(cast<Decl>(DC)); ContInfo.DC = DC; ContInfo.IndexCtx = this; } -CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { +CXCursor CXIndexDataConsumer::getRefCursor(const NamedDecl *D, SourceLocation Loc) { if (const TypeDecl *TD = dyn_cast<TypeDecl>(D)) return MakeCursorTypeRef(TD, Loc, CXTU); if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) @@ -1143,7 +1216,7 @@ CXCursor IndexingContext::getRefCursor(const NamedDecl *D, SourceLocation Loc) { return clang_getNullCursor(); } -bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { +bool CXIndexDataConsumer::shouldIgnoreIfImplicit(const Decl *D) { if (isa<ObjCInterfaceDecl>(D)) return false; if (isa<ObjCCategoryDecl>(D)) @@ -1157,7 +1230,7 @@ bool IndexingContext::shouldIgnoreIfImplicit(const Decl *D) { return true; } -bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { +bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { if (const ClassTemplateSpecializationDecl * SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { return SD->getSpecializationKind() == TSK_ImplicitInstantiation; @@ -1167,3 +1240,71 @@ bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) { } return false; } + +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage Lang) { + switch (K) { + case SymbolKind::Unknown: + case SymbolKind::Module: + case SymbolKind::Macro: + case SymbolKind::ClassProperty: + return CXIdxEntity_Unexposed; + + case SymbolKind::Enum: return CXIdxEntity_Enum; + case SymbolKind::Struct: return CXIdxEntity_Struct; + case SymbolKind::Union: return CXIdxEntity_Union; + case SymbolKind::TypeAlias: + if (Lang == SymbolLanguage::CXX) + return CXIdxEntity_CXXTypeAlias; + return CXIdxEntity_Typedef; + case SymbolKind::Function: return CXIdxEntity_Function; + case SymbolKind::Variable: return CXIdxEntity_Variable; + case SymbolKind::Field: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCIvar; + return CXIdxEntity_Field; + case SymbolKind::EnumConstant: return CXIdxEntity_EnumConstant; + case SymbolKind::Class: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCClass; + return CXIdxEntity_CXXClass; + case SymbolKind::Protocol: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCProtocol; + return CXIdxEntity_CXXInterface; + case SymbolKind::Extension: return CXIdxEntity_ObjCCategory; + case SymbolKind::InstanceMethod: + if (Lang == SymbolLanguage::ObjC) + return CXIdxEntity_ObjCInstanceMethod; + return CXIdxEntity_CXXInstanceMethod; + case SymbolKind::ClassMethod: return CXIdxEntity_ObjCClassMethod; + case SymbolKind::StaticMethod: return CXIdxEntity_CXXStaticMethod; + case SymbolKind::InstanceProperty: return CXIdxEntity_ObjCProperty; + case SymbolKind::StaticProperty: return CXIdxEntity_CXXStaticVariable; + case SymbolKind::Namespace: return CXIdxEntity_CXXNamespace; + case SymbolKind::NamespaceAlias: return CXIdxEntity_CXXNamespaceAlias; + case SymbolKind::Constructor: return CXIdxEntity_CXXConstructor; + case SymbolKind::Destructor: return CXIdxEntity_CXXDestructor; + case SymbolKind::ConversionFunction: return CXIdxEntity_CXXConversionFunction; + } + llvm_unreachable("invalid symbol kind"); +} + +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolSubKinds(SymbolSubKindSet K) { + if (K & (unsigned)SymbolSubKind::TemplatePartialSpecialization) + return CXIdxEntity_TemplatePartialSpecialization; + if (K & (unsigned)SymbolSubKind::TemplateSpecialization) + return CXIdxEntity_TemplateSpecialization; + if (K & (unsigned)SymbolSubKind::Generic) + return CXIdxEntity_Template; + return CXIdxEntity_NonTemplate; +} + +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L) { + switch (L) { + case SymbolLanguage::C: return CXIdxEntityLang_C; + case SymbolLanguage::ObjC: return CXIdxEntityLang_ObjC; + case SymbolLanguage::CXX: return CXIdxEntityLang_CXX; + } + llvm_unreachable("invalid symbol language"); +} diff --git a/tools/libclang/IndexingContext.h b/tools/libclang/CXIndexDataConsumer.h index 4da6aebaf648..308fa79488de 100644 --- a/tools/libclang/IndexingContext.h +++ b/tools/libclang/CXIndexDataConsumer.h @@ -1,4 +1,4 @@ -//===- IndexingContext.h - Higher level API functions -----------*- C++ -*-===// +//===- CXIndexDataConsumer.h - Index data consumer for libclang--*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -7,11 +7,12 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_TOOLS_LIBCLANG_INDEXINGCONTEXT_H -#define LLVM_CLANG_TOOLS_LIBCLANG_INDEXINGCONTEXT_H +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXINDEXDATACONSUMER_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXINDEXDATACONSUMER_H #include "CXCursor.h" #include "Index_Internal.h" +#include "clang/Index/IndexDataConsumer.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/DeclObjC.h" #include "llvm/ADT/DenseSet.h" @@ -27,14 +28,14 @@ namespace clang { class ClassTemplateSpecializationDecl; namespace cxindex { - class IndexingContext; + class CXIndexDataConsumer; class AttrListInfo; class ScratchAlloc { - IndexingContext &IdxCtx; + CXIndexDataConsumer &IdxCtx; public: - explicit ScratchAlloc(IndexingContext &indexCtx); + explicit ScratchAlloc(CXIndexDataConsumer &indexCtx); ScratchAlloc(const ScratchAlloc &SA); ~ScratchAlloc(); @@ -48,7 +49,7 @@ public: struct EntityInfo : public CXIdxEntityInfo { const NamedDecl *Dcl; - IndexingContext *IndexCtx; + CXIndexDataConsumer *IndexCtx; IntrusiveRefCntPtr<AttrListInfo> AttrList; EntityInfo() { @@ -60,7 +61,7 @@ struct EntityInfo : public CXIdxEntityInfo { struct ContainerInfo : public CXIdxContainerInfo { const DeclContext *DC; - IndexingContext *IndexCtx; + CXIndexDataConsumer *IndexCtx; }; struct DeclInfo : public CXIdxDeclInfo { @@ -248,10 +249,10 @@ class AttrListInfo { AttrListInfo(const AttrListInfo &) = delete; void operator=(const AttrListInfo &) = delete; public: - AttrListInfo(const Decl *D, IndexingContext &IdxCtx); + AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx); static IntrusiveRefCntPtr<AttrListInfo> create(const Decl *D, - IndexingContext &IdxCtx); + CXIndexDataConsumer &IdxCtx); const CXIdxAttrInfo *const *getAttrs() const { if (CXAttrs.empty()) @@ -273,7 +274,7 @@ public: } }; -class IndexingContext { +class CXIndexDataConsumer : public index::IndexDataConsumer { ASTContext *Ctx; CXClientData ClientData; IndexerCallbacks &CB; @@ -292,8 +293,6 @@ class IndexingContext { typedef std::pair<const FileEntry *, const Decl *> RefFileOccurrence; llvm::DenseSet<RefFileOccurrence> RefFileOccurrences; - std::deque<DeclGroupRef> TUDeclsInObjCContainer; - llvm::BumpPtrAllocator StrScratch; unsigned StrAdapterCount; friend class ScratchAlloc; @@ -310,7 +309,7 @@ class IndexingContext { } ObjCProtocolListInfo(const ObjCProtocolList &ProtList, - IndexingContext &IdxCtx, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); }; @@ -325,7 +324,7 @@ class IndexingContext { unsigned getNumBases() const { return (unsigned)CXBases.size(); } CXXBasesListInfo(const CXXRecordDecl *D, - IndexingContext &IdxCtx, ScratchAlloc &SA); + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); private: SourceLocation getBaseLoc(const CXXBaseSpecifier &Base) const; @@ -334,13 +333,14 @@ class IndexingContext { friend class AttrListInfo; public: - IndexingContext(CXClientData clientData, IndexerCallbacks &indexCallbacks, + CXIndexDataConsumer(CXClientData clientData, IndexerCallbacks &indexCallbacks, unsigned indexOptions, CXTranslationUnit cxTU) : Ctx(nullptr), ClientData(clientData), CB(indexCallbacks), IndexOptions(indexOptions), CXTU(cxTU), StrScratch(), StrAdapterCount(0) { } ASTContext &getASTContext() const { return *Ctx; } + CXTranslationUnit getCXTU() const { return CXTU; } void setASTContext(ASTContext &ctx); void setPreprocessor(Preprocessor &PP); @@ -393,6 +393,8 @@ public: void indexBody(const Stmt *S, const NamedDecl *Parent, const DeclContext *DC = nullptr); + void indexDiagnostics(); + void handleDiagnosticSet(CXDiagnosticSet CXDiagSet); bool handleFunction(const FunctionDecl *FD); @@ -446,13 +448,8 @@ public: bool isNotFromSourceFile(SourceLocation Loc) const; void indexTopLevelDecl(const Decl *D); - void indexTUDeclsInObjCContainer(); void indexDeclGroupRef(DeclGroupRef DG); - void addTUDeclInObjCContainer(DeclGroupRef DG) { - TUDeclsInObjCContainer.push_back(DG); - } - void translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, unsigned *line, unsigned *column, unsigned *offset); @@ -465,10 +462,22 @@ public: static bool isTemplateImplicitInstantiation(const Decl *D); private: + bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, + ArrayRef<index::SymbolRelation> Relations, + FileID FID, unsigned Offset, + ASTNodeInfo ASTNode) override; + + bool handleModuleOccurence(const ImportDecl *ImportD, + index::SymbolRoleSet Roles, + FileID FID, unsigned Offset) override; + + void finish() override; + bool handleDecl(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, DeclInfo &DInfo, - const DeclContext *LexicalDC = nullptr); + const DeclContext *LexicalDC = nullptr, + const DeclContext *SemaDC = nullptr); bool handleObjCContainer(const ObjCContainerDecl *D, SourceLocation Loc, CXCursor Cursor, @@ -501,7 +510,7 @@ private: static bool shouldIgnoreIfImplicit(const Decl *D); }; -inline ScratchAlloc::ScratchAlloc(IndexingContext &idxCtx) : IdxCtx(idxCtx) { +inline ScratchAlloc::ScratchAlloc(CXIndexDataConsumer &idxCtx) : IdxCtx(idxCtx) { ++IdxCtx.StrAdapterCount; } inline ScratchAlloc::ScratchAlloc(const ScratchAlloc &SA) : IdxCtx(SA.IdxCtx) { diff --git a/tools/libclang/CXSourceLocation.cpp b/tools/libclang/CXSourceLocation.cpp index 64a441e12807..1b7464b25af9 100644 --- a/tools/libclang/CXSourceLocation.cpp +++ b/tools/libclang/CXSourceLocation.cpp @@ -190,7 +190,6 @@ static void createNullLocation(CXFile *file, unsigned *line, *column = 0; if (offset) *offset = 0; - return; } static void createNullLocation(CXString *filename, unsigned *line, @@ -203,7 +202,6 @@ static void createNullLocation(CXString *filename, unsigned *line, *column = 0; if (offset) *offset = 0; - return; } extern "C" { @@ -235,7 +233,6 @@ void clang_getExpansionLocation(CXSourceLocation location, unsigned *line, unsigned *column, unsigned *offset) { - if (!isASTUnitSourceLocation(location)) { CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); return; @@ -276,7 +273,6 @@ void clang_getPresumedLocation(CXSourceLocation location, CXString *filename, unsigned *line, unsigned *column) { - if (!isASTUnitSourceLocation(location)) { // Other SourceLocation implementations do not support presumed locations // at this time. @@ -318,7 +314,6 @@ void clang_getSpellingLocation(CXSourceLocation location, unsigned *line, unsigned *column, unsigned *offset) { - if (!isASTUnitSourceLocation(location)) { CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); @@ -356,7 +351,6 @@ void clang_getFileLocation(CXSourceLocation location, unsigned *line, unsigned *column, unsigned *offset) { - if (!isASTUnitSourceLocation(location)) { CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 44bb631f7866..4fcd8864cd37 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -51,6 +51,7 @@ static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) { BTCASE(Float); BTCASE(Double); BTCASE(LongDouble); + BTCASE(Float128); BTCASE(NullPtr); BTCASE(Overload); BTCASE(Dependent); @@ -91,6 +92,7 @@ static CXTypeKind GetTypeKind(QualType T) { TKCASE(Vector); TKCASE(MemberPointer); TKCASE(Auto); + TKCASE(Elaborated); default: return CXType_Unexposed; } @@ -466,6 +468,7 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) { TKIND(Float); TKIND(Double); TKIND(LongDouble); + TKIND(Float128); TKIND(NullPtr); TKIND(Overload); TKIND(Dependent); @@ -491,6 +494,7 @@ CXString clang_getTypeKindSpelling(enum CXTypeKind K) { TKIND(Vector); TKIND(MemberPointer); TKIND(Auto); + TKIND(Elaborated); } #undef TKIND return cxstring::createRef(s); @@ -533,8 +537,11 @@ CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { TCALLINGCONV(AAPCS); TCALLINGCONV(AAPCS_VFP); TCALLINGCONV(IntelOclBicc); + TCALLINGCONV(Swift); + TCALLINGCONV(PreserveMost); + TCALLINGCONV(PreserveAll); case CC_SpirFunction: return CXCallingConv_Unexposed; - case CC_SpirKernel: return CXCallingConv_Unexposed; + case CC_OpenCLKernel: return CXCallingConv_Unexposed; break; } #undef TCALLINGCONV @@ -984,4 +991,14 @@ unsigned clang_Cursor_isAnonymous(CXCursor C){ return 0; } +CXType clang_Type_getNamedType(CXType CT){ + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP && TP->getTypeClass() == Type::Elaborated) + return MakeCXType(cast<ElaboratedType>(TP)->getNamedType(), GetTU(CT)); + + return MakeCXType(QualType(), GetTU(CT)); +} + } // end: extern "C" diff --git a/tools/libclang/CursorVisitor.h b/tools/libclang/CursorVisitor.h index 3e5b0c9120c5..a2dfaeedccc8 100644 --- a/tools/libclang/CursorVisitor.h +++ b/tools/libclang/CursorVisitor.h @@ -238,6 +238,7 @@ public: bool VisitUsingDecl(UsingDecl *D); bool VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D); bool VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D); + bool VisitStaticAssertDecl(StaticAssertDecl *D); // Name visitor bool VisitDeclarationNameInfo(DeclarationNameInfo Name); @@ -264,6 +265,9 @@ public: bool RunVisitorWorkList(VisitorWorkList &WL); void EnqueueWorkList(VisitorWorkList &WL, const Stmt *S); LLVM_ATTRIBUTE_NOINLINE bool Visit(const Stmt *S); + +private: + Optional<bool> handleDeclForVisitation(const Decl *D); }; } diff --git a/tools/libclang/IndexBody.cpp b/tools/libclang/IndexBody.cpp deleted file mode 100644 index 64df4b85beac..000000000000 --- a/tools/libclang/IndexBody.cpp +++ /dev/null @@ -1,177 +0,0 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IndexingContext.h" -#include "clang/AST/RecursiveASTVisitor.h" - -using namespace clang; -using namespace cxindex; - -namespace { - -class BodyIndexer : public RecursiveASTVisitor<BodyIndexer> { - IndexingContext &IndexCtx; - const NamedDecl *Parent; - const DeclContext *ParentDC; - - typedef RecursiveASTVisitor<BodyIndexer> base; -public: - BodyIndexer(IndexingContext &indexCtx, - const NamedDecl *Parent, const DeclContext *DC) - : IndexCtx(indexCtx), Parent(Parent), ParentDC(DC) { } - - bool shouldWalkTypesOfTypeLocs() const { return false; } - - bool TraverseTypeLoc(TypeLoc TL) { - IndexCtx.indexTypeLoc(TL, Parent, ParentDC); - return true; - } - - bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { - IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); - return true; - } - - bool VisitDeclRefExpr(DeclRefExpr *E) { - IndexCtx.handleReference(E->getDecl(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool VisitMemberExpr(MemberExpr *E) { - IndexCtx.handleReference(E->getMemberDecl(), E->getMemberLoc(), - Parent, ParentDC, E); - return true; - } - - bool VisitDesignatedInitExpr(DesignatedInitExpr *E) { - for (DesignatedInitExpr::reverse_designators_iterator - D = E->designators_rbegin(), DEnd = E->designators_rend(); - D != DEnd; ++D) { - if (D->isFieldDesignator()) - IndexCtx.handleReference(D->getField(), D->getFieldLoc(), - Parent, ParentDC, E); - } - return true; - } - - bool VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { - IndexCtx.handleReference(E->getDecl(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool VisitObjCMessageExpr(ObjCMessageExpr *E) { - if (ObjCMethodDecl *MD = E->getMethodDecl()) - IndexCtx.handleReference(MD, E->getSelectorStartLoc(), - Parent, ParentDC, E, - E->isImplicit() ? CXIdxEntityRef_Implicit - : CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { - if (E->isExplicitProperty()) - IndexCtx.handleReference(E->getExplicitProperty(), E->getLocation(), - Parent, ParentDC, E); - - // No need to do a handleReference for the objc method, because there will - // be a message expr as part of PseudoObjectExpr. - return true; - } - - bool VisitMSPropertyRefExpr(MSPropertyRefExpr *E) { - IndexCtx.handleReference(E->getPropertyDecl(), E->getMemberLoc(), Parent, - ParentDC, E, CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCProtocolExpr(ObjCProtocolExpr *E) { - IndexCtx.handleReference(E->getProtocol(), E->getProtocolIdLoc(), - Parent, ParentDC, E, CXIdxEntityRef_Direct); - return true; - } - - bool VisitObjCBoxedExpr(ObjCBoxedExpr *E) { - if (ObjCMethodDecl *MD = E->getBoxingMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitObjCDictionaryLiteral(ObjCDictionaryLiteral *E) { - if (ObjCMethodDecl *MD = E->getDictWithObjectsMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitObjCArrayLiteral(ObjCArrayLiteral *E) { - if (ObjCMethodDecl *MD = E->getArrayWithObjectsMethod()) - IndexCtx.handleReference(MD, E->getLocStart(), - Parent, ParentDC, E, CXIdxEntityRef_Implicit); - return true; - } - - bool VisitCXXConstructExpr(CXXConstructExpr *E) { - IndexCtx.handleReference(E->getConstructor(), E->getLocation(), - Parent, ParentDC, E); - return true; - } - - bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *E, - DataRecursionQueue *Q = nullptr) { - if (E->getOperatorLoc().isInvalid()) - return true; // implicit. - return base::TraverseCXXOperatorCallExpr(E, Q); - } - - bool VisitDeclStmt(DeclStmt *S) { - if (IndexCtx.shouldIndexFunctionLocalSymbols()) { - IndexCtx.indexDeclGroupRef(S->getDeclGroup()); - return true; - } - - DeclGroupRef DG = S->getDeclGroup(); - for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { - const Decl *D = *I; - if (!D) - continue; - if (!IndexCtx.isFunctionLocalDecl(D)) - IndexCtx.indexTopLevelDecl(D); - } - - return true; - } - - bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { - if (C->capturesThis() || C->capturesVLAType()) - return true; - - if (C->capturesVariable() && IndexCtx.shouldIndexFunctionLocalSymbols()) - IndexCtx.handleReference(C->getCapturedVar(), C->getLocation(), Parent, - ParentDC); - - // FIXME: Lambda init-captures. - return true; - } - -}; - -} // anonymous namespace - -void IndexingContext::indexBody(const Stmt *S, const NamedDecl *Parent, - const DeclContext *DC) { - if (!S) - return; - - if (!DC) - DC = Parent->getLexicalDeclContext(); - BodyIndexer(*this, Parent, DC).TraverseStmt(const_cast<Stmt*>(S)); -} diff --git a/tools/libclang/IndexDecl.cpp b/tools/libclang/IndexDecl.cpp deleted file mode 100644 index c8cf1d362147..000000000000 --- a/tools/libclang/IndexDecl.cpp +++ /dev/null @@ -1,357 +0,0 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IndexingContext.h" -#include "clang/AST/DeclVisitor.h" - -using namespace clang; -using namespace cxindex; - -namespace { - -class IndexingDeclVisitor : public ConstDeclVisitor<IndexingDeclVisitor, bool> { - IndexingContext &IndexCtx; - -public: - explicit IndexingDeclVisitor(IndexingContext &indexCtx) - : IndexCtx(indexCtx) { } - - /// \brief Returns true if the given method has been defined explicitly by the - /// user. - static bool hasUserDefined(const ObjCMethodDecl *D, - const ObjCImplDecl *Container) { - const ObjCMethodDecl *MD = Container->getMethod(D->getSelector(), - D->isInstanceMethod()); - return MD && !MD->isImplicit() && MD->isThisDeclarationADefinition(); - } - - void handleDeclarator(const DeclaratorDecl *D, - const NamedDecl *Parent = nullptr) { - if (!Parent) Parent = D; - - if (!IndexCtx.shouldIndexFunctionLocalSymbols()) { - IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), Parent); - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), Parent); - } else { - if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { - IndexCtx.handleVar(Parm); - } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { - for (auto PI : FD->params()) { - IndexCtx.handleVar(PI); - } - } - } - } - - void handleObjCMethod(const ObjCMethodDecl *D) { - IndexCtx.handleObjCMethod(D); - if (D->isImplicit()) - return; - - IndexCtx.indexTypeSourceInfo(D->getReturnTypeSourceInfo(), D); - for (const auto *I : D->params()) - handleDeclarator(I, D); - - if (D->isThisDeclarationADefinition()) { - const Stmt *Body = D->getBody(); - if (Body) { - IndexCtx.indexBody(Body, D, D); - } - } - } - - bool VisitFunctionDecl(const FunctionDecl *D) { - IndexCtx.handleFunction(D); - handleDeclarator(D); - - if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) { - // Constructor initializers. - for (const auto *Init : Ctor->inits()) { - if (Init->isWritten()) { - IndexCtx.indexTypeSourceInfo(Init->getTypeSourceInfo(), D); - if (const FieldDecl *Member = Init->getAnyMember()) - IndexCtx.handleReference(Member, Init->getMemberLocation(), D, D); - IndexCtx.indexBody(Init->getInit(), D, D); - } - } - } - - if (D->isThisDeclarationADefinition()) { - const Stmt *Body = D->getBody(); - if (Body) { - IndexCtx.indexBody(Body, D, D); - } - } - return true; - } - - bool VisitVarDecl(const VarDecl *D) { - IndexCtx.handleVar(D); - handleDeclarator(D); - IndexCtx.indexBody(D->getInit(), D); - return true; - } - - bool VisitFieldDecl(const FieldDecl *D) { - IndexCtx.handleField(D); - handleDeclarator(D); - if (D->isBitField()) - IndexCtx.indexBody(D->getBitWidth(), D); - else if (D->hasInClassInitializer()) - IndexCtx.indexBody(D->getInClassInitializer(), D); - return true; - } - - bool VisitMSPropertyDecl(const MSPropertyDecl *D) { - handleDeclarator(D); - return true; - } - - bool VisitEnumConstantDecl(const EnumConstantDecl *D) { - IndexCtx.handleEnumerator(D); - IndexCtx.indexBody(D->getInitExpr(), D); - return true; - } - - bool VisitTypedefNameDecl(const TypedefNameDecl *D) { - IndexCtx.handleTypedefName(D); - IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); - return true; - } - - bool VisitTagDecl(const TagDecl *D) { - // Non-free standing tags are handled in indexTypeSourceInfo. - if (D->isFreeStanding()) - IndexCtx.indexTagDecl(D); - return true; - } - - bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *D) { - IndexCtx.handleObjCInterface(D); - - if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTUDeclsInObjCContainer(); - IndexCtx.indexDeclContext(D); - } - return true; - } - - bool VisitObjCProtocolDecl(const ObjCProtocolDecl *D) { - IndexCtx.handleObjCProtocol(D); - - if (D->isThisDeclarationADefinition()) { - IndexCtx.indexTUDeclsInObjCContainer(); - IndexCtx.indexDeclContext(D); - } - return true; - } - - bool VisitObjCImplementationDecl(const ObjCImplementationDecl *D) { - const ObjCInterfaceDecl *Class = D->getClassInterface(); - if (!Class) - return true; - - if (Class->isImplicitInterfaceDecl()) - IndexCtx.handleObjCInterface(Class); - - IndexCtx.handleObjCImplementation(D); - - IndexCtx.indexTUDeclsInObjCContainer(); - - // Index the ivars first to make sure the synthesized ivars are indexed - // before indexing the methods that can reference them. - for (const auto *IvarI : D->ivars()) - IndexCtx.indexDecl(IvarI); - for (const auto *I : D->decls()) { - if (!isa<ObjCIvarDecl>(I)) - IndexCtx.indexDecl(I); - } - - return true; - } - - bool VisitObjCCategoryDecl(const ObjCCategoryDecl *D) { - IndexCtx.handleObjCCategory(D); - - IndexCtx.indexTUDeclsInObjCContainer(); - IndexCtx.indexDeclContext(D); - return true; - } - - bool VisitObjCCategoryImplDecl(const ObjCCategoryImplDecl *D) { - const ObjCCategoryDecl *Cat = D->getCategoryDecl(); - if (!Cat) - return true; - - IndexCtx.handleObjCCategoryImpl(D); - - IndexCtx.indexTUDeclsInObjCContainer(); - IndexCtx.indexDeclContext(D); - return true; - } - - bool VisitObjCMethodDecl(const ObjCMethodDecl *D) { - // Methods associated with a property, even user-declared ones, are - // handled when we handle the property. - if (D->isPropertyAccessor()) - return true; - - handleObjCMethod(D); - return true; - } - - bool VisitObjCPropertyDecl(const ObjCPropertyDecl *D) { - if (ObjCMethodDecl *MD = D->getGetterMethodDecl()) - if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) - handleObjCMethod(MD); - if (ObjCMethodDecl *MD = D->getSetterMethodDecl()) - if (MD->getLexicalDeclContext() == D->getLexicalDeclContext()) - handleObjCMethod(MD); - IndexCtx.handleObjCProperty(D); - IndexCtx.indexTypeSourceInfo(D->getTypeSourceInfo(), D); - return true; - } - - bool VisitObjCPropertyImplDecl(const ObjCPropertyImplDecl *D) { - ObjCPropertyDecl *PD = D->getPropertyDecl(); - IndexCtx.handleSynthesizedObjCProperty(D); - - if (D->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) - return true; - assert(D->getPropertyImplementation() == ObjCPropertyImplDecl::Synthesize); - - if (ObjCIvarDecl *IvarD = D->getPropertyIvarDecl()) { - if (!IvarD->getSynthesize()) - IndexCtx.handleReference(IvarD, D->getPropertyIvarDeclLoc(), nullptr, - D->getDeclContext()); - } - - if (ObjCMethodDecl *MD = PD->getGetterMethodDecl()) { - if (MD->isPropertyAccessor() && - !hasUserDefined(MD, cast<ObjCImplDecl>(D->getDeclContext()))) - IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), - D->getLexicalDeclContext()); - } - if (ObjCMethodDecl *MD = PD->getSetterMethodDecl()) { - if (MD->isPropertyAccessor() && - !hasUserDefined(MD, cast<ObjCImplDecl>(D->getDeclContext()))) - IndexCtx.handleSynthesizedObjCMethod(MD, D->getLocation(), - D->getLexicalDeclContext()); - } - return true; - } - - bool VisitNamespaceDecl(const NamespaceDecl *D) { - IndexCtx.handleNamespace(D); - IndexCtx.indexDeclContext(D); - return true; - } - - bool VisitUsingDecl(const UsingDecl *D) { - // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, - // we should do better. - - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); - for (const auto *I : D->shadows()) - IndexCtx.handleReference(I->getUnderlyingDecl(), D->getLocation(), D, - D->getLexicalDeclContext()); - return true; - } - - bool VisitUsingDirectiveDecl(const UsingDirectiveDecl *D) { - // FIXME: Parent for the following is CXIdxEntity_Unexposed with no USR, - // we should do better. - - IndexCtx.indexNestedNameSpecifierLoc(D->getQualifierLoc(), D); - IndexCtx.handleReference(D->getNominatedNamespaceAsWritten(), - D->getLocation(), D, D->getLexicalDeclContext()); - return true; - } - - bool VisitClassTemplateDecl(const ClassTemplateDecl *D) { - IndexCtx.handleClassTemplate(D); - if (D->isThisDeclarationADefinition()) - IndexCtx.indexDeclContext(D->getTemplatedDecl()); - return true; - } - - bool VisitClassTemplateSpecializationDecl(const - ClassTemplateSpecializationDecl *D) { - // FIXME: Notify subsequent callbacks if info comes from implicit - // instantiation. - if (D->isThisDeclarationADefinition() && - (IndexCtx.shouldIndexImplicitTemplateInsts() || - !IndexCtx.isTemplateImplicitInstantiation(D))) - IndexCtx.indexTagDecl(D); - return true; - } - - bool VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { - IndexCtx.handleFunctionTemplate(D); - FunctionDecl *FD = D->getTemplatedDecl(); - handleDeclarator(FD, D); - if (FD->isThisDeclarationADefinition()) { - const Stmt *Body = FD->getBody(); - if (Body) { - IndexCtx.indexBody(Body, D, FD); - } - } - return true; - } - - bool VisitTypeAliasTemplateDecl(const TypeAliasTemplateDecl *D) { - IndexCtx.handleTypeAliasTemplate(D); - IndexCtx.indexTypeSourceInfo(D->getTemplatedDecl()->getTypeSourceInfo(), D); - return true; - } - - bool VisitImportDecl(const ImportDecl *D) { - IndexCtx.importedModule(D); - return true; - } -}; - -} // anonymous namespace - -void IndexingContext::indexDecl(const Decl *D) { - if (D->isImplicit() && shouldIgnoreIfImplicit(D)) - return; - - bool Handled = IndexingDeclVisitor(*this).Visit(D); - if (!Handled && isa<DeclContext>(D)) - indexDeclContext(cast<DeclContext>(D)); -} - -void IndexingContext::indexDeclContext(const DeclContext *DC) { - for (const auto *I : DC->decls()) - indexDecl(I); -} - -void IndexingContext::indexTopLevelDecl(const Decl *D) { - if (isNotFromSourceFile(D->getLocation())) - return; - - if (isa<ObjCMethodDecl>(D)) - return; // Wait for the objc container. - - indexDecl(D); -} - -void IndexingContext::indexDeclGroupRef(DeclGroupRef DG) { - for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) - indexTopLevelDecl(*I); -} - -void IndexingContext::indexTUDeclsInObjCContainer() { - while (!TUDeclsInObjCContainer.empty()) { - DeclGroupRef DG = TUDeclsInObjCContainer.front(); - TUDeclsInObjCContainer.pop_front(); - indexDeclGroupRef(DG); - } -} diff --git a/tools/libclang/IndexTypeSourceInfo.cpp b/tools/libclang/IndexTypeSourceInfo.cpp deleted file mode 100644 index 9666052ed181..000000000000 --- a/tools/libclang/IndexTypeSourceInfo.cpp +++ /dev/null @@ -1,156 +0,0 @@ -//===- CIndexHigh.cpp - Higher level API functions ------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "IndexingContext.h" -#include "clang/AST/RecursiveASTVisitor.h" - -using namespace clang; -using namespace cxindex; - -namespace { - -class TypeIndexer : public RecursiveASTVisitor<TypeIndexer> { - IndexingContext &IndexCtx; - const NamedDecl *Parent; - const DeclContext *ParentDC; - -public: - TypeIndexer(IndexingContext &indexCtx, const NamedDecl *parent, - const DeclContext *DC) - : IndexCtx(indexCtx), Parent(parent), ParentDC(DC) { } - - bool shouldWalkTypesOfTypeLocs() const { return false; } - - bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { - IndexCtx.handleReference(TL.getTypedefNameDecl(), TL.getNameLoc(), - Parent, ParentDC); - return true; - } - - bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) { - IndexCtx.indexNestedNameSpecifierLoc(NNS, Parent, ParentDC); - return true; - } - - bool VisitTagTypeLoc(TagTypeLoc TL) { - TagDecl *D = TL.getDecl(); - if (D->getParentFunctionOrMethod()) - return true; - - if (TL.isDefinition()) { - IndexCtx.indexTagDecl(D); - return true; - } - - if (D->getLocation() == TL.getNameLoc()) - IndexCtx.handleTagDecl(D); - else - IndexCtx.handleReference(D, TL.getNameLoc(), - Parent, ParentDC); - return true; - } - - bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { - IndexCtx.handleReference(TL.getIFaceDecl(), TL.getNameLoc(), - Parent, ParentDC); - return true; - } - - bool VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { - for (unsigned i = 0, e = TL.getNumProtocols(); i != e; ++i) { - IndexCtx.handleReference(TL.getProtocol(i), TL.getProtocolLoc(i), - Parent, ParentDC); - } - return true; - } - - bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) { - if (const TemplateSpecializationType *T = TL.getTypePtr()) { - if (IndexCtx.shouldIndexImplicitTemplateInsts()) { - if (CXXRecordDecl *RD = T->getAsCXXRecordDecl()) - IndexCtx.handleReference(RD, TL.getTemplateNameLoc(), - Parent, ParentDC); - } else { - if (const TemplateDecl *D = T->getTemplateName().getAsTemplateDecl()) - IndexCtx.handleReference(D, TL.getTemplateNameLoc(), - Parent, ParentDC); - } - } - return true; - } - - bool TraverseStmt(Stmt *S) { - IndexCtx.indexBody(S, Parent, ParentDC); - return true; - } -}; - -} // anonymous namespace - -void IndexingContext::indexTypeSourceInfo(TypeSourceInfo *TInfo, - const NamedDecl *Parent, - const DeclContext *DC) { - if (!TInfo || TInfo->getTypeLoc().isNull()) - return; - - indexTypeLoc(TInfo->getTypeLoc(), Parent, DC); -} - -void IndexingContext::indexTypeLoc(TypeLoc TL, - const NamedDecl *Parent, - const DeclContext *DC) { - if (TL.isNull()) - return; - - if (!DC) - DC = Parent->getLexicalDeclContext(); - TypeIndexer(*this, Parent, DC).TraverseTypeLoc(TL); -} - -void IndexingContext::indexNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS, - const NamedDecl *Parent, - const DeclContext *DC) { - if (!NNS) - return; - - if (NestedNameSpecifierLoc Prefix = NNS.getPrefix()) - indexNestedNameSpecifierLoc(Prefix, Parent, DC); - - if (!DC) - DC = Parent->getLexicalDeclContext(); - SourceLocation Loc = NNS.getSourceRange().getBegin(); - - switch (NNS.getNestedNameSpecifier()->getKind()) { - case NestedNameSpecifier::Identifier: - case NestedNameSpecifier::Global: - case NestedNameSpecifier::Super: - break; - - case NestedNameSpecifier::Namespace: - handleReference(NNS.getNestedNameSpecifier()->getAsNamespace(), - Loc, Parent, DC); - break; - case NestedNameSpecifier::NamespaceAlias: - handleReference(NNS.getNestedNameSpecifier()->getAsNamespaceAlias(), - Loc, Parent, DC); - break; - - case NestedNameSpecifier::TypeSpec: - case NestedNameSpecifier::TypeSpecWithTemplate: - indexTypeLoc(NNS.getTypeLoc(), Parent, DC); - break; - } -} - -void IndexingContext::indexTagDecl(const TagDecl *D) { - if (handleTagDecl(D)) { - if (D->isThisDeclarationADefinition()) - indexDeclContext(D); - } -} diff --git a/tools/libclang/Indexing.cpp b/tools/libclang/Indexing.cpp index d6e35b0019c9..fe14cb2664af 100644 --- a/tools/libclang/Indexing.cpp +++ b/tools/libclang/Indexing.cpp @@ -7,21 +7,21 @@ // //===----------------------------------------------------------------------===// -#include "IndexingContext.h" #include "CIndexDiagnostic.h" #include "CIndexer.h" #include "CLog.h" #include "CXCursor.h" +#include "CXIndexDataConsumer.h" #include "CXSourceLocation.h" #include "CXString.h" #include "CXTranslationUnit.h" #include "clang/AST/ASTConsumer.h" -#include "clang/AST/DeclVisitor.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendAction.h" #include "clang/Frontend/Utils.h" +#include "clang/Index/IndexingAction.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/PPConditionalDirectiveRecord.h" @@ -32,39 +32,19 @@ #include "llvm/Support/Mutex.h" #include "llvm/Support/MutexGuard.h" #include <cstdio> +#include <utility> using namespace clang; +using namespace clang::index; using namespace cxtu; using namespace cxindex; -static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx); - namespace { //===----------------------------------------------------------------------===// // Skip Parsed Bodies //===----------------------------------------------------------------------===// -#ifdef LLVM_ON_WIN32 - -// FIXME: On windows it is disabled since current implementation depends on -// file inodes. - -class SessionSkipBodyData { }; - -class TUSkipBodyControl { -public: - TUSkipBodyControl(SessionSkipBodyData &sessionData, - PPConditionalDirectiveRecord &ppRec, - Preprocessor &pp) { } - bool isParsed(SourceLocation Loc, FileID FID, const FileEntry *FE) { - return false; - } - void finished() { } -}; - -#else - /// \brief A "region" in source code identified by the file/offset of the /// preprocessor conditional directive that it belongs to. /// Multiple, non-consecutive ranges can be parts of the same region. @@ -238,20 +218,18 @@ private: } }; -#endif - //===----------------------------------------------------------------------===// // IndexPPCallbacks //===----------------------------------------------------------------------===// class IndexPPCallbacks : public PPCallbacks { Preprocessor &PP; - IndexingContext &IndexCtx; + CXIndexDataConsumer &DataConsumer; bool IsMainFileEntered; public: - IndexPPCallbacks(Preprocessor &PP, IndexingContext &indexCtx) - : PP(PP), IndexCtx(indexCtx), IsMainFileEntered(false) { } + IndexPPCallbacks(Preprocessor &PP, CXIndexDataConsumer &dataConsumer) + : PP(PP), DataConsumer(dataConsumer), IsMainFileEntered(false) { } void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType, FileID PrevFID) override { @@ -263,7 +241,7 @@ public: if (Loc == MainFileLoc && Reason == PPCallbacks::EnterFile) { IsMainFileEntered = true; - IndexCtx.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); + DataConsumer.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); } } @@ -274,7 +252,7 @@ public: const Module *Imported) override { bool isImport = (IncludeTok.is(tok::identifier) && IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); - IndexCtx.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, + DataConsumer.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, Imported); } @@ -301,18 +279,18 @@ public: //===----------------------------------------------------------------------===// class IndexingConsumer : public ASTConsumer { - IndexingContext &IndexCtx; + CXIndexDataConsumer &DataConsumer; TUSkipBodyControl *SKCtrl; public: - IndexingConsumer(IndexingContext &indexCtx, TUSkipBodyControl *skCtrl) - : IndexCtx(indexCtx), SKCtrl(skCtrl) { } + IndexingConsumer(CXIndexDataConsumer &dataConsumer, TUSkipBodyControl *skCtrl) + : DataConsumer(dataConsumer), SKCtrl(skCtrl) { } // ASTConsumer Implementation void Initialize(ASTContext &Context) override { - IndexCtx.setASTContext(Context); - IndexCtx.startedTranslationUnit(); + DataConsumer.setASTContext(Context); + DataConsumer.startedTranslationUnit(); } void HandleTranslationUnit(ASTContext &Ctx) override { @@ -321,35 +299,7 @@ public: } bool HandleTopLevelDecl(DeclGroupRef DG) override { - IndexCtx.indexDeclGroupRef(DG); - return !IndexCtx.shouldAbort(); - } - - /// \brief Handle the specified top-level declaration that occurred inside - /// and ObjC container. - void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { - // They will be handled after the interface is seen first. - IndexCtx.addTUDeclInObjCContainer(D); - } - - /// \brief This is called by the AST reader when deserializing things. - /// The default implementation forwards to HandleTopLevelDecl but we don't - /// care about them when indexing, so have an empty definition. - void HandleInterestingDecl(DeclGroupRef D) override {} - - void HandleTagDeclDefinition(TagDecl *D) override { - if (!IndexCtx.shouldIndexImplicitTemplateInsts()) - return; - - if (IndexCtx.isTemplateImplicitInstantiation(D)) - IndexCtx.indexDecl(D); - } - - void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override { - if (!IndexCtx.shouldIndexImplicitTemplateInsts()) - return; - - IndexCtx.indexDecl(D); + return !DataConsumer.shouldAbort(); } bool shouldSkipFunctionBody(Decl *D) override { @@ -358,7 +308,7 @@ public: return true; } - const SourceManager &SM = IndexCtx.getASTContext().getSourceManager(); + const SourceManager &SM = DataConsumer.getASTContext().getSourceManager(); SourceLocation Loc = D->getLocation(); if (Loc.isMacroID()) return false; @@ -399,34 +349,29 @@ public: //===----------------------------------------------------------------------===// class IndexingFrontendAction : public ASTFrontendAction { - IndexingContext IndexCtx; - CXTranslationUnit CXTU; + std::shared_ptr<CXIndexDataConsumer> DataConsumer; SessionSkipBodyData *SKData; std::unique_ptr<TUSkipBodyControl> SKCtrl; public: - IndexingFrontendAction(CXClientData clientData, - IndexerCallbacks &indexCallbacks, - unsigned indexOptions, - CXTranslationUnit cxTU, + IndexingFrontendAction(std::shared_ptr<CXIndexDataConsumer> dataConsumer, SessionSkipBodyData *skData) - : IndexCtx(clientData, indexCallbacks, indexOptions, cxTU), - CXTU(cxTU), SKData(skData) { } + : DataConsumer(std::move(dataConsumer)), SKData(skData) {} std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override { PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); if (!PPOpts.ImplicitPCHInclude.empty()) { - IndexCtx.importedPCH( + DataConsumer->importedPCH( CI.getFileManager().getFile(PPOpts.ImplicitPCHInclude)); } - IndexCtx.setASTContext(CI.getASTContext()); + DataConsumer->setASTContext(CI.getASTContext()); Preprocessor &PP = CI.getPreprocessor(); - PP.addPPCallbacks(llvm::make_unique<IndexPPCallbacks>(PP, IndexCtx)); - IndexCtx.setPreprocessor(PP); + PP.addPPCallbacks(llvm::make_unique<IndexPPCallbacks>(PP, *DataConsumer)); + DataConsumer->setPreprocessor(PP); if (SKData) { auto *PPRec = new PPConditionalDirectiveRecord(PP.getSourceManager()); @@ -434,15 +379,11 @@ public: SKCtrl = llvm::make_unique<TUSkipBodyControl>(*SKData, *PPRec, PP); } - return llvm::make_unique<IndexingConsumer>(IndexCtx, SKCtrl.get()); - } - - void EndSourceFileAction() override { - indexDiagnostics(CXTU, IndexCtx); + return llvm::make_unique<IndexingConsumer>(*DataConsumer, SKCtrl.get()); } TranslationUnitKind getTranslationUnitKind() override { - if (IndexCtx.shouldIndexImplicitTemplateInsts()) + if (DataConsumer->shouldIndexImplicitTemplateInsts()) return TU_Complete; else return TU_Prefix; @@ -454,6 +395,13 @@ public: // clang_indexSourceFileUnit Implementation //===----------------------------------------------------------------------===// +static IndexingOptions getIndexingOptionsFromCXOptions(unsigned index_options) { + IndexingOptions IdxOpts; + if (index_options & CXIndexOpt_IndexFunctionLocalSymbols) + IdxOpts.IndexFunctionLocals = true; + return IdxOpts; +} + struct IndexSessionData { CXIndex CIdx; std::unique_ptr<SessionSkipBodyData> SkipBodyData; @@ -589,13 +537,18 @@ static CXErrorCode clang_indexSourceFile_Impl( if (SkipBodies) CInvok->getFrontendOpts().SkipFunctionBodies = true; - std::unique_ptr<IndexingFrontendAction> IndexAction; - IndexAction.reset(new IndexingFrontendAction(client_data, CB, - index_options, CXTU->getTU(), - SkipBodies ? IdxSession->SkipBodyData.get() : nullptr)); + auto DataConsumer = + std::make_shared<CXIndexDataConsumer>(client_data, CB, index_options, + CXTU->getTU()); + auto InterAction = llvm::make_unique<IndexingFrontendAction>(DataConsumer, + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); + std::unique_ptr<FrontendAction> IndexAction; + IndexAction = createIndexingAction(DataConsumer, + getIndexingOptionsFromCXOptions(index_options), + std::move(InterAction)); // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar<IndexingFrontendAction> + llvm::CrashRecoveryContextCleanupRegistrar<FrontendAction> IndexActionCleanup(IndexAction.get()); bool Persistent = requestedToGetTU; @@ -655,7 +608,7 @@ static CXErrorCode clang_indexSourceFile_Impl( // clang_indexTranslationUnit Implementation //===----------------------------------------------------------------------===// -static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { +static void indexPreprocessingRecord(ASTUnit &Unit, CXIndexDataConsumer &IdxCtx) { Preprocessor &PP = Unit.getPreprocessor(); if (!PP.getPreprocessingRecord()) return; @@ -678,24 +631,6 @@ static void indexPreprocessingRecord(ASTUnit &Unit, IndexingContext &IdxCtx) { } } -static bool topLevelDeclVisitor(void *context, const Decl *D) { - IndexingContext &IdxCtx = *static_cast<IndexingContext*>(context); - IdxCtx.indexTopLevelDecl(D); - return !IdxCtx.shouldAbort(); -} - -static void indexTranslationUnit(ASTUnit &Unit, IndexingContext &IdxCtx) { - Unit.visitLocalTopLevelDecls(&IdxCtx, topLevelDeclVisitor); -} - -static void indexDiagnostics(CXTranslationUnit TU, IndexingContext &IdxCtx) { - if (!IdxCtx.hasDiagnosticCallback()) - return; - - CXDiagnosticSetImpl *DiagSet = cxdiag::lazyCreateDiags(TU); - IdxCtx.handleDiagnosticSet(DiagSet); -} - static CXErrorCode clang_indexTranslationUnit_Impl( CXIndexAction idxAction, CXClientData client_data, IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size, @@ -719,19 +654,8 @@ static CXErrorCode clang_indexTranslationUnit_Impl( ? index_callbacks_size : sizeof(CB); memcpy(&CB, client_index_callbacks, ClientCBSize); - std::unique_ptr<IndexingContext> IndexCtx; - IndexCtx.reset(new IndexingContext(client_data, CB, index_options, TU)); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar<IndexingContext> - IndexCtxCleanup(IndexCtx.get()); - - std::unique_ptr<IndexingConsumer> IndexConsumer; - IndexConsumer.reset(new IndexingConsumer(*IndexCtx, nullptr)); - - // Recover resources if we crash before exiting this method. - llvm::CrashRecoveryContextCleanupRegistrar<IndexingConsumer> - IndexConsumerCleanup(IndexConsumer.get()); + auto DataConsumer = std::make_shared<CXIndexDataConsumer>(client_data, CB, + index_options, TU); ASTUnit *Unit = cxtu::getASTUnit(TU); if (!Unit) @@ -740,20 +664,21 @@ static CXErrorCode clang_indexTranslationUnit_Impl( ASTUnit::ConcurrencyCheck Check(*Unit); if (const FileEntry *PCHFile = Unit->getPCHFile()) - IndexCtx->importedPCH(PCHFile); + DataConsumer->importedPCH(PCHFile); FileManager &FileMgr = Unit->getFileManager(); if (Unit->getOriginalSourceFileName().empty()) - IndexCtx->enteredMainFile(nullptr); + DataConsumer->enteredMainFile(nullptr); else - IndexCtx->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); + DataConsumer->enteredMainFile(FileMgr.getFile(Unit->getOriginalSourceFileName())); - IndexConsumer->Initialize(Unit->getASTContext()); + DataConsumer->setASTContext(Unit->getASTContext()); + DataConsumer->startedTranslationUnit(); - indexPreprocessingRecord(*Unit, *IndexCtx); - indexTranslationUnit(*Unit, *IndexCtx); - indexDiagnostics(TU, *IndexCtx); + indexPreprocessingRecord(*Unit, *DataConsumer); + indexASTUnit(*Unit, DataConsumer, getIndexingOptionsFromCXOptions(index_options)); + DataConsumer->indexDiagnostics(); return CXError_Success; } @@ -1038,9 +963,9 @@ void clang_indexLoc_getFileLocation(CXIdxLoc location, if (!location.ptr_data[0] || Loc.isInvalid()) return; - IndexingContext &IndexCtx = - *static_cast<IndexingContext*>(location.ptr_data[0]); - IndexCtx.translateLoc(Loc, indexFile, file, line, column, offset); + CXIndexDataConsumer &DataConsumer = + *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]); + DataConsumer.translateLoc(Loc, indexFile, file, line, column, offset); } CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { @@ -1048,9 +973,9 @@ CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { if (!location.ptr_data[0] || Loc.isInvalid()) return clang_getNullLocation(); - IndexingContext &IndexCtx = - *static_cast<IndexingContext*>(location.ptr_data[0]); - return cxloc::translateSourceLocation(IndexCtx.getASTContext(), Loc); + CXIndexDataConsumer &DataConsumer = + *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]); + return cxloc::translateSourceLocation(DataConsumer.getASTContext(), Loc); } } // end: extern "C" diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile deleted file mode 100644 index 84914e0f4609..000000000000 --- a/tools/libclang/Makefile +++ /dev/null @@ -1,64 +0,0 @@ -##===- tools/libclang/Makefile -----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. -LIBRARYNAME = clang - -EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/libclang.exports - -LINK_LIBS_IN_SHARED = 1 -SHARED_LIBRARY = 1 - -include $(CLANG_LEVEL)/../../Makefile.config -LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter core \ - instrumentation ipo mc mcparser objcarcopts option support \ - object -USEDLIBS = clangIndex.a clangARCMigrate.a \ - clangRewriteFrontend.a \ - clangFormat.a \ - clangTooling.a clangToolingCore.a \ - clangFrontend.a clangCodeGen.a clangDriver.a \ - clangSerialization.a \ - clangParse.a clangSema.a \ - clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ - clangRewrite.a \ - clangAnalysis.a clangEdit.a \ - clangASTMatchers.a \ - clangAST.a clangLex.a clangBasic.a - -include $(CLANG_LEVEL)/Makefile - -# Add soname to the library. -ifeq ($(HOST_OS), $(filter $(HOST_OS), Linux FreeBSD GNU GNU/kFreeBSD)) - LLVMLibsOptions += -Wl,-soname,lib$(LIBRARYNAME)$(SHLIBEXT) -endif - -ifeq ($(ENABLE_CLANG_ARCMT),1) - CXX.Flags += -DCLANG_ENABLE_ARCMT -endif - -##===----------------------------------------------------------------------===## -# FIXME: This is copied from the 'lto' makefile. Should we share this? -##===----------------------------------------------------------------------===## - -ifeq ($(HOST_OS),Darwin) - LLVMLibsOptions += -Wl,-compatibility_version,1 - - # Set dylib internal version number to submission number. - ifdef LLVM_SUBMIT_VERSION - LLVMLibsOptions += -Wl,-current_version \ - -Wl,$(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) - endif - - # If we're doing an Apple-style build, add the LTO object path. - ifeq ($(RC_XBS),YES) - TempFile := $(shell mkdir -p ${OBJROOT}/dSYMs ; mktemp ${OBJROOT}/dSYMs/clang-lto.XXXXXX) - LLVMLibsOptions += -Wl,-object_path_lto -Wl,$(TempFile) - endif -endif diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 993644d02fc1..c8fe0a21d09d 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -2,7 +2,12 @@ clang_CXCursorSet_contains clang_CXCursorSet_insert clang_CXIndex_getGlobalOptions clang_CXIndex_setGlobalOptions +clang_CXXConstructor_isConvertingConstructor +clang_CXXConstructor_isCopyConstructor +clang_CXXConstructor_isDefaultConstructor +clang_CXXConstructor_isMoveConstructor clang_CXXField_isMutable +clang_CXXMethod_isDefaulted clang_CXXMethod_isConst clang_CXXMethod_isPureVirtual clang_CXXMethod_isStatic @@ -82,6 +87,7 @@ clang_Type_getNumTemplateArguments clang_Type_getTemplateArgumentAsType clang_Type_getCXXRefQualifier clang_Type_visitFields +clang_Type_getNamedType clang_VerbatimBlockLineComment_getText clang_VerbatimLineComment_getText clang_HTMLTagComment_getAsString @@ -320,3 +326,14 @@ clang_VirtualFileOverlay_create clang_VirtualFileOverlay_dispose clang_VirtualFileOverlay_setCaseSensitivity clang_VirtualFileOverlay_writeToBuffer +clang_Type_getObjCEncoding +clang_Cursor_isMacroFunctionLike +clang_Cursor_isMacroBuiltin +clang_Cursor_isFunctionInlined +clang_Cursor_hasAttrs +clang_Cursor_Evaluate +clang_EvalResult_getKind +clang_EvalResult_getAsInt +clang_EvalResult_getAsDouble +clang_EvalResult_getAsStr +clang_EvalResult_dispose diff --git a/tools/scan-build-py/bin/analyze-build.bat b/tools/scan-build-py/bin/analyze-build.bat new file mode 100644 index 000000000000..05d81ddfda4d --- /dev/null +++ b/tools/scan-build-py/bin/analyze-build.bat @@ -0,0 +1 @@ +python %~dp0analyze-build %*
diff --git a/tools/scan-build-py/bin/analyze-c++.bat b/tools/scan-build-py/bin/analyze-c++.bat new file mode 100644 index 000000000000..f57032f60bd4 --- /dev/null +++ b/tools/scan-build-py/bin/analyze-c++.bat @@ -0,0 +1 @@ +python %~dp0analyze-c++ %*
diff --git a/tools/scan-build-py/bin/analyze-cc.bat b/tools/scan-build-py/bin/analyze-cc.bat new file mode 100644 index 000000000000..41cd8f622eb2 --- /dev/null +++ b/tools/scan-build-py/bin/analyze-cc.bat @@ -0,0 +1 @@ +python %~dp0analyze-cc %*
diff --git a/tools/scan-build-py/bin/intercept-build.bat b/tools/scan-build-py/bin/intercept-build.bat new file mode 100644 index 000000000000..5c824635dfe4 --- /dev/null +++ b/tools/scan-build-py/bin/intercept-build.bat @@ -0,0 +1 @@ +python %~dp0intercept-build %*
diff --git a/tools/scan-build-py/bin/intercept-c++.bat b/tools/scan-build-py/bin/intercept-c++.bat new file mode 100644 index 000000000000..abbd4b177e0f --- /dev/null +++ b/tools/scan-build-py/bin/intercept-c++.bat @@ -0,0 +1 @@ +python %~dp0intercept-c++ %*
diff --git a/tools/scan-build-py/bin/intercept-cc.bat b/tools/scan-build-py/bin/intercept-cc.bat new file mode 100644 index 000000000000..23cbd8d22ca6 --- /dev/null +++ b/tools/scan-build-py/bin/intercept-cc.bat @@ -0,0 +1 @@ +python %~dp0intercept-cc %*
diff --git a/tools/scan-build-py/bin/scan-build.bat b/tools/scan-build-py/bin/scan-build.bat new file mode 100644 index 000000000000..8caf240a2f0b --- /dev/null +++ b/tools/scan-build-py/bin/scan-build.bat @@ -0,0 +1 @@ +python %~dp0scan-build %*
diff --git a/tools/scan-build-py/libscanbuild/analyze.py b/tools/scan-build-py/libscanbuild/analyze.py index 0d3547befeef..0ed0aef83873 100644 --- a/tools/scan-build-py/libscanbuild/analyze.py +++ b/tools/scan-build-py/libscanbuild/analyze.py @@ -25,8 +25,7 @@ from libscanbuild.runner import run from libscanbuild.intercept import capture from libscanbuild.report import report_directory, document from libscanbuild.clang import get_checkers -from libscanbuild.runner import action_check -from libscanbuild.command import classify_parameters, classify_source +from libscanbuild.compilation import split_command __all__ = ['analyze_build_main', 'analyze_build_wrapper'] @@ -106,7 +105,8 @@ def run_analyzer(args, output_dir): 'output_dir': output_dir, 'output_format': args.output_format, 'output_failures': args.output_failures, - 'direct_args': analyzer_params(args) + 'direct_args': analyzer_params(args), + 'force_debug': args.force_debug } logging.debug('run analyzer against compilation database') @@ -138,7 +138,8 @@ def setup_environment(args, destination, bin_dir): 'ANALYZE_BUILD_REPORT_DIR': destination, 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', - 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)) + 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)), + 'ANALYZE_BUILD_FORCE_DEBUG': 'yes' if args.force_debug else '' }) return environment @@ -160,30 +161,34 @@ def analyze_build_wrapper(cplusplus): return result # ... and run the analyzer if all went well. try: + # check is it a compilation + compilation = split_command(sys.argv) + if compilation is None: + return result # collect the needed parameters from environment, crash when missing - consts = { + parameters = { 'clang': os.getenv('ANALYZE_BUILD_CLANG'), 'output_dir': os.getenv('ANALYZE_BUILD_REPORT_DIR'), 'output_format': os.getenv('ANALYZE_BUILD_REPORT_FORMAT'), 'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'), 'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS', '').split(' '), + 'force_debug': os.getenv('ANALYZE_BUILD_FORCE_DEBUG'), 'directory': os.getcwd(), + 'command': [sys.argv[0], '-c'] + compilation.flags } - # get relevant parameters from command line arguments - args = classify_parameters(sys.argv) - filenames = args.pop('files', []) - for filename in (name for name in filenames if classify_source(name)): - parameters = dict(args, file=filename, **consts) + # call static analyzer against the compilation + for source in compilation.files: + parameters.update({'file': source}) logging.debug('analyzer parameters %s', parameters) - current = action_check(parameters) + current = run(parameters) # display error message from the static analyzer if current is not None: for line in current['error_output']: logging.info(line.rstrip()) except Exception: logging.exception("run analyzer inside compiler wrapper failed.") - return 0 + return result def analyzer_params(args): @@ -203,8 +208,8 @@ def analyzer_params(args): if args.store_model: result.append('-analyzer-store={0}'.format(args.store_model)) if args.constraints_model: - result.append( - '-analyzer-constraints={0}'.format(args.constraints_model)) + result.append('-analyzer-constraints={0}'.format( + args.constraints_model)) if args.internal_stats: result.append('-analyzer-stats') if args.analyze_headers: @@ -450,6 +455,12 @@ def create_parser(from_build_command): Could be usefull when project contains 3rd party libraries. The directory path shall be absolute path as file names in the compilation database.""") + advanced.add_argument( + '--force-analyze-debug-code', + dest='force_debug', + action='store_true', + help="""Tells analyzer to enable assertions in code even if they were + disabled during compilation, enabling more precise results.""") plugins = parser.add_argument_group('checker options') plugins.add_argument( diff --git a/tools/scan-build-py/libscanbuild/command.py b/tools/scan-build-py/libscanbuild/command.py deleted file mode 100644 index 69ca3393f955..000000000000 --- a/tools/scan-build-py/libscanbuild/command.py +++ /dev/null @@ -1,133 +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 for to parse a compiler invocation. """ - -import re -import os - -__all__ = ['Action', 'classify_parameters', 'classify_source'] - - -class Action(object): - """ Enumeration class for compiler action. """ - - Link, Compile, Ignored = range(3) - - -def classify_parameters(command): - """ Parses the command line arguments of the given invocation. """ - - # result value of this method. - # some value are preset, some will be set only when found. - result = { - 'action': Action.Link, - 'files': [], - 'output': None, - 'compile_options': [], - 'c++': is_cplusplus_compiler(command[0]) - # archs_seen - # language - } - - # data structure to ignore compiler parameters. - # key: parameter name, value: number of parameters to ignore afterwards. - ignored = { - '-g': 0, - '-fsyntax-only': 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 - } - - args = iter(command[1:]) - for arg in args: - # compiler action parameters are the most important ones... - if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}: - result.update({'action': Action.Ignored}) - elif arg == '-c': - result.update({'action': max(result['action'], Action.Compile)}) - # arch flags are taken... - elif arg == '-arch': - archs = result.get('archs_seen', []) - result.update({'archs_seen': archs + [next(args)]}) - # explicit language option taken... - elif arg == '-x': - result.update({'language': next(args)}) - # output flag taken... - elif arg == '-o': - result.update({'output': next(args)}) - # warning disable options are taken... - elif re.match(r'^-Wno-', arg): - result['compile_options'].append(arg) - # warning options are ignored... - elif re.match(r'^-[mW].+', arg): - pass - # some preprocessor parameters are ignored... - elif arg in {'-MD', '-MMD', '-MG', '-MP'}: - pass - elif arg in {'-MF', '-MT', '-MQ'}: - next(args) - # linker options are ignored... - elif arg in {'-static', '-shared', '-s', '-rdynamic'} or \ - re.match(r'^-[lL].+', arg): - pass - elif arg in {'-l', '-L', '-u', '-z', '-T', '-Xlinker'}: - next(args) - # some other options are ignored... - elif arg in ignored.keys(): - for _ in range(ignored[arg]): - next(args) - # parameters which looks source file are taken... - elif re.match(r'^[^-].+', arg) and classify_source(arg): - result['files'].append(arg) - # and consider everything else as compile option. - else: - result['compile_options'].append(arg) - - return result - - -def classify_source(filename, cplusplus=False): - """ Return the language from file name extension. """ - - mapping = { - '.c': 'c++' if cplusplus else 'c', - '.i': 'c++-cpp-output' if cplusplus else 'c-cpp-output', - '.ii': 'c++-cpp-output', - '.m': 'objective-c', - '.mi': 'objective-c-cpp-output', - '.mm': 'objective-c++', - '.mii': 'objective-c++-cpp-output', - '.C': 'c++', - '.cc': 'c++', - '.CC': 'c++', - '.cp': 'c++', - '.cpp': 'c++', - '.cxx': 'c++', - '.c++': 'c++', - '.C++': 'c++', - '.txx': 'c++' - } - - __, extension = os.path.splitext(os.path.basename(filename)) - return mapping.get(extension) - - -def is_cplusplus_compiler(name): - """ Returns true when the compiler name refer to a C++ compiler. """ - - match = re.match(r'^([^/]*/)*(\w*-)*(\w+\+\+)(-(\d+(\.\d+){0,3}))?$', name) - return False if match is None else True diff --git a/tools/scan-build-py/libscanbuild/compilation.py b/tools/scan-build-py/libscanbuild/compilation.py new file mode 100644 index 000000000000..ef906fa60b9b --- /dev/null +++ b/tools/scan-build-py/libscanbuild/compilation.py @@ -0,0 +1,141 @@ +# -*- 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 for to parse a compiler invocation. """ + +import re +import os +import collections + +__all__ = ['split_command', 'classify_source', 'compiler_language'] + +# Ignored compiler options map for compilation database creation. +# The map is used in `split_command` method. (Which does ignore and classify +# parameters.) Please note, that these are not the only parameters which +# might be ignored. +# +# Keys are the option name, value number of options to skip +IGNORED_FLAGS = { + # compiling only flag, ignored because the creator of compilation + # database will explicitly set it. + '-c': 0, + # preprocessor macros, ignored because would cause duplicate entries in + # the output (the only difference would be these flags). this is actual + # finding from users, who suffered longer execution time caused by the + # duplicates. + '-MD': 0, + '-MMD': 0, + '-MG': 0, + '-MP': 0, + '-MF': 1, + '-MT': 1, + '-MQ': 1, + # linker options, ignored because for compilation database will contain + # compilation commands only. so, the compiler would ignore these flags + # anyway. the benefit to get rid of them is to make the output more + # readable. + '-static': 0, + '-shared': 0, + '-s': 0, + '-rdynamic': 0, + '-l': 1, + '-L': 1, + '-u': 1, + '-z': 1, + '-T': 1, + '-Xlinker': 1 +} + +# Known C/C++ compiler executable name patterns +COMPILER_PATTERNS = frozenset([ + re.compile(r'^(intercept-|analyze-|)c(c|\+\+)$'), + re.compile(r'^([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$'), + re.compile(r'^([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$'), + re.compile(r'^llvm-g(cc|\+\+)$'), +]) + + +def split_command(command): + """ Returns a value when the command is a compilation, None otherwise. + + The value on success is a named tuple with the following attributes: + + files: list of source files + flags: list of compile options + compiler: string value of 'c' or 'c++' """ + + # the result of this method + result = collections.namedtuple('Compilation', + ['compiler', 'flags', 'files']) + result.compiler = compiler_language(command) + result.flags = [] + result.files = [] + # quit right now, if the program was not a C/C++ compiler + if not result.compiler: + return None + # iterate on the compile options + args = iter(command[1:]) + for arg in args: + # quit when compilation pass is not involved + if arg in {'-E', '-S', '-cc1', '-M', '-MM', '-###'}: + return None + # ignore some flags + elif arg in IGNORED_FLAGS: + count = IGNORED_FLAGS[arg] + for _ in range(count): + next(args) + elif re.match(r'^-(l|L|Wl,).+', arg): + pass + # some parameters could look like filename, take as compile option + elif arg in {'-D', '-I'}: + result.flags.extend([arg, next(args)]) + # parameter which looks source file is taken... + elif re.match(r'^[^-].+', arg) and classify_source(arg): + result.files.append(arg) + # and consider everything else as compile option. + else: + result.flags.append(arg) + # do extra check on number of source files + return result if result.files else None + + +def classify_source(filename, c_compiler=True): + """ Return the language from file name extension. """ + + mapping = { + '.c': 'c' if c_compiler else 'c++', + '.i': 'c-cpp-output' if c_compiler else 'c++-cpp-output', + '.ii': 'c++-cpp-output', + '.m': 'objective-c', + '.mi': 'objective-c-cpp-output', + '.mm': 'objective-c++', + '.mii': 'objective-c++-cpp-output', + '.C': 'c++', + '.cc': 'c++', + '.CC': 'c++', + '.cp': 'c++', + '.cpp': 'c++', + '.cxx': 'c++', + '.c++': 'c++', + '.C++': 'c++', + '.txx': 'c++' + } + + __, extension = os.path.splitext(os.path.basename(filename)) + return mapping.get(extension) + + +def compiler_language(command): + """ A predicate to decide the command is a compiler call or not. + + Returns 'c' or 'c++' when it match. None otherwise. """ + + cplusplus = re.compile(r'^(.+)(\+\+)(-.+|)$') + + if command: + executable = os.path.basename(command[0]) + if any(pattern.match(executable) for pattern in COMPILER_PATTERNS): + return 'c++' if cplusplus.match(executable) else 'c' + return None diff --git a/tools/scan-build-py/libscanbuild/intercept.py b/tools/scan-build-py/libscanbuild/intercept.py index 6062e2ea8ca9..6a9f75349fb5 100644 --- a/tools/scan-build-py/libscanbuild/intercept.py +++ b/tools/scan-build-py/libscanbuild/intercept.py @@ -31,9 +31,9 @@ import argparse import logging import subprocess from libear import build_libear, TemporaryDirectory -from libscanbuild import duplicate_check, tempdir, initialize_logging from libscanbuild import command_entry_point -from libscanbuild.command import Action, classify_parameters +from libscanbuild import duplicate_check, tempdir, initialize_logging +from libscanbuild.compilation import split_command from libscanbuild.shell import encode, decode __all__ = ['capture', 'intercept_build_main', 'intercept_build_wrapper'] @@ -72,23 +72,23 @@ def capture(args, bin_dir): from the arguments. And do shell escaping on the command. To support incremental builds, it is desired to read elements from - an existing compilation database from a previous run. These elemets + an existing compilation database from a previous run. These elements shall be merged with the new elements. """ # create entries from the current run current = itertools.chain.from_iterable( # creates a sequence of entry generators from an exec, - # but filter out non compiler calls before. - (format_entry(x) for x in commands if is_compiler_call(x))) + format_entry(command) for command in commands) # read entries from previous run - if 'append' in args and args.append and os.path.exists(args.cdb): + if 'append' in args and args.append and os.path.isfile(args.cdb): with open(args.cdb) as handle: previous = iter(json.load(handle)) else: previous = iter([]) # filter out duplicate entries from both duplicate = duplicate_check(entry_hash) - return (entry for entry in itertools.chain(previous, current) + return (entry + 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: @@ -98,14 +98,14 @@ def capture(args, bin_dir): exit_code = subprocess.call(args.build, env=environment) logging.info('build finished with exit code: %d', exit_code) # read the intercepted exec calls - commands = itertools.chain.from_iterable( + exec_traces = itertools.chain.from_iterable( parse_exec_trace(os.path.join(tmp_dir, filename)) for filename in sorted(glob.iglob(os.path.join(tmp_dir, '*.cmd')))) # do post processing only if that was requested if 'raw_entries' not in args or not args.raw_entries: - entries = post_processing(commands) + entries = post_processing(exec_traces) else: - entries = commands + entries = exec_traces # dump the compilation database with open(args.cdb, 'w+') as handle: json.dump(list(entries), handle, sort_keys=True, indent=4) @@ -209,7 +209,7 @@ def parse_exec_trace(filename): } -def format_entry(entry): +def format_entry(exec_trace): """ Generate the desired fields for compilation database entries. """ def abspath(cwd, name): @@ -217,40 +217,20 @@ def format_entry(entry): fullname = name if os.path.isabs(name) else os.path.join(cwd, name) return os.path.normpath(fullname) - logging.debug('format this command: %s', entry['command']) - atoms = classify_parameters(entry['command']) - if atoms['action'] <= Action.Compile: - for source in atoms['files']: - compiler = 'c++' if atoms['c++'] else 'cc' - flags = atoms['compile_options'] - flags += ['-o', atoms['output']] if atoms['output'] else [] - flags += ['-x', atoms['language']] if 'language' in atoms else [] - flags += [elem - for arch in atoms.get('archs_seen', []) - for elem in ['-arch', arch]] - command = [compiler, '-c'] + flags + [source] + logging.debug('format this command: %s', exec_trace['command']) + compilation = split_command(exec_trace['command']) + if compilation: + for source in compilation.files: + compiler = 'c++' if compilation.compiler == 'c++' else 'cc' + command = [compiler, '-c'] + compilation.flags + [source] logging.debug('formated as: %s', command) yield { - 'directory': entry['directory'], + 'directory': exec_trace['directory'], 'command': encode(command), - 'file': abspath(entry['directory'], source) + 'file': abspath(exec_trace['directory'], source) } -def is_compiler_call(entry): - """ A predicate to decide the entry is a compiler call or not. """ - - patterns = [ - re.compile(r'^([^/]*/)*intercept-c(c|\+\+)$'), - re.compile(r'^([^/]*/)*c(c|\+\+)$'), - re.compile(r'^([^/]*/)*([^-]*-)*[mg](cc|\+\+)(-\d+(\.\d+){0,2})?$'), - re.compile(r'^([^/]*/)*([^-]*-)*clang(\+\+)?(-\d+(\.\d+){0,2})?$'), - re.compile(r'^([^/]*/)*llvm-g(cc|\+\+)$'), - ] - executable = entry['command'][0] - return any((pattern.match(executable) for pattern in patterns)) - - def is_preload_disabled(platform): """ Library-based interposition will fail silently if SIP is enabled, so this should be detected. You can detect whether SIP is enabled on diff --git a/tools/scan-build-py/libscanbuild/report.py b/tools/scan-build-py/libscanbuild/report.py index efc0a55de619..5c33319e206d 100644 --- a/tools/scan-build-py/libscanbuild/report.py +++ b/tools/scan-build-py/libscanbuild/report.py @@ -35,7 +35,12 @@ def report_directory(hint, keep): keep -- a boolean value to keep or delete the empty report directory. """ stamp = time.strftime('scan-build-%Y-%m-%d-%H%M%S-', time.localtime()) - name = tempfile.mkdtemp(prefix=stamp, dir=hint) + + parentdir = os.path.abspath(hint) + if not os.path.exists(parentdir): + os.makedirs(parentdir) + + name = tempfile.mkdtemp(prefix=stamp, dir=parentdir) logging.info('Report directory created: %s', name) diff --git a/tools/scan-build-py/libscanbuild/runner.py b/tools/scan-build-py/libscanbuild/runner.py index 248ca90ad3e6..628ad90d627a 100644 --- a/tools/scan-build-py/libscanbuild/runner.py +++ b/tools/scan-build-py/libscanbuild/runner.py @@ -5,18 +5,44 @@ # 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.command import classify_parameters, Action, classify_source -from libscanbuild.clang import get_arguments, get_version +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. @@ -29,8 +55,8 @@ def require(required): 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__)) + raise KeyError('{0} not passed to {1}'.format( + key, function.__name__)) return function(*args, **kwargs) @@ -39,9 +65,15 @@ def require(required): return decorator -@require(['command', 'directory', 'file', # an entry from compilation database - 'clang', 'direct_args', # compiler name, and arguments from command - 'output_dir', 'output_format', 'output_failures']) +@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. @@ -57,16 +89,17 @@ def run(opts): 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(decode(command))) + opts.update(classify_parameters(command)) - return action_check(opts) + return arch_check(opts) except Exception: logging.error("Problem occured during analyzis.", exc_info=1) return None -@require(['report', 'directory', 'clang', 'output_dir', 'language', 'file', +@require(['clang', 'directory', 'flags', 'file', 'output_dir', 'language', 'error_type', 'error_output', 'exit_code']) def report_failure(opts): """ Create report when analyzer failed. @@ -95,36 +128,49 @@ def report_failure(opts): dir=destination(opts)) os.close(handle) cwd = opts['directory'] - cmd = get_arguments([opts['clang']] + opts['report'] + ['-o', name], cwd) + cmd = get_arguments([opts['clang'], '-fsyntax-only', '-E'] + + opts['flags'] + [opts['file'], '-o', name], cwd) logging.debug('exec command in %s: %s', cwd, ' '.join(cmd)) subprocess.call(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(cmd[0])) + 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() - + # return with the previous step exit code and output return { 'error_output': opts['error_output'], 'exit_code': opts['exit_code'] } -@require(['clang', 'analyze', 'directory', 'output']) +@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 output(): + """ 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'] + cwd = opts['directory'] - cmd = get_arguments([opts['clang']] + opts['analyze'] + opts['output'], + cmd = get_arguments([opts['clang'], '--analyze'] + opts['direct_args'] + + opts['flags'] + [opts['file'], '-o', output()], cwd) logging.debug('exec command in %s: %s', cwd, ' '.join(cmd)) child = subprocess.Popen(cmd, @@ -144,113 +190,124 @@ def run_analyzer(opts, continuation=report_failure): 'exit_code': child.returncode }) return continuation(opts) + # return the output for logging and exit code for testing return {'error_output': output, 'exit_code': child.returncode} -@require(['output_dir']) -def set_analyzer_output(opts, continuation=run_analyzer): - """ Create output file if was requested. - - This plays a role only if .plist files are requested. """ +@require(['flags', 'force_debug']) +def filter_debug_flags(opts, continuation=run_analyzer): + """ Filter out nondebug macros when requested. """ - if opts.get('output_format') in {'plist', 'plist-html'}: - with tempfile.NamedTemporaryFile(prefix='report-', - suffix='.plist', - delete=False, - dir=opts['output_dir']) as output: - opts.update({'output': ['-o', output.name]}) - return continuation(opts) - else: - opts.update({'output': ['-o', opts['output_dir']]}) - return continuation(opts) + 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(['file', 'directory', 'clang', 'direct_args', 'language', - 'output_dir', 'output_format', 'output_failures']) -def create_commands(opts, continuation=set_analyzer_output): - """ Create command to run analyzer or failure report generation. - It generates commands (from compilation database entries) which contains - enough information to run the analyzer (and the crash report generation - if that was requested). """ +@require(['file', 'directory']) +def set_file_path_relative(opts, continuation=filter_debug_flags): + """ Set source file path to relative to the working directory. - common = [] - if 'arch' in opts: - common.extend(['-arch', opts.pop('arch')]) - common.extend(opts.pop('compile_options', [])) - common.extend(['-x', opts['language']]) - common.append(os.path.relpath(opts['file'], opts['directory'])) + The only purpose of this function is to pass the SATestBuild.py tests. """ - opts.update({ - 'analyze': ['--analyze'] + opts['direct_args'] + common, - 'report': ['-fsyntax-only', '-E'] + common - }) + opts.update({'file': os.path.relpath(opts['file'], opts['directory'])}) return continuation(opts) -@require(['file', 'c++']) -def language_check(opts, continuation=create_commands): +@require(['language', 'compiler', 'file', 'flags']) +def language_check(opts, continuation=set_file_path_relative): """ Find out the language from command line parameters or file name extension. The decision also influenced by the compiler invocation. """ - accepteds = { + accepted = frozenset({ 'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output', 'c++-cpp-output', 'objective-c-cpp-output' - } + }) - key = 'language' - language = opts[key] if key in opts else \ - classify_source(opts['file'], opts['c++']) + # 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 accepteds: + elif language not in accepted: logging.debug('skip analysis, language not supported') return None else: logging.debug('analysis, language: %s', language) - opts.update({key: language}) + opts.update({'language': language, + 'flags': ['-x', language] + opts['flags']}) return continuation(opts) -@require([]) +@require(['arch_list', 'flags']) def arch_check(opts, continuation=language_check): """ Do run analyzer through one of the given architectures. """ - disableds = {'ppc', 'ppc64'} + disabled = frozenset({'ppc', 'ppc64'}) - key = 'archs_seen' - if key in opts: + received_list = opts.pop('arch_list') + if received_list: # filter out disabled architectures and -arch switches - archs = [a for a in opts[key] if a not in disableds] - - if not archs: - logging.debug('skip analysis, found not supported arch') - return None - else: + 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. - arch = archs.pop() - logging.debug('analysis, on arch: %s', arch) + current = filtered_list.pop() + logging.debug('analysis, on arch: %s', current) - opts.update({'arch': arch}) - del opts[key] + 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) -@require(['action']) -def action_check(opts, continuation=arch_check): - """ Continue analysis only if it compilation or link. """ +def classify_parameters(command): + """ Prepare compiler flags (filters some and add others) and take out + language (-x) and architecture (-arch) flags for future processing. """ - if opts.pop('action') <= Action.Compile: - return continuation(opts) - else: - logging.debug('skip analysis, not compilation nor link') - return None + 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/functional/cases/test_create_cdb.py b/tools/scan-build-py/tests/functional/cases/test_create_cdb.py index 6d449ba39c0b..c26fce0bbf8a 100644 --- a/tools/scan-build-py/tests/functional/cases/test_create_cdb.py +++ b/tools/scan-build-py/tests/functional/cases/test_create_cdb.py @@ -4,7 +4,7 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -from ...unit import fixtures +import libear from . import make_args, silent_check_call, silent_call, create_empty_file import unittest @@ -28,13 +28,13 @@ class CompilationDatabaseTest(unittest.TestCase): return len(content) def test_successful_build(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = self.run_intercept(tmpdir, ['build_regular']) self.assertTrue(os.path.isfile(result)) self.assertEqual(5, self.count_entries(result)) def test_successful_build_with_wrapper(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = os.path.join(tmpdir, 'cdb.json') make = make_args(tmpdir) + ['build_regular'] silent_check_call(['intercept-build', '--cdb', result, @@ -44,14 +44,14 @@ class CompilationDatabaseTest(unittest.TestCase): @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu make return -11') def test_successful_build_parallel(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = self.run_intercept(tmpdir, ['-j', '4', 'build_regular']) self.assertTrue(os.path.isfile(result)) self.assertEqual(5, self.count_entries(result)) @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu env remove clang from path') def test_successful_build_on_empty_env(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = os.path.join(tmpdir, 'cdb.json') make = make_args(tmpdir) + ['CC=clang', 'build_regular'] silent_check_call(['intercept-build', '--cdb', result, @@ -60,13 +60,13 @@ class CompilationDatabaseTest(unittest.TestCase): self.assertEqual(5, self.count_entries(result)) def test_successful_build_all_in_one(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = self.run_intercept(tmpdir, ['build_all_in_one']) self.assertTrue(os.path.isfile(result)) self.assertEqual(5, self.count_entries(result)) def test_not_successful_build(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = os.path.join(tmpdir, 'cdb.json') make = make_args(tmpdir) + ['build_broken'] silent_call( @@ -84,12 +84,12 @@ class ExitCodeTest(unittest.TestCase): ['intercept-build', '--cdb', result] + make) def test_successful_build(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: exitcode = self.run_intercept(tmpdir, 'build_clean') self.assertFalse(exitcode) def test_not_successful_build(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: exitcode = self.run_intercept(tmpdir, 'build_broken') self.assertTrue(exitcode) @@ -110,7 +110,7 @@ class ResumeFeatureTest(unittest.TestCase): return len(content) def test_overwrite_existing_cdb(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = self.run_intercept(tmpdir, 'build_clean', []) self.assertTrue(os.path.isfile(result)) result = self.run_intercept(tmpdir, 'build_regular', []) @@ -118,7 +118,7 @@ class ResumeFeatureTest(unittest.TestCase): self.assertEqual(2, self.count_entries(result)) def test_append_to_existing_cdb(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: result = self.run_intercept(tmpdir, 'build_clean', []) self.assertTrue(os.path.isfile(result)) result = self.run_intercept(tmpdir, 'build_regular', ['--append']) @@ -138,7 +138,7 @@ class ResultFormatingTest(unittest.TestCase): return content def assert_creates_number_of_entries(self, command, count): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: filename = os.path.join(tmpdir, 'test.c') create_empty_file(filename) command.append(filename) @@ -153,7 +153,7 @@ class ResultFormatingTest(unittest.TestCase): self.assert_creates_number_of_entries(['cc', '-c', '-MM'], 0) def assert_command_creates_entry(self, command, expected): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: filename = os.path.join(tmpdir, command[-1]) create_empty_file(filename) cmd = ['sh', '-c', ' '.join(command)] diff --git a/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py b/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py index 329a477e03d7..d58a61217b71 100644 --- a/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py +++ b/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py @@ -4,7 +4,7 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -from ...unit import fixtures +import libear import unittest import os.path @@ -45,6 +45,6 @@ class ExecAnatomyTest(unittest.TestCase): def test_all_exec_calls(self): this_dir, _ = os.path.split(__file__) source_dir = os.path.normpath(os.path.join(this_dir, '..', 'exec')) - with fixtures.TempDir() as tmp_dir: + with libear.TemporaryDirectory() as tmp_dir: expected, result = run(source_dir, tmp_dir) self.assertEqualJson(expected, result) diff --git a/tools/scan-build-py/tests/functional/cases/test_from_cdb.py b/tools/scan-build-py/tests/functional/cases/test_from_cdb.py index c579020db22c..50264005c811 100644 --- a/tools/scan-build-py/tests/functional/cases/test_from_cdb.py +++ b/tools/scan-build-py/tests/functional/cases/test_from_cdb.py @@ -4,13 +4,12 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -from ...unit import fixtures +import libear from . import call_and_report import unittest import os.path import string -import subprocess import glob @@ -37,19 +36,19 @@ def run_analyzer(directory, cdb, args): class OutputDirectoryTest(unittest.TestCase): def test_regular_keeps_report_dir(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, []) self.assertTrue(os.path.isdir(reportdir)) def test_clear_deletes_report_dir(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('clean', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, []) self.assertFalse(os.path.isdir(reportdir)) def test_clear_keeps_report_dir_when_asked(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('clean', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--keep-empty']) self.assertTrue(os.path.isdir(reportdir)) @@ -57,38 +56,38 @@ class OutputDirectoryTest(unittest.TestCase): class ExitCodeTest(unittest.TestCase): def test_regular_does_not_set_exit_code(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, __ = run_analyzer(tmpdir, cdb, []) self.assertFalse(exit_code) def test_clear_does_not_set_exit_code(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('clean', tmpdir) exit_code, __ = run_analyzer(tmpdir, cdb, []) self.assertFalse(exit_code) def test_regular_sets_exit_code_if_asked(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs']) self.assertTrue(exit_code) def test_clear_does_not_set_exit_code_if_asked(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('clean', tmpdir) exit_code, __ = run_analyzer(tmpdir, cdb, ['--status-bugs']) self.assertFalse(exit_code) def test_regular_sets_exit_code_if_asked_from_plist(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, __ = run_analyzer( tmpdir, cdb, ['--status-bugs', '--plist']) self.assertTrue(exit_code) def test_clear_does_not_set_exit_code_if_asked_from_plist(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('clean', tmpdir) exit_code, __ = run_analyzer( tmpdir, cdb, ['--status-bugs', '--plist']) @@ -105,7 +104,7 @@ class OutputFormatTest(unittest.TestCase): return len(glob.glob(os.path.join(directory, 'report-*.plist'))) def test_default_creates_html_report(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, []) self.assertTrue( @@ -114,7 +113,7 @@ class OutputFormatTest(unittest.TestCase): self.assertEqual(self.get_plist_count(reportdir), 0) def test_plist_and_html_creates_html_report(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist-html']) self.assertTrue( @@ -123,7 +122,7 @@ class OutputFormatTest(unittest.TestCase): self.assertEqual(self.get_plist_count(reportdir), 5) def test_plist_does_not_creates_html_report(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('regular', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist']) self.assertFalse( @@ -134,14 +133,14 @@ class OutputFormatTest(unittest.TestCase): class FailureReportTest(unittest.TestCase): def test_broken_creates_failure_reports(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('broken', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, []) self.assertTrue( os.path.isdir(os.path.join(reportdir, 'failures'))) def test_broken_does_not_creates_failure_reports(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('broken', tmpdir) exit_code, reportdir = run_analyzer( tmpdir, cdb, ['--no-failure-reports']) @@ -170,13 +169,13 @@ class TitleTest(unittest.TestCase): self.assertEqual(result['page'], expected) def test_default_title_in_report(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('broken', tmpdir) exit_code, reportdir = run_analyzer(tmpdir, cdb, []) self.assertTitleEqual(reportdir, 'src - analyzer results') def test_given_title_in_report(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: cdb = prepare_cdb('broken', tmpdir) exit_code, reportdir = run_analyzer( tmpdir, cdb, ['--html-title', 'this is the title']) diff --git a/tools/scan-build-py/tests/functional/cases/test_from_cmd.py b/tools/scan-build-py/tests/functional/cases/test_from_cmd.py index fe7ecf69915b..0eee4bb928f0 100644 --- a/tools/scan-build-py/tests/functional/cases/test_from_cmd.py +++ b/tools/scan-build-py/tests/functional/cases/test_from_cmd.py @@ -4,7 +4,7 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -from ...unit import fixtures +import libear from . import make_args, check_call_and_report, create_empty_file import unittest @@ -22,19 +22,19 @@ class OutputDirectoryTest(unittest.TestCase): cmd) def test_regular_keeps_report_dir(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: make = make_args(tmpdir) + ['build_regular'] outdir = self.run_analyzer(tmpdir, [], make) self.assertTrue(os.path.isdir(outdir)) def test_clear_deletes_report_dir(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: make = make_args(tmpdir) + ['build_clean'] outdir = self.run_analyzer(tmpdir, [], make) self.assertFalse(os.path.isdir(outdir)) def test_clear_keeps_report_dir_when_asked(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: make = make_args(tmpdir) + ['build_clean'] outdir = self.run_analyzer(tmpdir, ['--keep-empty'], make) self.assertTrue(os.path.isdir(outdir)) @@ -47,7 +47,7 @@ class RunAnalyzerTest(unittest.TestCase): return len(glob.glob(os.path.join(directory, 'report-*.plist'))) def test_interposition_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: make = make_args(tmpdir) + ['build_regular'] outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], @@ -57,7 +57,7 @@ class RunAnalyzerTest(unittest.TestCase): self.assertEqual(self.get_plist_count(outdir), 5) def test_intercept_wrapper_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: make = make_args(tmpdir) + ['build_regular'] outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--intercept-first', @@ -68,7 +68,7 @@ class RunAnalyzerTest(unittest.TestCase): self.assertEqual(self.get_plist_count(outdir), 5) def test_intercept_library_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: make = make_args(tmpdir) + ['build_regular'] outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--intercept-first'], @@ -88,21 +88,21 @@ class RunAnalyzerTest(unittest.TestCase): return ['sh', '-c', command] def test_interposition_cc_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], self.compile_empty_source_file(tmpdir, False)) self.assertEqual(self.get_plist_count(outdir), 1) def test_interposition_cxx_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], self.compile_empty_source_file(tmpdir, True)) self.assertEqual(self.get_plist_count(outdir), 1) def test_intercept_cc_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--override-compiler', '--intercept-first'], @@ -110,7 +110,7 @@ class RunAnalyzerTest(unittest.TestCase): self.assertEqual(self.get_plist_count(outdir), 1) def test_intercept_cxx_works(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: outdir = check_call_and_report( ['scan-build', '--plist', '-o', tmpdir, '--override-compiler', '--intercept-first'], diff --git a/tools/scan-build-py/tests/functional/exec/CMakeLists.txt b/tools/scan-build-py/tests/functional/exec/CMakeLists.txt index 6e5d2e966184..42ee1d11db82 100644 --- a/tools/scan-build-py/tests/functional/exec/CMakeLists.txt +++ b/tools/scan-build-py/tests/functional/exec/CMakeLists.txt @@ -1,6 +1,6 @@ project(exec C) -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.4.3) include(CheckCCompilerFlag) check_c_compiler_flag("-std=c99" C99_SUPPORTED) diff --git a/tools/scan-build-py/tests/unit/__init__.py b/tools/scan-build-py/tests/unit/__init__.py index 4fa9edc0fff1..dc8bf12eb47c 100644 --- a/tools/scan-build-py/tests/unit/__init__.py +++ b/tools/scan-build-py/tests/unit/__init__.py @@ -4,7 +4,8 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. -from . import test_command +from . import test_libear +from . import test_compilation from . import test_clang from . import test_runner from . import test_report @@ -13,8 +14,9 @@ from . import test_intercept from . import test_shell -def load_tests(loader, suite, pattern): - suite.addTests(loader.loadTestsFromModule(test_command)) +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)) diff --git a/tools/scan-build-py/tests/unit/fixtures.py b/tools/scan-build-py/tests/unit/fixtures.py deleted file mode 100644 index d80f5e64774c..000000000000 --- a/tools/scan-build-py/tests/unit/fixtures.py +++ /dev/null @@ -1,40 +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 contextlib -import tempfile -import shutil -import unittest - - -class Spy(object): - def __init__(self): - self.arg = None - self.success = 0 - - def call(self, params): - self.arg = params - return self.success - - -@contextlib.contextmanager -def TempDir(): - name = tempfile.mkdtemp(prefix='scan-build-test-') - try: - yield name - finally: - shutil.rmtree(name) - - -class TestCase(unittest.TestCase): - def assertIn(self, element, collection): - found = False - for it in collection: - if element == it: - found = True - - self.assertTrue(found, '{0} does not have {1}'.format(collection, - element)) diff --git a/tools/scan-build-py/tests/unit/test_analyze.py b/tools/scan-build-py/tests/unit/test_analyze.py index b77db4818024..481cc0c0993b 100644 --- a/tools/scan-build-py/tests/unit/test_analyze.py +++ b/tools/scan-build-py/tests/unit/test_analyze.py @@ -5,4 +5,3 @@ # License. See LICENSE.TXT for details. import libscanbuild.analyze as sut -from . import fixtures diff --git a/tools/scan-build-py/tests/unit/test_clang.py b/tools/scan-build-py/tests/unit/test_clang.py index 2f1fd79d4a92..04414a85b828 100644 --- a/tools/scan-build-py/tests/unit/test_clang.py +++ b/tools/scan-build-py/tests/unit/test_clang.py @@ -4,14 +4,15 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. +import libear import libscanbuild.clang as sut -from . import fixtures +import unittest import os.path -class GetClangArgumentsTest(fixtures.TestCase): +class GetClangArgumentsTest(unittest.TestCase): def test_get_clang_arguments(self): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: filename = os.path.join(tmpdir, 'test.c') with open(filename, 'w') as handle: handle.write('') @@ -20,8 +21,8 @@ class GetClangArgumentsTest(fixtures.TestCase): ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'], tmpdir) - self.assertIn('NDEBUG', result) - self.assertIn('var="this is it"', result) + self.assertTrue('NDEBUG' in result) + self.assertTrue('var="this is it"' in result) def test_get_clang_arguments_fails(self): self.assertRaises( @@ -29,7 +30,7 @@ class GetClangArgumentsTest(fixtures.TestCase): ['clang', '-###', '-fsyntax-only', '-x', 'c', 'notexist.c'], '.') -class GetCheckersTest(fixtures.TestCase): +class GetCheckersTest(unittest.TestCase): def test_get_checkers(self): # this test is only to see is not crashing result = sut.get_checkers('clang', []) diff --git a/tools/scan-build-py/tests/unit/test_command.py b/tools/scan-build-py/tests/unit/test_command.py deleted file mode 100644 index 9a6aae65c605..000000000000 --- a/tools/scan-build-py/tests/unit/test_command.py +++ /dev/null @@ -1,193 +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 libscanbuild.command as sut -from . import fixtures -import unittest - - -class ParseTest(unittest.TestCase): - - def test_action(self): - def test(expected, cmd): - opts = sut.classify_parameters(cmd) - self.assertEqual(expected, opts['action']) - - Link = sut.Action.Link - test(Link, ['clang', 'source.c']) - - Compile = sut.Action.Compile - test(Compile, ['clang', '-c', 'source.c']) - test(Compile, ['clang', '-c', 'source.c', '-MF', 'source.d']) - - Preprocess = sut.Action.Ignored - test(Preprocess, ['clang', '-E', 'source.c']) - test(Preprocess, ['clang', '-c', '-E', 'source.c']) - test(Preprocess, ['clang', '-c', '-M', 'source.c']) - test(Preprocess, ['clang', '-c', '-MM', 'source.c']) - - def test_optimalizations(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('compile_options', []) - - self.assertEqual(['-O'], test(['clang', '-c', 'source.c', '-O'])) - self.assertEqual(['-O1'], test(['clang', '-c', 'source.c', '-O1'])) - self.assertEqual(['-Os'], test(['clang', '-c', 'source.c', '-Os'])) - self.assertEqual(['-O2'], test(['clang', '-c', 'source.c', '-O2'])) - self.assertEqual(['-O3'], test(['clang', '-c', 'source.c', '-O3'])) - - def test_language(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('language') - - self.assertEqual(None, test(['clang', '-c', 'source.c'])) - self.assertEqual('c', test(['clang', '-c', 'source.c', '-x', 'c'])) - self.assertEqual('cpp', test(['clang', '-c', 'source.c', '-x', 'cpp'])) - - def test_output(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('output') - - self.assertEqual(None, test(['clang', '-c', 'source.c'])) - self.assertEqual('source.o', - test(['clang', '-c', '-o', 'source.o', 'source.c'])) - - def test_arch(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('archs_seen', []) - - eq = self.assertEqual - - eq([], test(['clang', '-c', 'source.c'])) - eq(['mips'], - test(['clang', '-c', 'source.c', '-arch', 'mips'])) - eq(['mips', 'i386'], - test(['clang', '-c', 'source.c', '-arch', 'mips', '-arch', 'i386'])) - - def test_input_file(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('files', []) - - eq = self.assertEqual - - eq(['src.c'], test(['clang', 'src.c'])) - eq(['src.c'], test(['clang', '-c', 'src.c'])) - eq(['s1.c', 's2.c'], test(['clang', '-c', 's1.c', 's2.c'])) - - def test_include(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('compile_options', []) - - eq = self.assertEqual - - eq([], test(['clang', '-c', 'src.c'])) - eq(['-include', '/usr/local/include'], - test(['clang', '-c', 'src.c', '-include', '/usr/local/include'])) - eq(['-I.'], - test(['clang', '-c', 'src.c', '-I.'])) - eq(['-I', '.'], - test(['clang', '-c', 'src.c', '-I', '.'])) - eq(['-I/usr/local/include'], - test(['clang', '-c', 'src.c', '-I/usr/local/include'])) - eq(['-I', '/usr/local/include'], - test(['clang', '-c', 'src.c', '-I', '/usr/local/include'])) - eq(['-I/opt', '-I', '/opt/otp/include'], - test(['clang', '-c', 'src.c', '-I/opt', '-I', '/opt/otp/include'])) - eq(['-isystem', '/path'], - test(['clang', '-c', 'src.c', '-isystem', '/path'])) - eq(['-isystem=/path'], - test(['clang', '-c', 'src.c', '-isystem=/path'])) - - def test_define(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('compile_options', []) - - eq = self.assertEqual - - eq([], test(['clang', '-c', 'src.c'])) - eq(['-DNDEBUG'], - test(['clang', '-c', 'src.c', '-DNDEBUG'])) - eq(['-UNDEBUG'], - test(['clang', '-c', 'src.c', '-UNDEBUG'])) - eq(['-Dvar1=val1', '-Dvar2=val2'], - test(['clang', '-c', 'src.c', '-Dvar1=val1', '-Dvar2=val2'])) - eq(['-Dvar="val ues"'], - test(['clang', '-c', 'src.c', '-Dvar="val ues"'])) - - def test_ignored_flags(self): - def test(flags): - cmd = ['clang', 'src.o'] - opts = sut.classify_parameters(cmd + flags) - self.assertEqual(['src.o'], opts.get('compile_options')) - - test([]) - test(['-lrt', '-L/opt/company/lib']) - test(['-static']) - test(['-Wnoexcept', '-Wall']) - test(['-mtune=i386', '-mcpu=i386']) - - def test_compile_only_flags(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('compile_options', []) - - eq = self.assertEqual - - eq(['-std=C99'], - test(['clang', '-c', 'src.c', '-std=C99'])) - eq(['-nostdinc'], - test(['clang', '-c', 'src.c', '-nostdinc'])) - eq(['-isystem', '/image/debian'], - test(['clang', '-c', 'src.c', '-isystem', '/image/debian'])) - eq(['-iprefix', '/usr/local'], - test(['clang', '-c', 'src.c', '-iprefix', '/usr/local'])) - eq(['-iquote=me'], - test(['clang', '-c', 'src.c', '-iquote=me'])) - eq(['-iquote', 'me'], - test(['clang', '-c', 'src.c', '-iquote', 'me'])) - - def test_compile_and_link_flags(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('compile_options', []) - - eq = self.assertEqual - - eq(['-fsinged-char'], - test(['clang', '-c', 'src.c', '-fsinged-char'])) - eq(['-fPIC'], - test(['clang', '-c', 'src.c', '-fPIC'])) - eq(['-stdlib=libc++'], - test(['clang', '-c', 'src.c', '-stdlib=libc++'])) - eq(['--sysroot', '/'], - test(['clang', '-c', 'src.c', '--sysroot', '/'])) - eq(['-isysroot', '/'], - test(['clang', '-c', 'src.c', '-isysroot', '/'])) - eq([], - test(['clang', '-c', 'src.c', '-fsyntax-only'])) - eq([], - test(['clang', '-c', 'src.c', '-sectorder', 'a', 'b', 'c'])) - - def test_detect_cxx_from_compiler_name(self): - def test(cmd): - opts = sut.classify_parameters(cmd) - return opts.get('c++') - - eq = self.assertEqual - - eq(False, test(['cc', '-c', 'src.c'])) - eq(True, test(['c++', '-c', 'src.c'])) - eq(False, test(['clang', '-c', 'src.c'])) - eq(True, test(['clang++', '-c', 'src.c'])) - eq(False, test(['gcc', '-c', 'src.c'])) - eq(True, test(['g++', '-c', 'src.c'])) diff --git a/tools/scan-build-py/tests/unit/test_compilation.py b/tools/scan-build-py/tests/unit/test_compilation.py new file mode 100644 index 000000000000..124febaf0195 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_compilation.py @@ -0,0 +1,122 @@ +# -*- 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 libscanbuild.compilation as sut +import unittest + + +class CompilerTest(unittest.TestCase): + + def test_is_compiler_call(self): + self.assertIsNotNone(sut.compiler_language(['clang'])) + self.assertIsNotNone(sut.compiler_language(['clang-3.6'])) + self.assertIsNotNone(sut.compiler_language(['clang++'])) + self.assertIsNotNone(sut.compiler_language(['clang++-3.5.1'])) + self.assertIsNotNone(sut.compiler_language(['cc'])) + self.assertIsNotNone(sut.compiler_language(['c++'])) + self.assertIsNotNone(sut.compiler_language(['gcc'])) + self.assertIsNotNone(sut.compiler_language(['g++'])) + self.assertIsNotNone(sut.compiler_language(['/usr/local/bin/gcc'])) + self.assertIsNotNone(sut.compiler_language(['/usr/local/bin/g++'])) + self.assertIsNotNone(sut.compiler_language(['/usr/local/bin/clang'])) + self.assertIsNotNone( + sut.compiler_language(['armv7_neno-linux-gnueabi-g++'])) + + self.assertIsNone(sut.compiler_language([])) + self.assertIsNone(sut.compiler_language([''])) + self.assertIsNone(sut.compiler_language(['ld'])) + self.assertIsNone(sut.compiler_language(['as'])) + self.assertIsNone(sut.compiler_language(['/usr/local/bin/compiler'])) + + +class SplitTest(unittest.TestCase): + + def test_detect_cxx_from_compiler_name(self): + def test(cmd): + result = sut.split_command([cmd, '-c', 'src.c']) + self.assertIsNotNone(result, "wrong input for test") + return result.compiler == 'c++' + + self.assertFalse(test('cc')) + self.assertFalse(test('gcc')) + self.assertFalse(test('clang')) + + self.assertTrue(test('c++')) + self.assertTrue(test('g++')) + self.assertTrue(test('g++-5.3.1')) + self.assertTrue(test('clang++')) + self.assertTrue(test('clang++-3.7.1')) + self.assertTrue(test('armv7_neno-linux-gnueabi-g++')) + + def test_action(self): + self.assertIsNotNone(sut.split_command(['clang', 'source.c'])) + self.assertIsNotNone(sut.split_command(['clang', '-c', 'source.c'])) + self.assertIsNotNone(sut.split_command(['clang', '-c', 'source.c', + '-MF', 'a.d'])) + + self.assertIsNone(sut.split_command(['clang', '-E', 'source.c'])) + self.assertIsNone(sut.split_command(['clang', '-c', '-E', 'source.c'])) + self.assertIsNone(sut.split_command(['clang', '-c', '-M', 'source.c'])) + self.assertIsNone( + sut.split_command(['clang', '-c', '-MM', 'source.c'])) + + def test_source_file(self): + def test(expected, cmd): + self.assertEqual(expected, sut.split_command(cmd).files) + + test(['src.c'], ['clang', 'src.c']) + test(['src.c'], ['clang', '-c', 'src.c']) + test(['src.C'], ['clang', '-x', 'c', 'src.C']) + test(['src.cpp'], ['clang++', '-c', 'src.cpp']) + test(['s1.c', 's2.c'], ['clang', '-c', 's1.c', 's2.c']) + test(['s1.c', 's2.c'], ['cc', 's1.c', 's2.c', '-ldep', '-o', 'a.out']) + test(['src.c'], ['clang', '-c', '-I', './include', 'src.c']) + test(['src.c'], ['clang', '-c', '-I', '/opt/me/include', 'src.c']) + test(['src.c'], ['clang', '-c', '-D', 'config=file.c', 'src.c']) + + self.assertIsNone( + sut.split_command(['cc', 'this.o', 'that.o', '-o', 'a.out'])) + self.assertIsNone( + sut.split_command(['cc', 'this.o', '-lthat', '-o', 'a.out'])) + + def test_filter_flags(self): + def test(expected, flags): + command = ['clang', '-c', 'src.c'] + flags + self.assertEqual(expected, sut.split_command(command).flags) + + def same(expected): + test(expected, expected) + + def filtered(flags): + test([], flags) + + same([]) + same(['-I', '/opt/me/include', '-DNDEBUG', '-ULIMITS']) + same(['-O', '-O2']) + same(['-m32', '-mmms']) + same(['-Wall', '-Wno-unused', '-g', '-funroll-loops']) + + filtered([]) + filtered(['-lclien', '-L/opt/me/lib', '-L', '/opt/you/lib']) + filtered(['-static']) + filtered(['-MD', '-MT', 'something']) + filtered(['-MMD', '-MF', 'something']) + + +class SourceClassifierTest(unittest.TestCase): + + def test_sources(self): + self.assertIsNone(sut.classify_source('file.o')) + self.assertIsNone(sut.classify_source('file.exe')) + self.assertIsNone(sut.classify_source('/path/file.o')) + self.assertIsNone(sut.classify_source('clang')) + + self.assertEqual('c', sut.classify_source('file.c')) + self.assertEqual('c', sut.classify_source('./file.c')) + self.assertEqual('c', sut.classify_source('/path/file.c')) + self.assertEqual('c++', sut.classify_source('file.c', False)) + self.assertEqual('c++', sut.classify_source('./file.c', False)) + self.assertEqual('c++', sut.classify_source('/path/file.c', False)) diff --git a/tools/scan-build-py/tests/unit/test_intercept.py b/tools/scan-build-py/tests/unit/test_intercept.py index b6f01f36eeb3..5b6ed2cee1f6 100644 --- a/tools/scan-build-py/tests/unit/test_intercept.py +++ b/tools/scan-build-py/tests/unit/test_intercept.py @@ -4,62 +4,37 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. +import libear import libscanbuild.intercept as sut -from . import fixtures +import unittest import os.path -class InterceptUtilTest(fixtures.TestCase): - - def test_is_compiler_call_filter(self): - def test(command): - return sut.is_compiler_call({'command': [command]}) - - self.assertTrue(test('clang')) - self.assertTrue(test('clang-3.6')) - self.assertTrue(test('clang++')) - self.assertTrue(test('clang++-3.5.1')) - self.assertTrue(test('cc')) - self.assertTrue(test('c++')) - self.assertTrue(test('gcc')) - self.assertTrue(test('g++')) - self.assertTrue(test('/usr/local/bin/gcc')) - self.assertTrue(test('/usr/local/bin/g++')) - self.assertTrue(test('/usr/local/bin/clang')) - self.assertTrue(test('armv7_neno-linux-gnueabi-g++')) - - self.assertFalse(test('')) - self.assertFalse(test('ld')) - self.assertFalse(test('as')) - self.assertFalse(test('/usr/local/bin/compiler')) +class InterceptUtilTest(unittest.TestCase): def test_format_entry_filters_action(self): def test(command): - return list(sut.format_entry( - {'command': command, 'directory': '/opt/src/project'})) + trace = {'command': command, 'directory': '/opt/src/project'} + return list(sut.format_entry(trace)) self.assertTrue(test(['cc', '-c', 'file.c', '-o', 'file.o'])) self.assertFalse(test(['cc', '-E', 'file.c'])) self.assertFalse(test(['cc', '-MM', 'file.c'])) self.assertFalse(test(['cc', 'this.o', 'that.o', '-o', 'a.out'])) - self.assertFalse(test(['cc', '-print-prog-name'])) def test_format_entry_normalize_filename(self): - directory = os.path.join(os.sep, 'home', 'me', 'project') + parent = os.path.join(os.sep, 'home', 'me') + current = os.path.join(parent, 'project') - def test(command): - result = list(sut.format_entry( - {'command': command, 'directory': directory})) - return result[0]['file'] + def test(filename): + trace = {'directory': current, 'command': ['cc', '-c', filename]} + return list(sut.format_entry(trace))[0]['file'] - self.assertEqual(test(['cc', '-c', 'file.c']), - os.path.join(directory, 'file.c')) - self.assertEqual(test(['cc', '-c', './file.c']), - os.path.join(directory, 'file.c')) - self.assertEqual(test(['cc', '-c', '../file.c']), - os.path.join(os.path.dirname(directory), 'file.c')) - self.assertEqual(test(['cc', '-c', '/opt/file.c']), - '/opt/file.c') + self.assertEqual(os.path.join(current, 'file.c'), test('file.c')) + self.assertEqual(os.path.join(current, 'file.c'), test('./file.c')) + self.assertEqual(os.path.join(parent, 'file.c'), test('../file.c')) + self.assertEqual(os.path.join(current, 'file.c'), + test(os.path.join(current, 'file.c'))) def test_sip(self): def create_status_report(filename, message): @@ -92,7 +67,7 @@ class InterceptUtilTest(fixtures.TestCase): OSX = 'darwin' LINUX = 'linux' - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: try: saved = os.environ['PATH'] os.environ['PATH'] = tmpdir + ':' + saved diff --git a/tools/scan-build-py/tests/unit/test_libear.py b/tools/scan-build-py/tests/unit/test_libear.py new file mode 100644 index 000000000000..f5b928028965 --- /dev/null +++ b/tools/scan-build-py/tests/unit/test_libear.py @@ -0,0 +1,30 @@ +# -*- 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 as sut +import unittest +import os.path + + +class TemporaryDirectoryTest(unittest.TestCase): + def test_creates_directory(self): + dirname = None + with sut.TemporaryDirectory() as tmpdir: + self.assertTrue(os.path.isdir(tmpdir)) + dirname = tmpdir + self.assertIsNotNone(dirname) + self.assertFalse(os.path.exists(dirname)) + + def test_removes_directory_when_exception(self): + dirname = None + try: + with sut.TemporaryDirectory() as tmpdir: + self.assertTrue(os.path.isdir(tmpdir)) + dirname = tmpdir + raise RuntimeError('message') + except: + self.assertIsNotNone(dirname) + self.assertFalse(os.path.exists(dirname)) diff --git a/tools/scan-build-py/tests/unit/test_report.py b/tools/scan-build-py/tests/unit/test_report.py index d505afc20a89..3f249ce2aa0c 100644 --- a/tools/scan-build-py/tests/unit/test_report.py +++ b/tools/scan-build-py/tests/unit/test_report.py @@ -4,15 +4,15 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. +import libear import libscanbuild.report as sut -from . import fixtures import unittest import os import os.path def run_bug_parse(content): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: file_name = os.path.join(tmpdir, 'test.html') with open(file_name, 'w') as handle: handle.writelines(content) @@ -21,7 +21,7 @@ def run_bug_parse(content): def run_crash_parse(content, preproc): - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: file_name = os.path.join(tmpdir, preproc + '.info.txt') with open(file_name, 'w') as handle: handle.writelines(content) @@ -77,20 +77,22 @@ class ParseFileTest(unittest.TestCase): def test_parse_real_crash(self): import libscanbuild.runner as sut2 import re - with fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: filename = os.path.join(tmpdir, 'test.c') with open(filename, 'w') as handle: handle.write('int main() { return 0') # produce failure report - opts = {'directory': os.getcwd(), - 'clang': 'clang', - 'file': filename, - 'report': ['-fsyntax-only', '-E', filename], - 'language': 'c', - 'output_dir': tmpdir, - 'error_type': 'other_error', - 'error_output': 'some output', - 'exit_code': 13} + opts = { + 'clang': 'clang', + 'directory': os.getcwd(), + 'flags': [], + 'file': filename, + 'output_dir': tmpdir, + 'language': 'c', + 'error_type': 'other_error', + 'error_output': 'some output', + 'exit_code': 13 + } sut2.report_failure(opts) # find the info file pp_file = None @@ -123,7 +125,7 @@ class ReportMethodTest(unittest.TestCase): '/prefix/src/file')) -class GetPrefixFromCompilationDatabaseTest(fixtures.TestCase): +class GetPrefixFromCompilationDatabaseTest(unittest.TestCase): def test_with_different_filenames(self): self.assertEqual( diff --git a/tools/scan-build-py/tests/unit/test_runner.py b/tools/scan-build-py/tests/unit/test_runner.py index ea10051d8506..b4730a1c5191 100644 --- a/tools/scan-build-py/tests/unit/test_runner.py +++ b/tools/scan-build-py/tests/unit/test_runner.py @@ -4,96 +4,164 @@ # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. +import libear import libscanbuild.runner as sut -from . import fixtures import unittest import re import os import os.path -def run_analyzer(content, opts): - with fixtures.TempDir() as tmpdir: - filename = os.path.join(tmpdir, 'test.cpp') - with open(filename, 'w') as handle: - handle.write(content) +class FilteringFlagsTest(unittest.TestCase): - opts.update({ - 'directory': os.getcwd(), - 'clang': 'clang', - 'file': filename, - 'language': 'c++', - 'analyze': ['--analyze', '-x', 'c++', filename], - 'output': ['-o', tmpdir]}) - spy = fixtures.Spy() - result = sut.run_analyzer(opts, spy.call) - return (result, spy.arg) + 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) = run_analyzer(content, dict()) + (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) = run_analyzer(content, dict()) + (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) = run_analyzer(content, {'output_failures': True}) + (_, fwds) = RunAnalyzerTest.run_analyzer(content, True) self.assertEqual('crash', fwds['error_type']) self.assertEqual(1, fwds['exit_code']) self.assertTrue(len(fwds['error_output']) > 0) -class SetAnalyzerOutputTest(fixtures.TestCase): - - def test_not_defined(self): - with fixtures.TempDir() as tmpdir: - opts = {'output_dir': tmpdir} - spy = fixtures.Spy() - sut.set_analyzer_output(opts, spy.call) - self.assertTrue(os.path.exists(spy.arg['output'][1])) - self.assertTrue(os.path.isdir(spy.arg['output'][1])) - - def test_html(self): - with fixtures.TempDir() as tmpdir: - opts = {'output_dir': tmpdir, 'output_format': 'html'} - spy = fixtures.Spy() - sut.set_analyzer_output(opts, spy.call) - self.assertTrue(os.path.exists(spy.arg['output'][1])) - self.assertTrue(os.path.isdir(spy.arg['output'][1])) - - def test_plist_html(self): - with fixtures.TempDir() as tmpdir: - opts = {'output_dir': tmpdir, 'output_format': 'plist-html'} - spy = fixtures.Spy() - sut.set_analyzer_output(opts, spy.call) - self.assertTrue(os.path.exists(spy.arg['output'][1])) - self.assertTrue(os.path.isfile(spy.arg['output'][1])) - - def test_plist(self): - with fixtures.TempDir() as tmpdir: - opts = {'output_dir': tmpdir, 'output_format': 'plist'} - spy = fixtures.Spy() - sut.set_analyzer_output(opts, spy.call) - self.assertTrue(os.path.exists(spy.arg['output'][1])) - self.assertTrue(os.path.isfile(spy.arg['output'][1])) - - -class ReportFailureTest(fixtures.TestCase): +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 fixtures.TempDir() as tmpdir: + with libear.TemporaryDirectory() as tmpdir: # create input file filename = os.path.join(tmpdir, 'test.c') with open(filename, 'w') as handle: @@ -101,15 +169,17 @@ class ReportFailureTest(fixtures.TestCase): uname_msg = ' '.join(os.uname()) + os.linesep error_msg = 'this is my error output' # execute test - opts = {'directory': os.getcwd(), - 'clang': 'clang', - 'file': filename, - 'report': ['-fsyntax-only', '-E', filename], - 'language': 'c', - 'output_dir': tmpdir, - 'error_type': 'other_error', - 'error_output': error_msg, - 'exit_code': 13} + 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() @@ -126,57 +196,110 @@ class ReportFailureTest(fixtures.TestCase): self.assertUnderFailures(pp_file) # info file generated and content dumped info_file = pp_file + '.info.txt' - self.assertIn(info_file, result) + 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.assertIn(error_file, result) + self.assertTrue(error_file in result) self.assertEqual([error_msg], result[error_file]) class AnalyzerTest(unittest.TestCase): - def test_set_language(self): + 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_file_relative_path(self): def test(expected, input): - spy = fixtures.Spy() + spy = Spy() + self.assertEqual(spy.success, + sut.set_file_path_relative(input, spy.call)) + self.assertEqual(expected, spy.arg['file']) + + test('source.c', + {'file': '/home/me/source.c', 'directory': '/home/me'}) + test('me/source.c', + {'file': '/home/me/source.c', 'directory': '/home'}) + test('../home/me/source.c', + {'file': '/home/me/source.c', 'directory': '/tmp'}) + + 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']) - l = 'language' - f = 'file' - i = 'c++' - test('c', {f: 'file.c', l: 'c', i: False}) - test('c++', {f: 'file.c', l: 'c++', i: False}) - test('c++', {f: 'file.c', i: True}) - test('c', {f: 'file.c', i: False}) - test('c++', {f: 'file.cxx', i: False}) - test('c-cpp-output', {f: 'file.i', i: False}) - test('c++-cpp-output', {f: 'file.i', i: True}) - test('c-cpp-output', {f: 'f.i', l: 'c-cpp-output', i: True}) + language('c', {'language': 'c', 'flags': []}) + language('c++', {'language': 'c++', 'flags': []}) - def test_arch_loop(self): - def test(input): - spy = fixtures.Spy() - sut.arch_check(input, spy.call) - return spy.arg + 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) - input = {'key': 'value'} - self.assertEqual(input, test(input)) + 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']) - input = {'archs_seen': ['i386']} - self.assertEqual({'arch': 'i386'}, test(input)) + 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'] - input = {'archs_seen': ['ppc']} - self.assertEqual(None, test(input)) + self.assertEqual([], flags([])) + self.assertEqual(['-arch', 'i386'], flags(['i386'])) + self.assertEqual(['-arch', 'i386'], flags(['i386', 'ppc'])) + self.assertEqual(['-arch', 'sparc'], flags(['i386', 'sparc'])) - input = {'archs_seen': ['i386', 'ppc']} - self.assertEqual({'arch': 'i386'}, test(input)) + 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) - input = {'archs_seen': ['i386', 'sparc']} - result = test(input) - self.assertTrue(result == {'arch': 'i386'} or - result == {'arch': 'sparc'}) + stop(['ppc']) + stop(['ppc64']) @sut.require([]) diff --git a/tools/scan-build/Makefile b/tools/scan-build/Makefile deleted file mode 100644 index 23aa19882a52..000000000000 --- a/tools/scan-build/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -##===- tools/scan-build/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. - -include $(CLANG_LEVEL)/../../Makefile.config -include $(CLANG_LEVEL)/Makefile - -ifeq ($(HOST_OS),MingW) - Suffix := .bat -endif - -CLANG_INSTALL_SCANBUILD ?= 1 - -ifeq ($(CLANG_INSTALL_SCANBUILD), 1) - InstallTargets := $(ToolDir)/scan-build$(Suffix) \ - $(LibexecDir)/c++-analyzer$(Suffix) \ - $(LibexecDir)/ccc-analyzer$(Suffix) \ - $(ShareDir)/scan-build/scanview.css \ - $(ShareDir)/scan-build/sorttable.js \ - $(ShareDir)/man/man1/scan-build.1 - - ifeq ($(HOST_OS),Darwin) - InstallTargets := $(InstallTargets) $(ToolDir)/set-xcode-analyzer - endif -endif - -all:: $(InstallTargets) - -$(ToolDir)/%: bin/% Makefile $(ToolDir)/.dir - $(Echo) "Copying $(notdir $<) to the 'bin' directory..." - $(Verb)cp $< $@ - $(Verb)chmod +x $@ - -$(LibexecDir)/%: libexec/% Makefile $(LibexecDir)/.dir - $(Echo) "Copying $(notdir $<) to the 'libexec' directory..." - $(Verb)cp $< $@ - $(Verb)chmod +x $@ - -$(ShareDir)/man/man1/%: man/% Makefile $(ShareDir)/man/man1/.dir - $(Echo) "Copying $(notdir $<) to the 'man' directory..." - $(Verb)cp $< $@ - -$(ShareDir)/scan-build/%: share/scan-build/% Makefile $(ShareDir)/scan-build/.dir - $(Echo) "Copying $(notdir $<) to the 'share' directory..." - $(Verb)cp $< $@ - diff --git a/tools/scan-build/bin/scan-build b/tools/scan-build/bin/scan-build index 6a14484970a2..3182a29767b9 100755 --- a/tools/scan-build/bin/scan-build +++ b/tools/scan-build/bin/scan-build @@ -69,7 +69,8 @@ my %Options = ( MaxLoop => 0, PluginsToLoad => [], AnalyzerDiscoveryMethod => undef, - OverrideCompiler => 0 # The flag corresponding to the --override-compiler command line option. + OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. + ForceAnalyzeDebugCode => 0 ); lock_keys(%Options); @@ -951,7 +952,8 @@ sub SetEnv { 'CCC_CC', 'CCC_CXX', 'CCC_REPORT_FAILURES', - 'CLANG_ANALYZER_TARGET') { + 'CLANG_ANALYZER_TARGET', + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') { my $x = $EnvVars->{$var}; if (defined $x) { $ENV{$var} = $x } } @@ -1118,6 +1120,11 @@ OPTIONS: Also analyze functions in #included files. By default, such functions are skipped unless they are called by functions within the main source file. + --force-analyze-debug-code + + Tells analyzer to enable assertions in code even if they were disabled + during compilation to enable more precise results. + -o <output location> Specifies the output directory for analyzer reports. Subdirectories will be @@ -1681,6 +1688,12 @@ sub ProcessArgs { next; } + if ($arg eq "--force-analyze-debug-code") { + shift @$Args; + $Options{ForceAnalyzeDebugCode} = 1; + next; + } + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); $NumArgs--; @@ -1796,7 +1809,8 @@ my %EnvVars = ( 'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel}, 'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats}, 'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat}, - 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget} + 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget}, + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode} ); # Run the build. diff --git a/tools/scan-build/libexec/ccc-analyzer b/tools/scan-build/libexec/ccc-analyzer index 831dd42e9c9c..bfda1d326f90 100755 --- a/tools/scan-build/libexec/ccc-analyzer +++ b/tools/scan-build/libexec/ccc-analyzer @@ -492,6 +492,9 @@ if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; } # Get the HTML output directory. my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; +# Get force-analyze-debug-code option. +my $ForceAnalyzeDebugCode = $ENV{'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'}; + my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); my %ArchsSeen; my $HadArch = 0; @@ -682,6 +685,11 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { } } +# Forcedly enable debugging if requested by user. +if ($ForceAnalyzeDebugCode) { + push @CompileOpts, '-UNDEBUG'; +} + # If we are on OSX and have an installation where the # default SDK is inferred by xcrun use xcrun to infer # the SDK. diff --git a/tools/scan-view/Makefile b/tools/scan-view/Makefile deleted file mode 100644 index 37e4404d6f83..000000000000 --- a/tools/scan-view/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -##===- tools/scan-view/Makefile ----------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. - -include $(CLANG_LEVEL)/../../Makefile.config -include $(CLANG_LEVEL)/Makefile - -CLANG_INSTALL_SCANVIEW ?= 1 - -ifeq ($(CLANG_INSTALL_SCANVIEW), 1) - InstallTargets := $(ToolDir)/scan-view \ - $(ShareDir)/scan-view/Reporter.py \ - $(ShareDir)/scan-view/ScanView.py \ - $(ShareDir)/scan-view/startfile.py \ - $(ShareDir)/scan-view/FileRadar.scpt \ - $(ShareDir)/scan-view/GetRadarVersion.scpt \ - $(ShareDir)/scan-view/bugcatcher.ico -endif - -all:: $(InstallTargets) - -$(ToolDir)/%: bin/% Makefile $(ToolDir)/.dir - $(Echo) "Copying $(notdir $<) to the 'bin' directory..." - $(Verb)cp $< $@ - $(Verb)chmod +x $@ - -$(ShareDir)/scan-view/%: share/% Makefile $(ShareDir)/scan-view/.dir - $(Echo) "Copying $(notdir $<) to the 'share' directory..." - $(Verb)cp $< $@ - |
