diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2020-08-03 14:33:11 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2020-08-03 14:33:11 +0000 |
commit | 7e0762870f961466d585c29c6992b4dd4608d2e6 (patch) | |
tree | b72d390e2fad5ebc43a5b207e070bddcb30a9b85 /gnu/llvm/clang/tools | |
parent | baa63be6f620fe92580888c3118df95dccdb98af (diff) |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/clang/tools')
224 files changed, 52557 insertions, 0 deletions
diff --git a/gnu/llvm/clang/tools/CMakeLists.txt b/gnu/llvm/clang/tools/CMakeLists.txt new file mode 100644 index 00000000000..e46c3669a2c --- /dev/null +++ b/gnu/llvm/clang/tools/CMakeLists.txt @@ -0,0 +1,42 @@ +create_subdirectory_options(CLANG TOOL) + +add_clang_subdirectory(diagtool) +add_clang_subdirectory(driver) +add_clang_subdirectory(clang-diff) +add_clang_subdirectory(clang-format) +add_clang_subdirectory(clang-format-vs) +add_clang_subdirectory(clang-fuzzer) +add_clang_subdirectory(clang-import-test) +add_clang_subdirectory(clang-offload-bundler) +add_clang_subdirectory(clang-offload-wrapper) +add_clang_subdirectory(clang-scan-deps) + +add_clang_subdirectory(c-index-test) + +add_clang_subdirectory(clang-rename) +add_clang_subdirectory(clang-refactor) +if(UNIX) + add_clang_subdirectory(clang-shlib) +endif() + +if(CLANG_ENABLE_ARCMT) + add_clang_subdirectory(arcmt-test) + add_clang_subdirectory(c-arcmt-test) +endif() + +if(CLANG_ENABLE_STATIC_ANALYZER) + add_clang_subdirectory(clang-check) + add_clang_subdirectory(clang-extdef-mapping) + add_clang_subdirectory(scan-build) + add_clang_subdirectory(scan-view) +endif() + +# We support checking out the clang-tools-extra repository into the 'extra' +# subdirectory. It contains tools developed as part of the Clang/LLVM project +# on top of the Clang tooling platform. We keep them in a separate repository +# 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/gnu/llvm/clang/tools/arcmt-test/CMakeLists.txt b/gnu/llvm/clang/tools/arcmt-test/CMakeLists.txt new file mode 100644 index 00000000000..d3e6580e16e --- /dev/null +++ b/gnu/llvm/clang/tools/arcmt-test/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + support + ) + +add_clang_executable(arcmt-test + arcmt-test.cpp + ) + +clang_target_link_libraries(arcmt-test + PRIVATE + clangARCMigrate + clangBasic + clangFrontend + clangLex + clangSerialization + ) diff --git a/gnu/llvm/clang/tools/arcmt-test/arcmt-test.cpp b/gnu/llvm/clang/tools/arcmt-test/arcmt-test.cpp new file mode 100644 index 00000000000..c4ba12d4f7c --- /dev/null +++ b/gnu/llvm/clang/tools/arcmt-test/arcmt-test.cpp @@ -0,0 +1,375 @@ +//===-- arcmt-test.cpp - ARC Migration Tool testbed -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/AST/ASTContext.h" +#include "clang/Frontend/PCHContainerOperations.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include <system_error> + +using namespace clang; +using namespace arcmt; + +static llvm::cl::opt<bool> +CheckOnly("check-only", + llvm::cl::desc("Just check for issues that need to be handled manually")); + +//static llvm::cl::opt<bool> +//TestResultForARC("test-result", +//llvm::cl::desc("Test the result of transformations by parsing it in ARC mode")); + +static llvm::cl::opt<bool> +OutputTransformations("output-transformations", + llvm::cl::desc("Print the source transformations")); + +static llvm::cl::opt<bool> +VerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings")); + +static llvm::cl::opt<bool> +VerboseOpt("v", llvm::cl::desc("Enable verbose output")); + +static llvm::cl::opt<bool> +VerifyTransformedFiles("verify-transformed-files", +llvm::cl::desc("Read pairs of file mappings (typically the output of " + "c-arcmt-test) and compare their contents with the filenames " + "provided in command-line")); + +static llvm::cl::opt<std::string> +RemappingsFile("remappings-file", + llvm::cl::desc("Pairs of file mappings (typically the output of " + "c-arcmt-test)")); + +static llvm::cl::list<std::string> +ResultFiles(llvm::cl::Positional, llvm::cl::desc("<filename>...")); + +static llvm::cl::extrahelp extraHelp( + "\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n"); + +// This function isn't referenced outside its translation unit, but it +// can't use the "static" keyword because its address is used for +// GetMainExecutable (since some platforms don't support taking the +// address of main, and some platforms can't implement GetMainExecutable +// without being given the address of a function in the main executable). +std::string GetExecutablePath(const char *Argv0) { + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *MainAddr = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::fs::getMainExecutable(Argv0, MainAddr); +} + +static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, + raw_ostream &OS); +static void printSourceRange(CharSourceRange range, ASTContext &Ctx, + raw_ostream &OS); + +namespace { + +class PrintTransforms : public MigrationProcess::RewriteListener { + ASTContext *Ctx; + raw_ostream &OS; + +public: + PrintTransforms(raw_ostream &OS) + : Ctx(nullptr), OS(OS) {} + + void start(ASTContext &ctx) override { Ctx = &ctx; } + void finish() override { Ctx = nullptr; } + + void insert(SourceLocation loc, StringRef text) override { + assert(Ctx); + OS << "Insert: "; + printSourceLocation(loc, *Ctx, OS); + OS << " \"" << text << "\"\n"; + } + + void remove(CharSourceRange range) override { + assert(Ctx); + OS << "Remove: "; + printSourceRange(range, *Ctx, OS); + OS << '\n'; + } +}; + +} // anonymous namespace + +static bool checkForMigration(StringRef resourcesPath, + ArrayRef<const char *> Args) { + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + DiagnosticConsumer *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient)); + // Chain in -verify checker, if requested. + VerifyDiagnosticConsumer *verifyDiag = nullptr; + if (VerifyDiags) { + verifyDiag = new VerifyDiagnosticConsumer(*Diags); + Diags->setClient(verifyDiag); + } + + CompilerInvocation CI; + if (!CompilerInvocation::CreateFromArgs(CI, Args, *Diags)) + return true; + + if (CI.getFrontendOpts().Inputs.empty()) { + llvm::errs() << "error: no input files\n"; + return true; + } + + if (!CI.getLangOpts()->ObjC) + return false; + + arcmt::checkForManualIssues(CI, CI.getFrontendOpts().Inputs[0], + std::make_shared<PCHContainerOperations>(), + Diags->getClient()); + return Diags->getClient()->getNumErrors() > 0; +} + +static void printResult(FileRemapper &remapper, raw_ostream &OS) { + PreprocessorOptions PPOpts; + remapper.applyMappings(PPOpts); + // The changed files will be in memory buffers, print them. + for (const auto &RB : PPOpts.RemappedFileBuffers) + OS << RB.second->getBuffer(); +} + +static bool performTransformations(StringRef resourcesPath, + ArrayRef<const char *> Args) { + // Check first. + if (checkForMigration(resourcesPath, Args)) + return true; + + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + DiagnosticConsumer *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> TopDiags( + new DiagnosticsEngine(DiagID, &*DiagOpts, &*DiagClient)); + + CompilerInvocation origCI; + if (!CompilerInvocation::CreateFromArgs(origCI, Args, *TopDiags)) + return true; + + if (origCI.getFrontendOpts().Inputs.empty()) { + llvm::errs() << "error: no input files\n"; + return true; + } + + if (!origCI.getLangOpts()->ObjC) + return false; + + MigrationProcess migration(origCI, std::make_shared<PCHContainerOperations>(), + DiagClient); + + std::vector<TransformFn> + transforms = arcmt::getAllTransformations(origCI.getLangOpts()->getGC(), + origCI.getMigratorOpts().NoFinalizeRemoval); + assert(!transforms.empty()); + + std::unique_ptr<PrintTransforms> transformPrinter; + if (OutputTransformations) + transformPrinter.reset(new PrintTransforms(llvm::outs())); + + for (unsigned i=0, e = transforms.size(); i != e; ++i) { + bool err = migration.applyTransform(transforms[i], transformPrinter.get()); + if (err) return true; + + if (VerboseOpt) { + if (i == e-1) + llvm::errs() << "\n##### FINAL RESULT #####\n"; + else + llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n"; + printResult(migration.getRemapper(), llvm::errs()); + llvm::errs() << "\n##########################\n\n"; + } + } + + if (!OutputTransformations) + printResult(migration.getRemapper(), llvm::outs()); + + // FIXME: TestResultForARC + + return false; +} + +static bool filesCompareEqual(StringRef fname1, StringRef fname2) { + using namespace llvm; + + ErrorOr<std::unique_ptr<MemoryBuffer>> file1 = MemoryBuffer::getFile(fname1); + if (!file1) + return false; + + ErrorOr<std::unique_ptr<MemoryBuffer>> file2 = MemoryBuffer::getFile(fname2); + if (!file2) + return false; + + return file1.get()->getBuffer() == file2.get()->getBuffer(); +} + +static bool verifyTransformedFiles(ArrayRef<std::string> resultFiles) { + using namespace llvm; + + assert(!resultFiles.empty()); + + std::map<StringRef, StringRef> resultMap; + + for (ArrayRef<std::string>::iterator + I = resultFiles.begin(), E = resultFiles.end(); I != E; ++I) { + StringRef fname(*I); + if (!fname.endswith(".result")) { + errs() << "error: filename '" << fname + << "' does not have '.result' extension\n"; + return true; + } + resultMap[sys::path::stem(fname)] = fname; + } + + ErrorOr<std::unique_ptr<MemoryBuffer>> inputBuf = std::error_code(); + if (RemappingsFile.empty()) + inputBuf = MemoryBuffer::getSTDIN(); + else + inputBuf = MemoryBuffer::getFile(RemappingsFile); + if (!inputBuf) { + errs() << "error: could not read remappings input\n"; + return true; + } + + SmallVector<StringRef, 8> strs; + inputBuf.get()->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, + /*KeepEmpty=*/false); + + if (strs.empty()) { + errs() << "error: no files to verify from stdin\n"; + return true; + } + if (strs.size() % 2 != 0) { + errs() << "error: files to verify are not original/result pairs\n"; + return true; + } + + for (unsigned i = 0, e = strs.size(); i != e; i += 2) { + StringRef inputOrigFname = strs[i]; + StringRef inputResultFname = strs[i+1]; + + std::map<StringRef, StringRef>::iterator It; + It = resultMap.find(sys::path::filename(inputOrigFname)); + if (It == resultMap.end()) { + errs() << "error: '" << inputOrigFname << "' is not in the list of " + << "transformed files to verify\n"; + return true; + } + + if (!sys::fs::exists(It->second)) { + errs() << "error: '" << It->second << "' does not exist\n"; + return true; + } + if (!sys::fs::exists(inputResultFname)) { + errs() << "error: '" << inputResultFname << "' does not exist\n"; + return true; + } + + if (!filesCompareEqual(It->second, inputResultFname)) { + errs() << "error: '" << It->second << "' is different than " + << "'" << inputResultFname << "'\n"; + return true; + } + + resultMap.erase(It); + } + + if (!resultMap.empty()) { + for (std::map<StringRef, StringRef>::iterator + I = resultMap.begin(), E = resultMap.end(); I != E; ++I) + errs() << "error: '" << I->second << "' was not verified!\n"; + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// Misc. functions. +//===----------------------------------------------------------------------===// + +static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, + raw_ostream &OS) { + SourceManager &SM = Ctx.getSourceManager(); + PresumedLoc PL = SM.getPresumedLoc(loc); + + OS << llvm::sys::path::filename(PL.getFilename()); + OS << ":" << PL.getLine() << ":" + << PL.getColumn(); +} + +static void printSourceRange(CharSourceRange range, ASTContext &Ctx, + raw_ostream &OS) { + SourceManager &SM = Ctx.getSourceManager(); + const LangOptions &langOpts = Ctx.getLangOpts(); + + PresumedLoc PL = SM.getPresumedLoc(range.getBegin()); + + OS << llvm::sys::path::filename(PL.getFilename()); + OS << " [" << PL.getLine() << ":" + << PL.getColumn(); + OS << " - "; + + SourceLocation end = range.getEnd(); + PL = SM.getPresumedLoc(end); + + unsigned endCol = PL.getColumn() - 1; + if (!range.isTokenRange()) + endCol += Lexer::MeasureTokenLength(end, SM, langOpts); + OS << PL.getLine() << ":" << endCol << "]"; +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int main(int argc, const char **argv) { + void *MainAddr = (void*) (intptr_t) GetExecutablePath; + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + + std::string + resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr); + + int optargc = 0; + for (; optargc != argc; ++optargc) { + if (StringRef(argv[optargc]) == "--args") + break; + } + llvm::cl::ParseCommandLineOptions(optargc, argv, "arcmt-test"); + + if (VerifyTransformedFiles) { + if (ResultFiles.empty()) { + llvm::cl::PrintHelpMessage(); + return 1; + } + return verifyTransformedFiles(ResultFiles); + } + + if (optargc == argc) { + llvm::cl::PrintHelpMessage(); + return 1; + } + + ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1); + + if (CheckOnly) + return checkForMigration(resourcesPath, Args); + + return performTransformations(resourcesPath, Args); +} diff --git a/gnu/llvm/clang/tools/c-arcmt-test/CMakeLists.txt b/gnu/llvm/clang/tools/c-arcmt-test/CMakeLists.txt new file mode 100644 index 00000000000..08ac93c176d --- /dev/null +++ b/gnu/llvm/clang/tools/c-arcmt-test/CMakeLists.txt @@ -0,0 +1,19 @@ +add_clang_executable(c-arcmt-test + c-arcmt-test.c + ) + +if (LLVM_BUILD_STATIC) + target_link_libraries(c-arcmt-test + PRIVATE + libclang_static + ) +else() + target_link_libraries(c-arcmt-test + PRIVATE + libclang + ) +endif() + +set_target_properties(c-arcmt-test + PROPERTIES + LINKER_LANGUAGE CXX) diff --git a/gnu/llvm/clang/tools/c-arcmt-test/c-arcmt-test.c b/gnu/llvm/clang/tools/c-arcmt-test/c-arcmt-test.c new file mode 100644 index 00000000000..3bbb2d5d6a8 --- /dev/null +++ b/gnu/llvm/clang/tools/c-arcmt-test/c-arcmt-test.c @@ -0,0 +1,129 @@ +/* c-arcmt-test.c */ + +#include "clang-c/Index.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#if defined(_WIN32) +#include <io.h> +#include <fcntl.h> +#endif + +static int print_remappings(const char *path) { + CXRemapping remap; + unsigned i, N; + CXString origFname; + CXString transFname; + + remap = clang_getRemappings(path); + if (!remap) + return 1; + + N = clang_remap_getNumFiles(remap); + for (i = 0; i != N; ++i) { + clang_remap_getFilenames(remap, i, &origFname, &transFname); + + fprintf(stdout, "%s\n", clang_getCString(origFname)); + fprintf(stdout, "%s\n", clang_getCString(transFname)); + + clang_disposeString(origFname); + clang_disposeString(transFname); + } + + clang_remap_dispose(remap); + return 0; +} + +static int print_remappings_filelist(const char **files, unsigned numFiles) { + CXRemapping remap; + unsigned i, N; + CXString origFname; + CXString transFname; + + remap = clang_getRemappingsFromFileList(files, numFiles); + if (!remap) + return 1; + + N = clang_remap_getNumFiles(remap); + for (i = 0; i != N; ++i) { + clang_remap_getFilenames(remap, i, &origFname, &transFname); + + fprintf(stdout, "%s\n", clang_getCString(origFname)); + fprintf(stdout, "%s\n", clang_getCString(transFname)); + + clang_disposeString(origFname); + clang_disposeString(transFname); + } + + clang_remap_dispose(remap); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ + +static void print_usage(void) { + fprintf(stderr, + "usage: c-arcmt-test -mt-migrate-directory <path>\n" + " c-arcmt-test <remap-file-path1> <remap-file-path2> ...\n\n\n"); +} + +/***/ + +int carcmttest_main(int argc, const char **argv) { + clang_enableStackTraces(); + if (argc == 3 && strncmp(argv[1], "-mt-migrate-directory", 21) == 0) + return print_remappings(argv[2]); + + if (argc > 1) + return print_remappings_filelist(argv+1, argc-1); + + print_usage(); + return 1; +} + +/***/ + +/* We intentionally run in a separate thread to ensure we at least minimal + * testing of a multithreaded environment (for example, having a reduced stack + * size). */ + +typedef struct thread_info { + 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 = carcmttest_main(client_data->argc, client_data->argv); +} + +static void flush_atexit(void) { + /* stdout, and surprisingly even stderr, are not always flushed on process + * and thread exit, particularly when the system is under heavy load. */ + fflush(stdout); + fflush(stderr); +} + +int main(int argc, const char **argv) { + thread_info client_data; + + atexit(flush_atexit); + +#if defined(_WIN32) + if (getenv("LIBCLANG_LOGGING") == NULL) + putenv("LIBCLANG_LOGGING=1"); + _setmode( _fileno(stdout), _O_BINARY ); +#else + setenv("LIBCLANG_LOGGING", "1", /*overwrite=*/0); +#endif + + if (getenv("CINDEXTEST_NOTHREADS")) + return carcmttest_main(argc, argv); + + client_data.argc = argc; + client_data.argv = argv; + clang_executeOnThread(thread_runner, &client_data, 0); + return client_data.result; +} diff --git a/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt b/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt new file mode 100644 index 00000000000..ceef4b08637 --- /dev/null +++ b/gnu/llvm/clang/tools/c-index-test/CMakeLists.txt @@ -0,0 +1,69 @@ +set(LLVM_LINK_COMPONENTS + support +) + +add_clang_executable(c-index-test + c-index-test.c + core_main.cpp + ) + +if(NOT MSVC) + set_property( + SOURCE c-index-test.c + PROPERTY COMPILE_FLAGS "-std=gnu89" + ) +endif() + +if (LLVM_BUILD_STATIC) + target_link_libraries(c-index-test + PRIVATE + libclang_static + clangCodeGen + clangIndex + ) +else() + target_link_libraries(c-index-test + PRIVATE + libclang + clangAST + clangBasic + clangCodeGen + clangFrontend + clangIndex + clangSerialization + ) +endif() + +set_target_properties(c-index-test + PROPERTIES + LINKER_LANGUAGE CXX) + +# If libxml2 is available, make it available for c-index-test. +if (CLANG_HAVE_LIBXML) + if ((CMAKE_OSX_SYSROOT) AND (EXISTS ${CMAKE_OSX_SYSROOT}/${LIBXML2_INCLUDE_DIR})) + include_directories(SYSTEM ${CMAKE_OSX_SYSROOT}/${LIBXML2_INCLUDE_DIR}) + else() + include_directories(SYSTEM ${LIBXML2_INCLUDE_DIR}) + endif() + target_link_libraries(c-index-test PRIVATE ${LIBXML2_LIBRARIES}) +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() + + install(TARGETS c-index-test + RUNTIME DESTINATION "${INSTALL_DESTINATION}" + COMPONENT c-index-test) + + if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-c-index-test + DEPENDS c-index-test + COMPONENT c-index-test) + endif() +endif() diff --git a/gnu/llvm/clang/tools/c-index-test/c-index-test.c b/gnu/llvm/clang/tools/c-index-test/c-index-test.c new file mode 100644 index 00000000000..d4de743f2e3 --- /dev/null +++ b/gnu/llvm/clang/tools/c-index-test/c-index-test.c @@ -0,0 +1,4990 @@ +/* c-index-test.c */ + +#include "clang/Config/config.h" +#include "clang-c/Index.h" +#include "clang-c/CXCompilationDatabase.h" +#include "clang-c/BuildSystem.h" +#include "clang-c/Documentation.h" +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#ifdef CLANG_HAVE_LIBXML +#include <libxml/parser.h> +#include <libxml/relaxng.h> +#include <libxml/xmlerror.h> +#endif + +#ifdef _WIN32 +# include <direct.h> +#else +# include <unistd.h> +#endif + +extern int indextest_core_main(int argc, const char **argv); + +/******************************************************************************/ +/* Utility functions. */ +/******************************************************************************/ + +#ifdef _MSC_VER +char *basename(const char* path) +{ + char* base1 = (char*)strrchr(path, '/'); + char* base2 = (char*)strrchr(path, '\\'); + if (base1 && base2) + return((base1 > base2) ? base1 + 1 : base2 + 1); + else if (base1) + return(base1 + 1); + else if (base2) + return(base2 + 1); + + return((char*)path); +} +char *dirname(char* path) +{ + char* base1 = (char*)strrchr(path, '/'); + char* base2 = (char*)strrchr(path, '\\'); + if (base1 && base2) + if (base1 > base2) + *base1 = 0; + else + *base2 = 0; + else if (base1) + *base1 = 0; + else if (base2) + *base2 = 0; + + return path; +} +#else +extern char *basename(const char *); +extern char *dirname(char *); +#endif + +/** Return the default parsing options. */ +static unsigned getDefaultParsingOptions() { + unsigned options = CXTranslationUnit_DetailedPreprocessingRecord; + + if (getenv("CINDEXTEST_EDITING")) + options |= clang_defaultEditingTranslationUnitOptions(); + if (getenv("CINDEXTEST_COMPLETION_CACHING")) + options |= CXTranslationUnit_CacheCompletionResults; + if (getenv("CINDEXTEST_COMPLETION_NO_CACHING")) + options &= ~CXTranslationUnit_CacheCompletionResults; + if (getenv("CINDEXTEST_SKIP_FUNCTION_BODIES")) + options |= CXTranslationUnit_SkipFunctionBodies; + if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS")) + options |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; + if (getenv("CINDEXTEST_CREATE_PREAMBLE_ON_FIRST_PARSE")) + options |= CXTranslationUnit_CreatePreambleOnFirstParse; + if (getenv("CINDEXTEST_KEEP_GOING")) + options |= CXTranslationUnit_KeepGoing; + if (getenv("CINDEXTEST_LIMIT_SKIP_FUNCTION_BODIES_TO_PREAMBLE")) + options |= CXTranslationUnit_LimitSkipFunctionBodiesToPreamble; + if (getenv("CINDEXTEST_INCLUDE_ATTRIBUTED_TYPES")) + options |= CXTranslationUnit_IncludeAttributedTypes; + if (getenv("CINDEXTEST_VISIT_IMPLICIT_ATTRIBUTES")) + options |= CXTranslationUnit_VisitImplicitAttributes; + if (getenv("CINDEXTEST_IGNORE_NONERRORS_FROM_INCLUDED_FILES")) + options |= CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles; + + return options; +} + +static void ModifyPrintingPolicyAccordingToEnv(CXPrintingPolicy Policy) { + struct Mapping { + const char *name; + enum CXPrintingPolicyProperty property; + }; + struct Mapping mappings[] = { + {"CINDEXTEST_PRINTINGPOLICY_INDENTATION", CXPrintingPolicy_Indentation}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSSPECIFIERS", + CXPrintingPolicy_SuppressSpecifiers}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSTAGKEYWORD", + CXPrintingPolicy_SuppressTagKeyword}, + {"CINDEXTEST_PRINTINGPOLICY_INCLUDETAGDEFINITION", + CXPrintingPolicy_IncludeTagDefinition}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSSCOPE", + CXPrintingPolicy_SuppressScope}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSUNWRITTENSCOPE", + CXPrintingPolicy_SuppressUnwrittenScope}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSINITIALIZERS", + CXPrintingPolicy_SuppressInitializers}, + {"CINDEXTEST_PRINTINGPOLICY_CONSTANTARRAYSIZEASWRITTEN", + CXPrintingPolicy_ConstantArraySizeAsWritten}, + {"CINDEXTEST_PRINTINGPOLICY_ANONYMOUSTAGLOCATIONS", + CXPrintingPolicy_AnonymousTagLocations}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSSTRONGLIFETIME", + CXPrintingPolicy_SuppressStrongLifetime}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSLIFETIMEQUALIFIERS", + CXPrintingPolicy_SuppressLifetimeQualifiers}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSTEMPLATEARGSINCXXCONSTRUCTORS", + CXPrintingPolicy_SuppressTemplateArgsInCXXConstructors}, + {"CINDEXTEST_PRINTINGPOLICY_BOOL", CXPrintingPolicy_Bool}, + {"CINDEXTEST_PRINTINGPOLICY_RESTRICT", CXPrintingPolicy_Restrict}, + {"CINDEXTEST_PRINTINGPOLICY_ALIGNOF", CXPrintingPolicy_Alignof}, + {"CINDEXTEST_PRINTINGPOLICY_UNDERSCOREALIGNOF", + CXPrintingPolicy_UnderscoreAlignof}, + {"CINDEXTEST_PRINTINGPOLICY_USEVOIDFORZEROPARAMS", + CXPrintingPolicy_UseVoidForZeroParams}, + {"CINDEXTEST_PRINTINGPOLICY_TERSEOUTPUT", CXPrintingPolicy_TerseOutput}, + {"CINDEXTEST_PRINTINGPOLICY_POLISHFORDECLARATION", + CXPrintingPolicy_PolishForDeclaration}, + {"CINDEXTEST_PRINTINGPOLICY_HALF", CXPrintingPolicy_Half}, + {"CINDEXTEST_PRINTINGPOLICY_MSWCHAR", CXPrintingPolicy_MSWChar}, + {"CINDEXTEST_PRINTINGPOLICY_INCLUDENEWLINES", + CXPrintingPolicy_IncludeNewlines}, + {"CINDEXTEST_PRINTINGPOLICY_MSVCFORMATTING", + CXPrintingPolicy_MSVCFormatting}, + {"CINDEXTEST_PRINTINGPOLICY_CONSTANTSASWRITTEN", + CXPrintingPolicy_ConstantsAsWritten}, + {"CINDEXTEST_PRINTINGPOLICY_SUPPRESSIMPLICITBASE", + CXPrintingPolicy_SuppressImplicitBase}, + {"CINDEXTEST_PRINTINGPOLICY_FULLYQUALIFIEDNAME", + CXPrintingPolicy_FullyQualifiedName}, + }; + + unsigned i; + for (i = 0; i < sizeof(mappings) / sizeof(struct Mapping); i++) { + char *value = getenv(mappings[i].name); + if (value) { + clang_PrintingPolicy_setProperty(Policy, mappings[i].property, + (unsigned)strtoul(value, 0L, 10)); + } + } +} + +/** Returns 0 in case of success, non-zero in case of a failure. */ +static int checkForErrors(CXTranslationUnit TU); + +static void describeLibclangFailure(enum CXErrorCode Err) { + switch (Err) { + case CXError_Success: + fprintf(stderr, "Success\n"); + return; + + case CXError_Failure: + fprintf(stderr, "Failure (no details available)\n"); + return; + + case CXError_Crashed: + fprintf(stderr, "Failure: libclang crashed\n"); + return; + + case CXError_InvalidArguments: + fprintf(stderr, "Failure: invalid arguments passed to a libclang routine\n"); + return; + + case CXError_ASTReadError: + fprintf(stderr, "Failure: AST deserialization error occurred\n"); + return; + } +} + +static void PrintExtent(FILE *out, unsigned begin_line, unsigned begin_column, + unsigned end_line, unsigned end_column) { + fprintf(out, "[%d:%d - %d:%d]", begin_line, begin_column, + end_line, end_column); +} + +static unsigned CreateTranslationUnit(CXIndex Idx, const char *file, + CXTranslationUnit *TU) { + enum CXErrorCode Err = clang_createTranslationUnit2(Idx, file, TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit from '%s'!\n", file); + describeLibclangFailure(Err); + *TU = 0; + return 0; + } + return 1; +} + +void free_remapped_files(struct CXUnsavedFile *unsaved_files, + int num_unsaved_files) { + int i; + for (i = 0; i != num_unsaved_files; ++i) { + free((char *)unsaved_files[i].Filename); + free((char *)unsaved_files[i].Contents); + } + free(unsaved_files); +} + +static int parse_remapped_files_with_opt(const char *opt_name, + int argc, const char **argv, + int start_arg, + struct CXUnsavedFile **unsaved_files, + int *num_unsaved_files) { + int i; + int arg; + int prefix_len = strlen(opt_name); + int arg_indices[20]; + *unsaved_files = 0; + *num_unsaved_files = 0; + + /* Count the number of remapped files. */ + for (arg = start_arg; arg < argc; ++arg) { + if (strncmp(argv[arg], opt_name, prefix_len)) + continue; + + assert(*num_unsaved_files < (int)(sizeof(arg_indices)/sizeof(int))); + arg_indices[*num_unsaved_files] = arg; + ++*num_unsaved_files; + } + + if (*num_unsaved_files == 0) + return 0; + + *unsaved_files + = (struct CXUnsavedFile *)malloc(sizeof(struct CXUnsavedFile) * + *num_unsaved_files); + assert(*unsaved_files); + for (i = 0; i != *num_unsaved_files; ++i) { + struct CXUnsavedFile *unsaved = *unsaved_files + i; + const char *arg_string = argv[arg_indices[i]] + prefix_len; + int filename_len; + char *filename; + char *contents; + FILE *to_file; + const char *sep = strchr(arg_string, ','); + if (!sep) { + fprintf(stderr, + "error: %sfrom:to argument is missing comma\n", opt_name); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + + /* Open the file that we're remapping to. */ + to_file = fopen(sep + 1, "rb"); + if (!to_file) { + fprintf(stderr, "error: cannot open file %s that we are remapping to\n", + sep + 1); + free_remapped_files(*unsaved_files, i); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + + /* Determine the length of the file we're remapping to. */ + fseek(to_file, 0, SEEK_END); + unsaved->Length = ftell(to_file); + fseek(to_file, 0, SEEK_SET); + + /* Read the contents of the file we're remapping to. */ + contents = (char *)malloc(unsaved->Length + 1); + assert(contents); + if (fread(contents, 1, unsaved->Length, to_file) != unsaved->Length) { + fprintf(stderr, "error: unexpected %s reading 'to' file %s\n", + (feof(to_file) ? "EOF" : "error"), sep + 1); + fclose(to_file); + free_remapped_files(*unsaved_files, i); + free(contents); + *unsaved_files = 0; + *num_unsaved_files = 0; + return -1; + } + contents[unsaved->Length] = 0; + unsaved->Contents = contents; + + /* Close the file. */ + fclose(to_file); + + /* Copy the file name that we're remapping from. */ + filename_len = sep - arg_string; + filename = (char *)malloc(filename_len + 1); + assert(filename); + memcpy(filename, arg_string, filename_len); + filename[filename_len] = 0; + unsaved->Filename = filename; + } + + return 0; +} + +static int parse_remapped_files(int argc, const char **argv, int start_arg, + struct CXUnsavedFile **unsaved_files, + int *num_unsaved_files) { + return parse_remapped_files_with_opt("-remap-file=", argc, argv, start_arg, + unsaved_files, num_unsaved_files); +} + +static int parse_remapped_files_with_try(int try_idx, + int argc, const char **argv, + int start_arg, + struct CXUnsavedFile **unsaved_files, + int *num_unsaved_files) { + struct CXUnsavedFile *unsaved_files_no_try_idx; + int num_unsaved_files_no_try_idx; + struct CXUnsavedFile *unsaved_files_try_idx; + int num_unsaved_files_try_idx; + int ret; + char opt_name[32]; + + ret = parse_remapped_files(argc, argv, start_arg, + &unsaved_files_no_try_idx, &num_unsaved_files_no_try_idx); + if (ret) + return ret; + + sprintf(opt_name, "-remap-file-%d=", try_idx); + ret = parse_remapped_files_with_opt(opt_name, argc, argv, start_arg, + &unsaved_files_try_idx, &num_unsaved_files_try_idx); + if (ret) + return ret; + + if (num_unsaved_files_no_try_idx == 0) { + *unsaved_files = unsaved_files_try_idx; + *num_unsaved_files = num_unsaved_files_try_idx; + return 0; + } + if (num_unsaved_files_try_idx == 0) { + *unsaved_files = unsaved_files_no_try_idx; + *num_unsaved_files = num_unsaved_files_no_try_idx; + return 0; + } + + *num_unsaved_files = num_unsaved_files_no_try_idx + num_unsaved_files_try_idx; + *unsaved_files + = (struct CXUnsavedFile *)realloc(unsaved_files_no_try_idx, + sizeof(struct CXUnsavedFile) * + *num_unsaved_files); + assert(*unsaved_files); + memcpy(*unsaved_files + num_unsaved_files_no_try_idx, + unsaved_files_try_idx, sizeof(struct CXUnsavedFile) * + num_unsaved_files_try_idx); + free(unsaved_files_try_idx); + return 0; +} + +static const char *parse_comments_schema(int argc, const char **argv) { + const char *CommentsSchemaArg = "-comments-xml-schema="; + const char *CommentSchemaFile = NULL; + + if (argc == 0) + return CommentSchemaFile; + + if (!strncmp(argv[0], CommentsSchemaArg, strlen(CommentsSchemaArg))) + CommentSchemaFile = argv[0] + strlen(CommentsSchemaArg); + + return CommentSchemaFile; +} + +/******************************************************************************/ +/* Pretty-printing. */ +/******************************************************************************/ + +static const char *FileCheckPrefix = "CHECK"; + +static void PrintCString(const char *CStr) { + if (CStr != NULL && CStr[0] != '\0') { + for ( ; *CStr; ++CStr) { + const char C = *CStr; + switch (C) { + case '\n': printf("\\n"); break; + case '\r': printf("\\r"); break; + case '\t': printf("\\t"); break; + case '\v': printf("\\v"); break; + case '\f': printf("\\f"); break; + default: putchar(C); break; + } + } + } +} + +static void PrintCStringWithPrefix(const char *Prefix, const char *CStr) { + printf(" %s=[", Prefix); + PrintCString(CStr); + printf("]"); +} + +static void PrintCXStringAndDispose(CXString Str) { + PrintCString(clang_getCString(Str)); + clang_disposeString(Str); +} + +static void PrintCXStringWithPrefix(const char *Prefix, CXString Str) { + PrintCStringWithPrefix(Prefix, clang_getCString(Str)); +} + +static void PrintCXStringWithPrefixAndDispose(const char *Prefix, + CXString Str) { + PrintCStringWithPrefix(Prefix, clang_getCString(Str)); + clang_disposeString(Str); +} + +static void PrintRange(CXSourceRange R, const char *str) { + CXFile begin_file, end_file; + unsigned begin_line, begin_column, end_line, end_column; + + clang_getSpellingLocation(clang_getRangeStart(R), + &begin_file, &begin_line, &begin_column, 0); + clang_getSpellingLocation(clang_getRangeEnd(R), + &end_file, &end_line, &end_column, 0); + if (!begin_file || !end_file) + return; + + if (str) + printf(" %s=", str); + PrintExtent(stdout, begin_line, begin_column, end_line, end_column); +} + +static enum DisplayType { + DisplayType_Spelling, + DisplayType_DisplayName, + DisplayType_Pretty +} wanted_display_type = DisplayType_Spelling; + +static void printVersion(const char *Prefix, CXVersion Version) { + if (Version.Major < 0) + return; + printf("%s%d", Prefix, Version.Major); + + if (Version.Minor < 0) + return; + printf(".%d", Version.Minor); + + if (Version.Subminor < 0) + return; + printf(".%d", Version.Subminor); +} + +struct CommentASTDumpingContext { + int IndentLevel; +}; + +static void DumpCXCommentInternal(struct CommentASTDumpingContext *Ctx, + CXComment Comment) { + unsigned i; + unsigned e; + enum CXCommentKind Kind = clang_Comment_getKind(Comment); + + Ctx->IndentLevel++; + for (i = 0, e = Ctx->IndentLevel; i != e; ++i) + printf(" "); + + printf("("); + switch (Kind) { + case CXComment_Null: + printf("CXComment_Null"); + break; + case CXComment_Text: + printf("CXComment_Text"); + PrintCXStringWithPrefixAndDispose("Text", + clang_TextComment_getText(Comment)); + if (clang_Comment_isWhitespace(Comment)) + printf(" IsWhitespace"); + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_InlineCommand: + printf("CXComment_InlineCommand"); + PrintCXStringWithPrefixAndDispose( + "CommandName", + clang_InlineCommandComment_getCommandName(Comment)); + switch (clang_InlineCommandComment_getRenderKind(Comment)) { + case CXCommentInlineCommandRenderKind_Normal: + printf(" RenderNormal"); + break; + case CXCommentInlineCommandRenderKind_Bold: + printf(" RenderBold"); + break; + case CXCommentInlineCommandRenderKind_Monospaced: + printf(" RenderMonospaced"); + break; + case CXCommentInlineCommandRenderKind_Emphasized: + printf(" RenderEmphasized"); + break; + case CXCommentInlineCommandRenderKind_Anchor: + printf(" RenderAnchor"); + break; + } + for (i = 0, e = clang_InlineCommandComment_getNumArgs(Comment); + i != e; ++i) { + printf(" Arg[%u]=", i); + PrintCXStringAndDispose( + clang_InlineCommandComment_getArgText(Comment, i)); + } + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_HTMLStartTag: { + unsigned NumAttrs; + printf("CXComment_HTMLStartTag"); + PrintCXStringWithPrefixAndDispose( + "Name", + clang_HTMLTagComment_getTagName(Comment)); + NumAttrs = clang_HTMLStartTag_getNumAttrs(Comment); + if (NumAttrs != 0) { + printf(" Attrs:"); + for (i = 0; i != NumAttrs; ++i) { + printf(" "); + PrintCXStringAndDispose(clang_HTMLStartTag_getAttrName(Comment, i)); + printf("="); + PrintCXStringAndDispose(clang_HTMLStartTag_getAttrValue(Comment, i)); + } + } + if (clang_HTMLStartTagComment_isSelfClosing(Comment)) + printf(" SelfClosing"); + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + } + case CXComment_HTMLEndTag: + printf("CXComment_HTMLEndTag"); + PrintCXStringWithPrefixAndDispose( + "Name", + clang_HTMLTagComment_getTagName(Comment)); + if (clang_InlineContentComment_hasTrailingNewline(Comment)) + printf(" HasTrailingNewline"); + break; + case CXComment_Paragraph: + printf("CXComment_Paragraph"); + if (clang_Comment_isWhitespace(Comment)) + printf(" IsWhitespace"); + break; + case CXComment_BlockCommand: + printf("CXComment_BlockCommand"); + PrintCXStringWithPrefixAndDispose( + "CommandName", + clang_BlockCommandComment_getCommandName(Comment)); + for (i = 0, e = clang_BlockCommandComment_getNumArgs(Comment); + i != e; ++i) { + printf(" Arg[%u]=", i); + PrintCXStringAndDispose( + clang_BlockCommandComment_getArgText(Comment, i)); + } + break; + case CXComment_ParamCommand: + printf("CXComment_ParamCommand"); + switch (clang_ParamCommandComment_getDirection(Comment)) { + case CXCommentParamPassDirection_In: + printf(" in"); + break; + case CXCommentParamPassDirection_Out: + printf(" out"); + break; + case CXCommentParamPassDirection_InOut: + printf(" in,out"); + break; + } + if (clang_ParamCommandComment_isDirectionExplicit(Comment)) + printf(" explicitly"); + else + printf(" implicitly"); + PrintCXStringWithPrefixAndDispose( + "ParamName", + clang_ParamCommandComment_getParamName(Comment)); + if (clang_ParamCommandComment_isParamIndexValid(Comment)) + printf(" ParamIndex=%u", clang_ParamCommandComment_getParamIndex(Comment)); + else + printf(" ParamIndex=Invalid"); + break; + case CXComment_TParamCommand: + printf("CXComment_TParamCommand"); + PrintCXStringWithPrefixAndDispose( + "ParamName", + clang_TParamCommandComment_getParamName(Comment)); + if (clang_TParamCommandComment_isParamPositionValid(Comment)) { + printf(" ParamPosition={"); + for (i = 0, e = clang_TParamCommandComment_getDepth(Comment); + i != e; ++i) { + printf("%u", clang_TParamCommandComment_getIndex(Comment, i)); + if (i != e - 1) + printf(", "); + } + printf("}"); + } else + printf(" ParamPosition=Invalid"); + break; + case CXComment_VerbatimBlockCommand: + printf("CXComment_VerbatimBlockCommand"); + PrintCXStringWithPrefixAndDispose( + "CommandName", + clang_BlockCommandComment_getCommandName(Comment)); + break; + case CXComment_VerbatimBlockLine: + printf("CXComment_VerbatimBlockLine"); + PrintCXStringWithPrefixAndDispose( + "Text", + clang_VerbatimBlockLineComment_getText(Comment)); + break; + case CXComment_VerbatimLine: + printf("CXComment_VerbatimLine"); + PrintCXStringWithPrefixAndDispose( + "Text", + clang_VerbatimLineComment_getText(Comment)); + break; + case CXComment_FullComment: + printf("CXComment_FullComment"); + break; + } + if (Kind != CXComment_Null) { + const unsigned NumChildren = clang_Comment_getNumChildren(Comment); + unsigned i; + for (i = 0; i != NumChildren; ++i) { + printf("\n// %s: ", FileCheckPrefix); + DumpCXCommentInternal(Ctx, clang_Comment_getChild(Comment, i)); + } + } + printf(")"); + Ctx->IndentLevel--; +} + +static void DumpCXComment(CXComment Comment) { + struct CommentASTDumpingContext Ctx; + Ctx.IndentLevel = 1; + printf("\n// %s: CommentAST=[\n// %s:", FileCheckPrefix, FileCheckPrefix); + DumpCXCommentInternal(&Ctx, Comment); + printf("]"); +} + +static void ValidateCommentXML(const char *Str, const char *CommentSchemaFile) { +#ifdef CLANG_HAVE_LIBXML + xmlRelaxNGParserCtxtPtr RNGParser; + xmlRelaxNGPtr Schema; + xmlDocPtr Doc; + xmlRelaxNGValidCtxtPtr ValidationCtxt; + int status; + + if (!CommentSchemaFile) + return; + + RNGParser = xmlRelaxNGNewParserCtxt(CommentSchemaFile); + if (!RNGParser) { + printf(" libXMLError"); + return; + } + Schema = xmlRelaxNGParse(RNGParser); + + Doc = xmlParseDoc((const xmlChar *) Str); + + if (!Doc) { + xmlErrorPtr Error = xmlGetLastError(); + printf(" CommentXMLInvalid [not well-formed XML: %s]", Error->message); + return; + } + + ValidationCtxt = xmlRelaxNGNewValidCtxt(Schema); + status = xmlRelaxNGValidateDoc(ValidationCtxt, Doc); + if (!status) + printf(" CommentXMLValid"); + else if (status > 0) { + xmlErrorPtr Error = xmlGetLastError(); + printf(" CommentXMLInvalid [not valid XML: %s]", Error->message); + } else + printf(" libXMLError"); + + xmlRelaxNGFreeValidCtxt(ValidationCtxt); + xmlFreeDoc(Doc); + xmlRelaxNGFree(Schema); + xmlRelaxNGFreeParserCtxt(RNGParser); +#endif +} + +static void PrintCursorComments(CXCursor Cursor, + const char *CommentSchemaFile) { + { + CXString RawComment; + const char *RawCommentCString; + CXString BriefComment; + const char *BriefCommentCString; + + RawComment = clang_Cursor_getRawCommentText(Cursor); + RawCommentCString = clang_getCString(RawComment); + if (RawCommentCString != NULL && RawCommentCString[0] != '\0') { + PrintCStringWithPrefix("RawComment", RawCommentCString); + PrintRange(clang_Cursor_getCommentRange(Cursor), "RawCommentRange"); + + BriefComment = clang_Cursor_getBriefCommentText(Cursor); + BriefCommentCString = clang_getCString(BriefComment); + if (BriefCommentCString != NULL && BriefCommentCString[0] != '\0') + PrintCStringWithPrefix("BriefComment", BriefCommentCString); + clang_disposeString(BriefComment); + } + clang_disposeString(RawComment); + } + + { + CXComment Comment = clang_Cursor_getParsedComment(Cursor); + if (clang_Comment_getKind(Comment) != CXComment_Null) { + PrintCXStringWithPrefixAndDispose("FullCommentAsHTML", + clang_FullComment_getAsHTML(Comment)); + { + CXString XML; + XML = clang_FullComment_getAsXML(Comment); + PrintCXStringWithPrefix("FullCommentAsXML", XML); + ValidateCommentXML(clang_getCString(XML), CommentSchemaFile); + clang_disposeString(XML); + } + + DumpCXComment(Comment); + } + } +} + +typedef struct { + unsigned line; + unsigned col; +} LineCol; + +static int lineCol_cmp(const void *p1, const void *p2) { + const LineCol *lhs = p1; + const LineCol *rhs = p2; + if (lhs->line != rhs->line) + return (int)lhs->line - (int)rhs->line; + return (int)lhs->col - (int)rhs->col; +} + +static CXString CursorToText(CXCursor Cursor) { + CXString text; + switch (wanted_display_type) { + case DisplayType_Spelling: + return clang_getCursorSpelling(Cursor); + case DisplayType_DisplayName: + return clang_getCursorDisplayName(Cursor); + case DisplayType_Pretty: { + CXPrintingPolicy Policy = clang_getCursorPrintingPolicy(Cursor); + ModifyPrintingPolicyAccordingToEnv(Policy); + text = clang_getCursorPrettyPrinted(Cursor, Policy); + clang_PrintingPolicy_dispose(Policy); + return text; + } + } + assert(0 && "unknown display type"); /* no llvm_unreachable in C. */ + /* Set to NULL to prevent uninitialized variable warnings. */ + text.data = NULL; + text.private_flags = 0; + return text; +} + +static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { + CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); + if (clang_isInvalid(Cursor.kind)) { + CXString ks = clang_getCursorKindSpelling(Cursor.kind); + printf("Invalid Cursor => %s", clang_getCString(ks)); + clang_disposeString(ks); + } + else { + CXString string, ks; + CXCursor Referenced; + unsigned line, column; + CXCursor SpecializationOf; + CXCursor *overridden; + unsigned num_overridden; + unsigned RefNameRangeNr; + CXSourceRange CursorExtent; + CXSourceRange RefNameRange; + int AlwaysUnavailable; + int AlwaysDeprecated; + CXString UnavailableMessage; + CXString DeprecatedMessage; + CXPlatformAvailability PlatformAvailability[2]; + int NumPlatformAvailability; + int I; + + ks = clang_getCursorKindSpelling(Cursor.kind); + string = CursorToText(Cursor); + printf("%s=%s", clang_getCString(ks), + clang_getCString(string)); + clang_disposeString(ks); + clang_disposeString(string); + + Referenced = clang_getCursorReferenced(Cursor); + if (!clang_equalCursors(Referenced, clang_getNullCursor())) { + if (clang_getCursorKind(Referenced) == CXCursor_OverloadedDeclRef) { + unsigned I, N = clang_getNumOverloadedDecls(Referenced); + printf("["); + for (I = 0; I != N; ++I) { + CXCursor Ovl = clang_getOverloadedDecl(Referenced, I); + CXSourceLocation Loc; + if (I) + printf(", "); + + Loc = clang_getCursorLocation(Ovl); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf("%d:%d", line, column); + } + printf("]"); + } else { + CXSourceLocation Loc = clang_getCursorLocation(Referenced); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf(":%d:%d", line, column); + } + + if (clang_getCursorKind(Referenced) == CXCursor_TypedefDecl) { + CXType T = clang_getCursorType(Referenced); + if (clang_Type_isTransparentTagTypedef(T)) { + CXType Underlying = clang_getTypedefDeclUnderlyingType(Referenced); + CXString S = clang_getTypeSpelling(Underlying); + printf(" (Transparent: %s)", clang_getCString(S)); + clang_disposeString(S); + } + } + } + + if (clang_isCursorDefinition(Cursor)) + printf(" (Definition)"); + + switch (clang_getCursorAvailability(Cursor)) { + case CXAvailability_Available: + break; + + case CXAvailability_Deprecated: + printf(" (deprecated)"); + break; + + case CXAvailability_NotAvailable: + printf(" (unavailable)"); + break; + + case CXAvailability_NotAccessible: + printf(" (inaccessible)"); + break; + } + + NumPlatformAvailability + = clang_getCursorPlatformAvailability(Cursor, + &AlwaysDeprecated, + &DeprecatedMessage, + &AlwaysUnavailable, + &UnavailableMessage, + PlatformAvailability, 2); + if (AlwaysUnavailable) { + printf(" (always unavailable: \"%s\")", + clang_getCString(UnavailableMessage)); + } else if (AlwaysDeprecated) { + printf(" (always deprecated: \"%s\")", + clang_getCString(DeprecatedMessage)); + } else { + for (I = 0; I != NumPlatformAvailability; ++I) { + if (I >= 2) + break; + + printf(" (%s", clang_getCString(PlatformAvailability[I].Platform)); + if (PlatformAvailability[I].Unavailable) + printf(", unavailable"); + else { + printVersion(", introduced=", PlatformAvailability[I].Introduced); + printVersion(", deprecated=", PlatformAvailability[I].Deprecated); + printVersion(", obsoleted=", PlatformAvailability[I].Obsoleted); + } + if (clang_getCString(PlatformAvailability[I].Message)[0]) + printf(", message=\"%s\"", + clang_getCString(PlatformAvailability[I].Message)); + printf(")"); + } + } + for (I = 0; I != NumPlatformAvailability; ++I) { + if (I >= 2) + break; + clang_disposeCXPlatformAvailability(PlatformAvailability + I); + } + + 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)) + printf(" (virtual)"); + if (clang_CXXMethod_isConst(Cursor)) + printf(" (const)"); + if (clang_CXXMethod_isPureVirtual(Cursor)) + printf(" (pure)"); + if (clang_CXXRecord_isAbstract(Cursor)) + printf(" (abstract)"); + if (clang_EnumDecl_isScoped(Cursor)) + printf(" (scoped)"); + if (clang_Cursor_isVariadic(Cursor)) + printf(" (variadic)"); + if (clang_Cursor_isObjCOptional(Cursor)) + printf(" (@optional)"); + if (clang_isInvalidDeclaration(Cursor)) + printf(" (invalid)"); + + switch (clang_getCursorExceptionSpecificationType(Cursor)) + { + case CXCursor_ExceptionSpecificationKind_None: + break; + + case CXCursor_ExceptionSpecificationKind_DynamicNone: + printf(" (noexcept dynamic none)"); + break; + + case CXCursor_ExceptionSpecificationKind_Dynamic: + printf(" (noexcept dynamic)"); + break; + + case CXCursor_ExceptionSpecificationKind_MSAny: + printf(" (noexcept dynamic any)"); + break; + + case CXCursor_ExceptionSpecificationKind_BasicNoexcept: + printf(" (noexcept)"); + break; + + case CXCursor_ExceptionSpecificationKind_ComputedNoexcept: + printf(" (computed-noexcept)"); + break; + + case CXCursor_ExceptionSpecificationKind_Unevaluated: + case CXCursor_ExceptionSpecificationKind_Uninstantiated: + case CXCursor_ExceptionSpecificationKind_Unparsed: + break; + } + + { + CXString language; + CXString definedIn; + unsigned generated; + if (clang_Cursor_isExternalSymbol(Cursor, &language, &definedIn, + &generated)) { + printf(" (external lang: %s, defined: %s, gen: %d)", + clang_getCString(language), clang_getCString(definedIn), generated); + clang_disposeString(language); + clang_disposeString(definedIn); + } + } + + if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { + CXType T = + clang_getCanonicalType(clang_getIBOutletCollectionType(Cursor)); + CXString S = clang_getTypeKindSpelling(T.kind); + printf(" [IBOutletCollection=%s]", clang_getCString(S)); + clang_disposeString(S); + } + + if (Cursor.kind == CXCursor_CXXBaseSpecifier) { + enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); + unsigned isVirtual = clang_isVirtualBase(Cursor); + const char *accessStr = 0; + + switch (access) { + case CX_CXXInvalidAccessSpecifier: + accessStr = "invalid"; break; + case CX_CXXPublic: + accessStr = "public"; break; + case CX_CXXProtected: + accessStr = "protected"; break; + case CX_CXXPrivate: + accessStr = "private"; break; + } + + printf(" [access=%s isVirtual=%s]", accessStr, + isVirtual ? "true" : "false"); + } + + SpecializationOf = clang_getSpecializedCursorTemplate(Cursor); + if (!clang_equalCursors(SpecializationOf, clang_getNullCursor())) { + CXSourceLocation Loc = clang_getCursorLocation(SpecializationOf); + CXString Name = clang_getCursorSpelling(SpecializationOf); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf(" [Specialization of %s:%d:%d]", + clang_getCString(Name), line, column); + clang_disposeString(Name); + + if (Cursor.kind == CXCursor_FunctionDecl) { + /* Collect the template parameter kinds from the base template. */ + int NumTemplateArgs = clang_Cursor_getNumTemplateArguments(Cursor); + int I; + if (NumTemplateArgs < 0) { + printf(" [no template arg info]"); + } + for (I = 0; I < NumTemplateArgs; I++) { + enum CXTemplateArgumentKind TAK = + clang_Cursor_getTemplateArgumentKind(Cursor, I); + switch(TAK) { + case CXTemplateArgumentKind_Type: + { + CXType T = clang_Cursor_getTemplateArgumentType(Cursor, I); + CXString S = clang_getTypeSpelling(T); + printf(" [Template arg %d: kind: %d, type: %s]", + I, TAK, clang_getCString(S)); + clang_disposeString(S); + } + break; + case CXTemplateArgumentKind_Integral: + printf(" [Template arg %d: kind: %d, intval: %lld]", + I, TAK, clang_Cursor_getTemplateArgumentValue(Cursor, I)); + break; + default: + printf(" [Template arg %d: kind: %d]\n", I, TAK); + } + } + } + } + + clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); + if (num_overridden) { + unsigned I; + LineCol lineCols[50]; + assert(num_overridden <= 50); + printf(" [Overrides "); + for (I = 0; I != num_overridden; ++I) { + CXSourceLocation Loc = clang_getCursorLocation(overridden[I]); + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + lineCols[I].line = line; + lineCols[I].col = column; + } + /* Make the order of the override list deterministic. */ + qsort(lineCols, num_overridden, sizeof(LineCol), lineCol_cmp); + for (I = 0; I != num_overridden; ++I) { + if (I) + printf(", "); + printf("@%d:%d", lineCols[I].line, lineCols[I].col); + } + printf("]"); + clang_disposeOverriddenCursors(overridden); + } + + if (Cursor.kind == CXCursor_InclusionDirective) { + CXFile File = clang_getIncludedFile(Cursor); + CXString Included = clang_getFileName(File); + const char *IncludedString = clang_getCString(Included); + printf(" (%s)", IncludedString ? IncludedString : "(null)"); + clang_disposeString(Included); + + if (clang_isFileMultipleIncludeGuarded(TU, File)) + printf(" [multi-include guarded]"); + } + + CursorExtent = clang_getCursorExtent(Cursor); + RefNameRange = clang_getCursorReferenceNameRange(Cursor, + CXNameRange_WantQualifier + | CXNameRange_WantSinglePiece + | CXNameRange_WantTemplateArgs, + 0); + if (!clang_equalRanges(CursorExtent, RefNameRange)) + PrintRange(RefNameRange, "SingleRefName"); + + for (RefNameRangeNr = 0; 1; RefNameRangeNr++) { + RefNameRange = clang_getCursorReferenceNameRange(Cursor, + CXNameRange_WantQualifier + | CXNameRange_WantTemplateArgs, + RefNameRangeNr); + if (clang_equalRanges(clang_getNullRange(), RefNameRange)) + break; + if (!clang_equalRanges(CursorExtent, RefNameRange)) + PrintRange(RefNameRange, "RefName"); + } + + PrintCursorComments(Cursor, CommentSchemaFile); + + { + unsigned PropAttrs = clang_Cursor_getObjCPropertyAttributes(Cursor, 0); + if (PropAttrs != CXObjCPropertyAttr_noattr) { + printf(" ["); + #define PRINT_PROP_ATTR(A) \ + if (PropAttrs & CXObjCPropertyAttr_##A) printf(#A ",") + PRINT_PROP_ATTR(readonly); + PRINT_PROP_ATTR(getter); + PRINT_PROP_ATTR(assign); + PRINT_PROP_ATTR(readwrite); + PRINT_PROP_ATTR(retain); + PRINT_PROP_ATTR(copy); + PRINT_PROP_ATTR(nonatomic); + PRINT_PROP_ATTR(setter); + PRINT_PROP_ATTR(atomic); + PRINT_PROP_ATTR(weak); + PRINT_PROP_ATTR(strong); + PRINT_PROP_ATTR(unsafe_unretained); + PRINT_PROP_ATTR(class); + printf("]"); + } + } + + if (Cursor.kind == CXCursor_ObjCPropertyDecl) { + CXString Name = clang_Cursor_getObjCPropertyGetterName(Cursor); + CXString Spelling = clang_getCursorSpelling(Cursor); + const char *CName = clang_getCString(Name); + const char *CSpelling = clang_getCString(Spelling); + if (CName && strcmp(CName, CSpelling)) { + printf(" (getter=%s)", CName); + } + clang_disposeString(Spelling); + clang_disposeString(Name); + } + + if (Cursor.kind == CXCursor_ObjCPropertyDecl) { + CXString Name = clang_Cursor_getObjCPropertySetterName(Cursor); + CXString Spelling = clang_getCursorSpelling(Cursor); + const char *CName = clang_getCString(Name); + const char *CSpelling = clang_getCString(Spelling); + char *DefaultSetter = malloc(strlen(CSpelling) + 5); + sprintf(DefaultSetter, "set%s:", CSpelling); + DefaultSetter[3] &= ~(1 << 5); /* Make uppercase */ + if (CName && strcmp(CName, DefaultSetter)) { + printf(" (setter=%s)", CName); + } + free(DefaultSetter); + clang_disposeString(Spelling); + clang_disposeString(Name); + } + + { + unsigned QT = clang_Cursor_getObjCDeclQualifiers(Cursor); + if (QT != CXObjCDeclQualifier_None) { + printf(" ["); + #define PRINT_OBJC_QUAL(A) \ + if (QT & CXObjCDeclQualifier_##A) printf(#A ",") + PRINT_OBJC_QUAL(In); + PRINT_OBJC_QUAL(Inout); + PRINT_OBJC_QUAL(Out); + PRINT_OBJC_QUAL(Bycopy); + PRINT_OBJC_QUAL(Byref); + PRINT_OBJC_QUAL(Oneway); + printf("]"); + } + } + } +} + +static const char* GetCursorSource(CXCursor Cursor) { + CXSourceLocation Loc = clang_getCursorLocation(Cursor); + CXString source; + CXFile file; + clang_getExpansionLocation(Loc, &file, 0, 0, 0); + source = clang_getFileName(file); + if (!clang_getCString(source)) { + clang_disposeString(source); + return "<invalid loc>"; + } + else { + const char *b = basename(clang_getCString(source)); + clang_disposeString(source); + return b; + } +} + +static CXString createCXString(const char *CS) { + CXString Str; + Str.data = (const void *) CS; + Str.private_flags = 0; + return Str; +} + +/******************************************************************************/ +/* Callbacks. */ +/******************************************************************************/ + +typedef void (*PostVisitTU)(CXTranslationUnit); + +void PrintDiagnostic(CXDiagnostic Diagnostic) { + FILE *out = stderr; + CXFile file; + CXString Msg; + unsigned display_opts = CXDiagnostic_DisplaySourceLocation + | CXDiagnostic_DisplayColumn | CXDiagnostic_DisplaySourceRanges + | CXDiagnostic_DisplayOption; + unsigned i, num_fixits; + + if (clang_getDiagnosticSeverity(Diagnostic) == CXDiagnostic_Ignored) + return; + + Msg = clang_formatDiagnostic(Diagnostic, display_opts); + fprintf(stderr, "%s\n", clang_getCString(Msg)); + clang_disposeString(Msg); + + clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic), + &file, 0, 0, 0); + if (!file) + return; + + num_fixits = clang_getDiagnosticNumFixIts(Diagnostic); + fprintf(stderr, "Number FIX-ITs = %d\n", num_fixits); + for (i = 0; i != num_fixits; ++i) { + CXSourceRange range; + CXString insertion_text = clang_getDiagnosticFixIt(Diagnostic, i, &range); + CXSourceLocation start = clang_getRangeStart(range); + CXSourceLocation end = clang_getRangeEnd(range); + unsigned start_line, start_column, end_line, end_column; + CXFile start_file, end_file; + clang_getSpellingLocation(start, &start_file, &start_line, + &start_column, 0); + clang_getSpellingLocation(end, &end_file, &end_line, &end_column, 0); + if (clang_equalLocations(start, end)) { + /* Insertion. */ + if (start_file == file) + fprintf(out, "FIX-IT: Insert \"%s\" at %d:%d\n", + clang_getCString(insertion_text), start_line, start_column); + } else if (strcmp(clang_getCString(insertion_text), "") == 0) { + /* Removal. */ + if (start_file == file && end_file == file) { + fprintf(out, "FIX-IT: Remove "); + PrintExtent(out, start_line, start_column, end_line, end_column); + fprintf(out, "\n"); + } + } else { + /* Replacement. */ + if (start_file == end_file) { + fprintf(out, "FIX-IT: Replace "); + PrintExtent(out, start_line, start_column, end_line, end_column); + fprintf(out, " with \"%s\"\n", clang_getCString(insertion_text)); + } + } + clang_disposeString(insertion_text); + } +} + +void PrintDiagnosticSet(CXDiagnosticSet Set) { + int i = 0, n = clang_getNumDiagnosticsInSet(Set); + for ( ; i != n ; ++i) { + CXDiagnostic Diag = clang_getDiagnosticInSet(Set, i); + CXDiagnosticSet ChildDiags = clang_getChildDiagnostics(Diag); + PrintDiagnostic(Diag); + if (ChildDiags) + PrintDiagnosticSet(ChildDiags); + } +} + +void PrintDiagnostics(CXTranslationUnit TU) { + CXDiagnosticSet TUSet = clang_getDiagnosticSetFromTU(TU); + PrintDiagnosticSet(TUSet); + clang_disposeDiagnosticSet(TUSet); +} + +void PrintMemoryUsage(CXTranslationUnit TU) { + unsigned long total = 0; + unsigned i = 0; + CXTUResourceUsage usage = clang_getCXTUResourceUsage(TU); + fprintf(stderr, "Memory usage:\n"); + for (i = 0 ; i != usage.numEntries; ++i) { + const char *name = clang_getTUResourceUsageName(usage.entries[i].kind); + unsigned long amount = usage.entries[i].amount; + total += amount; + fprintf(stderr, " %s : %ld bytes (%f MBytes)\n", name, amount, + ((double) amount)/(1024*1024)); + } + fprintf(stderr, " TOTAL = %ld bytes (%f MBytes)\n", total, + ((double) total)/(1024*1024)); + clang_disposeCXTUResourceUsage(usage); +} + +/******************************************************************************/ +/* Logic for testing traversal. */ +/******************************************************************************/ + +static void PrintCursorExtent(CXCursor C) { + CXSourceRange extent = clang_getCursorExtent(C); + PrintRange(extent, "Extent"); +} + +/* Data used by the visitors. */ +typedef struct { + CXTranslationUnit TU; + enum CXCursorKind *Filter; + const char *CommentSchemaFile; +} VisitorData; + + +enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, + CXCursor Parent, + CXClientData ClientData) { + VisitorData *Data = (VisitorData *)ClientData; + if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) { + CXSourceLocation Loc = clang_getCursorLocation(Cursor); + unsigned line, column; + clang_getSpellingLocation(Loc, 0, &line, &column, 0); + printf("// %s: %s:%d:%d: ", FileCheckPrefix, + GetCursorSource(Cursor), line, column); + PrintCursor(Cursor, Data->CommentSchemaFile); + PrintCursorExtent(Cursor); + if (clang_isDeclaration(Cursor.kind)) { + enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); + const char *accessStr = 0; + + switch (access) { + case CX_CXXInvalidAccessSpecifier: break; + case CX_CXXPublic: + accessStr = "public"; break; + case CX_CXXProtected: + accessStr = "protected"; break; + case CX_CXXPrivate: + accessStr = "private"; break; + } + + if (accessStr) + printf(" [access=%s]", accessStr); + } + printf("\n"); + return CXChildVisit_Recurse; + } + + return CXChildVisit_Continue; +} + +static enum CXChildVisitResult FunctionScanVisitor(CXCursor Cursor, + CXCursor Parent, + CXClientData ClientData) { + const char *startBuf, *endBuf; + unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn; + CXCursor Ref; + VisitorData *Data = (VisitorData *)ClientData; + + if (Cursor.kind != CXCursor_FunctionDecl || + !clang_isCursorDefinition(Cursor)) + return CXChildVisit_Continue; + + clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf, + &startLine, &startColumn, + &endLine, &endColumn); + /* Probe the entire body, looking for both decls and refs. */ + curLine = startLine; + curColumn = startColumn; + + while (startBuf < endBuf) { + CXSourceLocation Loc; + CXFile file; + CXString source; + + if (*startBuf == '\n') { + startBuf++; + curLine++; + curColumn = 1; + } else if (*startBuf != '\t') + curColumn++; + + Loc = clang_getCursorLocation(Cursor); + clang_getSpellingLocation(Loc, &file, 0, 0, 0); + + source = clang_getFileName(file); + if (clang_getCString(source)) { + CXSourceLocation RefLoc + = clang_getLocation(Data->TU, file, curLine, curColumn); + Ref = clang_getCursor(Data->TU, RefLoc); + if (Ref.kind == CXCursor_NoDeclFound) { + /* Nothing found here; that's fine. */ + } else if (Ref.kind != CXCursor_FunctionDecl) { + printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref), + curLine, curColumn); + PrintCursor(Ref, Data->CommentSchemaFile); + printf("\n"); + } + } + clang_disposeString(source); + startBuf++; + } + + return CXChildVisit_Continue; +} + +/******************************************************************************/ +/* USR testing. */ +/******************************************************************************/ + +enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent, + CXClientData ClientData) { + VisitorData *Data = (VisitorData *)ClientData; + if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) { + CXString USR = clang_getCursorUSR(C); + const char *cstr = clang_getCString(USR); + if (!cstr || cstr[0] == '\0') { + clang_disposeString(USR); + return CXChildVisit_Recurse; + } + printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), cstr); + + PrintCursorExtent(C); + printf("\n"); + clang_disposeString(USR); + + return CXChildVisit_Recurse; + } + + return CXChildVisit_Continue; +} + +/******************************************************************************/ +/* Inclusion stack testing. */ +/******************************************************************************/ + +void InclusionVisitor(CXFile includedFile, CXSourceLocation *includeStack, + unsigned includeStackLen, CXClientData data) { + + unsigned i; + CXString fname; + + fname = clang_getFileName(includedFile); + printf("file: %s\nincluded by:\n", clang_getCString(fname)); + clang_disposeString(fname); + + for (i = 0; i < includeStackLen; ++i) { + CXFile includingFile; + unsigned line, column; + clang_getSpellingLocation(includeStack[i], &includingFile, &line, + &column, 0); + fname = clang_getFileName(includingFile); + printf(" %s:%d:%d\n", clang_getCString(fname), line, column); + clang_disposeString(fname); + } + printf("\n"); +} + +void PrintInclusionStack(CXTranslationUnit TU) { + clang_getInclusions(TU, InclusionVisitor, NULL); +} + +/******************************************************************************/ +/* Linkage testing. */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintLinkage(CXCursor cursor, CXCursor p, + CXClientData d) { + const char *linkage = 0; + + if (clang_isInvalid(clang_getCursorKind(cursor))) + return CXChildVisit_Recurse; + + switch (clang_getCursorLinkage(cursor)) { + case CXLinkage_Invalid: break; + case CXLinkage_NoLinkage: linkage = "NoLinkage"; break; + case CXLinkage_Internal: linkage = "Internal"; break; + case CXLinkage_UniqueExternal: linkage = "UniqueExternal"; break; + case CXLinkage_External: linkage = "External"; break; + } + + if (linkage) { + PrintCursor(cursor, NULL); + printf("linkage=%s\n", linkage); + } + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Visibility testing. */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintVisibility(CXCursor cursor, CXCursor p, + CXClientData d) { + const char *visibility = 0; + + if (clang_isInvalid(clang_getCursorKind(cursor))) + return CXChildVisit_Recurse; + + switch (clang_getCursorVisibility(cursor)) { + case CXVisibility_Invalid: break; + case CXVisibility_Hidden: visibility = "Hidden"; break; + case CXVisibility_Protected: visibility = "Protected"; break; + case CXVisibility_Default: visibility = "Default"; break; + } + + if (visibility) { + PrintCursor(cursor, NULL); + printf("visibility=%s\n", visibility); + } + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Typekind testing. */ +/******************************************************************************/ + +static void PrintTypeAndTypeKind(CXType T, const char *Format) { + CXString TypeSpelling, TypeKindSpelling; + + TypeSpelling = clang_getTypeSpelling(T); + TypeKindSpelling = clang_getTypeKindSpelling(T.kind); + printf(Format, + clang_getCString(TypeSpelling), + clang_getCString(TypeKindSpelling)); + clang_disposeString(TypeSpelling); + clang_disposeString(TypeKindSpelling); +} + +static enum CXVisitorResult FieldVisitor(CXCursor C, + CXClientData client_data) { + (*(int *) client_data)+=1; + return CXVisit_Continue; +} + +static void PrintTypeTemplateArgs(CXType T, const char *Format) { + int NumTArgs = clang_Type_getNumTemplateArguments(T); + if (NumTArgs != -1 && NumTArgs != 0) { + int i; + CXType TArg; + printf(Format, NumTArgs); + for (i = 0; i < NumTArgs; ++i) { + TArg = clang_Type_getTemplateArgumentAsType(T, i); + if (TArg.kind != CXType_Invalid) { + PrintTypeAndTypeKind(TArg, " [type=%s] [typekind=%s]"); + } + } + /* Ensure that the returned type is invalid when indexing off-by-one. */ + TArg = clang_Type_getTemplateArgumentAsType(T, i); + assert(TArg.kind == CXType_Invalid); + printf("]"); + } +} + +static void PrintNullabilityKind(CXType T, const char *Format) { + enum CXTypeNullabilityKind N = clang_Type_getNullability(T); + + const char *nullability = 0; + switch (N) { + case CXTypeNullability_NonNull: nullability = "nonnull"; break; + case CXTypeNullability_Nullable: nullability = "nullable"; break; + case CXTypeNullability_Unspecified: nullability = "unspecified"; break; + case CXTypeNullability_Invalid: break; + } + + if (nullability) { + printf(Format, nullability); + } +} + +static enum CXChildVisitResult PrintType(CXCursor cursor, CXCursor p, + CXClientData d) { + if (!clang_isInvalid(clang_getCursorKind(cursor))) { + CXType T = clang_getCursorType(cursor); + CXType PT = clang_getPointeeType(T); + enum CXRefQualifierKind RQ = clang_Type_getCXXRefQualifier(T); + PrintCursor(cursor, NULL); + PrintTypeAndTypeKind(T, " [type=%s] [typekind=%s]"); + PrintNullabilityKind(T, " [nullability=%s]"); + if (clang_isConstQualifiedType(T)) + printf(" const"); + if (clang_isVolatileQualifiedType(T)) + printf(" volatile"); + if (clang_isRestrictQualifiedType(T)) + printf(" restrict"); + if (RQ == CXRefQualifier_LValue) + printf(" lvalue-ref-qualifier"); + if (RQ == CXRefQualifier_RValue) + printf(" rvalue-ref-qualifier"); + /* Print the template argument types if they exist. */ + PrintTypeTemplateArgs(T, " [templateargs/%d="); + /* Print the canonical type if it is different. */ + { + CXType CT = clang_getCanonicalType(T); + if (!clang_equalTypes(T, CT)) { + PrintTypeAndTypeKind(CT, " [canonicaltype=%s] [canonicaltypekind=%s]"); + PrintTypeTemplateArgs(CT, " [canonicaltemplateargs/%d="); + } + } + /* Print the modified type if it exists. */ + { + CXType MT = clang_Type_getModifiedType(T); + if (MT.kind != CXType_Invalid) { + PrintTypeAndTypeKind(MT, " [modifiedtype=%s] [modifiedtypekind=%s]"); + } + } + /* Print the return type if it exists. */ + { + CXType RT = clang_getCursorResultType(cursor); + if (RT.kind != CXType_Invalid) { + PrintTypeAndTypeKind(RT, " [resulttype=%s] [resulttypekind=%s]"); + } + PrintNullabilityKind(RT, " [resultnullability=%s]"); + } + /* Print the argument types if they exist. */ + { + int NumArgs = clang_Cursor_getNumArguments(cursor); + if (NumArgs != -1 && NumArgs != 0) { + int i; + printf(" [args="); + for (i = 0; i < NumArgs; ++i) { + CXType T = clang_getCursorType(clang_Cursor_getArgument(cursor, i)); + if (T.kind != CXType_Invalid) { + PrintTypeAndTypeKind(T, " [%s] [%s]"); + PrintNullabilityKind(T, " [%s]"); + } + } + printf("]"); + } + } + /* Print ObjC base types, type arguments, and protocol list if available. */ + { + CXType BT = clang_Type_getObjCObjectBaseType(PT); + if (BT.kind != CXType_Invalid) { + PrintTypeAndTypeKind(BT, " [basetype=%s] [basekind=%s]"); + } + } + { + unsigned NumTypeArgs = clang_Type_getNumObjCTypeArgs(PT); + if (NumTypeArgs > 0) { + unsigned i; + printf(" [typeargs="); + for (i = 0; i < NumTypeArgs; ++i) { + CXType TA = clang_Type_getObjCTypeArg(PT, i); + if (TA.kind != CXType_Invalid) { + PrintTypeAndTypeKind(TA, " [%s] [%s]"); + } + } + printf("]"); + } + } + { + unsigned NumProtocols = clang_Type_getNumObjCProtocolRefs(PT); + if (NumProtocols > 0) { + unsigned i; + printf(" [protocols="); + for (i = 0; i < NumProtocols; ++i) { + CXCursor P = clang_Type_getObjCProtocolDecl(PT, i); + if (!clang_isInvalid(clang_getCursorKind(P))) { + PrintCursor(P, NULL); + } + } + printf("]"); + } + } + /* Print if this is a non-POD type. */ + printf(" [isPOD=%d]", clang_isPODType(T)); + /* Print the pointee type. */ + { + if (PT.kind != CXType_Invalid) { + PrintTypeAndTypeKind(PT, " [pointeetype=%s] [pointeekind=%s]"); + } + } + /* Print the number of fields if they exist. */ + { + int numFields = 0; + if (clang_Type_visitFields(T, FieldVisitor, &numFields)){ + if (numFields != 0) { + printf(" [nbFields=%d]", numFields); + } + } + } + + /* Print if it is an anonymous record or namespace. */ + { + unsigned isAnon = clang_Cursor_isAnonymous(cursor); + if (isAnon != 0) { + printf(" [isAnon=%d]", isAnon); + } + } + + /* Print if it is an anonymous record decl */ + { + unsigned isAnonRecDecl = clang_Cursor_isAnonymousRecordDecl(cursor); + printf(" [isAnonRecDecl=%d]", isAnonRecDecl); + } + + /* Print if it is an inline namespace decl */ + { + unsigned isInlineNamespace = clang_Cursor_isInlineNamespace(cursor); + if (isInlineNamespace != 0) + printf(" [isInlineNamespace=%d]", isInlineNamespace); + } + + printf("\n"); + } + return CXChildVisit_Recurse; +} + +static void PrintSingleTypeSize(CXType T, const char *TypeKindFormat, + const char *SizeFormat, + const char *AlignFormat) { + PrintTypeAndTypeKind(T, TypeKindFormat); + /* Print the type sizeof if applicable. */ + { + long long Size = clang_Type_getSizeOf(T); + if (Size >= 0 || Size < -1 ) { + printf(SizeFormat, Size); + } + } + /* Print the type alignof if applicable. */ + { + long long Align = clang_Type_getAlignOf(T); + if (Align >= 0 || Align < -1) { + printf(AlignFormat, Align); + } + } + + /* Print the return type if it exists. */ + { + CXType RT = clang_getResultType(T); + if (RT.kind != CXType_Invalid) + PrintSingleTypeSize(RT, " [resulttype=%s] [resulttypekind=%s]", + " [resultsizeof=%lld]", " [resultalignof=%lld]"); + } +} + +static enum CXChildVisitResult PrintTypeSize(CXCursor cursor, CXCursor p, + CXClientData d) { + CXType T; + enum CXCursorKind K = clang_getCursorKind(cursor); + if (clang_isInvalid(K)) + return CXChildVisit_Recurse; + T = clang_getCursorType(cursor); + PrintCursor(cursor, NULL); + PrintSingleTypeSize(T, " [type=%s] [typekind=%s]", " [sizeof=%lld]", + " [alignof=%lld]"); + /* Print the record field offset if applicable. */ + { + CXString FieldSpelling = clang_getCursorSpelling(cursor); + const char *FieldName = clang_getCString(FieldSpelling); + /* recurse to get the first parent record that is not anonymous. */ + unsigned RecordIsAnonymous = 0; + if (clang_getCursorKind(cursor) == CXCursor_FieldDecl) { + CXCursor Record; + CXCursor Parent = p; + do { + Record = Parent; + Parent = clang_getCursorSemanticParent(Record); + RecordIsAnonymous = clang_Cursor_isAnonymous(Record); + /* Recurse as long as the parent is a CXType_Record and the Record + is anonymous */ + } while ( clang_getCursorType(Parent).kind == CXType_Record && + RecordIsAnonymous > 0); + { + long long Offset = clang_Type_getOffsetOf(clang_getCursorType(Record), + FieldName); + long long Offset2 = clang_Cursor_getOffsetOfField(cursor); + if (Offset == Offset2){ + printf(" [offsetof=%lld]", Offset); + } else { + /* Offsets will be different in anonymous records. */ + printf(" [offsetof=%lld/%lld]", Offset, Offset2); + } + } + } + clang_disposeString(FieldSpelling); + } + /* Print if its a bitfield */ + { + int IsBitfield = clang_Cursor_isBitField(cursor); + if (IsBitfield) + printf(" [BitFieldSize=%d]", clang_getFieldDeclBitWidth(cursor)); + } + + printf("\n"); + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Mangling testing. */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintMangledName(CXCursor cursor, CXCursor p, + CXClientData d) { + CXString MangledName; + if (clang_isUnexposed(clang_getCursorKind(cursor))) + return CXChildVisit_Recurse; + PrintCursor(cursor, NULL); + MangledName = clang_Cursor_getMangling(cursor); + printf(" [mangled=%s]\n", clang_getCString(MangledName)); + clang_disposeString(MangledName); + return CXChildVisit_Continue; +} + +static enum CXChildVisitResult PrintManglings(CXCursor cursor, CXCursor p, + CXClientData d) { + unsigned I, E; + CXStringSet *Manglings = NULL; + if (clang_isUnexposed(clang_getCursorKind(cursor))) + return CXChildVisit_Recurse; + if (!clang_isDeclaration(clang_getCursorKind(cursor))) + return CXChildVisit_Recurse; + if (clang_getCursorKind(cursor) == CXCursor_ParmDecl) + return CXChildVisit_Continue; + PrintCursor(cursor, NULL); + Manglings = clang_Cursor_getCXXManglings(cursor); + if (Manglings) { + for (I = 0, E = Manglings->Count; I < E; ++I) + printf(" [mangled=%s]", clang_getCString(Manglings->Strings[I])); + clang_disposeStringSet(Manglings); + printf("\n"); + } + Manglings = clang_Cursor_getObjCManglings(cursor); + if (Manglings) { + for (I = 0, E = Manglings->Count; I < E; ++I) + printf(" [mangled=%s]", clang_getCString(Manglings->Strings[I])); + clang_disposeStringSet(Manglings); + printf("\n"); + } + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Bitwidth testing. */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintBitWidth(CXCursor cursor, CXCursor p, + CXClientData d) { + int Bitwidth; + if (clang_getCursorKind(cursor) != CXCursor_FieldDecl) + return CXChildVisit_Recurse; + + Bitwidth = clang_getFieldDeclBitWidth(cursor); + if (Bitwidth >= 0) { + PrintCursor(cursor, NULL); + printf(" bitwidth=%d\n", Bitwidth); + } + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Type declaration testing */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintTypeDeclaration(CXCursor cursor, CXCursor p, + CXClientData d) { + CXCursor typeDeclaration = clang_getTypeDeclaration(clang_getCursorType(cursor)); + + if (clang_isDeclaration(typeDeclaration.kind)) { + PrintCursor(cursor, NULL); + PrintTypeAndTypeKind(clang_getCursorType(typeDeclaration), " [typedeclaration=%s] [typekind=%s]\n"); + } + + return CXChildVisit_Recurse; +} + +/******************************************************************************/ +/* Declaration attributes testing */ +/******************************************************************************/ + +static enum CXChildVisitResult PrintDeclAttributes(CXCursor cursor, CXCursor p, + CXClientData d) { + if (clang_isDeclaration(cursor.kind)) { + printf("\n"); + PrintCursor(cursor, NULL); + return CXChildVisit_Recurse; + } else if (clang_isAttribute(cursor.kind)) { + printf(" "); + PrintCursor(cursor, NULL); + } + return CXChildVisit_Continue; +} + +/******************************************************************************/ +/* Target information testing. */ +/******************************************************************************/ + +static int print_target_info(int argc, const char **argv) { + CXIndex Idx; + CXTranslationUnit TU; + CXTargetInfo TargetInfo; + CXString Triple; + const char *FileName; + enum CXErrorCode Err; + int PointerWidth; + + if (argc == 0) { + fprintf(stderr, "No filename specified\n"); + return 1; + } + + FileName = argv[1]; + + Idx = clang_createIndex(0, 1); + Err = clang_parseTranslationUnit2(Idx, FileName, argv, argc, NULL, 0, + getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Couldn't parse translation unit!\n"); + describeLibclangFailure(Err); + clang_disposeIndex(Idx); + return 1; + } + + TargetInfo = clang_getTranslationUnitTargetInfo(TU); + + Triple = clang_TargetInfo_getTriple(TargetInfo); + printf("TargetTriple: %s\n", clang_getCString(Triple)); + clang_disposeString(Triple); + + PointerWidth = clang_TargetInfo_getPointerWidth(TargetInfo); + printf("PointerWidth: %d\n", PointerWidth); + + clang_TargetInfo_dispose(TargetInfo); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(Idx); + return 0; +} + +/******************************************************************************/ +/* Loading ASTs/source. */ +/******************************************************************************/ + +static int perform_test_load(CXIndex Idx, CXTranslationUnit TU, + const char *filter, const char *prefix, + CXCursorVisitor Visitor, + PostVisitTU PV, + const char *CommentSchemaFile) { + + if (prefix) + FileCheckPrefix = prefix; + + if (Visitor) { + enum CXCursorKind K = CXCursor_NotImplemented; + enum CXCursorKind *ck = &K; + VisitorData Data; + + /* Perform some simple filtering. */ + if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL; + else if (!strcmp(filter, "all-display") || + !strcmp(filter, "local-display")) { + ck = NULL; + wanted_display_type = DisplayType_DisplayName; + } + else if (!strcmp(filter, "all-pretty") || + !strcmp(filter, "local-pretty")) { + ck = NULL; + wanted_display_type = DisplayType_Pretty; + } + else if (!strcmp(filter, "none")) K = (enum CXCursorKind) ~0; + else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl; + else if (!strcmp(filter, "interface")) K = CXCursor_ObjCInterfaceDecl; + else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl; + else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl; + else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl; + else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor; + else { + fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter); + return 1; + } + + Data.TU = TU; + Data.Filter = ck; + Data.CommentSchemaFile = CommentSchemaFile; + clang_visitChildren(clang_getTranslationUnitCursor(TU), Visitor, &Data); + } + + if (PV) + PV(TU); + + PrintDiagnostics(TU); + if (checkForErrors(TU) != 0) { + clang_disposeTranslationUnit(TU); + return -1; + } + + clang_disposeTranslationUnit(TU); + return 0; +} + +int perform_test_load_tu(const char *file, const char *filter, + const char *prefix, CXCursorVisitor Visitor, + PostVisitTU PV) { + CXIndex Idx; + CXTranslationUnit TU; + int result; + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + !strcmp(filter, "local") ? 1 : 0, + /* displayDiagnostics=*/1); + + if (!CreateTranslationUnit(Idx, file, &TU)) { + clang_disposeIndex(Idx); + return 1; + } + + result = perform_test_load(Idx, TU, filter, prefix, Visitor, PV, NULL); + clang_disposeIndex(Idx); + return result; +} + +int perform_test_load_source(int argc, const char **argv, + const char *filter, CXCursorVisitor Visitor, + PostVisitTU PV) { + CXIndex Idx; + CXTranslationUnit TU; + const char *CommentSchemaFile; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + enum CXErrorCode Err; + int result; + unsigned Repeats = 0; + unsigned I; + const char *InvocationPath; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + (!strcmp(filter, "local") || + !strcmp(filter, "local-display") || + !strcmp(filter, "local-pretty")) + ? 1 + : 0, + /* displayDiagnostics=*/1); + InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); + if (InvocationPath) + clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath); + + if ((CommentSchemaFile = parse_comments_schema(argc, argv))) { + argc--; + argv++; + } + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + clang_disposeIndex(Idx); + return -1; + } + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + Err = clang_parseTranslationUnit2(Idx, 0, + argv + num_unsaved_files, + argc - num_unsaved_files, + unsaved_files, num_unsaved_files, + getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return 1; + } + + for (I = 0; I != Repeats; ++I) { + if (checkForErrors(TU) != 0) + return -1; + + if (Repeats > 1) { + clang_suspendTranslationUnit(TU); + + Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU)); + if (Err != CXError_Success) { + describeLibclangFailure(Err); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return 1; + } + } + } + + result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV, + CommentSchemaFile); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return result; +} + +int perform_test_reparse_source(int argc, const char **argv, int trials, + const char *filter, CXCursorVisitor Visitor, + PostVisitTU PV) { + CXIndex Idx; + CXTranslationUnit TU; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + int compiler_arg_idx = 0; + enum CXErrorCode Err; + int result, i; + int trial; + int remap_after_trial = 0; + char *endptr = 0; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + !strcmp(filter, "local") ? 1 : 0, + /* displayDiagnostics=*/1); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + clang_disposeIndex(Idx); + return -1; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--") == 0) + break; + } + if (i < argc) + compiler_arg_idx = i+1; + if (num_unsaved_files > compiler_arg_idx) + compiler_arg_idx = num_unsaved_files; + + /* Load the initial translation unit -- we do this without honoring remapped + * files, so that we have a way to test results after changing the source. */ + Err = clang_parseTranslationUnit2(Idx, 0, + argv + compiler_arg_idx, + argc - compiler_arg_idx, + 0, 0, getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return 1; + } + + if (checkForErrors(TU) != 0) + return -1; + + if (getenv("CINDEXTEST_REMAP_AFTER_TRIAL")) { + remap_after_trial = + strtol(getenv("CINDEXTEST_REMAP_AFTER_TRIAL"), &endptr, 10); + } + + for (trial = 0; trial < trials; ++trial) { + free_remapped_files(unsaved_files, num_unsaved_files); + if (parse_remapped_files_with_try(trial, argc, argv, 0, + &unsaved_files, &num_unsaved_files)) { + clang_disposeTranslationUnit(TU); + clang_disposeIndex(Idx); + return -1; + } + + Err = clang_reparseTranslationUnit( + TU, + trial >= remap_after_trial ? num_unsaved_files : 0, + trial >= remap_after_trial ? unsaved_files : 0, + clang_defaultReparseOptions(TU)); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to reparse translation unit!\n"); + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + } + + result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV, NULL); + + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return result; +} + +static int perform_single_file_parse(const char *filename) { + CXIndex Idx; + CXTranslationUnit TU; + enum CXErrorCode Err; + int result; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */1, + /* displayDiagnostics=*/1); + + Err = clang_parseTranslationUnit2(Idx, filename, + /*command_line_args=*/NULL, + /*num_command_line_args=*/0, + /*unsaved_files=*/NULL, + /*num_unsaved_files=*/0, + CXTranslationUnit_SingleFileParse, &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + clang_disposeIndex(Idx); + return 1; + } + + result = perform_test_load(Idx, TU, /*filter=*/"all", /*prefix=*/NULL, FilteredPrintingVisitor, /*PostVisit=*/NULL, + /*CommentSchemaFile=*/NULL); + clang_disposeIndex(Idx); + return result; +} + +static int perform_file_retain_excluded_cb(const char *filename) { + CXIndex Idx; + CXTranslationUnit TU; + enum CXErrorCode Err; + int result; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */1, + /* displayDiagnostics=*/1); + + Err = clang_parseTranslationUnit2(Idx, filename, + /*command_line_args=*/NULL, + /*num_command_line_args=*/0, + /*unsaved_files=*/NULL, + /*num_unsaved_files=*/0, + CXTranslationUnit_RetainExcludedConditionalBlocks, &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + clang_disposeIndex(Idx); + return 1; + } + + result = perform_test_load(Idx, TU, /*filter=*/"all", /*prefix=*/NULL, FilteredPrintingVisitor, /*PostVisit=*/NULL, + /*CommentSchemaFile=*/NULL); + clang_disposeIndex(Idx); + return result; +} + +/******************************************************************************/ +/* Logic for testing clang_getCursor(). */ +/******************************************************************************/ + +static void print_cursor_file_scan(CXTranslationUnit TU, CXCursor cursor, + unsigned start_line, unsigned start_col, + unsigned end_line, unsigned end_col, + const char *prefix) { + printf("// %s: ", FileCheckPrefix); + if (prefix) + printf("-%s", prefix); + PrintExtent(stdout, start_line, start_col, end_line, end_col); + printf(" "); + PrintCursor(cursor, NULL); + printf("\n"); +} + +static int perform_file_scan(const char *ast_file, const char *source_file, + const char *prefix) { + CXIndex Idx; + CXTranslationUnit TU; + FILE *fp; + CXCursor prevCursor = clang_getNullCursor(); + CXFile file; + unsigned line = 1, col = 1; + unsigned start_line = 1, start_col = 1; + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + + if (!CreateTranslationUnit(Idx, ast_file, &TU)) + return 1; + + if ((fp = fopen(source_file, "r")) == NULL) { + fprintf(stderr, "Could not open '%s'\n", source_file); + clang_disposeTranslationUnit(TU); + return 1; + } + + file = clang_getFile(TU, source_file); + for (;;) { + CXCursor cursor; + int c = fgetc(fp); + + if (c == '\n') { + ++line; + col = 1; + } else + ++col; + + /* Check the cursor at this position, and dump the previous one if we have + * found something new. + */ + cursor = clang_getCursor(TU, clang_getLocation(TU, file, line, col)); + if ((c == EOF || !clang_equalCursors(cursor, prevCursor)) && + prevCursor.kind != CXCursor_InvalidFile) { + print_cursor_file_scan(TU, prevCursor, start_line, start_col, + line, col, prefix); + start_line = line; + start_col = col; + } + if (c == EOF) + break; + + prevCursor = cursor; + } + + fclose(fp); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(Idx); + return 0; +} + +/******************************************************************************/ +/* Logic for testing clang code completion. */ +/******************************************************************************/ + +/* Parse file:line:column from the input string. Returns 0 on success, non-zero + on failure. If successful, the pointer *filename will contain newly-allocated + memory (that will be owned by the caller) to store the file name. */ +int parse_file_line_column(const char *input, char **filename, unsigned *line, + unsigned *column, unsigned *second_line, + unsigned *second_column) { + /* Find the second colon. */ + const char *last_colon = strrchr(input, ':'); + unsigned values[4], i; + unsigned num_values = (second_line && second_column)? 4 : 2; + + char *endptr = 0; + if (!last_colon || last_colon == input) { + if (num_values == 4) + fprintf(stderr, "could not parse filename:line:column:line:column in " + "'%s'\n", input); + else + fprintf(stderr, "could not parse filename:line:column in '%s'\n", input); + return 1; + } + + for (i = 0; i != num_values; ++i) { + const char *prev_colon; + + /* Parse the next line or column. */ + values[num_values - i - 1] = strtol(last_colon + 1, &endptr, 10); + if (*endptr != 0 && *endptr != ':') { + fprintf(stderr, "could not parse %s in '%s'\n", + (i % 2 ? "column" : "line"), input); + return 1; + } + + if (i + 1 == num_values) + break; + + /* Find the previous colon. */ + prev_colon = last_colon - 1; + while (prev_colon != input && *prev_colon != ':') + --prev_colon; + if (prev_colon == input) { + fprintf(stderr, "could not parse %s in '%s'\n", + (i % 2 == 0? "column" : "line"), input); + return 1; + } + + last_colon = prev_colon; + } + + *line = values[0]; + *column = values[1]; + + if (second_line && second_column) { + *second_line = values[2]; + *second_column = values[3]; + } + + /* Copy the file name. */ + *filename = (char*)malloc(last_colon - input + 1); + assert(*filename); + memcpy(*filename, input, last_colon - input); + (*filename)[last_colon - input] = 0; + return 0; +} + +const char * +clang_getCompletionChunkKindSpelling(enum CXCompletionChunkKind Kind) { + switch (Kind) { + case CXCompletionChunk_Optional: return "Optional"; + case CXCompletionChunk_TypedText: return "TypedText"; + case CXCompletionChunk_Text: return "Text"; + case CXCompletionChunk_Placeholder: return "Placeholder"; + case CXCompletionChunk_Informative: return "Informative"; + case CXCompletionChunk_CurrentParameter: return "CurrentParameter"; + case CXCompletionChunk_LeftParen: return "LeftParen"; + case CXCompletionChunk_RightParen: return "RightParen"; + case CXCompletionChunk_LeftBracket: return "LeftBracket"; + case CXCompletionChunk_RightBracket: return "RightBracket"; + case CXCompletionChunk_LeftBrace: return "LeftBrace"; + case CXCompletionChunk_RightBrace: return "RightBrace"; + case CXCompletionChunk_LeftAngle: return "LeftAngle"; + case CXCompletionChunk_RightAngle: return "RightAngle"; + case CXCompletionChunk_Comma: return "Comma"; + case CXCompletionChunk_ResultType: return "ResultType"; + case CXCompletionChunk_Colon: return "Colon"; + case CXCompletionChunk_SemiColon: return "SemiColon"; + case CXCompletionChunk_Equal: return "Equal"; + case CXCompletionChunk_HorizontalSpace: return "HorizontalSpace"; + case CXCompletionChunk_VerticalSpace: return "VerticalSpace"; + } + + return "Unknown"; +} + +static int checkForErrors(CXTranslationUnit TU) { + unsigned Num, i; + CXDiagnostic Diag; + CXString DiagStr; + + if (!getenv("CINDEXTEST_FAILONERROR")) + return 0; + + Num = clang_getNumDiagnostics(TU); + for (i = 0; i != Num; ++i) { + Diag = clang_getDiagnostic(TU, i); + if (clang_getDiagnosticSeverity(Diag) >= CXDiagnostic_Error) { + DiagStr = clang_formatDiagnostic(Diag, + clang_defaultDiagnosticDisplayOptions()); + fprintf(stderr, "%s\n", clang_getCString(DiagStr)); + clang_disposeString(DiagStr); + clang_disposeDiagnostic(Diag); + return -1; + } + clang_disposeDiagnostic(Diag); + } + + return 0; +} + +static void print_completion_string(CXCompletionString completion_string, + FILE *file) { + int I, N; + + N = clang_getNumCompletionChunks(completion_string); + for (I = 0; I != N; ++I) { + CXString text; + const char *cstr; + enum CXCompletionChunkKind Kind + = clang_getCompletionChunkKind(completion_string, I); + + if (Kind == CXCompletionChunk_Optional) { + fprintf(file, "{Optional "); + print_completion_string( + clang_getCompletionChunkCompletionString(completion_string, I), + file); + fprintf(file, "}"); + continue; + } + + if (Kind == CXCompletionChunk_VerticalSpace) { + fprintf(file, "{VerticalSpace }"); + continue; + } + + text = clang_getCompletionChunkText(completion_string, I); + cstr = clang_getCString(text); + fprintf(file, "{%s %s}", + clang_getCompletionChunkKindSpelling(Kind), + cstr ? cstr : ""); + clang_disposeString(text); + } + +} + +static void print_line_column(CXSourceLocation location, FILE *file) { + unsigned line, column; + clang_getExpansionLocation(location, NULL, &line, &column, NULL); + fprintf(file, "%d:%d", line, column); +} + +static void print_token_range(CXTranslationUnit translation_unit, + CXSourceLocation start, FILE *file) { + CXToken *token = clang_getToken(translation_unit, start); + + fprintf(file, "{"); + if (token != NULL) { + CXSourceRange token_range = clang_getTokenExtent(translation_unit, *token); + print_line_column(clang_getRangeStart(token_range), file); + fprintf(file, "-"); + print_line_column(clang_getRangeEnd(token_range), file); + clang_disposeTokens(translation_unit, token, 1); + } + + fprintf(file, "}"); +} + +static void print_completion_result(CXTranslationUnit translation_unit, + CXCodeCompleteResults *completion_results, + unsigned index, + FILE *file) { + CXCompletionResult *completion_result = completion_results->Results + index; + CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind); + unsigned annotationCount; + enum CXCursorKind ParentKind; + CXString ParentName; + CXString BriefComment; + CXString Annotation; + const char *BriefCommentCString; + unsigned i; + + fprintf(file, "%s:", clang_getCString(ks)); + clang_disposeString(ks); + + print_completion_string(completion_result->CompletionString, file); + fprintf(file, " (%u)", + clang_getCompletionPriority(completion_result->CompletionString)); + switch (clang_getCompletionAvailability(completion_result->CompletionString)){ + case CXAvailability_Available: + break; + + case CXAvailability_Deprecated: + fprintf(file, " (deprecated)"); + break; + + case CXAvailability_NotAvailable: + fprintf(file, " (unavailable)"); + break; + + case CXAvailability_NotAccessible: + fprintf(file, " (inaccessible)"); + break; + } + + annotationCount = clang_getCompletionNumAnnotations( + completion_result->CompletionString); + if (annotationCount) { + unsigned i; + fprintf(file, " ("); + for (i = 0; i < annotationCount; ++i) { + if (i != 0) + fprintf(file, ", "); + Annotation = + clang_getCompletionAnnotation(completion_result->CompletionString, i); + fprintf(file, "\"%s\"", clang_getCString(Annotation)); + clang_disposeString(Annotation); + } + fprintf(file, ")"); + } + + if (!getenv("CINDEXTEST_NO_COMPLETION_PARENTS")) { + ParentName = clang_getCompletionParent(completion_result->CompletionString, + &ParentKind); + if (ParentKind != CXCursor_NotImplemented) { + CXString KindSpelling = clang_getCursorKindSpelling(ParentKind); + fprintf(file, " (parent: %s '%s')", + clang_getCString(KindSpelling), + clang_getCString(ParentName)); + clang_disposeString(KindSpelling); + } + clang_disposeString(ParentName); + } + + BriefComment = clang_getCompletionBriefComment( + completion_result->CompletionString); + BriefCommentCString = clang_getCString(BriefComment); + if (BriefCommentCString && *BriefCommentCString != '\0') { + fprintf(file, "(brief comment: %s)", BriefCommentCString); + } + clang_disposeString(BriefComment); + + for (i = 0; i < clang_getCompletionNumFixIts(completion_results, index); + ++i) { + CXSourceRange correction_range; + CXString FixIt = clang_getCompletionFixIt(completion_results, index, i, + &correction_range); + fprintf(file, " (requires fix-it: "); + print_token_range(translation_unit, clang_getRangeStart(correction_range), + file); + fprintf(file, " to \"%s\")", clang_getCString(FixIt)); + clang_disposeString(FixIt); + } + + fprintf(file, "\n"); +} + +void print_completion_contexts(unsigned long long contexts, FILE *file) { + fprintf(file, "Completion contexts:\n"); + if (contexts == CXCompletionContext_Unknown) { + fprintf(file, "Unknown\n"); + } + if (contexts & CXCompletionContext_AnyType) { + fprintf(file, "Any type\n"); + } + if (contexts & CXCompletionContext_AnyValue) { + fprintf(file, "Any value\n"); + } + if (contexts & CXCompletionContext_ObjCObjectValue) { + fprintf(file, "Objective-C object value\n"); + } + if (contexts & CXCompletionContext_ObjCSelectorValue) { + fprintf(file, "Objective-C selector value\n"); + } + if (contexts & CXCompletionContext_CXXClassTypeValue) { + fprintf(file, "C++ class type value\n"); + } + if (contexts & CXCompletionContext_DotMemberAccess) { + fprintf(file, "Dot member access\n"); + } + if (contexts & CXCompletionContext_ArrowMemberAccess) { + fprintf(file, "Arrow member access\n"); + } + if (contexts & CXCompletionContext_ObjCPropertyAccess) { + fprintf(file, "Objective-C property access\n"); + } + if (contexts & CXCompletionContext_EnumTag) { + fprintf(file, "Enum tag\n"); + } + if (contexts & CXCompletionContext_UnionTag) { + fprintf(file, "Union tag\n"); + } + if (contexts & CXCompletionContext_StructTag) { + fprintf(file, "Struct tag\n"); + } + if (contexts & CXCompletionContext_ClassTag) { + fprintf(file, "Class name\n"); + } + if (contexts & CXCompletionContext_Namespace) { + fprintf(file, "Namespace or namespace alias\n"); + } + if (contexts & CXCompletionContext_NestedNameSpecifier) { + fprintf(file, "Nested name specifier\n"); + } + if (contexts & CXCompletionContext_ObjCInterface) { + fprintf(file, "Objective-C interface\n"); + } + if (contexts & CXCompletionContext_ObjCProtocol) { + fprintf(file, "Objective-C protocol\n"); + } + if (contexts & CXCompletionContext_ObjCCategory) { + fprintf(file, "Objective-C category\n"); + } + if (contexts & CXCompletionContext_ObjCInstanceMessage) { + fprintf(file, "Objective-C instance method\n"); + } + if (contexts & CXCompletionContext_ObjCClassMessage) { + fprintf(file, "Objective-C class method\n"); + } + if (contexts & CXCompletionContext_ObjCSelectorName) { + fprintf(file, "Objective-C selector name\n"); + } + if (contexts & CXCompletionContext_MacroName) { + fprintf(file, "Macro name\n"); + } + if (contexts & CXCompletionContext_NaturalLanguage) { + fprintf(file, "Natural language\n"); + } +} + +int perform_code_completion(int argc, const char **argv, int timing_only) { + const char *input = argv[1]; + char *filename = 0; + unsigned line; + unsigned column; + CXIndex CIdx; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + CXCodeCompleteResults *results = 0; + enum CXErrorCode Err; + CXTranslationUnit TU; + unsigned I, Repeats = 1; + unsigned completionOptions = clang_defaultCodeCompleteOptions(); + const char *InvocationPath; + + if (getenv("CINDEXTEST_CODE_COMPLETE_PATTERNS")) + completionOptions |= CXCodeComplete_IncludeCodePatterns; + if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS")) + completionOptions |= CXCodeComplete_IncludeBriefComments; + if (getenv("CINDEXTEST_COMPLETION_SKIP_PREAMBLE")) + completionOptions |= CXCodeComplete_SkipPreamble; + if (getenv("CINDEXTEST_COMPLETION_INCLUDE_FIXITS")) + completionOptions |= CXCodeComplete_IncludeCompletionsWithFixIts; + + if (timing_only) + input += strlen("-code-completion-timing="); + else + input += strlen("-code-completion-at="); + + if ((errorCode = parse_file_line_column(input, &filename, &line, &column, + 0, 0))) + return errorCode; + + if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) + return -1; + + CIdx = clang_createIndex(0, 0); + InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); + if (InvocationPath) + clang_CXIndex_setInvocationEmissionPathOption(CIdx, InvocationPath); + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + Err = clang_parseTranslationUnit2(CIdx, 0, + argv + num_unsaved_files + 2, + argc - num_unsaved_files - 2, + 0, 0, getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + return 1; + } + + Err = clang_reparseTranslationUnit(TU, 0, 0, + clang_defaultReparseOptions(TU)); + + if (Err != CXError_Success) { + fprintf(stderr, "Unable to reparse translation unit!\n"); + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + return 1; + } + + for (I = 0; I != Repeats; ++I) { + results = clang_codeCompleteAt(TU, filename, line, column, + unsaved_files, num_unsaved_files, + completionOptions); + if (!results) { + fprintf(stderr, "Unable to perform code completion!\n"); + return 1; + } + if (I != Repeats-1) + clang_disposeCodeCompleteResults(results); + } + + if (results) { + unsigned i, n = results->NumResults, containerIsIncomplete = 0; + unsigned long long contexts; + enum CXCursorKind containerKind; + CXString objCSelector; + const char *selectorString; + if (!timing_only) { + /* Sort the code-completion results based on the typed text. */ + clang_sortCodeCompletionResults(results->Results, results->NumResults); + + for (i = 0; i != n; ++i) + print_completion_result(TU, results, i, stdout); + } + n = clang_codeCompleteGetNumDiagnostics(results); + for (i = 0; i != n; ++i) { + CXDiagnostic diag = clang_codeCompleteGetDiagnostic(results, i); + PrintDiagnostic(diag); + clang_disposeDiagnostic(diag); + } + + contexts = clang_codeCompleteGetContexts(results); + print_completion_contexts(contexts, stdout); + + containerKind = clang_codeCompleteGetContainerKind(results, + &containerIsIncomplete); + + if (containerKind != CXCursor_InvalidCode) { + /* We have found a container */ + CXString containerUSR, containerKindSpelling; + containerKindSpelling = clang_getCursorKindSpelling(containerKind); + printf("Container Kind: %s\n", clang_getCString(containerKindSpelling)); + clang_disposeString(containerKindSpelling); + + if (containerIsIncomplete) { + printf("Container is incomplete\n"); + } + else { + printf("Container is complete\n"); + } + + containerUSR = clang_codeCompleteGetContainerUSR(results); + printf("Container USR: %s\n", clang_getCString(containerUSR)); + clang_disposeString(containerUSR); + } + + objCSelector = clang_codeCompleteGetObjCSelector(results); + selectorString = clang_getCString(objCSelector); + if (selectorString && strlen(selectorString) > 0) { + printf("Objective-C selector: %s\n", selectorString); + } + clang_disposeString(objCSelector); + + clang_disposeCodeCompleteResults(results); + } + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(filename); + + free_remapped_files(unsaved_files, num_unsaved_files); + + return 0; +} + +typedef struct { + char *filename; + unsigned line; + unsigned column; +} CursorSourceLocation; + +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; + int num_unsaved_files = 0; + enum CXErrorCode Err; + CXTranslationUnit TU; + CXCursor Cursor; + CursorSourceLocation *Locations = 0; + unsigned NumLocations = 0, Loc; + unsigned Repeats = 1; + unsigned I; + + /* Count the number of locations. */ + while (strstr(argv[NumLocations+1], locations_flag) == argv[NumLocations+1]) + ++NumLocations; + + /* Parse the locations. */ + assert(NumLocations > 0 && "Unable to count locations?"); + Locations = (CursorSourceLocation *)malloc( + NumLocations * sizeof(CursorSourceLocation)); + assert(Locations); + for (Loc = 0; Loc < NumLocations; ++Loc) { + 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))) + return errorCode; + } + + if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, + &num_unsaved_files)) + return -1; + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + /* Parse the translation unit. When we're testing clang_getCursor() after + reparsing, don't remap unsaved files until the second parse. */ + CIdx = clang_createIndex(1, 1); + Err = clang_parseTranslationUnit2(CIdx, argv[argc - 1], + argv + num_unsaved_files + 1 + NumLocations, + argc - num_unsaved_files - 2 - NumLocations, + unsaved_files, + Repeats > 1? 0 : num_unsaved_files, + getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "unable to parse input\n"); + describeLibclangFailure(Err); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (I = 0; I != Repeats; ++I) { + if (Repeats > 1) { + Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU)); + if (Err != CXError_Success) { + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + return 1; + } + } + + if (checkForErrors(TU) != 0) + return -1; + + for (Loc = 0; Loc < NumLocations; ++Loc) { + CXFile file = clang_getFile(TU, Locations[Loc].filename); + if (!file) + continue; + + Cursor = clang_getCursor(TU, + clang_getLocation(TU, file, Locations[Loc].line, + Locations[Loc].column)); + + if (checkForErrors(TU) != 0) + return -1; + + if (I + 1 == Repeats) { + handler(Cursor); + free(Locations[Loc].filename); + } + } + } + + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(Locations); + free_remapped_files(unsaved_files, num_unsaved_files); + 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 || + Cursor.kind == CXCursor_MemberRefExpr) { + CXType T = clang_Cursor_getReceiverType(Cursor); + if (T.kind != CXType_Invalid) { + 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: + { + printf("Kind: Int, "); + if (clang_EvalResult_isUnsignedInt(result)) { + unsigned long long val = clang_EvalResult_getAsUnsigned(result); + printf("unsigned, Value: %llu", val); + } else { + long long val = clang_EvalResult_getAsLongLong(result); + printf("Value: %lld", 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)) + return CXVisit_Continue; + + PrintCursor(cursor, NULL); + PrintRange(range, ""); + printf("\n"); + return CXVisit_Continue; +} + +static int find_file_refs_at(int argc, const char **argv) { + CXIndex CIdx; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + enum CXErrorCode Err; + CXTranslationUnit TU; + CXCursor Cursor; + CursorSourceLocation *Locations = 0; + unsigned NumLocations = 0, Loc; + unsigned Repeats = 1; + unsigned I; + + /* Count the number of locations. */ + while (strstr(argv[NumLocations+1], "-file-refs-at=") == argv[NumLocations+1]) + ++NumLocations; + + /* Parse the locations. */ + assert(NumLocations > 0 && "Unable to count locations?"); + Locations = (CursorSourceLocation *)malloc( + NumLocations * sizeof(CursorSourceLocation)); + assert(Locations); + for (Loc = 0; Loc < NumLocations; ++Loc) { + const char *input = argv[Loc + 1] + strlen("-file-refs-at="); + if ((errorCode = parse_file_line_column(input, &Locations[Loc].filename, + &Locations[Loc].line, + &Locations[Loc].column, 0, 0))) + return errorCode; + } + + if (parse_remapped_files(argc, argv, NumLocations + 1, &unsaved_files, + &num_unsaved_files)) + return -1; + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 5; + + /* Parse the translation unit. When we're testing clang_getCursor() after + reparsing, don't remap unsaved files until the second parse. */ + CIdx = clang_createIndex(1, 1); + Err = clang_parseTranslationUnit2(CIdx, argv[argc - 1], + argv + num_unsaved_files + 1 + NumLocations, + argc - num_unsaved_files - 2 - NumLocations, + unsaved_files, + Repeats > 1? 0 : num_unsaved_files, + getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "unable to parse input\n"); + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (I = 0; I != Repeats; ++I) { + if (Repeats > 1) { + Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU)); + if (Err != CXError_Success) { + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + return 1; + } + } + + if (checkForErrors(TU) != 0) + return -1; + + for (Loc = 0; Loc < NumLocations; ++Loc) { + CXFile file = clang_getFile(TU, Locations[Loc].filename); + if (!file) + continue; + + Cursor = clang_getCursor(TU, + clang_getLocation(TU, file, Locations[Loc].line, + Locations[Loc].column)); + + if (checkForErrors(TU) != 0) + return -1; + + if (I + 1 == Repeats) { + CXCursorAndRangeVisitor visitor = { 0, findFileRefsVisit }; + PrintCursor(Cursor, NULL); + printf("\n"); + clang_findReferencesInFile(Cursor, file, visitor); + free(Locations[Loc].filename); + + if (checkForErrors(TU) != 0) + return -1; + } + } + } + + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(Locations); + free_remapped_files(unsaved_files, num_unsaved_files); + return 0; +} + +static enum CXVisitorResult findFileIncludesVisit(void *context, + CXCursor cursor, CXSourceRange range) { + PrintCursor(cursor, NULL); + PrintRange(range, ""); + printf("\n"); + return CXVisit_Continue; +} + +static int find_file_includes_in(int argc, const char **argv) { + CXIndex CIdx; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + enum CXErrorCode Err; + CXTranslationUnit TU; + const char **Filenames = 0; + unsigned NumFilenames = 0; + unsigned Repeats = 1; + unsigned I, FI; + + /* Count the number of locations. */ + while (strstr(argv[NumFilenames+1], "-file-includes-in=") == argv[NumFilenames+1]) + ++NumFilenames; + + /* Parse the locations. */ + assert(NumFilenames > 0 && "Unable to count filenames?"); + Filenames = (const char **)malloc(NumFilenames * sizeof(const char *)); + assert(Filenames); + for (I = 0; I < NumFilenames; ++I) { + const char *input = argv[I + 1] + strlen("-file-includes-in="); + /* Copy the file name. */ + Filenames[I] = input; + } + + if (parse_remapped_files(argc, argv, NumFilenames + 1, &unsaved_files, + &num_unsaved_files)) + return -1; + + if (getenv("CINDEXTEST_EDITING")) + Repeats = 2; + + /* Parse the translation unit. When we're testing clang_getCursor() after + reparsing, don't remap unsaved files until the second parse. */ + CIdx = clang_createIndex(1, 1); + Err = clang_parseTranslationUnit2( + CIdx, argv[argc - 1], + argv + num_unsaved_files + 1 + NumFilenames, + argc - num_unsaved_files - 2 - NumFilenames, + unsaved_files, + Repeats > 1 ? 0 : num_unsaved_files, getDefaultParsingOptions(), &TU); + + if (Err != CXError_Success) { + fprintf(stderr, "unable to parse input\n"); + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + return -1; + } + + if (checkForErrors(TU) != 0) + return -1; + + for (I = 0; I != Repeats; ++I) { + if (Repeats > 1) { + Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU)); + if (Err != CXError_Success) { + describeLibclangFailure(Err); + clang_disposeTranslationUnit(TU); + return 1; + } + } + + if (checkForErrors(TU) != 0) + return -1; + + for (FI = 0; FI < NumFilenames; ++FI) { + CXFile file = clang_getFile(TU, Filenames[FI]); + if (!file) + continue; + + if (checkForErrors(TU) != 0) + return -1; + + if (I + 1 == Repeats) { + CXCursorAndRangeVisitor visitor = { 0, findFileIncludesVisit }; + clang_findIncludesInFile(TU, file, visitor); + + if (checkForErrors(TU) != 0) + return -1; + } + } + } + + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free((void *)Filenames); + free_remapped_files(unsaved_files, num_unsaved_files); + return 0; +} + +#define MAX_IMPORTED_ASTFILES 200 + +typedef struct { + char **filenames; + unsigned num_files; +} ImportedASTFilesData; + +static ImportedASTFilesData *importedASTs_create() { + ImportedASTFilesData *p; + p = malloc(sizeof(ImportedASTFilesData)); + assert(p); + p->filenames = malloc(MAX_IMPORTED_ASTFILES * sizeof(const char *)); + assert(p->filenames); + p->num_files = 0; + return p; +} + +static void importedASTs_dispose(ImportedASTFilesData *p) { + unsigned i; + if (!p) + return; + + for (i = 0; i < p->num_files; ++i) + free(p->filenames[i]); + free(p->filenames); + free(p); +} + +static void importedASTS_insert(ImportedASTFilesData *p, const char *file) { + unsigned i; + assert(p && file); + for (i = 0; i < p->num_files; ++i) + if (strcmp(file, p->filenames[i]) == 0) + return; + assert(p->num_files + 1 < MAX_IMPORTED_ASTFILES); + p->filenames[p->num_files++] = strdup(file); +} + +typedef struct IndexDataStringList_ { + struct IndexDataStringList_ *next; + char data[1]; /* Dynamically sized. */ +} IndexDataStringList; + +typedef struct { + const char *check_prefix; + int first_check_printed; + int fail_for_error; + int abort; + CXString main_filename; + ImportedASTFilesData *importedASTs; + IndexDataStringList *strings; + CXTranslationUnit TU; +} IndexData; + +static void free_client_data(IndexData *index_data) { + IndexDataStringList *node = index_data->strings; + while (node) { + IndexDataStringList *next = node->next; + free(node); + node = next; + } + index_data->strings = NULL; +} + +static void printCheck(IndexData *data) { + if (data->check_prefix) { + if (data->first_check_printed) { + printf("// %s-NEXT: ", data->check_prefix); + } else { + printf("// %s : ", data->check_prefix); + data->first_check_printed = 1; + } + } +} + +static void printCXIndexFile(CXIdxClientFile file) { + CXString filename = clang_getFileName((CXFile)file); + printf("%s", clang_getCString(filename)); + clang_disposeString(filename); +} + +static void printCXIndexLoc(CXIdxLoc loc, CXClientData client_data) { + IndexData *index_data; + CXString filename; + const char *cname; + CXIdxClientFile file; + unsigned line, column; + const char *main_filename; + int isMainFile; + + index_data = (IndexData *)client_data; + clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); + if (line == 0) { + printf("<invalid>"); + return; + } + if (!file) { + printf("<no idxfile>"); + return; + } + filename = clang_getFileName((CXFile)file); + cname = clang_getCString(filename); + main_filename = clang_getCString(index_data->main_filename); + if (strcmp(cname, main_filename) == 0) + isMainFile = 1; + else + isMainFile = 0; + clang_disposeString(filename); + + if (!isMainFile) { + printCXIndexFile(file); + printf(":"); + } + printf("%d:%d", line, column); +} + +static unsigned digitCount(unsigned val) { + unsigned c = 1; + while (1) { + if (val < 10) + return c; + ++c; + val /= 10; + } +} + +static CXIdxClientContainer makeClientContainer(CXClientData *client_data, + const CXIdxEntityInfo *info, + CXIdxLoc loc) { + IndexData *index_data; + IndexDataStringList *node; + const char *name; + char *newStr; + CXIdxClientFile file; + unsigned line, column; + + name = info->name; + if (!name) + name = "<anon-tag>"; + + clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); + + node = + (IndexDataStringList *)malloc(sizeof(IndexDataStringList) + strlen(name) + + digitCount(line) + digitCount(column) + 2); + assert(node); + newStr = node->data; + sprintf(newStr, "%s:%d:%d", name, line, column); + + /* Remember string so it can be freed later. */ + index_data = (IndexData *)client_data; + node->next = index_data->strings; + index_data->strings = node; + + return (CXIdxClientContainer)newStr; +} + +static void printCXIndexContainer(const CXIdxContainerInfo *info) { + CXIdxClientContainer container; + container = clang_index_getClientContainer(info); + if (!container) + printf("[<<NULL>>]"); + else + printf("[%s]", (const char *)container); +} + +static const char *getEntityKindString(CXIdxEntityKind kind) { + switch (kind) { + case CXIdxEntity_Unexposed: return "<<UNEXPOSED>>"; + case CXIdxEntity_Typedef: return "typedef"; + case CXIdxEntity_Function: return "function"; + case CXIdxEntity_Variable: return "variable"; + case CXIdxEntity_Field: return "field"; + case CXIdxEntity_EnumConstant: return "enumerator"; + case CXIdxEntity_ObjCClass: return "objc-class"; + case CXIdxEntity_ObjCProtocol: return "objc-protocol"; + case CXIdxEntity_ObjCCategory: return "objc-category"; + case CXIdxEntity_ObjCInstanceMethod: return "objc-instance-method"; + case CXIdxEntity_ObjCClassMethod: return "objc-class-method"; + case CXIdxEntity_ObjCProperty: return "objc-property"; + case CXIdxEntity_ObjCIvar: return "objc-ivar"; + case CXIdxEntity_Enum: return "enum"; + case CXIdxEntity_Struct: return "struct"; + case CXIdxEntity_Union: return "union"; + case CXIdxEntity_CXXClass: return "c++-class"; + case CXIdxEntity_CXXNamespace: return "namespace"; + case CXIdxEntity_CXXNamespaceAlias: return "namespace-alias"; + case CXIdxEntity_CXXStaticVariable: return "c++-static-var"; + case CXIdxEntity_CXXStaticMethod: return "c++-static-method"; + case CXIdxEntity_CXXInstanceMethod: return "c++-instance-method"; + case CXIdxEntity_CXXConstructor: return "constructor"; + case CXIdxEntity_CXXDestructor: return "destructor"; + case CXIdxEntity_CXXConversionFunction: return "conversion-func"; + case CXIdxEntity_CXXTypeAlias: return "type-alias"; + case CXIdxEntity_CXXInterface: return "c++-__interface"; + } + assert(0 && "Garbage entity kind"); + return 0; +} + +static const char *getEntityTemplateKindString(CXIdxEntityCXXTemplateKind kind) { + switch (kind) { + case CXIdxEntity_NonTemplate: return ""; + case CXIdxEntity_Template: return "-template"; + case CXIdxEntity_TemplatePartialSpecialization: + return "-template-partial-spec"; + case CXIdxEntity_TemplateSpecialization: return "-template-spec"; + } + assert(0 && "Garbage entity kind"); + return 0; +} + +static const char *getEntityLanguageString(CXIdxEntityLanguage kind) { + switch (kind) { + case CXIdxEntityLang_None: return "<none>"; + case CXIdxEntityLang_C: return "C"; + case CXIdxEntityLang_ObjC: return "ObjC"; + case CXIdxEntityLang_CXX: return "C++"; + case CXIdxEntityLang_Swift: return "Swift"; + } + assert(0 && "Garbage language kind"); + return 0; +} + +static void printEntityInfo(const char *cb, + CXClientData client_data, + const CXIdxEntityInfo *info) { + const char *name; + IndexData *index_data; + unsigned i; + index_data = (IndexData *)client_data; + printCheck(index_data); + + if (!info) { + printf("%s: <<NULL>>", cb); + return; + } + + name = info->name; + if (!name) + name = "<anon-tag>"; + + printf("%s: kind: %s%s", cb, getEntityKindString(info->kind), + getEntityTemplateKindString(info->templateKind)); + printf(" | name: %s", name); + printf(" | USR: %s", info->USR); + printf(" | lang: %s", getEntityLanguageString(info->lang)); + + for (i = 0; i != info->numAttributes; ++i) { + const CXIdxAttrInfo *Attr = info->attributes[i]; + printf(" <attribute>: "); + PrintCursor(Attr->cursor, NULL); + } +} + +static void printBaseClassInfo(CXClientData client_data, + const CXIdxBaseClassInfo *info) { + printEntityInfo(" <base>", client_data, info->base); + printf(" | cursor: "); + PrintCursor(info->cursor, NULL); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); +} + +static void printProtocolList(const CXIdxObjCProtocolRefListInfo *ProtoInfo, + CXClientData client_data) { + unsigned i; + for (i = 0; i < ProtoInfo->numProtocols; ++i) { + printEntityInfo(" <protocol>", client_data, + ProtoInfo->protocols[i]->protocol); + printf(" | cursor: "); + PrintCursor(ProtoInfo->protocols[i]->cursor, NULL); + printf(" | loc: "); + printCXIndexLoc(ProtoInfo->protocols[i]->loc, client_data); + printf("\n"); + } +} + +static void printSymbolRole(CXSymbolRole role) { + if (role & CXSymbolRole_Declaration) + printf(" decl"); + if (role & CXSymbolRole_Definition) + printf(" def"); + if (role & CXSymbolRole_Reference) + printf(" ref"); + if (role & CXSymbolRole_Read) + printf(" read"); + if (role & CXSymbolRole_Write) + printf(" write"); + if (role & CXSymbolRole_Call) + printf(" call"); + if (role & CXSymbolRole_Dynamic) + printf(" dyn"); + if (role & CXSymbolRole_AddressOf) + printf(" addr"); + if (role & CXSymbolRole_Implicit) + printf(" implicit"); +} + +static void index_diagnostic(CXClientData client_data, + CXDiagnosticSet diagSet, void *reserved) { + CXString str; + const char *cstr; + unsigned numDiags, i; + CXDiagnostic diag; + IndexData *index_data; + index_data = (IndexData *)client_data; + printCheck(index_data); + + numDiags = clang_getNumDiagnosticsInSet(diagSet); + for (i = 0; i != numDiags; ++i) { + diag = clang_getDiagnosticInSet(diagSet, i); + str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); + cstr = clang_getCString(str); + printf("[diagnostic]: %s\n", cstr); + clang_disposeString(str); + + if (getenv("CINDEXTEST_FAILONERROR") && + clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error) { + index_data->fail_for_error = 1; + } + } +} + +static CXIdxClientFile index_enteredMainFile(CXClientData client_data, + CXFile file, void *reserved) { + IndexData *index_data; + + index_data = (IndexData *)client_data; + printCheck(index_data); + + index_data->main_filename = clang_getFileName(file); + + printf("[enteredMainFile]: "); + printCXIndexFile((CXIdxClientFile)file); + printf("\n"); + + return (CXIdxClientFile)file; +} + +static CXIdxClientFile index_ppIncludedFile(CXClientData client_data, + const CXIdxIncludedFileInfo *info) { + IndexData *index_data; + CXModule Mod; + index_data = (IndexData *)client_data; + printCheck(index_data); + + printf("[ppIncludedFile]: "); + printCXIndexFile((CXIdxClientFile)info->file); + printf(" | name: \"%s\"", info->filename); + printf(" | hash loc: "); + printCXIndexLoc(info->hashLoc, client_data); + printf(" | isImport: %d | isAngled: %d | isModule: %d", + info->isImport, info->isAngled, info->isModuleImport); + + Mod = clang_getModuleForFile(index_data->TU, (CXFile)info->file); + if (Mod) { + CXString str = clang_Module_getFullName(Mod); + const char *cstr = clang_getCString(str); + printf(" | module: %s", cstr); + clang_disposeString(str); + } + + printf("\n"); + + return (CXIdxClientFile)info->file; +} + +static CXIdxClientFile index_importedASTFile(CXClientData client_data, + const CXIdxImportedASTFileInfo *info) { + IndexData *index_data; + index_data = (IndexData *)client_data; + printCheck(index_data); + + if (index_data->importedASTs) { + CXString filename = clang_getFileName(info->file); + importedASTS_insert(index_data->importedASTs, clang_getCString(filename)); + clang_disposeString(filename); + } + + printf("[importedASTFile]: "); + printCXIndexFile((CXIdxClientFile)info->file); + if (info->module) { + CXString name = clang_Module_getFullName(info->module); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); + printf(" | name: \"%s\"", clang_getCString(name)); + printf(" | isImplicit: %d\n", info->isImplicit); + clang_disposeString(name); + } else { + /* PCH file, the rest are not relevant. */ + printf("\n"); + } + + return (CXIdxClientFile)info->file; +} + +static CXIdxClientContainer +index_startedTranslationUnit(CXClientData client_data, void *reserved) { + IndexData *index_data; + index_data = (IndexData *)client_data; + printCheck(index_data); + + printf("[startedTranslationUnit]\n"); + return (CXIdxClientContainer)"TU"; +} + +static void index_indexDeclaration(CXClientData client_data, + const CXIdxDeclInfo *info) { + IndexData *index_data; + const CXIdxObjCCategoryDeclInfo *CatInfo; + const CXIdxObjCInterfaceDeclInfo *InterInfo; + const CXIdxObjCProtocolRefListInfo *ProtoInfo; + const CXIdxObjCPropertyDeclInfo *PropInfo; + const CXIdxCXXClassDeclInfo *CXXClassInfo; + unsigned i; + index_data = (IndexData *)client_data; + + printEntityInfo("[indexDeclaration]", client_data, info->entityInfo); + printf(" | cursor: "); + PrintCursor(info->cursor, NULL); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); + printf(" | semantic-container: "); + printCXIndexContainer(info->semanticContainer); + printf(" | lexical-container: "); + printCXIndexContainer(info->lexicalContainer); + printf(" | isRedecl: %d", info->isRedeclaration); + printf(" | isDef: %d", info->isDefinition); + if (info->flags & CXIdxDeclFlag_Skipped) { + assert(!info->isContainer); + printf(" | isContainer: skipped"); + } else { + printf(" | isContainer: %d", info->isContainer); + } + printf(" | isImplicit: %d\n", info->isImplicit); + + for (i = 0; i != info->numAttributes; ++i) { + const CXIdxAttrInfo *Attr = info->attributes[i]; + printf(" <attribute>: "); + PrintCursor(Attr->cursor, NULL); + printf("\n"); + } + + if (clang_index_isEntityObjCContainerKind(info->entityInfo->kind)) { + const char *kindName = 0; + CXIdxObjCContainerKind K = clang_index_getObjCContainerDeclInfo(info)->kind; + switch (K) { + case CXIdxObjCContainer_ForwardRef: + kindName = "forward-ref"; break; + case CXIdxObjCContainer_Interface: + kindName = "interface"; break; + case CXIdxObjCContainer_Implementation: + kindName = "implementation"; break; + } + printCheck(index_data); + printf(" <ObjCContainerInfo>: kind: %s\n", kindName); + } + + if ((CatInfo = clang_index_getObjCCategoryDeclInfo(info))) { + printEntityInfo(" <ObjCCategoryInfo>: class", client_data, + CatInfo->objcClass); + printf(" | cursor: "); + PrintCursor(CatInfo->classCursor, NULL); + printf(" | loc: "); + printCXIndexLoc(CatInfo->classLoc, client_data); + printf("\n"); + } + + if ((InterInfo = clang_index_getObjCInterfaceDeclInfo(info))) { + if (InterInfo->superInfo) { + printBaseClassInfo(client_data, InterInfo->superInfo); + printf("\n"); + } + } + + if ((ProtoInfo = clang_index_getObjCProtocolRefListInfo(info))) { + printProtocolList(ProtoInfo, client_data); + } + + if ((PropInfo = clang_index_getObjCPropertyDeclInfo(info))) { + if (PropInfo->getter) { + printEntityInfo(" <getter>", client_data, PropInfo->getter); + printf("\n"); + } + if (PropInfo->setter) { + printEntityInfo(" <setter>", client_data, PropInfo->setter); + printf("\n"); + } + } + + if ((CXXClassInfo = clang_index_getCXXClassDeclInfo(info))) { + for (i = 0; i != CXXClassInfo->numBases; ++i) { + printBaseClassInfo(client_data, CXXClassInfo->bases[i]); + printf("\n"); + } + } + + if (info->declAsContainer) + clang_index_setClientContainer( + info->declAsContainer, + makeClientContainer(client_data, info->entityInfo, info->loc)); +} + +static void index_indexEntityReference(CXClientData client_data, + const CXIdxEntityRefInfo *info) { + printEntityInfo("[indexEntityReference]", client_data, + info->referencedEntity); + printf(" | cursor: "); + PrintCursor(info->cursor, NULL); + printf(" | loc: "); + printCXIndexLoc(info->loc, client_data); + printEntityInfo(" | <parent>:", client_data, info->parentEntity); + printf(" | container: "); + printCXIndexContainer(info->container); + printf(" | refkind: "); + switch (info->kind) { + case CXIdxEntityRef_Direct: printf("direct"); break; + case CXIdxEntityRef_Implicit: printf("implicit"); break; + } + printf(" | role:"); + printSymbolRole(info->role); + printf("\n"); +} + +static int index_abortQuery(CXClientData client_data, void *reserved) { + IndexData *index_data; + index_data = (IndexData *)client_data; + return index_data->abort; +} + +static IndexerCallbacks IndexCB = { + index_abortQuery, + index_diagnostic, + index_enteredMainFile, + index_ppIncludedFile, + index_importedASTFile, + index_startedTranslationUnit, + index_indexDeclaration, + index_indexEntityReference +}; + +static unsigned getIndexOptions(void) { + unsigned index_opts; + index_opts = 0; + if (getenv("CINDEXTEST_SUPPRESSREFS")) + index_opts |= CXIndexOpt_SuppressRedundantRefs; + if (getenv("CINDEXTEST_INDEXLOCALSYMBOLS")) + index_opts |= CXIndexOpt_IndexFunctionLocalSymbols; + if (!getenv("CINDEXTEST_DISABLE_SKIPPARSEDBODIES")) + index_opts |= CXIndexOpt_SkipParsedBodiesInSession; + if (getenv("CINDEXTEST_INDEXIMPLICITTEMPLATEINSTANTIATIONS")) + index_opts |= CXIndexOpt_IndexImplicitTemplateInstantiations; + + return index_opts; +} + +static int index_compile_args(int num_args, const char **args, + CXIndexAction idxAction, + ImportedASTFilesData *importedASTs, + const char *check_prefix) { + IndexData index_data; + unsigned index_opts; + int result; + + if (num_args == 0) { + fprintf(stderr, "no compiler arguments\n"); + return -1; + } + + index_data.check_prefix = check_prefix; + index_data.first_check_printed = 0; + index_data.fail_for_error = 0; + index_data.abort = 0; + index_data.main_filename = createCXString(""); + index_data.importedASTs = importedASTs; + index_data.strings = NULL; + index_data.TU = NULL; + + index_opts = getIndexOptions(); + result = clang_indexSourceFile(idxAction, &index_data, + &IndexCB,sizeof(IndexCB), index_opts, + 0, args, num_args, 0, 0, 0, + getDefaultParsingOptions()); + if (result != CXError_Success) + describeLibclangFailure(result); + + if (index_data.fail_for_error) + result = -1; + + clang_disposeString(index_data.main_filename); + free_client_data(&index_data); + return result; +} + +static int index_ast_file(const char *ast_file, + CXIndex Idx, + CXIndexAction idxAction, + ImportedASTFilesData *importedASTs, + const char *check_prefix) { + CXTranslationUnit TU; + IndexData index_data; + unsigned index_opts; + int result; + + if (!CreateTranslationUnit(Idx, ast_file, &TU)) + return -1; + + index_data.check_prefix = check_prefix; + index_data.first_check_printed = 0; + index_data.fail_for_error = 0; + index_data.abort = 0; + index_data.main_filename = createCXString(""); + index_data.importedASTs = importedASTs; + index_data.strings = NULL; + index_data.TU = TU; + + index_opts = getIndexOptions(); + result = clang_indexTranslationUnit(idxAction, &index_data, + &IndexCB,sizeof(IndexCB), + index_opts, TU); + if (index_data.fail_for_error) + result = -1; + + clang_disposeTranslationUnit(TU); + clang_disposeString(index_data.main_filename); + free_client_data(&index_data); + return result; +} + +static int index_file(int argc, const char **argv, int full) { + const char *check_prefix; + CXIndex Idx; + CXIndexAction idxAction; + ImportedASTFilesData *importedASTs; + int result; + + check_prefix = 0; + if (argc > 0) { + if (strstr(argv[0], "-check-prefix=") == argv[0]) { + check_prefix = argv[0] + strlen("-check-prefix="); + ++argv; + --argc; + } + } + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + idxAction = clang_IndexAction_create(Idx); + importedASTs = 0; + if (full) + importedASTs = importedASTs_create(); + + result = index_compile_args(argc, argv, idxAction, importedASTs, check_prefix); + if (result != 0) + goto finished; + + if (full) { + unsigned i; + for (i = 0; i < importedASTs->num_files && result == 0; ++i) { + result = index_ast_file(importedASTs->filenames[i], Idx, idxAction, + importedASTs, check_prefix); + } + } + +finished: + importedASTs_dispose(importedASTs); + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(Idx); + return result; +} + +static int index_tu(int argc, const char **argv) { + const char *check_prefix; + CXIndex Idx; + CXIndexAction idxAction; + int result; + + check_prefix = 0; + if (argc > 0) { + if (strstr(argv[0], "-check-prefix=") == argv[0]) { + check_prefix = argv[0] + strlen("-check-prefix="); + ++argv; + --argc; + } + } + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + idxAction = clang_IndexAction_create(Idx); + + result = index_ast_file(argv[0], Idx, idxAction, + /*importedASTs=*/0, check_prefix); + + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(Idx); + return result; +} + +static int index_compile_db(int argc, const char **argv) { + const char *check_prefix; + CXIndex Idx; + CXIndexAction idxAction; + int errorCode = 0; + + check_prefix = 0; + if (argc > 0) { + if (strstr(argv[0], "-check-prefix=") == argv[0]) { + check_prefix = argv[0] + strlen("-check-prefix="); + ++argv; + --argc; + } + } + + if (argc == 0) { + fprintf(stderr, "no compilation database\n"); + return -1; + } + + if (!(Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/1))) { + fprintf(stderr, "Could not create Index\n"); + return 1; + } + idxAction = clang_IndexAction_create(Idx); + + { + const char *database = argv[0]; + CXCompilationDatabase db = 0; + CXCompileCommands CCmds = 0; + CXCompileCommand CCmd; + CXCompilationDatabase_Error ec; + CXString wd; +#define MAX_COMPILE_ARGS 512 + CXString cxargs[MAX_COMPILE_ARGS]; + const char *args[MAX_COMPILE_ARGS]; + char *tmp; + unsigned len; + char *buildDir; + int i, a, numCmds, numArgs; + + len = strlen(database); + tmp = (char *) malloc(len+1); + assert(tmp); + memcpy(tmp, database, len+1); + buildDir = dirname(tmp); + + db = clang_CompilationDatabase_fromDirectory(buildDir, &ec); + + if (db) { + + if (ec!=CXCompilationDatabase_NoError) { + printf("unexpected error %d code while loading compilation database\n", ec); + errorCode = -1; + goto cdb_end; + } + + if (chdir(buildDir) != 0) { + printf("Could not chdir to %s\n", buildDir); + errorCode = -1; + goto cdb_end; + } + + CCmds = clang_CompilationDatabase_getAllCompileCommands(db); + if (!CCmds) { + printf("compilation db is empty\n"); + errorCode = -1; + goto cdb_end; + } + + numCmds = clang_CompileCommands_getSize(CCmds); + + if (numCmds==0) { + fprintf(stderr, "should not get an empty compileCommand set\n"); + errorCode = -1; + goto cdb_end; + } + + for (i=0; i<numCmds && errorCode == 0; ++i) { + CCmd = clang_CompileCommands_getCommand(CCmds, i); + + wd = clang_CompileCommand_getDirectory(CCmd); + if (chdir(clang_getCString(wd)) != 0) { + printf("Could not chdir to %s\n", clang_getCString(wd)); + errorCode = -1; + goto cdb_end; + } + clang_disposeString(wd); + + numArgs = clang_CompileCommand_getNumArgs(CCmd); + if (numArgs > MAX_COMPILE_ARGS){ + fprintf(stderr, "got more compile arguments than maximum\n"); + errorCode = -1; + goto cdb_end; + } + for (a=0; a<numArgs; ++a) { + cxargs[a] = clang_CompileCommand_getArg(CCmd, a); + args[a] = clang_getCString(cxargs[a]); + } + + errorCode = index_compile_args(numArgs, args, idxAction, + /*importedASTs=*/0, check_prefix); + + for (a=0; a<numArgs; ++a) + clang_disposeString(cxargs[a]); + } + } else { + printf("database loading failed with error code %d.\n", ec); + errorCode = -1; + } + + cdb_end: + clang_CompileCommands_dispose(CCmds); + clang_CompilationDatabase_dispose(db); + free(tmp); + + } + + clang_IndexAction_dispose(idxAction); + clang_disposeIndex(Idx); + return errorCode; +} + +int perform_token_annotation(int argc, const char **argv) { + const char *input = argv[1]; + char *filename = 0; + unsigned line, second_line; + unsigned column, second_column; + CXIndex CIdx; + CXTranslationUnit TU = 0; + int errorCode; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + CXToken *tokens; + unsigned num_tokens; + CXSourceRange range; + CXSourceLocation startLoc, endLoc; + CXFile file = 0; + CXCursor *cursors = 0; + CXSourceRangeList *skipped_ranges = 0; + enum CXErrorCode Err; + unsigned i; + + input += strlen("-test-annotate-tokens="); + if ((errorCode = parse_file_line_column(input, &filename, &line, &column, + &second_line, &second_column))) + return errorCode; + + if (parse_remapped_files(argc, argv, 2, &unsaved_files, &num_unsaved_files)) { + free(filename); + return -1; + } + + CIdx = clang_createIndex(0, 1); + Err = clang_parseTranslationUnit2(CIdx, argv[argc - 1], + argv + num_unsaved_files + 2, + argc - num_unsaved_files - 3, + unsaved_files, + num_unsaved_files, + getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "unable to parse input\n"); + describeLibclangFailure(Err); + clang_disposeIndex(CIdx); + free(filename); + free_remapped_files(unsaved_files, num_unsaved_files); + return -1; + } + errorCode = 0; + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + if (getenv("CINDEXTEST_EDITING")) { + for (i = 0; i < 5; ++i) { + Err = clang_reparseTranslationUnit(TU, num_unsaved_files, unsaved_files, + clang_defaultReparseOptions(TU)); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to reparse translation unit!\n"); + describeLibclangFailure(Err); + errorCode = -1; + goto teardown; + } + } + } + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + file = clang_getFile(TU, filename); + if (!file) { + fprintf(stderr, "file %s is not in this translation unit\n", filename); + errorCode = -1; + goto teardown; + } + + startLoc = clang_getLocation(TU, file, line, column); + if (clang_equalLocations(clang_getNullLocation(), startLoc)) { + fprintf(stderr, "invalid source location %s:%d:%d\n", filename, line, + column); + errorCode = -1; + goto teardown; + } + + endLoc = clang_getLocation(TU, file, second_line, second_column); + if (clang_equalLocations(clang_getNullLocation(), endLoc)) { + fprintf(stderr, "invalid source location %s:%d:%d\n", filename, + second_line, second_column); + errorCode = -1; + goto teardown; + } + + range = clang_getRange(startLoc, endLoc); + clang_tokenize(TU, range, &tokens, &num_tokens); + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + cursors = (CXCursor *)malloc(num_tokens * sizeof(CXCursor)); + assert(cursors); + clang_annotateTokens(TU, tokens, num_tokens, cursors); + + if (checkForErrors(TU) != 0) { + errorCode = -1; + goto teardown; + } + + skipped_ranges = clang_getSkippedRanges(TU, file); + for (i = 0; i != skipped_ranges->count; ++i) { + unsigned start_line, start_column, end_line, end_column; + clang_getSpellingLocation(clang_getRangeStart(skipped_ranges->ranges[i]), + 0, &start_line, &start_column, 0); + clang_getSpellingLocation(clang_getRangeEnd(skipped_ranges->ranges[i]), + 0, &end_line, &end_column, 0); + printf("Skipping: "); + PrintExtent(stdout, start_line, start_column, end_line, end_column); + printf("\n"); + } + clang_disposeSourceRangeList(skipped_ranges); + + for (i = 0; i != num_tokens; ++i) { + const char *kind = "<unknown>"; + CXString spelling = clang_getTokenSpelling(TU, tokens[i]); + CXSourceRange extent = clang_getTokenExtent(TU, tokens[i]); + unsigned start_line, start_column, end_line, end_column; + + switch (clang_getTokenKind(tokens[i])) { + case CXToken_Punctuation: kind = "Punctuation"; break; + case CXToken_Keyword: kind = "Keyword"; break; + case CXToken_Identifier: kind = "Identifier"; break; + case CXToken_Literal: kind = "Literal"; break; + case CXToken_Comment: kind = "Comment"; break; + } + clang_getSpellingLocation(clang_getRangeStart(extent), + 0, &start_line, &start_column, 0); + clang_getSpellingLocation(clang_getRangeEnd(extent), + 0, &end_line, &end_column, 0); + printf("%s: \"%s\" ", kind, clang_getCString(spelling)); + clang_disposeString(spelling); + PrintExtent(stdout, start_line, start_column, end_line, end_column); + if (!clang_isInvalid(cursors[i].kind)) { + printf(" "); + PrintCursor(cursors[i], NULL); + } + printf("\n"); + } + free(cursors); + clang_disposeTokens(TU, tokens, num_tokens); + + teardown: + PrintDiagnostics(TU); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(CIdx); + free(filename); + free_remapped_files(unsaved_files, num_unsaved_files); + return errorCode; +} + +static int +perform_test_compilation_db(const char *database, int argc, const char **argv) { + CXCompilationDatabase db; + CXCompileCommands CCmds; + CXCompileCommand CCmd; + CXCompilationDatabase_Error ec; + CXString wd; + CXString arg; + int errorCode = 0; + char *tmp; + unsigned len; + char *buildDir; + int i, j, a, numCmds, numArgs; + + len = strlen(database); + tmp = (char *) malloc(len+1); + assert(tmp); + memcpy(tmp, database, len+1); + buildDir = dirname(tmp); + + db = clang_CompilationDatabase_fromDirectory(buildDir, &ec); + + if (db) { + + if (ec!=CXCompilationDatabase_NoError) { + printf("unexpected error %d code while loading compilation database\n", ec); + errorCode = -1; + goto cdb_end; + } + + for (i=0; i<argc && errorCode==0; ) { + if (strcmp(argv[i],"lookup")==0){ + CCmds = clang_CompilationDatabase_getCompileCommands(db, argv[i+1]); + + if (!CCmds) { + printf("file %s not found in compilation db\n", argv[i+1]); + errorCode = -1; + break; + } + + numCmds = clang_CompileCommands_getSize(CCmds); + + if (numCmds==0) { + fprintf(stderr, "should not get an empty compileCommand set for file" + " '%s'\n", argv[i+1]); + errorCode = -1; + break; + } + + for (j=0; j<numCmds; ++j) { + CCmd = clang_CompileCommands_getCommand(CCmds, j); + + wd = clang_CompileCommand_getDirectory(CCmd); + printf("workdir:'%s'", clang_getCString(wd)); + clang_disposeString(wd); + + printf(" cmdline:'"); + numArgs = clang_CompileCommand_getNumArgs(CCmd); + for (a=0; a<numArgs; ++a) { + if (a) printf(" "); + arg = clang_CompileCommand_getArg(CCmd, a); + printf("%s", clang_getCString(arg)); + clang_disposeString(arg); + } + printf("'\n"); + } + + clang_CompileCommands_dispose(CCmds); + + i += 2; + } + } + clang_CompilationDatabase_dispose(db); + } else { + printf("database loading failed with error code %d.\n", ec); + errorCode = -1; + } + +cdb_end: + free(tmp); + + return errorCode; +} + +/******************************************************************************/ +/* USR printing. */ +/******************************************************************************/ + +static int insufficient_usr(const char *kind, const char *usage) { + fprintf(stderr, "USR for '%s' requires: %s\n", kind, usage); + return 1; +} + +static unsigned isUSR(const char *s) { + return s[0] == 'c' && s[1] == ':'; +} + +static int not_usr(const char *s, const char *arg) { + fprintf(stderr, "'%s' argument ('%s') is not a USR\n", s, arg); + return 1; +} + +static void print_usr(CXString usr) { + const char *s = clang_getCString(usr); + printf("%s\n", s); + clang_disposeString(usr); +} + +static void display_usrs() { + fprintf(stderr, "-print-usrs options:\n" + " ObjCCategory <class name> <category name>\n" + " ObjCClass <class name>\n" + " ObjCIvar <ivar name> <class USR>\n" + " ObjCMethod <selector> [0=class method|1=instance method] " + "<class USR>\n" + " ObjCProperty <property name> <class USR>\n" + " ObjCProtocol <protocol name>\n"); +} + +int print_usrs(const char **I, const char **E) { + while (I != E) { + const char *kind = *I; + unsigned len = strlen(kind); + switch (len) { + case 8: + if (memcmp(kind, "ObjCIvar", 8) == 0) { + if (I + 2 >= E) + return insufficient_usr(kind, "<ivar name> <class USR>"); + if (!isUSR(I[2])) + return not_usr("<class USR>", I[2]); + else { + CXString x = createCXString(I[2]); + print_usr(clang_constructUSR_ObjCIvar(I[1], x)); + } + + I += 3; + continue; + } + break; + case 9: + if (memcmp(kind, "ObjCClass", 9) == 0) { + if (I + 1 >= E) + return insufficient_usr(kind, "<class name>"); + print_usr(clang_constructUSR_ObjCClass(I[1])); + I += 2; + continue; + } + break; + case 10: + if (memcmp(kind, "ObjCMethod", 10) == 0) { + if (I + 3 >= E) + return insufficient_usr(kind, "<method selector> " + "[0=class method|1=instance method] <class USR>"); + if (!isUSR(I[3])) + return not_usr("<class USR>", I[3]); + else { + CXString x = createCXString(I[3]); + print_usr(clang_constructUSR_ObjCMethod(I[1], atoi(I[2]), x)); + } + I += 4; + continue; + } + break; + case 12: + if (memcmp(kind, "ObjCCategory", 12) == 0) { + if (I + 2 >= E) + return insufficient_usr(kind, "<class name> <category name>"); + print_usr(clang_constructUSR_ObjCCategory(I[1], I[2])); + I += 3; + continue; + } + if (memcmp(kind, "ObjCProtocol", 12) == 0) { + if (I + 1 >= E) + return insufficient_usr(kind, "<protocol name>"); + print_usr(clang_constructUSR_ObjCProtocol(I[1])); + I += 2; + continue; + } + if (memcmp(kind, "ObjCProperty", 12) == 0) { + if (I + 2 >= E) + return insufficient_usr(kind, "<property name> <class USR>"); + if (!isUSR(I[2])) + return not_usr("<class USR>", I[2]); + else { + CXString x = createCXString(I[2]); + print_usr(clang_constructUSR_ObjCProperty(I[1], x)); + } + I += 3; + continue; + } + break; + default: + break; + } + break; + } + + if (I != E) { + fprintf(stderr, "Invalid USR kind: %s\n", *I); + display_usrs(); + return 1; + } + return 0; +} + +int print_usrs_file(const char *file_name) { + char line[2048]; + const char *args[128]; + unsigned numChars = 0; + + FILE *fp = fopen(file_name, "r"); + if (!fp) { + fprintf(stderr, "error: cannot open '%s'\n", file_name); + return 1; + } + + /* This code is not really all that safe, but it works fine for testing. */ + while (!feof(fp)) { + char c = fgetc(fp); + if (c == '\n') { + unsigned i = 0; + const char *s = 0; + + if (numChars == 0) + continue; + + line[numChars] = '\0'; + numChars = 0; + + if (line[0] == '/' && line[1] == '/') + continue; + + s = strtok(line, " "); + while (s) { + args[i] = s; + ++i; + s = strtok(0, " "); + } + if (print_usrs(&args[0], &args[i])) + return 1; + } + else + line[numChars++] = c; + } + + fclose(fp); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ +int write_pch_file(const char *filename, int argc, const char *argv[]) { + CXIndex Idx; + CXTranslationUnit TU; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + enum CXErrorCode Err; + int result = 0; + + Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnostics=*/1); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + clang_disposeIndex(Idx); + return -1; + } + + Err = clang_parseTranslationUnit2( + Idx, 0, argv + num_unsaved_files, argc - num_unsaved_files, + unsaved_files, num_unsaved_files, + CXTranslationUnit_Incomplete | + CXTranslationUnit_DetailedPreprocessingRecord | + CXTranslationUnit_ForSerialization, + &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeTranslationUnit(TU); + clang_disposeIndex(Idx); + return 1; + } + + switch (clang_saveTranslationUnit(TU, filename, + clang_defaultSaveOptions(TU))) { + case CXSaveError_None: + break; + + case CXSaveError_TranslationErrors: + fprintf(stderr, "Unable to write PCH file %s: translation errors\n", + filename); + result = 2; + break; + + case CXSaveError_InvalidTU: + fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n", + filename); + result = 3; + break; + + case CXSaveError_Unknown: + default: + fprintf(stderr, "Unable to write PCH file %s: unknown error \n", filename); + result = 1; + break; + } + + clang_disposeTranslationUnit(TU); + free_remapped_files(unsaved_files, num_unsaved_files); + clang_disposeIndex(Idx); + return result; +} + +/******************************************************************************/ +/* Serialized diagnostics. */ +/******************************************************************************/ + +static const char *getDiagnosticCodeStr(enum CXLoadDiag_Error error) { + switch (error) { + case CXLoadDiag_CannotLoad: return "Cannot Load File"; + case CXLoadDiag_None: break; + case CXLoadDiag_Unknown: return "Unknown"; + case CXLoadDiag_InvalidFile: return "Invalid File"; + } + return "None"; +} + +static const char *getSeverityString(enum CXDiagnosticSeverity severity) { + switch (severity) { + case CXDiagnostic_Note: return "note"; + case CXDiagnostic_Error: return "error"; + case CXDiagnostic_Fatal: return "fatal"; + case CXDiagnostic_Ignored: return "ignored"; + case CXDiagnostic_Warning: return "warning"; + } + return "unknown"; +} + +static void printIndent(unsigned indent) { + if (indent == 0) + return; + fprintf(stderr, "+"); + --indent; + while (indent > 0) { + fprintf(stderr, "-"); + --indent; + } +} + +static void printLocation(CXSourceLocation L) { + CXFile File; + CXString FileName; + unsigned line, column, offset; + + clang_getExpansionLocation(L, &File, &line, &column, &offset); + FileName = clang_getFileName(File); + + fprintf(stderr, "%s:%d:%d", clang_getCString(FileName), line, column); + clang_disposeString(FileName); +} + +static void printRanges(CXDiagnostic D, unsigned indent) { + unsigned i, n = clang_getDiagnosticNumRanges(D); + + for (i = 0; i < n; ++i) { + CXSourceLocation Start, End; + CXSourceRange SR = clang_getDiagnosticRange(D, i); + Start = clang_getRangeStart(SR); + End = clang_getRangeEnd(SR); + + printIndent(indent); + fprintf(stderr, "Range: "); + printLocation(Start); + fprintf(stderr, " "); + printLocation(End); + fprintf(stderr, "\n"); + } +} + +static void printFixIts(CXDiagnostic D, unsigned indent) { + unsigned i, n = clang_getDiagnosticNumFixIts(D); + fprintf(stderr, "Number FIXITs = %d\n", n); + for (i = 0 ; i < n; ++i) { + CXSourceRange ReplacementRange; + CXString text; + text = clang_getDiagnosticFixIt(D, i, &ReplacementRange); + + printIndent(indent); + fprintf(stderr, "FIXIT: ("); + printLocation(clang_getRangeStart(ReplacementRange)); + fprintf(stderr, " - "); + printLocation(clang_getRangeEnd(ReplacementRange)); + fprintf(stderr, "): \"%s\"\n", clang_getCString(text)); + clang_disposeString(text); + } +} + +static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) { + unsigned i, n; + + if (!Diags) + return; + + n = clang_getNumDiagnosticsInSet(Diags); + for (i = 0; i < n; ++i) { + CXSourceLocation DiagLoc; + CXDiagnostic D; + CXFile File; + CXString FileName, DiagSpelling, DiagOption, DiagCat; + unsigned line, column, offset; + const char *FileNameStr = 0, *DiagOptionStr = 0, *DiagCatStr = 0; + + D = clang_getDiagnosticInSet(Diags, i); + DiagLoc = clang_getDiagnosticLocation(D); + clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset); + FileName = clang_getFileName(File); + FileNameStr = clang_getCString(FileName); + DiagSpelling = clang_getDiagnosticSpelling(D); + + printIndent(indent); + + fprintf(stderr, "%s:%d:%d: %s: %s", + FileNameStr ? FileNameStr : "(null)", + line, + column, + getSeverityString(clang_getDiagnosticSeverity(D)), + clang_getCString(DiagSpelling)); + + DiagOption = clang_getDiagnosticOption(D, 0); + DiagOptionStr = clang_getCString(DiagOption); + if (DiagOptionStr) { + fprintf(stderr, " [%s]", DiagOptionStr); + } + + DiagCat = clang_getDiagnosticCategoryText(D); + DiagCatStr = clang_getCString(DiagCat); + if (DiagCatStr) { + fprintf(stderr, " [%s]", DiagCatStr); + } + + fprintf(stderr, "\n"); + + printRanges(D, indent); + printFixIts(D, indent); + + /* Print subdiagnostics. */ + printDiagnosticSet(clang_getChildDiagnostics(D), indent+2); + + clang_disposeString(FileName); + clang_disposeString(DiagSpelling); + clang_disposeString(DiagOption); + clang_disposeString(DiagCat); + } +} + +static int read_diagnostics(const char *filename) { + enum CXLoadDiag_Error error; + CXString errorString; + CXDiagnosticSet Diags = 0; + + Diags = clang_loadDiagnostics(filename, &error, &errorString); + if (!Diags) { + fprintf(stderr, "Trouble deserializing file (%s): %s\n", + getDiagnosticCodeStr(error), + clang_getCString(errorString)); + clang_disposeString(errorString); + return 1; + } + + printDiagnosticSet(Diags, 0); + fprintf(stderr, "Number of diagnostics: %d\n", + clang_getNumDiagnosticsInSet(Diags)); + clang_disposeDiagnosticSet(Diags); + return 0; +} + +static int perform_print_build_session_timestamp(void) { + printf("%lld\n", clang_getBuildSessionTimestamp()); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ + +static CXCursorVisitor GetVisitor(const char *s) { + if (s[0] == '\0') + return FilteredPrintingVisitor; + if (strcmp(s, "-usrs") == 0) + return USRVisitor; + if (strncmp(s, "-memory-usage", 13) == 0) + return GetVisitor(s + 13); + return NULL; +} + +static void print_usage(void) { + fprintf(stderr, + "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, + " c-index-test -index-file [-check-prefix=<FileCheck prefix>] <compiler arguments>\n" + " c-index-test -index-file-full [-check-prefix=<FileCheck prefix>] <compiler arguments>\n" + " c-index-test -index-tu [-check-prefix=<FileCheck prefix>] <AST file>\n" + " c-index-test -index-compile-db [-check-prefix=<FileCheck prefix>] <compilation database>\n" + " c-index-test -test-file-scan <AST file> <source file> " + "[FileCheck prefix]\n"); + fprintf(stderr, + " c-index-test -test-load-tu <AST file> <symbol filter> " + "[FileCheck prefix]\n" + " c-index-test -test-load-tu-usrs <AST file> <symbol filter> " + "[FileCheck prefix]\n" + " c-index-test -test-load-source <symbol filter> {<args>}*\n"); + fprintf(stderr, + " c-index-test -test-load-source-memory-usage " + "<symbol filter> {<args>}*\n" + " c-index-test -test-load-source-reparse <trials> <symbol filter> " + " {<args>}*\n" + " c-index-test -test-load-source-usrs <symbol filter> {<args>}*\n" + " c-index-test -test-load-source-usrs-memory-usage " + "<symbol filter> {<args>}*\n" + " c-index-test -test-annotate-tokens=<range> {<args>}*\n" + " c-index-test -test-inclusion-stack-source {<args>}*\n" + " c-index-test -test-inclusion-stack-tu <AST file>\n"); + fprintf(stderr, + " c-index-test -test-print-linkage-source {<args>}*\n" + " c-index-test -test-print-visibility {<args>}*\n" + " c-index-test -test-print-type {<args>}*\n" + " c-index-test -test-print-type-size {<args>}*\n" + " c-index-test -test-print-bitwidth {<args>}*\n" + " c-index-test -test-print-target-info {<args>}*\n" + " c-index-test -test-print-type-declaration {<args>}*\n" + " c-index-test -print-usr [<CursorKind> {<args>}]*\n" + " c-index-test -print-usr-file <file>\n"); + fprintf(stderr, + " c-index-test -write-pch <file> <compiler arguments>\n" + " c-index-test -compilation-db [lookup <filename>] database\n"); + fprintf(stderr, + " c-index-test -print-build-session-timestamp\n"); + fprintf(stderr, + " c-index-test -read-diagnostics <file>\n\n"); + fprintf(stderr, + " <symbol filter> values:\n%s", + " all - load all symbols, including those from PCH\n" + " local - load all symbols except those in PCH\n" + " category - only load ObjC categories (non-PCH)\n" + " interface - only load ObjC interfaces (non-PCH)\n" + " protocol - only load ObjC protocols (non-PCH)\n" + " function - only load functions (non-PCH)\n" + " typedef - only load typdefs (non-PCH)\n" + " scan-function - scan function bodies (non-PCH)\n\n"); +} + +/***/ + +int cindextest_main(int argc, const char **argv) { + clang_enableStackTraces(); + if (argc > 2 && strcmp(argv[1], "-read-diagnostics") == 0) + return read_diagnostics(argv[2]); + if (argc > 2 && strstr(argv[1], "-code-completion-at=") == argv[1]) + return perform_code_completion(argc, argv, 0); + 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, "-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]) + return find_file_includes_in(argc, argv); + if (argc > 2 && strcmp(argv[1], "-index-file") == 0) + return index_file(argc - 2, argv + 2, /*full=*/0); + if (argc > 2 && strcmp(argv[1], "-index-file-full") == 0) + return index_file(argc - 2, argv + 2, /*full=*/1); + if (argc > 2 && strcmp(argv[1], "-index-tu") == 0) + return index_tu(argc - 2, argv + 2); + if (argc > 2 && strcmp(argv[1], "-index-compile-db") == 0) + return index_compile_db(argc - 2, argv + 2); + else if (argc >= 4 && strncmp(argv[1], "-test-load-tu", 13) == 0) { + CXCursorVisitor I = GetVisitor(argv[1] + 13); + if (I) + return perform_test_load_tu(argv[2], argv[3], argc >= 5 ? argv[4] : 0, I, + NULL); + } + else if (argc >= 5 && strncmp(argv[1], "-test-load-source-reparse", 25) == 0){ + CXCursorVisitor I = GetVisitor(argv[1] + 25); + if (I) { + int trials = atoi(argv[2]); + return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I, + NULL); + } + } + else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) { + CXCursorVisitor I = GetVisitor(argv[1] + 17); + + PostVisitTU postVisit = 0; + if (strstr(argv[1], "-memory-usage")) + postVisit = PrintMemoryUsage; + + if (I) + return perform_test_load_source(argc - 3, argv + 3, argv[2], I, + postVisit); + } + else if (argc >= 3 && strcmp(argv[1], "-single-file-parse") == 0) + return perform_single_file_parse(argv[2]); + else if (argc >= 3 && strcmp(argv[1], "-retain-excluded-conditional-blocks") == 0) + return perform_file_retain_excluded_cb(argv[2]); + else if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0) + return perform_file_scan(argv[2], argv[3], + argc >= 5 ? argv[4] : 0); + else if (argc > 2 && strstr(argv[1], "-test-annotate-tokens=") == argv[1]) + return perform_token_annotation(argc, argv); + else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-source") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", NULL, + PrintInclusionStack); + else if (argc > 2 && strcmp(argv[1], "-test-inclusion-stack-tu") == 0) + return perform_test_load_tu(argv[2], "all", NULL, NULL, + PrintInclusionStack); + else if (argc > 2 && strcmp(argv[1], "-test-print-linkage-source") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", PrintLinkage, + NULL); + else if (argc > 2 && strcmp(argv[1], "-test-print-visibility") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", PrintVisibility, + NULL); + else if (argc > 2 && strcmp(argv[1], "-test-print-type") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintType, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-type-size") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintTypeSize, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-type-declaration") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintTypeDeclaration, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-decl-attributes") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintDeclAttributes, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-bitwidth") == 0) + return perform_test_load_source(argc - 2, argv + 2, "all", + PrintBitWidth, 0); + else if (argc > 2 && strcmp(argv[1], "-test-print-mangle") == 0) + return perform_test_load_tu(argv[2], "all", NULL, PrintMangledName, NULL); + else if (argc > 2 && strcmp(argv[1], "-test-print-manglings") == 0) + return perform_test_load_tu(argv[2], "all", NULL, PrintManglings, NULL); + else if (argc > 2 && strcmp(argv[1], "-test-print-target-info") == 0) + return print_target_info(argc - 2, argv + 2); + else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) { + if (argc > 2) + return print_usrs(argv + 2, argv + argc); + else { + display_usrs(); + return 1; + } + } + else if (argc > 2 && strcmp(argv[1], "-print-usr-file") == 0) + return print_usrs_file(argv[2]); + else if (argc > 2 && strcmp(argv[1], "-write-pch") == 0) + return write_pch_file(argv[2], argc - 3, argv + 3); + else if (argc > 2 && strcmp(argv[1], "-compilation-db") == 0) + return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2); + else if (argc == 2 && strcmp(argv[1], "-print-build-session-timestamp") == 0) + return perform_print_build_session_timestamp(); + + print_usage(); + return 1; +} + +/***/ + +/* We intentionally run in a separate thread to ensure we at least minimal + * testing of a multithreaded environment (for example, having a reduced stack + * 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 = client_data->main_func(client_data->argc, + client_data->argv); +} + +static void flush_atexit(void) { + /* stdout, and surprisingly even stderr, are not always flushed on process + * and thread exit, particularly when the system is under heavy load. */ + fflush(stdout); + fflush(stderr); +} + +int main(int argc, const char **argv) { + thread_info client_data; + + atexit(flush_atexit); + +#ifdef CLANG_HAVE_LIBXML + LIBXML_TEST_VERSION +#endif + + if (argc > 1 && strcmp(argv[1], "core") == 0) + return indextest_core_main(argc, argv); + + client_data.main_func = cindextest_main; + client_data.argc = argc; + client_data.argv = argv; + + 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/gnu/llvm/clang/tools/c-index-test/core_main.cpp b/gnu/llvm/clang/tools/c-index-test/core_main.cpp new file mode 100644 index 00000000000..c4025080124 --- /dev/null +++ b/gnu/llvm/clang/tools/c-index-test/core_main.cpp @@ -0,0 +1,362 @@ +//===-- core_main.cpp - Core Index Tool testbed ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Mangle.h" +#include "clang/Basic/LangOptions.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#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/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.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")), + cl::cat(IndexTestCoreCategory)); + +static cl::extrahelp MoreHelp( + "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler " + "invocation\n" +); + +static cl::opt<bool> +DumpModuleImports("dump-imported-module-files", + cl::desc("Print symbols and input files from imported modules")); + +static cl::opt<bool> +IncludeLocals("include-locals", cl::desc("Print local symbols")); + +static cl::opt<std::string> +ModuleFilePath("module-file", + cl::desc("Path to module file to print symbols from")); +static cl::opt<std::string> + ModuleFormat("fmodule-format", cl::init("raw"), + cl::desc("Container format for clang modules and PCH, 'raw' or 'obj'")); + +} +} // anonymous namespace + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS); +static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx, + raw_ostream &OS); +static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS); + +namespace { + +class PrintIndexDataConsumer : public IndexDataConsumer { + raw_ostream &OS; + std::unique_ptr<ASTNameGenerator> ASTNameGen; + std::shared_ptr<Preprocessor> PP; + +public: + PrintIndexDataConsumer(raw_ostream &OS) : OS(OS) { + } + + void initialize(ASTContext &Ctx) override { + ASTNameGen.reset(new ASTNameGenerator(Ctx)); + } + + void setPreprocessor(std::shared_ptr<Preprocessor> PP) override { + this->PP = std::move(PP); + } + + bool handleDeclOccurrence(const Decl *D, SymbolRoleSet Roles, + ArrayRef<SymbolRelation> Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) override { + ASTContext &Ctx = D->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + Loc = SM.getFileLoc(Loc); + FileID FID = SM.getFileID(Loc); + unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc)); + unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc)); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(D), OS); + OS << " | "; + + printSymbolNameAndUSR(D, Ctx, OS); + OS << " | "; + + if (ASTNameGen->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 handleModuleOccurrence(const ImportDecl *ImportD, + const clang::Module *Mod, SymbolRoleSet Roles, + SourceLocation Loc) override { + ASTContext &Ctx = ImportD->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + Loc = SM.getFileLoc(Loc); + FileID FID = SM.getFileID(Loc); + unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc)); + unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc)); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfo(ImportD), OS); + OS << " | "; + + printSymbolNameAndUSR(Mod, OS); + OS << " | "; + + printSymbolRoles(Roles, OS); + OS << " |\n"; + + return true; + } + + bool handleMacroOccurrence(const IdentifierInfo *Name, const MacroInfo *MI, + SymbolRoleSet Roles, SourceLocation Loc) override { + assert(PP); + SourceManager &SM = PP->getSourceManager(); + + Loc = SM.getFileLoc(Loc); + FileID FID = SM.getFileID(Loc); + unsigned Line = SM.getLineNumber(FID, SM.getFileOffset(Loc)); + unsigned Col = SM.getColumnNumber(FID, SM.getFileOffset(Loc)); + OS << Line << ':' << Col << " | "; + + printSymbolInfo(getSymbolInfoForMacro(*MI), OS); + OS << " | "; + + OS << Name->getName(); + OS << " | "; + + SmallString<256> USRBuf; + if (generateUSRForMacro(Name->getName(), MI->getDefinitionLoc(), SM, + USRBuf)) { + OS << "<no-usr>"; + } else { + OS << USRBuf; + } + OS << " | "; + + printSymbolRoles(Roles, OS); + OS << " |\n"; + return true; + } +}; + +} // anonymous namespace + +//===----------------------------------------------------------------------===// +// Print Source Symbols +//===----------------------------------------------------------------------===// + +static void dumpModuleFileInputs(serialization::ModuleFile &Mod, + ASTReader &Reader, + raw_ostream &OS) { + OS << "---- Module Inputs ----\n"; + Reader.visitInputFiles(Mod, /*IncludeSystem=*/true, /*Complain=*/false, + [&](const serialization::InputFile &IF, bool isSystem) { + OS << (isSystem ? "system" : "user") << " | "; + OS << IF.getFile()->getName() << '\n'; + }); +} + +static bool printSourceSymbols(const char *Executable, + ArrayRef<const char *> Args, + bool dumpModuleImports, bool indexLocals) { + SmallVector<const char *, 4> ArgsWithProgName; + ArgsWithProgName.push_back(Executable); + ArgsWithProgName.append(Args.begin(), Args.end()); + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags); + if (!CInvok) + return true; + + raw_ostream &OS = outs(); + auto DataConsumer = std::make_shared<PrintIndexDataConsumer>(OS); + IndexingOptions IndexOpts; + IndexOpts.IndexFunctionLocals = indexLocals; + std::unique_ptr<FrontendAction> IndexAction = + createIndexingAction(DataConsumer, IndexOpts); + + auto PCHContainerOps = std::make_shared<PCHContainerOperations>(); + std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction( + std::move(CInvok), PCHContainerOps, Diags, IndexAction.get())); + + if (!Unit) + return true; + + if (dumpModuleImports) { + if (auto Reader = Unit->getASTReader()) { + Reader->getModuleManager().visit([&](serialization::ModuleFile &Mod) -> bool { + OS << "==== Module " << Mod.ModuleName << " ====\n"; + indexModuleFile(Mod, *Reader, *DataConsumer, IndexOpts); + dumpModuleFileInputs(Mod, *Reader, OS); + return true; // skip module dependencies. + }); + } + } + + return false; +} + +static bool printSourceSymbolsFromModule(StringRef modulePath, + StringRef format) { + FileSystemOptions FileSystemOpts; + auto pchContOps = std::make_shared<PCHContainerOperations>(); + // Register the support for object-file-wrapped Clang modules. + pchContOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>()); + auto pchRdr = pchContOps->getReaderOrNull(format); + if (!pchRdr) { + errs() << "unknown module format: " << format << '\n'; + return true; + } + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( + modulePath, *pchRdr, ASTUnit::LoadASTOnly, Diags, + FileSystemOpts, /*UseDebugInfo=*/false, + /*OnlyLocalDecls=*/true, None, + CaptureDiagsKind::None, + /*AllowPCHWithCompilerErrors=*/true, + /*UserFilesAreVolatile=*/false); + if (!AU) { + errs() << "failed to create TU for: " << modulePath << '\n'; + return true; + } + + PrintIndexDataConsumer DataConsumer(outs()); + IndexingOptions IndexOpts; + indexASTUnit(*AU, DataConsumer, IndexOpts); + + return false; +} + +//===----------------------------------------------------------------------===// +// Helper Utils +//===----------------------------------------------------------------------===// + +static void printSymbolInfo(SymbolInfo SymInfo, raw_ostream &OS) { + OS << getSymbolKindString(SymInfo.Kind); + if (SymInfo.SubKind != SymbolSubKind::None) + OS << '/' << getSymbolSubKindString(SymInfo.SubKind); + if (SymInfo.Properties) { + OS << '('; + printSymbolProperties(SymInfo.Properties, 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; + } +} + +static void printSymbolNameAndUSR(const clang::Module *Mod, raw_ostream &OS) { + assert(Mod); + OS << Mod->getFullModuleName() << " | "; + generateFullUSRForModule(Mod, OS); +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int indextest_core_main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + PrettyStackTraceProgram X(argc, argv); + void *MainAddr = (void*) (intptr_t) indextest_core_main; + std::string Executable = llvm::sys::fs::getMainExecutable(argv[0], MainAddr); + + 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 (!options::ModuleFilePath.empty()) { + return printSourceSymbolsFromModule(options::ModuleFilePath, + options::ModuleFormat); + } + if (CompArgs.empty()) { + errs() << "error: missing compiler args; pass '-- <compiler arguments>'\n"; + return 1; + } + return printSourceSymbols(Executable.c_str(), CompArgs, + options::DumpModuleImports, + options::IncludeLocals); + } + + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-check/CMakeLists.txt b/gnu/llvm/clang/tools/clang-check/CMakeLists.txt new file mode 100644 index 00000000000..6d2fc196631 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-check/CMakeLists.txt @@ -0,0 +1,21 @@ +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Option + Support + ) + +add_clang_tool(clang-check + ClangCheck.cpp + ) + +clang_target_link_libraries(clang-check + PRIVATE + clangAST + clangBasic + clangDriver + clangFrontend + clangRewriteFrontend + clangSerialization + clangStaticAnalyzerFrontend + clangTooling + ) diff --git a/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp b/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp new file mode 100644 index 00000000000..2f59e2b82d3 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-check/ClangCheck.cpp @@ -0,0 +1,188 @@ +//===--- tools/clang-check/ClangCheck.cpp - Clang check tool --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a clang-check tool that runs clang based on the info +// stored in a compilation database. +// +// This tool uses the Clang Tooling infrastructure, see +// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +// for details on setting it up with LLVM source tree. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" + +using namespace clang::driver; +using namespace clang::tooling; +using namespace llvm; + +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); +static cl::extrahelp MoreHelp( + "\tFor example, to run clang-check on all files in a subtree of the\n" + "\tsource tree, use:\n" + "\n" + "\t find path/in/subtree -name '*.cpp'|xargs clang-check\n" + "\n" + "\tor using a specific build path:\n" + "\n" + "\t find path/in/subtree -name '*.cpp'|xargs clang-check -p build/path\n" + "\n" + "\tNote, that path/in/subtree and current directory should follow the\n" + "\trules described above.\n" + "\n" +); + +static cl::OptionCategory ClangCheckCategory("clang-check options"); +static const opt::OptTable &Options = getDriverOptTable(); +static cl::opt<bool> + ASTDump("ast-dump", + cl::desc(Options.getOptionHelpText(options::OPT_ast_dump)), + cl::cat(ClangCheckCategory)); +static cl::opt<bool> + ASTList("ast-list", + cl::desc(Options.getOptionHelpText(options::OPT_ast_list)), + cl::cat(ClangCheckCategory)); +static cl::opt<bool> + ASTPrint("ast-print", + cl::desc(Options.getOptionHelpText(options::OPT_ast_print)), + cl::cat(ClangCheckCategory)); +static cl::opt<std::string> ASTDumpFilter( + "ast-dump-filter", + cl::desc(Options.getOptionHelpText(options::OPT_ast_dump_filter)), + cl::cat(ClangCheckCategory)); +static cl::opt<bool> + Analyze("analyze", + cl::desc(Options.getOptionHelpText(options::OPT_analyze)), + cl::cat(ClangCheckCategory)); + +static cl::opt<bool> + Fixit("fixit", cl::desc(Options.getOptionHelpText(options::OPT_fixit)), + cl::cat(ClangCheckCategory)); +static cl::opt<bool> FixWhatYouCan( + "fix-what-you-can", + cl::desc(Options.getOptionHelpText(options::OPT_fix_what_you_can)), + cl::cat(ClangCheckCategory)); + +namespace { + +// FIXME: Move FixItRewriteInPlace from lib/Rewrite/Frontend/FrontendActions.cpp +// into a header file and reuse that. +class FixItOptions : public clang::FixItOptions { +public: + FixItOptions() { + FixWhatYouCan = ::FixWhatYouCan; + } + + std::string RewriteFilename(const std::string& filename, int &fd) override { + // We don't need to do permission checking here since clang will diagnose + // any I/O errors itself. + + fd = -1; // No file descriptor for file. + + return filename; + } +}; + +/// Subclasses \c clang::FixItRewriter to not count fixed errors/warnings +/// in the final error counts. +/// +/// This has the side-effect that clang-check -fixit exits with code 0 on +/// successfully fixing all errors. +class FixItRewriter : public clang::FixItRewriter { +public: + FixItRewriter(clang::DiagnosticsEngine& Diags, + clang::SourceManager& SourceMgr, + const clang::LangOptions& LangOpts, + clang::FixItOptions* FixItOpts) + : clang::FixItRewriter(Diags, SourceMgr, LangOpts, FixItOpts) { + } + + bool IncludeInDiagnosticCounts() const override { return false; } +}; + +/// Subclasses \c clang::FixItAction so that we can install the custom +/// \c FixItRewriter. +class ClangCheckFixItAction : public clang::FixItAction { +public: + bool BeginSourceFileAction(clang::CompilerInstance& CI) override { + FixItOpts.reset(new FixItOptions); + Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get())); + return true; + } +}; + +class ClangCheckActionFactory { +public: + std::unique_ptr<clang::ASTConsumer> newASTConsumer() { + if (ASTList) + return clang::CreateASTDeclNodeLister(); + if (ASTDump) + return clang::CreateASTDumper(nullptr /*Dump to stdout.*/, ASTDumpFilter, + /*DumpDecls=*/true, + /*Deserialize=*/false, + /*DumpLookups=*/false, + clang::ADOF_Default); + if (ASTPrint) + return clang::CreateASTPrinter(nullptr, ASTDumpFilter); + return std::make_unique<clang::ASTConsumer>(); + } +}; + +} // namespace + +int main(int argc, const char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + + // Initialize targets for clang module support. + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); + + CommonOptionsParser OptionsParser(argc, argv, ClangCheckCategory); + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + + // Clear adjusters because -fsyntax-only is inserted by the default chain. + Tool.clearArgumentsAdjusters(); + Tool.appendArgumentsAdjuster(getClangStripOutputAdjuster()); + Tool.appendArgumentsAdjuster(getClangStripDependencyFileAdjuster()); + + // Running the analyzer requires --analyze. Other modes can work with the + // -fsyntax-only option. + Tool.appendArgumentsAdjuster(getInsertArgumentAdjuster( + Analyze ? "--analyze" : "-fsyntax-only", ArgumentInsertPosition::BEGIN)); + + ClangCheckActionFactory CheckFactory; + std::unique_ptr<FrontendActionFactory> FrontendFactory; + + // Choose the correct factory based on the selected mode. + if (Analyze) + FrontendFactory = newFrontendActionFactory<clang::ento::AnalysisAction>(); + else if (Fixit) + FrontendFactory = newFrontendActionFactory<ClangCheckFixItAction>(); + else + FrontendFactory = newFrontendActionFactory(&CheckFactory); + + return Tool.run(FrontendFactory.get()); +} diff --git a/gnu/llvm/clang/tools/clang-diff/CMakeLists.txt b/gnu/llvm/clang/tools/clang-diff/CMakeLists.txt new file mode 100644 index 00000000000..41f61fe968f --- /dev/null +++ b/gnu/llvm/clang/tools/clang-diff/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_executable(clang-diff + ClangDiff.cpp + ) + +clang_target_link_libraries(clang-diff + PRIVATE + clangBasic + clangFrontend + clangSerialization + clangTooling + clangToolingASTDiff + ) diff --git a/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp b/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp new file mode 100644 index 00000000000..502409eadf3 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-diff/ClangDiff.cpp @@ -0,0 +1,536 @@ +//===- ClangDiff.cpp - compare source files by AST nodes ------*- C++ -*- -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a tool for syntax tree based comparison using +// Tooling/ASTDiff. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/ASTDiff/ASTDiff.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace clang; +using namespace clang::tooling; + +static cl::OptionCategory ClangDiffCategory("clang-diff options"); + +static cl::opt<bool> + ASTDump("ast-dump", + cl::desc("Print the internal representation of the AST."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt<bool> ASTDumpJson( + "ast-dump-json", + cl::desc("Print the internal representation of the AST as JSON."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt<bool> PrintMatches("dump-matches", + cl::desc("Print the matched nodes."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt<bool> HtmlDiff("html", + cl::desc("Output a side-by-side diff in HTML."), + cl::init(false), cl::cat(ClangDiffCategory)); + +static cl::opt<std::string> SourcePath(cl::Positional, cl::desc("<source>"), + cl::Required, + cl::cat(ClangDiffCategory)); + +static cl::opt<std::string> DestinationPath(cl::Positional, + cl::desc("<destination>"), + cl::Optional, + cl::cat(ClangDiffCategory)); + +static cl::opt<std::string> StopAfter("stop-diff-after", + cl::desc("<topdown|bottomup>"), + cl::Optional, cl::init(""), + cl::cat(ClangDiffCategory)); + +static cl::opt<int> MaxSize("s", cl::desc("<maxsize>"), cl::Optional, + cl::init(-1), cl::cat(ClangDiffCategory)); + +static cl::opt<std::string> BuildPath("p", cl::desc("Build path"), cl::init(""), + cl::Optional, cl::cat(ClangDiffCategory)); + +static cl::list<std::string> ArgsAfter( + "extra-arg", + cl::desc("Additional argument to append to the compiler command line"), + cl::cat(ClangDiffCategory)); + +static cl::list<std::string> ArgsBefore( + "extra-arg-before", + cl::desc("Additional argument to prepend to the compiler command line"), + cl::cat(ClangDiffCategory)); + +static void addExtraArgs(std::unique_ptr<CompilationDatabase> &Compilations) { + if (!Compilations) + return; + auto AdjustingCompilations = + std::make_unique<ArgumentsAdjustingCompilations>( + std::move(Compilations)); + AdjustingCompilations->appendArgumentsAdjuster( + getInsertArgumentAdjuster(ArgsBefore, ArgumentInsertPosition::BEGIN)); + AdjustingCompilations->appendArgumentsAdjuster( + getInsertArgumentAdjuster(ArgsAfter, ArgumentInsertPosition::END)); + Compilations = std::move(AdjustingCompilations); +} + +static std::unique_ptr<ASTUnit> +getAST(const std::unique_ptr<CompilationDatabase> &CommonCompilations, + const StringRef Filename) { + std::string ErrorMessage; + std::unique_ptr<CompilationDatabase> Compilations; + if (!CommonCompilations) { + Compilations = CompilationDatabase::autoDetectFromSource( + BuildPath.empty() ? Filename : BuildPath, ErrorMessage); + if (!Compilations) { + llvm::errs() + << "Error while trying to load a compilation database, running " + "without flags.\n" + << ErrorMessage; + Compilations = + std::make_unique<clang::tooling::FixedCompilationDatabase>( + ".", std::vector<std::string>()); + } + } + addExtraArgs(Compilations); + std::array<std::string, 1> Files = {{Filename}}; + ClangTool Tool(Compilations ? *Compilations : *CommonCompilations, Files); + std::vector<std::unique_ptr<ASTUnit>> ASTs; + Tool.buildASTs(ASTs); + if (ASTs.size() != Files.size()) + return nullptr; + return std::move(ASTs[0]); +} + +static char hexdigit(int N) { return N &= 0xf, N + (N < 10 ? '0' : 'a' - 10); } + +static const char HtmlDiffHeader[] = R"( +<html> +<head> +<meta charset='utf-8'/> +<style> +span.d { color: red; } +span.u { color: #cc00cc; } +span.i { color: green; } +span.m { font-weight: bold; } +span { font-weight: normal; color: black; } +div.code { + width: 48%; + height: 98%; + overflow: scroll; + float: left; + padding: 0 0 0.5% 0.5%; + border: solid 2px LightGrey; + border-radius: 5px; +} +</style> +</head> +<script type='text/javascript'> +highlightStack = [] +function clearHighlight() { + while (highlightStack.length) { + var [l, r] = highlightStack.pop() + document.getElementById(l).style.backgroundColor = 'inherit' + if (r[1] != '-') + document.getElementById(r).style.backgroundColor = 'inherit' + } +} +function highlight(event) { + var id = event.target['id'] + doHighlight(id) +} +function doHighlight(id) { + clearHighlight() + source = document.getElementById(id) + if (!source.attributes['tid']) + return + var mapped = source + while (mapped && mapped.parentElement && mapped.attributes['tid'].value.substr(1) === '-1') + mapped = mapped.parentElement + var tid = null, target = null + if (mapped) { + tid = mapped.attributes['tid'].value + target = document.getElementById(tid) + } + if (source.parentElement && source.parentElement.classList.contains('code')) + return + source.style.backgroundColor = 'lightgrey' + source.scrollIntoView() + if (target) { + if (mapped === source) + target.style.backgroundColor = 'lightgrey' + target.scrollIntoView() + } + highlightStack.push([id, tid]) + location.hash = '#' + id +} +function scrollToBoth() { + doHighlight(location.hash.substr(1)) +} +function changed(elem) { + return elem.classList.length == 0 +} +function nextChangedNode(prefix, increment, number) { + do { + number += increment + var elem = document.getElementById(prefix + number) + } while(elem && !changed(elem)) + return elem ? number : null +} +function handleKey(e) { + var down = e.code === "KeyJ" + var up = e.code === "KeyK" + if (!down && !up) + return + var id = highlightStack[0] ? highlightStack[0][0] : 'R0' + var oldelem = document.getElementById(id) + var number = parseInt(id.substr(1)) + var increment = down ? 1 : -1 + var lastnumber = number + var prefix = id[0] + do { + number = nextChangedNode(prefix, increment, number) + var elem = document.getElementById(prefix + number) + if (up && elem) { + while (elem.parentElement && changed(elem.parentElement)) + elem = elem.parentElement + number = elem.id.substr(1) + } + } while ((down && id !== 'R0' && oldelem.contains(elem))) + if (!number) + number = lastnumber + elem = document.getElementById(prefix + number) + doHighlight(prefix + number) +} +window.onload = scrollToBoth +window.onkeydown = handleKey +</script> +<body> +<div onclick='highlight(event)'> +)"; + +static void printHtml(raw_ostream &OS, char C) { + switch (C) { + case '&': + OS << "&"; + break; + case '<': + OS << "<"; + break; + case '>': + OS << ">"; + break; + case '\'': + OS << "'"; + break; + case '"': + OS << """; + break; + default: + OS << C; + } +} + +static void printHtml(raw_ostream &OS, const StringRef Str) { + for (char C : Str) + printHtml(OS, C); +} + +static std::string getChangeKindAbbr(diff::ChangeKind Kind) { + switch (Kind) { + case diff::None: + return ""; + case diff::Delete: + return "d"; + case diff::Update: + return "u"; + case diff::Insert: + return "i"; + case diff::Move: + return "m"; + case diff::UpdateMove: + return "u m"; + } + llvm_unreachable("Invalid enumeration value."); +} + +static unsigned printHtmlForNode(raw_ostream &OS, const diff::ASTDiff &Diff, + diff::SyntaxTree &Tree, bool IsLeft, + diff::NodeId Id, unsigned Offset) { + const diff::Node &Node = Tree.getNode(Id); + char MyTag, OtherTag; + diff::NodeId LeftId, RightId; + diff::NodeId TargetId = Diff.getMapped(Tree, Id); + if (IsLeft) { + MyTag = 'L'; + OtherTag = 'R'; + LeftId = Id; + RightId = TargetId; + } else { + MyTag = 'R'; + OtherTag = 'L'; + LeftId = TargetId; + RightId = Id; + } + unsigned Begin, End; + std::tie(Begin, End) = Tree.getSourceRangeOffsets(Node); + const SourceManager &SrcMgr = Tree.getASTContext().getSourceManager(); + auto Code = SrcMgr.getBuffer(SrcMgr.getMainFileID())->getBuffer(); + for (; Offset < Begin; ++Offset) + printHtml(OS, Code[Offset]); + OS << "<span id='" << MyTag << Id << "' " + << "tid='" << OtherTag << TargetId << "' "; + OS << "title='"; + printHtml(OS, Node.getTypeLabel()); + OS << "\n" << LeftId << " -> " << RightId; + std::string Value = Tree.getNodeValue(Node); + if (!Value.empty()) { + OS << "\n"; + printHtml(OS, Value); + } + OS << "'"; + if (Node.Change != diff::None) + OS << " class='" << getChangeKindAbbr(Node.Change) << "'"; + OS << ">"; + + for (diff::NodeId Child : Node.Children) + Offset = printHtmlForNode(OS, Diff, Tree, IsLeft, Child, Offset); + + for (; Offset < End; ++Offset) + printHtml(OS, Code[Offset]); + if (Id == Tree.getRootId()) { + End = Code.size(); + for (; Offset < End; ++Offset) + printHtml(OS, Code[Offset]); + } + OS << "</span>"; + return Offset; +} + +static void printJsonString(raw_ostream &OS, const StringRef Str) { + for (signed char C : Str) { + switch (C) { + case '"': + OS << R"(\")"; + break; + case '\\': + OS << R"(\\)"; + break; + case '\n': + OS << R"(\n)"; + break; + case '\t': + OS << R"(\t)"; + break; + default: + if ('\x00' <= C && C <= '\x1f') { + OS << R"(\u00)" << hexdigit(C >> 4) << hexdigit(C); + } else { + OS << C; + } + } + } +} + +static void printNodeAttributes(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + const diff::Node &N = Tree.getNode(Id); + OS << R"("id":)" << int(Id); + OS << R"(,"type":")" << N.getTypeLabel() << '"'; + auto Offsets = Tree.getSourceRangeOffsets(N); + OS << R"(,"begin":)" << Offsets.first; + OS << R"(,"end":)" << Offsets.second; + std::string Value = Tree.getNodeValue(N); + if (!Value.empty()) { + OS << R"(,"value":")"; + printJsonString(OS, Value); + OS << '"'; + } +} + +static void printNodeAsJson(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + const diff::Node &N = Tree.getNode(Id); + OS << "{"; + printNodeAttributes(OS, Tree, Id); + auto Identifier = N.getIdentifier(); + auto QualifiedIdentifier = N.getQualifiedIdentifier(); + if (Identifier) { + OS << R"(,"identifier":")"; + printJsonString(OS, *Identifier); + OS << R"(")"; + if (QualifiedIdentifier && *Identifier != *QualifiedIdentifier) { + OS << R"(,"qualified_identifier":")"; + printJsonString(OS, *QualifiedIdentifier); + OS << R"(")"; + } + } + OS << R"(,"children":[)"; + if (N.Children.size() > 0) { + printNodeAsJson(OS, Tree, N.Children[0]); + for (size_t I = 1, E = N.Children.size(); I < E; ++I) { + OS << ","; + printNodeAsJson(OS, Tree, N.Children[I]); + } + } + OS << "]}"; +} + +static void printNode(raw_ostream &OS, diff::SyntaxTree &Tree, + diff::NodeId Id) { + if (Id.isInvalid()) { + OS << "None"; + return; + } + OS << Tree.getNode(Id).getTypeLabel(); + std::string Value = Tree.getNodeValue(Id); + if (!Value.empty()) + OS << ": " << Value; + OS << "(" << Id << ")"; +} + +static void printTree(raw_ostream &OS, diff::SyntaxTree &Tree) { + for (diff::NodeId Id : Tree) { + for (int I = 0; I < Tree.getNode(Id).Depth; ++I) + OS << " "; + printNode(OS, Tree, Id); + OS << "\n"; + } +} + +static void printDstChange(raw_ostream &OS, diff::ASTDiff &Diff, + diff::SyntaxTree &SrcTree, diff::SyntaxTree &DstTree, + diff::NodeId Dst) { + const diff::Node &DstNode = DstTree.getNode(Dst); + diff::NodeId Src = Diff.getMapped(DstTree, Dst); + switch (DstNode.Change) { + case diff::None: + break; + case diff::Delete: + llvm_unreachable("The destination tree can't have deletions."); + case diff::Update: + OS << "Update "; + printNode(OS, SrcTree, Src); + OS << " to " << DstTree.getNodeValue(Dst) << "\n"; + break; + case diff::Insert: + case diff::Move: + case diff::UpdateMove: + if (DstNode.Change == diff::Insert) + OS << "Insert"; + else if (DstNode.Change == diff::Move) + OS << "Move"; + else if (DstNode.Change == diff::UpdateMove) + OS << "Update and Move"; + OS << " "; + printNode(OS, DstTree, Dst); + OS << " into "; + printNode(OS, DstTree, DstNode.Parent); + OS << " at " << DstTree.findPositionInParent(Dst) << "\n"; + break; + } +} + +int main(int argc, const char **argv) { + std::string ErrorMessage; + std::unique_ptr<CompilationDatabase> CommonCompilations = + FixedCompilationDatabase::loadFromCommandLine(argc, argv, ErrorMessage); + if (!CommonCompilations && !ErrorMessage.empty()) + llvm::errs() << ErrorMessage; + cl::HideUnrelatedOptions(ClangDiffCategory); + if (!cl::ParseCommandLineOptions(argc, argv)) { + cl::PrintOptionValues(); + return 1; + } + + addExtraArgs(CommonCompilations); + + if (ASTDump || ASTDumpJson) { + if (!DestinationPath.empty()) { + llvm::errs() << "Error: Please specify exactly one filename.\n"; + return 1; + } + std::unique_ptr<ASTUnit> AST = getAST(CommonCompilations, SourcePath); + if (!AST) + return 1; + diff::SyntaxTree Tree(AST->getASTContext()); + if (ASTDump) { + printTree(llvm::outs(), Tree); + return 0; + } + llvm::outs() << R"({"filename":")"; + printJsonString(llvm::outs(), SourcePath); + llvm::outs() << R"(","root":)"; + printNodeAsJson(llvm::outs(), Tree, Tree.getRootId()); + llvm::outs() << "}\n"; + return 0; + } + + if (DestinationPath.empty()) { + llvm::errs() << "Error: Exactly two paths are required.\n"; + return 1; + } + + std::unique_ptr<ASTUnit> Src = getAST(CommonCompilations, SourcePath); + std::unique_ptr<ASTUnit> Dst = getAST(CommonCompilations, DestinationPath); + if (!Src || !Dst) + return 1; + + diff::ComparisonOptions Options; + if (MaxSize != -1) + Options.MaxSize = MaxSize; + if (!StopAfter.empty()) { + if (StopAfter == "topdown") + Options.StopAfterTopDown = true; + else if (StopAfter != "bottomup") { + llvm::errs() << "Error: Invalid argument for -stop-after\n"; + return 1; + } + } + diff::SyntaxTree SrcTree(Src->getASTContext()); + diff::SyntaxTree DstTree(Dst->getASTContext()); + diff::ASTDiff Diff(SrcTree, DstTree, Options); + + if (HtmlDiff) { + llvm::outs() << HtmlDiffHeader << "<pre>"; + llvm::outs() << "<div id='L' class='code'>"; + printHtmlForNode(llvm::outs(), Diff, SrcTree, true, SrcTree.getRootId(), 0); + llvm::outs() << "</div>"; + llvm::outs() << "<div id='R' class='code'>"; + printHtmlForNode(llvm::outs(), Diff, DstTree, false, DstTree.getRootId(), + 0); + llvm::outs() << "</div>"; + llvm::outs() << "</pre></div></body></html>\n"; + return 0; + } + + for (diff::NodeId Dst : DstTree) { + diff::NodeId Src = Diff.getMapped(DstTree, Dst); + if (PrintMatches && Src.isValid()) { + llvm::outs() << "Match "; + printNode(llvm::outs(), SrcTree, Src); + llvm::outs() << " to "; + printNode(llvm::outs(), DstTree, Dst); + llvm::outs() << "\n"; + } + printDstChange(llvm::outs(), Diff, SrcTree, DstTree, Dst); + } + for (diff::NodeId Src : SrcTree) { + if (Diff.getMapped(SrcTree, Src).isInvalid()) { + llvm::outs() << "Delete "; + printNode(llvm::outs(), SrcTree, Src); + llvm::outs() << "\n"; + } + } + + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-extdef-mapping/CMakeLists.txt b/gnu/llvm/clang/tools/clang-extdef-mapping/CMakeLists.txt new file mode 100644 index 00000000000..973b68db69d --- /dev/null +++ b/gnu/llvm/clang/tools/clang-extdef-mapping/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + support + ) + +add_clang_tool(clang-extdef-mapping + ClangExtDefMapGen.cpp + ) + +clang_target_link_libraries(clang-extdef-mapping + PRIVATE + clangAST + clangBasic + clangCrossTU + clangFrontend + clangSerialization + clangTooling + ) diff --git a/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp b/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp new file mode 100644 index 00000000000..0047aa4724f --- /dev/null +++ b/gnu/llvm/clang/tools/clang-extdef-mapping/ClangExtDefMapGen.cpp @@ -0,0 +1,129 @@ +//===- ClangExtDefMapGen.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--------------------------------------------------------------------===// +// +// Clang tool which creates a list of defined functions and the files in which +// they are defined. +// +//===--------------------------------------------------------------------===// + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/SourceManager.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" +#include <sstream> +#include <string> + +using namespace llvm; +using namespace clang; +using namespace clang::cross_tu; +using namespace clang::tooling; + +static cl::OptionCategory ClangExtDefMapGenCategory("clang-extdefmapgen options"); + +class MapExtDefNamesConsumer : public ASTConsumer { +public: + MapExtDefNamesConsumer(ASTContext &Context) + : Ctx(Context), SM(Context.getSourceManager()) {} + + ~MapExtDefNamesConsumer() { + // Flush results to standard output. + llvm::outs() << createCrossTUIndexString(Index); + } + + void HandleTranslationUnit(ASTContext &Context) override { + handleDecl(Context.getTranslationUnitDecl()); + } + +private: + void handleDecl(const Decl *D); + void addIfInMain(const DeclaratorDecl *DD, SourceLocation defStart); + + ASTContext &Ctx; + SourceManager &SM; + llvm::StringMap<std::string> Index; + std::string CurrentFileName; +}; + +void MapExtDefNamesConsumer::handleDecl(const Decl *D) { + if (!D) + return; + + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isThisDeclarationADefinition()) + if (const Stmt *Body = FD->getBody()) + addIfInMain(FD, Body->getBeginLoc()); + } else if (const auto *VD = dyn_cast<VarDecl>(D)) { + if (cross_tu::containsConst(VD, Ctx) && VD->hasInit()) + if (const Expr *Init = VD->getInit()) + addIfInMain(VD, Init->getBeginLoc()); + } + + if (const auto *DC = dyn_cast<DeclContext>(D)) + for (const Decl *D : DC->decls()) + handleDecl(D); +} + +void MapExtDefNamesConsumer::addIfInMain(const DeclaratorDecl *DD, + SourceLocation defStart) { + llvm::Optional<std::string> LookupName = + CrossTranslationUnitContext::getLookupName(DD); + if (!LookupName) + return; + assert(!LookupName->empty() && "Lookup name should be non-empty."); + + if (CurrentFileName.empty()) { + CurrentFileName = + SM.getFileEntryForID(SM.getMainFileID())->tryGetRealPathName(); + if (CurrentFileName.empty()) + CurrentFileName = "invalid_file"; + } + + switch (DD->getLinkageInternal()) { + case ExternalLinkage: + case VisibleNoLinkage: + case UniqueExternalLinkage: + if (SM.isInMainFile(defStart)) + Index[*LookupName] = CurrentFileName; + break; + default: + break; + } +} + +class MapExtDefNamesAction : public ASTFrontendAction { +protected: + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef) { + return std::make_unique<MapExtDefNamesConsumer>(CI.getASTContext()); + } +}; + +static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); + +int main(int argc, const char **argv) { + // Print a stack trace if we signal out. + sys::PrintStackTraceOnErrorSignal(argv[0], false); + PrettyStackTraceProgram X(argc, argv); + + const char *Overview = "\nThis tool collects the USR name and location " + "of external definitions in the source files " + "(excluding headers).\n"; + CommonOptionsParser OptionsParser(argc, argv, ClangExtDefMapGenCategory, + cl::ZeroOrMore, Overview); + + ClangTool Tool(OptionsParser.getCompilations(), + OptionsParser.getSourcePathList()); + + return Tool.run(newFrontendActionFactory<MapExtDefNamesAction>().get()); +} diff --git a/gnu/llvm/clang/tools/clang-format-vs/.gitignore b/gnu/llvm/clang/tools/clang-format-vs/.gitignore new file mode 100644 index 00000000000..270d840cb6d --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/.gitignore @@ -0,0 +1,11 @@ +# Visual Studio files +.vs/ +*.user +/packages/ +/ClangFormat/obj/ +/ClangFormat/bin/ + +# Generated and copied files +/ClangFormat/Key.snk +/ClangFormat/clang-format.exe +/ClangFormat/source.extension.vsixmanifest diff --git a/gnu/llvm/clang/tools/clang-format-vs/CMakeLists.txt b/gnu/llvm/clang/tools/clang-format-vs/CMakeLists.txt new file mode 100644 index 00000000000..1d44a47a313 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/CMakeLists.txt @@ -0,0 +1,33 @@ +option(BUILD_CLANG_FORMAT_VS_PLUGIN "Build clang-format VS plugin" OFF) +if (BUILD_CLANG_FORMAT_VS_PLUGIN) + add_custom_target(clang_format_exe_for_vsix + ${CMAKE_COMMAND} -E copy_if_different + "${LLVM_TOOLS_BINARY_DIR}/clang-format.exe" + "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/clang-format.exe" + DEPENDS clang-format) + + # Build number added to Clang version to ensure that new VSIX can be upgraded + string(TIMESTAMP CLANG_FORMAT_VSIX_BUILD %y%m%d%H%M UTC) + + if (NOT CLANG_FORMAT_VS_VERSION) + set(CLANG_FORMAT_VS_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}.${CLANG_FORMAT_VSIX_BUILD}") + endif() + + configure_file("source.extension.vsixmanifest.in" + "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/source.extension.vsixmanifest") + + find_program(NUGET_EXE nuget PATHS ${NUGET_EXE_DIR}) + if (NOT NUGET_EXE) + message(FATAL_ERROR "Could not find nuget.exe. Download from https://www.nuget.org/nuget.exe" + " and add parent directory to PATH or pass it via NUGET_EXE_DIR var.") + endif() + + add_custom_target(clang_format_vsix ALL + COMMAND ${NUGET_EXE} restore "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat.sln" + COMMAND devenv "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat.sln" /Build Release + DEPENDS clang_format_exe_for_vsix "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/source.extension.vsixmanifest" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_SOURCE_DIR}/ClangFormat/bin/Release/ClangFormat.vsix" + "${LLVM_TOOLS_BINARY_DIR}/ClangFormat.vsix" + DEPENDS clang_format_exe_for_vsix) +endif() diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat.sln b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat.sln new file mode 100644 index 00000000000..46d742bce3f --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat.sln @@ -0,0 +1,22 @@ +
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26228.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClangFormat", "ClangFormat\ClangFormat.csproj", "{7FD1783E-2D31-4D05-BF23-6EBE1B42B608}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7FD1783E-2D31-4D05-BF23-6EBE1B42B608}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormat.csproj b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormat.csproj new file mode 100644 index 00000000000..e5b7ec008a1 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormat.csproj @@ -0,0 +1,261 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{7FD1783E-2D31-4D05-BF23-6EBE1B42B608}</ProjectGuid>
+ <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>LLVM.ClangFormat</RootNamespace>
+ <AssemblyName>ClangFormat</AssemblyName>
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>Key.snk</AssemblyOriginatorKeyFile>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
+ <FileUpgradeFlags>
+ </FileUpgradeFlags>
+ <UpgradeBackupLocation>
+ </UpgradeBackupLocation>
+ <OldToolsVersion>4.0</OldToolsVersion>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <RunCodeAnalysis>true</RunCodeAnalysis>
+ <Prefer32Bit>false</Prefer32Bit>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <EmbedInteropTypes>True</EmbedInteropTypes>
+ </Reference>
+ <Reference Include="envdte80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <EmbedInteropTypes>True</EmbedInteropTypes>
+ </Reference>
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="Microsoft.VisualStudio.CoreUtility, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.CoreUtility.10.0.4\lib\net40\Microsoft.VisualStudio.CoreUtility.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Editor, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Editor.10.0.4\lib\net40\Microsoft.VisualStudio.Editor.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.OLE.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.OLE.Interop.dll</HintPath>
+ <Private>True</Private>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.10.10.0.3\lib\net40\Microsoft.VisualStudio.Shell.10.0.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Shell.Immutable.10.10.0.3\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll</HintPath>
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.7.0.4\lib\net20\Microsoft.VisualStudio.Shell.Interop.dll</HintPath>
+ <Private>True</Private>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.8.8.0.3\lib\net20\Microsoft.VisualStudio.Shell.Interop.8.0.dll</HintPath>
+ <Private>True</Private>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.10.0" />
+ <Reference Include="Microsoft.VisualStudio.Shell.Interop.9.0, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.Shell.Interop.9.9.0.3\lib\net20\Microsoft.VisualStudio.Shell.Interop.9.0.dll</HintPath>
+ <Private>True</Private>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Data, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Data.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.Logic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Logic.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.Wpf.dll</HintPath>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="Microsoft.VisualStudio.TextManager.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.TextManager.Interop.8.8.0.4\lib\net20\Microsoft.VisualStudio.TextManager.Interop.8.0.dll</HintPath>
+ <Private>True</Private>
+ <Private>False</Private>
+ </Reference>
+ <Reference Include="PresentationCore" />
+ <Reference Include="PresentationFramework" />
+ <Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <HintPath>..\packages\VSSDK.DTE.7.0.3\lib\net20\stdole.dll</HintPath>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.ComponentModel.Composition" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Design" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Windows.Forms" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="WindowsBase" />
+ </ItemGroup>
+ <ItemGroup>
+ <COMReference Include="Microsoft.VisualStudio.CommandBars">
+ <Guid>{1CBA492E-7263-47BB-87FE-639000619B15}</Guid>
+ <VersionMajor>8</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ <COMReference Include="stdole">
+ <Guid>{00020430-0000-0000-C000-000000000046}</Guid>
+ <VersionMajor>2</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>primary</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>False</EmbedInteropTypes>
+ </COMReference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Guids.cs" />
+ <Compile Include="Resources.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Resources.resx</DependentUpon>
+ </Compile>
+ <Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="ClangFormatPackage.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="PkgCmdID.cs" />
+ <Compile Include="RunningDocTableEventsDispatcher.cs" />
+ <Compile Include="Vsix.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Resources.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ <EmbeddedResource Include="VSPackage.resx">
+ <MergeWithCTO>true</MergeWithCTO>
+ <ManifestResourceName>VSPackage</ManifestResourceName>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Key.snk" />
+ <None Include="packages.config">
+ <SubType>Designer</SubType>
+ </None>
+ <None Include="source.extension.vsixmanifest">
+ <SubType>Designer</SubType>
+ </None>
+ </ItemGroup>
+ <ItemGroup>
+ <VSCTCompile Include="ClangFormat.vsct">
+ <ResourceName>Menus.ctmenu</ResourceName>
+ </VSCTCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Resources\Images_32bit.bmp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="clang-format.exe">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ </Content>
+ <Content Include="license.txt">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ </Content>
+ <Content Include="Resources\Package.ico" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.0">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.4.5">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 4.5</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <PropertyGroup>
+ <UseCodebase>true</UseCodebase>
+ </PropertyGroup>
+ <PropertyGroup>
+ <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
+ <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\VSSDK\Microsoft.VsSDK.targets" Condition="false" />
+ <PropertyGroup>
+ <PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(FrameworkSDKDir)Bin\NETFX 4.6 Tools\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>
+ </PropertyGroup>
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct new file mode 100644 index 00000000000..798957740d5 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormat.vsct @@ -0,0 +1,127 @@ +<?xml version="1.0" encoding="utf-8"?>
+<CommandTable xmlns="http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+ <!-- This is the file that defines the actual layout and type of the commands.
+ It is divided in different sections (e.g. command definition, command
+ placement, ...), with each defining a specific set of properties.
+ See the comment before each section for more details about how to
+ use it. -->
+
+ <!-- The VSCT compiler (the tool that translates this file into the binary
+ format that VisualStudio will consume) has the ability to run a preprocessor
+ on the vsct file; this preprocessor is (usually) the C++ preprocessor, so
+ it is possible to define includes and macros with the same syntax used
+ in C++ files. Using this ability of the compiler here, we include some files
+ defining some of the constants that we will use inside the file. -->
+
+ <!--This is the file that defines the IDs for all the commands exposed by VisualStudio. -->
+ <Extern href="stdidcmd.h"/>
+
+ <!--This header contains the command ids for the menus provided by the shell. -->
+ <Extern href="vsshlids.h"/>
+
+
+
+
+ <!--The Commands section is where we the commands, menus and menu groups are defined.
+ This section uses a Guid to identify the package that provides the command defined inside it. -->
+ <Commands package="guidClangFormatPkg">
+ <!-- Inside this section we have different sub-sections: one for the menus, another
+ for the menu groups, one for the buttons (the actual commands), one for the combos
+ and the last one for the bitmaps used. Each element is identified by a command id that
+ is a unique pair of guid and numeric identifier; the guid part of the identifier is usually
+ called "command set" and is used to group different command inside a logically related
+ group; your package should define its own command set in order to avoid collisions
+ with command ids defined by other packages. -->
+
+
+ <!-- In this section you can define new menu groups. A menu group is a container for
+ other menus or buttons (commands); from a visual point of view you can see the
+ group as the part of a menu contained between two lines. The parent of a group
+ must be a menu. -->
+ <Groups>
+
+ <Group guid="guidClangFormatCmdSet" id="MyMenuGroup" priority="0x0600">
+ <Parent guid="guidSHLMainMenu" id="IDM_VS_MENU_TOOLS"/>
+ </Group>
+
+
+
+ </Groups>
+
+ <!--Buttons section. -->
+ <!--This section defines the elements the user can interact with, like a menu command or a button
+ or combo box in a toolbar. -->
+ <Buttons>
+ <!--To define a menu group you have to specify its ID, the parent menu and its display priority.
+ The command is visible and enabled by default. If you need to change the visibility, status, etc, you can use
+ the CommandFlag node.
+ You can add more than one CommandFlag node e.g.:
+ <CommandFlag>DefaultInvisible</CommandFlag>
+ <CommandFlag>DynamicVisibility</CommandFlag>
+ If you do not want an image next to your command, remove the Icon node /> -->
+
+ <Button guid="guidClangFormatCmdSet" id="cmdidClangFormatSelection" priority="0x0100" type="Button">
+ <Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
+ <Icon guid="guidImages" id="bmpPic1" />
+ <Strings>
+ <ButtonText>Clang Format Selection</ButtonText>
+ </Strings>
+ </Button>
+
+ <Button guid="guidClangFormatCmdSet" id="cmdidClangFormatDocument" priority="0x0101" type="Button">
+ <Parent guid="guidClangFormatCmdSet" id="MyMenuGroup" />
+ <Icon guid="guidImages" id="bmpPic2" />
+ <Strings>
+ <ButtonText>Clang Format Document</ButtonText>
+ </Strings>
+ </Button>
+
+ </Buttons>
+
+ <!--The bitmaps section is used to define the bitmaps that are used for the commands.-->
+ <Bitmaps>
+ <!-- The bitmap id is defined in a way that is a little bit different from the others:
+ the declaration starts with a guid for the bitmap strip, then there is the resource id of the
+ bitmap strip containing the bitmaps and then there are the numeric ids of the elements used
+ inside a button definition. An important aspect of this declaration is that the element id
+ must be the actual index (1-based) of the bitmap inside the bitmap strip. -->
+ <Bitmap guid="guidImages" href="Resources\Images_32bit.bmp" usedList="bmpPic1, bmpPic2, bmpPicSearch, bmpPicX, bmpPicArrows"/>
+
+ </Bitmaps>
+
+ </Commands>
+
+
+ <KeyBindings>
+ <KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormatSelection" editor="guidTextEditor" key1="R" mod1="Control" key2="F" mod2="Control"/>
+ <KeyBinding guid="guidClangFormatCmdSet" id="cmdidClangFormatDocument" editor="guidTextEditor" key1="R" mod1="Control" key2="D" mod2="Control"/>
+ </KeyBindings>
+
+
+
+ <Symbols>
+ <!-- This is the package guid. -->
+ <GuidSymbol name="guidClangFormatPkg" value="{c5286038-25d3-4f65-83a8-51fa2df4a146}" />
+
+ <!-- This is the guid used to group the menu commands together -->
+ <GuidSymbol name="guidClangFormatCmdSet" value="{e39cbab1-0f96-4022-a2bc-da5a9db7eb78}">
+
+ <IDSymbol name="MyMenuGroup" value="0x1020" />
+ <IDSymbol name="cmdidClangFormatSelection" value="0x0100" />
+ <IDSymbol name="cmdidClangFormatDocument" value="0x0101" />
+ </GuidSymbol>
+
+ <GuidSymbol name="guidTextEditor" value="{8B382828-6202-11d1-8870-0000F87579D2}" />
+
+
+ <GuidSymbol name="guidImages" value="{6d53937b-9ae1-42e1-8849-d876dcdbad7b}" >
+ <IDSymbol name="bmpPic1" value="1" />
+ <IDSymbol name="bmpPic2" value="2" />
+ <IDSymbol name="bmpPicSearch" value="3" />
+ <IDSymbol name="bmpPicX" value="4" />
+ <IDSymbol name="bmpPicArrows" value="5" />
+ </GuidSymbol>
+ </Symbols>
+
+</CommandTable>
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs new file mode 100644 index 00000000000..26a0af3b55b --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/ClangFormatPackage.cs @@ -0,0 +1,464 @@ +//===-- ClangFormatPackages.cs - VSPackage for clang-format ------*- C# -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This class contains a VS extension package that runs clang-format over a +// selection in a VS text editor. +// +//===----------------------------------------------------------------------===// + +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using System; +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.IO; +using System.Runtime.InteropServices; +using System.Xml.Linq; +using System.Linq; +using System.Text; + +namespace LLVM.ClangFormat +{ + [ClassInterface(ClassInterfaceType.AutoDual)] + [CLSCompliant(false), ComVisible(true)] + public class OptionPageGrid : DialogPage + { + private string assumeFilename = ""; + private string fallbackStyle = "LLVM"; + private bool sortIncludes = false; + private string style = "file"; + private bool formatOnSave = false; + private string formatOnSaveFileExtensions = + ".c;.cpp;.cxx;.cc;.tli;.tlh;.h;.hh;.hpp;.hxx;.hh;.inl;" + + ".java;.js;.ts;.m;.mm;.proto;.protodevel;.td"; + + public OptionPageGrid Clone() + { + // Use MemberwiseClone to copy value types. + var clone = (OptionPageGrid)MemberwiseClone(); + return clone; + } + + public class StyleConverter : TypeConverter + { + protected ArrayList values; + public StyleConverter() + { + // Initializes the standard values list with defaults. + values = new ArrayList(new string[] { "file", "Chromium", "Google", "LLVM", "Mozilla", "WebKit" }); + } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) + { + return true; + } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + { + return new StandardValuesCollection(values); + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + return true; + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + string s = value as string; + if (s == null) + return base.ConvertFrom(context, culture, value); + + return value; + } + } + + [Category("Format Options")] + [DisplayName("Style")] + [Description("Coding style, currently supports:\n" + + " - Predefined styles ('LLVM', 'Google', 'Chromium', 'Mozilla', 'WebKit').\n" + + " - 'file' to search for a YAML .clang-format or _clang-format\n" + + " configuration file.\n" + + " - A YAML configuration snippet.\n\n" + + "'File':\n" + + " Searches for a .clang-format or _clang-format configuration file\n" + + " in the source file's directory and its parents.\n\n" + + "YAML configuration snippet:\n" + + " The content of a .clang-format configuration file, as string.\n" + + " Example: '{BasedOnStyle: \"LLVM\", IndentWidth: 8}'\n\n" + + "See also: http://clang.llvm.org/docs/ClangFormatStyleOptions.html.")] + [TypeConverter(typeof(StyleConverter))] + public string Style + { + get { return style; } + set { style = value; } + } + + public sealed class FilenameConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + return true; + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + string s = value as string; + if (s == null) + return base.ConvertFrom(context, culture, value); + + // Check if string contains quotes. On Windows, file names cannot contain quotes. + // We do not accept them however to avoid hard-to-debug problems. + // A quote in user input would end the parameter quote and so break the command invocation. + if (s.IndexOf('\"') != -1) + throw new NotSupportedException("Filename cannot contain quotes"); + + return value; + } + } + + [Category("Format Options")] + [DisplayName("Assume Filename")] + [Description("When reading from stdin, clang-format assumes this " + + "filename to look for a style config file (with 'file' style) " + + "and to determine the language.")] + [TypeConverter(typeof(FilenameConverter))] + public string AssumeFilename + { + get { return assumeFilename; } + set { assumeFilename = value; } + } + + public sealed class FallbackStyleConverter : StyleConverter + { + public FallbackStyleConverter() + { + // Add "none" to the list of styles. + values.Insert(0, "none"); + } + } + + [Category("Format Options")] + [DisplayName("Fallback Style")] + [Description("The name of the predefined style used as a fallback in case clang-format " + + "is invoked with 'file' style, but can not find the configuration file.\n" + + "Use 'none' fallback style to skip formatting.")] + [TypeConverter(typeof(FallbackStyleConverter))] + public string FallbackStyle + { + get { return fallbackStyle; } + set { fallbackStyle = value; } + } + + [Category("Format Options")] + [DisplayName("Sort includes")] + [Description("Sort touched include lines.\n\n" + + "See also: http://clang.llvm.org/docs/ClangFormat.html.")] + public bool SortIncludes + { + get { return sortIncludes; } + set { sortIncludes = value; } + } + + [Category("Format On Save")] + [DisplayName("Enable")] + [Description("Enable running clang-format when modified files are saved. " + + "Will only format if Style is found (ignores Fallback Style)." + )] + public bool FormatOnSave + { + get { return formatOnSave; } + set { formatOnSave = value; } + } + + [Category("Format On Save")] + [DisplayName("File extensions")] + [Description("When formatting on save, clang-format will be applied only to " + + "files with these extensions.")] + public string FormatOnSaveFileExtensions + { + get { return formatOnSaveFileExtensions; } + set { formatOnSaveFileExtensions = value; } + } + } + + [PackageRegistration(UseManagedResourcesOnly = true)] + [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] + [ProvideMenuResource("Menus.ctmenu", 1)] + [ProvideAutoLoad(UIContextGuids80.SolutionExists)] // Load package on solution load + [Guid(GuidList.guidClangFormatPkgString)] + [ProvideOptionPage(typeof(OptionPageGrid), "LLVM/Clang", "ClangFormat", 0, 0, true)] + public sealed class ClangFormatPackage : Package + { + #region Package Members + + RunningDocTableEventsDispatcher _runningDocTableEventsDispatcher; + + protected override void Initialize() + { + base.Initialize(); + + _runningDocTableEventsDispatcher = new RunningDocTableEventsDispatcher(this); + _runningDocTableEventsDispatcher.BeforeSave += OnBeforeSave; + + var commandService = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + if (commandService != null) + { + { + var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormatSelection); + var menuItem = new MenuCommand(MenuItemCallback, menuCommandID); + commandService.AddCommand(menuItem); + } + + { + var menuCommandID = new CommandID(GuidList.guidClangFormatCmdSet, (int)PkgCmdIDList.cmdidClangFormatDocument); + var menuItem = new MenuCommand(MenuItemCallback, menuCommandID); + commandService.AddCommand(menuItem); + } + } + } + #endregion + + OptionPageGrid GetUserOptions() + { + return (OptionPageGrid)GetDialogPage(typeof(OptionPageGrid)); + } + + private void MenuItemCallback(object sender, EventArgs args) + { + var mc = sender as System.ComponentModel.Design.MenuCommand; + if (mc == null) + return; + + switch (mc.CommandID.ID) + { + case (int)PkgCmdIDList.cmdidClangFormatSelection: + FormatSelection(GetUserOptions()); + break; + + case (int)PkgCmdIDList.cmdidClangFormatDocument: + FormatDocument(GetUserOptions()); + break; + } + } + + private static bool FileHasExtension(string filePath, string fileExtensions) + { + var extensions = fileExtensions.ToLower().Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + return extensions.Contains(Path.GetExtension(filePath).ToLower()); + } + + private void OnBeforeSave(object sender, Document document) + { + var options = GetUserOptions(); + + if (!options.FormatOnSave) + return; + + if (!FileHasExtension(document.FullName, options.FormatOnSaveFileExtensions)) + return; + + if (!Vsix.IsDocumentDirty(document)) + return; + + var optionsWithNoFallbackStyle = GetUserOptions().Clone(); + optionsWithNoFallbackStyle.FallbackStyle = "none"; + FormatDocument(document, optionsWithNoFallbackStyle); + } + + /// <summary> + /// Runs clang-format on the current selection + /// </summary> + private void FormatSelection(OptionPageGrid options) + { + IWpfTextView view = Vsix.GetCurrentView(); + if (view == null) + // We're not in a text view. + return; + string text = view.TextBuffer.CurrentSnapshot.GetText(); + int start = view.Selection.Start.Position.GetContainingLine().Start.Position; + int end = view.Selection.End.Position.GetContainingLine().End.Position; + + // clang-format doesn't support formatting a range that starts at the end + // of the file. + if (start >= text.Length && text.Length > 0) + start = text.Length - 1; + string path = Vsix.GetDocumentParent(view); + string filePath = Vsix.GetDocumentPath(view); + + RunClangFormatAndApplyReplacements(text, start, end, path, filePath, options, view); + } + + /// <summary> + /// Runs clang-format on the current document + /// </summary> + private void FormatDocument(OptionPageGrid options) + { + FormatView(Vsix.GetCurrentView(), options); + } + + private void FormatDocument(Document document, OptionPageGrid options) + { + FormatView(Vsix.GetDocumentView(document), options); + } + + private void FormatView(IWpfTextView view, OptionPageGrid options) + { + if (view == null) + // We're not in a text view. + return; + + string filePath = Vsix.GetDocumentPath(view); + var path = Path.GetDirectoryName(filePath); + + string text = view.TextBuffer.CurrentSnapshot.GetText(); + if (!text.EndsWith(Environment.NewLine)) + { + view.TextBuffer.Insert(view.TextBuffer.CurrentSnapshot.Length, Environment.NewLine); + text += Environment.NewLine; + } + + RunClangFormatAndApplyReplacements(text, 0, text.Length, path, filePath, options, view); + } + + private void RunClangFormatAndApplyReplacements(string text, int start, int end, string path, string filePath, OptionPageGrid options, IWpfTextView view) + { + try + { + string replacements = RunClangFormat(text, start, end, path, filePath, options); + ApplyClangFormatReplacements(replacements, view); + } + catch (Exception e) + { + var uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); + var id = Guid.Empty; + int result; + uiShell.ShowMessageBox( + 0, ref id, + "Error while running clang-format:", + e.Message, + string.Empty, 0, + OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, + OLEMSGICON.OLEMSGICON_INFO, + 0, out result); + } + } + + /// <summary> + /// Runs the given text through clang-format and returns the replacements as XML. + /// + /// Formats the text in range start and end. + /// </summary> + private static string RunClangFormat(string text, int start, int end, string path, string filePath, OptionPageGrid options) + { + string vsixPath = Path.GetDirectoryName( + typeof(ClangFormatPackage).Assembly.Location); + + System.Diagnostics.Process process = new System.Diagnostics.Process(); + process.StartInfo.UseShellExecute = false; + process.StartInfo.FileName = vsixPath + "\\clang-format.exe"; + char[] chars = text.ToCharArray(); + int offset = Encoding.UTF8.GetByteCount(chars, 0, start); + int length = Encoding.UTF8.GetByteCount(chars, 0, end) - offset; + // Poor man's escaping - this will not work when quotes are already escaped + // in the input (but we don't need more). + string style = options.Style.Replace("\"", "\\\""); + string fallbackStyle = options.FallbackStyle.Replace("\"", "\\\""); + process.StartInfo.Arguments = " -offset " + offset + + " -length " + length + + " -output-replacements-xml " + + " -style \"" + style + "\"" + + " -fallback-style \"" + fallbackStyle + "\""; + if (options.SortIncludes) + process.StartInfo.Arguments += " -sort-includes "; + string assumeFilename = options.AssumeFilename; + if (string.IsNullOrEmpty(assumeFilename)) + assumeFilename = filePath; + if (!string.IsNullOrEmpty(assumeFilename)) + process.StartInfo.Arguments += " -assume-filename \"" + assumeFilename + "\""; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + if (path != null) + process.StartInfo.WorkingDirectory = path; + // We have to be careful when communicating via standard input / output, + // as writes to the buffers will block until they are read from the other side. + // Thus, we: + // 1. Start the process - clang-format.exe will start to read the input from the + // standard input. + try + { + process.Start(); + } + catch (Exception e) + { + throw new Exception( + "Cannot execute " + process.StartInfo.FileName + ".\n\"" + + e.Message + "\".\nPlease make sure it is on the PATH."); + } + // 2. We write everything to the standard output - this cannot block, as clang-format + // reads the full standard input before analyzing it without writing anything to the + // standard output. + StreamWriter utf8Writer = new StreamWriter(process.StandardInput.BaseStream, new UTF8Encoding(false)); + utf8Writer.Write(text); + // 3. We notify clang-format that the input is done - after this point clang-format + // will start analyzing the input and eventually write the output. + utf8Writer.Close(); + // 4. We must read clang-format's output before waiting for it to exit; clang-format + // will close the channel by exiting. + string output = process.StandardOutput.ReadToEnd(); + // 5. clang-format is done, wait until it is fully shut down. + process.WaitForExit(); + if (process.ExitCode != 0) + { + // FIXME: If clang-format writes enough to the standard error stream to block, + // we will never reach this point; instead, read the standard error asynchronously. + throw new Exception(process.StandardError.ReadToEnd()); + } + return output; + } + + /// <summary> + /// Applies the clang-format replacements (xml) to the current view + /// </summary> + private static void ApplyClangFormatReplacements(string replacements, IWpfTextView view) + { + // clang-format returns no replacements if input text is empty + if (replacements.Length == 0) + return; + + string text = view.TextBuffer.CurrentSnapshot.GetText(); + byte[] bytes = Encoding.UTF8.GetBytes(text); + + var root = XElement.Parse(replacements); + var edit = view.TextBuffer.CreateEdit(); + foreach (XElement replacement in root.Descendants("replacement")) + { + int offset = int.Parse(replacement.Attribute("offset").Value); + int length = int.Parse(replacement.Attribute("length").Value); + var span = new Span( + Encoding.UTF8.GetCharCount(bytes, 0, offset), + Encoding.UTF8.GetCharCount(bytes, offset, length)); + edit.Replace(span, replacement.Value); + } + edit.Apply(); + } + } +} diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs new file mode 100644 index 00000000000..175a74e291d --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project. Project-level
+// suppressions either have no target or are given a specific target
+// and scoped to a namespace, type, member, etc.
+//
+// To add a suppression to this file, right-click the message in the
+// Error List, point to "Suppress Message(s)", and click "In Project
+// Suppression File". You do not need to add suppressions to this
+// file manually.
+
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")]
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Guids.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Guids.cs new file mode 100644 index 00000000000..ed1c12d61e4 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Guids.cs @@ -0,0 +1,12 @@ +using System;
+
+namespace LLVM.ClangFormat
+{
+ static class GuidList
+ {
+ public const string guidClangFormatPkgString = "c5286038-25d3-4f65-83a8-51fa2df4a146";
+ public const string guidClangFormatCmdSetString = "e39cbab1-0f96-4022-a2bc-da5a9db7eb78";
+
+ public static readonly Guid guidClangFormatCmdSet = new Guid(guidClangFormatCmdSetString);
+ };
+}
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs new file mode 100644 index 00000000000..c274d1ca1b4 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/PkgCmdID.cs @@ -0,0 +1,8 @@ +namespace LLVM.ClangFormat
+{
+ static class PkgCmdIDList
+ {
+ public const uint cmdidClangFormatSelection = 0x100;
+ public const uint cmdidClangFormatDocument = 0x101;
+ };
+}
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..b1cef49414b --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ClangFormat")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("LLVM")]
+[assembly: AssemblyProduct("ClangFormat")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+[assembly: CLSCompliant(false)]
+[assembly: NeutralResourcesLanguage("en-US")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.1.0.0")]
+[assembly: AssemblyFileVersion("1.1.0.0")]
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources.Designer.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources.Designer.cs new file mode 100644 index 00000000000..e3129b3db83 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace LLVM.ClangFormat {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LLVM.ClangFormat.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources.resx b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources.resx new file mode 100644 index 00000000000..352987aa07b --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources.resx @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+ VS SDK Notes: This resx file contains the resources that will be consumed directly by your package.
+ For example, if you chose to create a tool window, there is a resource with ID 'CanNotCreateWindow'. This
+ is used in VsPkg.cs to determine the string to show the user if there is an error when attempting to create
+ the tool window.
+
+ Resources that are accessed directly from your package *by Visual Studio* are stored in the VSPackage.resx
+ file.
+-->
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+</root>
\ No newline at end of file diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources/Images_32bit.bmp b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources/Images_32bit.bmp Binary files differnew file mode 100644 index 00000000000..2fa7ab00998 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources/Images_32bit.bmp diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources/Package.ico b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources/Package.ico Binary files differnew file mode 100644 index 00000000000..ea3b23fe8d4 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Resources/Package.ico diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs new file mode 100644 index 00000000000..163f68dbda3 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/RunningDocTableEventsDispatcher.cs @@ -0,0 +1,79 @@ +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System.Linq; + +namespace LLVM.ClangFormat +{ + // Exposes event sources for IVsRunningDocTableEvents3 events. + internal sealed class RunningDocTableEventsDispatcher : IVsRunningDocTableEvents3 + { + private RunningDocumentTable _runningDocumentTable; + private DTE _dte; + + public delegate void OnBeforeSaveHander(object sender, Document document); + public event OnBeforeSaveHander BeforeSave; + + public RunningDocTableEventsDispatcher(Package package) + { + _runningDocumentTable = new RunningDocumentTable(package); + _runningDocumentTable.Advise(this); + _dte = (DTE)Package.GetGlobalService(typeof(DTE)); + } + + public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) + { + return VSConstants.S_OK; + } + + public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew) + { + return VSConstants.S_OK; + } + + public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) + { + return VSConstants.S_OK; + } + + public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) + { + return VSConstants.S_OK; + } + + public int OnAfterSave(uint docCookie) + { + return VSConstants.S_OK; + } + + public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) + { + return VSConstants.S_OK; + } + + public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) + { + return VSConstants.S_OK; + } + + public int OnBeforeSave(uint docCookie) + { + if (BeforeSave != null) + { + var document = FindDocumentByCookie(docCookie); + if (document != null) // Not sure why this happens sometimes + { + BeforeSave(this, FindDocumentByCookie(docCookie)); + } + } + return VSConstants.S_OK; + } + + private Document FindDocumentByCookie(uint docCookie) + { + var documentInfo = _runningDocumentTable.GetDocumentInfo(docCookie); + return _dte.Documents.Cast<Document>().FirstOrDefault(doc => doc.FullName == documentInfo.Moniker); + } + } +} diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/VSPackage.resx b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/VSPackage.resx new file mode 100644 index 00000000000..81102d38a07 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/VSPackage.resx @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!--
+ VS SDK Notes: This resx file contains the resources that will be consumed from your package by Visual Studio.
+ For example, Visual Studio will attempt to load resource '400' from this resource stream when it needs to
+ load your package's icon. Because Visual Studio will always look in the VSPackage.resources stream first for
+ resources it needs, you should put additional resources that Visual Studio will load directly into this resx
+ file.
+
+ Resources that you would like to access directly from your package in a strong-typed fashion should be stored
+ in Resources.resx or another resx file.
+-->
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+ <data name="110" xml:space="preserve">
+ <value>ClangFormat</value>
+ </data>
+ <data name="112" xml:space="preserve">
+ <value>Formats code by calling the clang-format executable.</value>
+ </data>
+ <data name="400" type="System.Resources.ResXFileRef, System.Windows.Forms">
+ <value>Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+ </data>
+</root>
\ No newline at end of file diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Vsix.cs b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Vsix.cs new file mode 100644 index 00000000000..0d86cb59828 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/Vsix.cs @@ -0,0 +1,96 @@ +using EnvDTE; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using System; +using System.IO; + +namespace LLVM.ClangFormat +{ + internal sealed class Vsix + { + /// <summary> + /// Returns the currently active view if it is a IWpfTextView. + /// </summary> + public static IWpfTextView GetCurrentView() + { + // The SVsTextManager is a service through which we can get the active view. + var textManager = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager)); + IVsTextView textView; + textManager.GetActiveView(1, null, out textView); + + // Now we have the active view as IVsTextView, but the text interfaces we need + // are in the IWpfTextView. + return VsToWpfTextView(textView); + } + + public static bool IsDocumentDirty(Document document) + { + var textView = GetDocumentView(document); + var textDocument = GetTextDocument(textView); + return textDocument?.IsDirty == true; + } + + public static IWpfTextView GetDocumentView(Document document) + { + var textView = GetVsTextViewFrompPath(document.FullName); + return VsToWpfTextView(textView); + } + + public static IWpfTextView VsToWpfTextView(IVsTextView textView) + { + var userData = (IVsUserData)textView; + if (userData == null) + return null; + Guid guidWpfViewHost = DefGuidList.guidIWpfTextViewHost; + object host; + userData.GetData(ref guidWpfViewHost, out host); + return ((IWpfTextViewHost)host).TextView; + } + + public static IVsTextView GetVsTextViewFrompPath(string filePath) + { + // From http://stackoverflow.com/a/2427368/4039972 + var dte2 = (EnvDTE80.DTE2)Package.GetGlobalService(typeof(SDTE)); + var sp = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)dte2; + var serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(sp); + + IVsUIHierarchy uiHierarchy; + uint itemID; + IVsWindowFrame windowFrame; + if (VsShellUtilities.IsDocumentOpen(serviceProvider, filePath, Guid.Empty, + out uiHierarchy, out itemID, out windowFrame)) + { + // Get the IVsTextView from the windowFrame. + return VsShellUtilities.GetTextView(windowFrame); + } + return null; + } + + public static ITextDocument GetTextDocument(IWpfTextView view) + { + ITextDocument document; + if (view != null && view.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out document)) + return document; + return null; + } + + public static string GetDocumentParent(IWpfTextView view) + { + ITextDocument document = GetTextDocument(view); + if (document != null) + { + return Directory.GetParent(document.FilePath).ToString(); + } + return null; + } + + public static string GetDocumentPath(IWpfTextView view) + { + return GetTextDocument(view)?.FilePath; + } + } +} diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/license.txt b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/license.txt new file mode 100644 index 00000000000..63c17f148e7 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/license.txt @@ -0,0 +1,261 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2018 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. diff --git a/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/packages.config b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/packages.config new file mode 100644 index 00000000000..07dc281178f --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/ClangFormat/packages.config @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="VSSDK.CoreUtility" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.CoreUtility.10" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.Editor" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.Editor.10" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.IDE" version="7.0.4" targetFramework="net40" /> + <package id="VSSDK.IDE.10" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.IDE.8" version="8.0.4" targetFramework="net40" /> + <package id="VSSDK.IDE.9" version="9.0.3" targetFramework="net40" /> + <package id="VSSDK.OLE.Interop" version="7.0.4" targetFramework="net40" /> + <package id="VSSDK.Shell.10" version="10.0.3" targetFramework="net40" /> + <package id="VSSDK.Shell.Immutable.10" version="10.0.3" targetFramework="net40" /> + <package id="VSSDK.Shell.Interop" version="7.0.4" targetFramework="net40" /> + <package id="VSSDK.Shell.Interop.8" version="8.0.3" targetFramework="net40" /> + <package id="VSSDK.Shell.Interop.9" version="9.0.3" targetFramework="net40" /> + <package id="VSSDK.Text" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.Text.10" version="10.0.4" targetFramework="net40" /> + <package id="VSSDK.TextManager.Interop" version="7.0.4" targetFramework="net40" /> + <package id="VSSDK.TextManager.Interop.8" version="8.0.4" targetFramework="net40" /> +</packages>
\ No newline at end of file diff --git a/gnu/llvm/clang/tools/clang-format-vs/README.txt b/gnu/llvm/clang/tools/clang-format-vs/README.txt new file mode 100644 index 00000000000..2cac5b9af9e --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/README.txt @@ -0,0 +1,51 @@ +This directory contains a VSPackage project to generate a Visual Studio extension
+for clang-format.
+
+Build prerequisites are:
+- Visual Studio 2015
+- Extensions SDK (you'll be prompted to install it if you open ClangFormat.sln)
+
+The extension is built using CMake to generate the usual LLVM.sln by setting
+the following CMake vars:
+
+- BUILD_CLANG_FORMAT_VS_PLUGIN=ON
+
+- NUGET_EXE_DIR=path/to/nuget_dir (unless nuget.exe is already available in PATH)
+
+example:
+ cd /d C:\code\llvm
+ mkdir build & cd build
+ cmake -DBUILD_CLANG_FORMAT_VS_PLUGIN=ON -DNUGET_EXE_DIR=C:\nuget ..
+
+Once LLVM.sln is generated, build the clang_format_vsix target, which will build
+ClangFormat.sln, the C# extension application.
+
+The CMake build will copy clang-format.exe and LICENSE.TXT into the ClangFormat/
+directory so they can be bundled with the plug-in, as well as creating
+ClangFormat/source.extension.vsixmanifest. Once the plug-in has been built with
+CMake once, it can be built manually from the ClangFormat.sln solution in Visual
+Studio.
+
+===========
+ Debugging
+===========
+
+Once you've built the clang_format_vsix project from LLVM.sln at least once,
+open ClangFormat.sln in Visual Studio, then:
+
+- Make sure the "Debug" target is selected
+- Open the ClangFormat project properties
+- Select the Debug tab
+- Set "Start external program:" to where your devenv.exe is installed. Typically
+ it's "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe"
+- Set "Command line arguments" to: /rootsuffix Exp
+- You can now set breakpoints if you like
+- Press F5 to build and run with debugger
+
+If all goes well, a new instance of Visual Studio will be launched in a special
+mode where it uses the experimental hive instead of the normal configuration hive.
+By default, when you build a VSIX project in Visual Studio, it auto-registers the
+extension in the experimental hive, allowing you to test it. In the new Visual Studio
+instance, open or create a C++ solution, and you should now see the Clang Format
+entries in the Tool menu. You can test it out, and any breakpoints you set will be
+hit where you can debug as usual.
diff --git a/gnu/llvm/clang/tools/clang-format-vs/source.extension.vsixmanifest.in b/gnu/llvm/clang/tools/clang-format-vs/source.extension.vsixmanifest.in new file mode 100644 index 00000000000..d4820c051ad --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format-vs/source.extension.vsixmanifest.in @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?>
+<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
+ <Metadata>
+ <Identity Id="3cb18a5e-97e9-11e7-abc4-cec278b6b50a" Version="@CLANG_FORMAT_VS_VERSION@" Language="en-US" Publisher="LLVM"/>
+ <DisplayName>ClangFormat</DisplayName>
+ <Description xml:space="preserve">A tool to format C/C++/Obj-C code.</Description>
+ <MoreInfo>http://clang.llvm.org/docs/ClangFormat.html</MoreInfo>
+ <License>license.txt</License>
+ </Metadata>
+ <Installation InstalledByMsi="false">
+ <InstallationTarget Id="Microsoft.VisualStudio.Pro" Version="[11.0, 17.0)" />
+ </Installation>
+ <Dependencies>
+ <Dependency Id="Microsoft.VisualStudio.MPF" MinVersion="11.0" DisplayName="Visual Studio MPF" />
+ </Dependencies>
+ <Prerequisites>
+ <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[11.0,)" DisplayName="Visual Studio core editor" />
+ </Prerequisites>
+</PackageManifest>
diff --git a/gnu/llvm/clang/tools/clang-format/CMakeLists.txt b/gnu/llvm/clang/tools/clang-format/CMakeLists.txt new file mode 100644 index 00000000000..35ecdb11253 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/CMakeLists.txt @@ -0,0 +1,40 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_tool(clang-format + ClangFormat.cpp + ) + +set(CLANG_FORMAT_LIB_DEPS + clangBasic + clangFormat + clangRewrite + clangToolingCore + ) + +clang_target_link_libraries(clang-format + PRIVATE + ${CLANG_FORMAT_LIB_DEPS} + ) + +if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE ) + add_subdirectory(fuzzer) +endif() + +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/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp b/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp new file mode 100644 index 00000000000..aa40bab52df --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/ClangFormat.cpp @@ -0,0 +1,520 @@ +//===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a clang-format tool that automatically formats +/// (fragments of) C++ code. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/Format/Format.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Process.h" + +using namespace llvm; +using clang::tooling::Replacements; + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); + +// Mark all our options with this category, everything else (except for -version +// and -help) will be hidden. +static cl::OptionCategory ClangFormatCategory("Clang-format options"); + +static cl::list<unsigned> + Offsets("offset", + cl::desc("Format a range starting at this byte offset.\n" + "Multiple ranges can be formatted by specifying\n" + "several -offset and -length pairs.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); +static cl::list<unsigned> + Lengths("length", + cl::desc("Format a range of this length (in bytes).\n" + "Multiple ranges can be formatted by specifying\n" + "several -offset and -length pairs.\n" + "When only a single -offset is specified without\n" + "-length, clang-format will format up to the end\n" + "of the file.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); +static cl::list<std::string> + LineRanges("lines", + cl::desc("<start line>:<end line> - format a range of\n" + "lines (both 1-based).\n" + "Multiple ranges can be formatted by specifying\n" + "several -lines arguments.\n" + "Can't be used with -offset and -length.\n" + "Can only be used with one input file."), + cl::cat(ClangFormatCategory)); +static cl::opt<std::string> + Style("style", cl::desc(clang::format::StyleOptionHelpDescription), + cl::init(clang::format::DefaultFormatStyle), + cl::cat(ClangFormatCategory)); +static cl::opt<std::string> + FallbackStyle("fallback-style", + cl::desc("The name of the predefined style used as a\n" + "fallback in case clang-format is invoked with\n" + "-style=file, but can not find the .clang-format\n" + "file to use.\n" + "Use -fallback-style=none to skip formatting."), + cl::init(clang::format::DefaultFallbackStyle), + cl::cat(ClangFormatCategory)); + +static cl::opt<std::string> AssumeFileName( + "assume-filename", + cl::desc("Override filename used to determine the language.\n" + "When reading from stdin, clang-format assumes this\n" + "filename to determine the language."), + cl::init("<stdin>"), cl::cat(ClangFormatCategory)); + +static cl::opt<bool> Inplace("i", + cl::desc("Inplace edit <file>s, if specified."), + cl::cat(ClangFormatCategory)); + +static cl::opt<bool> OutputXML("output-replacements-xml", + cl::desc("Output replacements as XML."), + cl::cat(ClangFormatCategory)); +static cl::opt<bool> + DumpConfig("dump-config", + cl::desc("Dump configuration options to stdout and exit.\n" + "Can be used with -style option."), + cl::cat(ClangFormatCategory)); +static cl::opt<unsigned> + Cursor("cursor", + cl::desc("The position of the cursor when invoking\n" + "clang-format from an editor integration"), + cl::init(0), cl::cat(ClangFormatCategory)); + +static cl::opt<bool> SortIncludes( + "sort-includes", + cl::desc("If set, overrides the include sorting behavior determined by the " + "SortIncludes style flag"), + cl::cat(ClangFormatCategory)); + +static cl::opt<bool> + Verbose("verbose", cl::desc("If set, shows the list of processed files"), + cl::cat(ClangFormatCategory)); + +// Use --dry-run to match other LLVM tools when you mean do it but don't +// actually do it +static cl::opt<bool> + DryRun("dry-run", + cl::desc("If set, do not actually make the formatting changes"), + cl::cat(ClangFormatCategory)); + +// Use -n as a common command as an alias for --dry-run. (git and make use -n) +static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"), + cl::cat(ClangFormatCategory), cl::aliasopt(DryRun), + cl::NotHidden); + +// Emulate being able to turn on/off the warning. +static cl::opt<bool> + WarnFormat("Wclang-format-violations", + cl::desc("Warnings about individual formatting changes needed. " + "Used only with --dry-run or -n"), + cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::opt<bool> + NoWarnFormat("Wno-clang-format-violations", + cl::desc("Do not warn about individual formatting changes " + "needed. Used only with --dry-run or -n"), + cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::opt<unsigned> ErrorLimit( + "ferror-limit", + cl::desc("Set the maximum number of clang-format errors to emit before " + "stopping (0 = no limit). Used only with --dry-run or -n"), + cl::init(0), cl::cat(ClangFormatCategory)); + +static cl::opt<bool> + WarningsAsErrors("Werror", + cl::desc("If set, changes formatting warnings to errors"), + cl::cat(ClangFormatCategory)); + +static cl::opt<bool> + ShowColors("fcolor-diagnostics", + cl::desc("If set, and on a color-capable terminal controls " + "whether or not to print diagnostics in color"), + cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::opt<bool> + NoShowColors("fno-color-diagnostics", + cl::desc("If set, and on a color-capable terminal controls " + "whether or not to print diagnostics in color"), + cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"), + cl::cat(ClangFormatCategory)); + +namespace clang { +namespace format { + +static FileID createInMemoryFile(StringRef FileName, MemoryBuffer *Source, + SourceManager &Sources, FileManager &Files, + llvm::vfs::InMemoryFileSystem *MemFS) { + MemFS->addFileNoOwn(FileName, 0, Source); + auto File = Files.getFile(FileName); + return Sources.createFileID(File ? *File : nullptr, SourceLocation(), + SrcMgr::C_User); +} + +// Parses <start line>:<end line> input to a pair of line numbers. +// Returns true on error. +static bool parseLineRange(StringRef Input, unsigned &FromLine, + unsigned &ToLine) { + std::pair<StringRef, StringRef> LineRange = Input.split(':'); + return LineRange.first.getAsInteger(0, FromLine) || + LineRange.second.getAsInteger(0, ToLine); +} + +static bool fillRanges(MemoryBuffer *Code, + std::vector<tooling::Range> &Ranges) { + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + FileManager Files(FileSystemOptions(), InMemoryFileSystem); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + new DiagnosticOptions); + SourceManager Sources(Diagnostics, Files); + FileID ID = createInMemoryFile("<irrelevant>", Code, Sources, Files, + InMemoryFileSystem.get()); + if (!LineRanges.empty()) { + if (!Offsets.empty() || !Lengths.empty()) { + errs() << "error: cannot use -lines with -offset/-length\n"; + return true; + } + + for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) { + unsigned FromLine, ToLine; + if (parseLineRange(LineRanges[i], FromLine, ToLine)) { + errs() << "error: invalid <start line>:<end line> pair\n"; + return true; + } + if (FromLine > ToLine) { + errs() << "error: start line should be less than end line\n"; + return true; + } + SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); + SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); + if (Start.isInvalid() || End.isInvalid()) + return true; + unsigned Offset = Sources.getFileOffset(Start); + unsigned Length = Sources.getFileOffset(End) - Offset; + Ranges.push_back(tooling::Range(Offset, Length)); + } + return false; + } + + if (Offsets.empty()) + Offsets.push_back(0); + if (Offsets.size() != Lengths.size() && + !(Offsets.size() == 1 && Lengths.empty())) { + errs() << "error: number of -offset and -length arguments must match.\n"; + return true; + } + for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { + if (Offsets[i] >= Code->getBufferSize()) { + errs() << "error: offset " << Offsets[i] << " is outside the file\n"; + return true; + } + SourceLocation Start = + Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); + SourceLocation End; + if (i < Lengths.size()) { + if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { + errs() << "error: invalid length " << Lengths[i] + << ", offset + length (" << Offsets[i] + Lengths[i] + << ") is outside the file.\n"; + return true; + } + End = Start.getLocWithOffset(Lengths[i]); + } else { + End = Sources.getLocForEndOfFile(ID); + } + unsigned Offset = Sources.getFileOffset(Start); + unsigned Length = Sources.getFileOffset(End) - Offset; + Ranges.push_back(tooling::Range(Offset, Length)); + } + return false; +} + +static void outputReplacementXML(StringRef Text) { + // FIXME: When we sort includes, we need to make sure the stream is correct + // utf-8. + size_t From = 0; + size_t Index; + while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) { + outs() << Text.substr(From, Index - From); + switch (Text[Index]) { + case '\n': + outs() << " "; + break; + case '\r': + outs() << " "; + break; + case '<': + outs() << "<"; + break; + case '&': + outs() << "&"; + break; + default: + llvm_unreachable("Unexpected character encountered!"); + } + From = Index + 1; + } + outs() << Text.substr(From); +} + +static void outputReplacementsXML(const Replacements &Replaces) { + for (const auto &R : Replaces) { + outs() << "<replacement " + << "offset='" << R.getOffset() << "' " + << "length='" << R.getLength() << "'>"; + outputReplacementXML(R.getReplacementText()); + outs() << "</replacement>\n"; + } +} + +static bool +emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName, + const std::unique_ptr<llvm::MemoryBuffer> &Code) { + if (Replaces.empty()) + return false; + + unsigned Errors = 0; + if (WarnFormat && !NoWarnFormat) { + llvm::SourceMgr Mgr; + const char *StartBuf = Code->getBufferStart(); + + Mgr.AddNewSourceBuffer( + MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc()); + for (const auto &R : Replaces) { + SMDiagnostic Diag = Mgr.GetMessage( + SMLoc::getFromPointer(StartBuf + R.getOffset()), + WarningsAsErrors ? SourceMgr::DiagKind::DK_Error + : SourceMgr::DiagKind::DK_Warning, + "code should be clang-formatted [-Wclang-format-violations]"); + + Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors)); + if (ErrorLimit && ++Errors >= ErrorLimit) + break; + } + } + return WarningsAsErrors; +} + +static void outputXML(const Replacements &Replaces, + const Replacements &FormatChanges, + const FormattingAttemptStatus &Status, + const cl::opt<unsigned> &Cursor, + unsigned CursorPosition) { + outs() << "<?xml version='1.0'?>\n<replacements " + "xml:space='preserve' incomplete_format='" + << (Status.FormatComplete ? "false" : "true") << "'"; + if (!Status.FormatComplete) + outs() << " line='" << Status.Line << "'"; + outs() << ">\n"; + if (Cursor.getNumOccurrences() != 0) + outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition) + << "</cursor>\n"; + + outputReplacementsXML(Replaces); + outs() << "</replacements>\n"; +} + +// Returns true on error. +static bool format(StringRef FileName) { + if (!OutputXML && Inplace && FileName == "-") { + errs() << "error: cannot use -i when reading from stdin.\n"; + return false; + } + // On Windows, overwriting a file with an open file mapping doesn't work, + // so read the whole file into memory when formatting in-place. + ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = + !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName) + : MemoryBuffer::getFileOrSTDIN(FileName); + if (std::error_code EC = CodeOrErr.getError()) { + errs() << EC.message() << "\n"; + return true; + } + std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get()); + if (Code->getBufferSize() == 0) + return false; // Empty files are formatted correctly. + + StringRef BufStr = Code->getBuffer(); + + const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr); + + if (InvalidBOM) { + errs() << "error: encoding with unsupported byte order mark \"" + << InvalidBOM << "\" detected"; + if (FileName != "-") + errs() << " in file '" << FileName << "'"; + errs() << ".\n"; + return true; + } + + std::vector<tooling::Range> Ranges; + if (fillRanges(Code.get(), Ranges)) + return true; + StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName; + if (AssumedFileName.empty()) { + llvm::errs() << "error: empty filenames are not allowed\n"; + return true; + } + + llvm::Expected<FormatStyle> FormatStyle = + getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer()); + if (!FormatStyle) { + llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; + return true; + } + + if (SortIncludes.getNumOccurrences() != 0) + FormatStyle->SortIncludes = SortIncludes; + unsigned CursorPosition = Cursor; + Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, + AssumedFileName, &CursorPosition); + auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); + if (!ChangedCode) { + llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; + return true; + } + // Get new affected ranges after sorting `#includes`. + Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); + FormattingAttemptStatus Status; + Replacements FormatChanges = + reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); + Replaces = Replaces.merge(FormatChanges); + if (OutputXML || DryRun) { + if (DryRun) { + return emitReplacementWarnings(Replaces, AssumedFileName, Code); + } else { + outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition); + } + } else { + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + FileManager Files(FileSystemOptions(), InMemoryFileSystem); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + new DiagnosticOptions); + SourceManager Sources(Diagnostics, Files); + FileID ID = createInMemoryFile(AssumedFileName, Code.get(), Sources, Files, + InMemoryFileSystem.get()); + Rewriter Rewrite(Sources, LangOptions()); + tooling::applyAllReplacements(Replaces, Rewrite); + if (Inplace) { + if (Rewrite.overwriteChangedFiles()) + return true; + } else { + if (Cursor.getNumOccurrences() != 0) { + outs() << "{ \"Cursor\": " + << FormatChanges.getShiftedCodePosition(CursorPosition) + << ", \"IncompleteFormat\": " + << (Status.FormatComplete ? "false" : "true"); + if (!Status.FormatComplete) + outs() << ", \"Line\": " << Status.Line; + outs() << " }\n"; + } + Rewrite.getEditBuffer(ID).write(outs()); + } + } + return false; +} + +} // namespace format +} // namespace clang + +static void PrintVersion(raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-format") << '\n'; +} + +// Dump the configuration. +static int dumpConfig() { + StringRef FileName; + std::unique_ptr<llvm::MemoryBuffer> Code; + if (FileNames.empty()) { + // We can't read the code to detect the language if there's no + // file name, so leave Code empty here. + FileName = AssumeFileName; + } else { + // Read in the code in case the filename alone isn't enough to + // detect the language. + ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = + MemoryBuffer::getFileOrSTDIN(FileNames[0]); + if (std::error_code EC = CodeOrErr.getError()) { + llvm::errs() << EC.message() << "\n"; + return 1; + } + FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0]; + Code = std::move(CodeOrErr.get()); + } + llvm::Expected<clang::format::FormatStyle> FormatStyle = + clang::format::getStyle(Style, FileName, FallbackStyle, + Code ? Code->getBuffer() : ""); + if (!FormatStyle) { + llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; + return 1; + } + std::string Config = clang::format::configurationAsText(*FormatStyle); + outs() << Config << "\n"; + return 0; +} + +int main(int argc, const char **argv) { + llvm::InitLLVM X(argc, argv); + + cl::HideUnrelatedOptions(ClangFormatCategory); + + cl::SetVersionPrinter(PrintVersion); + cl::ParseCommandLineOptions( + argc, argv, + "A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf/C# code.\n\n" + "If no arguments are specified, it formats the code from standard input\n" + "and writes the result to the standard output.\n" + "If <file>s are given, it reformats the files. If -i is specified\n" + "together with <file>s, the files are edited in-place. Otherwise, the\n" + "result is written to the standard output.\n"); + + if (Help) { + cl::PrintHelpMessage(); + return 0; + } + + if (DumpConfig) { + return dumpConfig(); + } + + bool Error = false; + if (FileNames.empty()) { + Error = clang::format::format("-"); + return Error ? 1 : 0; + } + if (FileNames.size() != 1 && + (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) { + errs() << "error: -offset, -length and -lines can only be used for " + "single file.\n"; + return 1; + } + for (const auto &FileName : FileNames) { + if (Verbose) + errs() << "Formatting " << FileName << "\n"; + Error |= clang::format::format(FileName); + } + return Error ? 1 : 0; +} diff --git a/gnu/llvm/clang/tools/clang-format/clang-format-bbedit.applescript b/gnu/llvm/clang/tools/clang-format/clang-format-bbedit.applescript new file mode 100644 index 00000000000..fa88fe90048 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/clang-format-bbedit.applescript @@ -0,0 +1,27 @@ +-- In this file, change "/path/to/" to the path where you installed clang-format +-- and save it to ~/Library/Application Support/BBEdit/Scripts. You can then +-- select the script from the Script menu and clang-format will format the +-- selection. Note that you can rename the menu item by renaming the script, and +-- can assign the menu item a keyboard shortcut in the BBEdit preferences, under +-- Menus & Shortcuts. +on urlToPOSIXPath(theURL) + return do shell script "python -c \"import urllib, urlparse, sys; print urllib.unquote(urlparse.urlparse(sys.argv[1])[2])\" " & quoted form of theURL +end urlToPOSIXPath + +tell application "BBEdit" + set selectionOffset to characterOffset of selection + set selectionLength to length of selection + set fileURL to URL of text document 1 +end tell + +set filePath to urlToPOSIXPath(fileURL) +set newContents to do shell script "/path/to/clang-format -offset=" & selectionOffset & " -length=" & selectionLength & " " & quoted form of filePath + +tell application "BBEdit" + -- "set contents of text document 1 to newContents" scrolls to the bottom while + -- replacing a selection flashes a bit but doesn't affect the scroll position. + set currentLength to length of contents of text document 1 + select characters 1 thru currentLength of text document 1 + set text of selection to newContents + select characters selectionOffset thru (selectionOffset + selectionLength - 1) of text document 1 +end tell diff --git a/gnu/llvm/clang/tools/clang-format/clang-format-diff.py b/gnu/llvm/clang/tools/clang-format/clang-format-diff.py new file mode 100755 index 00000000000..122db49ff73 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/clang-format-diff.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# +#===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===------------------------------------------------------------------------===# + +""" +This script reads input from a unified diff and reformats all the changed +lines. This is useful to reformat all the lines touched by a specific patch. +Example usage for git/svn users: + + git diff -U0 --no-color HEAD^ | clang-format-diff.py -p1 -i + svn diff --diff-cmd=diff -x-U0 | clang-format-diff.py -i + +""" +from __future__ import absolute_import, division, print_function + +import argparse +import difflib +import re +import subprocess +import sys + +if sys.version_info.major >= 3: + from io import StringIO +else: + from io import BytesIO as StringIO + + +def main(): + parser = argparse.ArgumentParser(description=__doc__, + formatter_class= + argparse.RawDescriptionHelpFormatter) + parser.add_argument('-i', action='store_true', default=False, + help='apply edits to files instead of displaying a diff') + parser.add_argument('-p', metavar='NUM', default=0, + help='strip the smallest prefix containing P slashes') + parser.add_argument('-regex', metavar='PATTERN', default=None, + help='custom pattern selecting file paths to reformat ' + '(case sensitive, overrides -iregex)') + parser.add_argument('-iregex', metavar='PATTERN', default= + r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|m|mm|inc|js|ts|proto' + r'|protodevel|java|cs)', + help='custom pattern selecting file paths to reformat ' + '(case insensitive, overridden by -regex)') + parser.add_argument('-sort-includes', action='store_true', default=False, + 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('-binary', default='clang-format', + help='location of binary to use for clang-format') + args = parser.parse_args() + + # Extract changed lines for each file. + filename = None + lines_by_file = {} + for line in sys.stdin: + match = re.search(r'^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line) + if match: + filename = match.group(2) + if filename == None: + continue + + if args.regex is not None: + if not re.match('^%s$' % args.regex, filename): + continue + else: + if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE): + continue + + match = re.search(r'^@@.*\+(\d+)(,(\d+))?', line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).extend( + ['-lines', str(start_line) + ':' + str(end_line)]) + + # Reformat files containing changes in place. + for filename, lines in lines_by_file.items(): + if args.i and args.verbose: + print('Formatting {}'.format(filename)) + command = [args.binary, filename] + if args.i: + command.append('-i') + if args.sort_includes: + command.append('-sort-includes') + command.extend(lines) + if args.style: + command.extend(['-style', args.style]) + p = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=None, + stdin=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = p.communicate() + if p.returncode != 0: + sys.exit(p.returncode) + + if not args.i: + with open(filename) as f: + code = f.readlines() + formatted_code = StringIO(stdout).readlines() + diff = difflib.unified_diff(code, formatted_code, + filename, filename, + '(before formatting)', '(after formatting)') + diff_string = ''.join(diff) + if len(diff_string) > 0: + sys.stdout.write(diff_string) + +if __name__ == '__main__': + main() diff --git a/gnu/llvm/clang/tools/clang-format/clang-format-sublime.py b/gnu/llvm/clang/tools/clang-format/clang-format-sublime.py new file mode 100644 index 00000000000..20c867092ef --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/clang-format-sublime.py @@ -0,0 +1,60 @@ +# This file is a minimal clang-format sublime-integration. To install: +# - Change 'binary' if clang-format is not on the path (see below). +# - Put this file into your sublime Packages directory, e.g. on Linux: +# ~/.config/sublime-text-2/Packages/User/clang-format-sublime.py +# - Add a key binding: +# { "keys": ["ctrl+shift+c"], "command": "clang_format" }, +# +# With this integration you can press the bound key and clang-format will +# format the current lines and selections for all cursor positions. The lines +# or regions are extended to the next bigger syntactic entities. +# +# It operates on the current, potentially unsaved buffer and does not create +# or save any files. To revert a formatting, just undo. + +from __future__ import absolute_import, division, print_function +import sublime +import sublime_plugin +import subprocess + +# Change this to the full path if clang-format is not on the path. +binary = 'clang-format' + +# Change this to format according to other formatting styles. See the output of +# 'clang-format --help' for a list of supported styles. The default looks for +# a '.clang-format' or '_clang-format' file to indicate the style that should be +# used. +style = None + +class ClangFormatCommand(sublime_plugin.TextCommand): + def run(self, edit): + encoding = self.view.encoding() + if encoding == 'Undefined': + encoding = 'utf-8' + regions = [] + command = [binary] + if style: + command.extend(['-style', style]) + for region in self.view.sel(): + regions.append(region) + region_offset = min(region.a, region.b) + region_length = abs(region.b - region.a) + command.extend(['-offset', str(region_offset), + '-length', str(region_length), + '-assume-filename', str(self.view.file_name())]) + old_viewport_position = self.view.viewport_position() + buf = self.view.substr(sublime.Region(0, self.view.size())) + p = subprocess.Popen(command, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, stdin=subprocess.PIPE) + output, error = p.communicate(buf.encode(encoding)) + if error: + print(error) + self.view.replace( + edit, sublime.Region(0, self.view.size()), + output.decode(encoding)) + self.view.sel().clear() + for region in regions: + self.view.sel().add(region) + # FIXME: Without the 10ms delay, the viewport sometimes jumps. + sublime.set_timeout(lambda: self.view.set_viewport_position( + old_viewport_position, False), 10) diff --git a/gnu/llvm/clang/tools/clang-format/clang-format-test.el b/gnu/llvm/clang/tools/clang-format/clang-format-test.el new file mode 100644 index 00000000000..41df1a9ca68 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/clang-format-test.el @@ -0,0 +1,128 @@ +;;; clang-format-test.el --- unit tests for clang-format.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2017 Google Inc. + +;; Author: Philipp Stephani <phst@google.com> + +;; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +;; See https://llvm.org/LICENSE.txt for license information. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +;;; Commentary: + +;; Unit tests for clang-format.el. Not run by lit, run as: +;; emacs -Q -batch -l clang/tools/clang-format/clang-format.el -l clang/tools/clang-format/clang-format-test.el -f ert-run-tests-batch-and-exit + +;;; Code: + +(require 'clang-format) + +(require 'cl-lib) +(require 'ert) +(require 'pcase) + +(ert-deftest clang-format-buffer--buffer-encoding () + "Tests that encoded text is handled properly." + (cl-letf* ((call-process-args nil) + ((symbol-function 'call-process-region) + (lambda (&rest args) + (push args call-process-args) + (pcase-exhaustive args + (`(,_start ,_end ,_program ,_delete (,stdout ,_stderr) + ,_display . ,_args) + (with-current-buffer stdout + (insert "<?xml version='1.0'?> +<replacements xml:space='preserve' incomplete_format='false'> +<replacement offset='4' length='0'> </replacement> +<replacement offset='10' length='0'> </replacement> +</replacements> +")) + 0))))) + (with-temp-buffer + (let ((buffer-file-name "foo.cpp") + (buffer-file-coding-system 'utf-8-with-signature-dos) + (default-process-coding-system 'latin-1-unix)) + (insert "ä =ö;\nü= ß;\n") + (goto-char (point-min)) + (end-of-line) + (clang-format-buffer)) + (should (equal (buffer-string) "ä = ö;\nü = ß;\n")) + (should (eolp)) + (should (equal (buffer-substring (point) (point-max)) + "\nü = ß;\n"))) + (should-not (cdr call-process-args)) + (pcase-exhaustive call-process-args + (`((,start ,end ,_program ,delete (,_stdout ,_stderr) ,display . ,args)) + (should-not start) + (should-not end) + (should-not delete) + (should-not display) + (should (equal args + '("-output-replacements-xml" "-assume-filename" "foo.cpp" + "-fallback-style" "none" + ;; Beginning of buffer, no byte-order mark. + "-offset" "0" + ;; We have two lines with 2×2 bytes for the umlauts, + ;; 1 byte for the line ending, and 3 bytes for the + ;; other ASCII characters each. + "-length" "16" + ;; Length of a single line (without line ending). + "-cursor" "7"))))))) + +(ert-deftest clang-format-buffer--process-encoding () + "Tests that text is sent to the clang-format process in the +right encoding." + (cl-letf* ((hexdump (executable-find "hexdump")) + (original-call-process-region + (symbol-function 'call-process-region)) + (call-process-inputs nil) + ;; We redirect the input to hexdump so that we have guaranteed + ;; ASCII output. + ((symbol-function 'call-process-region) + (lambda (&rest args) + (pcase-exhaustive args + (`(,start ,end ,_program ,_delete (,stdout ,_stderr) + ,_display . ,_args) + (with-current-buffer stdout + (insert "<?xml version='1.0'?> +<replacements xml:space='preserve' incomplete_format='false'> +</replacements> +")) + (let ((stdin (current-buffer))) + (with-temp-buffer + (prog1 + (let ((stdout (current-buffer))) + (with-current-buffer stdin + (funcall original-call-process-region + start end hexdump nil stdout nil + "-v" "-e" "/1 \"%02x \""))) + (push (buffer-string) call-process-inputs))))))))) + (skip-unless hexdump) + (with-temp-buffer + (let ((buffer-file-name "foo.cpp") + (buffer-file-coding-system 'utf-8-with-signature-dos) + (default-process-coding-system 'latin-1-unix)) + (insert "ä\n") + (clang-format-buffer)) + (should (equal (buffer-string) "ä\n")) + (should (eobp))) + (should (equal call-process-inputs '("c3 a4 0a "))))) + +(ert-deftest clang-format-buffer--end-to-end () + "End-to-end test for ‘clang-format-buffer’. +Actually calls the clang-format binary." + (skip-unless (file-executable-p clang-format-executable)) + (with-temp-buffer + (let ((buffer-file-name "foo.cpp") + (buffer-file-coding-system 'utf-8-with-signature-dos) + (default-process-coding-system 'latin-1-unix)) + (insert "ä =ö;\nü= ß;\n") + (goto-char (point-min)) + (end-of-line) + (clang-format-buffer)) + (should (equal (buffer-string) "ä = ö;\nü = ß;\n")) + (should (eolp)) + (should (equal (buffer-substring (point) (point-max)) + "\nü = ß;\n")))) + +;;; clang-format-test.el ends here diff --git a/gnu/llvm/clang/tools/clang-format/clang-format.el b/gnu/llvm/clang/tools/clang-format/clang-format.el new file mode 100644 index 00000000000..768acb3a5d0 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/clang-format.el @@ -0,0 +1,220 @@ +;;; clang-format.el --- Format code using clang-format -*- lexical-binding: t; -*- + +;; Keywords: tools, c +;; Package-Requires: ((cl-lib "0.3")) +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +;;; Commentary: + +;; This package allows to filter code through clang-format to fix its formatting. +;; clang-format is a tool that formats C/C++/Obj-C code according to a set of +;; style options, see <http://clang.llvm.org/docs/ClangFormatStyleOptions.html>. +;; Note that clang-format 3.4 or newer is required. + +;; clang-format.el is available via MELPA and can be installed via +;; +;; M-x package-install clang-format +;; +;; when ("melpa" . "http://melpa.org/packages/") is included in +;; `package-archives'. Alternatively, ensure the directory of this +;; file is in your `load-path' and add +;; +;; (require 'clang-format) +;; +;; to your .emacs configuration. + +;; You may also want to bind `clang-format-region' to a key: +;; +;; (global-set-key [C-M-tab] 'clang-format-region) + +;;; Code: + +(require 'cl-lib) +(require 'xml) + +(defgroup clang-format nil + "Format code using clang-format." + :group 'tools) + +(defcustom clang-format-executable + (or (executable-find "clang-format") + "clang-format") + "Location of the clang-format executable. + +A string containing the name or the full path of the executable." + :group 'clang-format + :type '(file :must-match t) + :risky t) + +(defcustom clang-format-style nil + "Style argument to pass to clang-format. + +By default clang-format will load the style configuration from +a file named .clang-format located in one of the parent directories +of the buffer." + :group 'clang-format + :type '(choice (string) (const nil)) + :safe #'stringp) +(make-variable-buffer-local 'clang-format-style) + +(defcustom clang-format-fallback-style "none" + "Fallback style to pass to clang-format. + +This style will be used if clang-format-style is set to \"file\" +and no .clang-format is found in the directory of the buffer or +one of parent directories. Set to \"none\" to disable formatting +in such buffers." + :group 'clang-format + :type 'string + :safe #'stringp) +(make-variable-buffer-local 'clang-format-fallback-style) + +(defun clang-format--extract (xml-node) + "Extract replacements and cursor information from XML-NODE." + (unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements)) + (error "Expected <replacements> node")) + (let ((nodes (xml-node-children xml-node)) + (incomplete-format (xml-get-attribute xml-node 'incomplete_format)) + replacements + cursor) + (dolist (node nodes) + (when (listp node) + (let* ((children (xml-node-children node)) + (text (car children))) + (cl-case (xml-node-name node) + ('replacement + (let* ((offset (xml-get-attribute-or-nil node 'offset)) + (length (xml-get-attribute-or-nil node 'length))) + (when (or (null offset) (null length)) + (error "<replacement> node does not have offset and length attributes")) + (when (cdr children) + (error "More than one child node in <replacement> node")) + + (setq offset (string-to-number offset)) + (setq length (string-to-number length)) + (push (list offset length text) replacements))) + ('cursor + (setq cursor (string-to-number text))))))) + + ;; Sort by decreasing offset, length. + (setq replacements (sort (delq nil replacements) + (lambda (a b) + (or (> (car a) (car b)) + (and (= (car a) (car b)) + (> (cadr a) (cadr b))))))) + + (list replacements cursor (string= incomplete-format "true")))) + +(defun clang-format--replace (offset length &optional text) + "Replace the region defined by OFFSET and LENGTH with TEXT. +OFFSET and LENGTH are measured in bytes, not characters. OFFSET +is a zero-based file offset, assuming ‘utf-8-unix’ coding." + (let ((start (clang-format--filepos-to-bufferpos offset 'exact 'utf-8-unix)) + (end (clang-format--filepos-to-bufferpos (+ offset length) 'exact + 'utf-8-unix))) + (goto-char start) + (delete-region start end) + (when text + (insert text)))) + +;; ‘bufferpos-to-filepos’ and ‘filepos-to-bufferpos’ are new in Emacs 25.1. +;; Provide fallbacks for older versions. +(defalias 'clang-format--bufferpos-to-filepos + (if (fboundp 'bufferpos-to-filepos) + 'bufferpos-to-filepos + (lambda (position &optional _quality _coding-system) + (1- (position-bytes position))))) + +(defalias 'clang-format--filepos-to-bufferpos + (if (fboundp 'filepos-to-bufferpos) + 'filepos-to-bufferpos + (lambda (byte &optional _quality _coding-system) + (byte-to-position (1+ byte))))) + +;;;###autoload +(defun clang-format-region (start end &optional style assume-file-name) + "Use clang-format to format the code between START and END according to STYLE. +If called interactively uses the region or the current statement if there is no +no active region. If no STYLE is given uses `clang-format-style'. Use +ASSUME-FILE-NAME to locate a style config file, if no ASSUME-FILE-NAME is given +uses the function `buffer-file-name'." + (interactive + (if (use-region-p) + (list (region-beginning) (region-end)) + (list (point) (point)))) + + (unless style + (setq style clang-format-style)) + + (unless assume-file-name + (setq assume-file-name buffer-file-name)) + + (let ((file-start (clang-format--bufferpos-to-filepos start 'approximate + 'utf-8-unix)) + (file-end (clang-format--bufferpos-to-filepos end 'approximate + 'utf-8-unix)) + (cursor (clang-format--bufferpos-to-filepos (point) 'exact 'utf-8-unix)) + (temp-buffer (generate-new-buffer " *clang-format-temp*")) + (temp-file (make-temp-file "clang-format")) + ;; Output is XML, which is always UTF-8. Input encoding should match + ;; the encoding used to convert between buffer and file positions, + ;; otherwise the offsets calculated above are off. For simplicity, we + ;; always use ‘utf-8-unix’ and ignore the buffer coding system. + (default-process-coding-system '(utf-8-unix . utf-8-unix))) + (unwind-protect + (let ((status (apply #'call-process-region + nil nil clang-format-executable + nil `(,temp-buffer ,temp-file) nil + `("-output-replacements-xml" + ;; Guard against a nil assume-file-name. + ;; If the clang-format option -assume-filename + ;; is given a blank string it will crash as per + ;; the following bug report + ;; https://bugs.llvm.org/show_bug.cgi?id=34667 + ,@(and assume-file-name + (list "-assume-filename" assume-file-name)) + ,@(and style (list "-style" style)) + "-fallback-style" ,clang-format-fallback-style + "-offset" ,(number-to-string file-start) + "-length" ,(number-to-string (- file-end file-start)) + "-cursor" ,(number-to-string cursor)))) + (stderr (with-temp-buffer + (unless (zerop (cadr (insert-file-contents temp-file))) + (insert ": ")) + (buffer-substring-no-properties + (point-min) (line-end-position))))) + (cond + ((stringp status) + (error "(clang-format killed by signal %s%s)" status stderr)) + ((not (zerop status)) + (error "(clang-format failed with code %d%s)" status stderr))) + + (cl-destructuring-bind (replacements cursor incomplete-format) + (with-current-buffer temp-buffer + (clang-format--extract (car (xml-parse-region)))) + (save-excursion + (dolist (rpl replacements) + (apply #'clang-format--replace rpl))) + (when cursor + (goto-char (clang-format--filepos-to-bufferpos cursor 'exact + 'utf-8-unix))) + (if incomplete-format + (message "(clang-format: incomplete (syntax errors)%s)" stderr) + (message "(clang-format: success%s)" stderr)))) + (delete-file temp-file) + (when (buffer-name temp-buffer) (kill-buffer temp-buffer))))) + +;;;###autoload +(defun clang-format-buffer (&optional style assume-file-name) + "Use clang-format to format the current buffer according to STYLE. +If no STYLE is given uses `clang-format-style'. Use ASSUME-FILE-NAME +to locate a style config file. If no ASSUME-FILE-NAME is given uses +the function `buffer-file-name'." + (interactive) + (clang-format-region (point-min) (point-max) style assume-file-name)) + +;;;###autoload +(defalias 'clang-format 'clang-format-region) + +(provide 'clang-format) +;;; clang-format.el ends here diff --git a/gnu/llvm/clang/tools/clang-format/clang-format.py b/gnu/llvm/clang/tools/clang-format/clang-format.py new file mode 100644 index 00000000000..1a615b17072 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/clang-format.py @@ -0,0 +1,145 @@ +# This file is a minimal clang-format vim-integration. To install: +# - Change 'binary' if clang-format is not on the path (see below). +# - Add to your .vimrc: +# +# if has('python') +# map <C-I> :pyf <path-to-this-file>/clang-format.py<cr> +# imap <C-I> <c-o>:pyf <path-to-this-file>/clang-format.py<cr> +# elseif has('python3') +# map <C-I> :py3f <path-to-this-file>/clang-format.py<cr> +# imap <C-I> <c-o>:py3f <path-to-this-file>/clang-format.py<cr> +# endif +# +# The if-elseif-endif conditional should pick either the python3 or python2 +# integration depending on your vim setup. +# +# The first mapping enables clang-format for NORMAL and VISUAL mode, the second +# mapping adds support for INSERT mode. Change "C-I" to another binding if you +# need clang-format on a different key (C-I stands for Ctrl+i). +# +# With this integration you can press the bound key and clang-format will +# format the current line in NORMAL and INSERT mode or the selected region in +# VISUAL mode. The line or region is extended to the next bigger syntactic +# entity. +# +# You can also pass in the variable "l:lines" to choose the range for +# formatting. This variable can either contain "<start line>:<end line>" or +# "all" to format the full file. So, to format the full file, write a function +# like: +# :function FormatFile() +# : let l:lines="all" +# : if has('python') +# : pyf <path-to-this-file>/clang-format.py +# : elseif has('python3') +# : py3f <path-to-this-file>/clang-format.py +# : endif +# :endfunction +# +# It operates on the current, potentially unsaved buffer and does not create +# or save any files. To revert a formatting, just undo. +from __future__ import absolute_import, division, print_function + +import difflib +import json +import platform +import subprocess +import sys +import vim + +# set g:clang_format_path to the path to clang-format if it is not on the path +# Change this to the full path if clang-format is not on the path. +binary = 'clang-format' +if vim.eval('exists("g:clang_format_path")') == "1": + binary = vim.eval('g:clang_format_path') + +# Change this to format according to other formatting styles. See the output of +# 'clang-format --help' for a list of supported styles. The default looks for +# a '.clang-format' or '_clang-format' file to indicate the style that should be +# used. +style = None +fallback_style = None +if vim.eval('exists("g:clang_format_fallback_style")') == "1": + fallback_style = vim.eval('g:clang_format_fallback_style') + +def get_buffer(encoding): + if platform.python_version_tuple()[0] == '3': + return vim.current.buffer + return [ line.decode(encoding) for line in vim.current.buffer ] + +def main(): + # Get the current text. + encoding = vim.eval("&encoding") + buf = get_buffer(encoding) + # Join the buffer into a single string with a terminating newline + text = '\n'.join(buf) + '\n' + + # Determine range to format. + if vim.eval('exists("l:lines")') == '1': + lines = ['-lines', vim.eval('l:lines')] + elif vim.eval('exists("l:formatdiff")') == '1': + with open(vim.current.buffer.name, 'r') as f: + ondisk = f.read().splitlines(); + sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer) + lines = [] + for op in reversed(sequence.get_opcodes()): + if op[0] not in ['equal', 'delete']: + lines += ['-lines', '%s:%s' % (op[3] + 1, op[4])] + if lines == []: + return + else: + lines = ['-lines', '%s:%s' % (vim.current.range.start + 1, + vim.current.range.end + 1)] + + # Determine the cursor position. + cursor = int(vim.eval('line2byte(line("."))+col(".")')) - 2 + if cursor < 0: + print('Couldn\'t determine cursor position. Is your file empty?') + return + + # Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format. + startupinfo = None + if sys.platform.startswith('win32'): + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = subprocess.SW_HIDE + + # Call formatter. + command = [binary, '-cursor', str(cursor)] + if lines != ['-lines', 'all']: + command += lines + if style: + command.extend(['-style', style]) + if fallback_style: + command.extend(['-fallback-style', fallback_style]) + if vim.current.buffer.name: + command.extend(['-assume-filename', vim.current.buffer.name]) + p = subprocess.Popen(command, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE, startupinfo=startupinfo) + stdout, stderr = p.communicate(input=text.encode(encoding)) + + # If successful, replace buffer contents. + if stderr: + print(stderr) + + if not stdout: + print( + 'No output from clang-format (crashed?).\n' + 'Please report to bugs.llvm.org.' + ) + else: + lines = stdout.decode(encoding).split('\n') + output = json.loads(lines[0]) + # Strip off the trailing newline (added above). + # This maintains trailing empty lines present in the buffer if + # the -lines specification requests them to remain unchanged. + lines = lines[1:-1] + sequence = difflib.SequenceMatcher(None, buf, lines) + for op in reversed(sequence.get_opcodes()): + if op[0] != 'equal': + vim.current.buffer[op[1]:op[2]] = lines[op[3]:op[4]] + if output.get('IncompleteFormat'): + print('clang-format: incomplete (syntax errors)') + vim.command('goto %d' % (output['Cursor'] + 1)) + +main() diff --git a/gnu/llvm/clang/tools/clang-format/fuzzer/CMakeLists.txt b/gnu/llvm/clang/tools/clang-format/fuzzer/CMakeLists.txt new file mode 100644 index 00000000000..87ae05b62d1 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/fuzzer/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS support) + +if(LLVM_USE_SANITIZE_COVERAGE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer") +endif() + +add_clang_executable(clang-format-fuzzer + EXCLUDE_FROM_ALL + ClangFormatFuzzer.cpp + ) + +target_link_libraries(clang-format-fuzzer + PRIVATE + ${CLANG_FORMAT_LIB_DEPS} + ${LLVM_LIB_FUZZING_ENGINE} + ) diff --git a/gnu/llvm/clang/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp b/gnu/llvm/clang/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp new file mode 100644 index 00000000000..d39d8da0ccb --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/fuzzer/ClangFormatFuzzer.cpp @@ -0,0 +1,28 @@ +//===-- ClangFormatFuzzer.cpp - Fuzz the Clang format tool ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that runs Clang format on a single +/// input. This function is then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Format/Format.h" + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + // FIXME: fuzz more things: different styles, different style features. + std::string s((const char *)data, size); + auto Style = getGoogleStyle(clang::format::FormatStyle::LK_Cpp); + Style.ColumnLimit = 60; + auto Replaces = reformat(Style, s, clang::tooling::Range(0, s.size())); + auto Result = applyAllReplacements(s, Replaces); + + // Output must be checked, as otherwise we crash. + if (!Result) {} + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-format/git-clang-format b/gnu/llvm/clang/tools/clang-format/git-clang-format new file mode 100755 index 00000000000..bf05d9143fa --- /dev/null +++ b/gnu/llvm/clang/tools/clang-format/git-clang-format @@ -0,0 +1,580 @@ +#!/usr/bin/env python +# +#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===------------------------------------------------------------------------===# + +r""" +clang-format git integration +============================ + +This file provides a clang-format integration for git. Put it somewhere in your +path and ensure that it is executable. Then, "git clang-format" will invoke +clang-format on the changes in current files or a specific commit. + +For further details, run: +git clang-format -h + +Requires Python 2.7 or Python 3 +""" + +from __future__ import absolute_import, division, print_function +import argparse +import collections +import contextlib +import errno +import os +import re +import subprocess +import sys + +usage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]' + +desc = ''' +If zero or one commits are given, run clang-format on all lines that differ +between the working directory and <commit>, which defaults to HEAD. Changes are +only applied to the working directory. + +If two commits are given (requires --diff), run clang-format on all lines in the +second <commit> that differ from the first <commit>. + +The following git-config settings set the default of the corresponding option: + clangFormat.binary + clangFormat.commit + clangFormat.extension + clangFormat.style +''' + +# Name of the temporary index file in which save the output of clang-format. +# This file is created within the .git directory. +temp_index_basename = 'clang-format-index' + + +Range = collections.namedtuple('Range', 'start, count') + + +def main(): + config = load_git_config() + + # In order to keep '--' yet allow options after positionals, we need to + # check for '--' ourselves. (Setting nargs='*' throws away the '--', while + # nargs=argparse.REMAINDER disallows options after positionals.) + argv = sys.argv[1:] + try: + idx = argv.index('--') + except ValueError: + dash_dash = [] + else: + dash_dash = argv[idx:] + argv = argv[:idx] + + default_extensions = ','.join([ + # From clang/lib/Frontend/FrontendOptions.cpp, all lower case + 'c', 'h', # C + 'm', # ObjC + 'mm', # ObjC++ + 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', # C++ + 'cu', # CUDA + # Other languages that clang-format supports + 'proto', 'protodevel', # Protocol Buffers + 'java', # Java + 'js', # JavaScript + 'ts', # TypeScript + 'cs', # C Sharp + ]) + + p = argparse.ArgumentParser( + usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter, + description=desc) + p.add_argument('--binary', + default=config.get('clangformat.binary', 'clang-format'), + help='path to clang-format'), + p.add_argument('--commit', + default=config.get('clangformat.commit', 'HEAD'), + help='default commit to use if none is specified'), + p.add_argument('--diff', action='store_true', + help='print a diff instead of applying the changes') + p.add_argument('--extensions', + default=config.get('clangformat.extensions', + default_extensions), + help=('comma-separated list of file extensions to format, ' + 'excluding the period and case-insensitive')), + p.add_argument('-f', '--force', action='store_true', + help='allow changes to unstaged files') + p.add_argument('-p', '--patch', action='store_true', + help='select hunks interactively') + p.add_argument('-q', '--quiet', action='count', default=0, + help='print less information') + p.add_argument('--style', + default=config.get('clangformat.style', None), + help='passed to clang-format'), + p.add_argument('-v', '--verbose', action='count', default=0, + help='print extra information') + # We gather all the remaining positional arguments into 'args' since we need + # to use some heuristics to determine whether or not <commit> was present. + # However, to print pretty messages, we make use of metavar and help. + p.add_argument('args', nargs='*', metavar='<commit>', + help='revision from which to compute the diff') + p.add_argument('ignored', nargs='*', metavar='<file>...', + help='if specified, only consider differences in these files') + opts = p.parse_args(argv) + + opts.verbose -= opts.quiet + del opts.quiet + + commits, files = interpret_args(opts.args, dash_dash, opts.commit) + if len(commits) > 1: + if not opts.diff: + die('--diff is required when two commits are given') + else: + if len(commits) > 2: + die('at most two commits allowed; %d given' % len(commits)) + changed_lines = compute_diff_and_extract_lines(commits, files) + if opts.verbose >= 1: + ignored_files = set(changed_lines) + filter_by_extension(changed_lines, opts.extensions.lower().split(',')) + if opts.verbose >= 1: + ignored_files.difference_update(changed_lines) + if ignored_files: + print('Ignoring changes in the following files (wrong extension):') + for filename in ignored_files: + print(' %s' % filename) + if changed_lines: + print('Running clang-format on the following files:') + for filename in changed_lines: + print(' %s' % filename) + if not changed_lines: + print('no modified files to format') + return + # The computed diff outputs absolute paths, so we must cd before accessing + # those files. + cd_to_toplevel() + if len(commits) > 1: + old_tree = commits[1] + new_tree = run_clang_format_and_save_to_tree(changed_lines, + revision=commits[1], + binary=opts.binary, + style=opts.style) + else: + old_tree = create_tree_from_workdir(changed_lines) + new_tree = run_clang_format_and_save_to_tree(changed_lines, + binary=opts.binary, + style=opts.style) + if opts.verbose >= 1: + print('old tree: %s' % old_tree) + print('new tree: %s' % new_tree) + if old_tree == new_tree: + if opts.verbose >= 0: + print('clang-format did not modify any files') + elif opts.diff: + print_diff(old_tree, new_tree) + else: + changed_files = apply_changes(old_tree, new_tree, force=opts.force, + patch_mode=opts.patch) + if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1: + print('changed files:') + for filename in changed_files: + print(' %s' % filename) + + +def load_git_config(non_string_options=None): + """Return the git configuration as a dictionary. + + All options are assumed to be strings unless in `non_string_options`, in which + is a dictionary mapping option name (in lower case) to either "--bool" or + "--int".""" + if non_string_options is None: + non_string_options = {} + out = {} + for entry in run('git', 'config', '--list', '--null').split('\0'): + if entry: + name, value = entry.split('\n', 1) + if name in non_string_options: + value = run('git', 'config', non_string_options[name], name) + out[name] = value + return out + + +def interpret_args(args, dash_dash, default_commit): + """Interpret `args` as "[commits] [--] [files]" and return (commits, files). + + It is assumed that "--" and everything that follows has been removed from + args and placed in `dash_dash`. + + If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its + left (if present) are taken as commits. Otherwise, the arguments are checked + from left to right if they are commits or files. If commits are not given, + a list with `default_commit` is used.""" + if dash_dash: + if len(args) == 0: + commits = [default_commit] + else: + commits = args + for commit in commits: + object_type = get_object_type(commit) + if object_type not in ('commit', 'tag'): + if object_type is None: + die("'%s' is not a commit" % commit) + else: + die("'%s' is a %s, but a commit was expected" % (commit, object_type)) + files = dash_dash[1:] + elif args: + commits = [] + while args: + if not disambiguate_revision(args[0]): + break + commits.append(args.pop(0)) + if not commits: + commits = [default_commit] + files = args + else: + commits = [default_commit] + files = [] + return commits, files + + +def disambiguate_revision(value): + """Returns True if `value` is a revision, False if it is a file, or dies.""" + # If `value` is ambiguous (neither a commit nor a file), the following + # command will die with an appropriate error message. + run('git', 'rev-parse', value, verbose=False) + object_type = get_object_type(value) + if object_type is None: + return False + if object_type in ('commit', 'tag'): + return True + die('`%s` is a %s, but a commit or filename was expected' % + (value, object_type)) + + +def get_object_type(value): + """Returns a string description of an object's type, or None if it is not + a valid git object.""" + cmd = ['git', 'cat-file', '-t', value] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + return None + return convert_string(stdout.strip()) + + +def compute_diff_and_extract_lines(commits, files): + """Calls compute_diff() followed by extract_lines().""" + diff_process = compute_diff(commits, files) + changed_lines = extract_lines(diff_process.stdout) + diff_process.stdout.close() + diff_process.wait() + if diff_process.returncode != 0: + # Assume error was already printed to stderr. + sys.exit(2) + return changed_lines + + +def compute_diff(commits, files): + """Return a subprocess object producing the diff from `commits`. + + The return value's `stdin` file object will produce a patch with the + differences between the working directory and the first commit if a single + one was specified, or the difference between both specified commits, filtered + on `files` (if non-empty). Zero context lines are used in the patch.""" + git_tool = 'diff-index' + if len(commits) > 1: + git_tool = 'diff-tree' + cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--'] + cmd.extend(files) + p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p.stdin.close() + return p + + +def extract_lines(patch_file): + """Extract the changed lines in `patch_file`. + + The return value is a dictionary mapping filename to a list of (start_line, + line_count) pairs. + + The input must have been produced with ``-U0``, meaning unidiff format with + zero lines of context. The return value is a dict mapping filename to a + list of line `Range`s.""" + matches = {} + for line in patch_file: + line = convert_string(line) + match = re.search(r'^\+\+\+\ [^/]+/(.*)', line) + if match: + filename = match.group(1).rstrip('\r\n') + match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count > 0: + matches.setdefault(filename, []).append(Range(start_line, line_count)) + return matches + + +def filter_by_extension(dictionary, allowed_extensions): + """Delete every key in `dictionary` that doesn't have an allowed extension. + + `allowed_extensions` must be a collection of lowercase file extensions, + excluding the period.""" + allowed_extensions = frozenset(allowed_extensions) + for filename in list(dictionary.keys()): + base_ext = filename.rsplit('.', 1) + if len(base_ext) == 1 and '' in allowed_extensions: + continue + if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions: + del dictionary[filename] + + +def cd_to_toplevel(): + """Change to the top level of the git repository.""" + toplevel = run('git', 'rev-parse', '--show-toplevel') + os.chdir(toplevel) + + +def create_tree_from_workdir(filenames): + """Create a new git tree with the given files from the working directory. + + Returns the object ID (SHA-1) of the created tree.""" + return create_tree(filenames, '--stdin') + + +def run_clang_format_and_save_to_tree(changed_lines, revision=None, + binary='clang-format', style=None): + """Run clang-format on each file and save the result to a git tree. + + Returns the object ID (SHA-1) of the created tree.""" + def iteritems(container): + try: + return container.iteritems() # Python 2 + except AttributeError: + return container.items() # Python 3 + def index_info_generator(): + for filename, line_ranges in iteritems(changed_lines): + if revision: + git_metadata_cmd = ['git', 'ls-tree', + '%s:%s' % (revision, os.path.dirname(filename)), + os.path.basename(filename)] + git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + stdout = git_metadata.communicate()[0] + mode = oct(int(stdout.split()[0], 8)) + else: + mode = oct(os.stat(filename).st_mode) + # Adjust python3 octal format so that it matches what git expects + if mode.startswith('0o'): + mode = '0' + mode[2:] + blob_id = clang_format_to_blob(filename, line_ranges, + revision=revision, + binary=binary, + style=style) + yield '%s %s\t%s' % (mode, blob_id, filename) + return create_tree(index_info_generator(), '--index-info') + + +def create_tree(input_lines, mode): + """Create a tree object from the given input. + + If mode is '--stdin', it must be a list of filenames. If mode is + '--index-info' is must be a list of values suitable for "git update-index + --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode + is invalid.""" + assert mode in ('--stdin', '--index-info') + cmd = ['git', 'update-index', '--add', '-z', mode] + with temporary_index_file(): + p = subprocess.Popen(cmd, stdin=subprocess.PIPE) + for line in input_lines: + p.stdin.write(to_bytes('%s\0' % line)) + p.stdin.close() + if p.wait() != 0: + die('`%s` failed' % ' '.join(cmd)) + tree_id = run('git', 'write-tree') + return tree_id + + +def clang_format_to_blob(filename, line_ranges, revision=None, + binary='clang-format', style=None): + """Run clang-format on the given file and save the result to a git blob. + + Runs on the file in `revision` if not None, or on the file in the working + directory if `revision` is None. + + Returns the object ID (SHA-1) of the created blob.""" + clang_format_cmd = [binary] + if style: + clang_format_cmd.extend(['-style='+style]) + clang_format_cmd.extend([ + '-lines=%s:%s' % (start_line, start_line+line_count-1) + for start_line, line_count in line_ranges]) + if revision: + clang_format_cmd.extend(['-assume-filename='+filename]) + git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)] + git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + git_show.stdin.close() + clang_format_stdin = git_show.stdout + else: + clang_format_cmd.extend([filename]) + git_show = None + clang_format_stdin = subprocess.PIPE + try: + clang_format = subprocess.Popen(clang_format_cmd, stdin=clang_format_stdin, + stdout=subprocess.PIPE) + if clang_format_stdin == subprocess.PIPE: + clang_format_stdin = clang_format.stdin + except OSError as e: + if e.errno == errno.ENOENT: + die('cannot find executable "%s"' % binary) + else: + raise + clang_format_stdin.close() + hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin'] + hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout, + stdout=subprocess.PIPE) + clang_format.stdout.close() + stdout = hash_object.communicate()[0] + if hash_object.returncode != 0: + die('`%s` failed' % ' '.join(hash_object_cmd)) + if clang_format.wait() != 0: + die('`%s` failed' % ' '.join(clang_format_cmd)) + if git_show and git_show.wait() != 0: + die('`%s` failed' % ' '.join(git_show_cmd)) + return convert_string(stdout).rstrip('\r\n') + + +@contextlib.contextmanager +def temporary_index_file(tree=None): + """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting + the file afterward.""" + index_path = create_temporary_index(tree) + old_index_path = os.environ.get('GIT_INDEX_FILE') + os.environ['GIT_INDEX_FILE'] = index_path + try: + yield + finally: + if old_index_path is None: + del os.environ['GIT_INDEX_FILE'] + else: + os.environ['GIT_INDEX_FILE'] = old_index_path + os.remove(index_path) + + +def create_temporary_index(tree=None): + """Create a temporary index file and return the created file's path. + + If `tree` is not None, use that as the tree to read in. Otherwise, an + empty index is created.""" + gitdir = run('git', 'rev-parse', '--git-dir') + path = os.path.join(gitdir, temp_index_basename) + if tree is None: + tree = '--empty' + run('git', 'read-tree', '--index-output='+path, tree) + return path + + +def print_diff(old_tree, new_tree): + """Print the diff between the two trees to stdout.""" + # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output + # is expected to be viewed by the user, and only the former does nice things + # like color and pagination. + # + # We also only print modified files since `new_tree` only contains the files + # that were modified, so unmodified files would show as deleted without the + # filter. + subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree, + '--']) + + +def apply_changes(old_tree, new_tree, force=False, patch_mode=False): + """Apply the changes in `new_tree` to the working directory. + + Bails if there are local changes in those files and not `force`. If + `patch_mode`, runs `git checkout --patch` to select hunks interactively.""" + changed_files = run('git', 'diff-tree', '--diff-filter=M', '-r', '-z', + '--name-only', old_tree, + new_tree).rstrip('\0').split('\0') + if not force: + unstaged_files = run('git', 'diff-files', '--name-status', *changed_files) + if unstaged_files: + print('The following files would be modified but ' + 'have unstaged changes:', file=sys.stderr) + print(unstaged_files, file=sys.stderr) + print('Please commit, stage, or stash them first.', file=sys.stderr) + sys.exit(2) + if patch_mode: + # In patch mode, we could just as well create an index from the new tree + # and checkout from that, but then the user will be presented with a + # message saying "Discard ... from worktree". Instead, we use the old + # tree as the index and checkout from new_tree, which gives the slightly + # better message, "Apply ... to index and worktree". This is not quite + # right, since it won't be applied to the user's index, but oh well. + with temporary_index_file(old_tree): + subprocess.check_call(['git', 'checkout', '--patch', new_tree]) + index_tree = old_tree + else: + with temporary_index_file(new_tree): + run('git', 'checkout-index', '-a', '-f') + return changed_files + + +def run(*args, **kwargs): + stdin = kwargs.pop('stdin', '') + verbose = kwargs.pop('verbose', True) + strip = kwargs.pop('strip', True) + for name in kwargs: + raise TypeError("run() got an unexpected keyword argument '%s'" % name) + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + stdout, stderr = p.communicate(input=stdin) + + stdout = convert_string(stdout) + stderr = convert_string(stderr) + + if p.returncode == 0: + if stderr: + if verbose: + print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr) + print(stderr.rstrip(), file=sys.stderr) + if strip: + stdout = stdout.rstrip('\r\n') + return stdout + if verbose: + print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr) + if stderr: + print(stderr.rstrip(), file=sys.stderr) + sys.exit(2) + + +def die(message): + print('error:', message, file=sys.stderr) + sys.exit(2) + + +def to_bytes(str_input): + # Encode to UTF-8 to get binary data. + if isinstance(str_input, bytes): + return str_input + return str_input.encode('utf-8') + + +def to_string(bytes_input): + if isinstance(bytes_input, str): + return bytes_input + return bytes_input.encode('utf-8') + + +def convert_string(bytes_input): + try: + return to_string(bytes_input.decode('utf-8')) + except AttributeError: # 'str' object has no attribute 'decode'. + return str(bytes_input) + except UnicodeError: + return str(bytes_input) + +if __name__ == '__main__': + main() diff --git a/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt new file mode 100644 index 00000000000..4b2243c5ceb --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/CMakeLists.txt @@ -0,0 +1,135 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} FuzzMutate) +set(CXX_FLAGS_NOFUZZ ${CMAKE_CXX_FLAGS}) +set(DUMMY_MAIN DummyClangFuzzer.cpp) +if(LLVM_LIB_FUZZING_ENGINE) + unset(DUMMY_MAIN) +elseif(LLVM_USE_SANITIZE_COVERAGE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer") + set(CXX_FLAGS_NOFUZZ "${CXX_FLAGS_NOFUZZ} -fsanitize=fuzzer-no-link") + unset(DUMMY_MAIN) +endif() + +# Needed by LLVM's CMake checks because this file defines multiple targets. +set(LLVM_OPTIONAL_SOURCES + ClangFuzzer.cpp + ClangObjectiveCFuzzer.cpp + DummyClangFuzzer.cpp + ExampleClangProtoFuzzer.cpp + ExampleClangLoopProtoFuzzer.cpp + ExampleClangLLVMProtoFuzzer.cpp + ) + +if(CLANG_ENABLE_PROTO_FUZZER) + # Create protobuf .h and .cc files, and put them in a library for use by + # clang-proto-fuzzer components. + find_package(Protobuf REQUIRED) + add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI) + include_directories(${PROTOBUF_INCLUDE_DIRS}) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS cxx_proto.proto) + protobuf_generate_cpp(LOOP_PROTO_SRCS LOOP_PROTO_HDRS cxx_loop_proto.proto) + set(LLVM_OPTIONAL_SOURCES ${LLVM_OPTIONAL_SOURCES} ${PROTO_SRCS}) + add_clang_library(clangCXXProto + ${PROTO_SRCS} + ${PROTO_HDRS} + + LINK_LIBS + ${PROTOBUF_LIBRARIES} + ) + + add_clang_library(clangCXXLoopProto + ${LOOP_PROTO_SRCS} + ${LOOP_PROTO_HDRS} + + LINK_LIBS + ${PROTOBUF_LIBRARIES} + ) + + # Build and include libprotobuf-mutator + include(ProtobufMutator) + include_directories(${ProtobufMutator_INCLUDE_DIRS}) + + # Build the protobuf->C++ translation library and driver. + add_clang_subdirectory(proto-to-cxx) + + # Build the protobuf->LLVM IR translation library and driver. + add_clang_subdirectory(proto-to-llvm) + + # Build the fuzzer initialization library. + add_clang_subdirectory(fuzzer-initialize) + + # Build the protobuf fuzzer + add_clang_executable(clang-proto-fuzzer + ${DUMMY_MAIN} + ExampleClangProtoFuzzer.cpp + ) + + # Build the loop protobuf fuzzer + add_clang_executable(clang-loop-proto-fuzzer + ${DUMMY_MAIN} + ExampleClangLoopProtoFuzzer.cpp + ) + + # Build the llvm protobuf fuzzer + add_clang_executable(clang-llvm-proto-fuzzer + ${DUMMY_MAIN} + ExampleClangLLVMProtoFuzzer.cpp + ) + + set(COMMON_PROTO_FUZZ_LIBRARIES + ${ProtobufMutator_LIBRARIES} + ${PROTOBUF_LIBRARIES} + ${LLVM_LIB_FUZZING_ENGINE} + clangFuzzerInitialize + ) + + target_link_libraries(clang-proto-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} + clangHandleCXX + clangCXXProto + clangProtoToCXX + ) + target_link_libraries(clang-loop-proto-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} + clangHandleCXX + clangCXXLoopProto + clangLoopProtoToCXX + ) + target_link_libraries(clang-llvm-proto-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} + clangHandleLLVM + clangCXXLoopProto + clangLoopProtoToLLVM + ) + +endif() + +add_clang_subdirectory(handle-cxx) +add_clang_subdirectory(handle-llvm) + +add_clang_executable(clang-fuzzer + EXCLUDE_FROM_ALL + ${DUMMY_MAIN} + ClangFuzzer.cpp + ) + +target_link_libraries(clang-fuzzer + PRIVATE + ${LLVM_LIB_FUZZING_ENGINE} + clangHandleCXX + ) + +add_clang_executable(clang-objc-fuzzer + EXCLUDE_FROM_ALL + ${DUMMY_MAIN} + ClangObjectiveCFuzzer.cpp + ) + +target_link_libraries(clang-objc-fuzzer + PRIVATE + ${LLVM_LIB_FUZZING_ENGINE} + clangHandleCXX + ) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/ClangFuzzer.cpp b/gnu/llvm/clang/tools/clang-fuzzer/ClangFuzzer.cpp new file mode 100644 index 00000000000..3acdec64477 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/ClangFuzzer.cpp @@ -0,0 +1,25 @@ +//===-- ClangFuzzer.cpp - Fuzz Clang --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that runs Clang on a single +/// input. This function is then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "handle-cxx/handle_cxx.h" + +using namespace clang_fuzzer; + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { return 0; } + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + std::string s((const char *)data, size); + HandleCXX(s, "./test.cc", {"-O2"}); + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp b/gnu/llvm/clang/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp new file mode 100644 index 00000000000..908778f3d76 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/ClangObjectiveCFuzzer.cpp @@ -0,0 +1,24 @@ +//===-- ClangObjectiveCFuzzer.cpp - Fuzz Clang ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that runs Clang on a single Objective-C +/// input. This function is then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "handle-cxx/handle_cxx.h" + +using namespace clang_fuzzer; + +extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) { + std::string s(reinterpret_cast<const char *>(data), size); + HandleCXX(s, "./test.m", {"-O2"}); + return 0; +} + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/Dockerfile b/gnu/llvm/clang/tools/clang-fuzzer/Dockerfile new file mode 100644 index 00000000000..1ddf82954e2 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/Dockerfile @@ -0,0 +1,41 @@ +#===- llvm/tools/clang/tools/clang-fuzzer ---------------------------------===// +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===// +# Produces an image that builds clang-proto-fuzzer +FROM ubuntu:16.04 +RUN apt-get update -y +RUN apt-get install -y autoconf automake libtool curl make g++ unzip wget git \ + binutils liblzma-dev libz-dev python-all cmake ninja-build subversion \ + pkg-config docbook2x + +WORKDIR /root + +# Get protobuf +RUN wget -qO- https://github.com/google/protobuf/releases/download/v3.3.0/protobuf-cpp-3.3.0.tar.gz | tar zxf - +RUN cd protobuf-3.3.0 && ./autogen.sh && ./configure && make -j $(nproc) && make check -j $(nproc) && make install && ldconfig +# Get LLVM +RUN svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm +RUN cd llvm/tools && svn co http://llvm.org/svn/llvm-project/cfe/trunk clang -r $(cd ../ && svn info | grep Revision | awk '{print $2}') +RUN cd llvm/projects && svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt -r $(cd ../ && svn info | grep Revision | awk '{print $2}') +# Build plain LLVM (stage 0) +RUN mkdir build0 && cd build0 && cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../llvm && ninja +# Configure instrumented LLVM (stage 1) +RUN mkdir build1 && cd build1 && cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../llvm \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DCMAKE_C_COMPILER=`pwd`/../build0/bin/clang \ + -DCMAKE_CXX_COMPILER=`pwd`/../build0/bin/clang++ \ + -DLLVM_USE_SANITIZE_COVERAGE=YES \ + -DLLVM_USE_SANITIZER=Address -DCLANG_ENABLE_PROTO_FUZZER=ON +# Build the fuzzers +RUN cd build1 && ninja clang-fuzzer +RUN cd build1 && ninja clang-objc-fuzzer +RUN cd build1 && ninja clang-proto-fuzzer +RUN cd build1 && ninja clang-proto-to-cxx +RUN cd build1 && ninja clang-loop-proto-to-cxx +RUN cd build1 && ninja clang-loop-proto-to-llvm +RUN cd build1 && ninja clang-loop-proto-fuzzer +RUN cd build1 && ninja clang-llvm-proto-fuzzer diff --git a/gnu/llvm/clang/tools/clang-fuzzer/DummyClangFuzzer.cpp b/gnu/llvm/clang/tools/clang-fuzzer/DummyClangFuzzer.cpp new file mode 100644 index 00000000000..78fd801438a --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/DummyClangFuzzer.cpp @@ -0,0 +1,20 @@ +//===-- DummyClangFuzzer.cpp - Entry point to sanity check fuzzers --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Provides a main() to build without linking libFuzzer. +// +//===----------------------------------------------------------------------===// +#include "llvm/FuzzMutate/FuzzerCLI.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv); + +int main(int argc, char *argv[]) { + return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput, + LLVMFuzzerInitialize); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp b/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp new file mode 100644 index 00000000000..2401299be1e --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangLLVMProtoFuzzer.cpp @@ -0,0 +1,27 @@ +//===-- ExampleClangLLVMProtoFuzzer.cpp - Fuzz Clang ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that compiles a single LLVM IR string as +/// input and uses libprotobuf-mutator to find new inputs. This function is +/// then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "cxx_loop_proto.pb.h" +#include "fuzzer-initialize/fuzzer_initialize.h" +#include "handle-llvm/handle_llvm.h" +#include "proto-to-llvm/loop_proto_to_llvm.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +using namespace clang_fuzzer; + +DEFINE_BINARY_PROTO_FUZZER(const LoopFunction &input) { + auto S = LoopFunctionToLLVMString(input); + HandleLLVM(S, GetCLArgs()); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangLoopProtoFuzzer.cpp b/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangLoopProtoFuzzer.cpp new file mode 100644 index 00000000000..a9a03f1f46e --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangLoopProtoFuzzer.cpp @@ -0,0 +1,29 @@ +//===-- ExampleClangLoopProtoFuzzer.cpp - Fuzz Clang ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that runs Clang on a single +/// input and uses libprotobuf-mutator to find new inputs. This function is +/// then linked into the Fuzzer library. This file differs from +/// ExampleClangProtoFuzzer in that it uses a different protobuf that includes +/// C++ code with a single for loop. +/// +//===----------------------------------------------------------------------===// + +#include "cxx_loop_proto.pb.h" +#include "fuzzer-initialize/fuzzer_initialize.h" +#include "handle-cxx/handle_cxx.h" +#include "proto-to-cxx/proto_to_cxx.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +using namespace clang_fuzzer; + +DEFINE_BINARY_PROTO_FUZZER(const LoopFunction &input) { + auto S = LoopFunctionToString(input); + HandleCXX(S, GetCLArgs()); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp b/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp new file mode 100644 index 00000000000..1d9636e6f60 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/ExampleClangProtoFuzzer.cpp @@ -0,0 +1,27 @@ +//===-- ExampleClangProtoFuzzer.cpp - Fuzz Clang --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a function that runs Clang on a single +/// input and uses libprotobuf-mutator to find new inputs. This function is +/// then linked into the Fuzzer library. +/// +//===----------------------------------------------------------------------===// + +#include "cxx_proto.pb.h" +#include "handle-cxx/handle_cxx.h" +#include "proto-to-cxx/proto_to_cxx.h" +#include "fuzzer-initialize/fuzzer_initialize.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +using namespace clang_fuzzer; + +DEFINE_BINARY_PROTO_FUZZER(const Function& input) { + auto S = FunctionToString(input); + HandleCXX(S, "./test.cc", GetCLArgs()); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/README.txt b/gnu/llvm/clang/tools/clang-fuzzer/README.txt new file mode 100644 index 00000000000..eec4a9efdfc --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/README.txt @@ -0,0 +1,151 @@ +This directory contains three utilities for fuzzing Clang: clang-fuzzer, +clang-objc-fuzzer, and clang-proto-fuzzer. All use libFuzzer to generate inputs +to clang via coverage-guided mutation. + +The three utilities differ, however, in how they structure inputs to Clang. +clang-fuzzer makes no attempt to generate valid C++ programs and is therefore +primarily useful for stressing the surface layers of Clang (i.e. lexer, parser). + +clang-objc-fuzzer is similar but for Objective-C: it makes no attempt to +generate a valid Objective-C program. + +clang-proto-fuzzer uses a protobuf class to describe a subset of the C++ +language and then uses libprotobuf-mutator to mutate instantiations of that +class, producing valid C++ programs in the process. As a result, +clang-proto-fuzzer is better at stressing deeper layers of Clang and LLVM. + +Some of the fuzzers have example corpuses inside the corpus_examples directory. + +=================================== + Building clang-fuzzer +=================================== +Within your LLVM build directory, run CMake with the following variable +definitions: +- CMAKE_C_COMPILER=clang +- CMAKE_CXX_COMPILER=clang++ +- LLVM_USE_SANITIZE_COVERAGE=YES +- LLVM_USE_SANITIZER=Address + +Then build the clang-fuzzer target. + +Example: + cd $LLVM_SOURCE_DIR + mkdir build && cd build + cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_SANITIZE_COVERAGE=YES -DLLVM_USE_SANITIZER=Address + ninja clang-fuzzer + +====================== + Running clang-fuzzer +====================== + bin/clang-fuzzer CORPUS_DIR + + +=================================== + Building clang-objc-fuzzer +=================================== +Within your LLVM build directory, run CMake with the following variable +definitions: +- CMAKE_C_COMPILER=clang +- CMAKE_CXX_COMPILER=clang++ +- LLVM_USE_SANITIZE_COVERAGE=YES +- LLVM_USE_SANITIZER=Address + +Then build the clang-objc-fuzzer target. + +Example: + cd $LLVM_SOURCE_DIR + mkdir build && cd build + cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_SANITIZE_COVERAGE=YES -DLLVM_USE_SANITIZER=Address + ninja clang-objc-fuzzer + +====================== + Running clang-objc-fuzzer +====================== + bin/clang-objc-fuzzer CORPUS_DIR + +e.g. using the example objc corpus, + + bin/clang-objc-fuzzer <path to corpus_examples/objc> <path to new directory to store corpus findings> + + +======================================================= + Building clang-proto-fuzzer (Linux-only instructions) +======================================================= +Install the necessary dependencies: +- binutils // needed for libprotobuf-mutator +- liblzma-dev // needed for libprotobuf-mutator +- libz-dev // needed for libprotobuf-mutator +- docbook2x // needed for libprotobuf-mutator +- Recent version of protobuf [3.3.0 is known to work] + +Within your LLVM build directory, run CMake with the following variable +definitions: +- CMAKE_C_COMPILER=clang +- CMAKE_CXX_COMPILER=clang++ +- LLVM_USE_SANITIZE_COVERAGE=YES +- LLVM_USE_SANITIZER=Address +- CLANG_ENABLE_PROTO_FUZZER=ON + +Then build the clang-proto-fuzzer and clang-proto-to-cxx targets. Optionally, +you may also build clang-fuzzer with this setup. + +Example: + cd $LLVM_SOURCE_DIR + mkdir build && cd build + cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DLLVM_USE_SANITIZE_COVERAGE=YES -DLLVM_USE_SANITIZER=Address \ + -DCLANG_ENABLE_PROTO_FUZZER=ON + ninja clang-proto-fuzzer clang-proto-to-cxx + +This directory also contains a Dockerfile which sets up all required +dependencies and builds the fuzzers. + +============================ + Running clang-proto-fuzzer +============================ + bin/clang-proto-fuzzer CORPUS_DIR + +Arguments can be specified after -ignore_remaining_args=1 to modify the compiler +invocation. For example, the following command line will fuzz LLVM with a +custom optimization level and target triple: + bin/clang-proto-fuzzer CORPUS_DIR -ignore_remaining_args=1 -O3 -triple \ + arm64apple-ios9 + +To translate a clang-proto-fuzzer corpus output to C++: + bin/clang-proto-to-cxx CORPUS_OUTPUT_FILE + +=================== + llvm-proto-fuzzer +=================== +Like, clang-proto-fuzzer, llvm-proto-fuzzer is also a protobuf-mutator based +fuzzer. It receives as input a cxx_loop_proto which it then converts into a +string of valid LLVM IR: a function with either a single loop or two nested +loops. It then creates a new string of IR by running optimization passes over +the original IR. Currently, it only runs a loop-vectorize pass but more passes +can easily be added to the fuzzer. Once there are two versions of the input +function (optimized and not), llvm-proto-fuzzer uses LLVM's JIT Engine to +compile both functions. Lastly, it runs both functions on a suite of inputs and +checks that both functions behave the same on all inputs. In this way, +llvm-proto-fuzzer can find not only compiler crashes, but also miscompiles +originating from LLVM's optimization passes. + +llvm-proto-fuzzer is built very similarly to clang-proto-fuzzer. You can run the +fuzzer with the following command: + bin/clang-llvm-proto-fuzzer CORPUS_DIR + +To translate a cxx_loop_proto file into LLVM IR do: + bin/clang-loop-proto-to-llvm CORPUS_OUTPUT_FILE +To translate a cxx_loop_proto file into C++ do: + bin/clang-loop-proto-to-cxx CORPUS_OUTPUT_FILE + +Note: To get a higher number of executions per second with llvm-proto-fuzzer it +helps to build it without ASan instrumentation and with the -O2 flag. Because +the fuzzer is not only compiling code, but also running it, as the inputs get +large, the time necessary to fuzz one input can get very high. +Example: + cmake .. -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ \ + -DCLANG_ENABLE_PROTO_FUZZER=ON -DLLVM_USE_SANITIZE_COVERAGE=YES \ + -DCMAKE_CXX_FLAGS="-O2" + ninja clang-llvm-proto-fuzzer clang-loop-proto-to-llvm diff --git a/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m new file mode 100644 index 00000000000..600feb59f53 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/BasicClass.m @@ -0,0 +1,29 @@ +@interface RootObject +@end + +@interface BasicClass : RootObject { + int _foo; + char _boolean; +} + +@property(nonatomic, assign) int bar; +@property(atomic, retain) id objectField; +@property(nonatomic, assign) id delegate; + +- (void)someMethod; +@end + +@implementation BasicClass + +@synthesize bar = _bar; +@synthesize objectField = _objectField; +@synthesize delegate = _delegate; + +- (void)someMethod { + int value = self.bar; + _foo = (_boolean != 0) ? self.bar : [self.objectField bar]; + [self setBar:value]; + id obj = self.objectField; +} +@end + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m new file mode 100644 index 00000000000..b235adfc223 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/ClassCategory.m @@ -0,0 +1,20 @@ +@interface RootObject +@end + +@interface BaseClass : RootObject +@property(atomic, assign, readonly) int field; +@end + +@interface BaseClass(Private) +@property(atomic, assign, readwrite) int field; + +- (int)something; +@end + +@implementation BaseClass +- (int)something { + self.field = self.field + 1; + return self.field; +} +@end + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m new file mode 100644 index 00000000000..d1d3c95607c --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/ClassExtension.m @@ -0,0 +1,20 @@ +@interface RootObject +@end + +@interface BaseClass : RootObject +@end + +@interface BaseClass() { + int _field1; +} +@property(atomic, assign, readonly) int field2; + +- (int)addFields; +@end + +@implementation BaseClass +- (int)addFields { + return self->_field1 + [self field2]; +} +@end + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m new file mode 100644 index 00000000000..282baffc58f --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/corpus_examples/objc/SharedInstance.m @@ -0,0 +1,34 @@ +@interface RootObject ++ (instancetype)alloc; + +- (instancetype)init; +@end + +@interface BaseClass : RootObject ++ (instancetype)sharedInstance; + +- (instancetype)initWithFoo:(int)foo; +@end + +static BaseClass *sharedInstance = (void *)0; +static int counter = 0; + +@implementation BaseClass ++ (instancetype)sharedInstance { + if (sharedInstance) { + return sharedInstance; + } + sharedInstance = [[BaseClass alloc] initWithFoo:3]; + return sharedInstance; +} + + +- (instancetype)initWithFoo:(int)foo { + self = [super init]; + if (self) { + counter += foo; + } + return self; +} +@end + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/cxx_loop_proto.proto b/gnu/llvm/clang/tools/clang-fuzzer/cxx_loop_proto.proto new file mode 100644 index 00000000000..360042d382c --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/cxx_loop_proto.proto @@ -0,0 +1,81 @@ +//===-- cxx_loop_proto.proto - Protobuf description of C++ with for loops -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes a subset of C++ as a protobuf. It is used to +/// more easily find interesting inputs for fuzzing LLVM's vectorizer. +/// This subset differs from the one defined in cxx_proto.proto by eliminating +/// while loops and conditionals. The goal is that the C++ code generated will +/// be more likely to stress the LLVM loop vectorizer. The code generated will +/// contain either a single loop or two nested loops. +/// +//===----------------------------------------------------------------------===// + +syntax = "proto2"; + +message Const { + required int32 val = 1; +} + +message VarRef { + // Add an enum for each array in function signature + enum Arr { + ARR_A = 0; + ARR_B = 1; + ARR_C = 2; + }; + required Arr arr = 1; +} + +message BinaryOp { + enum Op { + PLUS = 0; + MINUS = 1; + MUL = 2; + XOR = 3; + AND = 4; + OR = 5; + EQ = 6; + NE = 7; + LE = 8; + GE = 9; + LT = 10; + GT = 11; + }; + required Op op = 1; + required Rvalue left = 2; + required Rvalue right = 3; +} + +message Rvalue { + oneof rvalue_oneof { + Const cons = 1; + BinaryOp binop = 2; + VarRef varref = 3; + } +} + +message AssignmentStatement { + required VarRef varref = 1; + required Rvalue rvalue = 2; +} + +message Statement { + required AssignmentStatement assignment = 1; +} + +message StatementSeq { + repeated Statement statements = 1; +} + +message LoopFunction { + optional StatementSeq inner_statements = 1; + required StatementSeq outer_statements = 2; +} + +package clang_fuzzer; diff --git a/gnu/llvm/clang/tools/clang-fuzzer/cxx_proto.proto b/gnu/llvm/clang/tools/clang-fuzzer/cxx_proto.proto new file mode 100644 index 00000000000..eaf69d18443 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/cxx_proto.proto @@ -0,0 +1,92 @@ +//===-- cxx_proto.proto - Protobuf description of C++ ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes a subset of C++ as a protobuf. It is used to +/// more easily find interesting inputs for fuzzing Clang. +/// +//===----------------------------------------------------------------------===// + +syntax = "proto2"; + +message VarRef { + required int32 varnum = 1; +} + +message Lvalue { + required VarRef varref = 1; +} + +message Const { + required int32 val = 1; +} + +message BinaryOp { + enum Op { + PLUS = 0; + MINUS = 1; + MUL = 2; + DIV = 3; + MOD = 4; + XOR = 5; + AND = 6; + OR = 7; + EQ = 8; + NE = 9; + LE = 10; + GE = 11; + LT = 12; + GT = 13; + }; + required Op op = 1; + required Rvalue left = 2; + required Rvalue right = 3; +} + +message Rvalue { + oneof rvalue_oneof { + VarRef varref = 1; + Const cons = 2; + BinaryOp binop = 3; + } +} + +message AssignmentStatement { + required Lvalue lvalue = 1; + required Rvalue rvalue = 2; +} + + +message IfElse { + required Rvalue cond = 1; + required StatementSeq if_body = 2; + required StatementSeq else_body = 3; +} + +message While { + required Rvalue cond = 1; + required StatementSeq body = 2; +} + +message Statement { + oneof stmt_oneof { + AssignmentStatement assignment = 1; + IfElse ifelse = 2; + While while_loop = 3; + } +} + +message StatementSeq { + repeated Statement statements = 1; +} + +message Function { + required StatementSeq statements = 1; +} + +package clang_fuzzer; diff --git a/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/CMakeLists.txt new file mode 100644 index 00000000000..c149fb3d4b3 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/CMakeLists.txt @@ -0,0 +1,3 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support) + +add_clang_library(clangFuzzerInitialize fuzzer_initialize.cpp) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp new file mode 100644 index 00000000000..20cf98896e2 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.cpp @@ -0,0 +1,64 @@ +//===-- fuzzer_initialize.cpp - Fuzz Clang --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements two functions: one that returns the command line +/// arguments for a given call to the fuzz target and one that initializes +/// the fuzzer with the correct command line arguments. +/// +//===----------------------------------------------------------------------===// + +#include "fuzzer_initialize.h" + +#include "llvm/InitializePasses.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include <cstring> + +using namespace clang_fuzzer; +using namespace llvm; + + +namespace clang_fuzzer { + +static std::vector<const char *> CLArgs; + +const std::vector<const char *>& GetCLArgs() { + return CLArgs; +} + +} + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmPrinters(); + InitializeAllAsmParsers(); + + PassRegistry &Registry = *PassRegistry::getPassRegistry(); + initializeCore(Registry); + initializeScalarOpts(Registry); + initializeVectorization(Registry); + initializeIPO(Registry); + initializeAnalysis(Registry); + initializeTransformUtils(Registry); + initializeInstCombine(Registry); + initializeAggressiveInstCombine(Registry); + initializeInstrumentation(Registry); + initializeTarget(Registry); + + CLArgs.push_back("-O2"); + for (int I = 1; I < *argc; I++) { + if (strcmp((*argv)[I], "-ignore_remaining_args=1") == 0) { + for (I++; I < *argc; I++) + CLArgs.push_back((*argv)[I]); + break; + } + } + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.h b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.h new file mode 100644 index 00000000000..9f32d6396bf --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/fuzzer-initialize/fuzzer_initialize.h @@ -0,0 +1,18 @@ +//==-- fuzzer_initialize.h - Fuzz Clang ------------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines a function that returns the command line arguments for a specific +// call to the fuzz target. +// +//===----------------------------------------------------------------------===// + +#include <vector> + +namespace clang_fuzzer { +const std::vector<const char *>& GetCLArgs(); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt new file mode 100644 index 00000000000..6d62421d9a6 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Support) + +add_clang_library(clangHandleCXX + handle_cxx.cpp + + LINK_LIBS + clangBasic + clangCodeGen + clangFrontend + clangLex + clangSerialization + clangTooling + ) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp new file mode 100644 index 00000000000..32d351f4c3e --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp @@ -0,0 +1,52 @@ +//==-- handle_cxx.cpp - Helper function for Clang fuzzers ------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements HandleCXX for use by the Clang fuzzers. +// +//===----------------------------------------------------------------------===// + +#include "handle_cxx.h" + +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Option/Option.h" + +using namespace clang; + +void clang_fuzzer::HandleCXX(const std::string &S, + const char *FileName, + const std::vector<const char *> &ExtraArgs) { + llvm::opt::ArgStringList CC1Args; + CC1Args.push_back("-cc1"); + for (auto &A : ExtraArgs) + CC1Args.push_back(A); + CC1Args.push_back(FileName); + + llvm::IntrusiveRefCntPtr<FileManager> Files( + new FileManager(FileSystemOptions())); + IgnoringDiagConsumer Diags; + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<clang::DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts, + &Diags, false); + std::unique_ptr<clang::CompilerInvocation> Invocation( + tooling::newInvocation(&Diagnostics, CC1Args)); + std::unique_ptr<llvm::MemoryBuffer> Input = + llvm::MemoryBuffer::getMemBuffer(S); + Invocation->getPreprocessorOpts().addRemappedFile(FileName, + Input.release()); + std::unique_ptr<tooling::ToolAction> action( + tooling::newFrontendActionFactory<clang::EmitObjAction>()); + std::shared_ptr<PCHContainerOperations> PCHContainerOps = + std::make_shared<PCHContainerOperations>(); + action->runInvocation(std::move(Invocation), Files.get(), PCHContainerOps, + &Diags); +} + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h new file mode 100644 index 00000000000..2126ae9ea37 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h @@ -0,0 +1,25 @@ +//==-- handle_cxx.h - Helper function for Clang fuzzers --------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines HandleCXX for use by the Clang fuzzers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_CXX_HANDLECXX_H +#define LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_CXX_HANDLECXX_H + +#include <string> +#include <vector> + +namespace clang_fuzzer { +void HandleCXX(const std::string &S, + const char *FileName, + const std::vector<const char *> &ExtraArgs); +} // namespace clang_fuzzer + +#endif diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt new file mode 100644 index 00000000000..47f9fdf68f4 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/CMakeLists.txt @@ -0,0 +1,30 @@ +set(LLVM_LINK_COMPONENTS + Analysis + CodeGen + Core + ExecutionEngine + IPO + IRReader + MC + MCJIT + Object + RuntimeDyld + SelectionDAG + Support + Target + TransformUtils + native +) + +# Depend on LLVM IR intrinsic generation. +set(handle_llvm_deps intrinsics_gen) +if (CLANG_BUILT_STANDALONE) + set(handle_llvm_deps) +endif() + +add_clang_library(clangHandleLLVM + handle_llvm.cpp + + DEPENDS + ${handle_llvm_deps} + ) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp new file mode 100644 index 00000000000..d8ab1459418 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.cpp @@ -0,0 +1,219 @@ +//==-- handle_llvm.cpp - Helper function for Clang fuzzers -----------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements HandleLLVM for use by the Clang fuzzers. First runs a loop +// vectorizer optimization pass over the given IR code. Then mimics lli on both +// versions to JIT the generated code and execute it. Currently, functions are +// executed on dummy inputs. +// +//===----------------------------------------------------------------------===// + +#include "handle_llvm.h" +#include "input_arrays.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/CommandFlags.inc" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/ObjectCache.h" +#include "llvm/ExecutionEngine/RTDyldMemoryManager.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/IRPrintingPasses.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/LegacyPassNameParser.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/Vectorize.h" + +using namespace llvm; + +// Define a type for the functions that are compiled and executed +typedef void (*LLVMFunc)(int*, int*, int*, int); + +// Helper function to parse command line args and find the optimization level +static void getOptLevel(const std::vector<const char *> &ExtraArgs, + CodeGenOpt::Level &OLvl) { + // Find the optimization level from the command line args + OLvl = CodeGenOpt::Default; + for (auto &A : ExtraArgs) { + if (A[0] == '-' && A[1] == 'O') { + switch(A[2]) { + case '0': OLvl = CodeGenOpt::None; break; + case '1': OLvl = CodeGenOpt::Less; break; + case '2': OLvl = CodeGenOpt::Default; break; + case '3': OLvl = CodeGenOpt::Aggressive; break; + default: + errs() << "error: opt level must be between 0 and 3.\n"; + std::exit(1); + } + } + } +} + +static void ErrorAndExit(std::string message) { + errs()<< "ERROR: " << message << "\n"; + std::exit(1); +} + +// Helper function to add optimization passes to the TargetMachine at the +// specified optimization level, OptLevel +static void AddOptimizationPasses(legacy::PassManagerBase &MPM, + CodeGenOpt::Level OptLevel, + unsigned SizeLevel) { + // Create and initialize a PassManagerBuilder + PassManagerBuilder Builder; + Builder.OptLevel = OptLevel; + Builder.SizeLevel = SizeLevel; + Builder.Inliner = createFunctionInliningPass(OptLevel, SizeLevel, false); + Builder.LoopVectorize = true; + Builder.populateModulePassManager(MPM); +} + +// Mimics the opt tool to run an optimization pass over the provided IR +static std::string OptLLVM(const std::string &IR, CodeGenOpt::Level OLvl) { + // Create a module that will run the optimization passes + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context); + if (!M || verifyModule(*M, &errs())) + ErrorAndExit("Could not parse IR"); + + Triple ModuleTriple(M->getTargetTriple()); + const TargetOptions Options = InitTargetOptionsFromCodeGenFlags(); + std::string E; + const Target *TheTarget = TargetRegistry::lookupTarget(MArch, ModuleTriple, E); + TargetMachine *Machine = + TheTarget->createTargetMachine(M->getTargetTriple(), getCPUStr(), + getFeaturesStr(), Options, getRelocModel(), + getCodeModel(), OLvl); + std::unique_ptr<TargetMachine> TM(Machine); + setFunctionAttributes(getCPUStr(), getFeaturesStr(), *M); + + legacy::PassManager Passes; + + Passes.add(new TargetLibraryInfoWrapperPass(ModuleTriple)); + Passes.add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); + + LLVMTargetMachine <M = static_cast<LLVMTargetMachine &>(*TM); + Passes.add(LTM.createPassConfig(Passes)); + + Passes.add(createVerifierPass()); + + AddOptimizationPasses(Passes, OLvl, 0); + + // Add a pass that writes the optimized IR to an output stream + std::string outString; + raw_string_ostream OS(outString); + Passes.add(createPrintModulePass(OS, "", false)); + + Passes.run(*M); + + return OS.str(); +} + +// Takes a function and runs it on a set of inputs +// First determines whether f is the optimized or unoptimized function +static void RunFuncOnInputs(LLVMFunc f, int Arr[kNumArrays][kArraySize]) { + for (int i = 0; i < kNumArrays / 3; i++) + f(Arr[i], Arr[i + (kNumArrays / 3)], Arr[i + (2 * kNumArrays / 3)], + kArraySize); +} + +// Takes a string of IR and compiles it using LLVM's JIT Engine +static void CreateAndRunJITFunc(const std::string &IR, CodeGenOpt::Level OLvl) { + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr<Module> M = parseIR(MemoryBufferRef(IR, "IR"), Err, Context); + if (!M) + ErrorAndExit("Could not parse IR"); + + Function *EntryFunc = M->getFunction("foo"); + if (!EntryFunc) + ErrorAndExit("Function not found in module"); + + std::string ErrorMsg; + EngineBuilder builder(std::move(M)); + builder.setMArch(MArch); + builder.setMCPU(getCPUStr()); + builder.setMAttrs(getFeatureList()); + builder.setErrorStr(&ErrorMsg); + builder.setEngineKind(EngineKind::JIT); + builder.setMCJITMemoryManager(std::make_unique<SectionMemoryManager>()); + builder.setOptLevel(OLvl); + builder.setTargetOptions(InitTargetOptionsFromCodeGenFlags()); + + std::unique_ptr<ExecutionEngine> EE(builder.create()); + if (!EE) + ErrorAndExit("Could not create execution engine"); + + EE->finalizeObject(); + EE->runStaticConstructorsDestructors(false); + +#if defined(__GNUC__) && !defined(__clang) && \ + ((__GNUC__ == 4) && (__GNUC_MINOR__ < 9)) +// Silence +// +// warning: ISO C++ forbids casting between pointer-to-function and +// pointer-to-object [-Wpedantic] +// +// Since C++11 this casting is conditionally supported and GCC versions +// starting from 4.9.0 don't warn about the cast. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#endif + LLVMFunc f = reinterpret_cast<LLVMFunc>(EE->getPointerToFunction(EntryFunc)); +#if defined(__GNUC__) && !defined(__clang) && \ + ((__GNUC__ == 4) && (__GNUC_MINOR__ < 9)) +#pragma GCC diagnostic pop +#endif + + // Figure out if we are running the optimized func or the unoptimized func + RunFuncOnInputs(f, (OLvl == CodeGenOpt::None) ? UnoptArrays : OptArrays); + + EE->runStaticConstructorsDestructors(true); +} + +// Main fuzz target called by ExampleClangLLVMProtoFuzzer.cpp +// Mimics the lli tool to JIT the LLVM IR code and execute it +void clang_fuzzer::HandleLLVM(const std::string &IR, + const std::vector<const char *> &ExtraArgs) { + // Populate OptArrays and UnoptArrays with the arrays from InputArrays + memcpy(OptArrays, InputArrays, kTotalSize); + memcpy(UnoptArrays, InputArrays, kTotalSize); + + // Parse ExtraArgs to set the optimization level + CodeGenOpt::Level OLvl; + getOptLevel(ExtraArgs, OLvl); + + // First we optimize the IR by running a loop vectorizer pass + std::string OptIR = OptLLVM(IR, OLvl); + + CreateAndRunJITFunc(OptIR, OLvl); + CreateAndRunJITFunc(IR, CodeGenOpt::None); + + if (memcmp(OptArrays, UnoptArrays, kTotalSize)) + ErrorAndExit("!!!BUG!!!"); + + return; +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.h b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.h new file mode 100644 index 00000000000..36e1a5f1aa1 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/handle_llvm.h @@ -0,0 +1,24 @@ +//==-- handle_llvm.h - Helper function for Clang fuzzers -------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines HandleLLVM for use by the Clang fuzzers. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_LLVM_HANDLELLVM_H +#define LLVM_CLANG_TOOLS_CLANG_FUZZER_HANDLE_LLVM_HANDLELLVM_H + +#include <string> +#include <vector> + +namespace clang_fuzzer { +void HandleLLVM(const std::string &S, + const std::vector<const char *> &ExtraArgs); +} // namespace clang_fuzzer + +#endif diff --git a/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/input_arrays.h b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/input_arrays.h new file mode 100644 index 00000000000..4b03d01babc --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/handle-llvm/input_arrays.h @@ -0,0 +1,117 @@ +//==-- input_arrays.h - Helper function for LLVM fuzzer inputs -------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Define a few static variables used by the LLVM Proto Fuzzer +// +//===----------------------------------------------------------------------===// + +#include <climits> + +static const int kArraySize = 64; +static const int kNumArrays = 93; +static const int kTotalSize = sizeof(int) * kArraySize * kNumArrays; + +// Define two arrays that will hold the input and output for the two functions +static int OptArrays[kNumArrays][kArraySize]; +static int UnoptArrays[kNumArrays][kArraySize]; + +// Define a corpus of possible inputs +static int InputArrays[kNumArrays][kArraySize] = +{ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX}, + {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024}, + {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 0, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}, + {-1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023}, + {INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX}, + {0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, }, + {1, 1, 0, 3, 2, 5, 6, 2, 6, 3, 0, 19, 18, 17, 16, 0, 13, 11, 9, 7, 5, 3, 1, -3000, -3000, -3000, -3000, -3000, -3000, -3000, 0, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000}, + {1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824}, + {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, + {0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, }, + {-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0}, + {1, 1, 2, 1, 6, 9, 10, 7, 2, 10, 4, 11, 10, 11, 0, 19, 26, 18, 10, 2, 14, 7, 0, 39, 34, 29, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91}, + {0, 0, 2, 0, 0, 1, 6, 9, 4, 12, 6, 13, 12, 0, 2, 21, 28, 20, 12, 4, 16, 9, 2, 41, 36, 31, 26, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93}, + {1, 1, 0, 2, 2, 3, 8, 11, 6, 14, 8, 15, 14, 2, 4, 23, 30, 22, 14, 6, 18, 11, 4, 43, 38, 33, 28, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95}, + {0, 0, 0, 4, 0, 0, 10, 13, 0, 16, 0, 17, 16, 4, 6, 25, 16, 24, 16, 8, 0, 13, 6, 45, 40, 35, 30, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97}, + {1, 1, 2, 3, 2, 2, 0, 1, 2, 9, 2, 19, 18, 6, 8, 27, 18, 26, 18, 10, 2, 15, 8, 1, 42, 37, 32, 27, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 107, 106, 105, 104, 103, 102, 101, 100, 99}, + {0, 0, 2, 5, 4, 4, 2, 3, 4, 11, 4, 21, 20, 8, 10, 29, 20, 28, 20, 12, 4, 17, 10, 3, 44, 39, 34, 29, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 109, 108, 107, 106, 105, 104, 103, 102, 101}, + {1, 1, 0, 1, 6, 6, 4, 5, 6, 13, 6, 1, 22, 10, 12, 1, 22, 30, 22, 14, 6, 19, 12, 5, 46, 41, 36, 31, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 110, 109, 108, 107, 106, 105, 104, 103}, + {0, 0, 0, 0, 4, 8, 0, 0, 8, 15, 8, 3, 12, 12, 0, 3, 24, 32, 24, 16, 8, 0, 14, 7, 0, 43, 38, 33, 28, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 111, 110, 109, 108, 107, 106, 105}, + {1, 1, 2, 2, 6, 5, 2, 2, 10, 17, 10, 5, 14, 14, 2, 5, 26, 17, 26, 18, 10, 2, 16, 9, 2, 45, 40, 35, 30, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 113, 112, 111, 110, 109, 108, 107}, + {0, 0, 2, 4, 0, 7, 4, 4, 12, 1, 12, 7, 16, 16, 4, 7, 28, 19, 28, 20, 12, 4, 18, 11, 4, 47, 42, 37, 32, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 114, 113, 112, 111, 110, 109}, + {1, 1, 0, 3, 2, 9, 6, 6, 14, 3, 14, 9, 18, 18, 6, 9, 30, 21, 30, 22, 14, 6, 20, 13, 6, 49, 44, 39, 34, 29, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 115, 114, 113, 112, 111}, + {1, 1, 0, 3, 2, 5, 6, 2, 6, 3, 0, 19, 18, 17, 16, 0, 13, 11, 9, 7, 5, 3, 1, -3000, -3000, -3000, -3000, -3000, -3000, -3000, 0, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000}, + {1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824}, + {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, + {0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, }, + {-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0}, + {1, 1, 2, 1, 6, 9, 10, 7, 2, 10, 4, 11, 10, 11, 0, 19, 26, 18, 10, 2, 14, 7, 0, 39, 34, 29, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91}, + {0, 0, 2, 0, 0, 1, 6, 9, 4, 12, 6, 13, 12, 0, 2, 21, 28, 20, 12, 4, 16, 9, 2, 41, 36, 31, 26, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93}, + {1, 1, 0, 2, 2, 3, 8, 11, 6, 14, 8, 15, 14, 2, 4, 23, 30, 22, 14, 6, 18, 11, 4, 43, 38, 33, 28, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95}, + {0, 0, 0, 4, 0, 0, 10, 13, 0, 16, 0, 17, 16, 4, 6, 25, 16, 24, 16, 8, 0, 13, 6, 45, 40, 35, 30, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97}, + {1, 1, 2, 3, 2, 2, 0, 1, 2, 9, 2, 19, 18, 6, 8, 27, 18, 26, 18, 10, 2, 15, 8, 1, 42, 37, 32, 27, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 107, 106, 105, 104, 103, 102, 101, 100, 99}, + {0, 0, 2, 5, 4, 4, 2, 3, 4, 11, 4, 21, 20, 8, 10, 29, 20, 28, 20, 12, 4, 17, 10, 3, 44, 39, 34, 29, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 109, 108, 107, 106, 105, 104, 103, 102, 101}, + {1, 1, 0, 1, 6, 6, 4, 5, 6, 13, 6, 1, 22, 10, 12, 1, 22, 30, 22, 14, 6, 19, 12, 5, 46, 41, 36, 31, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 110, 109, 108, 107, 106, 105, 104, 103}, + {0, 0, 0, 0, 4, 8, 0, 0, 8, 15, 8, 3, 12, 12, 0, 3, 24, 32, 24, 16, 8, 0, 14, 7, 0, 43, 38, 33, 28, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 111, 110, 109, 108, 107, 106, 105}, + {1, 1, 2, 2, 6, 5, 2, 2, 10, 17, 10, 5, 14, 14, 2, 5, 26, 17, 26, 18, 10, 2, 16, 9, 2, 45, 40, 35, 30, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 113, 112, 111, 110, 109, 108, 107}, + {0, 0, 2, 4, 0, 7, 4, 4, 12, 1, 12, 7, 16, 16, 4, 7, 28, 19, 28, 20, 12, 4, 18, 11, 4, 47, 42, 37, 32, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 114, 113, 112, 111, 110, 109}, + {1, 1, 0, 3, 2, 9, 6, 6, 14, 3, 14, 9, 18, 18, 6, 9, 30, 21, 30, 22, 14, 6, 20, 13, 6, 49, 44, 39, 34, 29, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 115, 114, 113, 112, 111}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX}, + {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024}, + {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 0, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}, + {-1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023}, + {INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX}, + {0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, }, + {0, 0, 2, 0, 0, 1, 6, 9, 4, 12, 6, 13, 12, 0, 2, 21, 28, 20, 12, 4, 16, 9, 2, 41, 36, 31, 26, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 0, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535}, + {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX}, + {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, + {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, + {0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, 0, -1, -2, -4, -8, -16, -32, -64, -128, -256, -512, -1024, -2048, -4096, -8192, -16384, }, + {1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, }, + {1, 1, 0, 3, 2, 5, 6, 2, 6, 3, 0, 19, 18, 17, 16, 0, 13, 11, 9, 7, 5, 3, 1, -3000, -3000, -3000, -3000, -3000, -3000, -3000, 0, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000, -3000}, + {0, 0, 2, 5, 4, 4, 2, 3, 4, 11, 4, 21, 20, 8, 10, 29, 20, 28, 20, 12, 4, 17, 10, 3, 44, 39, 34, 29, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 109, 108, 107, 106, 105, 104, 103, 102, 101}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, + {-0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0, -0}, + {1, 1, 2, 1, 6, 9, 10, 7, 2, 10, 4, 11, 10, 11, 0, 19, 26, 18, 10, 2, 14, 7, 0, 39, 34, 29, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91}, + {0, 0, 2, 4, 0, 7, 4, 4, 12, 1, 12, 7, 16, 16, 4, 7, 28, 19, 28, 20, 12, 4, 18, 11, 4, 47, 42, 37, 32, 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 114, 113, 112, 111, 110, 109}, + {1, 1, 0, 2, 2, 3, 8, 11, 6, 14, 8, 15, 14, 2, 4, 23, 30, 22, 14, 6, 18, 11, 4, 43, 38, 33, 28, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN}, + {0, 0, 0, 4, 0, 0, 10, 13, 0, 16, 0, 17, 16, 4, 6, 25, 16, 24, 16, 8, 0, 13, 6, 45, 40, 35, 30, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97}, + {1, 1, 2, 3, 2, 2, 0, 1, 2, 9, 2, 19, 18, 6, 8, 27, 18, 26, 18, 10, 2, 15, 8, 1, 42, 37, 32, 27, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 107, 106, 105, 104, 103, 102, 101, 100, 99}, + {1, 1, 0, 1, 6, 6, 4, 5, 6, 13, 6, 1, 22, 10, 12, 1, 22, 30, 22, 14, 6, 19, 12, 5, 46, 41, 36, 31, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 40, 37, 34, 31, 28, 25, 22, 19, 16, 13, 10, 7, 4, 1, 110, 109, 108, 107, 106, 105, 104, 103}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {-1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023, -1023}, + {0, 0, 0, 0, 4, 8, 0, 0, 8, 15, 8, 3, 12, 12, 0, 3, 24, 32, 24, 16, 8, 0, 14, 7, 0, 43, 38, 33, 28, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 111, 110, 109, 108, 107, 106, 105}, + {1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824, 1073741824}, + {1, 1, 2, 2, 6, 5, 2, 2, 10, 17, 10, 5, 14, 14, 2, 5, 26, 17, 26, 18, 10, 2, 16, 9, 2, 45, 40, 35, 30, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 41, 38, 35, 32, 29, 26, 23, 20, 17, 14, 11, 8, 5, 2, 113, 112, 111, 110, 109, 108, 107}, + {1, 1, 0, 3, 2, 9, 6, 6, 14, 3, 14, 9, 18, 18, 6, 9, 30, 21, 30, 22, 14, 6, 20, 13, 6, 49, 44, 39, 34, 29, 54, 50, 46, 42, 38, 34, 30, 26, 22, 18, 14, 10, 6, 2, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, 115, 114, 113, 112, 111}, + {INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MAX, INT_MAX, INT_MAX} }; diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt new file mode 100644 index 00000000000..339959b81af --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt @@ -0,0 +1,22 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD}) +set(CMAKE_CXX_FLAGS ${CXX_FLAGS_NOFUZZ}) + +# Needed by LLVM's CMake checks because this file defines multiple targets. +set(LLVM_OPTIONAL_SOURCES proto_to_cxx.cpp proto_to_cxx_main.cpp + loop_proto_to_cxx.cpp loop_proto_to_cxx_main.cpp) + +add_clang_library(clangProtoToCXX proto_to_cxx.cpp + DEPENDS clangCXXProto + LINK_LIBS clangCXXProto ${PROTOBUF_LIBRARIES} + ) + +add_clang_library(clangLoopProtoToCXX loop_proto_to_cxx.cpp + DEPENDS clangCXXLoopProto + LINK_LIBS clangCXXLoopProto ${PROTOBUF_LIBRARIES} + ) + +add_clang_executable(clang-proto-to-cxx proto_to_cxx_main.cpp) +add_clang_executable(clang-loop-proto-to-cxx loop_proto_to_cxx_main.cpp) + +target_link_libraries(clang-proto-to-cxx PRIVATE clangProtoToCXX) +target_link_libraries(clang-loop-proto-to-cxx PRIVATE clangLoopProtoToCXX) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx.cpp b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx.cpp new file mode 100644 index 00000000000..114f5fc40ed --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx.cpp @@ -0,0 +1,159 @@ +//==-- loop_proto_to_cxx.cpp - Protobuf-C++ conversion ---------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements functions for converting between protobufs and C++. Differs from +// proto_to_cxx.cpp by wrapping all the generated C++ code in either a single +// for loop or two nested loops. Also outputs a different function signature +// that includes a size_t parameter for the loop to use. The C++ code generated +// is meant to stress the LLVM loop vectorizer. +// +// Still a work in progress. +// +//===----------------------------------------------------------------------===// + +#include "cxx_loop_proto.pb.h" +#include "proto_to_cxx.h" + +// The following is needed to convert protos in human-readable form +#include <google/protobuf/text_format.h> + +#include <ostream> +#include <sstream> + +namespace clang_fuzzer { + +static bool inner_loop = false; +class InnerLoop { + public: + InnerLoop() { + inner_loop = true; + } + ~InnerLoop() { + inner_loop = false; + } +}; + +// Forward decls. +std::ostream &operator<<(std::ostream &os, const BinaryOp &x); +std::ostream &operator<<(std::ostream &os, const StatementSeq &x); + +// Proto to C++. +std::ostream &operator<<(std::ostream &os, const Const &x) { + return os << "(" << x.val() << ")"; +} +std::ostream &operator<<(std::ostream &os, const VarRef &x) { + std::string which_loop = inner_loop ? "j" : "i"; + switch (x.arr()) { + case VarRef::ARR_A: + return os << "a[" << which_loop << "]"; + case VarRef::ARR_B: + return os << "b[" << which_loop << "]"; + case VarRef::ARR_C: + return os << "c[" << which_loop << "]"; + } +} +std::ostream &operator<<(std::ostream &os, const Rvalue &x) { + if (x.has_cons()) + return os << x.cons(); + if (x.has_binop()) + return os << x.binop(); + if (x.has_varref()) + return os << x.varref(); + return os << "1"; +} +std::ostream &operator<<(std::ostream &os, const BinaryOp &x) { + os << "(" << x.left(); + switch (x.op()) { + case BinaryOp::PLUS: + os << "+"; + break; + case BinaryOp::MINUS: + os << "-"; + break; + case BinaryOp::MUL: + os << "*"; + break; + case BinaryOp::XOR: + os << "^"; + break; + case BinaryOp::AND: + os << "&"; + break; + case BinaryOp::OR: + os << "|"; + break; + case BinaryOp::EQ: + os << "=="; + break; + case BinaryOp::NE: + os << "!="; + break; + case BinaryOp::LE: + os << "<="; + break; + case BinaryOp::GE: + os << ">="; + break; + case BinaryOp::LT: + os << "<"; + break; + case BinaryOp::GT: + os << ">"; + break; + } + return os << x.right() << ")"; +} +std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) { + return os << x.varref() << "=" << x.rvalue() << ";\n"; +} +std::ostream &operator<<(std::ostream &os, const Statement &x) { + return os << x.assignment(); +} +std::ostream &operator<<(std::ostream &os, const StatementSeq &x) { + for (auto &st : x.statements()) + os << st; + return os; +} +void NestedLoopToString(std::ostream &os, const LoopFunction &x) { + os << "void foo(int *a, int *b, int *__restrict__ c, size_t s) {\n" + << "for (int i=0; i<s; i++){\n" + << "for (int j=0; j<s; j++){\n"; + { + InnerLoop IL; + os << x.inner_statements() << "}\n"; + } + os << x.outer_statements() << "}\n}\n"; +} +void SingleLoopToString(std::ostream &os, const LoopFunction &x) { + os << "void foo(int *a, int *b, int *__restrict__ c, size_t s) {\n" + << "for (int i=0; i<s; i++){\n" + << x.outer_statements() << "}\n}\n"; +} +std::ostream &operator<<(std::ostream &os, const LoopFunction &x) { + if (x.has_inner_statements()) + NestedLoopToString(os, x); + else + SingleLoopToString(os, x); + return os; +} + +// --------------------------------- + +std::string LoopFunctionToString(const LoopFunction &input) { + std::ostringstream os; + os << input; + return os.str(); +} +std::string LoopProtoToCxx(const uint8_t *data, size_t size) { + LoopFunction message; + if (!message.ParsePartialFromArray(data, size)) + return "#error invalid proto\n"; + return LoopFunctionToString(message); +} + +} // namespace clang_fuzzer diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx_main.cpp b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx_main.cpp new file mode 100644 index 00000000000..15f0ace9a10 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/loop_proto_to_cxx_main.cpp @@ -0,0 +1,30 @@ +//==-- loop_proto_to_cxx_main.cpp - Driver for protobuf-C++ conversion -----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements a simple driver to print a C++ program from a protobuf with loops. +// +//===----------------------------------------------------------------------===// + + +#include <fstream> +#include <iostream> +#include <streambuf> +#include <string> + +#include "proto_to_cxx.h" + +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::fstream in(argv[i]); + std::string str((std::istreambuf_iterator<char>(in)), + std::istreambuf_iterator<char>()); + std::cout << "// " << argv[i] << std::endl; + std::cout << clang_fuzzer::LoopProtoToCxx( + reinterpret_cast<const uint8_t *>(str.data()), str.size()); + } +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp new file mode 100644 index 00000000000..029b42ae0ef --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp @@ -0,0 +1,101 @@ +//==-- proto_to_cxx.cpp - Protobuf-C++ conversion --------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements functions for converting between protobufs and C++. +// +//===----------------------------------------------------------------------===// + +#include "proto_to_cxx.h" +#include "cxx_proto.pb.h" + +#include <ostream> +#include <sstream> + +namespace clang_fuzzer { + +// Forward decls. +std::ostream &operator<<(std::ostream &os, const BinaryOp &x); +std::ostream &operator<<(std::ostream &os, const StatementSeq &x); + +// Proto to C++. +std::ostream &operator<<(std::ostream &os, const Const &x) { + return os << "(" << x.val() << ")"; +} +std::ostream &operator<<(std::ostream &os, const VarRef &x) { + return os << "a[" << (static_cast<uint32_t>(x.varnum()) % 100) << "]"; +} +std::ostream &operator<<(std::ostream &os, const Lvalue &x) { + return os << x.varref(); +} +std::ostream &operator<<(std::ostream &os, const Rvalue &x) { + if (x.has_varref()) return os << x.varref(); + if (x.has_cons()) return os << x.cons(); + if (x.has_binop()) return os << x.binop(); + return os << "1"; +} +std::ostream &operator<<(std::ostream &os, const BinaryOp &x) { + os << "(" << x.left(); + switch (x.op()) { + case BinaryOp::PLUS: os << "+"; break; + case BinaryOp::MINUS: os << "-"; break; + case BinaryOp::MUL: os << "*"; break; + case BinaryOp::DIV: os << "/"; break; + case BinaryOp::MOD: os << "%"; break; + case BinaryOp::XOR: os << "^"; break; + case BinaryOp::AND: os << "&"; break; + case BinaryOp::OR: os << "|"; break; + case BinaryOp::EQ: os << "=="; break; + case BinaryOp::NE: os << "!="; break; + case BinaryOp::LE: os << "<="; break; + case BinaryOp::GE: os << ">="; break; + case BinaryOp::LT: os << "<"; break; + case BinaryOp::GT: os << ">"; break; + } + return os << x.right() << ")"; +} +std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) { + return os << x.lvalue() << "=" << x.rvalue() << ";\n"; +} +std::ostream &operator<<(std::ostream &os, const IfElse &x) { + return os << "if (" << x.cond() << "){\n" + << x.if_body() << "} else { \n" + << x.else_body() << "}\n"; +} +std::ostream &operator<<(std::ostream &os, const While &x) { + return os << "while (" << x.cond() << "){\n" << x.body() << "}\n"; +} +std::ostream &operator<<(std::ostream &os, const Statement &x) { + if (x.has_assignment()) return os << x.assignment(); + if (x.has_ifelse()) return os << x.ifelse(); + if (x.has_while_loop()) return os << x.while_loop(); + return os << "(void)0;\n"; +} +std::ostream &operator<<(std::ostream &os, const StatementSeq &x) { + for (auto &st : x.statements()) os << st; + return os; +} +std::ostream &operator<<(std::ostream &os, const Function &x) { + return os << "void foo(int *a) {\n" << x.statements() << "}\n"; +} + +// --------------------------------- + +std::string FunctionToString(const Function &input) { + std::ostringstream os; + os << input; + return os.str(); + +} +std::string ProtoToCxx(const uint8_t *data, size_t size) { + Function message; + if (!message.ParsePartialFromArray(data, size)) + return "#error invalid proto\n"; + return FunctionToString(message); +} + +} // namespace clang_fuzzer diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h new file mode 100644 index 00000000000..204f66de064 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h @@ -0,0 +1,25 @@ +//==-- proto_to_cxx.h - Protobuf-C++ conversion ----------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines functions for converting between protobufs and C++. +// +//===----------------------------------------------------------------------===// + +#include <cstdint> +#include <cstddef> +#include <string> + +namespace clang_fuzzer { +class Function; +class LoopFunction; + +std::string FunctionToString(const Function &input); +std::string ProtoToCxx(const uint8_t *data, size_t size); +std::string LoopFunctionToString(const LoopFunction &input); +std::string LoopProtoToCxx(const uint8_t *data, size_t size); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp new file mode 100644 index 00000000000..67ff01a31d2 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp @@ -0,0 +1,29 @@ +//==-- proto_to_cxx_main.cpp - Driver for protobuf-C++ conversion ----------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements a simple driver to print a C++ program from a protobuf. +// +//===----------------------------------------------------------------------===// +#include <fstream> +#include <iostream> +#include <streambuf> +#include <string> + +#include "proto_to_cxx.h" + +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::fstream in(argv[i]); + std::string str((std::istreambuf_iterator<char>(in)), + std::istreambuf_iterator<char>()); + std::cout << "// " << argv[i] << std::endl; + std::cout << clang_fuzzer::ProtoToCxx( + reinterpret_cast<const uint8_t *>(str.data()), str.size()); + } +} + diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/CMakeLists.txt b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/CMakeLists.txt new file mode 100644 index 00000000000..ae58523f227 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD}) +set(CMAKE_CXX_FLAGS ${CXX_FLAGS_NOFUZZ}) + +# Needed by LLVM's CMake checks because this file defines multiple targets. +set(LLVM_OPTIONAL_SOURCES loop_proto_to_llvm.cpp loop_proto_to_llvm_main.cpp) + +add_clang_library(clangLoopProtoToLLVM loop_proto_to_llvm.cpp + DEPENDS clangCXXLoopProto + LINK_LIBS clangCXXLoopProto ${PROTOBUF_LIBRARIES} + ) + +add_clang_executable(clang-loop-proto-to-llvm loop_proto_to_llvm_main.cpp) + +target_link_libraries(clang-loop-proto-to-llvm PRIVATE clangLoopProtoToLLVM) diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.cpp b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.cpp new file mode 100644 index 00000000000..409570c3f0f --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.cpp @@ -0,0 +1,209 @@ +//==-- loop_proto_to_llvm.cpp - Protobuf-C++ conversion +//---------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements functions for converting between protobufs and LLVM IR. +// +// +//===----------------------------------------------------------------------===// + +#include "loop_proto_to_llvm.h" +#include "cxx_loop_proto.pb.h" +#include "../handle-llvm/input_arrays.h" + +// The following is needed to convert protos in human-readable form +#include <google/protobuf/text_format.h> + +#include <ostream> +#include <sstream> + +namespace clang_fuzzer { + +// Forward decls +std::string BinopToString(std::ostream &os, const BinaryOp &x); +std::string StateSeqToString(std::ostream &os, const StatementSeq &x); + +// Counter variable to generate new LLVM IR variable names and wrapper function +static std::string get_var() { + static int ctr = 0; + return "%var" + std::to_string(ctr++); +} + +static bool inner_loop = false; +class InnerLoop { + public: + InnerLoop() { + inner_loop = true; + } + ~InnerLoop() { + inner_loop = false; + } +}; + + +// Proto to LLVM. + +std::string ConstToString(const Const &x) { + return std::to_string(x.val()); +} +std::string VarRefToString(std::ostream &os, const VarRef &x) { + std::string which_loop = inner_loop ? "inner" : "outer"; + std::string arr; + switch(x.arr()) { + case VarRef::ARR_A: + arr = "%a"; + break; + case VarRef::ARR_B: + arr = "%b"; + break; + case VarRef::ARR_C: + arr = "%c"; + break; + } + std::string ptr_var = get_var(); + os << ptr_var << " = getelementptr inbounds i32, i32* " << arr + << ", i64 %" << which_loop << "_ct\n"; + return ptr_var; +} +std::string RvalueToString(std::ostream &os, const Rvalue &x) { + if(x.has_cons()) + return ConstToString(x.cons()); + if(x.has_binop()) + return BinopToString(os, x.binop()); + if(x.has_varref()) { + std::string var_ref = VarRefToString(os, x.varref()); + std::string val_var = get_var(); + os << val_var << " = load i32, i32* " << var_ref << "\n"; + return val_var; + } + return "1"; + +} +std::string BinopToString(std::ostream &os, const BinaryOp &x) { + std::string left = RvalueToString(os, x.left()); + std::string right = RvalueToString(os, x.right()); + std::string op; + switch (x.op()) { + case BinaryOp::PLUS: + op = "add"; + break; + case BinaryOp::MINUS: + op = "sub"; + break; + case BinaryOp::MUL: + op = "mul"; + break; + case BinaryOp::XOR: + op = "xor"; + break; + case BinaryOp::AND: + op = "and"; + break; + case BinaryOp::OR: + op = "or"; + break; + // Support for Boolean operators will be added later + case BinaryOp::EQ: + case BinaryOp::NE: + case BinaryOp::LE: + case BinaryOp::GE: + case BinaryOp::LT: + case BinaryOp::GT: + op = "add"; + break; + } + std::string val_var = get_var(); + os << val_var << " = " << op << " i32 " << left << ", " << right << "\n"; + return val_var; +} +std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) { + std::string rvalue = RvalueToString(os, x.rvalue()); + std::string var_ref = VarRefToString(os, x.varref()); + return os << "store i32 " << rvalue << ", i32* " << var_ref << "\n"; +} +std::ostream &operator<<(std::ostream &os, const Statement &x) { + return os << x.assignment(); +} +std::ostream &operator<<(std::ostream &os, const StatementSeq &x) { + for (auto &st : x.statements()) { + os << st; + } + return os; +} +void NestedLoopToString(std::ostream &os, const LoopFunction &x) { + os << "target triple = \"x86_64-unknown-linux-gnu\"\n" + << "define void @foo(i32* %a, i32* %b, i32* noalias %c, i64 %s) {\n" + << "outer_loop_start:\n" + << "%cmp = icmp sgt i64 %s, 0\n" + << "br i1 %cmp, label %inner_loop_start, label %end\n" + << "outer_loop:\n" + << x.outer_statements() + << "%o_ct_new = add i64 %outer_ct, 1\n" + << "%jmp_outer = icmp eq i64 %o_ct_new, %s\n" + << "br i1 %jmp_outer, label %end, label %inner_loop_start\n" + << "inner_loop_start:\n" + << "%outer_ct = phi i64 [%o_ct_new, %outer_loop], [0, %outer_loop_start]\n" + << "br label %inner_loop\n" + << "inner_loop:\n" + << "%inner_ct = phi i64 [0, %inner_loop_start], [%i_ct_new, %inner_loop]\n"; + { + InnerLoop IL; + os << x.inner_statements(); + } + os << "%i_ct_new = add i64 %inner_ct, 1\n" + << "%jmp_inner = icmp eq i64 %i_ct_new, %s\n" + << "br i1 %jmp_inner, label %outer_loop, label %inner_loop, !llvm.loop !0\n" + << "end:\n" + << "ret void\n" + << "}\n" + << "!0 = distinct !{!0, !1, !2}\n" + << "!1 = !{!\"llvm.loop.vectorize.enable\", i1 true}\n" + << "!2 = !{!\"llvm.loop.vectorize.width\", i32 " << kArraySize << "}\n"; +} +void SingleLoopToString(std::ostream &os, const LoopFunction &x) { + os << "target triple = \"x86_64-unknown-linux-gnu\"\n" + << "define void @foo(i32* %a, i32* %b, i32* noalias %c, i64 %s) {\n" + << "%cmp = icmp sgt i64 %s, 0\n" + << "br i1 %cmp, label %start, label %end\n" + << "start:\n" + << "br label %loop\n" + << "end:\n" + << "ret void\n" + << "loop:\n" + << "%outer_ct = phi i64 [ %ctnew, %loop ], [ 0, %start ]\n" + << x.outer_statements() + << "%ctnew = add i64 %outer_ct, 1\n" + << "%j = icmp eq i64 %ctnew, %s\n" + << "br i1 %j, label %end, label %loop, !llvm.loop !0\n}\n" + << "!0 = distinct !{!0, !1, !2}\n" + << "!1 = !{!\"llvm.loop.vectorize.enable\", i1 true}\n" + << "!2 = !{!\"llvm.loop.vectorize.width\", i32 " << kArraySize << "}\n"; +} +std::ostream &operator<<(std::ostream &os, const LoopFunction &x) { + if (x.has_inner_statements()) + NestedLoopToString(os, x); + else + SingleLoopToString(os, x); + return os; +} + +// --------------------------------- + +std::string LoopFunctionToLLVMString(const LoopFunction &input) { + std::ostringstream os; + os << input; + return os.str(); +} +std::string LoopProtoToLLVM(const uint8_t *data, size_t size) { + LoopFunction message; + if (!message.ParsePartialFromArray(data, size)) + return "#error invalid proto\n"; + return LoopFunctionToLLVMString(message); +} + +} // namespace clang_fuzzer diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.h b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.h new file mode 100644 index 00000000000..173b937e527 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm.h @@ -0,0 +1,22 @@ +//==-- loop_proto_to_llvm.h - Protobuf-C++ conversion ----------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Defines functions for converting between protobufs and LLVM IR. +// +//===----------------------------------------------------------------------===// + +#include <cstdint> +#include <cstddef> +#include <string> + +namespace clang_fuzzer { +class LoopFunction; + +std::string LoopFunctionToLLVMString(const LoopFunction &input); +std::string LoopProtoToLLVM(const uint8_t *data, size_t size); +} diff --git a/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm_main.cpp b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm_main.cpp new file mode 100644 index 00000000000..e8b19158f73 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-fuzzer/proto-to-llvm/loop_proto_to_llvm_main.cpp @@ -0,0 +1,30 @@ +//==-- loop_proto_to_llvm_main.cpp - Driver for protobuf-LLVM conversion----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements a simple driver to print a LLVM program from a protobuf with loops +// +//===----------------------------------------------------------------------===// + + +#include <fstream> +#include <iostream> +#include <streambuf> +#include <string> + +#include "loop_proto_to_llvm.h" + +int main(int argc, char **argv) { + for (int i = 1; i < argc; i++) { + std::fstream in(argv[i]); + std::string str((std::istreambuf_iterator<char>(in)), + std::istreambuf_iterator<char>()); + std::cout << ";; " << argv[i] << std::endl; + std::cout << clang_fuzzer::LoopProtoToLLVM( + reinterpret_cast<const uint8_t *>(str.data()), str.size()); + } +} diff --git a/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt b/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt new file mode 100644 index 00000000000..39a5c41c151 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-import-test/CMakeLists.txt @@ -0,0 +1,30 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +if(NOT CLANG_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_clang_tool(clang-import-test + clang-import-test.cpp + DEPENDS + ${tablegen_deps} + ) + +set(CLANG_IMPORT_TEST_LIB_DEPS + clangAST + clangBasic + clangCodeGen + clangDriver + clangFrontend + clangLex + clangParse + clangSerialization + ) + +clang_target_link_libraries(clang-import-test + PRIVATE + ${CLANG_IMPORT_TEST_LIB_DEPS} + ) diff --git a/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp b/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp new file mode 100644 index 00000000000..b42c93e8d38 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-import-test/clang-import-test.cpp @@ -0,0 +1,382 @@ +//===-- clang-import-test.cpp - ASTImporter/ExternalASTSource testbed -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExternalASTMerger.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/Types.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Signals.h" + +#include <memory> +#include <string> + +using namespace clang; + +static llvm::cl::opt<std::string> Expression( + "expression", llvm::cl::Required, + llvm::cl::desc("Path to a file containing the expression to parse")); + +static llvm::cl::list<std::string> + Imports("import", llvm::cl::ZeroOrMore, + llvm::cl::desc("Path to a file containing declarations to import")); + +static llvm::cl::opt<bool> + Direct("direct", llvm::cl::Optional, + llvm::cl::desc("Use the parsed declarations without indirection")); + +static llvm::cl::opt<bool> UseOrigins( + "use-origins", llvm::cl::Optional, + llvm::cl::desc( + "Use DeclContext origin information for more accurate lookups")); + +static llvm::cl::list<std::string> + ClangArgs("Xcc", llvm::cl::ZeroOrMore, + llvm::cl::desc("Argument to pass to the CompilerInvocation"), + llvm::cl::CommaSeparated); + +static llvm::cl::opt<std::string> + Input("x", llvm::cl::Optional, + llvm::cl::desc("The language to parse (default: c++)"), + llvm::cl::init("c++")); + +static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false), + llvm::cl::desc("Dump combined AST")); + +static llvm::cl::opt<bool> DumpIR("dump-ir", llvm::cl::init(false), + llvm::cl::desc("Dump IR from final parse")); + +namespace init_convenience { +class TestDiagnosticConsumer : public DiagnosticConsumer { +private: + std::unique_ptr<TextDiagnosticBuffer> Passthrough; + const LangOptions *LangOpts = nullptr; + +public: + TestDiagnosticConsumer() + : Passthrough(std::make_unique<TextDiagnosticBuffer>()) {} + + virtual void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP = nullptr) override { + this->LangOpts = &LangOpts; + return Passthrough->BeginSourceFile(LangOpts, PP); + } + + virtual void EndSourceFile() override { + this->LangOpts = nullptr; + Passthrough->EndSourceFile(); + } + + virtual bool IncludeInDiagnosticCounts() const override { + return Passthrough->IncludeInDiagnosticCounts(); + } + +private: + static void PrintSourceForLocation(const SourceLocation &Loc, + SourceManager &SM) { + const char *LocData = SM.getCharacterData(Loc, /*Invalid=*/nullptr); + unsigned LocColumn = + SM.getSpellingColumnNumber(Loc, /*Invalid=*/nullptr) - 1; + FileID FID = SM.getFileID(Loc); + const llvm::MemoryBuffer *Buffer = + SM.getBuffer(FID, Loc, /*Invalid=*/nullptr); + + assert(LocData >= Buffer->getBufferStart() && + LocData < Buffer->getBufferEnd()); + + const char *LineBegin = LocData - LocColumn; + + assert(LineBegin >= Buffer->getBufferStart()); + + const char *LineEnd = nullptr; + + for (LineEnd = LineBegin; *LineEnd != '\n' && *LineEnd != '\r' && + LineEnd < Buffer->getBufferEnd(); + ++LineEnd) + ; + + llvm::StringRef LineString(LineBegin, LineEnd - LineBegin); + + llvm::errs() << LineString << '\n'; + llvm::errs().indent(LocColumn); + llvm::errs() << '^'; + llvm::errs() << '\n'; + } + + virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) override { + if (Info.hasSourceManager() && LangOpts) { + SourceManager &SM = Info.getSourceManager(); + + if (Info.getLocation().isValid()) { + Info.getLocation().print(llvm::errs(), SM); + llvm::errs() << ": "; + } + + SmallString<16> DiagText; + Info.FormatDiagnostic(DiagText); + llvm::errs() << DiagText << '\n'; + + if (Info.getLocation().isValid()) { + PrintSourceForLocation(Info.getLocation(), SM); + } + + for (const CharSourceRange &Range : Info.getRanges()) { + bool Invalid = true; + StringRef Ref = Lexer::getSourceText(Range, SM, *LangOpts, &Invalid); + if (!Invalid) { + llvm::errs() << Ref << '\n'; + } + } + } + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + } +}; + +std::unique_ptr<CompilerInstance> BuildCompilerInstance() { + auto Ins = std::make_unique<CompilerInstance>(); + auto DC = std::make_unique<TestDiagnosticConsumer>(); + const bool ShouldOwnClient = true; + Ins->createDiagnostics(DC.release(), ShouldOwnClient); + + auto Inv = std::make_unique<CompilerInvocation>(); + + std::vector<const char *> ClangArgv(ClangArgs.size()); + std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(), + [](const std::string &s) -> const char * { return s.data(); }); + CompilerInvocation::CreateFromArgs(*Inv, ClangArgv, Ins->getDiagnostics()); + + { + using namespace driver::types; + ID Id = lookupTypeForTypeSpecifier(Input.c_str()); + assert(Id != TY_INVALID); + if (isCXX(Id)) { + Inv->getLangOpts()->CPlusPlus = true; + Inv->getLangOpts()->CPlusPlus11 = true; + Inv->getHeaderSearchOpts().UseLibcxx = true; + } + if (isObjC(Id)) { + Inv->getLangOpts()->ObjC = 1; + } + } + Inv->getLangOpts()->Bool = true; + Inv->getLangOpts()->WChar = true; + Inv->getLangOpts()->Blocks = true; + Inv->getLangOpts()->DebuggerSupport = true; + Inv->getLangOpts()->SpellChecking = false; + Inv->getLangOpts()->ThreadsafeStatics = false; + Inv->getLangOpts()->AccessControl = false; + Inv->getLangOpts()->DollarIdents = true; + Inv->getLangOpts()->Exceptions = true; + Inv->getLangOpts()->CXXExceptions = true; + // Needed for testing dynamic_cast. + Inv->getLangOpts()->RTTI = true; + Inv->getCodeGenOpts().setDebugInfo(codegenoptions::FullDebugInfo); + Inv->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); + + Ins->setInvocation(std::move(Inv)); + + TargetInfo *TI = TargetInfo::CreateTargetInfo( + Ins->getDiagnostics(), Ins->getInvocation().TargetOpts); + Ins->setTarget(TI); + Ins->getTarget().adjust(Ins->getLangOpts()); + Ins->createFileManager(); + Ins->createSourceManager(Ins->getFileManager()); + Ins->createPreprocessor(TU_Complete); + + return Ins; +} + +std::unique_ptr<ASTContext> +BuildASTContext(CompilerInstance &CI, SelectorTable &ST, Builtin::Context &BC) { + auto AST = std::make_unique<ASTContext>( + CI.getLangOpts(), CI.getSourceManager(), + CI.getPreprocessor().getIdentifierTable(), ST, BC); + AST->InitBuiltinTypes(CI.getTarget()); + return AST; +} + +std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI, + llvm::LLVMContext &LLVMCtx) { + StringRef ModuleName("$__module"); + return std::unique_ptr<CodeGenerator>(CreateLLVMCodeGen( + CI.getDiagnostics(), ModuleName, CI.getHeaderSearchOpts(), + CI.getPreprocessorOpts(), CI.getCodeGenOpts(), LLVMCtx)); +} +} // namespace init_convenience + +namespace { + +/// A container for a CompilerInstance (possibly with an ExternalASTMerger +/// attached to its ASTContext). +/// +/// Provides an accessor for the DeclContext origins associated with the +/// ExternalASTMerger (or an empty list of origins if no ExternalASTMerger is +/// attached). +/// +/// This is the main unit of parsed source code maintained by clang-import-test. +struct CIAndOrigins { + using OriginMap = clang::ExternalASTMerger::OriginMap; + std::unique_ptr<CompilerInstance> CI; + + ASTContext &getASTContext() { return CI->getASTContext(); } + FileManager &getFileManager() { return CI->getFileManager(); } + const OriginMap &getOriginMap() { + static const OriginMap EmptyOriginMap{}; + if (ExternalASTSource *Source = CI->getASTContext().getExternalSource()) + return static_cast<ExternalASTMerger *>(Source)->GetOrigins(); + return EmptyOriginMap; + } + DiagnosticConsumer &getDiagnosticClient() { + return CI->getDiagnosticClient(); + } + CompilerInstance &getCompilerInstance() { return *CI; } +}; + +void AddExternalSource(CIAndOrigins &CI, + llvm::MutableArrayRef<CIAndOrigins> Imports) { + ExternalASTMerger::ImporterTarget Target( + {CI.getASTContext(), CI.getFileManager()}); + llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources; + for (CIAndOrigins &Import : Imports) + Sources.emplace_back(Import.getASTContext(), Import.getFileManager(), + Import.getOriginMap()); + auto ES = std::make_unique<ExternalASTMerger>(Target, Sources); + CI.getASTContext().setExternalSource(ES.release()); + CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(); +} + +CIAndOrigins BuildIndirect(CIAndOrigins &CI) { + CIAndOrigins IndirectCI{init_convenience::BuildCompilerInstance()}; + auto ST = std::make_unique<SelectorTable>(); + auto BC = std::make_unique<Builtin::Context>(); + std::unique_ptr<ASTContext> AST = init_convenience::BuildASTContext( + IndirectCI.getCompilerInstance(), *ST, *BC); + IndirectCI.getCompilerInstance().setASTContext(AST.release()); + AddExternalSource(IndirectCI, CI); + return IndirectCI; +} + +llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI, + ASTConsumer &Consumer) { + SourceManager &SM = CI.getSourceManager(); + auto FE = CI.getFileManager().getFile(Path); + if (!FE) { + return llvm::make_error<llvm::StringError>( + llvm::Twine("Couldn't open ", Path), std::error_code()); + } + SM.setMainFileID(SM.createFileID(*FE, SourceLocation(), SrcMgr::C_User)); + ParseAST(CI.getPreprocessor(), &Consumer, CI.getASTContext()); + return llvm::Error::success(); +} + +llvm::Expected<CIAndOrigins> Parse(const std::string &Path, + llvm::MutableArrayRef<CIAndOrigins> Imports, + bool ShouldDumpAST, bool ShouldDumpIR) { + CIAndOrigins CI{init_convenience::BuildCompilerInstance()}; + auto ST = std::make_unique<SelectorTable>(); + auto BC = std::make_unique<Builtin::Context>(); + std::unique_ptr<ASTContext> AST = + init_convenience::BuildASTContext(CI.getCompilerInstance(), *ST, *BC); + CI.getCompilerInstance().setASTContext(AST.release()); + if (Imports.size()) + AddExternalSource(CI, Imports); + + std::vector<std::unique_ptr<ASTConsumer>> ASTConsumers; + + auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); + ASTConsumers.push_back( + init_convenience::BuildCodeGen(CI.getCompilerInstance(), *LLVMCtx)); + auto &CG = *static_cast<CodeGenerator *>(ASTConsumers.back().get()); + + if (ShouldDumpAST) + ASTConsumers.push_back( + CreateASTDumper(nullptr /*Dump to stdout.*/, "", true, false, false, + clang::ADOF_Default)); + + CI.getDiagnosticClient().BeginSourceFile( + CI.getCompilerInstance().getLangOpts(), + &CI.getCompilerInstance().getPreprocessor()); + MultiplexConsumer Consumers(std::move(ASTConsumers)); + Consumers.Initialize(CI.getASTContext()); + + if (llvm::Error PE = ParseSource(Path, CI.getCompilerInstance(), Consumers)) + return std::move(PE); + CI.getDiagnosticClient().EndSourceFile(); + if (ShouldDumpIR) + CG.GetModule()->print(llvm::outs(), nullptr); + if (CI.getDiagnosticClient().getNumErrors()) + return llvm::make_error<llvm::StringError>( + "Errors occurred while parsing the expression.", std::error_code()); + return std::move(CI); +} + +void Forget(CIAndOrigins &CI, llvm::MutableArrayRef<CIAndOrigins> Imports) { + llvm::SmallVector<ExternalASTMerger::ImporterSource, 3> Sources; + for (CIAndOrigins &Import : Imports) + Sources.push_back({Import.getASTContext(), Import.getFileManager(), + Import.getOriginMap()}); + ExternalASTSource *Source = CI.CI->getASTContext().getExternalSource(); + auto *Merger = static_cast<ExternalASTMerger *>(Source); + Merger->RemoveSources(Sources); +} + +} // end namespace + +int main(int argc, const char **argv) { + const bool DisableCrashReporting = true; + llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting); + llvm::cl::ParseCommandLineOptions(argc, argv); + std::vector<CIAndOrigins> ImportCIs; + for (auto I : Imports) { + llvm::Expected<CIAndOrigins> ImportCI = Parse(I, {}, false, false); + if (auto E = ImportCI.takeError()) { + llvm::errs() << llvm::toString(std::move(E)); + exit(-1); + } + ImportCIs.push_back(std::move(*ImportCI)); + } + std::vector<CIAndOrigins> IndirectCIs; + if (!Direct || UseOrigins) { + for (auto &ImportCI : ImportCIs) { + CIAndOrigins IndirectCI = BuildIndirect(ImportCI); + IndirectCIs.push_back(std::move(IndirectCI)); + } + } + if (UseOrigins) + for (auto &ImportCI : ImportCIs) + IndirectCIs.push_back(std::move(ImportCI)); + llvm::Expected<CIAndOrigins> ExpressionCI = + Parse(Expression, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs, + DumpAST, DumpIR); + if (auto E = ExpressionCI.takeError()) { + llvm::errs() << llvm::toString(std::move(E)); + exit(-1); + } + Forget(*ExpressionCI, (Direct && !UseOrigins) ? ImportCIs : IndirectCIs); + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt b/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt new file mode 100644 index 00000000000..4ef09949336 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-offload-bundler/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LLVM_LINK_COMPONENTS Object Support) + +if(NOT CLANG_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_clang_tool(clang-offload-bundler + ClangOffloadBundler.cpp + + DEPENDS + ${tablegen_deps} + ) + +set(CLANG_OFFLOAD_BUNDLER_LIB_DEPS + clangBasic + ) + +add_dependencies(clang clang-offload-bundler) + +clang_target_link_libraries(clang-offload-bundler + PRIVATE + ${CLANG_OFFLOAD_BUNDLER_LIB_DEPS} + ) diff --git a/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp b/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp new file mode 100644 index 00000000000..a75d2a630cf --- /dev/null +++ b/gnu/llvm/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp @@ -0,0 +1,931 @@ +//===-- clang-offload-bundler/ClangOffloadBundler.cpp ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a clang-offload-bundler that bundles different +/// files that relate with the same source code but different targets into a +/// single one. Also the implements the opposite functionality, i.e. unbundle +/// files previous created by this tool. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Version.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdint> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +using namespace llvm; +using namespace llvm::object; + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); + +// Mark all our options with this category, everything else (except for -version +// and -help) will be hidden. +static cl::OptionCategory + ClangOffloadBundlerCategory("clang-offload-bundler options"); + +static cl::list<std::string> + InputFileNames("inputs", cl::CommaSeparated, cl::OneOrMore, + cl::desc("[<input file>,...]"), + cl::cat(ClangOffloadBundlerCategory)); +static cl::list<std::string> + OutputFileNames("outputs", cl::CommaSeparated, cl::OneOrMore, + cl::desc("[<output file>,...]"), + cl::cat(ClangOffloadBundlerCategory)); +static cl::list<std::string> + TargetNames("targets", cl::CommaSeparated, cl::OneOrMore, + cl::desc("[<offload kind>-<target triple>,...]"), + cl::cat(ClangOffloadBundlerCategory)); +static cl::opt<std::string> + FilesType("type", cl::Required, + cl::desc("Type of the files to be bundled/unbundled.\n" + "Current supported types are:\n" + " i - cpp-output\n" + " ii - c++-cpp-output\n" + " cui - cuda/hip-output\n" + " d - dependency\n" + " ll - llvm\n" + " bc - llvm-bc\n" + " s - assembler\n" + " o - object\n" + " gch - precompiled-header\n" + " ast - clang AST file"), + cl::cat(ClangOffloadBundlerCategory)); +static cl::opt<bool> + Unbundle("unbundle", + cl::desc("Unbundle bundled file into several output files.\n"), + cl::init(false), cl::cat(ClangOffloadBundlerCategory)); + +static cl::opt<bool> PrintExternalCommands( + "###", + cl::desc("Print any external commands that are to be executed " + "instead of actually executing them - for testing purposes.\n"), + cl::init(false), cl::cat(ClangOffloadBundlerCategory)); + +/// Magic string that marks the existence of offloading data. +#define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" + +/// The index of the host input in the list of inputs. +static unsigned HostInputIndex = ~0u; + +/// Path to the current binary. +static std::string BundlerExecutable; + +/// Obtain the offload kind and real machine triple out of the target +/// information specified by the user. +static void getOffloadKindAndTriple(StringRef Target, StringRef &OffloadKind, + StringRef &Triple) { + auto KindTriplePair = Target.split('-'); + OffloadKind = KindTriplePair.first; + Triple = KindTriplePair.second; +} +static bool hasHostKind(StringRef Target) { + StringRef OffloadKind; + StringRef Triple; + getOffloadKindAndTriple(Target, OffloadKind, Triple); + return OffloadKind == "host"; +} + +/// Generic file handler interface. +class FileHandler { +public: + FileHandler() {} + + virtual ~FileHandler() {} + + /// Update the file handler with information from the header of the bundled + /// file. + virtual Error ReadHeader(MemoryBuffer &Input) = 0; + + /// Read the marker of the next bundled to be read in the file. The bundle + /// name is returned if there is one in the file, or `None` if there are no + /// more bundles to be read. + virtual Expected<Optional<StringRef>> + ReadBundleStart(MemoryBuffer &Input) = 0; + + /// Read the marker that closes the current bundle. + virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; + + /// Read the current bundle and write the result into the stream \a OS. + virtual Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0; + + /// Write the header of the bundled file to \a OS based on the information + /// gathered from \a Inputs. + virtual Error WriteHeader(raw_fd_ostream &OS, + ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; + + /// Write the marker that initiates a bundle for the triple \a TargetTriple to + /// \a OS. + virtual Error WriteBundleStart(raw_fd_ostream &OS, + StringRef TargetTriple) = 0; + + /// Write the marker that closes a bundle for the triple \a TargetTriple to \a + /// OS. + virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0; + + /// Write the bundle from \a Input into \a OS. + virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0; +}; + +/// Handler for binary files. The bundled file will have the following format +/// (all integers are stored in little-endian format): +/// +/// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) +/// +/// NumberOfOffloadBundles (8-byte integer) +/// +/// OffsetOfBundle1 (8-byte integer) +/// SizeOfBundle1 (8-byte integer) +/// NumberOfBytesInTripleOfBundle1 (8-byte integer) +/// TripleOfBundle1 (byte length defined before) +/// +/// ... +/// +/// OffsetOfBundleN (8-byte integer) +/// SizeOfBundleN (8-byte integer) +/// NumberOfBytesInTripleOfBundleN (8-byte integer) +/// TripleOfBundleN (byte length defined before) +/// +/// Bundle1 +/// ... +/// BundleN + +/// Read 8-byte integers from a buffer in little-endian format. +static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { + uint64_t Res = 0; + const char *Data = Buffer.data(); + + for (unsigned i = 0; i < 8; ++i) { + Res <<= 8; + uint64_t Char = (uint64_t)Data[pos + 7 - i]; + Res |= 0xffu & Char; + } + return Res; +} + +/// Write 8-byte integers to a buffer in little-endian format. +static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) { + for (unsigned i = 0; i < 8; ++i) { + char Char = (char)(Val & 0xffu); + OS.write(&Char, 1); + Val >>= 8; + } +} + +class BinaryFileHandler final : public FileHandler { + /// Information about the bundles extracted from the header. + struct BundleInfo final { + /// Size of the bundle. + uint64_t Size = 0u; + /// Offset at which the bundle starts in the bundled file. + uint64_t Offset = 0u; + + BundleInfo() {} + BundleInfo(uint64_t Size, uint64_t Offset) : Size(Size), Offset(Offset) {} + }; + + /// Map between a triple and the corresponding bundle information. + StringMap<BundleInfo> BundlesInfo; + + /// Iterator for the bundle information that is being read. + StringMap<BundleInfo>::iterator CurBundleInfo; + StringMap<BundleInfo>::iterator NextBundleInfo; + +public: + BinaryFileHandler() : FileHandler() {} + + ~BinaryFileHandler() final {} + + Error ReadHeader(MemoryBuffer &Input) final { + StringRef FC = Input.getBuffer(); + + // Initialize the current bundle with the end of the container. + CurBundleInfo = BundlesInfo.end(); + + // Check if buffer is smaller than magic string. + size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; + if (ReadChars > FC.size()) + return Error::success(); + + // Check if no magic was found. + StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); + if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR)) + return Error::success(); + + // Read number of bundles. + if (ReadChars + 8 > FC.size()) + return Error::success(); + + uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); + ReadChars += 8; + + // Read bundle offsets, sizes and triples. + for (uint64_t i = 0; i < NumberOfBundles; ++i) { + + // Read offset. + if (ReadChars + 8 > FC.size()) + return Error::success(); + + uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); + ReadChars += 8; + + // Read size. + if (ReadChars + 8 > FC.size()) + return Error::success(); + + uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); + ReadChars += 8; + + // Read triple size. + if (ReadChars + 8 > FC.size()) + return Error::success(); + + uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); + ReadChars += 8; + + // Read triple. + if (ReadChars + TripleSize > FC.size()) + return Error::success(); + + StringRef Triple(&FC.data()[ReadChars], TripleSize); + ReadChars += TripleSize; + + // Check if the offset and size make sense. + if (!Offset || Offset + Size > FC.size()) + return Error::success(); + + assert(BundlesInfo.find(Triple) == BundlesInfo.end() && + "Triple is duplicated??"); + BundlesInfo[Triple] = BundleInfo(Size, Offset); + } + // Set the iterator to where we will start to read. + CurBundleInfo = BundlesInfo.end(); + NextBundleInfo = BundlesInfo.begin(); + return Error::success(); + } + + Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final { + if (NextBundleInfo == BundlesInfo.end()) + return None; + CurBundleInfo = NextBundleInfo++; + return CurBundleInfo->first(); + } + + Error ReadBundleEnd(MemoryBuffer &Input) final { + assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); + return Error::success(); + } + + Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { + assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); + StringRef FC = Input.getBuffer(); + OS.write(FC.data() + CurBundleInfo->second.Offset, + CurBundleInfo->second.Size); + return Error::success(); + } + + Error WriteHeader(raw_fd_ostream &OS, + ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { + // Compute size of the header. + uint64_t HeaderSize = 0; + + HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; + HeaderSize += 8; // Number of Bundles + + for (auto &T : TargetNames) { + HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. + HeaderSize += T.size(); // The triple. + } + + // Write to the buffer the header. + OS << OFFLOAD_BUNDLER_MAGIC_STR; + + Write8byteIntegerToBuffer(OS, TargetNames.size()); + + unsigned Idx = 0; + for (auto &T : TargetNames) { + MemoryBuffer &MB = *Inputs[Idx++]; + // Bundle offset. + Write8byteIntegerToBuffer(OS, HeaderSize); + // Size of the bundle (adds to the next bundle's offset) + Write8byteIntegerToBuffer(OS, MB.getBufferSize()); + HeaderSize += MB.getBufferSize(); + // Size of the triple + Write8byteIntegerToBuffer(OS, T.size()); + // Triple + OS << T; + } + return Error::success(); + } + + Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { + return Error::success(); + } + + Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { + return Error::success(); + } + + Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { + OS.write(Input.getBufferStart(), Input.getBufferSize()); + return Error::success(); + } +}; + +/// Handler for object files. The bundles are organized by sections with a +/// designated name. +/// +/// To unbundle, we just copy the contents of the designated section. +class ObjectFileHandler final : public FileHandler { + + /// The object file we are currently dealing with. + std::unique_ptr<ObjectFile> Obj; + + /// Return the input file contents. + StringRef getInputFileContents() const { return Obj->getData(); } + + /// Return bundle name (<kind>-<triple>) if the provided section is an offload + /// section. + static Expected<Optional<StringRef>> IsOffloadSection(SectionRef CurSection) { + Expected<StringRef> NameOrErr = CurSection.getName(); + if (!NameOrErr) + return NameOrErr.takeError(); + + // If it does not start with the reserved suffix, just skip this section. + if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR)) + return None; + + // Return the triple that is right after the reserved prefix. + return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); + } + + /// Total number of inputs. + unsigned NumberOfInputs = 0; + + /// Total number of processed inputs, i.e, inputs that were already + /// read from the buffers. + unsigned NumberOfProcessedInputs = 0; + + /// Iterator of the current and next section. + section_iterator CurrentSection; + section_iterator NextSection; + +public: + ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn) + : FileHandler(), Obj(std::move(ObjIn)), + CurrentSection(Obj->section_begin()), + NextSection(Obj->section_begin()) {} + + ~ObjectFileHandler() final {} + + Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } + + Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final { + while (NextSection != Obj->section_end()) { + CurrentSection = NextSection; + ++NextSection; + + // Check if the current section name starts with the reserved prefix. If + // so, return the triple. + Expected<Optional<StringRef>> TripleOrErr = + IsOffloadSection(*CurrentSection); + if (!TripleOrErr) + return TripleOrErr.takeError(); + if (*TripleOrErr) + return **TripleOrErr; + } + return None; + } + + Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } + + Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { + Expected<StringRef> Content = CurrentSection->getContents(); + if (!Content) + return Content.takeError(); + + OS.write(Content->data(), Content->size()); + return Error::success(); + } + + Error WriteHeader(raw_fd_ostream &OS, + ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { + assert(HostInputIndex != ~0u && "Host input index not defined."); + + // Record number of inputs. + NumberOfInputs = Inputs.size(); + return Error::success(); + } + + Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { + ++NumberOfProcessedInputs; + return Error::success(); + } + + Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { + assert(NumberOfProcessedInputs <= NumberOfInputs && + "Processing more inputs that actually exist!"); + assert(HostInputIndex != ~0u && "Host input index not defined."); + + // If this is not the last output, we don't have to do anything. + if (NumberOfProcessedInputs != NumberOfInputs) + return Error::success(); + + // Find llvm-objcopy in order to create the bundle binary. + ErrorOr<std::string> Objcopy = sys::findProgramByName( + "llvm-objcopy", sys::path::parent_path(BundlerExecutable)); + if (!Objcopy) + Objcopy = sys::findProgramByName("llvm-objcopy"); + if (!Objcopy) + return createStringError(Objcopy.getError(), + "unable to find 'llvm-objcopy' in path"); + + // We write to the output file directly. So, we close it and use the name + // to pass down to llvm-objcopy. + OS.close(); + + // Compose command line for the objcopy tool. + BumpPtrAllocator Alloc; + StringSaver SS{Alloc}; + SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; + for (unsigned I = 0; I < NumberOfInputs; ++I) + ObjcopyArgs.push_back(SS.save(Twine("--add-section=") + + OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] + + "=" + InputFileNames[I])); + ObjcopyArgs.push_back(InputFileNames[HostInputIndex]); + ObjcopyArgs.push_back(OutputFileNames.front()); + + // If the user asked for the commands to be printed out, we do that instead + // of executing it. + if (PrintExternalCommands) { + errs() << "\"" << *Objcopy << "\""; + for (StringRef Arg : drop_begin(ObjcopyArgs, 1)) + errs() << " \"" << Arg << "\""; + errs() << "\n"; + } else { + if (sys::ExecuteAndWait(*Objcopy, ObjcopyArgs)) + return createStringError(inconvertibleErrorCode(), + "'llvm-objcopy' tool failed"); + } + + return Error::success(); + } + + Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { + return Error::success(); + } +}; + +/// Handler for text files. The bundled file will have the following format. +/// +/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" +/// Bundle 1 +/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" +/// ... +/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" +/// Bundle N +/// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" +class TextFileHandler final : public FileHandler { + /// String that begins a line comment. + StringRef Comment; + + /// String that initiates a bundle. + std::string BundleStartString; + + /// String that closes a bundle. + std::string BundleEndString; + + /// Number of chars read from input. + size_t ReadChars = 0u; + +protected: + Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } + + Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final { + StringRef FC = Input.getBuffer(); + + // Find start of the bundle. + ReadChars = FC.find(BundleStartString, ReadChars); + if (ReadChars == FC.npos) + return None; + + // Get position of the triple. + size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); + + // Get position that closes the triple. + size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); + if (TripleEnd == FC.npos) + return None; + + // Next time we read after the new line. + ++ReadChars; + + return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); + } + + Error ReadBundleEnd(MemoryBuffer &Input) final { + StringRef FC = Input.getBuffer(); + + // Read up to the next new line. + assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); + + size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); + if (TripleEnd != FC.npos) + // Next time we read after the new line. + ++ReadChars; + + return Error::success(); + } + + Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { + StringRef FC = Input.getBuffer(); + size_t BundleStart = ReadChars; + + // Find end of the bundle. + size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); + + StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); + OS << Bundle; + + return Error::success(); + } + + Error WriteHeader(raw_fd_ostream &OS, + ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { + return Error::success(); + } + + Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { + OS << BundleStartString << TargetTriple << "\n"; + return Error::success(); + } + + Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { + OS << BundleEndString << TargetTriple << "\n"; + return Error::success(); + } + + Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { + OS << Input.getBuffer(); + return Error::success(); + } + +public: + TextFileHandler(StringRef Comment) + : FileHandler(), Comment(Comment), ReadChars(0) { + BundleStartString = + "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; + BundleEndString = + "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; + } +}; + +/// Return an appropriate object file handler. We use the specific object +/// handler if we know how to deal with that format, otherwise we use a default +/// binary file handler. +static std::unique_ptr<FileHandler> +CreateObjectFileHandler(MemoryBuffer &FirstInput) { + // Check if the input file format is one that we know how to deal with. + Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); + + // We only support regular object files. If failed to open the input as a + // known binary or this is not an object file use the default binary handler. + if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) + return std::make_unique<BinaryFileHandler>(); + + // Otherwise create an object file handler. The handler will be owned by the + // client of this function. + return std::make_unique<ObjectFileHandler>( + std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release()))); +} + +/// Return an appropriate handler given the input files and options. +static Expected<std::unique_ptr<FileHandler>> +CreateFileHandler(MemoryBuffer &FirstInput) { + if (FilesType == "i") + return std::make_unique<TextFileHandler>(/*Comment=*/"//"); + if (FilesType == "ii") + return std::make_unique<TextFileHandler>(/*Comment=*/"//"); + if (FilesType == "cui") + return std::make_unique<TextFileHandler>(/*Comment=*/"//"); + // TODO: `.d` should be eventually removed once `-M` and its variants are + // handled properly in offload compilation. + if (FilesType == "d") + return std::make_unique<TextFileHandler>(/*Comment=*/"#"); + if (FilesType == "ll") + return std::make_unique<TextFileHandler>(/*Comment=*/";"); + if (FilesType == "bc") + return std::make_unique<BinaryFileHandler>(); + if (FilesType == "s") + return std::make_unique<TextFileHandler>(/*Comment=*/"#"); + if (FilesType == "o") + return CreateObjectFileHandler(FirstInput); + if (FilesType == "gch") + return std::make_unique<BinaryFileHandler>(); + if (FilesType == "ast") + return std::make_unique<BinaryFileHandler>(); + + return createStringError(errc::invalid_argument, + "'" + FilesType + "': invalid file type specified"); +} + +/// Bundle the files. Return true if an error was found. +static Error BundleFiles() { + std::error_code EC; + + // Create output file. + raw_fd_ostream OutputFile(OutputFileNames.front(), EC, sys::fs::OF_None); + if (EC) + return createFileError(OutputFileNames.front(), EC); + + // Open input files. + SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; + InputBuffers.reserve(InputFileNames.size()); + for (auto &I : InputFileNames) { + ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = + MemoryBuffer::getFileOrSTDIN(I); + if (std::error_code EC = CodeOrErr.getError()) + return createFileError(I, EC); + InputBuffers.emplace_back(std::move(*CodeOrErr)); + } + + // Get the file handler. We use the host buffer as reference. + assert(HostInputIndex != ~0u && "Host input index undefined??"); + Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = + CreateFileHandler(*InputBuffers[HostInputIndex]); + if (!FileHandlerOrErr) + return FileHandlerOrErr.takeError(); + + std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; + assert(FH); + + // Write header. + if (Error Err = FH->WriteHeader(OutputFile, InputBuffers)) + return Err; + + // Write all bundles along with the start/end markers. If an error was found + // writing the end of the bundle component, abort the bundle writing. + auto Input = InputBuffers.begin(); + for (auto &Triple : TargetNames) { + if (Error Err = FH->WriteBundleStart(OutputFile, Triple)) + return Err; + if (Error Err = FH->WriteBundle(OutputFile, **Input)) + return Err; + if (Error Err = FH->WriteBundleEnd(OutputFile, Triple)) + return Err; + ++Input; + } + return Error::success(); +} + +// Unbundle the files. Return true if an error was found. +static Error UnbundleFiles() { + // Open Input file. + ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = + MemoryBuffer::getFileOrSTDIN(InputFileNames.front()); + if (std::error_code EC = CodeOrErr.getError()) + return createFileError(InputFileNames.front(), EC); + + MemoryBuffer &Input = **CodeOrErr; + + // Select the right files handler. + Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = + CreateFileHandler(Input); + if (!FileHandlerOrErr) + return FileHandlerOrErr.takeError(); + + std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; + assert(FH); + + // Read the header of the bundled file. + if (Error Err = FH->ReadHeader(Input)) + return Err; + + // Create a work list that consist of the map triple/output file. + StringMap<StringRef> Worklist; + auto Output = OutputFileNames.begin(); + for (auto &Triple : TargetNames) { + Worklist[Triple] = *Output; + ++Output; + } + + // Read all the bundles that are in the work list. If we find no bundles we + // assume the file is meant for the host target. + bool FoundHostBundle = false; + while (!Worklist.empty()) { + Expected<Optional<StringRef>> CurTripleOrErr = FH->ReadBundleStart(Input); + if (!CurTripleOrErr) + return CurTripleOrErr.takeError(); + + // We don't have more bundles. + if (!*CurTripleOrErr) + break; + + StringRef CurTriple = **CurTripleOrErr; + assert(!CurTriple.empty()); + + auto Output = Worklist.find(CurTriple); + // The file may have more bundles for other targets, that we don't care + // about. Therefore, move on to the next triple + if (Output == Worklist.end()) + continue; + + // Check if the output file can be opened and copy the bundle to it. + std::error_code EC; + raw_fd_ostream OutputFile(Output->second, EC, sys::fs::OF_None); + if (EC) + return createFileError(Output->second, EC); + if (Error Err = FH->ReadBundle(OutputFile, Input)) + return Err; + if (Error Err = FH->ReadBundleEnd(Input)) + return Err; + Worklist.erase(Output); + + // Record if we found the host bundle. + if (hasHostKind(CurTriple)) + FoundHostBundle = true; + } + + // If no bundles were found, assume the input file is the host bundle and + // create empty files for the remaining targets. + if (Worklist.size() == TargetNames.size()) { + for (auto &E : Worklist) { + std::error_code EC; + raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); + if (EC) + return createFileError(E.second, EC); + + // If this entry has a host kind, copy the input file to the output file. + if (hasHostKind(E.first())) + OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); + } + return Error::success(); + } + + // If we found elements, we emit an error if none of those were for the host + // in case host bundle name was provided in command line. + if (!FoundHostBundle && HostInputIndex != ~0u) + return createStringError(inconvertibleErrorCode(), + "Can't find bundle for the host target"); + + // If we still have any elements in the worklist, create empty files for them. + for (auto &E : Worklist) { + std::error_code EC; + raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); + if (EC) + return createFileError(E.second, EC); + } + + return Error::success(); +} + +static void PrintVersion(raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n'; +} + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + + cl::HideUnrelatedOptions(ClangOffloadBundlerCategory); + cl::SetVersionPrinter(PrintVersion); + cl::ParseCommandLineOptions( + argc, argv, + "A tool to bundle several input files of the specified type <type> \n" + "referring to the same source file but different targets into a single \n" + "one. The resulting file can also be unbundled into different files by \n" + "this tool if -unbundle is provided.\n"); + + if (Help) { + cl::PrintHelpMessage(); + return 0; + } + + auto reportError = [argv](Error E) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); + }; + + bool Error = false; + if (Unbundle) { + if (InputFileNames.size() != 1) { + Error = true; + reportError(createStringError( + errc::invalid_argument, + "only one input file supported in unbundling mode")); + } + if (OutputFileNames.size() != TargetNames.size()) { + Error = true; + reportError(createStringError(errc::invalid_argument, + "number of output files and targets should " + "match in unbundling mode")); + } + } else { + if (OutputFileNames.size() != 1) { + Error = true; + reportError(createStringError( + errc::invalid_argument, + "only one output file supported in bundling mode")); + } + if (InputFileNames.size() != TargetNames.size()) { + Error = true; + reportError(createStringError( + errc::invalid_argument, + "number of input files and targets should match in bundling mode")); + } + } + + // Verify that the offload kinds and triples are known. We also check that we + // have exactly one host target. + unsigned Index = 0u; + unsigned HostTargetNum = 0u; + for (StringRef Target : TargetNames) { + StringRef Kind; + StringRef Triple; + getOffloadKindAndTriple(Target, Kind, Triple); + + bool KindIsValid = !Kind.empty(); + KindIsValid = KindIsValid && StringSwitch<bool>(Kind) + .Case("host", true) + .Case("openmp", true) + .Case("hip", true) + .Default(false); + + bool TripleIsValid = !Triple.empty(); + llvm::Triple T(Triple); + TripleIsValid &= T.getArch() != Triple::UnknownArch; + + if (!KindIsValid || !TripleIsValid) { + Error = true; + + SmallVector<char, 128u> Buf; + raw_svector_ostream Msg(Buf); + Msg << "invalid target '" << Target << "'"; + if (!KindIsValid) + Msg << ", unknown offloading kind '" << Kind << "'"; + if (!TripleIsValid) + Msg << ", unknown target triple '" << Triple << "'"; + reportError(createStringError(errc::invalid_argument, Msg.str())); + } + + if (KindIsValid && Kind == "host") { + ++HostTargetNum; + // Save the index of the input that refers to the host. + HostInputIndex = Index; + } + + ++Index; + } + + // Host triple is not really needed for unbundling operation, so do not + // treat missing host triple as error if we do unbundling. + if ((Unbundle && HostTargetNum > 1) || (!Unbundle && HostTargetNum != 1)) { + Error = true; + reportError(createStringError(errc::invalid_argument, + "expecting exactly one host target but got " + + Twine(HostTargetNum))); + } + + if (Error) + return 1; + + // Save the current executable directory as it will be useful to find other + // tools. + BundlerExecutable = sys::fs::getMainExecutable(argv[0], &BundlerExecutable); + + if (llvm::Error Err = Unbundle ? UnbundleFiles() : BundleFiles()) { + reportError(std::move(Err)); + return 1; + } + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-offload-wrapper/CMakeLists.txt b/gnu/llvm/clang/tools/clang-offload-wrapper/CMakeLists.txt new file mode 100644 index 00000000000..6f8940f88ea --- /dev/null +++ b/gnu/llvm/clang/tools/clang-offload-wrapper/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LLVM_LINK_COMPONENTS BitWriter Core Support TransformUtils) + +if(NOT CLANG_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_clang_tool(clang-offload-wrapper + ClangOffloadWrapper.cpp + + DEPENDS + ${tablegen_deps} + ) + +set(CLANG_OFFLOAD_WRAPPER_LIB_DEPS + clangBasic + ) + +add_dependencies(clang clang-offload-wrapper) + +clang_target_link_libraries(clang-offload-wrapper + PRIVATE + ${CLANG_OFFLOAD_WRAPPER_LIB_DEPS} + ) diff --git a/gnu/llvm/clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp b/gnu/llvm/clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp new file mode 100644 index 00000000000..c3863422adf --- /dev/null +++ b/gnu/llvm/clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp @@ -0,0 +1,371 @@ +//===-- clang-offload-wrapper/ClangOffloadWrapper.cpp -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation of the offload wrapper tool. It takes offload target binaries +/// as input and creates wrapper bitcode file containing target binaries +/// packaged as data. Wrapper bitcode also includes initialization code which +/// registers target binaries in offloading runtime at program startup. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Version.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include <cassert> +#include <cstdint> + +using namespace llvm; + +static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); + +// Mark all our options with this category, everything else (except for -version +// and -help) will be hidden. +static cl::OptionCategory + ClangOffloadWrapperCategory("clang-offload-wrapper options"); + +static cl::opt<std::string> Output("o", cl::Required, + cl::desc("Output filename"), + cl::value_desc("filename"), + cl::cat(ClangOffloadWrapperCategory)); + +static cl::list<std::string> Inputs(cl::Positional, cl::OneOrMore, + cl::desc("<input files>"), + cl::cat(ClangOffloadWrapperCategory)); + +static cl::opt<std::string> + Target("target", cl::Required, + cl::desc("Target triple for the output module"), + cl::value_desc("triple"), cl::cat(ClangOffloadWrapperCategory)); + +namespace { + +class BinaryWrapper { + LLVMContext C; + Module M; + + StructType *EntryTy = nullptr; + StructType *ImageTy = nullptr; + StructType *DescTy = nullptr; + +private: + IntegerType *getSizeTTy() { + switch (M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C))) { + case 4u: + return Type::getInt32Ty(C); + case 8u: + return Type::getInt64Ty(C); + } + llvm_unreachable("unsupported pointer type size"); + } + + // struct __tgt_offload_entry { + // void *addr; + // char *name; + // size_t size; + // int32_t flags; + // int32_t reserved; + // }; + StructType *getEntryTy() { + if (!EntryTy) + EntryTy = StructType::create("__tgt_offload_entry", Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), getSizeTTy(), + Type::getInt32Ty(C), Type::getInt32Ty(C)); + return EntryTy; + } + + PointerType *getEntryPtrTy() { return PointerType::getUnqual(getEntryTy()); } + + // struct __tgt_device_image { + // void *ImageStart; + // void *ImageEnd; + // __tgt_offload_entry *EntriesBegin; + // __tgt_offload_entry *EntriesEnd; + // }; + StructType *getDeviceImageTy() { + if (!ImageTy) + ImageTy = StructType::create("__tgt_device_image", Type::getInt8PtrTy(C), + Type::getInt8PtrTy(C), getEntryPtrTy(), + getEntryPtrTy()); + return ImageTy; + } + + PointerType *getDeviceImagePtrTy() { + return PointerType::getUnqual(getDeviceImageTy()); + } + + // struct __tgt_bin_desc { + // int32_t NumDeviceImages; + // __tgt_device_image *DeviceImages; + // __tgt_offload_entry *HostEntriesBegin; + // __tgt_offload_entry *HostEntriesEnd; + // }; + StructType *getBinDescTy() { + if (!DescTy) + DescTy = StructType::create("__tgt_bin_desc", Type::getInt32Ty(C), + getDeviceImagePtrTy(), getEntryPtrTy(), + getEntryPtrTy()); + return DescTy; + } + + PointerType *getBinDescPtrTy() { + return PointerType::getUnqual(getBinDescTy()); + } + + /// Creates binary descriptor for the given device images. Binary descriptor + /// is an object that is passed to the offloading runtime at program startup + /// and it describes all device images available in the executable or shared + /// library. It is defined as follows + /// + /// __attribute__((visibility("hidden"))) + /// extern __tgt_offload_entry *__start_omp_offloading_entries; + /// __attribute__((visibility("hidden"))) + /// extern __tgt_offload_entry *__stop_omp_offloading_entries; + /// + /// static const char Image0[] = { <Bufs.front() contents> }; + /// ... + /// static const char ImageN[] = { <Bufs.back() contents> }; + /// + /// static const __tgt_device_image Images[] = { + /// { + /// Image0, /*ImageStart*/ + /// Image0 + sizeof(Image0), /*ImageEnd*/ + /// __start_omp_offloading_entries, /*EntriesBegin*/ + /// __stop_omp_offloading_entries /*EntriesEnd*/ + /// }, + /// ... + /// { + /// ImageN, /*ImageStart*/ + /// ImageN + sizeof(ImageN), /*ImageEnd*/ + /// __start_omp_offloading_entries, /*EntriesBegin*/ + /// __stop_omp_offloading_entries /*EntriesEnd*/ + /// } + /// }; + /// + /// static const __tgt_bin_desc BinDesc = { + /// sizeof(Images) / sizeof(Images[0]), /*NumDeviceImages*/ + /// Images, /*DeviceImages*/ + /// __start_omp_offloading_entries, /*HostEntriesBegin*/ + /// __stop_omp_offloading_entries /*HostEntriesEnd*/ + /// }; + /// + /// Global variable that represents BinDesc is returned. + GlobalVariable *createBinDesc(ArrayRef<ArrayRef<char>> Bufs) { + // Create external begin/end symbols for the offload entries table. + auto *EntriesB = new GlobalVariable( + M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage, + /*Initializer*/ nullptr, "__start_omp_offloading_entries"); + EntriesB->setVisibility(GlobalValue::HiddenVisibility); + auto *EntriesE = new GlobalVariable( + M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage, + /*Initializer*/ nullptr, "__stop_omp_offloading_entries"); + EntriesE->setVisibility(GlobalValue::HiddenVisibility); + + // We assume that external begin/end symbols that we have created above will + // be defined by the linker. But linker will do that only if linker inputs + // have section with "omp_offloading_entries" name which is not guaranteed. + // So, we just create dummy zero sized object in the offload entries section + // to force linker to define those symbols. + auto *DummyInit = + ConstantAggregateZero::get(ArrayType::get(getEntryTy(), 0u)); + auto *DummyEntry = new GlobalVariable( + M, DummyInit->getType(), true, GlobalVariable::ExternalLinkage, + DummyInit, "__dummy.omp_offloading.entry"); + DummyEntry->setSection("omp_offloading_entries"); + DummyEntry->setVisibility(GlobalValue::HiddenVisibility); + + auto *Zero = ConstantInt::get(getSizeTTy(), 0u); + Constant *ZeroZero[] = {Zero, Zero}; + + // Create initializer for the images array. + SmallVector<Constant *, 4u> ImagesInits; + ImagesInits.reserve(Bufs.size()); + for (ArrayRef<char> Buf : Bufs) { + auto *Data = ConstantDataArray::get(C, Buf); + auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant*/ true, + GlobalVariable::InternalLinkage, Data, + ".omp_offloading.device_image"); + Image->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + auto *Size = ConstantInt::get(getSizeTTy(), Buf.size()); + Constant *ZeroSize[] = {Zero, Size}; + + auto *ImageB = ConstantExpr::getGetElementPtr(Image->getValueType(), + Image, ZeroZero); + auto *ImageE = ConstantExpr::getGetElementPtr(Image->getValueType(), + Image, ZeroSize); + + ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(), ImageB, + ImageE, EntriesB, EntriesE)); + } + + // Then create images array. + auto *ImagesData = ConstantArray::get( + ArrayType::get(getDeviceImageTy(), ImagesInits.size()), ImagesInits); + + auto *Images = + new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true, + GlobalValue::InternalLinkage, ImagesData, + ".omp_offloading.device_images"); + Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + auto *ImagesB = ConstantExpr::getGetElementPtr(Images->getValueType(), + Images, ZeroZero); + + // And finally create the binary descriptor object. + auto *DescInit = ConstantStruct::get( + getBinDescTy(), + ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), ImagesB, + EntriesB, EntriesE); + + return new GlobalVariable(M, DescInit->getType(), /*isConstant*/ true, + GlobalValue::InternalLinkage, DescInit, + ".omp_offloading.descriptor"); + } + + void createRegisterFunction(GlobalVariable *BinDesc) { + auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false); + auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage, + ".omp_offloading.descriptor_reg", &M); + Func->setSection(".text.startup"); + + // Get __tgt_register_lib function declaration. + auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(), + /*isVarArg*/ false); + FunctionCallee RegFuncC = + M.getOrInsertFunction("__tgt_register_lib", RegFuncTy); + + // Construct function body + IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func)); + Builder.CreateCall(RegFuncC, BinDesc); + Builder.CreateRetVoid(); + + // Add this function to constructors. + appendToGlobalCtors(M, Func, 0); + } + + void createUnregisterFunction(GlobalVariable *BinDesc) { + auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false); + auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage, + ".omp_offloading.descriptor_unreg", &M); + Func->setSection(".text.startup"); + + // Get __tgt_unregister_lib function declaration. + auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(), + /*isVarArg*/ false); + FunctionCallee UnRegFuncC = + M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy); + + // Construct function body + IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func)); + Builder.CreateCall(UnRegFuncC, BinDesc); + Builder.CreateRetVoid(); + + // Add this function to global destructors. + appendToGlobalDtors(M, Func, 0); + } + +public: + BinaryWrapper(StringRef Target) : M("offload.wrapper.object", C) { + M.setTargetTriple(Target); + } + + const Module &wrapBinaries(ArrayRef<ArrayRef<char>> Binaries) { + GlobalVariable *Desc = createBinDesc(Binaries); + assert(Desc && "no binary descriptor"); + createRegisterFunction(Desc); + createUnregisterFunction(Desc); + return M; + } +}; + +} // anonymous namespace + +int main(int argc, const char **argv) { + sys::PrintStackTraceOnErrorSignal(argv[0]); + + cl::HideUnrelatedOptions(ClangOffloadWrapperCategory); + cl::SetVersionPrinter([](raw_ostream &OS) { + OS << clang::getClangToolFullVersion("clang-offload-wrapper") << '\n'; + }); + cl::ParseCommandLineOptions( + argc, argv, + "A tool to create a wrapper bitcode for offload target binaries. Takes " + "offload\ntarget binaries as input and produces bitcode file containing " + "target binaries packaged\nas data and initialization code which " + "registers target binaries in offload runtime.\n"); + + if (Help) { + cl::PrintHelpMessage(); + return 0; + } + + auto reportError = [argv](Error E) { + logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0])); + }; + + if (Triple(Target).getArch() == Triple::UnknownArch) { + reportError(createStringError( + errc::invalid_argument, "'" + Target + "': unsupported target triple")); + return 1; + } + + // Read device binaries. + SmallVector<std::unique_ptr<MemoryBuffer>, 4u> Buffers; + SmallVector<ArrayRef<char>, 4u> Images; + Buffers.reserve(Inputs.size()); + Images.reserve(Inputs.size()); + for (const std::string &File : Inputs) { + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFileOrSTDIN(File); + if (!BufOrErr) { + reportError(createFileError(File, BufOrErr.getError())); + return 1; + } + const std::unique_ptr<MemoryBuffer> &Buf = + Buffers.emplace_back(std::move(*BufOrErr)); + Images.emplace_back(Buf->getBufferStart(), Buf->getBufferSize()); + } + + // Create the output file to write the resulting bitcode to. + std::error_code EC; + ToolOutputFile Out(Output, EC, sys::fs::OF_None); + if (EC) { + reportError(createFileError(Output, EC)); + return 1; + } + + // Create a wrapper for device binaries and write its bitcode to the file. + WriteBitcodeToFile(BinaryWrapper(Target).wrapBinaries( + makeArrayRef(Images.data(), Images.size())), + Out.os()); + if (Out.os().has_error()) { + reportError(createFileError(Output, Out.os().error())); + return 1; + } + + // Success. + Out.keep(); + return 0; +} diff --git a/gnu/llvm/clang/tools/clang-refactor/CMakeLists.txt b/gnu/llvm/clang/tools/clang-refactor/CMakeLists.txt new file mode 100644 index 00000000000..a21d84d5385 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-refactor/CMakeLists.txt @@ -0,0 +1,23 @@ +set(LLVM_LINK_COMPONENTS + Option + Support + ) + +add_clang_tool(clang-refactor + ClangRefactor.cpp + TestSupport.cpp + ) + +clang_target_link_libraries(clang-refactor + PRIVATE + clangAST + clangBasic + clangFormat + clangFrontend + clangLex + clangRewrite + clangSerialization + clangTooling + clangToolingCore + clangToolingRefactoring + ) diff --git a/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp b/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp new file mode 100644 index 00000000000..8b44c7fa6ed --- /dev/null +++ b/gnu/llvm/clang/tools/clang-refactor/ClangRefactor.cpp @@ -0,0 +1,639 @@ +//===--- ClangRefactor.cpp - Clang-based refactoring tool -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a clang-refactor tool that performs various +/// source transformations. +/// +//===----------------------------------------------------------------------===// + +#include "TestSupport.h" +#include "clang/Frontend/CommandLineSourceLoc.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Refactoring/RefactoringAction.h" +#include "clang/Tooling/Refactoring/RefactoringOptions.h" +#include "clang/Tooling/Refactoring/Rename/RenamingAction.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include <string> + +using namespace clang; +using namespace tooling; +using namespace refactor; +namespace cl = llvm::cl; + +namespace opts { + +static cl::OptionCategory CommonRefactorOptions("Refactoring options"); + +static cl::opt<bool> Verbose("v", cl::desc("Use verbose output"), + cl::cat(cl::GeneralCategory), + cl::sub(*cl::AllSubCommands)); + +static cl::opt<bool> Inplace("i", cl::desc("Inplace edit <file>s"), + cl::cat(cl::GeneralCategory), + cl::sub(*cl::AllSubCommands)); + +} // end namespace opts + +namespace { + +/// Stores the parsed `-selection` argument. +class SourceSelectionArgument { +public: + virtual ~SourceSelectionArgument() {} + + /// Parse the `-selection` argument. + /// + /// \returns A valid argument when the parse succedeed, null otherwise. + static std::unique_ptr<SourceSelectionArgument> fromString(StringRef Value); + + /// Prints any additional state associated with the selection argument to + /// the given output stream. + virtual void print(raw_ostream &OS) {} + + /// Returns a replacement refactoring result consumer (if any) that should + /// consume the results of a refactoring operation. + /// + /// The replacement refactoring result consumer is used by \c + /// TestSourceSelectionArgument to inject a test-specific result handling + /// logic into the refactoring operation. The test-specific consumer + /// ensures that the individual results in a particular test group are + /// identical. + virtual std::unique_ptr<ClangRefactorToolConsumerInterface> + createCustomConsumer() { + return nullptr; + } + + /// Runs the give refactoring function for each specified selection. + /// + /// \returns true if an error occurred, false otherwise. + virtual bool + forAllRanges(const SourceManager &SM, + llvm::function_ref<void(SourceRange R)> Callback) = 0; +}; + +/// Stores the parsed -selection=test:<filename> option. +class TestSourceSelectionArgument final : public SourceSelectionArgument { +public: + TestSourceSelectionArgument(TestSelectionRangesInFile TestSelections) + : TestSelections(std::move(TestSelections)) {} + + void print(raw_ostream &OS) override { TestSelections.dump(OS); } + + std::unique_ptr<ClangRefactorToolConsumerInterface> + createCustomConsumer() override { + return TestSelections.createConsumer(); + } + + /// Testing support: invokes the selection action for each selection range in + /// the test file. + bool forAllRanges(const SourceManager &SM, + llvm::function_ref<void(SourceRange R)> Callback) override { + return TestSelections.foreachRange(SM, Callback); + } + +private: + TestSelectionRangesInFile TestSelections; +}; + +/// Stores the parsed -selection=filename:line:column[-line:column] option. +class SourceRangeSelectionArgument final : public SourceSelectionArgument { +public: + SourceRangeSelectionArgument(ParsedSourceRange Range) + : Range(std::move(Range)) {} + + bool forAllRanges(const SourceManager &SM, + llvm::function_ref<void(SourceRange R)> Callback) override { + auto FE = SM.getFileManager().getFile(Range.FileName); + FileID FID = FE ? SM.translateFile(*FE) : FileID(); + if (!FE || FID.isInvalid()) { + llvm::errs() << "error: -selection=" << Range.FileName + << ":... : given file is not in the target TU\n"; + return true; + } + + SourceLocation Start = SM.getMacroArgExpandedLocation( + SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second)); + SourceLocation End = SM.getMacroArgExpandedLocation( + SM.translateLineCol(FID, Range.End.first, Range.End.second)); + if (Start.isInvalid() || End.isInvalid()) { + llvm::errs() << "error: -selection=" << Range.FileName << ':' + << Range.Begin.first << ':' << Range.Begin.second << '-' + << Range.End.first << ':' << Range.End.second + << " : invalid source location\n"; + return true; + } + Callback(SourceRange(Start, End)); + return false; + } + +private: + ParsedSourceRange Range; +}; + +std::unique_ptr<SourceSelectionArgument> +SourceSelectionArgument::fromString(StringRef Value) { + if (Value.startswith("test:")) { + StringRef Filename = Value.drop_front(strlen("test:")); + Optional<TestSelectionRangesInFile> ParsedTestSelection = + findTestSelectionRanges(Filename); + if (!ParsedTestSelection) + return nullptr; // A parsing error was already reported. + return std::make_unique<TestSourceSelectionArgument>( + std::move(*ParsedTestSelection)); + } + Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value); + if (Range) + return std::make_unique<SourceRangeSelectionArgument>(std::move(*Range)); + llvm::errs() << "error: '-selection' option must be specified using " + "<file>:<line>:<column> or " + "<file>:<line>:<column>-<line>:<column> format\n"; + return nullptr; +} + +/// A container that stores the command-line options used by a single +/// refactoring option. +class RefactoringActionCommandLineOptions { +public: + void addStringOption(const RefactoringOption &Option, + std::unique_ptr<cl::opt<std::string>> CLOption) { + StringOptions[&Option] = std::move(CLOption); + } + + const cl::opt<std::string> & + getStringOption(const RefactoringOption &Opt) const { + auto It = StringOptions.find(&Opt); + return *It->second; + } + +private: + llvm::DenseMap<const RefactoringOption *, + std::unique_ptr<cl::opt<std::string>>> + StringOptions; +}; + +/// Passes the command-line option values to the options used by a single +/// refactoring action rule. +class CommandLineRefactoringOptionVisitor final + : public RefactoringOptionVisitor { +public: + CommandLineRefactoringOptionVisitor( + const RefactoringActionCommandLineOptions &Options) + : Options(Options) {} + + void visit(const RefactoringOption &Opt, + Optional<std::string> &Value) override { + const cl::opt<std::string> &CLOpt = Options.getStringOption(Opt); + if (!CLOpt.getValue().empty()) { + Value = CLOpt.getValue(); + return; + } + Value = None; + if (Opt.isRequired()) + MissingRequiredOptions.push_back(&Opt); + } + + ArrayRef<const RefactoringOption *> getMissingRequiredOptions() const { + return MissingRequiredOptions; + } + +private: + llvm::SmallVector<const RefactoringOption *, 4> MissingRequiredOptions; + const RefactoringActionCommandLineOptions &Options; +}; + +/// Creates the refactoring options used by all the rules in a single +/// refactoring action. +class CommandLineRefactoringOptionCreator final + : public RefactoringOptionVisitor { +public: + CommandLineRefactoringOptionCreator( + cl::OptionCategory &Category, cl::SubCommand &Subcommand, + RefactoringActionCommandLineOptions &Options) + : Category(Category), Subcommand(Subcommand), Options(Options) {} + + void visit(const RefactoringOption &Opt, Optional<std::string> &) override { + if (Visited.insert(&Opt).second) + Options.addStringOption(Opt, create<std::string>(Opt)); + } + +private: + template <typename T> + std::unique_ptr<cl::opt<T>> create(const RefactoringOption &Opt) { + if (!OptionNames.insert(Opt.getName()).second) + llvm::report_fatal_error("Multiple identical refactoring options " + "specified for one refactoring action"); + // FIXME: cl::Required can be specified when this option is present + // in all rules in an action. + return std::make_unique<cl::opt<T>>( + Opt.getName(), cl::desc(Opt.getDescription()), cl::Optional, + cl::cat(Category), cl::sub(Subcommand)); + } + + llvm::SmallPtrSet<const RefactoringOption *, 8> Visited; + llvm::StringSet<> OptionNames; + cl::OptionCategory &Category; + cl::SubCommand &Subcommand; + RefactoringActionCommandLineOptions &Options; +}; + +/// A subcommand that corresponds to individual refactoring action. +class RefactoringActionSubcommand : public cl::SubCommand { +public: + RefactoringActionSubcommand(std::unique_ptr<RefactoringAction> Action, + RefactoringActionRules ActionRules, + cl::OptionCategory &Category) + : SubCommand(Action->getCommand(), Action->getDescription()), + Action(std::move(Action)), ActionRules(std::move(ActionRules)) { + // Check if the selection option is supported. + for (const auto &Rule : this->ActionRules) { + if (Rule->hasSelectionRequirement()) { + Selection = std::make_unique<cl::opt<std::string>>( + "selection", + cl::desc( + "The selected source range in which the refactoring should " + "be initiated (<file>:<line>:<column>-<line>:<column> or " + "<file>:<line>:<column>)"), + cl::cat(Category), cl::sub(*this)); + break; + } + } + // Create the refactoring options. + for (const auto &Rule : this->ActionRules) { + CommandLineRefactoringOptionCreator OptionCreator(Category, *this, + Options); + Rule->visitRefactoringOptions(OptionCreator); + } + } + + ~RefactoringActionSubcommand() { unregisterSubCommand(); } + + const RefactoringActionRules &getActionRules() const { return ActionRules; } + + /// Parses the "-selection" command-line argument. + /// + /// \returns true on error, false otherwise. + bool parseSelectionArgument() { + if (Selection) { + ParsedSelection = SourceSelectionArgument::fromString(*Selection); + if (!ParsedSelection) + return true; + } + return false; + } + + SourceSelectionArgument *getSelection() const { + assert(Selection && "selection not supported!"); + return ParsedSelection.get(); + } + + const RefactoringActionCommandLineOptions &getOptions() const { + return Options; + } + +private: + std::unique_ptr<RefactoringAction> Action; + RefactoringActionRules ActionRules; + std::unique_ptr<cl::opt<std::string>> Selection; + std::unique_ptr<SourceSelectionArgument> ParsedSelection; + RefactoringActionCommandLineOptions Options; +}; + +class ClangRefactorConsumer final : public ClangRefactorToolConsumerInterface { +public: + ClangRefactorConsumer(AtomicChanges &Changes) : SourceChanges(&Changes) {} + + void handleError(llvm::Error Err) override { + Optional<PartialDiagnosticAt> Diag = DiagnosticError::take(Err); + if (!Diag) { + llvm::errs() << llvm::toString(std::move(Err)) << "\n"; + return; + } + llvm::cantFail(std::move(Err)); // This is a success. + DiagnosticBuilder DB( + getDiags().Report(Diag->first, Diag->second.getDiagID())); + Diag->second.Emit(DB); + } + + void handle(AtomicChanges Changes) override { + SourceChanges->insert(SourceChanges->begin(), Changes.begin(), + Changes.end()); + } + + void handle(SymbolOccurrences Occurrences) override { + llvm_unreachable("symbol occurrence results are not handled yet"); + } + +private: + AtomicChanges *SourceChanges; +}; + +class ClangRefactorTool { +public: + ClangRefactorTool() + : SelectedSubcommand(nullptr), MatchingRule(nullptr), + Consumer(new ClangRefactorConsumer(Changes)), HasFailed(false) { + std::vector<std::unique_ptr<RefactoringAction>> Actions = + createRefactoringActions(); + + // Actions must have unique command names so that we can map them to one + // subcommand. + llvm::StringSet<> CommandNames; + for (const auto &Action : Actions) { + if (!CommandNames.insert(Action->getCommand()).second) { + llvm::errs() << "duplicate refactoring action command '" + << Action->getCommand() << "'!"; + exit(1); + } + } + + // Create subcommands and command-line options. + for (auto &Action : Actions) { + SubCommands.push_back(std::make_unique<RefactoringActionSubcommand>( + std::move(Action), Action->createActiveActionRules(), + opts::CommonRefactorOptions)); + } + } + + // Initializes the selected subcommand and refactoring rule based on the + // command line options. + llvm::Error Init() { + auto Subcommand = getSelectedSubcommand(); + if (!Subcommand) + return Subcommand.takeError(); + auto Rule = getMatchingRule(**Subcommand); + if (!Rule) + return Rule.takeError(); + + SelectedSubcommand = *Subcommand; + MatchingRule = *Rule; + + return llvm::Error::success(); + } + + bool hasFailed() const { return HasFailed; } + + using TUCallbackType = std::function<void(ASTContext &)>; + + // Callback of an AST action. This invokes the matching rule on the given AST. + void callback(ASTContext &AST) { + assert(SelectedSubcommand && MatchingRule && Consumer); + RefactoringRuleContext Context(AST.getSourceManager()); + Context.setASTContext(AST); + + // If the selection option is test specific, we use a test-specific + // consumer. + std::unique_ptr<ClangRefactorToolConsumerInterface> TestConsumer; + bool HasSelection = MatchingRule->hasSelectionRequirement(); + if (HasSelection) + TestConsumer = SelectedSubcommand->getSelection()->createCustomConsumer(); + ClangRefactorToolConsumerInterface *ActiveConsumer = + TestConsumer ? TestConsumer.get() : Consumer.get(); + ActiveConsumer->beginTU(AST); + + auto InvokeRule = [&](RefactoringResultConsumer &Consumer) { + if (opts::Verbose) + logInvocation(*SelectedSubcommand, Context); + MatchingRule->invoke(*ActiveConsumer, Context); + }; + if (HasSelection) { + assert(SelectedSubcommand->getSelection() && + "Missing selection argument?"); + if (opts::Verbose) + SelectedSubcommand->getSelection()->print(llvm::outs()); + if (SelectedSubcommand->getSelection()->forAllRanges( + Context.getSources(), [&](SourceRange R) { + Context.setSelectionRange(R); + InvokeRule(*ActiveConsumer); + })) + HasFailed = true; + ActiveConsumer->endTU(); + return; + } + InvokeRule(*ActiveConsumer); + ActiveConsumer->endTU(); + } + + llvm::Expected<std::unique_ptr<FrontendActionFactory>> + getFrontendActionFactory() { + class ToolASTConsumer : public ASTConsumer { + public: + TUCallbackType Callback; + ToolASTConsumer(TUCallbackType Callback) + : Callback(std::move(Callback)) {} + + void HandleTranslationUnit(ASTContext &Context) override { + Callback(Context); + } + }; + class ToolASTAction : public ASTFrontendAction { + public: + explicit ToolASTAction(TUCallbackType Callback) + : Callback(std::move(Callback)) {} + + protected: + std::unique_ptr<clang::ASTConsumer> + CreateASTConsumer(clang::CompilerInstance &compiler, + StringRef /* dummy */) override { + std::unique_ptr<clang::ASTConsumer> Consumer{ + new ToolASTConsumer(Callback)}; + return Consumer; + } + + private: + TUCallbackType Callback; + }; + + class ToolActionFactory : public FrontendActionFactory { + public: + ToolActionFactory(TUCallbackType Callback) + : Callback(std::move(Callback)) {} + + std::unique_ptr<FrontendAction> create() override { + return std::make_unique<ToolASTAction>(Callback); + } + + private: + TUCallbackType Callback; + }; + + return std::make_unique<ToolActionFactory>( + [this](ASTContext &AST) { return callback(AST); }); + } + + // FIXME(ioeric): this seems to only works for changes in a single file at + // this point. + bool applySourceChanges() { + std::set<std::string> Files; + for (const auto &Change : Changes) + Files.insert(Change.getFilePath()); + // FIXME: Add automatic formatting support as well. + tooling::ApplyChangesSpec Spec; + // FIXME: We should probably cleanup the result by default as well. + Spec.Cleanup = false; + for (const auto &File : Files) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr = + llvm::MemoryBuffer::getFile(File); + if (!BufferErr) { + llvm::errs() << "error: failed to open " << File << " for rewriting\n"; + return true; + } + auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(), + Changes, Spec); + if (!Result) { + llvm::errs() << toString(Result.takeError()); + return true; + } + + if (opts::Inplace) { + std::error_code EC; + llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::OF_Text); + if (EC) { + llvm::errs() << EC.message() << "\n"; + return true; + } + OS << *Result; + continue; + } + + llvm::outs() << *Result; + } + return false; + } + +private: + /// Logs an individual refactoring action invocation to STDOUT. + void logInvocation(RefactoringActionSubcommand &Subcommand, + const RefactoringRuleContext &Context) { + llvm::outs() << "invoking action '" << Subcommand.getName() << "':\n"; + if (Context.getSelectionRange().isValid()) { + SourceRange R = Context.getSelectionRange(); + llvm::outs() << " -selection="; + R.getBegin().print(llvm::outs(), Context.getSources()); + llvm::outs() << " -> "; + R.getEnd().print(llvm::outs(), Context.getSources()); + llvm::outs() << "\n"; + } + } + + llvm::Expected<RefactoringActionRule *> + getMatchingRule(RefactoringActionSubcommand &Subcommand) { + SmallVector<RefactoringActionRule *, 4> MatchingRules; + llvm::StringSet<> MissingOptions; + + for (const auto &Rule : Subcommand.getActionRules()) { + CommandLineRefactoringOptionVisitor Visitor(Subcommand.getOptions()); + Rule->visitRefactoringOptions(Visitor); + if (Visitor.getMissingRequiredOptions().empty()) { + if (!Rule->hasSelectionRequirement()) { + MatchingRules.push_back(Rule.get()); + } else { + Subcommand.parseSelectionArgument(); + if (Subcommand.getSelection()) { + MatchingRules.push_back(Rule.get()); + } else { + MissingOptions.insert("selection"); + } + } + } + for (const RefactoringOption *Opt : Visitor.getMissingRequiredOptions()) + MissingOptions.insert(Opt->getName()); + } + if (MatchingRules.empty()) { + std::string Error; + llvm::raw_string_ostream OS(Error); + OS << "ERROR: '" << Subcommand.getName() + << "' can't be invoked with the given arguments:\n"; + for (const auto &Opt : MissingOptions) + OS << " missing '-" << Opt.getKey() << "' option\n"; + OS.flush(); + return llvm::make_error<llvm::StringError>( + Error, llvm::inconvertibleErrorCode()); + } + if (MatchingRules.size() != 1) { + return llvm::make_error<llvm::StringError>( + llvm::Twine("ERROR: more than one matching rule of action") + + Subcommand.getName() + "was found with given options.", + llvm::inconvertibleErrorCode()); + } + return MatchingRules.front(); + } + // Figure out which action is specified by the user. The user must specify the + // action using a command-line subcommand, e.g. the invocation `clang-refactor + // local-rename` corresponds to the `LocalRename` refactoring action. All + // subcommands must have a unique names. This allows us to figure out which + // refactoring action should be invoked by looking at the first subcommand + // that's enabled by LLVM's command-line parser. + llvm::Expected<RefactoringActionSubcommand *> getSelectedSubcommand() { + auto It = llvm::find_if( + SubCommands, + [](const std::unique_ptr<RefactoringActionSubcommand> &SubCommand) { + return !!(*SubCommand); + }); + if (It == SubCommands.end()) { + std::string Error; + llvm::raw_string_ostream OS(Error); + OS << "error: no refactoring action given\n"; + OS << "note: the following actions are supported:\n"; + for (const auto &Subcommand : SubCommands) + OS.indent(2) << Subcommand->getName() << "\n"; + OS.flush(); + return llvm::make_error<llvm::StringError>( + Error, llvm::inconvertibleErrorCode()); + } + RefactoringActionSubcommand *Subcommand = &(**It); + return Subcommand; + } + + std::vector<std::unique_ptr<RefactoringActionSubcommand>> SubCommands; + RefactoringActionSubcommand *SelectedSubcommand; + RefactoringActionRule *MatchingRule; + std::unique_ptr<ClangRefactorToolConsumerInterface> Consumer; + AtomicChanges Changes; + bool HasFailed; +}; + +} // end anonymous namespace + +int main(int argc, const char **argv) { + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + + ClangRefactorTool RefactorTool; + + CommonOptionsParser Options( + argc, argv, cl::GeneralCategory, cl::ZeroOrMore, + "Clang-based refactoring tool for C, C++ and Objective-C"); + + if (auto Err = RefactorTool.Init()) { + llvm::errs() << llvm::toString(std::move(Err)) << "\n"; + return 1; + } + + auto ActionFactory = RefactorTool.getFrontendActionFactory(); + if (!ActionFactory) { + llvm::errs() << llvm::toString(ActionFactory.takeError()) << "\n"; + return 1; + } + ClangTool Tool(Options.getCompilations(), Options.getSourcePathList()); + bool Failed = false; + if (Tool.run(ActionFactory->get()) != 0) { + llvm::errs() << "Failed to run refactoring action on files\n"; + // It is possible that TUs are broken while changes are generated correctly, + // so we still try applying changes. + Failed = true; + } + return RefactorTool.applySourceChanges() || Failed || + RefactorTool.hasFailed(); +} diff --git a/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp b/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp new file mode 100644 index 00000000000..617a671d127 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-refactor/TestSupport.cpp @@ -0,0 +1,392 @@ +//===--- TestSupport.cpp - Clang-based refactoring tool -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements routines that provide refactoring testing +/// utilities. +/// +//===----------------------------------------------------------------------===// + +#include "TestSupport.h" +#include "clang/Basic/DiagnosticError.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +namespace clang { +namespace refactor { + +void TestSelectionRangesInFile::dump(raw_ostream &OS) const { + for (const auto &Group : GroupedRanges) { + OS << "Test selection group '" << Group.Name << "':\n"; + for (const auto &Range : Group.Ranges) { + OS << " " << Range.Begin << "-" << Range.End << "\n"; + } + } +} + +bool TestSelectionRangesInFile::foreachRange( + const SourceManager &SM, + llvm::function_ref<void(SourceRange)> Callback) const { + auto FE = SM.getFileManager().getFile(Filename); + FileID FID = FE ? SM.translateFile(*FE) : FileID(); + if (!FE || FID.isInvalid()) { + llvm::errs() << "error: -selection=test:" << Filename + << " : given file is not in the target TU"; + return true; + } + SourceLocation FileLoc = SM.getLocForStartOfFile(FID); + for (const auto &Group : GroupedRanges) { + for (const TestSelectionRange &Range : Group.Ranges) { + // Translate the offset pair to a true source range. + SourceLocation Start = + SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Range.Begin)); + SourceLocation End = + SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Range.End)); + assert(Start.isValid() && End.isValid() && "unexpected invalid range"); + Callback(SourceRange(Start, End)); + } + } + return false; +} + +namespace { + +void dumpChanges(const tooling::AtomicChanges &Changes, raw_ostream &OS) { + for (const auto &Change : Changes) + OS << const_cast<tooling::AtomicChange &>(Change).toYAMLString() << "\n"; +} + +bool areChangesSame(const tooling::AtomicChanges &LHS, + const tooling::AtomicChanges &RHS) { + if (LHS.size() != RHS.size()) + return false; + for (auto I : llvm::zip(LHS, RHS)) { + if (!(std::get<0>(I) == std::get<1>(I))) + return false; + } + return true; +} + +bool printRewrittenSources(const tooling::AtomicChanges &Changes, + raw_ostream &OS) { + std::set<std::string> Files; + for (const auto &Change : Changes) + Files.insert(Change.getFilePath()); + tooling::ApplyChangesSpec Spec; + Spec.Cleanup = false; + for (const auto &File : Files) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr = + llvm::MemoryBuffer::getFile(File); + if (!BufferErr) { + llvm::errs() << "failed to open" << File << "\n"; + return true; + } + auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(), + Changes, Spec); + if (!Result) { + llvm::errs() << toString(Result.takeError()); + return true; + } + OS << *Result; + } + return false; +} + +class TestRefactoringResultConsumer final + : public ClangRefactorToolConsumerInterface { +public: + TestRefactoringResultConsumer(const TestSelectionRangesInFile &TestRanges) + : TestRanges(TestRanges) { + Results.push_back({}); + } + + ~TestRefactoringResultConsumer() { + // Ensure all results are checked. + for (auto &Group : Results) { + for (auto &Result : Group) { + if (!Result) { + (void)llvm::toString(Result.takeError()); + } + } + } + } + + void handleError(llvm::Error Err) override { handleResult(std::move(Err)); } + + void handle(tooling::AtomicChanges Changes) override { + handleResult(std::move(Changes)); + } + + void handle(tooling::SymbolOccurrences Occurrences) override { + tooling::RefactoringResultConsumer::handle(std::move(Occurrences)); + } + +private: + bool handleAllResults(); + + void handleResult(Expected<tooling::AtomicChanges> Result) { + Results.back().push_back(std::move(Result)); + size_t GroupIndex = Results.size() - 1; + if (Results.back().size() >= + TestRanges.GroupedRanges[GroupIndex].Ranges.size()) { + ++GroupIndex; + if (GroupIndex >= TestRanges.GroupedRanges.size()) { + if (handleAllResults()) + exit(1); // error has occurred. + return; + } + Results.push_back({}); + } + } + + const TestSelectionRangesInFile &TestRanges; + std::vector<std::vector<Expected<tooling::AtomicChanges>>> Results; +}; + +std::pair<unsigned, unsigned> getLineColumn(StringRef Filename, + unsigned Offset) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile = + MemoryBuffer::getFile(Filename); + if (!ErrOrFile) + return {0, 0}; + StringRef Source = ErrOrFile.get()->getBuffer(); + Source = Source.take_front(Offset); + size_t LastLine = Source.find_last_of("\r\n"); + return {Source.count('\n') + 1, + (LastLine == StringRef::npos ? Offset : Offset - LastLine) + 1}; +} + +} // end anonymous namespace + +bool TestRefactoringResultConsumer::handleAllResults() { + bool Failed = false; + for (auto &Group : llvm::enumerate(Results)) { + // All ranges in the group must produce the same result. + Optional<tooling::AtomicChanges> CanonicalResult; + Optional<std::string> CanonicalErrorMessage; + for (auto &I : llvm::enumerate(Group.value())) { + Expected<tooling::AtomicChanges> &Result = I.value(); + std::string ErrorMessage; + bool HasResult = !!Result; + if (!HasResult) { + handleAllErrors( + Result.takeError(), + [&](StringError &Err) { ErrorMessage = Err.getMessage(); }, + [&](DiagnosticError &Err) { + const PartialDiagnosticAt &Diag = Err.getDiagnostic(); + llvm::SmallString<100> DiagText; + Diag.second.EmitToString(getDiags(), DiagText); + ErrorMessage = DiagText.str().str(); + }); + } + if (!CanonicalResult && !CanonicalErrorMessage) { + if (HasResult) + CanonicalResult = std::move(*Result); + else + CanonicalErrorMessage = std::move(ErrorMessage); + continue; + } + + // Verify that this result corresponds to the canonical result. + if (CanonicalErrorMessage) { + // The error messages must match. + if (!HasResult && ErrorMessage == *CanonicalErrorMessage) + continue; + } else { + assert(CanonicalResult && "missing canonical result"); + // The results must match. + if (HasResult && areChangesSame(*Result, *CanonicalResult)) + continue; + } + Failed = true; + // Report the mismatch. + std::pair<unsigned, unsigned> LineColumn = getLineColumn( + TestRanges.Filename, + TestRanges.GroupedRanges[Group.index()].Ranges[I.index()].Begin); + llvm::errs() + << "error: unexpected refactoring result for range starting at " + << LineColumn.first << ':' << LineColumn.second << " in group '" + << TestRanges.GroupedRanges[Group.index()].Name << "':\n "; + if (HasResult) + llvm::errs() << "valid result"; + else + llvm::errs() << "error '" << ErrorMessage << "'"; + llvm::errs() << " does not match initial "; + if (CanonicalErrorMessage) + llvm::errs() << "error '" << *CanonicalErrorMessage << "'\n"; + else + llvm::errs() << "valid result\n"; + if (HasResult && !CanonicalErrorMessage) { + llvm::errs() << " Expected to Produce:\n"; + dumpChanges(*CanonicalResult, llvm::errs()); + llvm::errs() << " Produced:\n"; + dumpChanges(*Result, llvm::errs()); + } + } + + // Dump the results: + const auto &TestGroup = TestRanges.GroupedRanges[Group.index()]; + if (!CanonicalResult) { + llvm::outs() << TestGroup.Ranges.size() << " '" << TestGroup.Name + << "' results:\n"; + llvm::outs() << *CanonicalErrorMessage << "\n"; + } else { + llvm::outs() << TestGroup.Ranges.size() << " '" << TestGroup.Name + << "' results:\n"; + if (printRewrittenSources(*CanonicalResult, llvm::outs())) + return true; + } + } + return Failed; +} + +std::unique_ptr<ClangRefactorToolConsumerInterface> +TestSelectionRangesInFile::createConsumer() const { + return std::make_unique<TestRefactoringResultConsumer>(*this); +} + +/// Adds the \p ColumnOffset to file offset \p Offset, without going past a +/// newline. +static unsigned addColumnOffset(StringRef Source, unsigned Offset, + unsigned ColumnOffset) { + if (!ColumnOffset) + return Offset; + StringRef Substr = Source.drop_front(Offset).take_front(ColumnOffset); + size_t NewlinePos = Substr.find_first_of("\r\n"); + return Offset + + (NewlinePos == StringRef::npos ? ColumnOffset : (unsigned)NewlinePos); +} + +static unsigned addEndLineOffsetAndEndColumn(StringRef Source, unsigned Offset, + unsigned LineNumberOffset, + unsigned Column) { + StringRef Line = Source.drop_front(Offset); + unsigned LineOffset = 0; + for (; LineNumberOffset != 0; --LineNumberOffset) { + size_t NewlinePos = Line.find_first_of("\r\n"); + // Line offset goes out of bounds. + if (NewlinePos == StringRef::npos) + break; + LineOffset += NewlinePos + 1; + Line = Line.drop_front(NewlinePos + 1); + } + // Source now points to the line at +lineOffset; + size_t LineStart = Source.find_last_of("\r\n", /*From=*/Offset + LineOffset); + return addColumnOffset( + Source, LineStart == StringRef::npos ? 0 : LineStart + 1, Column - 1); +} + +Optional<TestSelectionRangesInFile> +findTestSelectionRanges(StringRef Filename) { + ErrorOr<std::unique_ptr<MemoryBuffer>> ErrOrFile = + MemoryBuffer::getFile(Filename); + if (!ErrOrFile) { + llvm::errs() << "error: -selection=test:" << Filename + << " : could not open the given file"; + return None; + } + StringRef Source = ErrOrFile.get()->getBuffer(); + + // See the doc comment for this function for the explanation of this + // syntax. + static const Regex RangeRegex( + "range[[:blank:]]*([[:alpha:]_]*)?[[:blank:]]*=[[:" + "blank:]]*(\\+[[:digit:]]+)?[[:blank:]]*(->[[:blank:]" + "]*[\\+\\:[:digit:]]+)?"); + + std::map<std::string, SmallVector<TestSelectionRange, 8>> GroupedRanges; + + LangOptions LangOpts; + LangOpts.CPlusPlus = 1; + LangOpts.CPlusPlus11 = 1; + Lexer Lex(SourceLocation::getFromRawEncoding(0), LangOpts, Source.begin(), + Source.begin(), Source.end()); + Lex.SetCommentRetentionState(true); + Token Tok; + for (Lex.LexFromRawLexer(Tok); Tok.isNot(tok::eof); + Lex.LexFromRawLexer(Tok)) { + if (Tok.isNot(tok::comment)) + continue; + StringRef Comment = + Source.substr(Tok.getLocation().getRawEncoding(), Tok.getLength()); + SmallVector<StringRef, 4> Matches; + // Try to detect mistyped 'range:' comments to ensure tests don't miss + // anything. + auto DetectMistypedCommand = [&]() -> bool { + if (Comment.contains_lower("range") && Comment.contains("=") && + !Comment.contains_lower("run") && !Comment.contains("CHECK")) { + llvm::errs() << "error: suspicious comment '" << Comment + << "' that " + "resembles the range command found\n"; + llvm::errs() << "note: please reword if this isn't a range command\n"; + } + return false; + }; + // Allow CHECK: comments to contain range= commands. + if (!RangeRegex.match(Comment, &Matches) || Comment.contains("CHECK")) { + if (DetectMistypedCommand()) + return None; + continue; + } + unsigned Offset = Tok.getEndLoc().getRawEncoding(); + unsigned ColumnOffset = 0; + if (!Matches[2].empty()) { + // Don't forget to drop the '+'! + if (Matches[2].drop_front().getAsInteger(10, ColumnOffset)) + assert(false && "regex should have produced a number"); + } + Offset = addColumnOffset(Source, Offset, ColumnOffset); + unsigned EndOffset; + + if (!Matches[3].empty()) { + static const Regex EndLocRegex( + "->[[:blank:]]*(\\+[[:digit:]]+):([[:digit:]]+)"); + SmallVector<StringRef, 4> EndLocMatches; + if (!EndLocRegex.match(Matches[3], &EndLocMatches)) { + if (DetectMistypedCommand()) + return None; + continue; + } + unsigned EndLineOffset = 0, EndColumn = 0; + if (EndLocMatches[1].drop_front().getAsInteger(10, EndLineOffset) || + EndLocMatches[2].getAsInteger(10, EndColumn)) + assert(false && "regex should have produced a number"); + EndOffset = addEndLineOffsetAndEndColumn(Source, Offset, EndLineOffset, + EndColumn); + } else { + EndOffset = Offset; + } + TestSelectionRange Range = {Offset, EndOffset}; + auto It = GroupedRanges.insert(std::make_pair( + Matches[1].str(), SmallVector<TestSelectionRange, 8>{Range})); + if (!It.second) + It.first->second.push_back(Range); + } + if (GroupedRanges.empty()) { + llvm::errs() << "error: -selection=test:" << Filename + << ": no 'range' commands"; + return None; + } + + TestSelectionRangesInFile TestRanges = {Filename.str(), {}}; + for (auto &Group : GroupedRanges) + TestRanges.GroupedRanges.push_back({Group.first, std::move(Group.second)}); + return std::move(TestRanges); +} + +} // end namespace refactor +} // end namespace clang diff --git a/gnu/llvm/clang/tools/clang-refactor/TestSupport.h b/gnu/llvm/clang/tools/clang-refactor/TestSupport.h new file mode 100644 index 00000000000..1282c3a90f3 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-refactor/TestSupport.h @@ -0,0 +1,103 @@ +//===--- TestSupport.h - Clang-based refactoring tool -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Declares datatypes and routines that are used by test-specific code +/// in clang-refactor. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H +#define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H + +#include "ToolRefactoringResultConsumer.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Error.h" +#include <map> +#include <string> + +namespace clang { + +class SourceManager; + +namespace refactor { + +/// A source selection range that's specified in a test file using an inline +/// command in the comment. These commands can take the following forms: +/// +/// - /*range=*/ will create an empty selection range in the default group +/// right after the comment. +/// - /*range a=*/ will create an empty selection range in the 'a' group right +/// after the comment. +/// - /*range = +1*/ will create an empty selection range at a location that's +/// right after the comment with one offset to the column. +/// - /*range= -> +2:3*/ will create a selection range that starts at the +/// location right after the comment, and ends at column 3 of the 2nd line +/// after the line of the starting location. +/// +/// Clang-refactor will expected all ranges in one test group to produce +/// identical results. +struct TestSelectionRange { + unsigned Begin, End; +}; + +/// A set of test selection ranges specified in one file. +struct TestSelectionRangesInFile { + std::string Filename; + struct RangeGroup { + std::string Name; + SmallVector<TestSelectionRange, 8> Ranges; + }; + std::vector<RangeGroup> GroupedRanges; + + bool foreachRange(const SourceManager &SM, + llvm::function_ref<void(SourceRange)> Callback) const; + + std::unique_ptr<ClangRefactorToolConsumerInterface> createConsumer() const; + + void dump(llvm::raw_ostream &OS) const; +}; + +/// Extracts the grouped selection ranges from the file that's specified in +/// the -selection=test:<filename> option. +/// +/// The grouped ranges are specified in comments using the following syntax: +/// "range" [ group-name ] "=" [ "+" starting-column-offset ] [ "->" +/// "+" ending-line-offset ":" +/// ending-column-position ] +/// +/// The selection range is then computed from this command by taking the ending +/// location of the comment, and adding 'starting-column-offset' to the column +/// for that location. That location in turns becomes the whole selection range, +/// unless 'ending-line-offset' and 'ending-column-position' are specified. If +/// they are specified, then the ending location of the selection range is +/// the starting location's line + 'ending-line-offset' and the +/// 'ending-column-position' column. +/// +/// All selection ranges in one group are expected to produce the same +/// refactoring result. +/// +/// When testing, zero is returned from clang-refactor even when a group +/// produces an initiation error, which is different from normal invocation +/// that returns a non-zero value. This is done on purpose, to ensure that group +/// consistency checks can return non-zero, but still print the output of +/// the group. So even if a test matches the output of group, it will still fail +/// because clang-refactor should return zero on exit when the group results are +/// consistent. +/// +/// \returns None on failure (errors are emitted to stderr), or a set of +/// grouped source ranges in the given file otherwise. +Optional<TestSelectionRangesInFile> findTestSelectionRanges(StringRef Filename); + +} // end namespace refactor +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_CLANG_REFACTOR_TEST_SUPPORT_H diff --git a/gnu/llvm/clang/tools/clang-refactor/ToolRefactoringResultConsumer.h b/gnu/llvm/clang/tools/clang-refactor/ToolRefactoringResultConsumer.h new file mode 100644 index 00000000000..b69f9d673fb --- /dev/null +++ b/gnu/llvm/clang/tools/clang-refactor/ToolRefactoringResultConsumer.h @@ -0,0 +1,47 @@ +//===--- ToolRefactoringResultConsumer.h - ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H +#define LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H + +#include "clang/AST/ASTContext.h" +#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h" + +namespace clang { +namespace refactor { + +/// An interface that subclasses the \c RefactoringResultConsumer interface +/// that stores the reference to the TU-specific diagnostics engine. +class ClangRefactorToolConsumerInterface + : public tooling::RefactoringResultConsumer { +public: + /// Called when a TU is entered. + void beginTU(ASTContext &Context) { + assert(!Diags && "Diags has been set"); + Diags = &Context.getDiagnostics(); + } + + /// Called when the tool is done with a TU. + void endTU() { + assert(Diags && "Diags unset"); + Diags = nullptr; + } + + DiagnosticsEngine &getDiags() const { + assert(Diags && "no diags"); + return *Diags; + } + +private: + DiagnosticsEngine *Diags = nullptr; +}; + +} // end namespace refactor +} // end namespace clang + +#endif // LLVM_CLANG_TOOLS_CLANG_REFACTOR_TOOL_REFACTORING_RESULT_CONSUMER_H diff --git a/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt b/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt new file mode 100644 index 00000000000..cda8e29ec5b --- /dev/null +++ b/gnu/llvm/clang/tools/clang-rename/CMakeLists.txt @@ -0,0 +1,26 @@ +set(LLVM_LINK_COMPONENTS + Option + Support + ) + +add_clang_tool(clang-rename + ClangRename.cpp + ) + +clang_target_link_libraries(clang-rename + PRIVATE + clangBasic + clangFrontend + clangRewrite + clangSerialization + clangTooling + clangToolingCore + clangToolingRefactoring + ) + +install(PROGRAMS clang-rename.py + DESTINATION share/clang + COMPONENT clang-rename) +install(PROGRAMS clang-rename.el + DESTINATION share/clang + COMPONENT clang-rename) diff --git a/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp b/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp new file mode 100644 index 00000000000..6dcd33aeb16 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-rename/ClangRename.cpp @@ -0,0 +1,232 @@ +//===--- tools/extra/clang-rename/ClangRename.cpp - Clang rename tool -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements a clang-rename tool that automatically finds and +/// renames symbols in C++ code. +/// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Refactoring/Rename/RenamingAction.h" +#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h" +#include "clang/Tooling/ReplacementsYaml.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include <string> +#include <system_error> + +using namespace llvm; +using namespace clang; + +/// An oldname -> newname rename. +struct RenameAllInfo { + unsigned Offset = 0; + std::string QualifiedName; + std::string NewName; +}; + +LLVM_YAML_IS_SEQUENCE_VECTOR(RenameAllInfo) + +namespace llvm { +namespace yaml { + +/// Specialized MappingTraits to describe how a RenameAllInfo is +/// (de)serialized. +template <> struct MappingTraits<RenameAllInfo> { + static void mapping(IO &IO, RenameAllInfo &Info) { + IO.mapOptional("Offset", Info.Offset); + IO.mapOptional("QualifiedName", Info.QualifiedName); + IO.mapRequired("NewName", Info.NewName); + } +}; + +} // end namespace yaml +} // end namespace llvm + +static cl::OptionCategory ClangRenameOptions("clang-rename common options"); + +static cl::list<unsigned> SymbolOffsets( + "offset", + cl::desc("Locates the symbol by offset as opposed to <line>:<column>."), + cl::ZeroOrMore, cl::cat(ClangRenameOptions)); +static cl::opt<bool> Inplace("i", cl::desc("Overwrite edited <file>s."), + cl::cat(ClangRenameOptions)); +static cl::list<std::string> + QualifiedNames("qualified-name", + cl::desc("The fully qualified name of the symbol."), + cl::ZeroOrMore, cl::cat(ClangRenameOptions)); + +static cl::list<std::string> + NewNames("new-name", cl::desc("The new name to change the symbol to."), + cl::ZeroOrMore, cl::cat(ClangRenameOptions)); +static cl::opt<bool> PrintName( + "pn", + cl::desc("Print the found symbol's name prior to renaming to stderr."), + cl::cat(ClangRenameOptions)); +static cl::opt<bool> PrintLocations( + "pl", cl::desc("Print the locations affected by renaming to stderr."), + cl::cat(ClangRenameOptions)); +static cl::opt<std::string> + ExportFixes("export-fixes", + cl::desc("YAML file to store suggested fixes in."), + cl::value_desc("filename"), cl::cat(ClangRenameOptions)); +static cl::opt<std::string> + Input("input", cl::desc("YAML file to load oldname-newname pairs from."), + cl::Optional, cl::cat(ClangRenameOptions)); +static cl::opt<bool> Force("force", + cl::desc("Ignore nonexistent qualified names."), + cl::cat(ClangRenameOptions)); + +int main(int argc, const char **argv) { + tooling::CommonOptionsParser OP(argc, argv, ClangRenameOptions); + + if (!Input.empty()) { + // Populate QualifiedNames and NewNames from a YAML file. + ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = + llvm::MemoryBuffer::getFile(Input); + if (!Buffer) { + errs() << "clang-rename: failed to read " << Input << ": " + << Buffer.getError().message() << "\n"; + return 1; + } + + std::vector<RenameAllInfo> Infos; + llvm::yaml::Input YAML(Buffer.get()->getBuffer()); + YAML >> Infos; + for (const auto &Info : Infos) { + if (!Info.QualifiedName.empty()) + QualifiedNames.push_back(Info.QualifiedName); + else + SymbolOffsets.push_back(Info.Offset); + NewNames.push_back(Info.NewName); + } + } + + // Check the arguments for correctness. + if (NewNames.empty()) { + errs() << "clang-rename: -new-name must be specified.\n\n"; + return 1; + } + + if (SymbolOffsets.empty() == QualifiedNames.empty()) { + errs() << "clang-rename: -offset and -qualified-name can't be present at " + "the same time.\n"; + return 1; + } + + // Check if NewNames is a valid identifier in C++17. + LangOptions Options; + Options.CPlusPlus = true; + Options.CPlusPlus17 = true; + IdentifierTable Table(Options); + for (const auto &NewName : NewNames) { + auto NewNameTokKind = Table.get(NewName).getTokenID(); + if (!tok::isAnyIdentifier(NewNameTokKind)) { + errs() << "ERROR: new name is not a valid identifier in C++17.\n\n"; + return 1; + } + } + + if (SymbolOffsets.size() + QualifiedNames.size() != NewNames.size()) { + errs() << "clang-rename: number of symbol offsets(" << SymbolOffsets.size() + << ") + number of qualified names (" << QualifiedNames.size() + << ") must be equal to number of new names(" << NewNames.size() + << ").\n\n"; + cl::PrintHelpMessage(); + return 1; + } + + auto Files = OP.getSourcePathList(); + tooling::RefactoringTool Tool(OP.getCompilations(), Files); + tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force); + Tool.run(tooling::newFrontendActionFactory(&FindingAction).get()); + const std::vector<std::vector<std::string>> &USRList = + FindingAction.getUSRList(); + const std::vector<std::string> &PrevNames = FindingAction.getUSRSpellings(); + if (PrintName) { + for (const auto &PrevName : PrevNames) { + outs() << "clang-rename found name: " << PrevName << '\n'; + } + } + + if (FindingAction.errorOccurred()) { + // Diagnostics are already issued at this point. + return 1; + } + + // Perform the renaming. + tooling::RenamingAction RenameAction(NewNames, PrevNames, USRList, + Tool.getReplacements(), PrintLocations); + std::unique_ptr<tooling::FrontendActionFactory> Factory = + tooling::newFrontendActionFactory(&RenameAction); + int ExitCode; + + if (Inplace) { + ExitCode = Tool.runAndSave(Factory.get()); + } else { + ExitCode = Tool.run(Factory.get()); + + if (!ExportFixes.empty()) { + std::error_code EC; + llvm::raw_fd_ostream OS(ExportFixes, EC, llvm::sys::fs::OF_None); + if (EC) { + llvm::errs() << "Error opening output file: " << EC.message() << '\n'; + return 1; + } + + // Export replacements. + tooling::TranslationUnitReplacements TUR; + const auto &FileToReplacements = Tool.getReplacements(); + for (const auto &Entry : FileToReplacements) + TUR.Replacements.insert(TUR.Replacements.end(), Entry.second.begin(), + Entry.second.end()); + + yaml::Output YAML(OS); + YAML << TUR; + OS.close(); + return 0; + } + + // Write every file to stdout. Right now we just barf the files without any + // indication of which files start where, other than that we print the files + // in the same order we see them. + LangOptions DefaultLangOptions; + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter DiagnosticPrinter(errs(), &*DiagOpts); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts, + &DiagnosticPrinter, false); + auto &FileMgr = Tool.getFiles(); + SourceManager Sources(Diagnostics, FileMgr); + Rewriter Rewrite(Sources, DefaultLangOptions); + + Tool.applyAllReplacements(Rewrite); + for (const auto &File : Files) { + auto Entry = FileMgr.getFile(File); + const auto ID = Sources.getOrCreateFileID(*Entry, SrcMgr::C_User); + Rewrite.getEditBuffer(ID).write(outs()); + } + } + + return ExitCode; +} diff --git a/gnu/llvm/clang/tools/clang-rename/clang-rename.el b/gnu/llvm/clang/tools/clang-rename/clang-rename.el new file mode 100644 index 00000000000..b6c3ed4c686 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-rename/clang-rename.el @@ -0,0 +1,79 @@ +;;; clang-rename.el --- Renames every occurrence of a symbol found at <offset>. -*- lexical-binding: t; -*- + +;; Keywords: tools, c + +;;; Commentary: + +;; To install clang-rename.el make sure the directory of this file is in your +;; `load-path' and add +;; +;; (require 'clang-rename) +;; +;; to your .emacs configuration. + +;;; Code: + +(defgroup clang-rename nil + "Integration with clang-rename" + :group 'c) + +(defcustom clang-rename-binary "clang-rename" + "Path to clang-rename executable." + :type '(file :must-match t) + :group 'clang-rename) + +;;;###autoload +(defun clang-rename (new-name) + "Rename all instances of the symbol at point to NEW-NAME using clang-rename." + (interactive "sEnter a new name: ") + (save-some-buffers :all) + ;; clang-rename should not be combined with other operations when undoing. + (undo-boundary) + (let ((output-buffer (get-buffer-create "*clang-rename*"))) + (with-current-buffer output-buffer (erase-buffer)) + (let ((exit-code (call-process + clang-rename-binary nil output-buffer nil + (format "-offset=%d" + ;; clang-rename wants file (byte) offsets, not + ;; buffer (character) positions. + (clang-rename--bufferpos-to-filepos + ;; Emacs treats one character after a symbol as + ;; part of the symbol, but clang-rename doesn’t. + ;; Use the beginning of the current symbol, if + ;; available, to resolve the inconsistency. + (or (car (bounds-of-thing-at-point 'symbol)) + (point)) + 'exact)) + (format "-new-name=%s" new-name) + "-i" (buffer-file-name)))) + (if (and (integerp exit-code) (zerop exit-code)) + ;; Success; revert current buffer so it gets the modifications. + (progn + (kill-buffer output-buffer) + (revert-buffer :ignore-auto :noconfirm :preserve-modes)) + ;; Failure; append exit code to output buffer and display it. + (let ((message (clang-rename--format-message + "clang-rename failed with %s %s" + (if (integerp exit-code) "exit status" "signal") + exit-code))) + (with-current-buffer output-buffer + (insert ?\n message ?\n)) + (message "%s" message) + (display-buffer output-buffer)))))) + +(defalias 'clang-rename--bufferpos-to-filepos + (if (fboundp 'bufferpos-to-filepos) + 'bufferpos-to-filepos + ;; Emacs 24 doesn’t have ‘bufferpos-to-filepos’, simulate it using + ;; ‘position-bytes’. + (lambda (position &optional _quality _coding-system) + (1- (position-bytes position))))) + +;; ‘format-message’ is new in Emacs 25.1. Provide a fallback for older +;; versions. +(defalias 'clang-rename--format-message + (if (fboundp 'format-message) 'format-message 'format)) + +(provide 'clang-rename) + +;;; clang-rename.el ends here diff --git a/gnu/llvm/clang/tools/clang-rename/clang-rename.py b/gnu/llvm/clang/tools/clang-rename/clang-rename.py new file mode 100644 index 00000000000..3381c5267f1 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-rename/clang-rename.py @@ -0,0 +1,66 @@ +''' +Minimal clang-rename integration with Vim. + +Before installing make sure one of the following is satisfied: + +* clang-rename is in your PATH +* `g:clang_rename_path` in ~/.vimrc points to valid clang-rename executable +* `binary` in clang-rename.py points to valid to clang-rename executable + +To install, simply put this into your ~/.vimrc for python2 support + + noremap <leader>cr :pyf <path-to>/clang-rename.py<cr> + +For python3 use the following command (note the change from :pyf to :py3f) + + noremap <leader>cr :py3f <path-to>/clang-rename.py<cr> + +IMPORTANT NOTE: Before running the tool, make sure you saved the file. + +All you have to do now is to place a cursor on a variable/function/class which +you would like to rename and press '<leader>cr'. You will be prompted for a new +name if the cursor points to a valid symbol. +''' + +from __future__ import absolute_import, division, print_function +import vim +import subprocess +import sys + +def main(): + binary = 'clang-rename' + if vim.eval('exists("g:clang_rename_path")') == "1": + binary = vim.eval('g:clang_rename_path') + + # Get arguments for clang-rename binary. + offset = int(vim.eval('line2byte(line("."))+col(".")')) - 2 + if offset < 0: + print('Couldn\'t determine cursor position. Is your file empty?', + file=sys.stderr) + return + filename = vim.current.buffer.name + + new_name_request_message = 'type new name:' + new_name = vim.eval("input('{}\n')".format(new_name_request_message)) + + # Call clang-rename. + command = [binary, + filename, + '-i', + '-offset', str(offset), + '-new-name', str(new_name)] + # FIXME: make it possible to run the tool on unsaved file. + p = subprocess.Popen(command, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + + if stderr: + print(stderr) + + # Reload all buffers in Vim. + vim.command("checktime") + + +if __name__ == '__main__': + main() diff --git a/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt b/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt new file mode 100644 index 00000000000..6aa914f3b25 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-scan-deps/CMakeLists.txt @@ -0,0 +1,28 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +add_clang_tool(clang-scan-deps + ClangScanDeps.cpp + ) + +set(CLANG_SCAN_DEPS_LIB_DEPS + clangAST + clangBasic + clangCodeGen + clangDriver + clangFrontend + clangFrontendTool + clangLex + clangParse + clangSerialization + clangTooling + clangDependencyScanning + ) + +clang_target_link_libraries(clang-scan-deps + PRIVATE + ${CLANG_SCAN_DEPS_LIB_DEPS} + ) + diff --git a/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp new file mode 100644 index 00000000000..1294e668284 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -0,0 +1,360 @@ +//===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningService.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" +#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" +#include "clang/Tooling/JSONCompilationDatabase.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Threading.h" +#include <mutex> +#include <thread> + +using namespace clang; +using namespace tooling::dependencies; + +namespace { + +class SharedStream { +public: + SharedStream(raw_ostream &OS) : OS(OS) {} + void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) { + std::unique_lock<std::mutex> LockGuard(Lock); + Fn(OS); + OS.flush(); + } + +private: + std::mutex Lock; + raw_ostream &OS; +}; + +class ResourceDirectoryCache { +public: + /// findResourceDir finds the resource directory relative to the clang + /// compiler being used in Args, by running it with "-print-resource-dir" + /// option and cache the results for reuse. \returns resource directory path + /// associated with the given invocation command or empty string if the + /// compiler path is NOT an absolute path. + StringRef findResourceDir(const tooling::CommandLineArguments &Args) { + if (Args.size() < 1) + return ""; + + const std::string &ClangBinaryPath = Args[0]; + if (!llvm::sys::path::is_absolute(ClangBinaryPath)) + return ""; + + const std::string &ClangBinaryName = + llvm::sys::path::filename(ClangBinaryPath); + + std::unique_lock<std::mutex> LockGuard(CacheLock); + const auto &CachedResourceDir = Cache.find(ClangBinaryPath); + if (CachedResourceDir != Cache.end()) + return CachedResourceDir->second; + + std::vector<StringRef> PrintResourceDirArgs{ClangBinaryName, + "-print-resource-dir"}; + llvm::SmallString<64> OutputFile, ErrorFile; + llvm::sys::fs::createTemporaryFile("print-resource-dir-output", + "" /*no-suffix*/, OutputFile); + llvm::sys::fs::createTemporaryFile("print-resource-dir-error", + "" /*no-suffix*/, ErrorFile); + llvm::FileRemover OutputRemover(OutputFile.c_str()); + llvm::FileRemover ErrorRemover(ErrorFile.c_str()); + llvm::Optional<StringRef> Redirects[] = { + {""}, // Stdin + StringRef(OutputFile), + StringRef(ErrorFile), + }; + if (const int RC = llvm::sys::ExecuteAndWait( + ClangBinaryPath, PrintResourceDirArgs, {}, Redirects)) { + auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str()); + llvm::errs() << ErrorBuf.get()->getBuffer(); + return ""; + } + + auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str()); + if (!OutputBuf) + return ""; + StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n'); + + Cache[ClangBinaryPath] = Output.str(); + return Cache[ClangBinaryPath]; + } + +private: + std::map<std::string, std::string> Cache; + std::mutex CacheLock; +}; + +llvm::cl::opt<bool> Help("h", llvm::cl::desc("Alias for -help"), + llvm::cl::Hidden); + +llvm::cl::OptionCategory DependencyScannerCategory("Tool options"); + +static llvm::cl::opt<ScanningMode> ScanMode( + "mode", + llvm::cl::desc("The preprocessing mode used to compute the dependencies"), + llvm::cl::values( + clEnumValN(ScanningMode::MinimizedSourcePreprocessing, + "preprocess-minimized-sources", + "The set of dependencies is computed by preprocessing the " + "source files that were minimized to only include the " + "contents that might affect the dependencies"), + clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess", + "The set of dependencies is computed by preprocessing the " + "unmodified source files")), + llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing), + llvm::cl::cat(DependencyScannerCategory)); + +static llvm::cl::opt<ScanningOutputFormat> Format( + "format", llvm::cl::desc("The output format for the dependencies"), + llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make", + "Makefile compatible dep file"), + clEnumValN(ScanningOutputFormat::Full, "experimental-full", + "Full dependency graph suitable" + " for explicitly building modules. This format " + "is experimental and will change.")), + llvm::cl::init(ScanningOutputFormat::Make), + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt<unsigned> + NumThreads("j", llvm::cl::Optional, + llvm::cl::desc("Number of worker threads to use (default: use " + "all concurrent threads)"), + llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt<std::string> + CompilationDB("compilation-database", + llvm::cl::desc("Compilation database"), llvm::cl::Required, + llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt<bool> ReuseFileManager( + "reuse-filemanager", + llvm::cl::desc("Reuse the file manager and its cache between invocations."), + llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt<bool> SkipExcludedPPRanges( + "skip-excluded-pp-ranges", + llvm::cl::desc( + "Use the preprocessor optimization that skips excluded conditionals by " + "bumping the buffer pointer in the lexer instead of lexing the tokens " + "until reaching the end directive."), + llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory)); + +llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional, + llvm::cl::desc("Use verbose output."), + llvm::cl::init(false), + llvm::cl::cat(DependencyScannerCategory)); + +} // end anonymous namespace + +/// \returns object-file path derived from source-file path. +static std::string getObjFilePath(StringRef SrcFile) { + SmallString<128> ObjFileName(SrcFile); + llvm::sys::path::replace_extension(ObjFileName, "o"); + return ObjFileName.str(); +} + +class SingleCommandCompilationDatabase : public tooling::CompilationDatabase { +public: + SingleCommandCompilationDatabase(tooling::CompileCommand Cmd) + : Command(std::move(Cmd)) {} + + virtual std::vector<tooling::CompileCommand> + getCompileCommands(StringRef FilePath) const { + return {Command}; + } + + virtual std::vector<tooling::CompileCommand> getAllCompileCommands() const { + return {Command}; + } + +private: + tooling::CompileCommand Command; +}; + +/// Takes the result of a dependency scan and prints error / dependency files +/// based on the result. +/// +/// \returns True on error. +static bool handleDependencyToolResult(const std::string &Input, + llvm::Expected<std::string> &MaybeFile, + SharedStream &OS, SharedStream &Errs) { + if (!MaybeFile) { + llvm::handleAllErrors( + MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) { + Errs.applyLocked([&](raw_ostream &OS) { + OS << "Error while scanning dependencies for " << Input << ":\n"; + OS << Err.getMessage(); + }); + }); + return true; + } + OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; }); + return false; +} + +int main(int argc, const char **argv) { + llvm::InitLLVM X(argc, argv); + llvm::cl::HideUnrelatedOptions(DependencyScannerCategory); + if (!llvm::cl::ParseCommandLineOptions(argc, argv)) + return 1; + + std::string ErrorMessage; + std::unique_ptr<tooling::JSONCompilationDatabase> Compilations = + tooling::JSONCompilationDatabase::loadFromFile( + CompilationDB, ErrorMessage, + tooling::JSONCommandLineSyntax::AutoDetect); + if (!Compilations) { + llvm::errs() << "error: " << ErrorMessage << "\n"; + return 1; + } + + llvm::cl::PrintOptionValues(); + + // The command options are rewritten to run Clang in preprocessor only mode. + auto AdjustingCompilations = + std::make_unique<tooling::ArgumentsAdjustingCompilations>( + std::move(Compilations)); + ResourceDirectoryCache ResourceDirCache; + AdjustingCompilations->appendArgumentsAdjuster( + [&ResourceDirCache](const tooling::CommandLineArguments &Args, + StringRef FileName) { + std::string LastO = ""; + bool HasMT = false; + bool HasMQ = false; + bool HasMD = false; + bool HasResourceDir = false; + // We need to find the last -o value. + if (!Args.empty()) { + std::size_t Idx = Args.size() - 1; + for (auto It = Args.rbegin(); It != Args.rend(); ++It) { + if (It != Args.rbegin()) { + if (Args[Idx] == "-o") + LastO = Args[Idx + 1]; + if (Args[Idx] == "-MT") + HasMT = true; + if (Args[Idx] == "-MQ") + HasMQ = true; + if (Args[Idx] == "-MD") + HasMD = true; + if (Args[Idx] == "-resource-dir") + HasResourceDir = true; + } + --Idx; + } + } + // If there's no -MT/-MQ Driver would add -MT with the value of the last + // -o option. + tooling::CommandLineArguments AdjustedArgs = Args; + AdjustedArgs.push_back("-o"); + AdjustedArgs.push_back("/dev/null"); + if (!HasMT && !HasMQ) { + AdjustedArgs.push_back("-M"); + AdjustedArgs.push_back("-MT"); + // We're interested in source dependencies of an object file. + if (!HasMD) { + // FIXME: We are missing the directory unless the -o value is an + // absolute path. + AdjustedArgs.push_back(!LastO.empty() ? LastO + : getObjFilePath(FileName)); + } else { + AdjustedArgs.push_back(FileName); + } + } + AdjustedArgs.push_back("-Xclang"); + AdjustedArgs.push_back("-Eonly"); + AdjustedArgs.push_back("-Xclang"); + AdjustedArgs.push_back("-sys-header-deps"); + AdjustedArgs.push_back("-Wno-error"); + + if (!HasResourceDir) { + StringRef ResourceDir = + ResourceDirCache.findResourceDir(Args); + if (!ResourceDir.empty()) { + AdjustedArgs.push_back("-resource-dir"); + AdjustedArgs.push_back(ResourceDir); + } + } + return AdjustedArgs; + }); + AdjustingCompilations->appendArgumentsAdjuster( + tooling::getClangStripSerializeDiagnosticAdjuster()); + + SharedStream Errs(llvm::errs()); + // Print out the dependency results to STDOUT by default. + SharedStream DependencyOS(llvm::outs()); + + DependencyScanningService Service(ScanMode, Format, ReuseFileManager, + SkipExcludedPPRanges); +#if LLVM_ENABLE_THREADS + unsigned NumWorkers = + NumThreads == 0 ? llvm::hardware_concurrency() : NumThreads; +#else + unsigned NumWorkers = 1; +#endif + std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools; + for (unsigned I = 0; I < NumWorkers; ++I) + WorkerTools.push_back(std::make_unique<DependencyScanningTool>(Service)); + + std::vector<SingleCommandCompilationDatabase> Inputs; + for (tooling::CompileCommand Cmd : + AdjustingCompilations->getAllCompileCommands()) + Inputs.emplace_back(Cmd); + + std::vector<std::thread> WorkerThreads; + std::atomic<bool> HadErrors(false); + std::mutex Lock; + size_t Index = 0; + + if (Verbose) { + llvm::outs() << "Running clang-scan-deps on " << Inputs.size() + << " files using " << NumWorkers << " workers\n"; + } + for (unsigned I = 0; I < NumWorkers; ++I) { + auto Worker = [I, &Lock, &Index, &Inputs, &HadErrors, &WorkerTools, + &DependencyOS, &Errs]() { + while (true) { + const SingleCommandCompilationDatabase *Input; + std::string Filename; + std::string CWD; + // Take the next input. + { + std::unique_lock<std::mutex> LockGuard(Lock); + if (Index >= Inputs.size()) + return; + Input = &Inputs[Index++]; + tooling::CompileCommand Cmd = Input->getAllCompileCommands()[0]; + Filename = std::move(Cmd.Filename); + CWD = std::move(Cmd.Directory); + } + // Run the tool on it. + auto MaybeFile = WorkerTools[I]->getDependencyFile(*Input, CWD); + if (handleDependencyToolResult(Filename, MaybeFile, DependencyOS, Errs)) + HadErrors = true; + } + }; +#if LLVM_ENABLE_THREADS + WorkerThreads.emplace_back(std::move(Worker)); +#else + // Run the worker without spawning a thread when threads are disabled. + Worker(); +#endif + } + for (auto &W : WorkerThreads) + W.join(); + + return HadErrors; +} diff --git a/gnu/llvm/clang/tools/clang-shlib/CMakeLists.txt b/gnu/llvm/clang/tools/clang-shlib/CMakeLists.txt new file mode 100644 index 00000000000..07ee0f0a9a9 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-shlib/CMakeLists.txt @@ -0,0 +1,45 @@ +# Building libclang-cpp.so fails if LLVM_ENABLE_PIC=Off +if (NOT LLVM_ENABLE_PIC) + return() +endif() + +get_property(clang_libs GLOBAL PROPERTY CLANG_STATIC_LIBS) + +foreach (lib ${clang_libs}) + if(XCODE) + # Xcode doesn't support object libraries, so we have to trick it into + # linking the static libraries instead. + list(APPEND _DEPS "-force_load" ${lib}) + else() + list(APPEND _OBJECTS $<TARGET_OBJECTS:obj.${lib}>) + endif() + list(APPEND _DEPS $<TARGET_PROPERTY:${lib},INTERFACE_LINK_LIBRARIES>) + + # clang libraries are redundant since we are linking all the individual + # object files into libclang-cpp.so, so filter them out from _DEPS. + # This avoids problems with LLVM global data when building with + # BUILD_SHARED_LIBS=ON + # FIXME: We could use list(FILTER) with cmake >= 3.6 + # FIXME: With cmake >= 3.15 we could use the generator expression + # $<FILTER:list,INCLUDE|EXCLUDE,regex> + get_target_property(interface ${lib} LINK_LIBRARIES) + if (interface) + foreach(lib ${interface}) + if (NOT ${lib} MATCHES "^clang") + list(APPEND _DEPS ${lib}) + endif() + endforeach() + endif() +endforeach () + +if (CLANG_LINK_CLANG_DYLIB) + set(INSTALL_WITH_TOOLCHAIN INSTALL_WITH_TOOLCHAIN) +endif() + +add_clang_library(clang-cpp + SHARED + ${INSTALL_WITH_TOOLCHAIN} + clang-shlib.cpp + ${_OBJECTS} + LINK_LIBS + ${_DEPS}) diff --git a/gnu/llvm/clang/tools/clang-shlib/clang-shlib.cpp b/gnu/llvm/clang/tools/clang-shlib/clang-shlib.cpp new file mode 100644 index 00000000000..0093622e6a1 --- /dev/null +++ b/gnu/llvm/clang/tools/clang-shlib/clang-shlib.cpp @@ -0,0 +1 @@ +// Intentionally empty source file to make CMake happy diff --git a/gnu/llvm/clang/tools/diag-build/diag-build.sh b/gnu/llvm/clang/tools/diag-build/diag-build.sh new file mode 100755 index 00000000000..018288dda95 --- /dev/null +++ b/gnu/llvm/clang/tools/diag-build/diag-build.sh @@ -0,0 +1,124 @@ +#!/bin/bash + +# diag-build: a tool showing enabled warnings in a project. +# +# diag-build acts as a wrapper for 'diagtool show-enabled', in the same way +# that scan-build acts as a wrapper for the static analyzer. The common case is +# simple: use 'diag-build make' or 'diag-build xcodebuild' to list the warnings +# enabled for the first compilation command we see. Other build systems require +# you to manually specify "dry-run" and "use $CC and $CXX"; if there is a build +# system you are interested in, please add it to the switch statement. + +print_usage () { + echo 'Usage: diag-build.sh [-v] xcodebuild [flags]' + echo ' diag-build.sh [-v] make [flags]' + echo ' diag-build.sh [-v] <other build command>' + echo + echo 'diagtool must be in your PATH' + echo 'If using an alternate build command, you must ensure that' + echo 'the compiler used matches the CC environment variable.' +} + +# Mac OS X's BSD sed uses -E for extended regular expressions, +# but GNU sed uses -r. Find out which one this system accepts. +EXTENDED_SED_FLAG='-E' +echo -n | sed $EXTENDED_SED_FLAG 's/a/b/' 2>/dev/null || EXTENDED_SED_FLAG='-r' + +if [[ "$1" == "-v" ]]; then + verbose=$1 + shift +fi + +guessing_cc=0 + +if [[ -z "$CC" ]]; then + guessing_cc=1 + if [[ -x $(dirname $0)/clang ]]; then + CC=$(dirname $0)/clang + elif [[ ! -z $(which clang) ]]; then + CC=$(which clang) + else + echo -n 'Error: could not find an appropriate compiler' + echo ' to generate build commands.' 1>&2 + echo 'Use the CC environment variable to set one explicitly.' 1>&2 + exit 1 + fi +fi + +if [[ -z "$CXX" ]]; then + if [[ -x $(dirname $0)/clang++ ]]; then + CXX=$(dirname $0)/clang++ + elif [[ ! -z $(which clang++) ]]; then + CXX=$(which clang++) + else + CXX=$CC + fi +fi + +diagtool=$(which diagtool) +if [[ -z "$diagtool" ]]; then + if [[ -x $(dirname $0)/diagtool ]]; then + diagtool=$(dirname $0)/diagtool + else + echo 'Error: could not find diagtool.' 1>&2 + exit 1 + fi +fi + + +tool=$1 +shift + +if [[ -z "$tool" ]]; then + print_usage + exit 1 +elif [[ "$tool" == "xcodebuild" ]]; then + dry_run='-dry-run' + set_compiler="CC='$CC' CXX='$CXX'" +elif [[ "$tool" == "make" ]]; then + dry_run='-n' + set_compiler="CC='$CC' CXX='$CXX'" +else + echo "Warning: unknown build system '$tool'" 1>&2 + if [[ $guessing_cc -eq 1 ]]; then + # FIXME: We really only need $CC /or/ $CXX + echo 'Error: $CC must be set for other build systems' 1>&2 + exit 1 + fi +fi + +escape () { + echo $@ | sed 's:[]:\\|/.+*?^$(){}[]:\\&:g' +} + +escCC=$(escape $CC) +escCXX=$(escape $CXX) +command=$( + eval $tool $dry_run $set_compiler $@ 2>/dev/null | + # Remove "if" early on so we can find the right command line. + sed $EXTENDED_SED_FLAG "s:^[[:blank:]]*if[[:blank:]]{1,}::g" | + # Combine lines with trailing backslashes + sed -e :a -e '/\\$/N; s/\\\n//; ta' | + grep -E "^[[:blank:]]*($escCC|$escCXX)" | + head -n1 | + sed $EXTENDED_SED_FLAG "s:($escCC|$escCXX):${diagtool//:/\\:} show-enabled:g" +) + +if [[ -z "$command" ]]; then + echo 'Error: could not find any build commands.' 1>&2 + if [[ "$tool" != "xcodebuild" ]]; then + # xcodebuild always echoes the compile commands on their own line, + # but other tools give no such guarantees. + echo -n 'This may occur if your build system embeds the call to ' 2>&1 + echo -n 'the compiler in a larger expression. ' 2>&1 + fi + exit 2 +fi + +# Chop off trailing '&&', '||', and ';' +command=${command%%&&*} +command=${command%%||*} +command=${command%%;*} + +[[ -n "$verbose" ]] && echo $command +eval $command diff --git a/gnu/llvm/clang/tools/diagtool/CMakeLists.txt b/gnu/llvm/clang/tools/diagtool/CMakeLists.txt new file mode 100644 index 00000000000..a95444be40e --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/CMakeLists.txt @@ -0,0 +1,31 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_executable(diagtool + diagtool_main.cpp + DiagTool.cpp + DiagnosticNames.cpp + FindDiagnosticID.cpp + ListWarnings.cpp + ShowEnabledWarnings.cpp + TreeView.cpp +) + +clang_target_link_libraries(diagtool + PRIVATE + clangBasic + clangFrontend + ) + +if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY) + install(TARGETS diagtool + COMPONENT diagtool + RUNTIME DESTINATION bin) + + if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-diagtool + DEPENDS diagtool + COMPONENT diagtool) + endif() +endif() diff --git a/gnu/llvm/clang/tools/diagtool/DiagTool.cpp b/gnu/llvm/clang/tools/diagtool/DiagTool.cpp new file mode 100644 index 00000000000..6cd67cca393 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/DiagTool.cpp @@ -0,0 +1,66 @@ +//===- DiagTool.cpp - Classes for defining diagtool tools -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the boilerplate for defining diagtool tools. +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "llvm/ADT/StringMap.h" +#include <vector> + +using namespace diagtool; + +DiagTool::DiagTool(llvm::StringRef toolCmd, + llvm::StringRef toolDesc) + : cmd(toolCmd), description(toolDesc) {} + +DiagTool::~DiagTool() {} + +typedef llvm::StringMap<DiagTool *> ToolMap; +static inline ToolMap *getTools(void *v) { return static_cast<ToolMap*>(v); } + +DiagTools::DiagTools() : tools(new ToolMap()) {} +DiagTools::~DiagTools() { delete getTools(tools); } + +DiagTool *DiagTools::getTool(llvm::StringRef toolCmd) { + ToolMap::iterator it = getTools(tools)->find(toolCmd); + return (it == getTools(tools)->end()) ? nullptr : it->getValue(); +} + +void DiagTools::registerTool(DiagTool *tool) { + (*getTools(tools))[tool->getName()] = tool; +} + +void DiagTools::printCommands(llvm::raw_ostream &out) { + std::vector<llvm::StringRef> toolNames; + unsigned maxName = 0; + for (ToolMap::iterator it = getTools(tools)->begin(), + ei = getTools(tools)->end(); it != ei; ++it) { + toolNames.push_back(it->getKey()); + unsigned len = it->getKey().size(); + if (len > maxName) + maxName = len; + } + llvm::sort(toolNames); + + for (std::vector<llvm::StringRef>::iterator it = toolNames.begin(), + ei = toolNames.end(); it != ei; ++it) { + + out << " " << (*it); + unsigned spaces = (maxName + 3) - (it->size()); + for (unsigned i = 0; i < spaces; ++i) + out << ' '; + + out << getTool(*it)->getDescription() << '\n'; + } +} + +namespace diagtool { + llvm::ManagedStatic<DiagTools> diagTools; +} diff --git a/gnu/llvm/clang/tools/diagtool/DiagTool.h b/gnu/llvm/clang/tools/diagtool/DiagTool.h new file mode 100644 index 00000000000..1d9da75bf39 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/DiagTool.h @@ -0,0 +1,69 @@ +//===- DiagTool.h - Classes for defining diagtool tools -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the boilerplate for defining diagtool tools. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_DIAGTOOL_DIAGTOOL_H +#define LLVM_CLANG_TOOLS_DIAGTOOL_DIAGTOOL_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/raw_ostream.h" +#include <string> + + +namespace diagtool { + +class DiagTool { + const std::string cmd; + const std::string description; +public: + DiagTool(llvm::StringRef toolCmd, llvm::StringRef toolDesc); + virtual ~DiagTool(); + + llvm::StringRef getName() const { return cmd; } + llvm::StringRef getDescription() const { return description; } + + virtual int run(unsigned argc, char *argv[], llvm::raw_ostream &out) = 0; +}; + +class DiagTools { + void *tools; +public: + DiagTools(); + ~DiagTools(); + + DiagTool *getTool(llvm::StringRef toolCmd); + void registerTool(DiagTool *tool); + void printCommands(llvm::raw_ostream &out); +}; + +extern llvm::ManagedStatic<DiagTools> diagTools; + +template <typename DIAGTOOL> +class RegisterDiagTool { +public: + RegisterDiagTool() { diagTools->registerTool(new DIAGTOOL()); } +}; + +} // end diagtool namespace + +#define DEF_DIAGTOOL(NAME, DESC, CLSNAME)\ +namespace {\ +class CLSNAME : public diagtool::DiagTool {\ +public:\ + CLSNAME() : DiagTool(NAME, DESC) {}\ + virtual ~CLSNAME() {}\ + int run(unsigned argc, char *argv[], llvm::raw_ostream &out) override;\ +};\ +diagtool::RegisterDiagTool<CLSNAME> Register##CLSNAME;\ +} + +#endif diff --git a/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp b/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp new file mode 100644 index 00000000000..eddb99d1f57 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/DiagnosticNames.cpp @@ -0,0 +1,106 @@ +//===- DiagnosticNames.cpp - Defines a table of all builtin diagnostics ----==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DiagnosticNames.h" +#include "clang/Basic/AllDiagnostics.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; +using namespace diagtool; + +static const DiagnosticRecord BuiltinDiagnosticsByName[] = { +#define DIAG_NAME_INDEX(ENUM) { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, +#include "clang/Basic/DiagnosticIndexName.inc" +#undef DIAG_NAME_INDEX +}; + +llvm::ArrayRef<DiagnosticRecord> diagtool::getBuiltinDiagnosticsByName() { + return llvm::makeArrayRef(BuiltinDiagnosticsByName); +} + + +// FIXME: Is it worth having two tables, especially when this one can get +// out of sync easily? +static const DiagnosticRecord BuiltinDiagnosticsByID[] = { +#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP, \ + SFINAE,NOWERROR,SHOWINSYSHEADER,CATEGORY) \ + { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, +#include "clang/Basic/DiagnosticCommonKinds.inc" +#include "clang/Basic/DiagnosticCrossTUKinds.inc" +#include "clang/Basic/DiagnosticDriverKinds.inc" +#include "clang/Basic/DiagnosticFrontendKinds.inc" +#include "clang/Basic/DiagnosticSerializationKinds.inc" +#include "clang/Basic/DiagnosticLexKinds.inc" +#include "clang/Basic/DiagnosticParseKinds.inc" +#include "clang/Basic/DiagnosticASTKinds.inc" +#include "clang/Basic/DiagnosticCommentKinds.inc" +#include "clang/Basic/DiagnosticSemaKinds.inc" +#include "clang/Basic/DiagnosticAnalysisKinds.inc" +#include "clang/Basic/DiagnosticRefactoringKinds.inc" +#undef DIAG +}; + +static bool orderByID(const DiagnosticRecord &Left, + const DiagnosticRecord &Right) { + return Left.DiagID < Right.DiagID; +} + +const DiagnosticRecord &diagtool::getDiagnosticForID(short DiagID) { + DiagnosticRecord Key = {nullptr, DiagID, 0}; + + const DiagnosticRecord *Result = + llvm::lower_bound(BuiltinDiagnosticsByID, Key, orderByID); + assert(Result && "diagnostic not found; table may be out of date"); + return *Result; +} + + +#define GET_DIAG_ARRAYS +#include "clang/Basic/DiagnosticGroups.inc" +#undef GET_DIAG_ARRAYS + +// Second the table of options, sorted by name for fast binary lookup. +static const GroupRecord OptionTable[] = { +#define GET_DIAG_TABLE +#include "clang/Basic/DiagnosticGroups.inc" +#undef GET_DIAG_TABLE +}; + +llvm::StringRef GroupRecord::getName() const { + return StringRef(DiagGroupNames + NameOffset + 1, DiagGroupNames[NameOffset]); +} + +GroupRecord::subgroup_iterator GroupRecord::subgroup_begin() const { + return DiagSubGroups + SubGroups; +} + +GroupRecord::subgroup_iterator GroupRecord::subgroup_end() const { + return nullptr; +} + +llvm::iterator_range<diagtool::GroupRecord::subgroup_iterator> +GroupRecord::subgroups() const { + return llvm::make_range(subgroup_begin(), subgroup_end()); +} + +GroupRecord::diagnostics_iterator GroupRecord::diagnostics_begin() const { + return DiagArrays + Members; +} + +GroupRecord::diagnostics_iterator GroupRecord::diagnostics_end() const { + return nullptr; +} + +llvm::iterator_range<diagtool::GroupRecord::diagnostics_iterator> +GroupRecord::diagnostics() const { + return llvm::make_range(diagnostics_begin(), diagnostics_end()); +} + +llvm::ArrayRef<GroupRecord> diagtool::getDiagnosticGroups() { + return llvm::makeArrayRef(OptionTable); +} diff --git a/gnu/llvm/clang/tools/diagtool/DiagnosticNames.h b/gnu/llvm/clang/tools/diagtool/DiagnosticNames.h new file mode 100644 index 00000000000..d8fd6401ef6 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/DiagnosticNames.h @@ -0,0 +1,119 @@ +//===- DiagnosticNames.h - Defines a table of all builtin diagnostics ------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_DIAGTOOL_DIAGNOSTICNAMES_H +#define LLVM_CLANG_TOOLS_DIAGTOOL_DIAGNOSTICNAMES_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" + +namespace diagtool { + + struct DiagnosticRecord { + const char *NameStr; + short DiagID; + uint8_t NameLen; + + llvm::StringRef getName() const { + return llvm::StringRef(NameStr, NameLen); + } + + bool operator<(const DiagnosticRecord &Other) const { + return getName() < Other.getName(); + } + }; + + /// Get every diagnostic in the system, sorted by name. + llvm::ArrayRef<DiagnosticRecord> getBuiltinDiagnosticsByName(); + + /// Get a diagnostic by its ID. + const DiagnosticRecord &getDiagnosticForID(short DiagID); + + + struct GroupRecord { + uint16_t NameOffset; + uint16_t Members; + uint16_t SubGroups; + + llvm::StringRef getName() const; + + template<typename RecordType> + class group_iterator { + const short *CurrentID; + + friend struct GroupRecord; + group_iterator(const short *Start) : CurrentID(Start) { + if (CurrentID && *CurrentID == -1) + CurrentID = nullptr; + } + + public: + typedef RecordType value_type; + typedef const value_type & reference; + typedef const value_type * pointer; + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + + inline reference operator*() const; + inline pointer operator->() const { + return &operator*(); + } + + inline short getID() const { + return *CurrentID; + } + + group_iterator &operator++() { + ++CurrentID; + if (*CurrentID == -1) + CurrentID = nullptr; + return *this; + } + + bool operator==(group_iterator &Other) const { + return CurrentID == Other.CurrentID; + } + + bool operator!=(group_iterator &Other) const { + return CurrentID != Other.CurrentID; + } + }; + + typedef group_iterator<GroupRecord> subgroup_iterator; + subgroup_iterator subgroup_begin() const; + subgroup_iterator subgroup_end() const; + llvm::iterator_range<subgroup_iterator> subgroups() const; + + typedef group_iterator<DiagnosticRecord> diagnostics_iterator; + diagnostics_iterator diagnostics_begin() const; + diagnostics_iterator diagnostics_end() const; + llvm::iterator_range<diagnostics_iterator> diagnostics() const; + + bool operator<(llvm::StringRef Other) const { + return getName() < Other; + } + }; + + /// Get every diagnostic group in the system, sorted by name. + llvm::ArrayRef<GroupRecord> getDiagnosticGroups(); + + template<> + inline GroupRecord::subgroup_iterator::reference + GroupRecord::subgroup_iterator::operator*() const { + return getDiagnosticGroups()[*CurrentID]; + } + + template<> + inline GroupRecord::diagnostics_iterator::reference + GroupRecord::diagnostics_iterator::operator*() const { + return getDiagnosticForID(*CurrentID); + } +} // end namespace diagtool + +#endif diff --git a/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp b/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp new file mode 100644 index 00000000000..2a08814478f --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/FindDiagnosticID.cpp @@ -0,0 +1,73 @@ +//===- FindDiagnosticID.cpp - diagtool tool for finding diagnostic id -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "DiagnosticNames.h" +#include "clang/Basic/AllDiagnostics.h" +#include "llvm/Support/CommandLine.h" + +DEF_DIAGTOOL("find-diagnostic-id", "Print the id of the given diagnostic", + FindDiagnosticID) + +using namespace clang; +using namespace diagtool; + +static StringRef getNameFromID(StringRef Name) { + int DiagID; + if(!Name.getAsInteger(0, DiagID)) { + const DiagnosticRecord &Diag = getDiagnosticForID(DiagID); + return Diag.getName(); + } + return StringRef(); +} + +static Optional<DiagnosticRecord> +findDiagnostic(ArrayRef<DiagnosticRecord> Diagnostics, StringRef Name) { + for (const auto &Diag : Diagnostics) { + StringRef DiagName = Diag.getName(); + if (DiagName == Name) + return Diag; + } + return None; +} + +int FindDiagnosticID::run(unsigned int argc, char **argv, + llvm::raw_ostream &OS) { + static llvm::cl::OptionCategory FindDiagnosticIDOptions( + "diagtool find-diagnostic-id options"); + + static llvm::cl::opt<std::string> DiagnosticName( + llvm::cl::Positional, llvm::cl::desc("<diagnostic-name>"), + llvm::cl::Required, llvm::cl::cat(FindDiagnosticIDOptions)); + + std::vector<const char *> Args; + Args.push_back("diagtool find-diagnostic-id"); + for (const char *A : llvm::makeArrayRef(argv, argc)) + Args.push_back(A); + + llvm::cl::HideUnrelatedOptions(FindDiagnosticIDOptions); + llvm::cl::ParseCommandLineOptions((int)Args.size(), Args.data(), + "Diagnostic ID mapping utility"); + + ArrayRef<DiagnosticRecord> AllDiagnostics = getBuiltinDiagnosticsByName(); + Optional<DiagnosticRecord> Diag = + findDiagnostic(AllDiagnostics, DiagnosticName); + if (!Diag) { + // Name to id failed, so try id to name. + auto Name = getNameFromID(DiagnosticName); + if (!Name.empty()) { + OS << Name << '\n'; + return 0; + } + + llvm::errs() << "error: invalid diagnostic '" << DiagnosticName << "'\n"; + return 1; + } + OS << Diag->DiagID << "\n"; + return 0; +} diff --git a/gnu/llvm/clang/tools/diagtool/ListWarnings.cpp b/gnu/llvm/clang/tools/diagtool/ListWarnings.cpp new file mode 100644 index 00000000000..a71f6e3a66c --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/ListWarnings.cpp @@ -0,0 +1,100 @@ +//===- ListWarnings.h - diagtool tool for printing warning flags ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file provides a diagtool tool that displays warning flags for +// diagnostics. +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "DiagnosticNames.h" +#include "clang/Basic/AllDiagnostics.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Format.h" + +DEF_DIAGTOOL("list-warnings", + "List warnings and their corresponding flags", + ListWarnings) + +using namespace clang; +using namespace diagtool; + +namespace { +struct Entry { + llvm::StringRef DiagName; + llvm::StringRef Flag; + + Entry(llvm::StringRef diagN, llvm::StringRef flag) + : DiagName(diagN), Flag(flag) {} + + bool operator<(const Entry &x) const { return DiagName < x.DiagName; } +}; +} + +static void printEntries(std::vector<Entry> &entries, llvm::raw_ostream &out) { + for (const Entry &E : entries) { + out << " " << E.DiagName; + if (!E.Flag.empty()) + out << " [-W" << E.Flag << "]"; + out << '\n'; + } +} + +int ListWarnings::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { + std::vector<Entry> Flagged, Unflagged; + llvm::StringMap<std::vector<unsigned> > flagHistogram; + + for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) { + const unsigned diagID = DR.DiagID; + + if (DiagnosticIDs::isBuiltinNote(diagID)) + continue; + + if (!DiagnosticIDs::isBuiltinWarningOrExtension(diagID)) + continue; + + Entry entry(DR.getName(), DiagnosticIDs::getWarningOptionForDiag(diagID)); + + if (entry.Flag.empty()) + Unflagged.push_back(entry); + else { + Flagged.push_back(entry); + flagHistogram[entry.Flag].push_back(diagID); + } + } + + out << "Warnings with flags (" << Flagged.size() << "):\n"; + printEntries(Flagged, out); + + out << "Warnings without flags (" << Unflagged.size() << "):\n"; + printEntries(Unflagged, out); + + out << "\nSTATISTICS:\n\n"; + + double percentFlagged = + ((double)Flagged.size()) / (Flagged.size() + Unflagged.size()) * 100.0; + + out << " Percentage of warnings with flags: " + << llvm::format("%.4g", percentFlagged) << "%\n"; + + out << " Number of unique flags: " + << flagHistogram.size() << '\n'; + + double avgDiagsPerFlag = (double) Flagged.size() / flagHistogram.size(); + out << " Average number of diagnostics per flag: " + << llvm::format("%.4g", avgDiagsPerFlag) << '\n'; + + out << " Number in -Wpedantic (not covered by other -W flags): " + << flagHistogram["pedantic"].size() << '\n'; + + out << '\n'; + + return 0; +} + diff --git a/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp b/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp new file mode 100644 index 00000000000..ae2d3e37e84 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/ShowEnabledWarnings.cpp @@ -0,0 +1,145 @@ +//===- ShowEnabledWarnings - diagtool tool for printing enabled flags -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "DiagnosticNames.h" +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "llvm/Support/TargetSelect.h" + +DEF_DIAGTOOL("show-enabled", + "Show which warnings are enabled for a given command line", + ShowEnabledWarnings) + +using namespace clang; +using namespace diagtool; + +namespace { + struct PrettyDiag { + StringRef Name; + StringRef Flag; + DiagnosticsEngine::Level Level; + + PrettyDiag(StringRef name, StringRef flag, DiagnosticsEngine::Level level) + : Name(name), Flag(flag), Level(level) {} + + bool operator<(const PrettyDiag &x) const { return Name < x.Name; } + }; +} + +static void printUsage() { + llvm::errs() << "Usage: diagtool show-enabled [<flags>] <single-input.c>\n"; +} + +static char getCharForLevel(DiagnosticsEngine::Level Level) { + switch (Level) { + case DiagnosticsEngine::Ignored: return ' '; + case DiagnosticsEngine::Note: return '-'; + case DiagnosticsEngine::Remark: return 'R'; + case DiagnosticsEngine::Warning: return 'W'; + case DiagnosticsEngine::Error: return 'E'; + case DiagnosticsEngine::Fatal: return 'F'; + } + + llvm_unreachable("Unknown diagnostic level"); +} + +static IntrusiveRefCntPtr<DiagnosticsEngine> +createDiagnostics(unsigned int argc, char **argv) { + IntrusiveRefCntPtr<DiagnosticIDs> DiagIDs(new DiagnosticIDs()); + + // Buffer diagnostics from argument parsing so that we can output them using a + // well formed diagnostic object. + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + IntrusiveRefCntPtr<DiagnosticsEngine> InterimDiags( + new DiagnosticsEngine(DiagIDs, new DiagnosticOptions(), DiagsBuffer)); + + // Try to build a CompilerInvocation. + SmallVector<const char *, 4> Args; + Args.push_back("diagtool"); + Args.append(argv, argv + argc); + std::unique_ptr<CompilerInvocation> Invocation = + createInvocationFromCommandLine(Args, InterimDiags); + if (!Invocation) + return nullptr; + + // Build the diagnostics parser + IntrusiveRefCntPtr<DiagnosticsEngine> FinalDiags = + CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts()); + if (!FinalDiags) + return nullptr; + + // Flush any errors created when initializing everything. This could happen + // for invalid command lines, which will probably give non-sensical results. + DiagsBuffer->FlushDiagnostics(*FinalDiags); + + return FinalDiags; +} + +int ShowEnabledWarnings::run(unsigned int argc, char **argv, raw_ostream &Out) { + // First check our one flag (--levels). + bool ShouldShowLevels = true; + if (argc > 0) { + StringRef FirstArg(*argv); + if (FirstArg.equals("--no-levels")) { + ShouldShowLevels = false; + --argc; + ++argv; + } else if (FirstArg.equals("--levels")) { + ShouldShowLevels = true; + --argc; + ++argv; + } + } + + // Create the diagnostic engine. + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = createDiagnostics(argc, argv); + if (!Diags) { + printUsage(); + return EXIT_FAILURE; + } + + // Now we have our diagnostics. Iterate through EVERY diagnostic and see + // which ones are turned on. + // FIXME: It would be very nice to print which flags are turning on which + // diagnostics, but this can be done with a diff. + std::vector<PrettyDiag> Active; + + for (const DiagnosticRecord &DR : getBuiltinDiagnosticsByName()) { + unsigned DiagID = DR.DiagID; + + if (DiagnosticIDs::isBuiltinNote(DiagID)) + continue; + + if (!DiagnosticIDs::isBuiltinWarningOrExtension(DiagID)) + continue; + + DiagnosticsEngine::Level DiagLevel = + Diags->getDiagnosticLevel(DiagID, SourceLocation()); + if (DiagLevel == DiagnosticsEngine::Ignored) + continue; + + StringRef WarningOpt = DiagnosticIDs::getWarningOptionForDiag(DiagID); + Active.push_back(PrettyDiag(DR.getName(), WarningOpt, DiagLevel)); + } + + // Print them all out. + for (const PrettyDiag &PD : Active) { + if (ShouldShowLevels) + Out << getCharForLevel(PD.Level) << " "; + Out << PD.Name; + if (!PD.Flag.empty()) + Out << " [-W" << PD.Flag << "]"; + Out << '\n'; + } + + return EXIT_SUCCESS; +} diff --git a/gnu/llvm/clang/tools/diagtool/TreeView.cpp b/gnu/llvm/clang/tools/diagtool/TreeView.cpp new file mode 100644 index 00000000000..96951287cc4 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/TreeView.cpp @@ -0,0 +1,166 @@ +//===- TreeView.cpp - diagtool tool for printing warning flags ------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" +#include "DiagnosticNames.h" +#include "clang/Basic/AllDiagnostics.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Process.h" + +DEF_DIAGTOOL("tree", "Show warning flags in a tree view", TreeView) + +using namespace clang; +using namespace diagtool; + +class TreePrinter { + using Colors = llvm::raw_ostream::Colors; + +public: + llvm::raw_ostream &out; + bool Internal; + + TreePrinter(llvm::raw_ostream &out) : out(out), Internal(false) {} + + static bool isIgnored(unsigned DiagID) { + // FIXME: This feels like a hack. + static clang::DiagnosticsEngine Diags(new DiagnosticIDs, + new DiagnosticOptions); + return Diags.isIgnored(DiagID, SourceLocation()); + } + + static bool enabledByDefault(const GroupRecord &Group) { + for (const DiagnosticRecord &DR : Group.diagnostics()) { + if (isIgnored(DR.DiagID)) + return false; + } + + for (const GroupRecord &GR : Group.subgroups()) { + if (!enabledByDefault(GR)) + return false; + } + + return true; + } + + void printGroup(const GroupRecord &Group, unsigned Indent = 0) { + out.indent(Indent * 2); + + if (enabledByDefault(Group)) + out << Colors::GREEN; + else + out << Colors::YELLOW; + + out << "-W" << Group.getName() << "\n" << Colors::RESET; + + ++Indent; + for (const GroupRecord &GR : Group.subgroups()) { + printGroup(GR, Indent); + } + + if (Internal) { + for (const DiagnosticRecord &DR : Group.diagnostics()) { + if (!isIgnored(DR.DiagID)) + out << Colors::GREEN; + out.indent(Indent * 2); + out << DR.getName() << Colors::RESET << "\n"; + } + } + } + + int showGroup(StringRef RootGroup) { + ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); + + if (RootGroup.size() > UINT16_MAX) { + llvm::errs() << "No such diagnostic group exists\n"; + return 1; + } + + const GroupRecord *Found = llvm::lower_bound(AllGroups, RootGroup); + if (Found == AllGroups.end() || Found->getName() != RootGroup) { + llvm::errs() << "No such diagnostic group exists\n"; + return 1; + } + + printGroup(*Found); + + return 0; + } + + int showAll() { + ArrayRef<GroupRecord> AllGroups = getDiagnosticGroups(); + llvm::DenseSet<unsigned> NonRootGroupIDs; + + for (const GroupRecord &GR : AllGroups) { + for (auto SI = GR.subgroup_begin(), SE = GR.subgroup_end(); SI != SE; + ++SI) { + NonRootGroupIDs.insert((unsigned)SI.getID()); + } + } + + assert(NonRootGroupIDs.size() < AllGroups.size()); + + for (unsigned i = 0, e = AllGroups.size(); i != e; ++i) { + if (!NonRootGroupIDs.count(i)) + printGroup(AllGroups[i]); + } + + return 0; + } + + void showKey() { + out << '\n' << Colors::GREEN << "GREEN" << Colors::RESET + << " = enabled by default\n\n"; + } +}; + +static void printUsage() { + llvm::errs() << "Usage: diagtool tree [--internal] [<diagnostic-group>]\n"; +} + +int TreeView::run(unsigned int argc, char **argv, llvm::raw_ostream &out) { + // First check our one flag (--flags-only). + bool Internal = false; + if (argc > 0) { + StringRef FirstArg(*argv); + if (FirstArg.equals("--internal")) { + Internal = true; + --argc; + ++argv; + } + } + + bool ShowAll = false; + StringRef RootGroup; + + switch (argc) { + case 0: + ShowAll = true; + break; + case 1: + RootGroup = argv[0]; + if (RootGroup.startswith("-W")) + RootGroup = RootGroup.substr(2); + if (RootGroup == "everything") + ShowAll = true; + // FIXME: Handle other special warning flags, like -pedantic. + break; + default: + printUsage(); + return -1; + } + + out.enable_colors(out.has_colors()); + + TreePrinter TP(out); + TP.Internal = Internal; + TP.showKey(); + return ShowAll ? TP.showAll() : TP.showGroup(RootGroup); +} diff --git a/gnu/llvm/clang/tools/diagtool/diagtool_main.cpp b/gnu/llvm/clang/tools/diagtool/diagtool_main.cpp new file mode 100644 index 00000000000..7cbe93de9c1 --- /dev/null +++ b/gnu/llvm/clang/tools/diagtool/diagtool_main.cpp @@ -0,0 +1,25 @@ +//===- diagtool_main.h - Entry point for invoking all diagnostic tools ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the main function for diagtool. +// +//===----------------------------------------------------------------------===// + +#include "DiagTool.h" + +using namespace diagtool; + +int main(int argc, char *argv[]) { + if (argc > 1) + if (DiagTool *tool = diagTools->getTool(argv[1])) + return tool->run(argc - 2, &argv[2], llvm::outs()); + + llvm::errs() << "usage: diagtool <command> [<args>]\n\n"; + diagTools->printCommands(llvm::errs()); + return 1; +} diff --git a/gnu/llvm/clang/tools/driver/CMakeLists.txt b/gnu/llvm/clang/tools/driver/CMakeLists.txt new file mode 100644 index 00000000000..2b783cff095 --- /dev/null +++ b/gnu/llvm/clang/tools/driver/CMakeLists.txt @@ -0,0 +1,124 @@ +set( LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Analysis + CodeGen + Core + IPO + AggressiveInstCombine + InstCombine + Instrumentation + MC + MCParser + ObjCARCOpts + Option + ScalarOpts + Support + TransformUtils + Vectorize + ) + +option(CLANG_PLUGIN_SUPPORT "Build clang with plugin support" ON) + +# Support plugins. +if(CLANG_PLUGIN_SUPPORT) + set(support_plugins SUPPORT_PLUGINS) +endif() + +if(NOT CLANG_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_clang_tool(clang + driver.cpp + cc1_main.cpp + cc1as_main.cpp + cc1gen_reproducer_main.cpp + + DEPENDS + ${tablegen_deps} + ${support_plugins} + ) + +clang_target_link_libraries(clang + PRIVATE + clangBasic + clangCodeGen + clangDriver + clangFrontend + clangFrontendTool + clangSerialization + ) + +if(WIN32 AND NOT CYGWIN) + # Prevent versioning if the buildhost is targeting for Win32. +else() + set_target_properties(clang PROPERTIES VERSION ${CLANG_EXECUTABLE_VERSION}) +endif() + +# Support plugins. +if(CLANG_PLUGIN_SUPPORT) + export_executable_symbols(clang) +endif() + +add_dependencies(clang clang-resource-headers) + +if(NOT CLANG_LINKS_TO_CREATE) + set(CLANG_LINKS_TO_CREATE clang++ clang-cl clang-cpp) +endif() + +foreach(link ${CLANG_LINKS_TO_CREATE}) + add_clang_symlink(${link} clang) +endforeach() + +# Configure plist creation for OS X. +set (TOOL_INFO_PLIST "Info.plist" CACHE STRING "Plist name") +if (APPLE) + if (CLANG_VENDOR) + set(TOOL_INFO_NAME "${CLANG_VENDOR} clang") + else() + set(TOOL_INFO_NAME "clang") + endif() + + set(TOOL_INFO_UTI "${CLANG_VENDOR_UTI}") + set(TOOL_INFO_VERSION "${CLANG_VERSION}") + set(TOOL_INFO_BUILD_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}") + + set(TOOL_INFO_PLIST_OUT "${CMAKE_CURRENT_BINARY_DIR}/${TOOL_INFO_PLIST}") + target_link_libraries(clang + PRIVATE + "-Wl,-sectcreate,__TEXT,__info_plist,${TOOL_INFO_PLIST_OUT}") + configure_file("${TOOL_INFO_PLIST}.in" "${TOOL_INFO_PLIST_OUT}" @ONLY) + + set(TOOL_INFO_UTI) + set(TOOL_INFO_NAME) + set(TOOL_INFO_VERSION) + set(TOOL_INFO_BUILD_VERSION) +endif() + +if(CLANG_ORDER_FILE AND + (LLVM_LINKER_IS_LD64 OR LLVM_LINKER_IS_GOLD OR LLVM_LINKER_IS_LLD)) + include(CheckLinkerFlag) + + if (LLVM_LINKER_IS_LD64) + set(LINKER_ORDER_FILE_OPTION "-Wl,-order_file,${CLANG_ORDER_FILE}") + elseif (LLVM_LINKER_IS_GOLD) + set(LINKER_ORDER_FILE_OPTION "-Wl,--section-ordering-file,${CLANG_ORDER_FILE}") + elseif (LLVM_LINKER_IS_LLD) + set(LINKER_ORDER_FILE_OPTION "-Wl,--symbol-ordering-file,${CLANG_ORDER_FILE}") + endif() + + # This is a test to ensure the actual order file works with the linker. + check_linker_flag(${LINKER_ORDER_FILE_OPTION} LINKER_ORDER_FILE_WORKS) + + # Passing an empty order file disables some linker layout optimizations. + # To work around this and enable workflows for re-linking when the order file + # changes we check during configuration if the file is empty, and make it a + # configuration dependency. + file(READ ${CLANG_ORDER_FILE} ORDER_FILE LIMIT 20) + if("${ORDER_FILE}" STREQUAL "\n") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CLANG_ORDER_FILE}) + elseif(LINKER_ORDER_FILE_WORKS) + target_link_libraries(clang PRIVATE ${LINKER_ORDER_FILE_OPTION}) + set_target_properties(clang PROPERTIES LINK_DEPENDS ${CLANG_ORDER_FILE}) + endif() +endif() diff --git a/gnu/llvm/clang/tools/driver/Info.plist.in b/gnu/llvm/clang/tools/driver/Info.plist.in new file mode 100644 index 00000000000..c2b157021df --- /dev/null +++ b/gnu/llvm/clang/tools/driver/Info.plist.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleIdentifier</key> + <string>@TOOL_INFO_UTI@</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>@TOOL_INFO_NAME@</string> + <key>CFBundleShortVersionString</key> + <string>@TOOL_INFO_VERSION@</string> + <key>CFBundleVersion</key> + <string>@TOOL_INFO_BUILD_VERSION@</string> + <key>CFBundleSignature</key> + <string>????</string> +</dict> +</plist> diff --git a/gnu/llvm/clang/tools/driver/cc1_main.cpp b/gnu/llvm/clang/tools/driver/cc1_main.cpp new file mode 100644 index 00000000000..6d1a67f2a4f --- /dev/null +++ b/gnu/llvm/clang/tools/driver/cc1_main.cpp @@ -0,0 +1,277 @@ +//===-- cc1_main.cpp - Clang CC1 Compiler Frontend ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1 functionality, which implements the +// core compiler functionality along with a number of additional tools for +// demonstration and testing purposes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Stack.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Config/config.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/FrontendTool/Utils.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/LinkAllPasses.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/BuryPointer.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/TimeProfiler.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include <cstdio> + +#ifdef CLANG_HAVE_RLIMITS +#include <sys/resource.h> +#endif + +using namespace clang; +using namespace llvm::opt; + +//===----------------------------------------------------------------------===// +// Main driver +//===----------------------------------------------------------------------===// + +static void LLVMErrorHandler(void *UserData, const std::string &Message, + bool GenCrashDiag) { + DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // Run the interrupt handlers to make sure any special cleanups get done, in + // particular that we remove files registered with RemoveFileOnSignal. + llvm::sys::RunInterruptHandlers(); + + // We cannot recover from llvm errors. When reporting a fatal error, exit + // with status 70 to generate crash diagnostics. For BSD systems this is + // defined as an internal software error. Otherwise, exit with status 1. + llvm::sys::Process::Exit(GenCrashDiag ? 70 : 1); +} + +#ifdef CLANG_HAVE_RLIMITS +#if defined(__linux__) && defined(__PIE__) +static size_t getCurrentStackAllocation() { + // If we can't compute the current stack usage, allow for 512K of command + // line arguments and environment. + size_t Usage = 512 * 1024; + if (FILE *StatFile = fopen("/proc/self/stat", "r")) { + // We assume that the stack extends from its current address to the end of + // the environment space. In reality, there is another string literal (the + // program name) after the environment, but this is close enough (we only + // need to be within 100K or so). + unsigned long StackPtr, EnvEnd; + // Disable silly GCC -Wformat warning that complains about length + // modifiers on ignored format specifiers. We want to retain these + // for documentation purposes even though they have no effect. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" +#endif + if (fscanf(StatFile, + "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %*lu " + "%*lu %*ld %*ld %*ld %*ld %*ld %*ld %*llu %*lu %*ld %*lu %*lu " + "%*lu %*lu %lu %*lu %*lu %*lu %*lu %*lu %*llu %*lu %*lu %*d %*d " + "%*u %*u %*llu %*lu %*ld %*lu %*lu %*lu %*lu %*lu %*lu %lu %*d", + &StackPtr, &EnvEnd) == 2) { +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + Usage = StackPtr < EnvEnd ? EnvEnd - StackPtr : StackPtr - EnvEnd; + } + fclose(StatFile); + } + return Usage; +} + +#include <alloca.h> + +LLVM_ATTRIBUTE_NOINLINE +static void ensureStackAddressSpace() { + // Linux kernels prior to 4.1 will sometimes locate the heap of a PIE binary + // relatively close to the stack (they are only guaranteed to be 128MiB + // apart). This results in crashes if we happen to heap-allocate more than + // 128MiB before we reach our stack high-water mark. + // + // To avoid these crashes, ensure that we have sufficient virtual memory + // pages allocated before we start running. + size_t Curr = getCurrentStackAllocation(); + const int kTargetStack = DesiredStackSize - 256 * 1024; + if (Curr < kTargetStack) { + volatile char *volatile Alloc = + static_cast<volatile char *>(alloca(kTargetStack - Curr)); + Alloc[0] = 0; + Alloc[kTargetStack - Curr - 1] = 0; + } +} +#else +static void ensureStackAddressSpace() {} +#endif + +/// Attempt to ensure that we have at least 8MiB of usable stack space. +static void ensureSufficientStack() { + struct rlimit rlim; + if (getrlimit(RLIMIT_STACK, &rlim) != 0) + return; + + // Increase the soft stack limit to our desired level, if necessary and + // possible. + if (rlim.rlim_cur != RLIM_INFINITY && + rlim.rlim_cur < rlim_t(DesiredStackSize)) { + // Try to allocate sufficient stack. + if (rlim.rlim_max == RLIM_INFINITY || + rlim.rlim_max >= rlim_t(DesiredStackSize)) + rlim.rlim_cur = DesiredStackSize; + else if (rlim.rlim_cur == rlim.rlim_max) + return; + else + rlim.rlim_cur = rlim.rlim_max; + + if (setrlimit(RLIMIT_STACK, &rlim) != 0 || + rlim.rlim_cur != DesiredStackSize) + return; + } + + // We should now have a stack of size at least DesiredStackSize. Ensure + // that we can actually use that much, if necessary. + ensureStackAddressSpace(); +} +#else +static void ensureSufficientStack() {} +#endif + +/// Print supported cpus of the given target. +static int PrintSupportedCPUs(std::string TargetStr) { + std::string Error; + const llvm::Target *TheTarget = + llvm::TargetRegistry::lookupTarget(TargetStr, Error); + if (!TheTarget) { + llvm::errs() << Error; + return 1; + } + + // the target machine will handle the mcpu printing + llvm::TargetOptions Options; + std::unique_ptr<llvm::TargetMachine> TheTargetMachine( + TheTarget->createTargetMachine(TargetStr, "", "+cpuHelp", Options, None)); + return 0; +} + +int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { + ensureSufficientStack(); + + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + + // Register the support for object-file-wrapped Clang modules. + auto PCHOps = Clang->getPCHContainerOperations(); + PCHOps->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>()); + PCHOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>()); + + // Initialize targets first, so that --version shows registered targets. + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); + + // Buffer diagnostics from argument parsing so that we can output them using a + // well formed diagnostic object. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + bool Success = + CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, Diags); + + if (Clang->getFrontendOpts().TimeTrace) { + llvm::timeTraceProfilerInitialize( + Clang->getFrontendOpts().TimeTraceGranularity, Argv0); + } + // --print-supported-cpus takes priority over the actual compilation. + if (Clang->getFrontendOpts().PrintSupportedCPUs) + return PrintSupportedCPUs(Clang->getTargetOpts().Triple); + + // Infer the builtin include path if unspecified. + if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && + Clang->getHeaderSearchOpts().ResourceDir.empty()) + Clang->getHeaderSearchOpts().ResourceDir = + CompilerInvocation::GetResourcesPath(Argv0, MainAddr); + + // Create the actual diagnostics engine. + Clang->createDiagnostics(); + if (!Clang->hasDiagnostics()) + return 1; + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + llvm::install_fatal_error_handler(LLVMErrorHandler, + static_cast<void*>(&Clang->getDiagnostics())); + + DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); + if (!Success) + return 1; + + // Execute the frontend actions. + { + llvm::TimeTraceScope TimeScope("ExecuteCompiler"); + Success = ExecuteCompilerInvocation(Clang.get()); + } + + // If any timers were active but haven't been destroyed yet, print their + // results now. This happens in -disable-free mode. + llvm::TimerGroup::printAll(llvm::errs()); + llvm::TimerGroup::clearAll(); + + if (llvm::timeTraceProfilerEnabled()) { + SmallString<128> Path(Clang->getFrontendOpts().OutputFile); + llvm::sys::path::replace_extension(Path, "json"); + if (auto profilerOutput = + Clang->createOutputFile(Path.str(), + /*Binary=*/false, + /*RemoveFileOnSignal=*/false, "", + /*Extension=*/"json", + /*useTemporary=*/false)) { + + llvm::timeTraceProfilerWrite(*profilerOutput); + // FIXME(ibiryukov): make profilerOutput flush in destructor instead. + profilerOutput->flush(); + llvm::timeTraceProfilerCleanup(); + } + } + + // Our error handler depends on the Diagnostics object, which we're + // potentially about to delete. Uninstall the handler now so that any + // later errors use the default handling behavior instead. + llvm::remove_fatal_error_handler(); + + // When running with -disable-free, don't do any destruction or shutdown. + if (Clang->getFrontendOpts().DisableFree) { + llvm::BuryPointer(std::move(Clang)); + return !Success; + } + + return !Success; +} diff --git a/gnu/llvm/clang/tools/driver/cc1as_main.cpp b/gnu/llvm/clang/tools/driver/cc1as_main.cpp new file mode 100644 index 00000000000..e1041f91bfd --- /dev/null +++ b/gnu/llvm/clang/tools/driver/cc1as_main.cpp @@ -0,0 +1,617 @@ +//===-- cc1as_main.cpp - Clang Assembler ---------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1as functionality, which implements +// the direct interface to the LLVM MC based assembler. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <system_error> +using namespace clang; +using namespace clang::driver; +using namespace clang::driver::options; +using namespace llvm; +using namespace llvm::opt; + +namespace { + +/// Helper class for representing a single invocation of the assembler. +struct AssemblerInvocation { + /// @name Target Options + /// @{ + + /// The name of the target triple to assemble for. + std::string Triple; + + /// If given, the name of the target CPU to determine which instructions + /// are legal. + std::string CPU; + + /// The list of target specific features to enable or disable -- this should + /// be a list of strings starting with '+' or '-'. + std::vector<std::string> Features; + + /// The list of symbol definitions. + std::vector<std::string> SymbolDefs; + + /// @} + /// @name Language Options + /// @{ + + std::vector<std::string> IncludePaths; + unsigned NoInitialTextSection : 1; + unsigned SaveTemporaryLabels : 1; + unsigned GenDwarfForAssembly : 1; + unsigned RelaxELFRelocations : 1; + unsigned DwarfVersion; + std::string DwarfDebugFlags; + std::string DwarfDebugProducer; + std::string DebugCompilationDir; + std::map<const std::string, const std::string> DebugPrefixMap; + llvm::DebugCompressionType CompressDebugSections = + llvm::DebugCompressionType::None; + std::string MainFileName; + std::string SplitDwarfOutput; + + /// @} + /// @name Frontend Options + /// @{ + + std::string InputFile; + std::vector<std::string> LLVMArgs; + std::string OutputPath; + enum FileType { + FT_Asm, ///< Assembly (.s) output, transliterate mode. + FT_Null, ///< No output, for timing purposes. + FT_Obj ///< Object file output. + }; + FileType OutputType; + unsigned ShowHelp : 1; + unsigned ShowVersion : 1; + + /// @} + /// @name Transliterate Options + /// @{ + + unsigned OutputAsmVariant; + unsigned ShowEncoding : 1; + unsigned ShowInst : 1; + + /// @} + /// @name Assembler Options + /// @{ + + unsigned RelaxAll : 1; + unsigned NoExecStack : 1; + unsigned FatalWarnings : 1; + unsigned NoWarn : 1; + unsigned IncrementalLinkerCompatible : 1; + unsigned EmbedBitcode : 1; + + /// The name of the relocation model to use. + std::string RelocationModel; + + /// The ABI targeted by the backend. Specified using -target-abi. Empty + /// otherwise. + std::string TargetABI; + + /// @} + +public: + AssemblerInvocation() { + Triple = ""; + NoInitialTextSection = 0; + InputFile = "-"; + OutputPath = "-"; + OutputType = FT_Asm; + OutputAsmVariant = 0; + ShowInst = 0; + ShowEncoding = 0; + RelaxAll = 0; + NoExecStack = 0; + FatalWarnings = 0; + NoWarn = 0; + IncrementalLinkerCompatible = 0; + DwarfVersion = 0; + EmbedBitcode = 0; + } + + static bool CreateFromArgs(AssemblerInvocation &Res, + ArrayRef<const char *> Argv, + DiagnosticsEngine &Diags); +}; + +} + +bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, + ArrayRef<const char *> Argv, + DiagnosticsEngine &Diags) { + bool Success = true; + + // Parse the arguments. + const OptTable &OptTbl = getDriverOptTable(); + + const unsigned IncludedFlagsBitmask = options::CC1AsOption; + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = OptTbl.ParseArgs(Argv, MissingArgIndex, MissingArgCount, + IncludedFlagsBitmask); + + // Check for missing argument error. + if (MissingArgCount) { + Diags.Report(diag::err_drv_missing_argument) + << Args.getArgString(MissingArgIndex) << MissingArgCount; + Success = false; + } + + // Issue errors on unknown arguments. + for (const Arg *A : Args.filtered(OPT_UNKNOWN)) { + auto ArgString = A->getAsString(Args); + std::string Nearest; + if (OptTbl.findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1) + Diags.Report(diag::err_drv_unknown_argument) << ArgString; + else + Diags.Report(diag::err_drv_unknown_argument_with_suggestion) + << ArgString << Nearest; + Success = false; + } + + // Construct the invocation. + + // Target Options + Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); + Opts.CPU = Args.getLastArgValue(OPT_target_cpu); + Opts.Features = Args.getAllArgValues(OPT_target_feature); + + // Use the default target triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getDefaultTargetTriple(); + + // Language Options + Opts.IncludePaths = Args.getAllArgValues(OPT_I); + Opts.NoInitialTextSection = Args.hasArg(OPT_n); + Opts.SaveTemporaryLabels = Args.hasArg(OPT_msave_temp_labels); + // Any DebugInfoKind implies GenDwarfForAssembly. + Opts.GenDwarfForAssembly = Args.hasArg(OPT_debug_info_kind_EQ); + + if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections, + OPT_compress_debug_sections_EQ)) { + if (A->getOption().getID() == OPT_compress_debug_sections) { + // TODO: be more clever about the compression type auto-detection + Opts.CompressDebugSections = llvm::DebugCompressionType::GNU; + } else { + Opts.CompressDebugSections = + llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue()) + .Case("none", llvm::DebugCompressionType::None) + .Case("zlib", llvm::DebugCompressionType::Z) + .Case("zlib-gnu", llvm::DebugCompressionType::GNU) + .Default(llvm::DebugCompressionType::None); + } + } + + Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations); + Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 2, Diags); + Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); + Opts.DwarfDebugProducer = Args.getLastArgValue(OPT_dwarf_debug_producer); + Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); + + for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) + Opts.DebugPrefixMap.insert(StringRef(Arg).split('=')); + + // Frontend Options + if (Args.hasArg(OPT_INPUT)) { + bool First = true; + for (const Arg *A : Args.filtered(OPT_INPUT)) { + if (First) { + Opts.InputFile = A->getValue(); + First = false; + } else { + Diags.Report(diag::err_drv_unknown_argument) << A->getAsString(Args); + Success = false; + } + } + } + Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); + Opts.OutputPath = Args.getLastArgValue(OPT_o); + Opts.SplitDwarfOutput = Args.getLastArgValue(OPT_split_dwarf_output); + if (Arg *A = Args.getLastArg(OPT_filetype)) { + StringRef Name = A->getValue(); + unsigned OutputType = StringSwitch<unsigned>(Name) + .Case("asm", FT_Asm) + .Case("null", FT_Null) + .Case("obj", FT_Obj) + .Default(~0U); + if (OutputType == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else + Opts.OutputType = FileType(OutputType); + } + Opts.ShowHelp = Args.hasArg(OPT_help); + Opts.ShowVersion = Args.hasArg(OPT_version); + + // Transliterate Options + Opts.OutputAsmVariant = + getLastArgIntValue(Args, OPT_output_asm_variant, 0, Diags); + Opts.ShowEncoding = Args.hasArg(OPT_show_encoding); + Opts.ShowInst = Args.hasArg(OPT_show_inst); + + // Assemble Options + Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); + Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); + Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); + Opts.NoWarn = Args.hasArg(OPT_massembler_no_warn); + Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); + Opts.TargetABI = Args.getLastArgValue(OPT_target_abi); + Opts.IncrementalLinkerCompatible = + Args.hasArg(OPT_mincremental_linker_compatible); + Opts.SymbolDefs = Args.getAllArgValues(OPT_defsym); + + // EmbedBitcode Option. If -fembed-bitcode is enabled, set the flag. + // EmbedBitcode behaves the same for all embed options for assembly files. + if (auto *A = Args.getLastArg(OPT_fembed_bitcode_EQ)) { + Opts.EmbedBitcode = llvm::StringSwitch<unsigned>(A->getValue()) + .Case("all", 1) + .Case("bitcode", 1) + .Case("marker", 1) + .Default(0); + } + + return Success; +} + +static std::unique_ptr<raw_fd_ostream> +getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) { + // Make sure that the Out file gets unlinked from the disk if we get a + // SIGINT. + if (Path != "-") + sys::RemoveFileOnSignal(Path); + + std::error_code EC; + auto Out = std::make_unique<raw_fd_ostream>( + Path, EC, (Binary ? sys::fs::OF_None : sys::fs::OF_Text)); + if (EC) { + Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message(); + return nullptr; + } + + return Out; +} + +static bool ExecuteAssembler(AssemblerInvocation &Opts, + DiagnosticsEngine &Diags) { + // Get the target specific parser. + std::string Error; + const Target *TheTarget = TargetRegistry::lookupTarget(Opts.Triple, Error); + if (!TheTarget) + return Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; + + ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = + MemoryBuffer::getFileOrSTDIN(Opts.InputFile); + + if (std::error_code EC = Buffer.getError()) { + Error = EC.message(); + return Diags.Report(diag::err_fe_error_reading) << Opts.InputFile; + } + + SourceMgr SrcMgr; + + // Tell SrcMgr about this buffer, which is what the parser will pick up. + unsigned BufferIndex = SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc()); + + // Record the location of the include directories so that the lexer can find + // it later. + SrcMgr.setIncludeDirs(Opts.IncludePaths); + + std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Opts.Triple)); + assert(MRI && "Unable to create target register info!"); + + MCTargetOptions MCOptions; + std::unique_ptr<MCAsmInfo> MAI( + TheTarget->createMCAsmInfo(*MRI, Opts.Triple, MCOptions)); + assert(MAI && "Unable to create target asm info!"); + + // Ensure MCAsmInfo initialization occurs before any use, otherwise sections + // may be created with a combination of default and explicit settings. + MAI->setCompressDebugSections(Opts.CompressDebugSections); + + MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations); + + bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; + if (Opts.OutputPath.empty()) + Opts.OutputPath = "-"; + std::unique_ptr<raw_fd_ostream> FDOS = + getOutputStream(Opts.OutputPath, Diags, IsBinary); + if (!FDOS) + return true; + std::unique_ptr<raw_fd_ostream> DwoOS; + if (!Opts.SplitDwarfOutput.empty()) + DwoOS = getOutputStream(Opts.SplitDwarfOutput, Diags, IsBinary); + + // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and + // MCObjectFileInfo needs a MCContext reference in order to initialize itself. + std::unique_ptr<MCObjectFileInfo> MOFI(new MCObjectFileInfo()); + + MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr, &MCOptions); + + bool PIC = false; + if (Opts.RelocationModel == "static") { + PIC = false; + } else if (Opts.RelocationModel == "pic") { + PIC = true; + } else { + assert(Opts.RelocationModel == "dynamic-no-pic" && + "Invalid PIC model!"); + PIC = false; + } + + MOFI->InitMCObjectFileInfo(Triple(Opts.Triple), PIC, Ctx); + if (Opts.SaveTemporaryLabels) + Ctx.setAllowTemporaryLabels(false); + if (Opts.GenDwarfForAssembly) + Ctx.setGenDwarfForAssembly(true); + if (!Opts.DwarfDebugFlags.empty()) + Ctx.setDwarfDebugFlags(StringRef(Opts.DwarfDebugFlags)); + if (!Opts.DwarfDebugProducer.empty()) + Ctx.setDwarfDebugProducer(StringRef(Opts.DwarfDebugProducer)); + if (!Opts.DebugCompilationDir.empty()) + Ctx.setCompilationDir(Opts.DebugCompilationDir); + else { + // If no compilation dir is set, try to use the current directory. + SmallString<128> CWD; + if (!sys::fs::current_path(CWD)) + Ctx.setCompilationDir(CWD); + } + if (!Opts.DebugPrefixMap.empty()) + for (const auto &KV : Opts.DebugPrefixMap) + Ctx.addDebugPrefixMapEntry(KV.first, KV.second); + if (!Opts.MainFileName.empty()) + Ctx.setMainFileName(StringRef(Opts.MainFileName)); + Ctx.setDwarfVersion(Opts.DwarfVersion); + if (Opts.GenDwarfForAssembly) + Ctx.setGenDwarfRootFile(Opts.InputFile, + SrcMgr.getMemoryBuffer(BufferIndex)->getBuffer()); + + // Build up the feature string from the target feature list. + std::string FS; + if (!Opts.Features.empty()) { + FS = Opts.Features[0]; + for (unsigned i = 1, e = Opts.Features.size(); i != e; ++i) + FS += "," + Opts.Features[i]; + } + + std::unique_ptr<MCStreamer> Str; + + std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); + std::unique_ptr<MCSubtargetInfo> STI( + TheTarget->createMCSubtargetInfo(Opts.Triple, Opts.CPU, FS)); + + raw_pwrite_stream *Out = FDOS.get(); + std::unique_ptr<buffer_ostream> BOS; + + MCOptions.MCNoWarn = Opts.NoWarn; + MCOptions.MCFatalWarnings = Opts.FatalWarnings; + MCOptions.ABIName = Opts.TargetABI; + + // FIXME: There is a bit of code duplication with addPassesToEmitFile. + if (Opts.OutputType == AssemblerInvocation::FT_Asm) { + MCInstPrinter *IP = TheTarget->createMCInstPrinter( + llvm::Triple(Opts.Triple), Opts.OutputAsmVariant, *MAI, *MCII, *MRI); + + std::unique_ptr<MCCodeEmitter> CE; + if (Opts.ShowEncoding) + CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + std::unique_ptr<MCAsmBackend> MAB( + TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); + + auto FOut = std::make_unique<formatted_raw_ostream>(*Out); + Str.reset(TheTarget->createAsmStreamer( + Ctx, std::move(FOut), /*asmverbose*/ true, + /*useDwarfDirectory*/ true, IP, std::move(CE), std::move(MAB), + Opts.ShowInst)); + } else if (Opts.OutputType == AssemblerInvocation::FT_Null) { + Str.reset(createNullStreamer(Ctx)); + } else { + assert(Opts.OutputType == AssemblerInvocation::FT_Obj && + "Invalid file type!"); + if (!FDOS->supportsSeeking()) { + BOS = std::make_unique<buffer_ostream>(*FDOS); + Out = BOS.get(); + } + + std::unique_ptr<MCCodeEmitter> CE( + TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); + std::unique_ptr<MCAsmBackend> MAB( + TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); + std::unique_ptr<MCObjectWriter> OW = + DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS) + : MAB->createObjectWriter(*Out); + + Triple T(Opts.Triple); + Str.reset(TheTarget->createMCObjectStreamer( + T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI, + Opts.RelaxAll, Opts.IncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ true)); + Str.get()->InitSections(Opts.NoExecStack); + } + + // When -fembed-bitcode is passed to clang_as, a 1-byte marker + // is emitted in __LLVM,__asm section if the object file is MachO format. + if (Opts.EmbedBitcode && Ctx.getObjectFileInfo()->getObjectFileType() == + MCObjectFileInfo::IsMachO) { + MCSection *AsmLabel = Ctx.getMachOSection( + "__LLVM", "__asm", MachO::S_REGULAR, 4, SectionKind::getReadOnly()); + Str.get()->SwitchSection(AsmLabel); + Str.get()->EmitZeros(1); + } + + // Assembly to object compilation should leverage assembly info. + Str->setUseAssemblerInfoForParsing(true); + + bool Failed = false; + + std::unique_ptr<MCAsmParser> Parser( + createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); + + // FIXME: init MCTargetOptions from sanitizer flags here. + std::unique_ptr<MCTargetAsmParser> TAP( + TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions)); + if (!TAP) + Failed = Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; + + // Set values for symbols, if any. + for (auto &S : Opts.SymbolDefs) { + auto Pair = StringRef(S).split('='); + auto Sym = Pair.first; + auto Val = Pair.second; + int64_t Value; + // We have already error checked this in the driver. + Val.getAsInteger(0, Value); + Ctx.setSymbolValue(Parser->getStreamer(), Sym, Value); + } + + if (!Failed) { + Parser->setTargetParser(*TAP.get()); + Failed = Parser->Run(Opts.NoInitialTextSection); + } + + // Close Streamer first. + // It might have a reference to the output stream. + Str.reset(); + // Close the output stream early. + BOS.reset(); + FDOS.reset(); + + // Delete output file if there were errors. + if (Failed) { + if (Opts.OutputPath != "-") + sys::fs::remove(Opts.OutputPath); + if (!Opts.SplitDwarfOutput.empty() && Opts.SplitDwarfOutput != "-") + sys::fs::remove(Opts.SplitDwarfOutput); + } + + return Failed; +} + +static void LLVMErrorHandler(void *UserData, const std::string &Message, + bool GenCrashDiag) { + DiagnosticsEngine &Diags = *static_cast<DiagnosticsEngine*>(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + sys::Process::Exit(1); +} + +int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) { + // Initialize targets and assembly printers/parsers. + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + + // Construct our diagnostic client. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticPrinter *DiagClient + = new TextDiagnosticPrinter(errs(), &*DiagOpts); + DiagClient->setPrefix("clang -cc1as"); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + ScopedFatalErrorHandler FatalErrorHandler + (LLVMErrorHandler, static_cast<void*>(&Diags)); + + // Parse the arguments. + AssemblerInvocation Asm; + if (!AssemblerInvocation::CreateFromArgs(Asm, Argv, Diags)) + return 1; + + if (Asm.ShowHelp) { + getDriverOptTable().PrintHelp( + llvm::outs(), "clang -cc1as [options] file...", + "Clang Integrated Assembler", + /*Include=*/driver::options::CC1AsOption, /*Exclude=*/0, + /*ShowAllAliases=*/false); + return 0; + } + + // Honor -version. + // + // FIXME: Use a better -version message? + if (Asm.ShowVersion) { + llvm::cl::PrintVersionMessage(); + return 0; + } + + // Honor -mllvm. + // + // FIXME: Remove this, one day. + if (!Asm.LLVMArgs.empty()) { + unsigned NumArgs = Asm.LLVMArgs.size(); + auto Args = std::make_unique<const char*[]>(NumArgs + 2); + Args[0] = "clang (LLVM option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = Asm.LLVMArgs[i].c_str(); + Args[NumArgs + 1] = nullptr; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, Args.get()); + } + + // Execute the invocation, unless there were parsing errors. + bool Failed = Diags.hasErrorOccurred() || ExecuteAssembler(Asm, Diags); + + // If any timers were active but haven't been destroyed yet, print their + // results now. + TimerGroup::printAll(errs()); + TimerGroup::clearAll(); + + return !!Failed; +} diff --git a/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp b/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp new file mode 100644 index 00000000000..4aadab7301b --- /dev/null +++ b/gnu/llvm/clang/tools/driver/cc1gen_reproducer_main.cpp @@ -0,0 +1,195 @@ +//===-- cc1gen_reproducer_main.cpp - Clang reproducer generator ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang -cc1gen-reproducer functionality, which +// generates reproducers for invocations for clang-based tools. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { + +struct UnsavedFileHash { + std::string Name; + std::string MD5; +}; + +struct ClangInvocationInfo { + std::string Toolchain; + std::string LibclangOperation; + std::string LibclangOptions; + std::vector<std::string> Arguments; + std::vector<std::string> InvocationArguments; + std::vector<UnsavedFileHash> UnsavedFileHashes; + bool Dump = false; +}; + +} // end anonymous namespace + +LLVM_YAML_IS_SEQUENCE_VECTOR(UnsavedFileHash) + +namespace llvm { +namespace yaml { + +template <> struct MappingTraits<UnsavedFileHash> { + static void mapping(IO &IO, UnsavedFileHash &Info) { + IO.mapRequired("name", Info.Name); + IO.mapRequired("md5", Info.MD5); + } +}; + +template <> struct MappingTraits<ClangInvocationInfo> { + static void mapping(IO &IO, ClangInvocationInfo &Info) { + IO.mapRequired("toolchain", Info.Toolchain); + IO.mapOptional("libclang.operation", Info.LibclangOperation); + IO.mapOptional("libclang.opts", Info.LibclangOptions); + IO.mapRequired("args", Info.Arguments); + IO.mapOptional("invocation-args", Info.InvocationArguments); + IO.mapOptional("unsaved_file_hashes", Info.UnsavedFileHashes); + } +}; + +} // end namespace yaml +} // end namespace llvm + +static std::string generateReproducerMetaInfo(const ClangInvocationInfo &Info) { + std::string Result; + llvm::raw_string_ostream OS(Result); + OS << '{'; + bool NeedComma = false; + auto EmitKey = [&](StringRef Key) { + if (NeedComma) + OS << ", "; + NeedComma = true; + OS << '"' << Key << "\": "; + }; + auto EmitStringKey = [&](StringRef Key, StringRef Value) { + if (Value.empty()) + return; + EmitKey(Key); + OS << '"' << Value << '"'; + }; + EmitStringKey("libclang.operation", Info.LibclangOperation); + EmitStringKey("libclang.opts", Info.LibclangOptions); + if (!Info.InvocationArguments.empty()) { + EmitKey("invocation-args"); + OS << '['; + for (const auto &Arg : llvm::enumerate(Info.InvocationArguments)) { + if (Arg.index()) + OS << ','; + OS << '"' << Arg.value() << '"'; + } + OS << ']'; + } + OS << '}'; + // FIXME: Compare unsaved file hashes and report mismatch in the reproducer. + if (Info.Dump) + llvm::outs() << "REPRODUCER METAINFO: " << OS.str() << "\n"; + return std::move(OS.str()); +} + +/// Generates a reproducer for a set of arguments from a specific invocation. +static llvm::Optional<driver::Driver::CompilationDiagnosticReport> +generateReproducerForInvocationArguments(ArrayRef<const char *> Argv, + const ClangInvocationInfo &Info) { + using namespace driver; + auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(Argv[0]); + + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions; + + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + DiagnosticsEngine Diags(DiagID, &*DiagOpts, new IgnoringDiagConsumer()); + ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); + Driver TheDriver(Argv[0], llvm::sys::getDefaultTargetTriple(), Diags); + TheDriver.setTargetAndMode(TargetAndMode); + + std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(Argv)); + if (C && !C->containsError()) { + for (const auto &J : C->getJobs()) { + if (const Command *Cmd = dyn_cast<Command>(&J)) { + Driver::CompilationDiagnosticReport Report; + TheDriver.generateCompilationDiagnostics( + *C, *Cmd, generateReproducerMetaInfo(Info), &Report); + return Report; + } + } + } + + return None; +} + +std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes); + +static void printReproducerInformation( + llvm::raw_ostream &OS, const ClangInvocationInfo &Info, + const driver::Driver::CompilationDiagnosticReport &Report) { + OS << "REPRODUCER:\n"; + OS << "{\n"; + OS << R"("files":[)"; + for (const auto &File : llvm::enumerate(Report.TemporaryFiles)) { + if (File.index()) + OS << ','; + OS << '"' << File.value() << '"'; + } + OS << "]\n}\n"; +} + +int cc1gen_reproducer_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr) { + if (Argv.size() < 1) { + llvm::errs() << "error: missing invocation file\n"; + return 1; + } + // Parse the invocation descriptor. + StringRef Input = Argv[0]; + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = + llvm::MemoryBuffer::getFile(Input); + if (!Buffer) { + llvm::errs() << "error: failed to read " << Input << ": " + << Buffer.getError().message() << "\n"; + return 1; + } + llvm::yaml::Input YAML(Buffer.get()->getBuffer()); + ClangInvocationInfo InvocationInfo; + YAML >> InvocationInfo; + if (Argv.size() > 1 && Argv[1] == StringRef("-v")) + InvocationInfo.Dump = true; + + // Create an invocation that will produce the reproducer. + std::vector<const char *> DriverArgs; + for (const auto &Arg : InvocationInfo.Arguments) + DriverArgs.push_back(Arg.c_str()); + std::string Path = GetExecutablePath(Argv0, /*CanonicalPrefixes=*/true); + DriverArgs[0] = Path.c_str(); + llvm::Optional<driver::Driver::CompilationDiagnosticReport> Report = + generateReproducerForInvocationArguments(DriverArgs, InvocationInfo); + + // Emit the information about the reproduce files to stdout. + int Result = 1; + if (Report) { + printReproducerInformation(llvm::outs(), InvocationInfo, *Report); + Result = 0; + } + + // Remove the input file. + llvm::sys::fs::remove(Input); + return Result; +} diff --git a/gnu/llvm/clang/tools/driver/driver.cpp b/gnu/llvm/clang/tools/driver/driver.cpp new file mode 100644 index 00000000000..7b3968341cc --- /dev/null +++ b/gnu/llvm/clang/tools/driver/driver.cpp @@ -0,0 +1,557 @@ +//===-- driver.cpp - Clang GCC-Compatible Driver --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This is the entry point to the clang driver; it is a thin wrapper +// for functionality in the Driver clang library. +// +//===----------------------------------------------------------------------===// + +#include "clang/Driver/Driver.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/Stack.h" +#include "clang/Config/config.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/ToolChain.h" +#include "clang/Frontend/ChainedDiagnosticConsumer.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/BuryPointer.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <set> +#include <system_error> +using namespace clang; +using namespace clang::driver; +using namespace llvm::opt; + +std::string GetExecutablePath(const char *Argv0, bool CanonicalPrefixes) { + if (!CanonicalPrefixes) { + SmallString<128> ExecutablePath(Argv0); + // Do a PATH lookup if Argv0 isn't a valid path. + if (!llvm::sys::fs::exists(ExecutablePath)) + if (llvm::ErrorOr<std::string> P = + llvm::sys::findProgramByName(ExecutablePath)) + ExecutablePath = *P; + return ExecutablePath.str(); + } + + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *P = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::fs::getMainExecutable(Argv0, P); +} + +static const char *GetStableCStr(std::set<std::string> &SavedStrings, + StringRef S) { + return SavedStrings.insert(S).first->c_str(); +} + +/// ApplyQAOverride - Apply a list of edits to the input argument lists. +/// +/// The input string is a space separate list of edits to perform, +/// they are applied in order to the input argument lists. Edits +/// should be one of the following forms: +/// +/// '#': Silence information about the changes to the command line arguments. +/// +/// '^': Add FOO as a new argument at the beginning of the command line. +/// +/// '+': Add FOO as a new argument at the end of the command line. +/// +/// 's/XXX/YYY/': Substitute the regular expression XXX with YYY in the command +/// line. +/// +/// 'xOPTION': Removes all instances of the literal argument OPTION. +/// +/// 'XOPTION': Removes all instances of the literal argument OPTION, +/// and the following argument. +/// +/// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox' +/// at the end of the command line. +/// +/// \param OS - The stream to write edit information to. +/// \param Args - The vector of command line arguments. +/// \param Edit - The override command to perform. +/// \param SavedStrings - Set to use for storing string representations. +static void ApplyOneQAOverride(raw_ostream &OS, + SmallVectorImpl<const char*> &Args, + StringRef Edit, + std::set<std::string> &SavedStrings) { + // This does not need to be efficient. + + if (Edit[0] == '^') { + const char *Str = + GetStableCStr(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at beginning\n"; + Args.insert(Args.begin() + 1, Str); + } else if (Edit[0] == '+') { + const char *Str = + GetStableCStr(SavedStrings, Edit.substr(1)); + OS << "### Adding argument " << Str << " at end\n"; + Args.push_back(Str); + } else if (Edit[0] == 's' && Edit[1] == '/' && Edit.endswith("/") && + Edit.slice(2, Edit.size()-1).find('/') != StringRef::npos) { + StringRef MatchPattern = Edit.substr(2).split('/').first; + StringRef ReplPattern = Edit.substr(2).split('/').second; + ReplPattern = ReplPattern.slice(0, ReplPattern.size()-1); + + for (unsigned i = 1, e = Args.size(); i != e; ++i) { + // Ignore end-of-line response file markers + if (Args[i] == nullptr) + continue; + std::string Repl = llvm::Regex(MatchPattern).sub(ReplPattern, Args[i]); + + if (Repl != Args[i]) { + OS << "### Replacing '" << Args[i] << "' with '" << Repl << "'\n"; + Args[i] = GetStableCStr(SavedStrings, Repl); + } + } + } else if (Edit[0] == 'x' || Edit[0] == 'X') { + auto Option = Edit.substr(1); + for (unsigned i = 1; i < Args.size();) { + if (Option == Args[i]) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + if (Edit[0] == 'X') { + if (i < Args.size()) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + OS << "### Invalid X edit, end of command line!\n"; + } + } else + ++i; + } + } else if (Edit[0] == 'O') { + for (unsigned i = 1; i < Args.size();) { + const char *A = Args[i]; + // Ignore end-of-line response file markers + if (A == nullptr) + continue; + if (A[0] == '-' && A[1] == 'O' && + (A[2] == '\0' || + (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || + ('0' <= A[2] && A[2] <= '9'))))) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + ++i; + } + OS << "### Adding argument " << Edit << " at end\n"; + Args.push_back(GetStableCStr(SavedStrings, '-' + Edit.str())); + } else { + OS << "### Unrecognized edit: " << Edit << "\n"; + } +} + +/// ApplyQAOverride - Apply a comma separate list of edits to the +/// input argument lists. See ApplyOneQAOverride. +static void ApplyQAOverride(SmallVectorImpl<const char*> &Args, + const char *OverrideStr, + std::set<std::string> &SavedStrings) { + raw_ostream *OS = &llvm::errs(); + + if (OverrideStr[0] == '#') { + ++OverrideStr; + OS = &llvm::nulls(); + } + + *OS << "### CCC_OVERRIDE_OPTIONS: " << OverrideStr << "\n"; + + // This does not need to be efficient. + + const char *S = OverrideStr; + while (*S) { + const char *End = ::strchr(S, ' '); + if (!End) + End = S + strlen(S); + if (End != S) + ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings); + S = End; + if (*S != '\0') + ++S; + } +} + +extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr); +extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0, + void *MainAddr); +extern int cc1gen_reproducer_main(ArrayRef<const char *> Argv, + const char *Argv0, void *MainAddr); + +static void insertTargetAndModeArgs(const ParsedClangName &NameParts, + SmallVectorImpl<const char *> &ArgVector, + std::set<std::string> &SavedStrings) { + // Put target and mode arguments at the start of argument list so that + // arguments specified in command line could override them. Avoid putting + // them at index 0, as an option like '-cc1' must remain the first. + int InsertionPoint = 0; + if (ArgVector.size() > 0) + ++InsertionPoint; + + if (NameParts.DriverMode) { + // Add the mode flag to the arguments. + ArgVector.insert(ArgVector.begin() + InsertionPoint, + GetStableCStr(SavedStrings, NameParts.DriverMode)); + } + + if (NameParts.TargetIsValid) { + const char *arr[] = {"-target", GetStableCStr(SavedStrings, + NameParts.TargetPrefix)}; + ArgVector.insert(ArgVector.begin() + InsertionPoint, + std::begin(arr), std::end(arr)); + } +} + +static void getCLEnvVarOptions(std::string &EnvValue, llvm::StringSaver &Saver, + SmallVectorImpl<const char *> &Opts) { + llvm::cl::TokenizeWindowsCommandLine(EnvValue, Saver, Opts); + // The first instance of '#' should be replaced with '=' in each option. + for (const char *Opt : Opts) + if (char *NumberSignPtr = const_cast<char *>(::strchr(Opt, '#'))) + *NumberSignPtr = '='; +} + +static void SetBackdoorDriverOutputsFromEnvVars(Driver &TheDriver) { + // Handle CC_PRINT_OPTIONS and CC_PRINT_OPTIONS_FILE. + TheDriver.CCPrintOptions = !!::getenv("CC_PRINT_OPTIONS"); + if (TheDriver.CCPrintOptions) + TheDriver.CCPrintOptionsFilename = ::getenv("CC_PRINT_OPTIONS_FILE"); + + // Handle CC_PRINT_HEADERS and CC_PRINT_HEADERS_FILE. + TheDriver.CCPrintHeaders = !!::getenv("CC_PRINT_HEADERS"); + if (TheDriver.CCPrintHeaders) + TheDriver.CCPrintHeadersFilename = ::getenv("CC_PRINT_HEADERS_FILE"); + + // Handle CC_LOG_DIAGNOSTICS and CC_LOG_DIAGNOSTICS_FILE. + TheDriver.CCLogDiagnostics = !!::getenv("CC_LOG_DIAGNOSTICS"); + if (TheDriver.CCLogDiagnostics) + TheDriver.CCLogDiagnosticsFilename = ::getenv("CC_LOG_DIAGNOSTICS_FILE"); +} + +static void FixupDiagPrefixExeName(TextDiagnosticPrinter *DiagClient, + const std::string &Path) { + // If the clang binary happens to be named cl.exe for compatibility reasons, + // use clang-cl.exe as the prefix to avoid confusion between clang and MSVC. + StringRef ExeBasename(llvm::sys::path::stem(Path)); + if (ExeBasename.equals_lower("cl")) + ExeBasename = "clang-cl"; + DiagClient->setPrefix(ExeBasename); +} + +// This lets us create the DiagnosticsEngine with a properly-filled-out +// DiagnosticOptions instance. +static DiagnosticOptions * +CreateAndPopulateDiagOpts(ArrayRef<const char *> argv, bool &UseNewCC1Process) { + auto *DiagOpts = new DiagnosticOptions; + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = getDriverOptTable().ParseArgs( + argv.slice(1), MissingArgIndex, MissingArgCount); + // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. + // Any errors that would be diagnosed here will also be diagnosed later, + // when the DiagnosticsEngine actually exists. + (void)ParseDiagnosticArgs(*DiagOpts, Args); + + UseNewCC1Process = + Args.hasFlag(clang::driver::options::OPT_fno_integrated_cc1, + clang::driver::options::OPT_fintegrated_cc1, + /*Default=*/CLANG_SPAWN_CC1); + + return DiagOpts; +} + +static void SetInstallDir(SmallVectorImpl<const char *> &argv, + Driver &TheDriver, bool CanonicalPrefixes) { + // Attempt to find the original path used to invoke the driver, to determine + // the installed path. We do this manually, because we want to support that + // path being a symlink. + SmallString<128> InstalledPath(argv[0]); + + // Do a PATH lookup, if there are no directory components. + if (llvm::sys::path::filename(InstalledPath) == InstalledPath) + if (llvm::ErrorOr<std::string> Tmp = llvm::sys::findProgramByName( + llvm::sys::path::filename(InstalledPath.str()))) + InstalledPath = *Tmp; + + // FIXME: We don't actually canonicalize this, we just make it absolute. + if (CanonicalPrefixes) + llvm::sys::fs::make_absolute(InstalledPath); + + StringRef InstalledPathParent(llvm::sys::path::parent_path(InstalledPath)); + if (llvm::sys::fs::exists(InstalledPathParent)) + TheDriver.setInstalledDir(InstalledPathParent); +} + +static int ExecuteCC1Tool(SmallVectorImpl<const char *> &ArgV) { + // If we call the cc1 tool from the clangDriver library (through + // Driver::CC1Main), we need to clean up the options usage count. The options + // are currently global, and they might have been used previously by the + // driver. + llvm::cl::ResetAllOptionOccurrences(); + + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver(A); + llvm::cl::ExpandResponseFiles(Saver, &llvm::cl::TokenizeGNUCommandLine, ArgV, + /*MarkEOLs=*/false); + StringRef Tool = ArgV[1]; + void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath; + if (Tool == "-cc1") + return cc1_main(makeArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); + if (Tool == "-cc1as") + return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0], + GetExecutablePathVP); + if (Tool == "-cc1gen-reproducer") + return cc1gen_reproducer_main(makeArrayRef(ArgV).slice(2), ArgV[0], + GetExecutablePathVP); + // Reject unknown tools. + llvm::errs() << "error: unknown integrated tool '" << Tool << "'. " + << "Valid tools include '-cc1' and '-cc1as'.\n"; + return 1; +} + +int main(int argc_, const char **argv_) { + noteBottomOfStack(); + llvm::InitLLVM X(argc_, argv_); + SmallVector<const char *, 256> argv(argv_, argv_ + argc_); + + if (llvm::sys::Process::FixupStandardFileDescriptors()) + return 1; + + llvm::InitializeAllTargets(); + auto TargetAndMode = ToolChain::getTargetAndModeFromProgramName(argv[0]); + + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver(A); + + // Parse response files using the GNU syntax, unless we're in CL mode. There + // are two ways to put clang in CL compatibility mode: argv[0] is either + // clang-cl or cl, or --driver-mode=cl is on the command line. The normal + // command line parsing can't happen until after response file parsing, so we + // 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. + bool ClangCLMode = false; + if (StringRef(TargetAndMode.DriverMode).equals("--driver-mode=cl") || + llvm::find_if(argv, [](const char *F) { + return F && strcmp(F, "--driver-mode=cl") == 0; + }) != argv.end()) { + 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 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); + + // Handle -cc1 integrated tools, even if -cc1 was expanded from a response + // file. + auto FirstArg = std::find_if(argv.begin() + 1, argv.end(), + [](const char *A) { return A != nullptr; }); + if (FirstArg != argv.end() && StringRef(*FirstArg).startswith("-cc1")) { + // If -cc1 came from a response file, remove the EOL sentinels. + if (MarkEOLs) { + auto newEnd = std::remove(argv.begin(), argv.end(), nullptr); + argv.resize(newEnd - argv.begin()); + } + return ExecuteCC1Tool(argv); + } + + // Handle options that need handling before the real command line parsing in + // Driver::BuildCompilation() + bool CanonicalPrefixes = true; + for (int i = 1, size = argv.size(); i < size; ++i) { + // Skip end-of-line response file markers + if (argv[i] == nullptr) + continue; + if (StringRef(argv[i]) == "-no-canonical-prefixes") { + CanonicalPrefixes = false; + break; + } + } + + // Handle CL and _CL_ which permits additional command line options to be + // prepended or appended. + if (ClangCLMode) { + // Arguments in "CL" are prepended. + llvm::Optional<std::string> OptCL = llvm::sys::Process::GetEnv("CL"); + if (OptCL.hasValue()) { + SmallVector<const char *, 8> PrependedOpts; + getCLEnvVarOptions(OptCL.getValue(), Saver, PrependedOpts); + + // Insert right after the program name to prepend to the argument list. + argv.insert(argv.begin() + 1, PrependedOpts.begin(), PrependedOpts.end()); + } + // Arguments in "_CL_" are appended. + llvm::Optional<std::string> Opt_CL_ = llvm::sys::Process::GetEnv("_CL_"); + if (Opt_CL_.hasValue()) { + SmallVector<const char *, 8> AppendedOpts; + getCLEnvVarOptions(Opt_CL_.getValue(), Saver, AppendedOpts); + + // Insert at the end of the argument list to append. + argv.append(AppendedOpts.begin(), AppendedOpts.end()); + } + } + + std::set<std::string> SavedStrings; + // Handle CCC_OVERRIDE_OPTIONS, used for editing a command line behind the + // scenes. + if (const char *OverrideStr = ::getenv("CCC_OVERRIDE_OPTIONS")) { + // FIXME: Driver shouldn't take extra initial argument. + ApplyQAOverride(argv, OverrideStr, SavedStrings); + } + + std::string Path = GetExecutablePath(argv[0], CanonicalPrefixes); + + // Whether the cc1 tool should be called inside the current process, or if we + // should spawn a new clang subprocess (old behavior). + // Not having an additional process saves some execution time of Windows, + // and makes debugging and profiling easier. + bool UseNewCC1Process; + + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = + CreateAndPopulateDiagOpts(argv, UseNewCC1Process); + + TextDiagnosticPrinter *DiagClient + = new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts); + FixupDiagPrefixExeName(DiagClient, Path); + + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient); + + if (!DiagOpts->DiagnosticSerializationFile.empty()) { + auto SerializedConsumer = + clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile, + &*DiagOpts, /*MergeChildRecords=*/true); + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.takeClient(), std::move(SerializedConsumer))); + } + + ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false); + + Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags); + SetInstallDir(argv, TheDriver, CanonicalPrefixes); + TheDriver.setTargetAndMode(TargetAndMode); + + insertTargetAndModeArgs(TargetAndMode, argv, SavedStrings); + + SetBackdoorDriverOutputsFromEnvVars(TheDriver); + + if (!UseNewCC1Process) { + TheDriver.CC1Main = &ExecuteCC1Tool; + // Ensure the CC1Command actually catches cc1 crashes + llvm::CrashRecoveryContext::Enable(); + } + + std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv)); + int Res = 1; + bool IsCrash = false; + if (C && !C->containsError()) { + SmallVector<std::pair<int, const Command *>, 4> FailingCommands; + Res = TheDriver.ExecuteCompilation(*C, FailingCommands); + + // Force a crash to test the diagnostics. + if (TheDriver.GenReproducer) { + Diags.Report(diag::err_drv_force_crash) + << !::getenv("FORCE_CLANG_DIAGNOSTICS_CRASH"); + + // Pretend that every command failed. + FailingCommands.clear(); + for (const auto &J : C->getJobs()) + if (const Command *C = dyn_cast<Command>(&J)) + FailingCommands.push_back(std::make_pair(-1, C)); + } + + for (const auto &P : FailingCommands) { + int CommandRes = P.first; + const Command *FailingCommand = P.second; + if (!Res) + Res = CommandRes; + + // If result status is < 0, then the driver command signalled an error. + // If result status is 70, then the driver command reported a fatal error. + // On Windows, abort will return an exit code of 3. In these cases, + // generate additional diagnostic information if possible. + IsCrash = CommandRes < 0 || CommandRes == 70; +#ifdef _WIN32 + IsCrash |= CommandRes == 3; +#endif + if (IsCrash) { + TheDriver.generateCompilationDiagnostics(*C, *FailingCommand); + break; + } + } + } + + Diags.getClient()->finish(); + + if (!UseNewCC1Process && IsCrash) { + // When crashing in -fintegrated-cc1 mode, bury the timer pointers, because + // the internal linked list might point to already released stack frames. + llvm::BuryPointer(llvm::TimerGroup::aquireDefaultGroup()); + } else { + // If any timers were active but haven't been destroyed yet, print their + // results now. This happens in -disable-free mode. + llvm::TimerGroup::printAll(llvm::errs()); + llvm::TimerGroup::clearAll(); + } + +#ifdef _WIN32 + // Exit status should not be negative on Win32, unless abnormal termination. + // Once abnormal termination was caught, negative status should not be + // propagated. + if (Res < 0) + Res = 1; +#endif + + // If we have multiple failing commands, we return the result of the first + // failing command. + return Res; +} diff --git a/gnu/llvm/clang/tools/libclang/ARCMigrate.cpp b/gnu/llvm/clang/tools/libclang/ARCMigrate.cpp new file mode 100644 index 00000000000..da8a7e4b913 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/ARCMigrate.cpp @@ -0,0 +1,138 @@ +//===- ARCMigrate.cpp - Clang-C ARC Migration Library ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the main API hooks in the Clang-C ARC Migration library. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/Index.h" +#include "CXString.h" +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/Config/config.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "llvm/Support/FileSystem.h" + +using namespace clang; +using namespace arcmt; + +namespace { + +struct Remap { + std::vector<std::pair<std::string, std::string> > Vec; +}; + +} // anonymous namespace. + +//===----------------------------------------------------------------------===// +// libClang public APIs. +//===----------------------------------------------------------------------===// + +CXRemapping clang_getRemappings(const char *migrate_dir_path) { +#if !CLANG_ENABLE_ARCMT + llvm::errs() << "error: feature not enabled in this build\n"; + return nullptr; +#else + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + if (!migrate_dir_path) { + if (Logging) + llvm::errs() << "clang_getRemappings was called with NULL parameter\n"; + return nullptr; + } + + if (!llvm::sys::fs::exists(migrate_dir_path)) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappings(\"" << migrate_dir_path + << "\")\n"; + llvm::errs() << "\"" << migrate_dir_path << "\" does not exist\n"; + } + return nullptr; + } + + TextDiagnosticBuffer diagBuffer; + std::unique_ptr<Remap> remap(new Remap()); + + bool err = arcmt::getFileRemappings(remap->Vec, migrate_dir_path,&diagBuffer); + + if (err) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappings(\"" << migrate_dir_path + << "\")\n"; + for (TextDiagnosticBuffer::const_iterator + I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I) + llvm::errs() << I->second << '\n'; + } + return nullptr; + } + + return remap.release(); +#endif +} + +CXRemapping clang_getRemappingsFromFileList(const char **filePaths, + unsigned numFiles) { +#if !CLANG_ENABLE_ARCMT + llvm::errs() << "error: feature not enabled in this build\n"; + return nullptr; +#else + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + std::unique_ptr<Remap> remap(new Remap()); + + if (numFiles == 0) { + if (Logging) + llvm::errs() << "clang_getRemappingsFromFileList was called with " + "numFiles=0\n"; + return remap.release(); + } + + if (!filePaths) { + if (Logging) + llvm::errs() << "clang_getRemappingsFromFileList was called with " + "NULL filePaths\n"; + return nullptr; + } + + TextDiagnosticBuffer diagBuffer; + SmallVector<StringRef, 32> Files(filePaths, filePaths + numFiles); + + bool err = arcmt::getFileRemappingsFromFileList(remap->Vec, Files, + &diagBuffer); + + if (err) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappingsFromFileList\n"; + for (TextDiagnosticBuffer::const_iterator + I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I) + llvm::errs() << I->second << '\n'; + } + return remap.release(); + } + + return remap.release(); +#endif +} + +unsigned clang_remap_getNumFiles(CXRemapping map) { + return static_cast<Remap *>(map)->Vec.size(); + +} + +void clang_remap_getFilenames(CXRemapping map, unsigned index, + CXString *original, CXString *transformed) { + if (original) + *original = cxstring::createDup( + static_cast<Remap *>(map)->Vec[index].first); + if (transformed) + *transformed = cxstring::createDup( + static_cast<Remap *>(map)->Vec[index].second); +} + +void clang_remap_dispose(CXRemapping map) { + delete static_cast<Remap *>(map); +} diff --git a/gnu/llvm/clang/tools/libclang/BuildSystem.cpp b/gnu/llvm/clang/tools/libclang/BuildSystem.cpp new file mode 100644 index 00000000000..0d69dcf1725 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/BuildSystem.cpp @@ -0,0 +1,151 @@ +//===- BuildSystem.cpp - Utilities for use by build systems ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements various utilities for use by build systems. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/BuildSystem.h" +#include "CXString.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace llvm::sys; + +unsigned long long clang_getBuildSessionTimestamp(void) { + return llvm::sys::toTimeT(std::chrono::system_clock::now()); +} + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(llvm::vfs::YAMLVFSWriter, + CXVirtualFileOverlay) + +CXVirtualFileOverlay clang_VirtualFileOverlay_create(unsigned) { + return wrap(new llvm::vfs::YAMLVFSWriter()); +} + +enum CXErrorCode +clang_VirtualFileOverlay_addFileMapping(CXVirtualFileOverlay VFO, + const char *virtualPath, + const char *realPath) { + if (!VFO || !virtualPath || !realPath) + return CXError_InvalidArguments; + if (!path::is_absolute(virtualPath)) + return CXError_InvalidArguments; + if (!path::is_absolute(realPath)) + return CXError_InvalidArguments; + + for (path::const_iterator + PI = path::begin(virtualPath), + PE = path::end(virtualPath); PI != PE; ++PI) { + StringRef Comp = *PI; + if (Comp == "." || Comp == "..") + return CXError_InvalidArguments; + } + + unwrap(VFO)->addFileMapping(virtualPath, realPath); + return CXError_Success; +} + +enum CXErrorCode +clang_VirtualFileOverlay_setCaseSensitivity(CXVirtualFileOverlay VFO, + int caseSensitive) { + if (!VFO) + return CXError_InvalidArguments; + unwrap(VFO)->setCaseSensitivity(caseSensitive); + return CXError_Success; +} + +enum CXErrorCode +clang_VirtualFileOverlay_writeToBuffer(CXVirtualFileOverlay VFO, unsigned, + char **out_buffer_ptr, + unsigned *out_buffer_size) { + if (!VFO || !out_buffer_ptr || !out_buffer_size) + return CXError_InvalidArguments; + + llvm::SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + unwrap(VFO)->write(OS); + + StringRef Data = OS.str(); + *out_buffer_ptr = static_cast<char*>(llvm::safe_malloc(Data.size())); + *out_buffer_size = Data.size(); + memcpy(*out_buffer_ptr, Data.data(), Data.size()); + return CXError_Success; +} + +void clang_free(void *buffer) { + free(buffer); +} + +void clang_VirtualFileOverlay_dispose(CXVirtualFileOverlay VFO) { + delete unwrap(VFO); +} + + +struct CXModuleMapDescriptorImpl { + std::string ModuleName; + std::string UmbrellaHeader; +}; + +CXModuleMapDescriptor clang_ModuleMapDescriptor_create(unsigned) { + return new CXModuleMapDescriptorImpl(); +} + +enum CXErrorCode +clang_ModuleMapDescriptor_setFrameworkModuleName(CXModuleMapDescriptor MMD, + const char *name) { + if (!MMD || !name) + return CXError_InvalidArguments; + + MMD->ModuleName = name; + return CXError_Success; +} + +enum CXErrorCode +clang_ModuleMapDescriptor_setUmbrellaHeader(CXModuleMapDescriptor MMD, + const char *name) { + if (!MMD || !name) + return CXError_InvalidArguments; + + MMD->UmbrellaHeader = name; + return CXError_Success; +} + +enum CXErrorCode +clang_ModuleMapDescriptor_writeToBuffer(CXModuleMapDescriptor MMD, unsigned, + char **out_buffer_ptr, + unsigned *out_buffer_size) { + if (!MMD || !out_buffer_ptr || !out_buffer_size) + return CXError_InvalidArguments; + + llvm::SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "framework module " << MMD->ModuleName << " {\n"; + OS << " umbrella header \""; + OS.write_escaped(MMD->UmbrellaHeader) << "\"\n"; + OS << '\n'; + OS << " export *\n"; + OS << " module * { export * }\n"; + OS << "}\n"; + + StringRef Data = OS.str(); + *out_buffer_ptr = static_cast<char*>(llvm::safe_malloc(Data.size())); + *out_buffer_size = Data.size(); + memcpy(*out_buffer_ptr, Data.data(), Data.size()); + return CXError_Success; +} + +void clang_ModuleMapDescriptor_dispose(CXModuleMapDescriptor MMD) { + delete MMD; +} diff --git a/gnu/llvm/clang/tools/libclang/CIndex.cpp b/gnu/llvm/clang/tools/libclang/CIndex.cpp new file mode 100644 index 00000000000..9a09fca5e26 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndex.cpp @@ -0,0 +1,9052 @@ +//===- CIndex.cpp - Clang-C Source Indexing Library -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the main API hooks in the Clang-C Source Indexing +// library. +// +//===----------------------------------------------------------------------===// + +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CLog.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "CXType.h" +#include "CursorVisitor.h" +#include "clang-c/FatalErrorHandler.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/Stack.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Index/CommentToXML.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <mutex> + +#if LLVM_ENABLE_THREADS != 0 && defined(__APPLE__) +#define USE_DARWIN_THREADS +#endif + +#ifdef USE_DARWIN_THREADS +#include <pthread.h> +#endif + +using namespace clang; +using namespace clang::cxcursor; +using namespace clang::cxtu; +using namespace clang::cxindex; + +CXTranslationUnit cxtu::MakeCXTranslationUnit(CIndexer *CIdx, + std::unique_ptr<ASTUnit> AU) { + if (!AU) + return nullptr; + assert(CIdx); + CXTranslationUnit D = new CXTranslationUnitImpl(); + D->CIdx = CIdx; + D->TheASTUnit = AU.release(); + D->StringPool = new cxstring::CXStringPool(); + D->Diagnostics = nullptr; + D->OverridenCursorsPool = createOverridenCXCursorsPool(); + D->CommentToXML = nullptr; + D->ParsingOptions = 0; + D->Arguments = {}; + return D; +} + +bool cxtu::isASTReadError(ASTUnit *AU) { + for (ASTUnit::stored_diag_iterator D = AU->stored_diag_begin(), + DEnd = AU->stored_diag_end(); + D != DEnd; ++D) { + if (D->getLevel() >= DiagnosticsEngine::Error && + DiagnosticIDs::getCategoryNumberForDiag(D->getID()) == + diag::DiagCat_AST_Deserialization_Issue) + return true; + } + return false; +} + +cxtu::CXTUOwner::~CXTUOwner() { + if (TU) + clang_disposeTranslationUnit(TU); +} + +/// Compare two source ranges to determine their relative position in +/// the translation unit. +static RangeComparisonResult RangeCompare(SourceManager &SM, + SourceRange R1, + SourceRange R2) { + assert(R1.isValid() && "First range is invalid?"); + assert(R2.isValid() && "Second range is invalid?"); + if (R1.getEnd() != R2.getBegin() && + SM.isBeforeInTranslationUnit(R1.getEnd(), R2.getBegin())) + return RangeBefore; + if (R2.getEnd() != R1.getBegin() && + SM.isBeforeInTranslationUnit(R2.getEnd(), R1.getBegin())) + return RangeAfter; + return RangeOverlap; +} + +/// Determine if a source location falls within, before, or after a +/// a given source range. +static RangeComparisonResult LocationCompare(SourceManager &SM, + SourceLocation L, SourceRange R) { + assert(R.isValid() && "First range is invalid?"); + assert(L.isValid() && "Second range is invalid?"); + if (L == R.getBegin() || L == R.getEnd()) + return RangeOverlap; + if (SM.isBeforeInTranslationUnit(L, R.getBegin())) + return RangeBefore; + if (SM.isBeforeInTranslationUnit(R.getEnd(), L)) + return RangeAfter; + return RangeOverlap; +} + +/// Translate a Clang source range into a CIndex source range. +/// +/// Clang internally represents ranges where the end location points to the +/// start of the token at the end. However, for external clients it is more +/// useful to have a CXSourceRange be a proper half-open interval. This routine +/// does the appropriate translation. +CXSourceRange cxloc::translateSourceRange(const SourceManager &SM, + const LangOptions &LangOpts, + const CharSourceRange &R) { + // We want the last character in this location, so we will adjust the + // location accordingly. + SourceLocation EndLoc = R.getEnd(); + bool IsTokenRange = R.isTokenRange(); + if (EndLoc.isValid() && EndLoc.isMacroID() && !SM.isMacroArgExpansion(EndLoc)) { + CharSourceRange Expansion = SM.getExpansionRange(EndLoc); + EndLoc = Expansion.getEnd(); + IsTokenRange = Expansion.isTokenRange(); + } + if (IsTokenRange && EndLoc.isValid()) { + unsigned Length = Lexer::MeasureTokenLength(SM.getSpellingLoc(EndLoc), + SM, LangOpts); + EndLoc = EndLoc.getLocWithOffset(Length); + } + + CXSourceRange Result = { + { &SM, &LangOpts }, + R.getBegin().getRawEncoding(), + EndLoc.getRawEncoding() + }; + return Result; +} + +//===----------------------------------------------------------------------===// +// Cursor visitor. +//===----------------------------------------------------------------------===// + +static SourceRange getRawCursorExtent(CXCursor C); +static SourceRange getFullCursorExtent(CXCursor C, SourceManager &SrcMgr); + +RangeComparisonResult CursorVisitor::CompareRegionOfInterest(SourceRange R) { + return RangeCompare(AU->getSourceManager(), R, RegionOfInterest); +} + +/// Visit the given cursor and, if requested by the visitor, +/// its children. +/// +/// \param Cursor the cursor to visit. +/// +/// \param CheckedRegionOfInterest if true, then the caller already checked +/// that this cursor is within the region of interest. +/// +/// \returns true if the visitation should be aborted, false if it +/// should continue. +bool CursorVisitor::Visit(CXCursor Cursor, bool CheckedRegionOfInterest) { + if (clang_isInvalid(Cursor.kind)) + return false; + + if (clang_isDeclaration(Cursor.kind)) { + const Decl *D = getCursorDecl(Cursor); + if (!D) { + assert(0 && "Invalid declaration cursor"); + return true; // abort. + } + + // Ignore implicit declarations, unless it's an objc method because + // currently we should report implicit methods for properties when indexing. + if (D->isImplicit() && !isa<ObjCMethodDecl>(D)) + return false; + } + + // If we have a range of interest, and this cursor doesn't intersect with it, + // we're done. + if (RegionOfInterest.isValid() && !CheckedRegionOfInterest) { + SourceRange Range = getRawCursorExtent(Cursor); + if (Range.isInvalid() || CompareRegionOfInterest(Range)) + return false; + } + + switch (Visitor(Cursor, Parent, ClientData)) { + case CXChildVisit_Break: + return true; + + case CXChildVisit_Continue: + return false; + + case CXChildVisit_Recurse: { + bool ret = VisitChildren(Cursor); + if (PostChildrenVisitor) + if (PostChildrenVisitor(Cursor, ClientData)) + return true; + return ret; + } + } + + llvm_unreachable("Invalid CXChildVisitResult!"); +} + +static bool visitPreprocessedEntitiesInRange(SourceRange R, + PreprocessingRecord &PPRec, + CursorVisitor &Visitor) { + SourceManager &SM = Visitor.getASTUnit()->getSourceManager(); + FileID FID; + + if (!Visitor.shouldVisitIncludedEntities()) { + // If the begin/end of the range lie in the same FileID, do the optimization + // where we skip preprocessed entities that do not come from the same FileID. + FID = SM.getFileID(SM.getFileLoc(R.getBegin())); + if (FID != SM.getFileID(SM.getFileLoc(R.getEnd()))) + FID = FileID(); + } + + const auto &Entities = PPRec.getPreprocessedEntitiesInRange(R); + return Visitor.visitPreprocessedEntities(Entities.begin(), Entities.end(), + PPRec, FID); +} + +bool CursorVisitor::visitFileRegion() { + if (RegionOfInterest.isInvalid()) + return false; + + ASTUnit *Unit = cxtu::getASTUnit(TU); + SourceManager &SM = Unit->getSourceManager(); + + std::pair<FileID, unsigned> + Begin = SM.getDecomposedLoc(SM.getFileLoc(RegionOfInterest.getBegin())), + End = SM.getDecomposedLoc(SM.getFileLoc(RegionOfInterest.getEnd())); + + if (End.first != Begin.first) { + // If the end does not reside in the same file, try to recover by + // picking the end of the file of begin location. + End.first = Begin.first; + End.second = SM.getFileIDSize(Begin.first); + } + + assert(Begin.first == End.first); + if (Begin.second > End.second) + return false; + + FileID File = Begin.first; + unsigned Offset = Begin.second; + unsigned Length = End.second - Begin.second; + + if (!VisitDeclsOnly && !VisitPreprocessorLast) + if (visitPreprocessedEntitiesInRegion()) + return true; // visitation break. + + if (visitDeclsFromFileRegion(File, Offset, Length)) + return true; // visitation break. + + if (!VisitDeclsOnly && VisitPreprocessorLast) + return visitPreprocessedEntitiesInRegion(); + + return false; +} + +static bool isInLexicalContext(Decl *D, DeclContext *DC) { + if (!DC) + return false; + + for (DeclContext *DeclDC = D->getLexicalDeclContext(); + DeclDC; DeclDC = DeclDC->getLexicalParent()) { + if (DeclDC == DC) + return true; + } + return false; +} + +bool CursorVisitor::visitDeclsFromFileRegion(FileID File, + unsigned Offset, unsigned Length) { + ASTUnit *Unit = cxtu::getASTUnit(TU); + SourceManager &SM = Unit->getSourceManager(); + SourceRange Range = RegionOfInterest; + + SmallVector<Decl *, 16> Decls; + Unit->findFileRegionDecls(File, Offset, Length, Decls); + + // If we didn't find any file level decls for the file, try looking at the + // file that it was included from. + while (Decls.empty() || Decls.front()->isTopLevelDeclInObjCContainer()) { + bool Invalid = false; + const SrcMgr::SLocEntry &SLEntry = SM.getSLocEntry(File, &Invalid); + if (Invalid) + return false; + + SourceLocation Outer; + if (SLEntry.isFile()) + Outer = SLEntry.getFile().getIncludeLoc(); + else + Outer = SLEntry.getExpansion().getExpansionLocStart(); + if (Outer.isInvalid()) + return false; + + std::tie(File, Offset) = SM.getDecomposedExpansionLoc(Outer); + Length = 0; + Unit->findFileRegionDecls(File, Offset, Length, Decls); + } + + assert(!Decls.empty()); + + bool VisitedAtLeastOnce = false; + DeclContext *CurDC = nullptr; + SmallVectorImpl<Decl *>::iterator DIt = Decls.begin(); + for (SmallVectorImpl<Decl *>::iterator DE = Decls.end(); DIt != DE; ++DIt) { + Decl *D = *DIt; + if (D->getSourceRange().isInvalid()) + continue; + + if (isInLexicalContext(D, CurDC)) + continue; + + CurDC = dyn_cast<DeclContext>(D); + + if (TagDecl *TD = dyn_cast<TagDecl>(D)) + if (!TD->isFreeStanding()) + continue; + + RangeComparisonResult CompRes = RangeCompare(SM, D->getSourceRange(),Range); + if (CompRes == RangeBefore) + continue; + if (CompRes == RangeAfter) + break; + + assert(CompRes == RangeOverlap); + VisitedAtLeastOnce = true; + + if (isa<ObjCContainerDecl>(D)) { + FileDI_current = &DIt; + FileDE_current = DE; + } else { + FileDI_current = nullptr; + } + + if (Visit(MakeCXCursor(D, TU, Range), /*CheckedRegionOfInterest=*/true)) + return true; // visitation break. + } + + if (VisitedAtLeastOnce) + return false; + + // No Decls overlapped with the range. Move up the lexical context until there + // is a context that contains the range or we reach the translation unit + // level. + DeclContext *DC = DIt == Decls.begin() ? (*DIt)->getLexicalDeclContext() + : (*(DIt-1))->getLexicalDeclContext(); + + while (DC && !DC->isTranslationUnit()) { + Decl *D = cast<Decl>(DC); + SourceRange CurDeclRange = D->getSourceRange(); + if (CurDeclRange.isInvalid()) + break; + + if (RangeCompare(SM, CurDeclRange, Range) == RangeOverlap) { + if (Visit(MakeCXCursor(D, TU, Range), /*CheckedRegionOfInterest=*/true)) + return true; // visitation break. + } + + DC = D->getLexicalDeclContext(); + } + + return false; +} + +bool CursorVisitor::visitPreprocessedEntitiesInRegion() { + if (!AU->getPreprocessor().getPreprocessingRecord()) + return false; + + PreprocessingRecord &PPRec + = *AU->getPreprocessor().getPreprocessingRecord(); + SourceManager &SM = AU->getSourceManager(); + + if (RegionOfInterest.isValid()) { + SourceRange MappedRange = AU->mapRangeToPreamble(RegionOfInterest); + SourceLocation B = MappedRange.getBegin(); + SourceLocation E = MappedRange.getEnd(); + + if (AU->isInPreambleFileID(B)) { + if (SM.isLoadedSourceLocation(E)) + return visitPreprocessedEntitiesInRange(SourceRange(B, E), + PPRec, *this); + + // Beginning of range lies in the preamble but it also extends beyond + // it into the main file. Split the range into 2 parts, one covering + // the preamble and another covering the main file. This allows subsequent + // calls to visitPreprocessedEntitiesInRange to accept a source range that + // lies in the same FileID, allowing it to skip preprocessed entities that + // do not come from the same FileID. + bool breaked = + visitPreprocessedEntitiesInRange( + SourceRange(B, AU->getEndOfPreambleFileID()), + PPRec, *this); + if (breaked) return true; + return visitPreprocessedEntitiesInRange( + SourceRange(AU->getStartOfMainFileID(), E), + PPRec, *this); + } + + return visitPreprocessedEntitiesInRange(SourceRange(B, E), PPRec, *this); + } + + bool OnlyLocalDecls + = !AU->isMainFileAST() && AU->getOnlyLocalDecls(); + + if (OnlyLocalDecls) + return visitPreprocessedEntities(PPRec.local_begin(), PPRec.local_end(), + PPRec); + + return visitPreprocessedEntities(PPRec.begin(), PPRec.end(), PPRec); +} + +template<typename InputIterator> +bool CursorVisitor::visitPreprocessedEntities(InputIterator First, + InputIterator Last, + PreprocessingRecord &PPRec, + FileID FID) { + for (; First != Last; ++First) { + if (!FID.isInvalid() && !PPRec.isEntityInFileID(First, FID)) + continue; + + PreprocessedEntity *PPE = *First; + if (!PPE) + continue; + + if (MacroExpansion *ME = dyn_cast<MacroExpansion>(PPE)) { + if (Visit(MakeMacroExpansionCursor(ME, TU))) + return true; + + continue; + } + + if (MacroDefinitionRecord *MD = dyn_cast<MacroDefinitionRecord>(PPE)) { + if (Visit(MakeMacroDefinitionCursor(MD, TU))) + return true; + + continue; + } + + if (InclusionDirective *ID = dyn_cast<InclusionDirective>(PPE)) { + if (Visit(MakeInclusionDirectiveCursor(ID, TU))) + return true; + + continue; + } + } + + return false; +} + +/// Visit the children of the given cursor. +/// +/// \returns true if the visitation should be aborted, false if it +/// should continue. +bool CursorVisitor::VisitChildren(CXCursor Cursor) { + if (clang_isReference(Cursor.kind) && + Cursor.kind != CXCursor_CXXBaseSpecifier) { + // By definition, references have no children. + return false; + } + + // Set the Parent field to Cursor, then back to its old value once we're + // done. + SetParentRAII SetParent(Parent, StmtParent, Cursor); + + if (clang_isDeclaration(Cursor.kind)) { + Decl *D = const_cast<Decl *>(getCursorDecl(Cursor)); + if (!D) + return false; + + return VisitAttributes(D) || Visit(D); + } + + if (clang_isStatement(Cursor.kind)) { + if (const Stmt *S = getCursorStmt(Cursor)) + return Visit(S); + + return false; + } + + if (clang_isExpression(Cursor.kind)) { + if (const Expr *E = getCursorExpr(Cursor)) + return Visit(E); + + return false; + } + + if (clang_isTranslationUnit(Cursor.kind)) { + CXTranslationUnit TU = getCursorTU(Cursor); + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + + int VisitOrder[2] = { VisitPreprocessorLast, !VisitPreprocessorLast }; + for (unsigned I = 0; I != 2; ++I) { + if (VisitOrder[I]) { + if (!CXXUnit->isMainFileAST() && CXXUnit->getOnlyLocalDecls() && + RegionOfInterest.isInvalid()) { + for (ASTUnit::top_level_iterator TL = CXXUnit->top_level_begin(), + TLEnd = CXXUnit->top_level_end(); + TL != TLEnd; ++TL) { + const Optional<bool> V = handleDeclForVisitation(*TL); + if (!V.hasValue()) + continue; + return V.getValue(); + } + } else if (VisitDeclContext( + CXXUnit->getASTContext().getTranslationUnitDecl())) + return true; + continue; + } + + // Walk the preprocessing record. + if (CXXUnit->getPreprocessor().getPreprocessingRecord()) + visitPreprocessedEntitiesInRegion(); + } + + return false; + } + + if (Cursor.kind == CXCursor_CXXBaseSpecifier) { + if (const CXXBaseSpecifier *Base = getCursorCXXBaseSpecifier(Cursor)) { + if (TypeSourceInfo *BaseTSInfo = Base->getTypeSourceInfo()) { + return Visit(BaseTSInfo->getTypeLoc()); + } + } + } + + if (Cursor.kind == CXCursor_IBOutletCollectionAttr) { + const IBOutletCollectionAttr *A = + cast<IBOutletCollectionAttr>(cxcursor::getCursorAttr(Cursor)); + if (const ObjCObjectType *ObjT = A->getInterface()->getAs<ObjCObjectType>()) + return Visit(cxcursor::MakeCursorObjCClassRef( + ObjT->getInterface(), + A->getInterfaceLoc()->getTypeLoc().getBeginLoc(), TU)); + } + + // If pointing inside a macro definition, check if the token is an identifier + // that was ever defined as a macro. In such a case, create a "pseudo" macro + // expansion cursor for that token. + SourceLocation BeginLoc = RegionOfInterest.getBegin(); + if (Cursor.kind == CXCursor_MacroDefinition && + BeginLoc == RegionOfInterest.getEnd()) { + SourceLocation Loc = AU->mapLocationToPreamble(BeginLoc); + const MacroInfo *MI = + getMacroInfo(cxcursor::getCursorMacroDefinition(Cursor), TU); + if (MacroDefinitionRecord *MacroDef = + checkForMacroInMacroDefinition(MI, Loc, TU)) + return Visit(cxcursor::MakeMacroExpansionCursor(MacroDef, BeginLoc, TU)); + } + + // Nothing to visit at the moment. + return false; +} + +bool CursorVisitor::VisitBlockDecl(BlockDecl *B) { + if (TypeSourceInfo *TSInfo = B->getSignatureAsWritten()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + if (Stmt *Body = B->getBody()) + return Visit(MakeCXCursor(Body, StmtParent, TU, RegionOfInterest)); + + return false; +} + +Optional<bool> CursorVisitor::shouldVisitCursor(CXCursor Cursor) { + if (RegionOfInterest.isValid()) { + SourceRange Range = getFullCursorExtent(Cursor, AU->getSourceManager()); + if (Range.isInvalid()) + return None; + + switch (CompareRegionOfInterest(Range)) { + case RangeBefore: + // This declaration comes before the region of interest; skip it. + return None; + + case RangeAfter: + // This declaration comes after the region of interest; we're done. + return false; + + case RangeOverlap: + // This declaration overlaps the region of interest; visit it. + break; + } + } + return true; +} + +bool CursorVisitor::VisitDeclContext(DeclContext *DC) { + DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); + + // FIXME: Eventually remove. This part of a hack to support proper + // iteration over all Decls contained lexically within an ObjC container. + SaveAndRestore<DeclContext::decl_iterator*> DI_saved(DI_current, &I); + SaveAndRestore<DeclContext::decl_iterator> DE_saved(DE_current, E); + + for ( ; I != E; ++I) { + Decl *D = *I; + if (D->getLexicalDeclContext() != DC) + continue; + // Filter out synthesized property accessor redeclarations. + if (isa<ObjCImplDecl>(DC)) + if (auto *OMD = dyn_cast<ObjCMethodDecl>(D)) + if (OMD->isSynthesizedAccessorStub()) + continue; + const Optional<bool> V = handleDeclForVisitation(D); + if (!V.hasValue()) + continue; + return V.getValue(); + } + return false; +} + +Optional<bool> CursorVisitor::handleDeclForVisitation(const Decl *D) { + CXCursor Cursor = MakeCXCursor(D, TU, RegionOfInterest); + + // 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; + } + + // 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); + + } else if (Cursor.kind == CXCursor_ObjCProtocolDecl) { + auto *PD = cast<ObjCProtocolDecl>(D); + if (!PD->isThisDeclarationADefinition()) + Cursor = MakeCursorObjCProtocolRef(PD, PD->getLocation(), TU); + } + + 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) { + llvm_unreachable("Translation units are visited directly by Visit()"); +} + +bool CursorVisitor::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + return Visit(MakeCXCursor(D->getTemplatedDecl(), TU, RegionOfInterest)); +} + +bool CursorVisitor::VisitTypeAliasDecl(TypeAliasDecl *D) { + if (TypeSourceInfo *TSInfo = D->getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitTypedefDecl(TypedefDecl *D) { + if (TypeSourceInfo *TSInfo = D->getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitTagDecl(TagDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitClassTemplateSpecializationDecl( + ClassTemplateSpecializationDecl *D) { + bool ShouldVisitBody = false; + switch (D->getSpecializationKind()) { + case TSK_Undeclared: + case TSK_ImplicitInstantiation: + // Nothing to visit + return false; + + case TSK_ExplicitInstantiationDeclaration: + case TSK_ExplicitInstantiationDefinition: + break; + + case TSK_ExplicitSpecialization: + ShouldVisitBody = true; + break; + } + + // Visit the template arguments used in the specialization. + if (TypeSourceInfo *SpecType = D->getTypeAsWritten()) { + TypeLoc TL = SpecType->getTypeLoc(); + if (TemplateSpecializationTypeLoc TSTLoc = + TL.getAs<TemplateSpecializationTypeLoc>()) { + for (unsigned I = 0, N = TSTLoc.getNumArgs(); I != N; ++I) + if (VisitTemplateArgumentLoc(TSTLoc.getArgLoc(I))) + return true; + } + } + + return ShouldVisitBody && VisitCXXRecordDecl(D); +} + +bool CursorVisitor::VisitClassTemplatePartialSpecializationDecl( + ClassTemplatePartialSpecializationDecl *D) { + // FIXME: Visit the "outer" template parameter lists on the TagDecl + // before visiting these template parameters. + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + // Visit the partial specialization arguments. + const ASTTemplateArgumentListInfo *Info = D->getTemplateArgsAsWritten(); + const TemplateArgumentLoc *TemplateArgs = Info->getTemplateArgs(); + for (unsigned I = 0, N = Info->NumTemplateArgs; I != N; ++I) + if (VisitTemplateArgumentLoc(TemplateArgs[I])) + return true; + + return VisitCXXRecordDecl(D); +} + +bool CursorVisitor::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { + if (const auto *TC = D->getTypeConstraint()) + if (Visit(MakeCXCursor(TC->getImmediatelyDeclaredConstraint(), StmtParent, + TU, RegionOfInterest))) + return true; + + // Visit the default argument. + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) + if (TypeSourceInfo *DefArg = D->getDefaultArgumentInfo()) + if (Visit(DefArg->getTypeLoc())) + return true; + + return false; +} + +bool CursorVisitor::VisitEnumConstantDecl(EnumConstantDecl *D) { + if (Expr *Init = D->getInitExpr()) + return Visit(MakeCXCursor(Init, StmtParent, TU, RegionOfInterest)); + return false; +} + +bool CursorVisitor::VisitDeclaratorDecl(DeclaratorDecl *DD) { + unsigned NumParamList = DD->getNumTemplateParameterLists(); + for (unsigned i = 0; i < NumParamList; i++) { + TemplateParameterList* Params = DD->getTemplateParameterList(i); + if (VisitTemplateParameters(Params)) + return true; + } + + if (TypeSourceInfo *TSInfo = DD->getTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + // Visit the nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = DD->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return false; +} + +static bool HasTrailingReturnType(FunctionDecl *ND) { + const QualType Ty = ND->getType(); + if (const FunctionType *AFT = Ty->getAs<FunctionType>()) { + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(AFT)) + return FT->hasTrailingReturn(); + } + + return false; +} + +/// Compare two base or member initializers based on their source order. +static int CompareCXXCtorInitializers(CXXCtorInitializer *const *X, + CXXCtorInitializer *const *Y) { + return (*X)->getSourceOrder() - (*Y)->getSourceOrder(); +} + +bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { + unsigned NumParamList = ND->getNumTemplateParameterLists(); + for (unsigned i = 0; i < NumParamList; i++) { + TemplateParameterList* Params = ND->getTemplateParameterList(i); + if (VisitTemplateParameters(Params)) + return true; + } + + if (TypeSourceInfo *TSInfo = ND->getTypeSourceInfo()) { + // Visit the function declaration's syntactic components in the order + // written. This requires a bit of work. + TypeLoc TL = TSInfo->getTypeLoc().IgnoreParens(); + FunctionTypeLoc FTL = TL.getAs<FunctionTypeLoc>(); + const bool HasTrailingRT = HasTrailingReturnType(ND); + + // If we have a function declared directly (without the use of a typedef), + // visit just the return type. Otherwise, just visit the function's type + // now. + if ((FTL && !isa<CXXConversionDecl>(ND) && !HasTrailingRT && + Visit(FTL.getReturnLoc())) || + (!FTL && Visit(TL))) + return true; + + // Visit the nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = ND->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + // Visit the declaration name. + if (!isa<CXXDestructorDecl>(ND)) + if (VisitDeclarationNameInfo(ND->getNameInfo())) + return true; + + // FIXME: Visit explicitly-specified template arguments! + + // Visit the function parameters, if we have a function type. + if (FTL && VisitFunctionTypeLoc(FTL, true)) + return true; + + // Visit the function's trailing return type. + if (FTL && HasTrailingRT && Visit(FTL.getReturnLoc())) + return true; + + // FIXME: Attributes? + } + + if (ND->doesThisDeclarationHaveABody() && !ND->isLateTemplateParsed()) { + if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(ND)) { + // Find the initializers that were written in the source. + SmallVector<CXXCtorInitializer *, 4> WrittenInits; + for (auto *I : Constructor->inits()) { + if (!I->isWritten()) + continue; + + WrittenInits.push_back(I); + } + + // Sort the initializers in source order + llvm::array_pod_sort(WrittenInits.begin(), WrittenInits.end(), + &CompareCXXCtorInitializers); + + // Visit the initializers in source order + for (unsigned I = 0, N = WrittenInits.size(); I != N; ++I) { + CXXCtorInitializer *Init = WrittenInits[I]; + if (Init->isAnyMemberInitializer()) { + if (Visit(MakeCursorMemberRef(Init->getAnyMember(), + Init->getMemberLocation(), TU))) + return true; + } else if (TypeSourceInfo *TInfo = Init->getTypeSourceInfo()) { + if (Visit(TInfo->getTypeLoc())) + return true; + } + + // Visit the initializer value. + if (Expr *Initializer = Init->getInit()) + if (Visit(MakeCXCursor(Initializer, ND, TU, RegionOfInterest))) + return true; + } + } + + if (Visit(MakeCXCursor(ND->getBody(), StmtParent, TU, RegionOfInterest))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitFieldDecl(FieldDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (Expr *BitWidth = D->getBitWidth()) + return Visit(MakeCXCursor(BitWidth, StmtParent, TU, RegionOfInterest)); + + if (Expr *Init = D->getInClassInitializer()) + return Visit(MakeCXCursor(Init, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitVarDecl(VarDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (Expr *Init = D->getInit()) + return Visit(MakeCXCursor(Init, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) + if (Expr *DefArg = D->getDefaultArgument()) + return Visit(MakeCXCursor(DefArg, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { + // FIXME: Visit the "outer" template parameter lists on the FunctionDecl + // before visiting these template parameters. + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + auto* FD = D->getTemplatedDecl(); + return VisitAttributes(FD) || VisitFunctionDecl(FD); +} + +bool CursorVisitor::VisitClassTemplateDecl(ClassTemplateDecl *D) { + // FIXME: Visit the "outer" template parameter lists on the TagDecl + // before visiting these template parameters. + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + auto* CD = D->getTemplatedDecl(); + return VisitAttributes(CD) || VisitCXXRecordDecl(CD); +} + +bool CursorVisitor::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) { + if (VisitTemplateParameters(D->getTemplateParameters())) + return true; + + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited() && + VisitTemplateArgumentLoc(D->getDefaultArgument())) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCTypeParamDecl(ObjCTypeParamDecl *D) { + // Visit the bound, if it's explicit. + if (D->hasExplicitBound()) { + if (auto TInfo = D->getTypeSourceInfo()) { + if (Visit(TInfo->getTypeLoc())) + return true; + } + } + + return false; +} + +bool CursorVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) { + if (TypeSourceInfo *TSInfo = ND->getReturnTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + for (const auto *P : ND->parameters()) { + if (Visit(MakeCXCursor(P, TU, RegionOfInterest))) + return true; + } + + return ND->isThisDeclarationADefinition() && + Visit(MakeCXCursor(ND->getBody(), StmtParent, TU, RegionOfInterest)); +} + +template <typename DeclIt> +static void addRangedDeclsInContainer(DeclIt *DI_current, DeclIt DE_current, + SourceManager &SM, SourceLocation EndLoc, + SmallVectorImpl<Decl *> &Decls) { + DeclIt next = *DI_current; + while (++next != DE_current) { + Decl *D_next = *next; + if (!D_next) + break; + SourceLocation L = D_next->getBeginLoc(); + if (!L.isValid()) + break; + if (SM.isBeforeInTranslationUnit(L, EndLoc)) { + *DI_current = next; + Decls.push_back(D_next); + continue; + } + break; + } +} + +bool CursorVisitor::VisitObjCContainerDecl(ObjCContainerDecl *D) { + // FIXME: Eventually convert back to just 'VisitDeclContext()'. Essentially + // an @implementation can lexically contain Decls that are not properly + // nested in the AST. When we identify such cases, we need to retrofit + // this nesting here. + if (!DI_current && !FileDI_current) + return VisitDeclContext(D); + + // Scan the Decls that immediately come after the container + // in the current DeclContext. If any fall within the + // container's lexical region, stash them into a vector + // for later processing. + SmallVector<Decl *, 24> DeclsInContainer; + SourceLocation EndLoc = D->getSourceRange().getEnd(); + SourceManager &SM = AU->getSourceManager(); + if (EndLoc.isValid()) { + if (DI_current) { + addRangedDeclsInContainer(DI_current, DE_current, SM, EndLoc, + DeclsInContainer); + } else { + addRangedDeclsInContainer(FileDI_current, FileDE_current, SM, EndLoc, + DeclsInContainer); + } + } + + // The common case. + if (DeclsInContainer.empty()) + return VisitDeclContext(D); + + // Get all the Decls in the DeclContext, and sort them with the + // additional ones we've collected. Then visit them. + for (auto *SubDecl : D->decls()) { + if (!SubDecl || SubDecl->getLexicalDeclContext() != D || + SubDecl->getBeginLoc().isInvalid()) + continue; + DeclsInContainer.push_back(SubDecl); + } + + // Now sort the Decls so that they appear in lexical order. + llvm::sort(DeclsInContainer, + [&SM](Decl *A, Decl *B) { + SourceLocation L_A = A->getBeginLoc(); + SourceLocation L_B = B->getBeginLoc(); + return L_A != L_B ? SM.isBeforeInTranslationUnit(L_A, L_B) + : SM.isBeforeInTranslationUnit(A->getEndLoc(), + B->getEndLoc()); + }); + + // Now visit the decls. + for (SmallVectorImpl<Decl*>::iterator I = DeclsInContainer.begin(), + E = DeclsInContainer.end(); I != E; ++I) { + CXCursor Cursor = MakeCXCursor(*I, TU, RegionOfInterest); + const Optional<bool> &V = shouldVisitCursor(Cursor); + if (!V.hasValue()) + continue; + if (!V.getValue()) + return false; + if (Visit(Cursor, true)) + return true; + } + return false; +} + +bool CursorVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { + if (Visit(MakeCursorObjCClassRef(ND->getClassInterface(), ND->getLocation(), + TU))) + return true; + + if (VisitObjCTypeParamList(ND->getTypeParamList())) + return true; + + ObjCCategoryDecl::protocol_loc_iterator PL = ND->protocol_loc_begin(); + for (ObjCCategoryDecl::protocol_iterator I = ND->protocol_begin(), + E = ND->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(ND); +} + +bool CursorVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) { + if (!PID->isThisDeclarationADefinition()) + return Visit(MakeCursorObjCProtocolRef(PID, PID->getLocation(), TU)); + + ObjCProtocolDecl::protocol_loc_iterator PL = PID->protocol_loc_begin(); + for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), + E = PID->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(PID); +} + +bool CursorVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *PD) { + if (PD->getTypeSourceInfo() && Visit(PD->getTypeSourceInfo()->getTypeLoc())) + return true; + + // FIXME: This implements a workaround with @property declarations also being + // installed in the DeclContext for the @interface. Eventually this code + // should be removed. + ObjCCategoryDecl *CDecl = dyn_cast<ObjCCategoryDecl>(PD->getDeclContext()); + if (!CDecl || !CDecl->IsClassExtension()) + return false; + + ObjCInterfaceDecl *ID = CDecl->getClassInterface(); + if (!ID) + return false; + + IdentifierInfo *PropertyId = PD->getIdentifier(); + ObjCPropertyDecl *prevDecl = + ObjCPropertyDecl::findPropertyDecl(cast<DeclContext>(ID), PropertyId, + PD->getQueryKind()); + + if (!prevDecl) + return false; + + // Visit synthesized methods since they will be skipped when visiting + // the @interface. + if (ObjCMethodDecl *MD = prevDecl->getGetterMethodDecl()) + if (MD->isPropertyAccessor() && MD->getLexicalDeclContext() == CDecl) + if (Visit(MakeCXCursor(MD, TU, RegionOfInterest))) + return true; + + if (ObjCMethodDecl *MD = prevDecl->getSetterMethodDecl()) + if (MD->isPropertyAccessor() && MD->getLexicalDeclContext() == CDecl) + if (Visit(MakeCXCursor(MD, TU, RegionOfInterest))) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCTypeParamList(ObjCTypeParamList *typeParamList) { + if (!typeParamList) + return false; + + for (auto *typeParam : *typeParamList) { + // Visit the type parameter. + if (Visit(MakeCXCursor(typeParam, TU, RegionOfInterest))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { + if (!D->isThisDeclarationADefinition()) { + // Forward declaration is treated like a reference. + return Visit(MakeCursorObjCClassRef(D, D->getLocation(), TU)); + } + + // Objective-C type parameters. + if (VisitObjCTypeParamList(D->getTypeParamListAsWritten())) + return true; + + // Issue callbacks for super class. + if (D->getSuperClass() && + Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(), + D->getSuperClassLoc(), + TU))) + return true; + + if (TypeSourceInfo *SuperClassTInfo = D->getSuperClassTInfo()) + if (Visit(SuperClassTInfo->getTypeLoc())) + return true; + + ObjCInterfaceDecl::protocol_loc_iterator PL = D->protocol_loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator I = D->protocol_begin(), + E = D->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(D); +} + +bool CursorVisitor::VisitObjCImplDecl(ObjCImplDecl *D) { + return VisitObjCContainerDecl(D); +} + +bool CursorVisitor::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { + // 'ID' could be null when dealing with invalid code. + if (ObjCInterfaceDecl *ID = D->getClassInterface()) + if (Visit(MakeCursorObjCClassRef(ID, D->getLocation(), TU))) + return true; + + return VisitObjCImplDecl(D); +} + +bool CursorVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { +#if 0 + // Issue callbacks for super class. + // FIXME: No source location information! + if (D->getSuperClass() && + Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(), + D->getSuperClassLoc(), + TU))) + return true; +#endif + + return VisitObjCImplDecl(D); +} + +bool CursorVisitor::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *PD) { + if (ObjCIvarDecl *Ivar = PD->getPropertyIvarDecl()) + if (PD->isIvarNameSpecified()) + return Visit(MakeCursorMemberRef(Ivar, PD->getPropertyIvarDeclLoc(), TU)); + + return false; +} + +bool CursorVisitor::VisitNamespaceDecl(NamespaceDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return Visit(MakeCursorNamespaceRef(D->getAliasedNamespace(), + D->getTargetNameLoc(), TU)); +} + +bool CursorVisitor::VisitUsingDecl(UsingDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) { + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + } + + if (Visit(MakeCursorOverloadedDeclRef(D, D->getLocation(), TU))) + return true; + + return VisitDeclarationNameInfo(D->getNameInfo()); +} + +bool CursorVisitor::VisitUsingDirectiveDecl(UsingDirectiveDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return Visit(MakeCursorNamespaceRef(D->getNominatedNamespaceAsWritten(), + D->getIdentLocation(), TU)); +} + +bool CursorVisitor::VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) { + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + } + + return VisitDeclarationNameInfo(D->getNameInfo()); +} + +bool CursorVisitor::VisitUnresolvedUsingTypenameDecl( + UnresolvedUsingTypenameDecl *D) { + // Visit nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + return false; +} + +bool CursorVisitor::VisitStaticAssertDecl(StaticAssertDecl *D) { + if (Visit(MakeCXCursor(D->getAssertExpr(), StmtParent, TU, RegionOfInterest))) + return true; + if (StringLiteral *Message = D->getMessage()) + if (Visit(MakeCXCursor(Message, StmtParent, TU, RegionOfInterest))) + return true; + return false; +} + +bool CursorVisitor::VisitFriendDecl(FriendDecl *D) { + if (NamedDecl *FriendD = D->getFriendDecl()) { + if (Visit(MakeCXCursor(FriendD, TU, RegionOfInterest))) + return true; + } else if (TypeSourceInfo *TI = D->getFriendType()) { + if (Visit(TI->getTypeLoc())) + return true; + } + return false; +} + +bool CursorVisitor::VisitDeclarationNameInfo(DeclarationNameInfo Name) { + switch (Name.getName().getNameKind()) { + case clang::DeclarationName::Identifier: + case clang::DeclarationName::CXXLiteralOperatorName: + case clang::DeclarationName::CXXDeductionGuideName: + case clang::DeclarationName::CXXOperatorName: + case clang::DeclarationName::CXXUsingDirective: + return false; + + case clang::DeclarationName::CXXConstructorName: + case clang::DeclarationName::CXXDestructorName: + case clang::DeclarationName::CXXConversionFunctionName: + if (TypeSourceInfo *TSInfo = Name.getNamedTypeInfo()) + return Visit(TSInfo->getTypeLoc()); + return false; + + case clang::DeclarationName::ObjCZeroArgSelector: + case clang::DeclarationName::ObjCOneArgSelector: + case clang::DeclarationName::ObjCMultiArgSelector: + // FIXME: Per-identifier location info? + return false; + } + + llvm_unreachable("Invalid DeclarationName::Kind!"); +} + +bool CursorVisitor::VisitNestedNameSpecifier(NestedNameSpecifier *NNS, + SourceRange Range) { + // FIXME: This whole routine is a hack to work around the lack of proper + // source information in nested-name-specifiers (PR5791). Since we do have + // a beginning source location, we can visit the first component of the + // nested-name-specifier, if it's a single-token component. + if (!NNS) + return false; + + // Get the first component in the nested-name-specifier. + while (NestedNameSpecifier *Prefix = NNS->getPrefix()) + NNS = Prefix; + + switch (NNS->getKind()) { + case NestedNameSpecifier::Namespace: + return Visit(MakeCursorNamespaceRef(NNS->getAsNamespace(), Range.getBegin(), + TU)); + + case NestedNameSpecifier::NamespaceAlias: + return Visit(MakeCursorNamespaceRef(NNS->getAsNamespaceAlias(), + Range.getBegin(), TU)); + + case NestedNameSpecifier::TypeSpec: { + // If the type has a form where we know that the beginning of the source + // range matches up with a reference cursor. Visit the appropriate reference + // cursor. + const Type *T = NNS->getAsType(); + if (const TypedefType *Typedef = dyn_cast<TypedefType>(T)) + return Visit(MakeCursorTypeRef(Typedef->getDecl(), Range.getBegin(), TU)); + if (const TagType *Tag = dyn_cast<TagType>(T)) + return Visit(MakeCursorTypeRef(Tag->getDecl(), Range.getBegin(), TU)); + if (const TemplateSpecializationType *TST + = dyn_cast<TemplateSpecializationType>(T)) + return VisitTemplateName(TST->getTemplateName(), Range.getBegin()); + break; + } + + case NestedNameSpecifier::TypeSpecWithTemplate: + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Super: + break; + } + + return false; +} + +bool +CursorVisitor::VisitNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier) { + SmallVector<NestedNameSpecifierLoc, 4> Qualifiers; + for (; Qualifier; Qualifier = Qualifier.getPrefix()) + Qualifiers.push_back(Qualifier); + + while (!Qualifiers.empty()) { + NestedNameSpecifierLoc Q = Qualifiers.pop_back_val(); + NestedNameSpecifier *NNS = Q.getNestedNameSpecifier(); + switch (NNS->getKind()) { + case NestedNameSpecifier::Namespace: + if (Visit(MakeCursorNamespaceRef(NNS->getAsNamespace(), + Q.getLocalBeginLoc(), + TU))) + return true; + + break; + + case NestedNameSpecifier::NamespaceAlias: + if (Visit(MakeCursorNamespaceRef(NNS->getAsNamespaceAlias(), + Q.getLocalBeginLoc(), + TU))) + return true; + + break; + + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: + if (Visit(Q.getTypeLoc())) + return true; + + break; + + case NestedNameSpecifier::Global: + case NestedNameSpecifier::Identifier: + case NestedNameSpecifier::Super: + break; + } + } + + return false; +} + +bool CursorVisitor::VisitTemplateParameters( + const TemplateParameterList *Params) { + if (!Params) + return false; + + for (TemplateParameterList::const_iterator P = Params->begin(), + PEnd = Params->end(); + P != PEnd; ++P) { + if (Visit(MakeCXCursor(*P, TU, RegionOfInterest))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) { + switch (Name.getKind()) { + case TemplateName::Template: + return Visit(MakeCursorTemplateRef(Name.getAsTemplateDecl(), Loc, TU)); + + case TemplateName::OverloadedTemplate: + // Visit the overloaded template set. + if (Visit(MakeCursorOverloadedDeclRef(Name, Loc, TU))) + return true; + + return false; + + case TemplateName::AssumedTemplate: + // FIXME: Visit DeclarationName? + return false; + + case TemplateName::DependentTemplate: + // FIXME: Visit nested-name-specifier. + return false; + + case TemplateName::QualifiedTemplate: + // FIXME: Visit nested-name-specifier. + return Visit(MakeCursorTemplateRef( + Name.getAsQualifiedTemplateName()->getDecl(), + Loc, TU)); + + case TemplateName::SubstTemplateTemplateParm: + return Visit(MakeCursorTemplateRef( + Name.getAsSubstTemplateTemplateParm()->getParameter(), + Loc, TU)); + + case TemplateName::SubstTemplateTemplateParmPack: + return Visit(MakeCursorTemplateRef( + Name.getAsSubstTemplateTemplateParmPack()->getParameterPack(), + Loc, TU)); + } + + llvm_unreachable("Invalid TemplateName::Kind!"); +} + +bool CursorVisitor::VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL) { + switch (TAL.getArgument().getKind()) { + case TemplateArgument::Null: + case TemplateArgument::Integral: + case TemplateArgument::Pack: + return false; + + case TemplateArgument::Type: + if (TypeSourceInfo *TSInfo = TAL.getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + return false; + + case TemplateArgument::Declaration: + if (Expr *E = TAL.getSourceDeclExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + + case TemplateArgument::NullPtr: + if (Expr *E = TAL.getSourceNullPtrExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + + case TemplateArgument::Expression: + if (Expr *E = TAL.getSourceExpression()) + return Visit(MakeCXCursor(E, StmtParent, TU, RegionOfInterest)); + return false; + + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + if (VisitNestedNameSpecifierLoc(TAL.getTemplateQualifierLoc())) + return true; + + return VisitTemplateName(TAL.getArgument().getAsTemplateOrTemplatePattern(), + TAL.getTemplateNameLoc()); + } + + llvm_unreachable("Invalid TemplateArgument::Kind!"); +} + +bool CursorVisitor::VisitLinkageSpecDecl(LinkageSpecDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitQualifiedTypeLoc(QualifiedTypeLoc TL) { + return Visit(TL.getUnqualifiedLoc()); +} + +bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) { + ASTContext &Context = AU->getASTContext(); + + // Some builtin types (such as Objective-C's "id", "sel", and + // "Class") have associated declarations. Create cursors for those. + QualType VisitType; + switch (TL.getTypePtr()->getKind()) { + + case BuiltinType::Void: + case BuiltinType::NullPtr: + case BuiltinType::Dependent: +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLImageTypes.def" +#define EXT_OPAQUE_TYPE(ExtTYpe, Id, Ext) \ + case BuiltinType::Id: +#include "clang/Basic/OpenCLExtensionTypes.def" + case BuiltinType::OCLSampler: + case BuiltinType::OCLEvent: + case BuiltinType::OCLClkEvent: + case BuiltinType::OCLQueue: + case BuiltinType::OCLReserveID: +#define SVE_TYPE(Name, Id, SingletonId) \ + case BuiltinType::Id: +#include "clang/Basic/AArch64SVEACLETypes.def" +#define BUILTIN_TYPE(Id, SingletonId) +#define SIGNED_TYPE(Id, SingletonId) case BuiltinType::Id: +#define UNSIGNED_TYPE(Id, SingletonId) case BuiltinType::Id: +#define FLOATING_TYPE(Id, SingletonId) case BuiltinType::Id: +#define PLACEHOLDER_TYPE(Id, SingletonId) case BuiltinType::Id: +#include "clang/AST/BuiltinTypes.def" + break; + + case BuiltinType::ObjCId: + VisitType = Context.getObjCIdType(); + break; + + case BuiltinType::ObjCClass: + VisitType = Context.getObjCClassType(); + break; + + case BuiltinType::ObjCSel: + VisitType = Context.getObjCSelType(); + break; + } + + if (!VisitType.isNull()) { + if (const TypedefType *Typedef = VisitType->getAs<TypedefType>()) + return Visit(MakeCursorTypeRef(Typedef->getDecl(), TL.getBuiltinLoc(), + TU)); + } + + return false; +} + +bool CursorVisitor::VisitTypedefTypeLoc(TypedefTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getTypedefNameDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitUnresolvedUsingTypeLoc(UnresolvedUsingTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitTagTypeLoc(TagTypeLoc TL) { + if (TL.isDefinition()) + return Visit(MakeCXCursor(TL.getDecl(), TU, RegionOfInterest)); + + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitTemplateTypeParmTypeLoc(TemplateTypeParmTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { + return Visit(MakeCursorObjCClassRef(TL.getIFaceDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitObjCTypeParamTypeLoc(ObjCTypeParamTypeLoc TL) { + if (Visit(MakeCursorTypeRef(TL.getDecl(), TL.getBeginLoc(), TU))) + return true; + for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) { + if (Visit(MakeCursorObjCProtocolRef(TL.getProtocol(I), TL.getProtocolLoc(I), + TU))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitObjCObjectTypeLoc(ObjCObjectTypeLoc TL) { + if (TL.hasBaseTypeAsWritten() && Visit(TL.getBaseLoc())) + return true; + + for (unsigned I = 0, N = TL.getNumTypeArgs(); I != N; ++I) { + if (Visit(TL.getTypeArgTInfo(I)->getTypeLoc())) + return true; + } + + for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) { + if (Visit(MakeCursorObjCProtocolRef(TL.getProtocol(I), TL.getProtocolLoc(I), + TU))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitObjCObjectPointerTypeLoc(ObjCObjectPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitParenTypeLoc(ParenTypeLoc TL) { + return Visit(TL.getInnerLoc()); +} + +bool CursorVisitor::VisitMacroQualifiedTypeLoc(MacroQualifiedTypeLoc TL) { + return Visit(TL.getInnerLoc()); +} + +bool CursorVisitor::VisitPointerTypeLoc(PointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitLValueReferenceTypeLoc(LValueReferenceTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitRValueReferenceTypeLoc(RValueReferenceTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitAttributedTypeLoc(AttributedTypeLoc TL) { + return Visit(TL.getModifiedLoc()); +} + +bool CursorVisitor::VisitFunctionTypeLoc(FunctionTypeLoc TL, + bool SkipResultType) { + if (!SkipResultType && Visit(TL.getReturnLoc())) + return true; + + for (unsigned I = 0, N = TL.getNumParams(); I != N; ++I) + if (Decl *D = TL.getParam(I)) + if (Visit(MakeCXCursor(D, TU, RegionOfInterest))) + return true; + + return false; +} + +bool CursorVisitor::VisitArrayTypeLoc(ArrayTypeLoc TL) { + if (Visit(TL.getElementLoc())) + return true; + + if (Expr *Size = TL.getSizeExpr()) + return Visit(MakeCXCursor(Size, StmtParent, TU, RegionOfInterest)); + + return false; +} + +bool CursorVisitor::VisitDecayedTypeLoc(DecayedTypeLoc TL) { + return Visit(TL.getOriginalLoc()); +} + +bool CursorVisitor::VisitAdjustedTypeLoc(AdjustedTypeLoc TL) { + return Visit(TL.getOriginalLoc()); +} + +bool CursorVisitor::VisitDeducedTemplateSpecializationTypeLoc( + DeducedTemplateSpecializationTypeLoc TL) { + if (VisitTemplateName(TL.getTypePtr()->getTemplateName(), + TL.getTemplateNameLoc())) + return true; + + return false; +} + +bool CursorVisitor::VisitTemplateSpecializationTypeLoc( + TemplateSpecializationTypeLoc TL) { + // Visit the template name. + if (VisitTemplateName(TL.getTypePtr()->getTemplateName(), + TL.getTemplateNameLoc())) + return true; + + // Visit the template arguments. + for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) + if (VisitTemplateArgumentLoc(TL.getArgLoc(I))) + return true; + + return false; +} + +bool CursorVisitor::VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) { + return Visit(MakeCXCursor(TL.getUnderlyingExpr(), StmtParent, TU)); +} + +bool CursorVisitor::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) { + if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { + if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitDependentNameTypeLoc(DependentNameTypeLoc TL) { + return VisitNestedNameSpecifierLoc(TL.getQualifierLoc()); +} + +bool CursorVisitor::VisitDependentTemplateSpecializationTypeLoc( + DependentTemplateSpecializationTypeLoc TL) { + // Visit the nested-name-specifier, if there is one. + if (TL.getQualifierLoc() && + VisitNestedNameSpecifierLoc(TL.getQualifierLoc())) + return true; + + // Visit the template arguments. + for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) + if (VisitTemplateArgumentLoc(TL.getArgLoc(I))) + return true; + + return false; +} + +bool CursorVisitor::VisitElaboratedTypeLoc(ElaboratedTypeLoc TL) { + if (VisitNestedNameSpecifierLoc(TL.getQualifierLoc())) + return true; + + return Visit(TL.getNamedTypeLoc()); +} + +bool CursorVisitor::VisitPackExpansionTypeLoc(PackExpansionTypeLoc TL) { + return Visit(TL.getPatternLoc()); +} + +bool CursorVisitor::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) { + if (Expr *E = TL.getUnderlyingExpr()) + return Visit(MakeCXCursor(E, StmtParent, TU)); + + return false; +} + +bool CursorVisitor::VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitAtomicTypeLoc(AtomicTypeLoc TL) { + return Visit(TL.getValueLoc()); +} + +bool CursorVisitor::VisitPipeTypeLoc(PipeTypeLoc TL) { + return Visit(TL.getValueLoc()); +} + +#define DEFAULT_TYPELOC_IMPL(CLASS, PARENT) \ +bool CursorVisitor::Visit##CLASS##TypeLoc(CLASS##TypeLoc TL) { \ + return Visit##PARENT##Loc(TL); \ +} + +DEFAULT_TYPELOC_IMPL(Complex, Type) +DEFAULT_TYPELOC_IMPL(ConstantArray, ArrayType) +DEFAULT_TYPELOC_IMPL(IncompleteArray, ArrayType) +DEFAULT_TYPELOC_IMPL(VariableArray, ArrayType) +DEFAULT_TYPELOC_IMPL(DependentSizedArray, ArrayType) +DEFAULT_TYPELOC_IMPL(DependentAddressSpace, Type) +DEFAULT_TYPELOC_IMPL(DependentVector, Type) +DEFAULT_TYPELOC_IMPL(DependentSizedExtVector, Type) +DEFAULT_TYPELOC_IMPL(Vector, Type) +DEFAULT_TYPELOC_IMPL(ExtVector, VectorType) +DEFAULT_TYPELOC_IMPL(FunctionProto, FunctionType) +DEFAULT_TYPELOC_IMPL(FunctionNoProto, FunctionType) +DEFAULT_TYPELOC_IMPL(Record, TagType) +DEFAULT_TYPELOC_IMPL(Enum, TagType) +DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParm, Type) +DEFAULT_TYPELOC_IMPL(SubstTemplateTypeParmPack, Type) +DEFAULT_TYPELOC_IMPL(Auto, Type) + +bool CursorVisitor::VisitCXXRecordDecl(CXXRecordDecl *D) { + // Visit the nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = D->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + if (D->isCompleteDefinition()) { + for (const auto &I : D->bases()) { + if (Visit(cxcursor::MakeCursorCXXBaseSpecifier(&I, TU))) + return true; + } + } + + return VisitTagDecl(D); +} + +bool CursorVisitor::VisitAttributes(Decl *D) { + for (const auto *I : D->attrs()) + if ((TU->ParsingOptions & CXTranslationUnit_VisitImplicitAttributes || + !I->isImplicit()) && + Visit(MakeCXCursor(I, D, TU))) + return true; + + return false; +} + +//===----------------------------------------------------------------------===// +// Data-recursive visitor methods. +//===----------------------------------------------------------------------===// + +namespace { +#define DEF_JOB(NAME, DATA, KIND)\ +class NAME : public VisitorJob {\ +public:\ + NAME(const DATA *d, CXCursor parent) : \ + VisitorJob(parent, VisitorJob::KIND, d) {} \ + static bool classof(const VisitorJob *VJ) { return VJ->getKind() == KIND; }\ + const DATA *get() const { return static_cast<const DATA*>(data[0]); }\ +}; + +DEF_JOB(StmtVisit, Stmt, StmtVisitKind) +DEF_JOB(MemberExprParts, MemberExpr, MemberExprPartsKind) +DEF_JOB(DeclRefExprParts, DeclRefExpr, DeclRefExprPartsKind) +DEF_JOB(OverloadExprParts, OverloadExpr, OverloadExprPartsKind) +DEF_JOB(SizeOfPackExprParts, SizeOfPackExpr, SizeOfPackExprPartsKind) +DEF_JOB(LambdaExprParts, LambdaExpr, LambdaExprPartsKind) +DEF_JOB(PostChildrenVisit, void, PostChildrenVisitKind) +#undef DEF_JOB + +class ExplicitTemplateArgsVisit : public VisitorJob { +public: + ExplicitTemplateArgsVisit(const TemplateArgumentLoc *Begin, + const TemplateArgumentLoc *End, CXCursor parent) + : VisitorJob(parent, VisitorJob::ExplicitTemplateArgsVisitKind, Begin, + End) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == ExplicitTemplateArgsVisitKind; + } + const TemplateArgumentLoc *begin() const { + return static_cast<const TemplateArgumentLoc *>(data[0]); + } + const TemplateArgumentLoc *end() { + return static_cast<const TemplateArgumentLoc *>(data[1]); + } +}; +class DeclVisit : public VisitorJob { +public: + DeclVisit(const Decl *D, CXCursor parent, bool isFirst) : + VisitorJob(parent, VisitorJob::DeclVisitKind, + D, isFirst ? (void*) 1 : (void*) nullptr) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == DeclVisitKind; + } + const Decl *get() const { return static_cast<const Decl *>(data[0]); } + bool isFirst() const { return data[1] != nullptr; } +}; +class TypeLocVisit : public VisitorJob { +public: + TypeLocVisit(TypeLoc tl, CXCursor parent) : + VisitorJob(parent, VisitorJob::TypeLocVisitKind, + tl.getType().getAsOpaquePtr(), tl.getOpaqueData()) {} + + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == TypeLocVisitKind; + } + + TypeLoc get() const { + QualType T = QualType::getFromOpaquePtr(data[0]); + return TypeLoc(T, const_cast<void *>(data[1])); + } +}; + +class LabelRefVisit : public VisitorJob { +public: + LabelRefVisit(LabelDecl *LD, SourceLocation labelLoc, CXCursor parent) + : VisitorJob(parent, VisitorJob::LabelRefVisitKind, LD, + labelLoc.getPtrEncoding()) {} + + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::LabelRefVisitKind; + } + const LabelDecl *get() const { + return static_cast<const LabelDecl *>(data[0]); + } + SourceLocation getLoc() const { + return SourceLocation::getFromPtrEncoding(data[1]); } +}; + +class NestedNameSpecifierLocVisit : public VisitorJob { +public: + NestedNameSpecifierLocVisit(NestedNameSpecifierLoc Qualifier, CXCursor parent) + : VisitorJob(parent, VisitorJob::NestedNameSpecifierLocVisitKind, + Qualifier.getNestedNameSpecifier(), + Qualifier.getOpaqueData()) { } + + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::NestedNameSpecifierLocVisitKind; + } + + NestedNameSpecifierLoc get() const { + return NestedNameSpecifierLoc( + const_cast<NestedNameSpecifier *>( + static_cast<const NestedNameSpecifier *>(data[0])), + const_cast<void *>(data[1])); + } +}; + +class DeclarationNameInfoVisit : public VisitorJob { +public: + DeclarationNameInfoVisit(const Stmt *S, CXCursor parent) + : VisitorJob(parent, VisitorJob::DeclarationNameInfoVisitKind, S) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::DeclarationNameInfoVisitKind; + } + DeclarationNameInfo get() const { + const Stmt *S = static_cast<const Stmt *>(data[0]); + switch (S->getStmtClass()) { + default: + llvm_unreachable("Unhandled Stmt"); + case clang::Stmt::MSDependentExistsStmtClass: + return cast<MSDependentExistsStmt>(S)->getNameInfo(); + case Stmt::CXXDependentScopeMemberExprClass: + return cast<CXXDependentScopeMemberExpr>(S)->getMemberNameInfo(); + case Stmt::DependentScopeDeclRefExprClass: + return cast<DependentScopeDeclRefExpr>(S)->getNameInfo(); + case Stmt::OMPCriticalDirectiveClass: + return cast<OMPCriticalDirective>(S)->getDirectiveName(); + } + } +}; +class MemberRefVisit : public VisitorJob { +public: + MemberRefVisit(const FieldDecl *D, SourceLocation L, CXCursor parent) + : VisitorJob(parent, VisitorJob::MemberRefVisitKind, D, + L.getPtrEncoding()) {} + static bool classof(const VisitorJob *VJ) { + return VJ->getKind() == VisitorJob::MemberRefVisitKind; + } + const FieldDecl *get() const { + return static_cast<const FieldDecl *>(data[0]); + } + SourceLocation getLoc() const { + return SourceLocation::getFromRawEncoding((unsigned)(uintptr_t) data[1]); + } +}; +class EnqueueVisitor : public ConstStmtVisitor<EnqueueVisitor, void> { + friend class OMPClauseEnqueue; + VisitorWorkList &WL; + CXCursor Parent; +public: + EnqueueVisitor(VisitorWorkList &wl, CXCursor parent) + : WL(wl), Parent(parent) {} + + void VisitAddrLabelExpr(const AddrLabelExpr *E); + void VisitBlockExpr(const BlockExpr *B); + void VisitCompoundLiteralExpr(const CompoundLiteralExpr *E); + void VisitCompoundStmt(const CompoundStmt *S); + void VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { /* Do nothing. */ } + void VisitMSDependentExistsStmt(const MSDependentExistsStmt *S); + void VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E); + void VisitCXXNewExpr(const CXXNewExpr *E); + void VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E); + void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *E); + void VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E); + void VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *E); + void VisitCXXTypeidExpr(const CXXTypeidExpr *E); + void VisitCXXUnresolvedConstructExpr(const CXXUnresolvedConstructExpr *E); + void VisitCXXUuidofExpr(const CXXUuidofExpr *E); + void VisitCXXCatchStmt(const CXXCatchStmt *S); + void VisitCXXForRangeStmt(const CXXForRangeStmt *S); + void VisitDeclRefExpr(const DeclRefExpr *D); + void VisitDeclStmt(const DeclStmt *S); + void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *E); + void VisitDesignatedInitExpr(const DesignatedInitExpr *E); + void VisitExplicitCastExpr(const ExplicitCastExpr *E); + void VisitForStmt(const ForStmt *FS); + void VisitGotoStmt(const GotoStmt *GS); + void VisitIfStmt(const IfStmt *If); + void VisitInitListExpr(const InitListExpr *IE); + void VisitMemberExpr(const MemberExpr *M); + void VisitOffsetOfExpr(const OffsetOfExpr *E); + void VisitObjCEncodeExpr(const ObjCEncodeExpr *E); + void VisitObjCMessageExpr(const ObjCMessageExpr *M); + void VisitOverloadExpr(const OverloadExpr *E); + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E); + void VisitStmt(const Stmt *S); + void VisitSwitchStmt(const SwitchStmt *S); + void VisitWhileStmt(const WhileStmt *W); + void VisitTypeTraitExpr(const TypeTraitExpr *E); + void VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E); + void VisitExpressionTraitExpr(const ExpressionTraitExpr *E); + void VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *U); + void VisitVAArgExpr(const VAArgExpr *E); + void VisitSizeOfPackExpr(const SizeOfPackExpr *E); + void VisitPseudoObjectExpr(const PseudoObjectExpr *E); + void VisitOpaqueValueExpr(const OpaqueValueExpr *E); + void VisitLambdaExpr(const LambdaExpr *E); + void VisitOMPExecutableDirective(const OMPExecutableDirective *D); + void VisitOMPLoopDirective(const OMPLoopDirective *D); + void VisitOMPParallelDirective(const OMPParallelDirective *D); + void VisitOMPSimdDirective(const OMPSimdDirective *D); + void VisitOMPForDirective(const OMPForDirective *D); + void VisitOMPForSimdDirective(const OMPForSimdDirective *D); + void VisitOMPSectionsDirective(const OMPSectionsDirective *D); + void VisitOMPSectionDirective(const OMPSectionDirective *D); + void VisitOMPSingleDirective(const OMPSingleDirective *D); + void VisitOMPMasterDirective(const OMPMasterDirective *D); + void VisitOMPCriticalDirective(const OMPCriticalDirective *D); + void VisitOMPParallelForDirective(const OMPParallelForDirective *D); + void VisitOMPParallelForSimdDirective(const OMPParallelForSimdDirective *D); + void VisitOMPParallelMasterDirective(const OMPParallelMasterDirective *D); + void VisitOMPParallelSectionsDirective(const OMPParallelSectionsDirective *D); + void VisitOMPTaskDirective(const OMPTaskDirective *D); + void VisitOMPTaskyieldDirective(const OMPTaskyieldDirective *D); + void VisitOMPBarrierDirective(const OMPBarrierDirective *D); + void VisitOMPTaskwaitDirective(const OMPTaskwaitDirective *D); + void VisitOMPTaskgroupDirective(const OMPTaskgroupDirective *D); + void + VisitOMPCancellationPointDirective(const OMPCancellationPointDirective *D); + void VisitOMPCancelDirective(const OMPCancelDirective *D); + void VisitOMPFlushDirective(const OMPFlushDirective *D); + void VisitOMPOrderedDirective(const OMPOrderedDirective *D); + 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 VisitOMPMasterTaskLoopDirective(const OMPMasterTaskLoopDirective *D); + void + VisitOMPMasterTaskLoopSimdDirective(const OMPMasterTaskLoopSimdDirective *D); + void VisitOMPParallelMasterTaskLoopDirective( + const OMPParallelMasterTaskLoopDirective *D); + void VisitOMPParallelMasterTaskLoopSimdDirective( + const OMPParallelMasterTaskLoopSimdDirective *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); + void VisitOMPTargetSimdDirective(const OMPTargetSimdDirective *D); + void VisitOMPTeamsDistributeDirective(const OMPTeamsDistributeDirective *D); + void VisitOMPTeamsDistributeSimdDirective( + const OMPTeamsDistributeSimdDirective *D); + void VisitOMPTeamsDistributeParallelForSimdDirective( + const OMPTeamsDistributeParallelForSimdDirective *D); + void VisitOMPTeamsDistributeParallelForDirective( + const OMPTeamsDistributeParallelForDirective *D); + void VisitOMPTargetTeamsDirective(const OMPTargetTeamsDirective *D); + void VisitOMPTargetTeamsDistributeDirective( + const OMPTargetTeamsDistributeDirective *D); + void VisitOMPTargetTeamsDistributeParallelForDirective( + const OMPTargetTeamsDistributeParallelForDirective *D); + void VisitOMPTargetTeamsDistributeParallelForSimdDirective( + const OMPTargetTeamsDistributeParallelForSimdDirective *D); + void VisitOMPTargetTeamsDistributeSimdDirective( + const OMPTargetTeamsDistributeSimdDirective *D); + +private: + void AddDeclarationNameInfo(const Stmt *S); + void AddNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier); + void AddExplicitTemplateArgs(const TemplateArgumentLoc *A, + unsigned NumTemplateArgs); + void AddMemberRef(const FieldDecl *D, SourceLocation L); + void AddStmt(const Stmt *S); + void AddDecl(const Decl *D, bool isFirst = true); + void AddTypeLoc(TypeSourceInfo *TI); + void EnqueueChildren(const Stmt *S); + void EnqueueChildren(const OMPClause *S); +}; +} // end anonyous namespace + +void EnqueueVisitor::AddDeclarationNameInfo(const Stmt *S) { + // 'S' should always be non-null, since it comes from the + // statement we are visiting. + WL.push_back(DeclarationNameInfoVisit(S, Parent)); +} + +void +EnqueueVisitor::AddNestedNameSpecifierLoc(NestedNameSpecifierLoc Qualifier) { + if (Qualifier) + WL.push_back(NestedNameSpecifierLocVisit(Qualifier, Parent)); +} + +void EnqueueVisitor::AddStmt(const Stmt *S) { + if (S) + WL.push_back(StmtVisit(S, Parent)); +} +void EnqueueVisitor::AddDecl(const Decl *D, bool isFirst) { + if (D) + WL.push_back(DeclVisit(D, Parent, isFirst)); +} +void EnqueueVisitor::AddExplicitTemplateArgs(const TemplateArgumentLoc *A, + unsigned NumTemplateArgs) { + WL.push_back(ExplicitTemplateArgsVisit(A, A + NumTemplateArgs, Parent)); +} +void EnqueueVisitor::AddMemberRef(const FieldDecl *D, SourceLocation L) { + if (D) + WL.push_back(MemberRefVisit(D, L, Parent)); +} +void EnqueueVisitor::AddTypeLoc(TypeSourceInfo *TI) { + if (TI) + WL.push_back(TypeLocVisit(TI->getTypeLoc(), Parent)); + } +void EnqueueVisitor::EnqueueChildren(const Stmt *S) { + unsigned size = WL.size(); + for (const Stmt *SubStmt : S->children()) { + AddStmt(SubStmt); + } + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} +namespace { +class OMPClauseEnqueue : public ConstOMPClauseVisitor<OMPClauseEnqueue> { + EnqueueVisitor *Visitor; + /// Process clauses with list of variables. + template <typename T> + void VisitOMPClauseList(T *Node); +public: + OMPClauseEnqueue(EnqueueVisitor *Visitor) : Visitor(Visitor) { } +#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) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getCondition()); +} + +void OMPClauseEnqueue::VisitOMPFinalClause(const OMPFinalClause *C) { + Visitor->AddStmt(C->getCondition()); +} + +void OMPClauseEnqueue::VisitOMPNumThreadsClause(const OMPNumThreadsClause *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getNumThreads()); +} + +void OMPClauseEnqueue::VisitOMPSafelenClause(const OMPSafelenClause *C) { + Visitor->AddStmt(C->getSafelen()); +} + +void OMPClauseEnqueue::VisitOMPSimdlenClause(const OMPSimdlenClause *C) { + Visitor->AddStmt(C->getSimdlen()); +} + +void OMPClauseEnqueue::VisitOMPAllocatorClause(const OMPAllocatorClause *C) { + Visitor->AddStmt(C->getAllocator()); +} + +void OMPClauseEnqueue::VisitOMPCollapseClause(const OMPCollapseClause *C) { + Visitor->AddStmt(C->getNumForLoops()); +} + +void OMPClauseEnqueue::VisitOMPDefaultClause(const OMPDefaultClause *C) { } + +void OMPClauseEnqueue::VisitOMPProcBindClause(const OMPProcBindClause *C) { } + +void OMPClauseEnqueue::VisitOMPScheduleClause(const OMPScheduleClause *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getChunkSize()); +} + +void OMPClauseEnqueue::VisitOMPOrderedClause(const OMPOrderedClause *C) { + Visitor->AddStmt(C->getNumForLoops()); +} + +void OMPClauseEnqueue::VisitOMPNowaitClause(const OMPNowaitClause *) {} + +void OMPClauseEnqueue::VisitOMPUntiedClause(const OMPUntiedClause *) {} + +void OMPClauseEnqueue::VisitOMPMergeableClause(const OMPMergeableClause *) {} + +void OMPClauseEnqueue::VisitOMPReadClause(const OMPReadClause *) {} + +void OMPClauseEnqueue::VisitOMPWriteClause(const OMPWriteClause *) {} + +void OMPClauseEnqueue::VisitOMPUpdateClause(const OMPUpdateClause *) {} + +void OMPClauseEnqueue::VisitOMPCaptureClause(const OMPCaptureClause *) {} + +void OMPClauseEnqueue::VisitOMPSeqCstClause(const OMPSeqCstClause *) {} + +void OMPClauseEnqueue::VisitOMPThreadsClause(const OMPThreadsClause *) {} + +void OMPClauseEnqueue::VisitOMPSIMDClause(const OMPSIMDClause *) {} + +void OMPClauseEnqueue::VisitOMPNogroupClause(const OMPNogroupClause *) {} + +void OMPClauseEnqueue::VisitOMPUnifiedAddressClause( + const OMPUnifiedAddressClause *) {} + +void OMPClauseEnqueue::VisitOMPUnifiedSharedMemoryClause( + const OMPUnifiedSharedMemoryClause *) {} + +void OMPClauseEnqueue::VisitOMPReverseOffloadClause( + const OMPReverseOffloadClause *) {} + +void OMPClauseEnqueue::VisitOMPDynamicAllocatorsClause( + const OMPDynamicAllocatorsClause *) {} + +void OMPClauseEnqueue::VisitOMPAtomicDefaultMemOrderClause( + const OMPAtomicDefaultMemOrderClause *) {} + +void OMPClauseEnqueue::VisitOMPDeviceClause(const OMPDeviceClause *C) { + Visitor->AddStmt(C->getDevice()); +} + +void OMPClauseEnqueue::VisitOMPNumTeamsClause(const OMPNumTeamsClause *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getNumTeams()); +} + +void OMPClauseEnqueue::VisitOMPThreadLimitClause(const OMPThreadLimitClause *C) { + VisitOMPClauseWithPreInit(C); + Visitor->AddStmt(C->getThreadLimit()); +} + +void OMPClauseEnqueue::VisitOMPPriorityClause(const OMPPriorityClause *C) { + Visitor->AddStmt(C->getPriority()); +} + +void OMPClauseEnqueue::VisitOMPGrainsizeClause(const OMPGrainsizeClause *C) { + Visitor->AddStmt(C->getGrainsize()); +} + +void OMPClauseEnqueue::VisitOMPNumTasksClause(const OMPNumTasksClause *C) { + Visitor->AddStmt(C->getNumTasks()); +} + +void OMPClauseEnqueue::VisitOMPHintClause(const OMPHintClause *C) { + Visitor->AddStmt(C->getHint()); +} + +template<typename T> +void OMPClauseEnqueue::VisitOMPClauseList(T *Node) { + for (const auto *I : Node->varlists()) { + Visitor->AddStmt(I); + } +} + +void OMPClauseEnqueue::VisitOMPAllocateClause(const OMPAllocateClause *C) { + VisitOMPClauseList(C); + Visitor->AddStmt(C->getAllocator()); +} +void OMPClauseEnqueue::VisitOMPPrivateClause(const OMPPrivateClause *C) { + VisitOMPClauseList(C); + for (const auto *E : C->private_copies()) { + Visitor->AddStmt(E); + } +} +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); + } + for (auto *E : C->source_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->destination_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->assignment_ops()) { + Visitor->AddStmt(E); + } +} +void OMPClauseEnqueue::VisitOMPSharedClause(const OMPSharedClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPReductionClause(const OMPReductionClause *C) { + VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); + for (auto *E : C->privates()) { + Visitor->AddStmt(E); + } + for (auto *E : C->lhs_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->rhs_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->reduction_ops()) { + Visitor->AddStmt(E); + } +} +void OMPClauseEnqueue::VisitOMPTaskReductionClause( + const OMPTaskReductionClause *C) { + VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); + for (auto *E : C->privates()) { + Visitor->AddStmt(E); + } + for (auto *E : C->lhs_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->rhs_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->reduction_ops()) { + Visitor->AddStmt(E); + } +} +void OMPClauseEnqueue::VisitOMPInReductionClause( + const OMPInReductionClause *C) { + VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); + for (auto *E : C->privates()) { + Visitor->AddStmt(E); + } + for (auto *E : C->lhs_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->rhs_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->reduction_ops()) { + Visitor->AddStmt(E); + } + for (auto *E : C->taskgroup_descriptors()) + Visitor->AddStmt(E); +} +void OMPClauseEnqueue::VisitOMPLinearClause(const OMPLinearClause *C) { + VisitOMPClauseList(C); + VisitOMPClauseWithPostUpdate(C); + for (const auto *E : C->privates()) { + Visitor->AddStmt(E); + } + for (const auto *E : C->inits()) { + Visitor->AddStmt(E); + } + for (const auto *E : C->updates()) { + Visitor->AddStmt(E); + } + for (const auto *E : C->finals()) { + Visitor->AddStmt(E); + } + Visitor->AddStmt(C->getStep()); + Visitor->AddStmt(C->getCalcStep()); +} +void OMPClauseEnqueue::VisitOMPAlignedClause(const OMPAlignedClause *C) { + VisitOMPClauseList(C); + Visitor->AddStmt(C->getAlignment()); +} +void OMPClauseEnqueue::VisitOMPCopyinClause(const OMPCopyinClause *C) { + VisitOMPClauseList(C); + for (auto *E : C->source_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->destination_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->assignment_ops()) { + Visitor->AddStmt(E); + } +} +void +OMPClauseEnqueue::VisitOMPCopyprivateClause(const OMPCopyprivateClause *C) { + VisitOMPClauseList(C); + for (auto *E : C->source_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->destination_exprs()) { + Visitor->AddStmt(E); + } + for (auto *E : C->assignment_ops()) { + Visitor->AddStmt(E); + } +} +void OMPClauseEnqueue::VisitOMPFlushClause(const OMPFlushClause *C) { + VisitOMPClauseList(C); +} +void OMPClauseEnqueue::VisitOMPDependClause(const OMPDependClause *C) { + VisitOMPClauseList(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 OMPClauseEnqueue::VisitOMPNontemporalClause( + const OMPNontemporalClause *C) { + VisitOMPClauseList(C); + for (const auto *E : C->private_refs()) + Visitor->AddStmt(E); +} +} + +void EnqueueVisitor::EnqueueChildren(const OMPClause *S) { + unsigned size = WL.size(); + OMPClauseEnqueue Visitor(this); + Visitor.Visit(S); + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} +void EnqueueVisitor::VisitAddrLabelExpr(const AddrLabelExpr *E) { + WL.push_back(LabelRefVisit(E->getLabel(), E->getLabelLoc(), Parent)); +} +void EnqueueVisitor::VisitBlockExpr(const BlockExpr *B) { + AddDecl(B->getBlockDecl()); +} +void EnqueueVisitor::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCompoundStmt(const CompoundStmt *S) { + for (auto &I : llvm::reverse(S->body())) + AddStmt(I); +} +void EnqueueVisitor:: +VisitMSDependentExistsStmt(const MSDependentExistsStmt *S) { + AddStmt(S->getSubStmt()); + AddDeclarationNameInfo(S); + if (NestedNameSpecifierLoc QualifierLoc = S->getQualifierLoc()) + AddNestedNameSpecifierLoc(QualifierLoc); +} + +void EnqueueVisitor:: +VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *E) { + if (E->hasExplicitTemplateArgs()) + AddExplicitTemplateArgs(E->getTemplateArgs(), E->getNumTemplateArgs()); + AddDeclarationNameInfo(E); + if (NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc()) + AddNestedNameSpecifierLoc(QualifierLoc); + if (!E->isImplicitAccess()) + AddStmt(E->getBase()); +} +void EnqueueVisitor::VisitCXXNewExpr(const CXXNewExpr *E) { + // Enqueue the initializer , if any. + AddStmt(E->getInitializer()); + // Enqueue the array size, if any. + AddStmt(E->getArraySize().getValueOr(nullptr)); + // Enqueue the allocated type. + AddTypeLoc(E->getAllocatedTypeSourceInfo()); + // Enqueue the placement arguments. + for (unsigned I = E->getNumPlacementArgs(); I > 0; --I) + AddStmt(E->getPlacementArg(I-1)); +} +void EnqueueVisitor::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *CE) { + for (unsigned I = CE->getNumArgs(); I > 1 /* Yes, this is 1 */; --I) + AddStmt(CE->getArg(I-1)); + AddStmt(CE->getCallee()); + AddStmt(CE->getArg(0)); +} +void EnqueueVisitor::VisitCXXPseudoDestructorExpr( + const CXXPseudoDestructorExpr *E) { + // Visit the name of the type being destroyed. + AddTypeLoc(E->getDestroyedTypeInfo()); + // Visit the scope type that looks disturbingly like the nested-name-specifier + // but isn't. + AddTypeLoc(E->getScopeTypeInfo()); + // Visit the nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc()) + AddNestedNameSpecifierLoc(QualifierLoc); + // Visit base expression. + AddStmt(E->getBase()); +} +void EnqueueVisitor::VisitCXXScalarValueInitExpr( + const CXXScalarValueInitExpr *E) { + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCXXTemporaryObjectExpr( + const CXXTemporaryObjectExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCXXTypeidExpr(const CXXTypeidExpr *E) { + EnqueueChildren(E); + if (E->isTypeOperand()) + AddTypeLoc(E->getTypeOperandSourceInfo()); +} + +void EnqueueVisitor::VisitCXXUnresolvedConstructExpr( + const CXXUnresolvedConstructExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitCXXUuidofExpr(const CXXUuidofExpr *E) { + EnqueueChildren(E); + if (E->isTypeOperand()) + AddTypeLoc(E->getTypeOperandSourceInfo()); +} + +void EnqueueVisitor::VisitCXXCatchStmt(const CXXCatchStmt *S) { + EnqueueChildren(S); + AddDecl(S->getExceptionDecl()); +} + +void EnqueueVisitor::VisitCXXForRangeStmt(const CXXForRangeStmt *S) { + AddStmt(S->getBody()); + AddStmt(S->getRangeInit()); + AddDecl(S->getLoopVariable()); +} + +void EnqueueVisitor::VisitDeclRefExpr(const DeclRefExpr *DR) { + if (DR->hasExplicitTemplateArgs()) + AddExplicitTemplateArgs(DR->getTemplateArgs(), DR->getNumTemplateArgs()); + WL.push_back(DeclRefExprParts(DR, Parent)); +} +void EnqueueVisitor::VisitDependentScopeDeclRefExpr( + const DependentScopeDeclRefExpr *E) { + if (E->hasExplicitTemplateArgs()) + AddExplicitTemplateArgs(E->getTemplateArgs(), E->getNumTemplateArgs()); + AddDeclarationNameInfo(E); + AddNestedNameSpecifierLoc(E->getQualifierLoc()); +} +void EnqueueVisitor::VisitDeclStmt(const DeclStmt *S) { + unsigned size = WL.size(); + bool isFirst = true; + for (const auto *D : S->decls()) { + AddDecl(D, isFirst); + isFirst = false; + } + if (size == WL.size()) + return; + // Now reverse the entries we just added. This will match the DFS + // ordering performed by the worklist. + VisitorWorkList::iterator I = WL.begin() + size, E = WL.end(); + std::reverse(I, E); +} +void EnqueueVisitor::VisitDesignatedInitExpr(const DesignatedInitExpr *E) { + AddStmt(E->getInit()); + 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)); + continue; + } + assert(D.isArrayRangeDesignator() && "Unknown designator kind"); + AddStmt(E->getArrayRangeEnd(D)); + AddStmt(E->getArrayRangeStart(D)); + } +} +void EnqueueVisitor::VisitExplicitCastExpr(const ExplicitCastExpr *E) { + EnqueueChildren(E); + AddTypeLoc(E->getTypeInfoAsWritten()); +} +void EnqueueVisitor::VisitForStmt(const ForStmt *FS) { + AddStmt(FS->getBody()); + AddStmt(FS->getInc()); + AddStmt(FS->getCond()); + AddDecl(FS->getConditionVariable()); + AddStmt(FS->getInit()); +} +void EnqueueVisitor::VisitGotoStmt(const GotoStmt *GS) { + WL.push_back(LabelRefVisit(GS->getLabel(), GS->getLabelLoc(), Parent)); +} +void EnqueueVisitor::VisitIfStmt(const IfStmt *If) { + AddStmt(If->getElse()); + AddStmt(If->getThen()); + AddStmt(If->getCond()); + AddDecl(If->getConditionVariable()); +} +void EnqueueVisitor::VisitInitListExpr(const InitListExpr *IE) { + // We care about the syntactic form of the initializer list, only. + if (InitListExpr *Syntactic = IE->getSyntacticForm()) + IE = Syntactic; + EnqueueChildren(IE); +} +void EnqueueVisitor::VisitMemberExpr(const MemberExpr *M) { + WL.push_back(MemberExprParts(M, Parent)); + + // If the base of the member access expression is an implicit 'this', don't + // visit it. + // FIXME: If we ever want to show these implicit accesses, this will be + // unfortunate. However, clang_getCursor() relies on this behavior. + if (M->isImplicitAccess()) + return; + + // Ignore base anonymous struct/union fields, otherwise they will shadow the + // real field that we are interested in. + if (auto *SubME = dyn_cast<MemberExpr>(M->getBase())) { + if (auto *FD = dyn_cast_or_null<FieldDecl>(SubME->getMemberDecl())) { + if (FD->isAnonymousStructOrUnion()) { + AddStmt(SubME->getBase()); + return; + } + } + } + + AddStmt(M->getBase()); +} +void EnqueueVisitor::VisitObjCEncodeExpr(const ObjCEncodeExpr *E) { + AddTypeLoc(E->getEncodedTypeSourceInfo()); +} +void EnqueueVisitor::VisitObjCMessageExpr(const ObjCMessageExpr *M) { + EnqueueChildren(M); + AddTypeLoc(M->getClassReceiverTypeInfo()); +} +void EnqueueVisitor::VisitOffsetOfExpr(const OffsetOfExpr *E) { + // Visit the components of the offsetof expression. + for (unsigned N = E->getNumComponents(), I = N; I > 0; --I) { + const OffsetOfNode &Node = E->getComponent(I-1); + switch (Node.getKind()) { + case OffsetOfNode::Array: + AddStmt(E->getIndexExpr(Node.getArrayExprIndex())); + break; + case OffsetOfNode::Field: + AddMemberRef(Node.getField(), Node.getSourceRange().getEnd()); + break; + case OffsetOfNode::Identifier: + case OffsetOfNode::Base: + continue; + } + } + // Visit the type into which we're computing the offset. + AddTypeLoc(E->getTypeSourceInfo()); +} +void EnqueueVisitor::VisitOverloadExpr(const OverloadExpr *E) { + if (E->hasExplicitTemplateArgs()) + AddExplicitTemplateArgs(E->getTemplateArgs(), E->getNumTemplateArgs()); + WL.push_back(OverloadExprParts(E, Parent)); +} +void EnqueueVisitor::VisitUnaryExprOrTypeTraitExpr( + const UnaryExprOrTypeTraitExpr *E) { + EnqueueChildren(E); + if (E->isArgumentType()) + AddTypeLoc(E->getArgumentTypeInfo()); +} +void EnqueueVisitor::VisitStmt(const Stmt *S) { + EnqueueChildren(S); +} +void EnqueueVisitor::VisitSwitchStmt(const SwitchStmt *S) { + AddStmt(S->getBody()); + AddStmt(S->getCond()); + AddDecl(S->getConditionVariable()); +} + +void EnqueueVisitor::VisitWhileStmt(const WhileStmt *W) { + AddStmt(W->getBody()); + AddStmt(W->getCond()); + AddDecl(W->getConditionVariable()); +} + +void EnqueueVisitor::VisitTypeTraitExpr(const TypeTraitExpr *E) { + for (unsigned I = E->getNumArgs(); I > 0; --I) + AddTypeLoc(E->getArg(I-1)); +} + +void EnqueueVisitor::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) { + AddTypeLoc(E->getQueriedTypeSourceInfo()); +} + +void EnqueueVisitor::VisitExpressionTraitExpr(const ExpressionTraitExpr *E) { + EnqueueChildren(E); +} + +void EnqueueVisitor::VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *U) { + VisitOverloadExpr(U); + if (!U->isImplicitAccess()) + AddStmt(U->getBase()); +} +void EnqueueVisitor::VisitVAArgExpr(const VAArgExpr *E) { + AddStmt(E->getSubExpr()); + AddTypeLoc(E->getWrittenTypeInfo()); +} +void EnqueueVisitor::VisitSizeOfPackExpr(const SizeOfPackExpr *E) { + WL.push_back(SizeOfPackExprParts(E, Parent)); +} +void EnqueueVisitor::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { + // If the opaque value has a source expression, just transparently + // visit that. This is useful for (e.g.) pseudo-object expressions. + if (Expr *SourceExpr = E->getSourceExpr()) + return Visit(SourceExpr); +} +void EnqueueVisitor::VisitLambdaExpr(const LambdaExpr *E) { + AddStmt(E->getBody()); + WL.push_back(LambdaExprParts(E, Parent)); +} +void EnqueueVisitor::VisitPseudoObjectExpr(const PseudoObjectExpr *E) { + // Treat the expression like its syntactic form. + Visit(E->getSyntacticForm()); +} + +void EnqueueVisitor::VisitOMPExecutableDirective( + const OMPExecutableDirective *D) { + EnqueueChildren(D); + for (ArrayRef<OMPClause *>::iterator I = D->clauses().begin(), + E = D->clauses().end(); + I != E; ++I) + EnqueueChildren(*I); +} + +void EnqueueVisitor::VisitOMPLoopDirective(const OMPLoopDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPParallelDirective(const OMPParallelDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPSimdDirective(const OMPSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPForDirective(const OMPForDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPForSimdDirective(const OMPForSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPSectionsDirective(const OMPSectionsDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPSectionDirective(const OMPSectionDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPSingleDirective(const OMPSingleDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPMasterDirective(const OMPMasterDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPCriticalDirective(const OMPCriticalDirective *D) { + VisitOMPExecutableDirective(D); + AddDeclarationNameInfo(D); +} + +void +EnqueueVisitor::VisitOMPParallelForDirective(const OMPParallelForDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPParallelForSimdDirective( + const OMPParallelForSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPParallelMasterDirective( + const OMPParallelMasterDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPParallelSectionsDirective( + const OMPParallelSectionsDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTaskDirective(const OMPTaskDirective *D) { + VisitOMPExecutableDirective(D); +} + +void +EnqueueVisitor::VisitOMPTaskyieldDirective(const OMPTaskyieldDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPBarrierDirective(const OMPBarrierDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTaskwaitDirective(const OMPTaskwaitDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTaskgroupDirective( + const OMPTaskgroupDirective *D) { + VisitOMPExecutableDirective(D); + if (const Expr *E = D->getReductionRef()) + VisitStmt(E); +} + +void EnqueueVisitor::VisitOMPFlushDirective(const OMPFlushDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPOrderedDirective(const OMPOrderedDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPAtomicDirective(const OMPAtomicDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetDirective(const OMPTargetDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetDataDirective(const + OMPTargetDataDirective *D) { + 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); +} + +void EnqueueVisitor::VisitOMPCancellationPointDirective( + const OMPCancellationPointDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPCancelDirective(const OMPCancelDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTaskLoopDirective(const OMPTaskLoopDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTaskLoopSimdDirective( + const OMPTaskLoopSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPMasterTaskLoopDirective( + const OMPMasterTaskLoopDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPMasterTaskLoopSimdDirective( + const OMPMasterTaskLoopSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPParallelMasterTaskLoopDirective( + const OMPParallelMasterTaskLoopDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPParallelMasterTaskLoopSimdDirective( + const OMPParallelMasterTaskLoopSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPDistributeDirective( + const OMPDistributeDirective *D) { + 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 EnqueueVisitor::VisitOMPTargetSimdDirective( + const OMPTargetSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTeamsDistributeDirective( + const OMPTeamsDistributeDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTeamsDistributeSimdDirective( + const OMPTeamsDistributeSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTeamsDistributeParallelForSimdDirective( + const OMPTeamsDistributeParallelForSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTeamsDistributeParallelForDirective( + const OMPTeamsDistributeParallelForDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetTeamsDirective( + const OMPTargetTeamsDirective *D) { + VisitOMPExecutableDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetTeamsDistributeDirective( + const OMPTargetTeamsDistributeDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetTeamsDistributeParallelForDirective( + const OMPTargetTeamsDistributeParallelForDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetTeamsDistributeParallelForSimdDirective( + const OMPTargetTeamsDistributeParallelForSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void EnqueueVisitor::VisitOMPTargetTeamsDistributeSimdDirective( + const OMPTargetTeamsDistributeSimdDirective *D) { + VisitOMPLoopDirective(D); +} + +void CursorVisitor::EnqueueWorkList(VisitorWorkList &WL, const Stmt *S) { + EnqueueVisitor(WL, MakeCXCursor(S, StmtParent, TU,RegionOfInterest)).Visit(S); +} + +bool CursorVisitor::IsInRegionOfInterest(CXCursor C) { + if (RegionOfInterest.isValid()) { + SourceRange Range = getRawCursorExtent(C); + if (Range.isInvalid() || CompareRegionOfInterest(Range)) + return false; + } + return true; +} + +bool CursorVisitor::RunVisitorWorkList(VisitorWorkList &WL) { + while (!WL.empty()) { + // Dequeue the worklist item. + VisitorJob LI = WL.pop_back_val(); + + // Set the Parent field, then back to its old value once we're done. + SetParentRAII SetParent(Parent, StmtParent, LI.getParent()); + + switch (LI.getKind()) { + case VisitorJob::DeclVisitKind: { + const Decl *D = cast<DeclVisit>(&LI)->get(); + if (!D) + continue; + + // For now, perform default visitation for Decls. + if (Visit(MakeCXCursor(D, TU, RegionOfInterest, + cast<DeclVisit>(&LI)->isFirst()))) + return true; + + continue; + } + case VisitorJob::ExplicitTemplateArgsVisitKind: { + for (const TemplateArgumentLoc &Arg : + *cast<ExplicitTemplateArgsVisit>(&LI)) { + if (VisitTemplateArgumentLoc(Arg)) + return true; + } + continue; + } + case VisitorJob::TypeLocVisitKind: { + // Perform default visitation for TypeLocs. + if (Visit(cast<TypeLocVisit>(&LI)->get())) + return true; + continue; + } + case VisitorJob::LabelRefVisitKind: { + const LabelDecl *LS = cast<LabelRefVisit>(&LI)->get(); + if (LabelStmt *stmt = LS->getStmt()) { + if (Visit(MakeCursorLabelRef(stmt, cast<LabelRefVisit>(&LI)->getLoc(), + TU))) { + return true; + } + } + continue; + } + + case VisitorJob::NestedNameSpecifierLocVisitKind: { + NestedNameSpecifierLocVisit *V = cast<NestedNameSpecifierLocVisit>(&LI); + if (VisitNestedNameSpecifierLoc(V->get())) + return true; + continue; + } + + case VisitorJob::DeclarationNameInfoVisitKind: { + if (VisitDeclarationNameInfo(cast<DeclarationNameInfoVisit>(&LI) + ->get())) + return true; + continue; + } + case VisitorJob::MemberRefVisitKind: { + MemberRefVisit *V = cast<MemberRefVisit>(&LI); + if (Visit(MakeCursorMemberRef(V->get(), V->getLoc(), TU))) + return true; + continue; + } + case VisitorJob::StmtVisitKind: { + const Stmt *S = cast<StmtVisit>(&LI)->get(); + if (!S) + continue; + + // Update the current cursor. + CXCursor Cursor = MakeCXCursor(S, StmtParent, TU, RegionOfInterest); + if (!IsInRegionOfInterest(Cursor)) + continue; + switch (Visitor(Cursor, Parent, ClientData)) { + case CXChildVisit_Break: return true; + case CXChildVisit_Continue: break; + case CXChildVisit_Recurse: + if (PostChildrenVisitor) + WL.push_back(PostChildrenVisit(nullptr, Cursor)); + EnqueueWorkList(WL, S); + break; + } + continue; + } + case VisitorJob::MemberExprPartsKind: { + // Handle the other pieces in the MemberExpr besides the base. + const MemberExpr *M = cast<MemberExprParts>(&LI)->get(); + + // Visit the nested-name-specifier + if (NestedNameSpecifierLoc QualifierLoc = M->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + + // Visit the declaration name. + if (VisitDeclarationNameInfo(M->getMemberNameInfo())) + return true; + + // Visit the explicitly-specified template arguments, if any. + if (M->hasExplicitTemplateArgs()) { + for (const TemplateArgumentLoc *Arg = M->getTemplateArgs(), + *ArgEnd = Arg + M->getNumTemplateArgs(); + Arg != ArgEnd; ++Arg) { + if (VisitTemplateArgumentLoc(*Arg)) + return true; + } + } + continue; + } + case VisitorJob::DeclRefExprPartsKind: { + const DeclRefExpr *DR = cast<DeclRefExprParts>(&LI)->get(); + // Visit nested-name-specifier, if present. + if (NestedNameSpecifierLoc QualifierLoc = DR->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + // Visit declaration name. + if (VisitDeclarationNameInfo(DR->getNameInfo())) + return true; + continue; + } + case VisitorJob::OverloadExprPartsKind: { + const OverloadExpr *O = cast<OverloadExprParts>(&LI)->get(); + // Visit the nested-name-specifier. + if (NestedNameSpecifierLoc QualifierLoc = O->getQualifierLoc()) + if (VisitNestedNameSpecifierLoc(QualifierLoc)) + return true; + // Visit the declaration name. + if (VisitDeclarationNameInfo(O->getNameInfo())) + return true; + // Visit the overloaded declaration reference. + if (Visit(MakeCursorOverloadedDeclRef(O, TU))) + return true; + continue; + } + case VisitorJob::SizeOfPackExprPartsKind: { + const SizeOfPackExpr *E = cast<SizeOfPackExprParts>(&LI)->get(); + NamedDecl *Pack = E->getPack(); + if (isa<TemplateTypeParmDecl>(Pack)) { + if (Visit(MakeCursorTypeRef(cast<TemplateTypeParmDecl>(Pack), + E->getPackLoc(), TU))) + return true; + + continue; + } + + if (isa<TemplateTemplateParmDecl>(Pack)) { + if (Visit(MakeCursorTemplateRef(cast<TemplateTemplateParmDecl>(Pack), + E->getPackLoc(), TU))) + return true; + + continue; + } + + // Non-type template parameter packs and function parameter packs are + // treated like DeclRefExpr cursors. + continue; + } + + case VisitorJob::LambdaExprPartsKind: { + // Visit non-init captures. + const LambdaExpr *E = cast<LambdaExprParts>(&LI)->get(); + for (LambdaExpr::capture_iterator C = E->explicit_capture_begin(), + CEnd = E->explicit_capture_end(); + C != CEnd; ++C) { + if (!C->capturesVariable()) + continue; + + if (Visit(MakeCursorVariableRef(C->getCapturedVar(), + C->getLocation(), + TU))) + return true; + } + // Visit init captures + for (auto InitExpr : E->capture_inits()) { + if (Visit(InitExpr)) + return true; + } + + TypeLoc TL = E->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); + // Visit parameters and return type, if present. + if (FunctionTypeLoc Proto = TL.getAs<FunctionProtoTypeLoc>()) { + if (E->hasExplicitParameters()) { + // Visit parameters. + for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I) + if (Visit(MakeCXCursor(Proto.getParam(I), TU))) + return true; + } + if (E->hasExplicitResultType()) { + // Visit result type. + if (Visit(Proto.getReturnLoc())) + return true; + } + } + break; + } + + case VisitorJob::PostChildrenVisitKind: + if (PostChildrenVisitor(Parent, ClientData)) + return true; + break; + } + } + return false; +} + +bool CursorVisitor::Visit(const Stmt *S) { + VisitorWorkList *WL = nullptr; + if (!WorkListFreeList.empty()) { + WL = WorkListFreeList.back(); + WL->clear(); + WorkListFreeList.pop_back(); + } + else { + WL = new VisitorWorkList(); + WorkListCache.push_back(WL); + } + EnqueueWorkList(*WL, S); + bool result = RunVisitorWorkList(*WL); + WorkListFreeList.push_back(WL); + return result; +} + +namespace { +typedef SmallVector<SourceRange, 4> RefNamePieces; +RefNamePieces buildPieces(unsigned NameFlags, bool IsMemberRefExpr, + const DeclarationNameInfo &NI, SourceRange QLoc, + const SourceRange *TemplateArgsLoc = nullptr) { + const bool WantQualifier = NameFlags & CXNameRange_WantQualifier; + const bool WantTemplateArgs = NameFlags & CXNameRange_WantTemplateArgs; + const bool WantSinglePiece = NameFlags & CXNameRange_WantSinglePiece; + + const DeclarationName::NameKind Kind = NI.getName().getNameKind(); + + RefNamePieces Pieces; + + if (WantQualifier && QLoc.isValid()) + Pieces.push_back(QLoc); + + if (Kind != DeclarationName::CXXOperatorName || IsMemberRefExpr) + Pieces.push_back(NI.getLoc()); + + if (WantTemplateArgs && TemplateArgsLoc && TemplateArgsLoc->isValid()) + Pieces.push_back(*TemplateArgsLoc); + + if (Kind == DeclarationName::CXXOperatorName) { + Pieces.push_back(SourceLocation::getFromRawEncoding( + NI.getInfo().CXXOperatorName.BeginOpNameLoc)); + Pieces.push_back(SourceLocation::getFromRawEncoding( + NI.getInfo().CXXOperatorName.EndOpNameLoc)); + } + + if (WantSinglePiece) { + SourceRange R(Pieces.front().getBegin(), Pieces.back().getEnd()); + Pieces.clear(); + Pieces.push_back(R); + } + + return Pieces; +} +} + +//===----------------------------------------------------------------------===// +// Misc. API hooks. +//===----------------------------------------------------------------------===// + +namespace { +struct RegisterFatalErrorHandler { + RegisterFatalErrorHandler() { + clang_install_aborting_llvm_fatal_error_handler(); + } +}; +} + +static llvm::ManagedStatic<RegisterFatalErrorHandler> RegisterFatalErrorHandlerOnce; + +CXIndex clang_createIndex(int excludeDeclarationsFromPCH, + int displayDiagnostics) { + // We use crash recovery to make some of our APIs more reliable, implicitly + // enable it. + if (!getenv("LIBCLANG_DISABLE_CRASH_RECOVERY")) + llvm::CrashRecoveryContext::Enable(); + + // Look through the managed static to trigger construction of the managed + // static which registers our fatal error handler. This ensures it is only + // registered once. + (void)*RegisterFatalErrorHandlerOnce; + + // Initialize targets for clang module support. + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllAsmParsers(); + + CIndexer *CIdxr = new CIndexer(); + + if (excludeDeclarationsFromPCH) + CIdxr->setOnlyLocalDecls(); + if (displayDiagnostics) + CIdxr->setDisplayDiagnostics(); + + if (getenv("LIBCLANG_BGPRIO_INDEX")) + CIdxr->setCXGlobalOptFlags(CIdxr->getCXGlobalOptFlags() | + CXGlobalOpt_ThreadBackgroundPriorityForIndexing); + if (getenv("LIBCLANG_BGPRIO_EDIT")) + CIdxr->setCXGlobalOptFlags(CIdxr->getCXGlobalOptFlags() | + CXGlobalOpt_ThreadBackgroundPriorityForEditing); + + return CIdxr; +} + +void clang_disposeIndex(CXIndex CIdx) { + if (CIdx) + delete static_cast<CIndexer *>(CIdx); +} + +void clang_CXIndex_setGlobalOptions(CXIndex CIdx, unsigned options) { + if (CIdx) + static_cast<CIndexer *>(CIdx)->setCXGlobalOptFlags(options); +} + +unsigned clang_CXIndex_getGlobalOptions(CXIndex CIdx) { + if (CIdx) + return static_cast<CIndexer *>(CIdx)->getCXGlobalOptFlags(); + return 0; +} + +void clang_CXIndex_setInvocationEmissionPathOption(CXIndex CIdx, + const char *Path) { + if (CIdx) + static_cast<CIndexer *>(CIdx)->setInvocationEmissionPath(Path ? Path : ""); +} + +void clang_toggleCrashRecovery(unsigned isEnabled) { + if (isEnabled) + llvm::CrashRecoveryContext::Enable(); + else + llvm::CrashRecoveryContext::Disable(); +} + +CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, + const char *ast_filename) { + CXTranslationUnit TU; + enum CXErrorCode Result = + clang_createTranslationUnit2(CIdx, ast_filename, &TU); + (void)Result; + assert((TU && Result == CXError_Success) || + (!TU && Result != CXError_Success)); + return TU; +} + +enum CXErrorCode clang_createTranslationUnit2(CXIndex CIdx, + const char *ast_filename, + CXTranslationUnit *out_TU) { + if (out_TU) + *out_TU = nullptr; + + if (!CIdx || !ast_filename || !out_TU) + return CXError_InvalidArguments; + + LOG_FUNC_SECTION { + *Log << ast_filename; + } + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + FileSystemOptions FileSystemOpts; + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags = + CompilerInstance::createDiagnostics(new DiagnosticOptions()); + std::unique_ptr<ASTUnit> AU = ASTUnit::LoadFromASTFile( + ast_filename, CXXIdx->getPCHContainerOperations()->getRawReader(), + ASTUnit::LoadEverything, Diags, + FileSystemOpts, /*UseDebugInfo=*/false, + CXXIdx->getOnlyLocalDecls(), None, + CaptureDiagsKind::All, + /*AllowPCHWithCompilerErrors=*/true, + /*UserFilesAreVolatile=*/true); + *out_TU = MakeCXTranslationUnit(CXXIdx, std::move(AU)); + return *out_TU ? CXError_Success : CXError_Failure; +} + +unsigned clang_defaultEditingTranslationUnitOptions() { + return CXTranslationUnit_PrecompiledPreamble | + CXTranslationUnit_CacheCompletionResults; +} + +CXTranslationUnit +clang_createTranslationUnitFromSourceFile(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char * const *command_line_args, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files) { + unsigned Options = CXTranslationUnit_DetailedPreprocessingRecord; + return clang_parseTranslationUnit(CIdx, source_filename, + command_line_args, num_command_line_args, + unsaved_files, num_unsaved_files, + Options); +} + +static CXErrorCode +clang_parseTranslationUnit_Impl(CXIndex CIdx, const char *source_filename, + const char *const *command_line_args, + int num_command_line_args, + ArrayRef<CXUnsavedFile> unsaved_files, + unsigned options, CXTranslationUnit *out_TU) { + // Set up the initial return values. + if (out_TU) + *out_TU = nullptr; + + // Check arguments. + if (!CIdx || !out_TU) + return CXError_InvalidArguments; + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + bool PrecompilePreamble = options & CXTranslationUnit_PrecompiledPreamble; + bool CreatePreambleOnFirstParse = + options & CXTranslationUnit_CreatePreambleOnFirstParse; + // FIXME: Add a flag for modules. + TranslationUnitKind TUKind + = (options & (CXTranslationUnit_Incomplete | + CXTranslationUnit_SingleFileParse))? TU_Prefix : TU_Complete; + bool CacheCodeCompletionResults + = options & CXTranslationUnit_CacheCompletionResults; + bool IncludeBriefCommentsInCodeCompletion + = options & CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; + bool SingleFileParse = options & CXTranslationUnit_SingleFileParse; + bool ForSerialization = options & CXTranslationUnit_ForSerialization; + bool RetainExcludedCB = options & + CXTranslationUnit_RetainExcludedConditionalBlocks; + SkipFunctionBodiesScope SkipFunctionBodies = SkipFunctionBodiesScope::None; + if (options & CXTranslationUnit_SkipFunctionBodies) { + SkipFunctionBodies = + (options & CXTranslationUnit_LimitSkipFunctionBodiesToPreamble) + ? SkipFunctionBodiesScope::Preamble + : SkipFunctionBodiesScope::PreambleAndMainFile; + } + + // Configure the diagnostics. + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions)); + + if (options & CXTranslationUnit_KeepGoing) + Diags->setFatalsAsError(true); + + CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::All; + if (options & CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles) + CaptureDiagnostics = CaptureDiagsKind::AllWithoutNonErrorsFromIncludes; + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.get()); + + std::unique_ptr<std::vector<ASTUnit::RemappedFile>> RemappedFiles( + new std::vector<ASTUnit::RemappedFile>()); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar< + std::vector<ASTUnit::RemappedFile> > RemappedCleanup(RemappedFiles.get()); + + for (auto &UF : unsaved_files) { + std::unique_ptr<llvm::MemoryBuffer> MB = + llvm::MemoryBuffer::getMemBufferCopy(getContents(UF), UF.Filename); + RemappedFiles->push_back(std::make_pair(UF.Filename, MB.release())); + } + + std::unique_ptr<std::vector<const char *>> Args( + new std::vector<const char *>()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<std::vector<const char*> > + ArgsCleanup(Args.get()); + + // Since the Clang C library is primarily used by batch tools dealing with + // (often very broken) source code, where spell-checking can have a + // significant negative impact on performance (particularly when + // precompiled headers are involved), we disable it by default. + // Only do this if we haven't found a spell-checking-related argument. + bool FoundSpellCheckingArgument = false; + for (int I = 0; I != num_command_line_args; ++I) { + if (strcmp(command_line_args[I], "-fno-spell-checking") == 0 || + strcmp(command_line_args[I], "-fspell-checking") == 0) { + FoundSpellCheckingArgument = true; + break; + } + } + Args->insert(Args->end(), command_line_args, + command_line_args + num_command_line_args); + + if (!FoundSpellCheckingArgument) + Args->insert(Args->begin() + 1, "-fno-spell-checking"); + + // The 'source_filename' argument is optional. If the caller does not + // specify it then it is assumed that the source file is specified + // in the actual argument list. + // Put the source file after command_line_args otherwise if '-x' flag is + // present it will be unused. + if (source_filename) + Args->push_back(source_filename); + + // Do we need the detailed preprocessing record? + if (options & CXTranslationUnit_DetailedPreprocessingRecord) { + Args->push_back("-Xclang"); + Args->push_back("-detailed-preprocessing-record"); + } + + // Suppress any editor placeholder diagnostics. + Args->push_back("-fallow-editor-placeholders"); + + unsigned NumErrors = Diags->getClient()->getNumErrors(); + std::unique_ptr<ASTUnit> ErrUnit; + // Unless the user specified that they want the preamble on the first parse + // set it up to be created on the first reparse. This makes the first parse + // faster, trading for a slower (first) reparse. + unsigned PrecompilePreambleAfterNParses = + !PrecompilePreamble ? 0 : 2 - CreatePreambleOnFirstParse; + + LibclangInvocationReporter InvocationReporter( + *CXXIdx, LibclangInvocationReporter::OperationKind::ParseOperation, + options, llvm::makeArrayRef(*Args), /*InvocationArgs=*/None, + unsaved_files); + std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCommandLine( + Args->data(), Args->data() + Args->size(), + CXXIdx->getPCHContainerOperations(), Diags, + CXXIdx->getClangResourcesPath(), CXXIdx->getOnlyLocalDecls(), + CaptureDiagnostics, *RemappedFiles.get(), + /*RemappedFilesKeepOriginalName=*/true, PrecompilePreambleAfterNParses, + TUKind, CacheCodeCompletionResults, IncludeBriefCommentsInCodeCompletion, + /*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodies, SingleFileParse, + /*UserFilesAreVolatile=*/true, ForSerialization, RetainExcludedCB, + CXXIdx->getPCHContainerOperations()->getRawReader().getFormat(), + &ErrUnit)); + + // Early failures in LoadFromCommandLine may return with ErrUnit unset. + if (!Unit && !ErrUnit) + return CXError_ASTReadError; + + if (NumErrors != Diags->getClient()->getNumErrors()) { + // Make sure to check that 'Unit' is non-NULL. + if (CXXIdx->getDisplayDiagnostics()) + printDiagsToStderr(Unit ? Unit.get() : ErrUnit.get()); + } + + if (isASTReadError(Unit ? Unit.get() : ErrUnit.get())) + return CXError_ASTReadError; + + *out_TU = MakeCXTranslationUnit(CXXIdx, std::move(Unit)); + if (CXTranslationUnitImpl *TU = *out_TU) { + TU->ParsingOptions = options; + TU->Arguments.reserve(Args->size()); + for (const char *Arg : *Args) + TU->Arguments.push_back(Arg); + return CXError_Success; + } + return CXError_Failure; +} + +CXTranslationUnit +clang_parseTranslationUnit(CXIndex CIdx, + const char *source_filename, + const char *const *command_line_args, + int num_command_line_args, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + unsigned options) { + CXTranslationUnit TU; + enum CXErrorCode Result = clang_parseTranslationUnit2( + CIdx, source_filename, command_line_args, num_command_line_args, + unsaved_files, num_unsaved_files, options, &TU); + (void)Result; + assert((TU && Result == CXError_Success) || + (!TU && Result != CXError_Success)); + return TU; +} + +enum CXErrorCode clang_parseTranslationUnit2( + CXIndex CIdx, const char *source_filename, + const char *const *command_line_args, int num_command_line_args, + struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files, + unsigned options, CXTranslationUnit *out_TU) { + noteBottomOfStack(); + SmallVector<const char *, 4> Args; + Args.push_back("clang"); + Args.append(command_line_args, command_line_args + num_command_line_args); + return clang_parseTranslationUnit2FullArgv( + CIdx, source_filename, Args.data(), Args.size(), unsaved_files, + num_unsaved_files, options, out_TU); +} + +enum CXErrorCode clang_parseTranslationUnit2FullArgv( + CXIndex CIdx, const char *source_filename, + const char *const *command_line_args, int num_command_line_args, + struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files, + unsigned options, CXTranslationUnit *out_TU) { + LOG_FUNC_SECTION { + *Log << source_filename << ": "; + for (int i = 0; i != num_command_line_args; ++i) + *Log << command_line_args[i] << " "; + } + + if (num_unsaved_files && !unsaved_files) + return CXError_InvalidArguments; + + CXErrorCode result = CXError_Failure; + auto ParseTranslationUnitImpl = [=, &result] { + noteBottomOfStack(); + result = clang_parseTranslationUnit_Impl( + CIdx, source_filename, command_line_args, num_command_line_args, + llvm::makeArrayRef(unsaved_files, num_unsaved_files), options, out_TU); + }; + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, ParseTranslationUnitImpl)) { + fprintf(stderr, "libclang: crash detected during parsing: {\n"); + fprintf(stderr, " 'source_filename' : '%s'\n", source_filename); + fprintf(stderr, " 'command_line_args' : ["); + for (int i = 0; i != num_command_line_args; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "'%s'", command_line_args[i]); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'unsaved_files' : ["); + for (unsigned i = 0; i != num_unsaved_files; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "('%s', '...', %ld)", unsaved_files[i].Filename, + unsaved_files[i].Length); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'options' : %d,\n", options); + fprintf(stderr, "}\n"); + + return CXError_Crashed; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) { + if (CXTranslationUnit *TU = out_TU) + PrintLibclangResourceUsage(*TU); + } + + 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 { + unsigned long long unsignedVal; + long long intVal; + double floatVal; + char *stringVal; + } EvalData; + bool IsUnsignedInt; + ~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) { + return clang_EvalResult_getAsLongLong(E); +} + +long long clang_EvalResult_getAsLongLong(CXEvalResult E) { + if (!E) { + return 0; + } + ExprEvalResult *Result = (ExprEvalResult*)E; + if (Result->IsUnsignedInt) + return Result->EvalData.unsignedVal; + return Result->EvalData.intVal; +} + +unsigned clang_EvalResult_isUnsignedInt(CXEvalResult E) { + return ((ExprEvalResult *)E)->IsUnsignedInt; +} + +unsigned long long clang_EvalResult_getAsUnsigned(CXEvalResult E) { + if (!E) { + return 0; + } + + ExprEvalResult *Result = (ExprEvalResult*)E; + if (Result->IsUnsignedInt) + return Result->EvalData.unsignedVal; + return Result->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->isValueDependent()) + return nullptr; + if (!expr->EvaluateAsRValue(ER, ctx)) + return nullptr; + + QualType rettype; + CallExpr *callExpr; + auto result = std::make_unique<ExprEvalResult>(); + result->EvalType = CXEval_UnExposed; + result->IsUnsignedInt = false; + + if (ER.Val.isInt()) { + result->EvalType = CXEval_Int; + + auto& val = ER.Val.getInt(); + if (val.isUnsigned()) { + result->IsUnsignedInt = true; + result->EvalData.unsignedVal = val.getZExtValue(); + } else { + result->EvalData.intVal = val.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; +} + +static const Expr *evaluateDeclExpr(const Decl *D) { + if (!D) + return nullptr; + if (auto *Var = dyn_cast<VarDecl>(D)) + return Var->getInit(); + else if (auto *Field = dyn_cast<FieldDecl>(D)) + return Field->getInClassInitializer(); + return nullptr; +} + +static const Expr *evaluateCompoundStmtExpr(const CompoundStmt *CS) { + assert(CS && "invalid compound statement"); + for (auto *bodyIterator : CS->body()) { + if (const auto *E = dyn_cast<Expr>(bodyIterator)) + return E; + } + return nullptr; +} + +CXEvalResult clang_Cursor_Evaluate(CXCursor C) { + if (const Expr *E = + clang_getCursorKind(C) == CXCursor_CompoundStmt + ? evaluateCompoundStmtExpr(cast<CompoundStmt>(getCursorStmt(C))) + : evaluateDeclExpr(getCursorDecl(C))) + return const_cast<CXEvalResult>( + reinterpret_cast<const void *>(evaluateExpr(const_cast<Expr *>(E), 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; +} + +static CXSaveError clang_saveTranslationUnit_Impl(CXTranslationUnit TU, + const char *FileName, + unsigned options) { + CIndexer *CXXIdx = TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + bool hadError = cxtu::getASTUnit(TU)->Save(FileName); + return hadError ? CXSaveError_Unknown : CXSaveError_None; +} + +int clang_saveTranslationUnit(CXTranslationUnit TU, const char *FileName, + unsigned options) { + LOG_FUNC_SECTION { + *Log << TU << ' ' << FileName; + } + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXSaveError_InvalidTU; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + if (!CXXUnit->hasSema()) + return CXSaveError_InvalidTU; + + CXSaveError result; + auto SaveTranslationUnitImpl = [=, &result]() { + result = clang_saveTranslationUnit_Impl(TU, FileName, options); + }; + + if (!CXXUnit->getDiagnostics().hasUnrecoverableErrorOccurred()) { + SaveTranslationUnitImpl(); + + if (getenv("LIBCLANG_RESOURCE_USAGE")) + PrintLibclangResourceUsage(TU); + + return result; + } + + // We have an AST that has invalid nodes due to compiler errors. + // Use a crash recovery thread for protection. + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, SaveTranslationUnitImpl)) { + fprintf(stderr, "libclang: crash detected during AST saving: {\n"); + fprintf(stderr, " 'filename' : '%s'\n", FileName); + fprintf(stderr, " 'options' : %d,\n", options); + fprintf(stderr, "}\n"); + + return CXSaveError_Unknown; + + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) { + PrintLibclangResourceUsage(TU); + } + + return result; +} + +void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { + if (CTUnit) { + // If the translation unit has been marked as unsafe to free, just discard + // it. + ASTUnit *Unit = cxtu::getASTUnit(CTUnit); + if (Unit && Unit->isUnsafeToFree()) + return; + + delete cxtu::getASTUnit(CTUnit); + delete CTUnit->StringPool; + delete static_cast<CXDiagnosticSetImpl *>(CTUnit->Diagnostics); + disposeOverridenCXCursorsPool(CTUnit->OverridenCursorsPool); + delete CTUnit->CommentToXML; + delete CTUnit; + } +} + +unsigned clang_suspendTranslationUnit(CXTranslationUnit CTUnit) { + if (CTUnit) { + ASTUnit *Unit = cxtu::getASTUnit(CTUnit); + + if (Unit && Unit->isUnsafeToFree()) + return false; + + Unit->ResetForParse(); + return true; + } + + return false; +} + +unsigned clang_defaultReparseOptions(CXTranslationUnit TU) { + return CXReparse_None; +} + +static CXErrorCode +clang_reparseTranslationUnit_Impl(CXTranslationUnit TU, + ArrayRef<CXUnsavedFile> unsaved_files, + unsigned options) { + // Check arguments. + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXError_InvalidArguments; + } + + // Reset the associated diagnostics. + delete static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); + TU->Diagnostics = nullptr; + + CIndexer *CXXIdx = TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) + setThreadBackgroundPriority(); + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + std::unique_ptr<std::vector<ASTUnit::RemappedFile>> RemappedFiles( + new std::vector<ASTUnit::RemappedFile>()); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar< + std::vector<ASTUnit::RemappedFile> > RemappedCleanup(RemappedFiles.get()); + + for (auto &UF : unsaved_files) { + std::unique_ptr<llvm::MemoryBuffer> MB = + llvm::MemoryBuffer::getMemBufferCopy(getContents(UF), UF.Filename); + RemappedFiles->push_back(std::make_pair(UF.Filename, MB.release())); + } + + if (!CXXUnit->Reparse(CXXIdx->getPCHContainerOperations(), + *RemappedFiles.get())) + return CXError_Success; + if (isASTReadError(CXXUnit)) + return CXError_ASTReadError; + return CXError_Failure; +} + +int clang_reparseTranslationUnit(CXTranslationUnit TU, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + unsigned options) { + LOG_FUNC_SECTION { + *Log << TU; + } + + if (num_unsaved_files && !unsaved_files) + return CXError_InvalidArguments; + + CXErrorCode result; + auto ReparseTranslationUnitImpl = [=, &result]() { + result = clang_reparseTranslationUnit_Impl( + TU, llvm::makeArrayRef(unsaved_files, num_unsaved_files), options); + }; + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, ReparseTranslationUnitImpl)) { + fprintf(stderr, "libclang: crash detected during reparsing\n"); + cxtu::getASTUnit(TU)->setUnsafeToFree(true); + return CXError_Crashed; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) + PrintLibclangResourceUsage(TU); + + return result; +} + + +CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) { + if (isNotUsableTU(CTUnit)) { + LOG_BAD_TU(CTUnit); + return cxstring::createEmpty(); + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(CTUnit); + return cxstring::createDup(CXXUnit->getOriginalSourceFileName()); +} + +CXCursor clang_getTranslationUnitCursor(CXTranslationUnit TU) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return clang_getNullCursor(); + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + return MakeCXCursor(CXXUnit->getASTContext().getTranslationUnitDecl(), TU); +} + +CXTargetInfo clang_getTranslationUnitTargetInfo(CXTranslationUnit CTUnit) { + if (isNotUsableTU(CTUnit)) { + LOG_BAD_TU(CTUnit); + return nullptr; + } + + CXTargetInfoImpl* impl = new CXTargetInfoImpl(); + impl->TranslationUnit = CTUnit; + return impl; +} + +CXString clang_TargetInfo_getTriple(CXTargetInfo TargetInfo) { + if (!TargetInfo) + return cxstring::createEmpty(); + + CXTranslationUnit CTUnit = TargetInfo->TranslationUnit; + assert(!isNotUsableTU(CTUnit) && + "Unexpected unusable translation unit in TargetInfo"); + + ASTUnit *CXXUnit = cxtu::getASTUnit(CTUnit); + std::string Triple = + CXXUnit->getASTContext().getTargetInfo().getTriple().normalize(); + return cxstring::createDup(Triple); +} + +int clang_TargetInfo_getPointerWidth(CXTargetInfo TargetInfo) { + if (!TargetInfo) + return -1; + + CXTranslationUnit CTUnit = TargetInfo->TranslationUnit; + assert(!isNotUsableTU(CTUnit) && + "Unexpected unusable translation unit in TargetInfo"); + + ASTUnit *CXXUnit = cxtu::getASTUnit(CTUnit); + return CXXUnit->getASTContext().getTargetInfo().getMaxPointerWidth(); +} + +void clang_TargetInfo_dispose(CXTargetInfo TargetInfo) { + if (!TargetInfo) + return; + + delete TargetInfo; +} + +//===----------------------------------------------------------------------===// +// CXFile Operations. +//===----------------------------------------------------------------------===// + +CXString clang_getFileName(CXFile SFile) { + if (!SFile) + return cxstring::createNull(); + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return cxstring::createRef(FEnt->getName()); +} + +time_t clang_getFileTime(CXFile SFile) { + if (!SFile) + return 0; + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return FEnt->getModificationTime(); +} + +CXFile clang_getFile(CXTranslationUnit TU, const char *file_name) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return nullptr; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + + FileManager &FMgr = CXXUnit->getFileManager(); + auto File = FMgr.getFile(file_name); + if (!File) + return nullptr; + return const_cast<FileEntry *>(*File); +} + +const char *clang_getFileContents(CXTranslationUnit TU, CXFile file, + size_t *size) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return nullptr; + } + + const SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); + FileID fid = SM.translateFile(static_cast<FileEntry *>(file)); + bool Invalid = true; + const llvm::MemoryBuffer *buf = SM.getBuffer(fid, &Invalid); + if (Invalid) { + if (size) + *size = 0; + return nullptr; + } + if (size) + *size = buf->getBufferSize(); + return buf->getBufferStart(); +} + +unsigned clang_isFileMultipleIncludeGuarded(CXTranslationUnit TU, + CXFile file) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return 0; + } + + if (!file) + return 0; + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + FileEntry *FEnt = static_cast<FileEntry *>(file); + return CXXUnit->getPreprocessor().getHeaderSearchInfo() + .isFileMultipleIncludeGuarded(FEnt); +} + +int clang_getFileUniqueID(CXFile file, CXFileUniqueID *outID) { + if (!file || !outID) + return 1; + + FileEntry *FEnt = static_cast<FileEntry *>(file); + const llvm::sys::fs::UniqueID &ID = FEnt->getUniqueID(); + outID->data[0] = ID.getDevice(); + outID->data[1] = ID.getFile(); + outID->data[2] = FEnt->getModificationTime(); + return 0; +} + +int clang_File_isEqual(CXFile file1, CXFile file2) { + if (file1 == file2) + return true; + + if (!file1 || !file2) + return false; + + FileEntry *FEnt1 = static_cast<FileEntry *>(file1); + FileEntry *FEnt2 = static_cast<FileEntry *>(file2); + return FEnt1->getUniqueID() == FEnt2->getUniqueID(); +} + +CXString clang_File_tryGetRealPathName(CXFile SFile) { + if (!SFile) + return cxstring::createNull(); + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return cxstring::createRef(FEnt->tryGetRealPathName()); +} + +//===----------------------------------------------------------------------===// +// CXCursor Operations. +//===----------------------------------------------------------------------===// + +static const Decl *getDeclFromExpr(const Stmt *E) { + if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) + return getDeclFromExpr(CE->getSubExpr()); + + if (const DeclRefExpr *RefExpr = dyn_cast<DeclRefExpr>(E)) + return RefExpr->getDecl(); + if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + if (const ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(E)) + return RE->getDecl(); + if (const ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { + if (PRE->isExplicitProperty()) + return PRE->getExplicitProperty(); + // It could be messaging both getter and setter as in: + // ++myobj.myprop; + // in which case prefer to associate the setter since it is less obvious + // from inspecting the source that the setter is going to get called. + if (PRE->isMessagingSetter()) + return PRE->getImplicitPropertySetter(); + return PRE->getImplicitPropertyGetter(); + } + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) + return getDeclFromExpr(POE->getSyntacticForm()); + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) + if (Expr *Src = OVE->getSourceExpr()) + return getDeclFromExpr(Src); + + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) + return getDeclFromExpr(CE->getCallee()); + 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(); + + if (const ObjCProtocolExpr *PE = dyn_cast<ObjCProtocolExpr>(E)) + return PE->getProtocol(); + if (const SubstNonTypeTemplateParmPackExpr *NTTP + = dyn_cast<SubstNonTypeTemplateParmPackExpr>(E)) + return NTTP->getParameterPack(); + if (const SizeOfPackExpr *SizeOfPack = dyn_cast<SizeOfPackExpr>(E)) + if (isa<NonTypeTemplateParmDecl>(SizeOfPack->getPack()) || + isa<ParmVarDecl>(SizeOfPack->getPack())) + return SizeOfPack->getPack(); + + return nullptr; +} + +static SourceLocation getLocationFromExpr(const Expr *E) { + if (const ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(E)) + return getLocationFromExpr(CE->getSubExpr()); + + if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) + return /*FIXME:*/Msg->getLeftLoc(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getLocation(); + if (const MemberExpr *Member = dyn_cast<MemberExpr>(E)) + return Member->getMemberLoc(); + if (const ObjCIvarRefExpr *Ivar = dyn_cast<ObjCIvarRefExpr>(E)) + return Ivar->getLocation(); + if (const SizeOfPackExpr *SizeOfPack = dyn_cast<SizeOfPackExpr>(E)) + return SizeOfPack->getPackLoc(); + if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) + return PropRef->getLocation(); + + return E->getBeginLoc(); +} + +extern "C" { + +unsigned clang_visitChildren(CXCursor parent, + CXCursorVisitor visitor, + CXClientData client_data) { + CursorVisitor CursorVis(getCursorTU(parent), visitor, client_data, + /*VisitPreprocessorLast=*/false); + return CursorVis.VisitChildren(parent); +} + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif +#if __has_feature(blocks) +typedef enum CXChildVisitResult + (^CXCursorVisitorBlock)(CXCursor cursor, CXCursor parent); + +static enum CXChildVisitResult visitWithBlock(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + CXCursorVisitorBlock block = (CXCursorVisitorBlock)client_data; + return block(cursor, parent); +} +#else +// If we are compiled with a compiler that doesn't have native blocks support, +// define and call the block manually, so the +typedef struct _CXChildVisitResult +{ + void *isa; + int flags; + int reserved; + enum CXChildVisitResult(*invoke)(struct _CXChildVisitResult*, CXCursor, + CXCursor); +} *CXCursorVisitorBlock; + +static enum CXChildVisitResult visitWithBlock(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + CXCursorVisitorBlock block = (CXCursorVisitorBlock)client_data; + return block->invoke(block, cursor, parent); +} +#endif + + +unsigned clang_visitChildrenWithBlock(CXCursor parent, + CXCursorVisitorBlock block) { + return clang_visitChildren(parent, visitWithBlock, block); +} + +static CXString getDeclSpelling(const Decl *D) { + if (!D) + return cxstring::createEmpty(); + + const NamedDecl *ND = dyn_cast<NamedDecl>(D); + if (!ND) { + if (const ObjCPropertyImplDecl *PropImpl = + dyn_cast<ObjCPropertyImplDecl>(D)) + if (ObjCPropertyDecl *Property = PropImpl->getPropertyDecl()) + return cxstring::createDup(Property->getIdentifier()->getName()); + + if (const ImportDecl *ImportD = dyn_cast<ImportDecl>(D)) + if (Module *Mod = ImportD->getImportedModule()) + return cxstring::createDup(Mod->getFullModuleName()); + + return cxstring::createEmpty(); + } + + if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(ND)) + return cxstring::createDup(OMD->getSelector().getAsString()); + + if (const ObjCCategoryImplDecl *CIMP = dyn_cast<ObjCCategoryImplDecl>(ND)) + // No, this isn't the same as the code below. getIdentifier() is non-virtual + // and returns different names. NamedDecl returns the class name and + // ObjCCategoryImplDecl returns the category name. + return cxstring::createRef(CIMP->getIdentifier()->getNameStart()); + + if (isa<UsingDirectiveDecl>(D)) + return cxstring::createEmpty(); + + SmallString<1024> S; + llvm::raw_svector_ostream os(S); + ND->printName(os); + + return cxstring::createDup(os.str()); +} + +CXString clang_getCursorSpelling(CXCursor C) { + if (clang_isTranslationUnit(C.kind)) + return clang_getTranslationUnitSpelling(getCursorTU(C)); + + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + const ObjCInterfaceDecl *Super = getCursorObjCSuperClassRef(C).first; + return cxstring::createRef(Super->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCClassRef: { + const ObjCInterfaceDecl *Class = getCursorObjCClassRef(C).first; + return cxstring::createRef(Class->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCProtocolRef: { + const ObjCProtocolDecl *OID = getCursorObjCProtocolRef(C).first; + assert(OID && "getCursorSpelling(): Missing protocol decl"); + return cxstring::createRef(OID->getIdentifier()->getNameStart()); + } + case CXCursor_CXXBaseSpecifier: { + const CXXBaseSpecifier *B = getCursorCXXBaseSpecifier(C); + return cxstring::createDup(B->getType().getAsString()); + } + case CXCursor_TypeRef: { + const TypeDecl *Type = getCursorTypeRef(C).first; + assert(Type && "Missing type decl"); + + return cxstring::createDup(getCursorContext(C).getTypeDeclType(Type). + getAsString()); + } + case CXCursor_TemplateRef: { + const TemplateDecl *Template = getCursorTemplateRef(C).first; + assert(Template && "Missing template decl"); + + return cxstring::createDup(Template->getNameAsString()); + } + + case CXCursor_NamespaceRef: { + const NamedDecl *NS = getCursorNamespaceRef(C).first; + assert(NS && "Missing namespace decl"); + + return cxstring::createDup(NS->getNameAsString()); + } + + case CXCursor_MemberRef: { + const FieldDecl *Field = getCursorMemberRef(C).first; + assert(Field && "Missing member decl"); + + return cxstring::createDup(Field->getNameAsString()); + } + + case CXCursor_LabelRef: { + const LabelStmt *Label = getCursorLabelRef(C).first; + assert(Label && "Missing label"); + + return cxstring::createRef(Label->getName()); + } + + case CXCursor_OverloadedDeclRef: { + OverloadedDeclRefStorage Storage = getCursorOverloadedDeclRef(C).first; + if (const Decl *D = Storage.dyn_cast<const Decl *>()) { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + return cxstring::createDup(ND->getNameAsString()); + return cxstring::createEmpty(); + } + if (const OverloadExpr *E = Storage.dyn_cast<const OverloadExpr *>()) + return cxstring::createDup(E->getName().getAsString()); + OverloadedTemplateStorage *Ovl + = Storage.get<OverloadedTemplateStorage*>(); + if (Ovl->size() == 0) + return cxstring::createEmpty(); + return cxstring::createDup((*Ovl->begin())->getNameAsString()); + } + + case CXCursor_VariableRef: { + const VarDecl *Var = getCursorVariableRef(C).first; + assert(Var && "Missing variable decl"); + + return cxstring::createDup(Var->getNameAsString()); + } + + default: + return cxstring::createRef("<not implemented>"); + } + } + + if (clang_isExpression(C.kind)) { + const Expr *E = getCursorExpr(C); + + if (C.kind == CXCursor_ObjCStringLiteral || + C.kind == CXCursor_StringLiteral) { + const StringLiteral *SLit; + if (const ObjCStringLiteral *OSL = dyn_cast<ObjCStringLiteral>(E)) { + SLit = OSL->getString(); + } else { + SLit = cast<StringLiteral>(E); + } + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + SLit->outputString(OS); + return cxstring::createDup(OS.str()); + } + + const Decl *D = getDeclFromExpr(getCursorExpr(C)); + if (D) + return getDeclSpelling(D); + return cxstring::createEmpty(); + } + + if (clang_isStatement(C.kind)) { + const Stmt *S = getCursorStmt(C); + if (const LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S)) + return cxstring::createRef(Label->getName()); + + return cxstring::createEmpty(); + } + + if (C.kind == CXCursor_MacroExpansion) + return cxstring::createRef(getCursorMacroExpansion(C).getName() + ->getNameStart()); + + if (C.kind == CXCursor_MacroDefinition) + return cxstring::createRef(getCursorMacroDefinition(C)->getName() + ->getNameStart()); + + if (C.kind == CXCursor_InclusionDirective) + return cxstring::createDup(getCursorInclusionDirective(C)->getFileName()); + + if (clang_isDeclaration(C.kind)) + return getDeclSpelling(getCursorDecl(C)); + + if (C.kind == CXCursor_AnnotateAttr) { + const AnnotateAttr *AA = cast<AnnotateAttr>(cxcursor::getCursorAttr(C)); + return cxstring::createDup(AA->getAnnotation()); + } + + if (C.kind == CXCursor_AsmLabelAttr) { + const AsmLabelAttr *AA = cast<AsmLabelAttr>(cxcursor::getCursorAttr(C)); + return cxstring::createDup(AA->getLabel()); + } + + if (C.kind == CXCursor_PackedAttr) { + return cxstring::createRef("packed"); + } + + if (C.kind == CXCursor_VisibilityAttr) { + const VisibilityAttr *AA = cast<VisibilityAttr>(cxcursor::getCursorAttr(C)); + switch (AA->getVisibility()) { + case VisibilityAttr::VisibilityType::Default: + return cxstring::createRef("default"); + case VisibilityAttr::VisibilityType::Hidden: + return cxstring::createRef("hidden"); + case VisibilityAttr::VisibilityType::Protected: + return cxstring::createRef("protected"); + } + llvm_unreachable("unknown visibility type"); + } + + return cxstring::createEmpty(); +} + +CXSourceRange clang_Cursor_getSpellingNameRange(CXCursor C, + unsigned pieceIndex, + unsigned options) { + if (clang_Cursor_isNull(C)) + return clang_getNullRange(); + + ASTContext &Ctx = getCursorContext(C); + + if (clang_isStatement(C.kind)) { + const Stmt *S = getCursorStmt(C); + if (const LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S)) { + if (pieceIndex > 0) + return clang_getNullRange(); + return cxloc::translateSourceRange(Ctx, Label->getIdentLoc()); + } + + return clang_getNullRange(); + } + + if (C.kind == CXCursor_ObjCMessageExpr) { + if (const ObjCMessageExpr * + ME = dyn_cast_or_null<ObjCMessageExpr>(getCursorExpr(C))) { + if (pieceIndex >= ME->getNumSelectorLocs()) + return clang_getNullRange(); + return cxloc::translateSourceRange(Ctx, ME->getSelectorLoc(pieceIndex)); + } + } + + if (C.kind == CXCursor_ObjCInstanceMethodDecl || + C.kind == CXCursor_ObjCClassMethodDecl) { + if (const ObjCMethodDecl * + MD = dyn_cast_or_null<ObjCMethodDecl>(getCursorDecl(C))) { + if (pieceIndex >= MD->getNumSelectorLocs()) + return clang_getNullRange(); + return cxloc::translateSourceRange(Ctx, MD->getSelectorLoc(pieceIndex)); + } + } + + if (C.kind == CXCursor_ObjCCategoryDecl || + C.kind == CXCursor_ObjCCategoryImplDecl) { + if (pieceIndex > 0) + return clang_getNullRange(); + if (const ObjCCategoryDecl * + CD = dyn_cast_or_null<ObjCCategoryDecl>(getCursorDecl(C))) + return cxloc::translateSourceRange(Ctx, CD->getCategoryNameLoc()); + if (const ObjCCategoryImplDecl * + CID = dyn_cast_or_null<ObjCCategoryImplDecl>(getCursorDecl(C))) + return cxloc::translateSourceRange(Ctx, CID->getCategoryNameLoc()); + } + + if (C.kind == CXCursor_ModuleImportDecl) { + if (pieceIndex > 0) + return clang_getNullRange(); + if (const ImportDecl *ImportD = + dyn_cast_or_null<ImportDecl>(getCursorDecl(C))) { + ArrayRef<SourceLocation> Locs = ImportD->getIdentifierLocs(); + if (!Locs.empty()) + return cxloc::translateSourceRange(Ctx, + SourceRange(Locs.front(), Locs.back())); + } + return clang_getNullRange(); + } + + if (C.kind == CXCursor_CXXMethod || C.kind == CXCursor_Destructor || + C.kind == CXCursor_ConversionFunction || + C.kind == CXCursor_FunctionDecl) { + if (pieceIndex > 0) + return clang_getNullRange(); + if (const FunctionDecl *FD = + dyn_cast_or_null<FunctionDecl>(getCursorDecl(C))) { + DeclarationNameInfo FunctionName = FD->getNameInfo(); + return cxloc::translateSourceRange(Ctx, FunctionName.getSourceRange()); + } + return clang_getNullRange(); + } + + // FIXME: A CXCursor_InclusionDirective should give the location of the + // filename, but we don't keep track of this. + + // FIXME: A CXCursor_AnnotateAttr should give the location of the annotation + // but we don't keep track of this. + + // FIXME: A CXCursor_AsmLabelAttr should give the location of the label + // but we don't keep track of this. + + // Default handling, give the location of the cursor. + + if (pieceIndex > 0) + return clang_getNullRange(); + + CXSourceLocation CXLoc = clang_getCursorLocation(C); + SourceLocation Loc = cxloc::translateSourceLocation(CXLoc); + return cxloc::translateSourceRange(Ctx, Loc); +} + +CXString clang_Cursor_getMangling(CXCursor C) { + if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) + return cxstring::createEmpty(); + + // Mangling only works for functions and variables. + const Decl *D = getCursorDecl(C); + if (!D || !(isa<FunctionDecl>(D) || isa<VarDecl>(D))) + return cxstring::createEmpty(); + + ASTContext &Ctx = D->getASTContext(); + ASTNameGenerator ASTNameGen(Ctx); + return cxstring::createDup(ASTNameGen.getName(D)); +} + +CXStringSet *clang_Cursor_getCXXManglings(CXCursor C) { + if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) + return nullptr; + + const Decl *D = getCursorDecl(C); + if (!(isa<CXXRecordDecl>(D) || isa<CXXMethodDecl>(D))) + return nullptr; + + ASTContext &Ctx = D->getASTContext(); + ASTNameGenerator ASTNameGen(Ctx); + std::vector<std::string> Manglings = ASTNameGen.getAllManglings(D); + return cxstring::createSet(Manglings); +} + +CXStringSet *clang_Cursor_getObjCManglings(CXCursor C) { + if (clang_isInvalid(C.kind) || !clang_isDeclaration(C.kind)) + return nullptr; + + const Decl *D = getCursorDecl(C); + if (!(isa<ObjCInterfaceDecl>(D) || isa<ObjCImplementationDecl>(D))) + return nullptr; + + ASTContext &Ctx = D->getASTContext(); + ASTNameGenerator ASTNameGen(Ctx); + std::vector<std::string> Manglings = ASTNameGen.getAllManglings(D); + return cxstring::createSet(Manglings); +} + +CXPrintingPolicy clang_getCursorPrintingPolicy(CXCursor C) { + if (clang_Cursor_isNull(C)) + return 0; + return new PrintingPolicy(getCursorContext(C).getPrintingPolicy()); +} + +void clang_PrintingPolicy_dispose(CXPrintingPolicy Policy) { + if (Policy) + delete static_cast<PrintingPolicy *>(Policy); +} + +unsigned +clang_PrintingPolicy_getProperty(CXPrintingPolicy Policy, + enum CXPrintingPolicyProperty Property) { + if (!Policy) + return 0; + + PrintingPolicy *P = static_cast<PrintingPolicy *>(Policy); + switch (Property) { + case CXPrintingPolicy_Indentation: + return P->Indentation; + case CXPrintingPolicy_SuppressSpecifiers: + return P->SuppressSpecifiers; + case CXPrintingPolicy_SuppressTagKeyword: + return P->SuppressTagKeyword; + case CXPrintingPolicy_IncludeTagDefinition: + return P->IncludeTagDefinition; + case CXPrintingPolicy_SuppressScope: + return P->SuppressScope; + case CXPrintingPolicy_SuppressUnwrittenScope: + return P->SuppressUnwrittenScope; + case CXPrintingPolicy_SuppressInitializers: + return P->SuppressInitializers; + case CXPrintingPolicy_ConstantArraySizeAsWritten: + return P->ConstantArraySizeAsWritten; + case CXPrintingPolicy_AnonymousTagLocations: + return P->AnonymousTagLocations; + case CXPrintingPolicy_SuppressStrongLifetime: + return P->SuppressStrongLifetime; + case CXPrintingPolicy_SuppressLifetimeQualifiers: + return P->SuppressLifetimeQualifiers; + case CXPrintingPolicy_SuppressTemplateArgsInCXXConstructors: + return P->SuppressTemplateArgsInCXXConstructors; + case CXPrintingPolicy_Bool: + return P->Bool; + case CXPrintingPolicy_Restrict: + return P->Restrict; + case CXPrintingPolicy_Alignof: + return P->Alignof; + case CXPrintingPolicy_UnderscoreAlignof: + return P->UnderscoreAlignof; + case CXPrintingPolicy_UseVoidForZeroParams: + return P->UseVoidForZeroParams; + case CXPrintingPolicy_TerseOutput: + return P->TerseOutput; + case CXPrintingPolicy_PolishForDeclaration: + return P->PolishForDeclaration; + case CXPrintingPolicy_Half: + return P->Half; + case CXPrintingPolicy_MSWChar: + return P->MSWChar; + case CXPrintingPolicy_IncludeNewlines: + return P->IncludeNewlines; + case CXPrintingPolicy_MSVCFormatting: + return P->MSVCFormatting; + case CXPrintingPolicy_ConstantsAsWritten: + return P->ConstantsAsWritten; + case CXPrintingPolicy_SuppressImplicitBase: + return P->SuppressImplicitBase; + case CXPrintingPolicy_FullyQualifiedName: + return P->FullyQualifiedName; + } + + assert(false && "Invalid CXPrintingPolicyProperty"); + return 0; +} + +void clang_PrintingPolicy_setProperty(CXPrintingPolicy Policy, + enum CXPrintingPolicyProperty Property, + unsigned Value) { + if (!Policy) + return; + + PrintingPolicy *P = static_cast<PrintingPolicy *>(Policy); + switch (Property) { + case CXPrintingPolicy_Indentation: + P->Indentation = Value; + return; + case CXPrintingPolicy_SuppressSpecifiers: + P->SuppressSpecifiers = Value; + return; + case CXPrintingPolicy_SuppressTagKeyword: + P->SuppressTagKeyword = Value; + return; + case CXPrintingPolicy_IncludeTagDefinition: + P->IncludeTagDefinition = Value; + return; + case CXPrintingPolicy_SuppressScope: + P->SuppressScope = Value; + return; + case CXPrintingPolicy_SuppressUnwrittenScope: + P->SuppressUnwrittenScope = Value; + return; + case CXPrintingPolicy_SuppressInitializers: + P->SuppressInitializers = Value; + return; + case CXPrintingPolicy_ConstantArraySizeAsWritten: + P->ConstantArraySizeAsWritten = Value; + return; + case CXPrintingPolicy_AnonymousTagLocations: + P->AnonymousTagLocations = Value; + return; + case CXPrintingPolicy_SuppressStrongLifetime: + P->SuppressStrongLifetime = Value; + return; + case CXPrintingPolicy_SuppressLifetimeQualifiers: + P->SuppressLifetimeQualifiers = Value; + return; + case CXPrintingPolicy_SuppressTemplateArgsInCXXConstructors: + P->SuppressTemplateArgsInCXXConstructors = Value; + return; + case CXPrintingPolicy_Bool: + P->Bool = Value; + return; + case CXPrintingPolicy_Restrict: + P->Restrict = Value; + return; + case CXPrintingPolicy_Alignof: + P->Alignof = Value; + return; + case CXPrintingPolicy_UnderscoreAlignof: + P->UnderscoreAlignof = Value; + return; + case CXPrintingPolicy_UseVoidForZeroParams: + P->UseVoidForZeroParams = Value; + return; + case CXPrintingPolicy_TerseOutput: + P->TerseOutput = Value; + return; + case CXPrintingPolicy_PolishForDeclaration: + P->PolishForDeclaration = Value; + return; + case CXPrintingPolicy_Half: + P->Half = Value; + return; + case CXPrintingPolicy_MSWChar: + P->MSWChar = Value; + return; + case CXPrintingPolicy_IncludeNewlines: + P->IncludeNewlines = Value; + return; + case CXPrintingPolicy_MSVCFormatting: + P->MSVCFormatting = Value; + return; + case CXPrintingPolicy_ConstantsAsWritten: + P->ConstantsAsWritten = Value; + return; + case CXPrintingPolicy_SuppressImplicitBase: + P->SuppressImplicitBase = Value; + return; + case CXPrintingPolicy_FullyQualifiedName: + P->FullyQualifiedName = Value; + return; + } + + assert(false && "Invalid CXPrintingPolicyProperty"); +} + +CXString clang_getCursorPrettyPrinted(CXCursor C, CXPrintingPolicy cxPolicy) { + if (clang_Cursor_isNull(C)) + return cxstring::createEmpty(); + + if (clang_isDeclaration(C.kind)) { + const Decl *D = getCursorDecl(C); + if (!D) + return cxstring::createEmpty(); + + SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + PrintingPolicy *UserPolicy = static_cast<PrintingPolicy *>(cxPolicy); + D->print(OS, UserPolicy ? *UserPolicy + : getCursorContext(C).getPrintingPolicy()); + + return cxstring::createDup(OS.str()); + } + + return cxstring::createEmpty(); +} + +CXString clang_getCursorDisplayName(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return clang_getCursorSpelling(C); + + const Decl *D = getCursorDecl(C); + if (!D) + return cxstring::createEmpty(); + + PrintingPolicy Policy = getCursorContext(C).getPrintingPolicy(); + if (const FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D)) + D = FunTmpl->getTemplatedDecl(); + + if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) { + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << *Function; + if (Function->getPrimaryTemplate()) + OS << "<>"; + OS << "("; + for (unsigned I = 0, N = Function->getNumParams(); I != N; ++I) { + if (I) + OS << ", "; + OS << Function->getParamDecl(I)->getType().getAsString(Policy); + } + + if (Function->isVariadic()) { + if (Function->getNumParams()) + OS << ", "; + OS << "..."; + } + OS << ")"; + return cxstring::createDup(OS.str()); + } + + if (const ClassTemplateDecl *ClassTemplate = dyn_cast<ClassTemplateDecl>(D)) { + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << *ClassTemplate; + OS << "<"; + TemplateParameterList *Params = ClassTemplate->getTemplateParameters(); + for (unsigned I = 0, N = Params->size(); I != N; ++I) { + if (I) + OS << ", "; + + NamedDecl *Param = Params->getParam(I); + if (Param->getIdentifier()) { + OS << Param->getIdentifier()->getName(); + continue; + } + + // There is no parameter name, which makes this tricky. Try to come up + // with something useful that isn't too long. + if (TemplateTypeParmDecl *TTP = dyn_cast<TemplateTypeParmDecl>(Param)) + if (const auto *TC = TTP->getTypeConstraint()) { + TC->getConceptNameInfo().printName(OS, Policy); + if (TC->hasExplicitTemplateArgs()) + OS << "<...>"; + } else + OS << (TTP->wasDeclaredWithTypename()? "typename" : "class"); + else if (NonTypeTemplateParmDecl *NTTP + = dyn_cast<NonTypeTemplateParmDecl>(Param)) + OS << NTTP->getType().getAsString(Policy); + else + OS << "template<...> class"; + } + + OS << ">"; + return cxstring::createDup(OS.str()); + } + + if (const ClassTemplateSpecializationDecl *ClassSpec + = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + // If the type was explicitly written, use that. + if (TypeSourceInfo *TSInfo = ClassSpec->getTypeAsWritten()) + return cxstring::createDup(TSInfo->getType().getAsString(Policy)); + + SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + OS << *ClassSpec; + printTemplateArgumentList(OS, ClassSpec->getTemplateArgs().asArray(), + Policy); + return cxstring::createDup(OS.str()); + } + + return clang_getCursorSpelling(C); +} + +CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { + switch (Kind) { + case CXCursor_FunctionDecl: + return cxstring::createRef("FunctionDecl"); + case CXCursor_TypedefDecl: + return cxstring::createRef("TypedefDecl"); + case CXCursor_EnumDecl: + return cxstring::createRef("EnumDecl"); + case CXCursor_EnumConstantDecl: + return cxstring::createRef("EnumConstantDecl"); + case CXCursor_StructDecl: + return cxstring::createRef("StructDecl"); + case CXCursor_UnionDecl: + return cxstring::createRef("UnionDecl"); + case CXCursor_ClassDecl: + return cxstring::createRef("ClassDecl"); + case CXCursor_FieldDecl: + return cxstring::createRef("FieldDecl"); + case CXCursor_VarDecl: + return cxstring::createRef("VarDecl"); + case CXCursor_ParmDecl: + return cxstring::createRef("ParmDecl"); + case CXCursor_ObjCInterfaceDecl: + return cxstring::createRef("ObjCInterfaceDecl"); + case CXCursor_ObjCCategoryDecl: + return cxstring::createRef("ObjCCategoryDecl"); + case CXCursor_ObjCProtocolDecl: + return cxstring::createRef("ObjCProtocolDecl"); + case CXCursor_ObjCPropertyDecl: + return cxstring::createRef("ObjCPropertyDecl"); + case CXCursor_ObjCIvarDecl: + return cxstring::createRef("ObjCIvarDecl"); + case CXCursor_ObjCInstanceMethodDecl: + return cxstring::createRef("ObjCInstanceMethodDecl"); + case CXCursor_ObjCClassMethodDecl: + return cxstring::createRef("ObjCClassMethodDecl"); + case CXCursor_ObjCImplementationDecl: + return cxstring::createRef("ObjCImplementationDecl"); + case CXCursor_ObjCCategoryImplDecl: + return cxstring::createRef("ObjCCategoryImplDecl"); + case CXCursor_CXXMethod: + return cxstring::createRef("CXXMethod"); + case CXCursor_UnexposedDecl: + return cxstring::createRef("UnexposedDecl"); + case CXCursor_ObjCSuperClassRef: + return cxstring::createRef("ObjCSuperClassRef"); + case CXCursor_ObjCProtocolRef: + return cxstring::createRef("ObjCProtocolRef"); + case CXCursor_ObjCClassRef: + return cxstring::createRef("ObjCClassRef"); + case CXCursor_TypeRef: + return cxstring::createRef("TypeRef"); + case CXCursor_TemplateRef: + return cxstring::createRef("TemplateRef"); + case CXCursor_NamespaceRef: + return cxstring::createRef("NamespaceRef"); + case CXCursor_MemberRef: + return cxstring::createRef("MemberRef"); + case CXCursor_LabelRef: + return cxstring::createRef("LabelRef"); + case CXCursor_OverloadedDeclRef: + return cxstring::createRef("OverloadedDeclRef"); + case CXCursor_VariableRef: + return cxstring::createRef("VariableRef"); + case CXCursor_IntegerLiteral: + return cxstring::createRef("IntegerLiteral"); + case CXCursor_FixedPointLiteral: + return cxstring::createRef("FixedPointLiteral"); + case CXCursor_FloatingLiteral: + return cxstring::createRef("FloatingLiteral"); + case CXCursor_ImaginaryLiteral: + return cxstring::createRef("ImaginaryLiteral"); + case CXCursor_StringLiteral: + return cxstring::createRef("StringLiteral"); + case CXCursor_CharacterLiteral: + return cxstring::createRef("CharacterLiteral"); + case CXCursor_ParenExpr: + return cxstring::createRef("ParenExpr"); + case CXCursor_UnaryOperator: + return cxstring::createRef("UnaryOperator"); + case CXCursor_ArraySubscriptExpr: + return cxstring::createRef("ArraySubscriptExpr"); + case CXCursor_OMPArraySectionExpr: + return cxstring::createRef("OMPArraySectionExpr"); + case CXCursor_BinaryOperator: + return cxstring::createRef("BinaryOperator"); + case CXCursor_CompoundAssignOperator: + return cxstring::createRef("CompoundAssignOperator"); + case CXCursor_ConditionalOperator: + return cxstring::createRef("ConditionalOperator"); + case CXCursor_CStyleCastExpr: + return cxstring::createRef("CStyleCastExpr"); + case CXCursor_CompoundLiteralExpr: + return cxstring::createRef("CompoundLiteralExpr"); + case CXCursor_InitListExpr: + return cxstring::createRef("InitListExpr"); + case CXCursor_AddrLabelExpr: + return cxstring::createRef("AddrLabelExpr"); + case CXCursor_StmtExpr: + return cxstring::createRef("StmtExpr"); + case CXCursor_GenericSelectionExpr: + return cxstring::createRef("GenericSelectionExpr"); + case CXCursor_GNUNullExpr: + return cxstring::createRef("GNUNullExpr"); + case CXCursor_CXXStaticCastExpr: + return cxstring::createRef("CXXStaticCastExpr"); + case CXCursor_CXXDynamicCastExpr: + return cxstring::createRef("CXXDynamicCastExpr"); + case CXCursor_CXXReinterpretCastExpr: + return cxstring::createRef("CXXReinterpretCastExpr"); + case CXCursor_CXXConstCastExpr: + return cxstring::createRef("CXXConstCastExpr"); + case CXCursor_CXXFunctionalCastExpr: + return cxstring::createRef("CXXFunctionalCastExpr"); + case CXCursor_CXXTypeidExpr: + return cxstring::createRef("CXXTypeidExpr"); + case CXCursor_CXXBoolLiteralExpr: + return cxstring::createRef("CXXBoolLiteralExpr"); + case CXCursor_CXXNullPtrLiteralExpr: + return cxstring::createRef("CXXNullPtrLiteralExpr"); + case CXCursor_CXXThisExpr: + return cxstring::createRef("CXXThisExpr"); + case CXCursor_CXXThrowExpr: + return cxstring::createRef("CXXThrowExpr"); + case CXCursor_CXXNewExpr: + return cxstring::createRef("CXXNewExpr"); + case CXCursor_CXXDeleteExpr: + return cxstring::createRef("CXXDeleteExpr"); + case CXCursor_UnaryExpr: + return cxstring::createRef("UnaryExpr"); + case CXCursor_ObjCStringLiteral: + 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: + return cxstring::createRef("ObjCEncodeExpr"); + case CXCursor_ObjCSelectorExpr: + return cxstring::createRef("ObjCSelectorExpr"); + case CXCursor_ObjCProtocolExpr: + return cxstring::createRef("ObjCProtocolExpr"); + case CXCursor_ObjCBridgedCastExpr: + return cxstring::createRef("ObjCBridgedCastExpr"); + case CXCursor_BlockExpr: + return cxstring::createRef("BlockExpr"); + case CXCursor_PackExpansionExpr: + return cxstring::createRef("PackExpansionExpr"); + case CXCursor_SizeOfPackExpr: + return cxstring::createRef("SizeOfPackExpr"); + case CXCursor_LambdaExpr: + return cxstring::createRef("LambdaExpr"); + case CXCursor_UnexposedExpr: + return cxstring::createRef("UnexposedExpr"); + case CXCursor_DeclRefExpr: + return cxstring::createRef("DeclRefExpr"); + case CXCursor_MemberRefExpr: + return cxstring::createRef("MemberRefExpr"); + case CXCursor_CallExpr: + return cxstring::createRef("CallExpr"); + case CXCursor_ObjCMessageExpr: + return cxstring::createRef("ObjCMessageExpr"); + case CXCursor_BuiltinBitCastExpr: + return cxstring::createRef("BuiltinBitCastExpr"); + case CXCursor_UnexposedStmt: + return cxstring::createRef("UnexposedStmt"); + case CXCursor_DeclStmt: + return cxstring::createRef("DeclStmt"); + case CXCursor_LabelStmt: + return cxstring::createRef("LabelStmt"); + case CXCursor_CompoundStmt: + return cxstring::createRef("CompoundStmt"); + case CXCursor_CaseStmt: + return cxstring::createRef("CaseStmt"); + case CXCursor_DefaultStmt: + return cxstring::createRef("DefaultStmt"); + case CXCursor_IfStmt: + return cxstring::createRef("IfStmt"); + case CXCursor_SwitchStmt: + return cxstring::createRef("SwitchStmt"); + case CXCursor_WhileStmt: + return cxstring::createRef("WhileStmt"); + case CXCursor_DoStmt: + return cxstring::createRef("DoStmt"); + case CXCursor_ForStmt: + return cxstring::createRef("ForStmt"); + case CXCursor_GotoStmt: + return cxstring::createRef("GotoStmt"); + case CXCursor_IndirectGotoStmt: + return cxstring::createRef("IndirectGotoStmt"); + case CXCursor_ContinueStmt: + return cxstring::createRef("ContinueStmt"); + case CXCursor_BreakStmt: + return cxstring::createRef("BreakStmt"); + case CXCursor_ReturnStmt: + return cxstring::createRef("ReturnStmt"); + case CXCursor_GCCAsmStmt: + return cxstring::createRef("GCCAsmStmt"); + case CXCursor_MSAsmStmt: + return cxstring::createRef("MSAsmStmt"); + case CXCursor_ObjCAtTryStmt: + return cxstring::createRef("ObjCAtTryStmt"); + case CXCursor_ObjCAtCatchStmt: + return cxstring::createRef("ObjCAtCatchStmt"); + case CXCursor_ObjCAtFinallyStmt: + return cxstring::createRef("ObjCAtFinallyStmt"); + case CXCursor_ObjCAtThrowStmt: + return cxstring::createRef("ObjCAtThrowStmt"); + case CXCursor_ObjCAtSynchronizedStmt: + return cxstring::createRef("ObjCAtSynchronizedStmt"); + case CXCursor_ObjCAutoreleasePoolStmt: + return cxstring::createRef("ObjCAutoreleasePoolStmt"); + case CXCursor_ObjCForCollectionStmt: + return cxstring::createRef("ObjCForCollectionStmt"); + case CXCursor_CXXCatchStmt: + return cxstring::createRef("CXXCatchStmt"); + case CXCursor_CXXTryStmt: + return cxstring::createRef("CXXTryStmt"); + case CXCursor_CXXForRangeStmt: + return cxstring::createRef("CXXForRangeStmt"); + case CXCursor_SEHTryStmt: + return cxstring::createRef("SEHTryStmt"); + case CXCursor_SEHExceptStmt: + return cxstring::createRef("SEHExceptStmt"); + case CXCursor_SEHFinallyStmt: + return cxstring::createRef("SEHFinallyStmt"); + case CXCursor_SEHLeaveStmt: + return cxstring::createRef("SEHLeaveStmt"); + case CXCursor_NullStmt: + return cxstring::createRef("NullStmt"); + case CXCursor_InvalidFile: + return cxstring::createRef("InvalidFile"); + case CXCursor_InvalidCode: + return cxstring::createRef("InvalidCode"); + case CXCursor_NoDeclFound: + return cxstring::createRef("NoDeclFound"); + case CXCursor_NotImplemented: + return cxstring::createRef("NotImplemented"); + case CXCursor_TranslationUnit: + return cxstring::createRef("TranslationUnit"); + case CXCursor_UnexposedAttr: + return cxstring::createRef("UnexposedAttr"); + case CXCursor_IBActionAttr: + return cxstring::createRef("attribute(ibaction)"); + case CXCursor_IBOutletAttr: + return cxstring::createRef("attribute(iboutlet)"); + case CXCursor_IBOutletCollectionAttr: + return cxstring::createRef("attribute(iboutletcollection)"); + case CXCursor_CXXFinalAttr: + return cxstring::createRef("attribute(final)"); + case CXCursor_CXXOverrideAttr: + return cxstring::createRef("attribute(override)"); + case CXCursor_AnnotateAttr: + return cxstring::createRef("attribute(annotate)"); + case CXCursor_AsmLabelAttr: + return cxstring::createRef("asm label"); + case CXCursor_PackedAttr: + return cxstring::createRef("attribute(packed)"); + case CXCursor_PureAttr: + return cxstring::createRef("attribute(pure)"); + case CXCursor_ConstAttr: + return cxstring::createRef("attribute(const)"); + case CXCursor_NoDuplicateAttr: + return cxstring::createRef("attribute(noduplicate)"); + case CXCursor_CUDAConstantAttr: + return cxstring::createRef("attribute(constant)"); + case CXCursor_CUDADeviceAttr: + return cxstring::createRef("attribute(device)"); + case CXCursor_CUDAGlobalAttr: + return cxstring::createRef("attribute(global)"); + case CXCursor_CUDAHostAttr: + return cxstring::createRef("attribute(host)"); + case CXCursor_CUDASharedAttr: + return cxstring::createRef("attribute(shared)"); + case CXCursor_VisibilityAttr: + return cxstring::createRef("attribute(visibility)"); + case CXCursor_DLLExport: + return cxstring::createRef("attribute(dllexport)"); + case CXCursor_DLLImport: + return cxstring::createRef("attribute(dllimport)"); + case CXCursor_NSReturnsRetained: + return cxstring::createRef("attribute(ns_returns_retained)"); + case CXCursor_NSReturnsNotRetained: + return cxstring::createRef("attribute(ns_returns_not_retained)"); + case CXCursor_NSReturnsAutoreleased: + return cxstring::createRef("attribute(ns_returns_autoreleased)"); + case CXCursor_NSConsumesSelf: + return cxstring::createRef("attribute(ns_consumes_self)"); + case CXCursor_NSConsumed: + return cxstring::createRef("attribute(ns_consumed)"); + case CXCursor_ObjCException: + return cxstring::createRef("attribute(objc_exception)"); + case CXCursor_ObjCNSObject: + return cxstring::createRef("attribute(NSObject)"); + case CXCursor_ObjCIndependentClass: + return cxstring::createRef("attribute(objc_independent_class)"); + case CXCursor_ObjCPreciseLifetime: + return cxstring::createRef("attribute(objc_precise_lifetime)"); + case CXCursor_ObjCReturnsInnerPointer: + return cxstring::createRef("attribute(objc_returns_inner_pointer)"); + case CXCursor_ObjCRequiresSuper: + return cxstring::createRef("attribute(objc_requires_super)"); + case CXCursor_ObjCRootClass: + return cxstring::createRef("attribute(objc_root_class)"); + case CXCursor_ObjCSubclassingRestricted: + return cxstring::createRef("attribute(objc_subclassing_restricted)"); + case CXCursor_ObjCExplicitProtocolImpl: + return cxstring::createRef("attribute(objc_protocol_requires_explicit_implementation)"); + case CXCursor_ObjCDesignatedInitializer: + return cxstring::createRef("attribute(objc_designated_initializer)"); + case CXCursor_ObjCRuntimeVisible: + return cxstring::createRef("attribute(objc_runtime_visible)"); + case CXCursor_ObjCBoxable: + return cxstring::createRef("attribute(objc_boxable)"); + case CXCursor_FlagEnum: + return cxstring::createRef("attribute(flag_enum)"); + case CXCursor_PreprocessingDirective: + return cxstring::createRef("preprocessing directive"); + case CXCursor_MacroDefinition: + return cxstring::createRef("macro definition"); + case CXCursor_MacroExpansion: + return cxstring::createRef("macro expansion"); + case CXCursor_InclusionDirective: + return cxstring::createRef("inclusion directive"); + case CXCursor_Namespace: + return cxstring::createRef("Namespace"); + case CXCursor_LinkageSpec: + return cxstring::createRef("LinkageSpec"); + case CXCursor_CXXBaseSpecifier: + return cxstring::createRef("C++ base class specifier"); + case CXCursor_Constructor: + return cxstring::createRef("CXXConstructor"); + case CXCursor_Destructor: + return cxstring::createRef("CXXDestructor"); + case CXCursor_ConversionFunction: + return cxstring::createRef("CXXConversion"); + case CXCursor_TemplateTypeParameter: + return cxstring::createRef("TemplateTypeParameter"); + case CXCursor_NonTypeTemplateParameter: + return cxstring::createRef("NonTypeTemplateParameter"); + case CXCursor_TemplateTemplateParameter: + return cxstring::createRef("TemplateTemplateParameter"); + case CXCursor_FunctionTemplate: + return cxstring::createRef("FunctionTemplate"); + case CXCursor_ClassTemplate: + return cxstring::createRef("ClassTemplate"); + case CXCursor_ClassTemplatePartialSpecialization: + return cxstring::createRef("ClassTemplatePartialSpecialization"); + case CXCursor_NamespaceAlias: + return cxstring::createRef("NamespaceAlias"); + case CXCursor_UsingDirective: + return cxstring::createRef("UsingDirective"); + case CXCursor_UsingDeclaration: + return cxstring::createRef("UsingDeclaration"); + case CXCursor_TypeAliasDecl: + return cxstring::createRef("TypeAliasDecl"); + case CXCursor_ObjCSynthesizeDecl: + return cxstring::createRef("ObjCSynthesizeDecl"); + case CXCursor_ObjCDynamicDecl: + return cxstring::createRef("ObjCDynamicDecl"); + case CXCursor_CXXAccessSpecifier: + return cxstring::createRef("CXXAccessSpecifier"); + case CXCursor_ModuleImportDecl: + return cxstring::createRef("ModuleImport"); + case CXCursor_OMPParallelDirective: + return cxstring::createRef("OMPParallelDirective"); + case CXCursor_OMPSimdDirective: + return cxstring::createRef("OMPSimdDirective"); + case CXCursor_OMPForDirective: + return cxstring::createRef("OMPForDirective"); + case CXCursor_OMPForSimdDirective: + return cxstring::createRef("OMPForSimdDirective"); + case CXCursor_OMPSectionsDirective: + return cxstring::createRef("OMPSectionsDirective"); + case CXCursor_OMPSectionDirective: + return cxstring::createRef("OMPSectionDirective"); + case CXCursor_OMPSingleDirective: + return cxstring::createRef("OMPSingleDirective"); + case CXCursor_OMPMasterDirective: + return cxstring::createRef("OMPMasterDirective"); + case CXCursor_OMPCriticalDirective: + return cxstring::createRef("OMPCriticalDirective"); + case CXCursor_OMPParallelForDirective: + return cxstring::createRef("OMPParallelForDirective"); + case CXCursor_OMPParallelForSimdDirective: + return cxstring::createRef("OMPParallelForSimdDirective"); + case CXCursor_OMPParallelMasterDirective: + return cxstring::createRef("OMPParallelMasterDirective"); + case CXCursor_OMPParallelSectionsDirective: + return cxstring::createRef("OMPParallelSectionsDirective"); + case CXCursor_OMPTaskDirective: + return cxstring::createRef("OMPTaskDirective"); + case CXCursor_OMPTaskyieldDirective: + return cxstring::createRef("OMPTaskyieldDirective"); + case CXCursor_OMPBarrierDirective: + return cxstring::createRef("OMPBarrierDirective"); + case CXCursor_OMPTaskwaitDirective: + return cxstring::createRef("OMPTaskwaitDirective"); + case CXCursor_OMPTaskgroupDirective: + return cxstring::createRef("OMPTaskgroupDirective"); + case CXCursor_OMPFlushDirective: + return cxstring::createRef("OMPFlushDirective"); + case CXCursor_OMPOrderedDirective: + return cxstring::createRef("OMPOrderedDirective"); + case CXCursor_OMPAtomicDirective: + return cxstring::createRef("OMPAtomicDirective"); + case CXCursor_OMPTargetDirective: + 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: + return cxstring::createRef("OMPCancellationPointDirective"); + case CXCursor_OMPCancelDirective: + return cxstring::createRef("OMPCancelDirective"); + case CXCursor_OMPTaskLoopDirective: + return cxstring::createRef("OMPTaskLoopDirective"); + case CXCursor_OMPTaskLoopSimdDirective: + return cxstring::createRef("OMPTaskLoopSimdDirective"); + case CXCursor_OMPMasterTaskLoopDirective: + return cxstring::createRef("OMPMasterTaskLoopDirective"); + case CXCursor_OMPMasterTaskLoopSimdDirective: + return cxstring::createRef("OMPMasterTaskLoopSimdDirective"); + case CXCursor_OMPParallelMasterTaskLoopDirective: + return cxstring::createRef("OMPParallelMasterTaskLoopDirective"); + case CXCursor_OMPParallelMasterTaskLoopSimdDirective: + return cxstring::createRef("OMPParallelMasterTaskLoopSimdDirective"); + 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_OMPTargetSimdDirective: + return cxstring::createRef("OMPTargetSimdDirective"); + case CXCursor_OMPTeamsDistributeDirective: + return cxstring::createRef("OMPTeamsDistributeDirective"); + case CXCursor_OMPTeamsDistributeSimdDirective: + return cxstring::createRef("OMPTeamsDistributeSimdDirective"); + case CXCursor_OMPTeamsDistributeParallelForSimdDirective: + return cxstring::createRef("OMPTeamsDistributeParallelForSimdDirective"); + case CXCursor_OMPTeamsDistributeParallelForDirective: + return cxstring::createRef("OMPTeamsDistributeParallelForDirective"); + case CXCursor_OMPTargetTeamsDirective: + return cxstring::createRef("OMPTargetTeamsDirective"); + case CXCursor_OMPTargetTeamsDistributeDirective: + return cxstring::createRef("OMPTargetTeamsDistributeDirective"); + case CXCursor_OMPTargetTeamsDistributeParallelForDirective: + return cxstring::createRef("OMPTargetTeamsDistributeParallelForDirective"); + case CXCursor_OMPTargetTeamsDistributeParallelForSimdDirective: + return cxstring::createRef( + "OMPTargetTeamsDistributeParallelForSimdDirective"); + case CXCursor_OMPTargetTeamsDistributeSimdDirective: + return cxstring::createRef("OMPTargetTeamsDistributeSimdDirective"); + case CXCursor_OverloadCandidate: + return cxstring::createRef("OverloadCandidate"); + case CXCursor_TypeAliasTemplateDecl: + return cxstring::createRef("TypeAliasTemplateDecl"); + case CXCursor_StaticAssert: + return cxstring::createRef("StaticAssert"); + case CXCursor_FriendDecl: + return cxstring::createRef("FriendDecl"); + case CXCursor_ConvergentAttr: + return cxstring::createRef("attribute(convergent)"); + case CXCursor_WarnUnusedAttr: + return cxstring::createRef("attribute(warn_unused)"); + case CXCursor_WarnUnusedResultAttr: + return cxstring::createRef("attribute(warn_unused_result)"); + case CXCursor_AlignedAttr: + return cxstring::createRef("attribute(aligned)"); + } + + llvm_unreachable("Unhandled CXCursorKind"); +} + +struct GetCursorData { + SourceLocation TokenBeginLoc; + bool PointsAtMacroArgExpansion; + bool VisitedObjCPropertyImplDecl; + SourceLocation VisitedDeclaratorDeclStartLoc; + CXCursor &BestCursor; + + GetCursorData(SourceManager &SM, + SourceLocation tokenBegin, CXCursor &outputCursor) + : TokenBeginLoc(tokenBegin), BestCursor(outputCursor) { + PointsAtMacroArgExpansion = SM.isMacroArgExpansion(tokenBegin); + VisitedObjCPropertyImplDecl = false; + } +}; + +static enum CXChildVisitResult GetCursorVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + GetCursorData *Data = static_cast<GetCursorData *>(client_data); + CXCursor *BestCursor = &Data->BestCursor; + + // If we point inside a macro argument we should provide info of what the + // token is so use the actual cursor, don't replace it with a macro expansion + // cursor. + if (cursor.kind == CXCursor_MacroExpansion && Data->PointsAtMacroArgExpansion) + return CXChildVisit_Recurse; + + if (clang_isDeclaration(cursor.kind)) { + // Avoid having the implicit methods override the property decls. + if (const ObjCMethodDecl *MD + = dyn_cast_or_null<ObjCMethodDecl>(getCursorDecl(cursor))) { + if (MD->isImplicit()) + return CXChildVisit_Break; + + } else if (const ObjCInterfaceDecl *ID + = dyn_cast_or_null<ObjCInterfaceDecl>(getCursorDecl(cursor))) { + // Check that when we have multiple @class references in the same line, + // that later ones do not override the previous ones. + // If we have: + // @class Foo, Bar; + // source ranges for both start at '@', so 'Bar' will end up overriding + // 'Foo' even though the cursor location was at 'Foo'. + if (BestCursor->kind == CXCursor_ObjCInterfaceDecl || + BestCursor->kind == CXCursor_ObjCClassRef) + if (const ObjCInterfaceDecl *PrevID + = dyn_cast_or_null<ObjCInterfaceDecl>(getCursorDecl(*BestCursor))){ + if (PrevID != ID && + !PrevID->isThisDeclarationADefinition() && + !ID->isThisDeclarationADefinition()) + return CXChildVisit_Break; + } + + } else if (const DeclaratorDecl *DD + = dyn_cast_or_null<DeclaratorDecl>(getCursorDecl(cursor))) { + SourceLocation StartLoc = DD->getSourceRange().getBegin(); + // Check that when we have multiple declarators in the same line, + // that later ones do not override the previous ones. + // If we have: + // int Foo, Bar; + // source ranges for both start at 'int', so 'Bar' will end up overriding + // 'Foo' even though the cursor location was at 'Foo'. + if (Data->VisitedDeclaratorDeclStartLoc == StartLoc) + return CXChildVisit_Break; + Data->VisitedDeclaratorDeclStartLoc = StartLoc; + + } else if (const ObjCPropertyImplDecl *PropImp + = dyn_cast_or_null<ObjCPropertyImplDecl>(getCursorDecl(cursor))) { + (void)PropImp; + // Check that when we have multiple @synthesize in the same line, + // that later ones do not override the previous ones. + // If we have: + // @synthesize Foo, Bar; + // source ranges for both start at '@', so 'Bar' will end up overriding + // 'Foo' even though the cursor location was at 'Foo'. + if (Data->VisitedObjCPropertyImplDecl) + return CXChildVisit_Break; + Data->VisitedObjCPropertyImplDecl = true; + } + } + + if (clang_isExpression(cursor.kind) && + clang_isDeclaration(BestCursor->kind)) { + if (const Decl *D = getCursorDecl(*BestCursor)) { + // Avoid having the cursor of an expression replace the declaration cursor + // when the expression source range overlaps the declaration range. + // This can happen for C++ constructor expressions whose range generally + // include the variable declaration, e.g.: + // MyCXXClass foo; // Make sure pointing at 'foo' returns a VarDecl cursor. + if (D->getLocation().isValid() && Data->TokenBeginLoc.isValid() && + D->getLocation() == Data->TokenBeginLoc) + return CXChildVisit_Break; + } + } + + // If our current best cursor is the construction of a temporary object, + // don't replace that cursor with a type reference, because we want + // clang_getCursor() to point at the constructor. + if (clang_isExpression(BestCursor->kind) && + isa<CXXTemporaryObjectExpr>(getCursorExpr(*BestCursor)) && + cursor.kind == CXCursor_TypeRef) { + // Keep the cursor pointing at CXXTemporaryObjectExpr but also mark it + // as having the actual point on the type reference. + *BestCursor = getTypeRefedCallExprCursor(*BestCursor); + return CXChildVisit_Recurse; + } + + // If we already have an Objective-C superclass reference, don't + // update it further. + if (BestCursor->kind == CXCursor_ObjCSuperClassRef) + return CXChildVisit_Break; + + *BestCursor = cursor; + return CXChildVisit_Recurse; +} + +CXCursor clang_getCursor(CXTranslationUnit TU, CXSourceLocation Loc) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return clang_getNullCursor(); + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SourceLocation SLoc = cxloc::translateSourceLocation(Loc); + CXCursor Result = cxcursor::getCursor(TU, SLoc); + + LOG_FUNC_SECTION { + CXFile SearchFile; + unsigned SearchLine, SearchColumn; + CXFile ResultFile; + unsigned ResultLine, ResultColumn; + CXString SearchFileName, ResultFileName, KindSpelling, USR; + const char *IsDef = clang_isCursorDefinition(Result)? " (Definition)" : ""; + CXSourceLocation ResultLoc = clang_getCursorLocation(Result); + + clang_getFileLocation(Loc, &SearchFile, &SearchLine, &SearchColumn, + nullptr); + clang_getFileLocation(ResultLoc, &ResultFile, &ResultLine, + &ResultColumn, nullptr); + SearchFileName = clang_getFileName(SearchFile); + ResultFileName = clang_getFileName(ResultFile); + KindSpelling = clang_getCursorKindSpelling(Result.kind); + USR = clang_getCursorUSR(Result); + *Log << llvm::format("(%s:%d:%d) = %s", + clang_getCString(SearchFileName), SearchLine, SearchColumn, + clang_getCString(KindSpelling)) + << llvm::format("(%s:%d:%d):%s%s", + clang_getCString(ResultFileName), ResultLine, ResultColumn, + clang_getCString(USR), IsDef); + clang_disposeString(SearchFileName); + clang_disposeString(ResultFileName); + clang_disposeString(KindSpelling); + clang_disposeString(USR); + + CXCursor Definition = clang_getCursorDefinition(Result); + if (!clang_equalCursors(Definition, clang_getNullCursor())) { + CXSourceLocation DefinitionLoc = clang_getCursorLocation(Definition); + CXString DefinitionKindSpelling + = clang_getCursorKindSpelling(Definition.kind); + CXFile DefinitionFile; + unsigned DefinitionLine, DefinitionColumn; + clang_getFileLocation(DefinitionLoc, &DefinitionFile, + &DefinitionLine, &DefinitionColumn, nullptr); + CXString DefinitionFileName = clang_getFileName(DefinitionFile); + *Log << llvm::format(" -> %s(%s:%d:%d)", + clang_getCString(DefinitionKindSpelling), + clang_getCString(DefinitionFileName), + DefinitionLine, DefinitionColumn); + clang_disposeString(DefinitionFileName); + clang_disposeString(DefinitionKindSpelling); + } + } + + return Result; +} + +CXCursor clang_getNullCursor(void) { + return MakeCXCursorInvalid(CXCursor_InvalidFile); +} + +unsigned clang_equalCursors(CXCursor X, CXCursor Y) { + // Clear out the "FirstInDeclGroup" part in a declaration cursor, since we + // can't set consistently. For example, when visiting a DeclStmt we will set + // it but we don't set it on the result of clang_getCursorDefinition for + // a reference of the same declaration. + // FIXME: Setting "FirstInDeclGroup" in CXCursors is a hack that only works + // when visiting a DeclStmt currently, the AST should be enhanced to be able + // to provide that kind of info. + if (clang_isDeclaration(X.kind)) + X.data[1] = nullptr; + if (clang_isDeclaration(Y.kind)) + Y.data[1] = nullptr; + + return X == Y; +} + +unsigned clang_hashCursor(CXCursor C) { + unsigned Index = 0; + if (clang_isExpression(C.kind) || clang_isStatement(C.kind)) + Index = 1; + + return llvm::DenseMapInfo<std::pair<unsigned, const void*> >::getHashValue( + std::make_pair(C.kind, C.data[Index])); +} + +unsigned clang_isInvalid(enum CXCursorKind K) { + return K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid; +} + +unsigned clang_isDeclaration(enum CXCursorKind K) { + return (K >= CXCursor_FirstDecl && K <= CXCursor_LastDecl) || + (K >= CXCursor_FirstExtraDecl && K <= CXCursor_LastExtraDecl); +} + +unsigned clang_isInvalidDeclaration(CXCursor C) { + if (clang_isDeclaration(C.kind)) { + if (const Decl *D = getCursorDecl(C)) + return D->isInvalidDecl(); + } + + return 0; +} + +unsigned clang_isReference(enum CXCursorKind K) { + return K >= CXCursor_FirstRef && K <= CXCursor_LastRef; +} + +unsigned clang_isExpression(enum CXCursorKind K) { + return K >= CXCursor_FirstExpr && K <= CXCursor_LastExpr; +} + +unsigned clang_isStatement(enum CXCursorKind K) { + return K >= CXCursor_FirstStmt && K <= CXCursor_LastStmt; +} + +unsigned clang_isAttribute(enum CXCursorKind K) { + return K >= CXCursor_FirstAttr && K <= CXCursor_LastAttr; +} + +unsigned clang_isTranslationUnit(enum CXCursorKind K) { + return K == CXCursor_TranslationUnit; +} + +unsigned clang_isPreprocessing(enum CXCursorKind K) { + return K >= CXCursor_FirstPreprocessing && K <= CXCursor_LastPreprocessing; +} + +unsigned clang_isUnexposed(enum CXCursorKind K) { + switch (K) { + case CXCursor_UnexposedDecl: + case CXCursor_UnexposedExpr: + case CXCursor_UnexposedStmt: + case CXCursor_UnexposedAttr: + return true; + default: + return false; + } +} + +CXCursorKind clang_getCursorKind(CXCursor C) { + return C.kind; +} + +CXSourceLocation clang_getCursorLocation(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + std::pair<const ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCSuperClassRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCProtocolRef: { + std::pair<const ObjCProtocolDecl *, SourceLocation> P + = getCursorObjCProtocolRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCClassRef: { + std::pair<const ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCClassRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_TypeRef: { + std::pair<const TypeDecl *, SourceLocation> P = getCursorTypeRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_TemplateRef: { + std::pair<const TemplateDecl *, SourceLocation> P = + getCursorTemplateRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_NamespaceRef: { + std::pair<const NamedDecl *, SourceLocation> P = getCursorNamespaceRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_MemberRef: { + std::pair<const FieldDecl *, SourceLocation> P = getCursorMemberRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_VariableRef: { + std::pair<const VarDecl *, SourceLocation> P = getCursorVariableRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_CXXBaseSpecifier: { + const CXXBaseSpecifier *BaseSpec = getCursorCXXBaseSpecifier(C); + if (!BaseSpec) + return clang_getNullLocation(); + + if (TypeSourceInfo *TSInfo = BaseSpec->getTypeSourceInfo()) + return cxloc::translateSourceLocation(getCursorContext(C), + TSInfo->getTypeLoc().getBeginLoc()); + + return cxloc::translateSourceLocation(getCursorContext(C), + BaseSpec->getBeginLoc()); + } + + case CXCursor_LabelRef: { + std::pair<const LabelStmt *, SourceLocation> P = getCursorLabelRef(C); + return cxloc::translateSourceLocation(getCursorContext(C), P.second); + } + + case CXCursor_OverloadedDeclRef: + return cxloc::translateSourceLocation(getCursorContext(C), + getCursorOverloadedDeclRef(C).second); + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (clang_isExpression(C.kind)) + return cxloc::translateSourceLocation(getCursorContext(C), + getLocationFromExpr(getCursorExpr(C))); + + if (clang_isStatement(C.kind)) + return cxloc::translateSourceLocation(getCursorContext(C), + getCursorStmt(C)->getBeginLoc()); + + if (C.kind == CXCursor_PreprocessingDirective) { + SourceLocation L = cxcursor::getCursorPreprocessingDirective(C).getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_MacroExpansion) { + SourceLocation L + = cxcursor::getCursorMacroExpansion(C).getSourceRange().getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_MacroDefinition) { + SourceLocation L = cxcursor::getCursorMacroDefinition(C)->getLocation(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_InclusionDirective) { + SourceLocation L + = cxcursor::getCursorInclusionDirective(C)->getSourceRange().getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (clang_isAttribute(C.kind)) { + SourceLocation L + = cxcursor::getCursorAttr(C)->getLocation(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (!clang_isDeclaration(C.kind)) + return clang_getNullLocation(); + + const Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullLocation(); + + SourceLocation Loc = D->getLocation(); + // FIXME: Multiple variables declared in a single declaration + // currently lack the information needed to correctly determine their + // ranges when accounting for the type-specifier. We use context + // stored in the CXCursor to determine if the VarDecl is in a DeclGroup, + // and if so, whether it is the first decl. + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!cxcursor::isFirstInDeclGroup(C)) + Loc = VD->getLocation(); + } + + // For ObjC methods, give the start location of the method name. + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + Loc = MD->getSelectorStartLoc(); + + return cxloc::translateSourceLocation(getCursorContext(C), Loc); +} + +} // end extern "C" + +CXCursor cxcursor::getCursor(CXTranslationUnit TU, SourceLocation SLoc) { + assert(TU); + + // Guard against an invalid SourceLocation, or we may assert in one + // of the following calls. + if (SLoc.isInvalid()) + return clang_getNullCursor(); + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + + // Translate the given source location to make it point at the beginning of + // the token under the cursor. + SLoc = Lexer::GetBeginningOfToken(SLoc, CXXUnit->getSourceManager(), + CXXUnit->getASTContext().getLangOpts()); + + CXCursor Result = MakeCXCursorInvalid(CXCursor_NoDeclFound); + if (SLoc.isValid()) { + GetCursorData ResultData(CXXUnit->getSourceManager(), SLoc, Result); + CursorVisitor CursorVis(TU, GetCursorVisitor, &ResultData, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + SourceLocation(SLoc)); + CursorVis.visitFileRegion(); + } + + return Result; +} + +static SourceRange getRawCursorExtent(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: + return getCursorObjCSuperClassRef(C).second; + + case CXCursor_ObjCProtocolRef: + return getCursorObjCProtocolRef(C).second; + + case CXCursor_ObjCClassRef: + return getCursorObjCClassRef(C).second; + + case CXCursor_TypeRef: + return getCursorTypeRef(C).second; + + case CXCursor_TemplateRef: + return getCursorTemplateRef(C).second; + + case CXCursor_NamespaceRef: + return getCursorNamespaceRef(C).second; + + case CXCursor_MemberRef: + return getCursorMemberRef(C).second; + + case CXCursor_CXXBaseSpecifier: + return getCursorCXXBaseSpecifier(C)->getSourceRange(); + + case CXCursor_LabelRef: + return getCursorLabelRef(C).second; + + case CXCursor_OverloadedDeclRef: + return getCursorOverloadedDeclRef(C).second; + + case CXCursor_VariableRef: + return getCursorVariableRef(C).second; + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (clang_isExpression(C.kind)) + return getCursorExpr(C)->getSourceRange(); + + if (clang_isStatement(C.kind)) + return getCursorStmt(C)->getSourceRange(); + + if (clang_isAttribute(C.kind)) + return getCursorAttr(C)->getRange(); + + if (C.kind == CXCursor_PreprocessingDirective) + return cxcursor::getCursorPreprocessingDirective(C); + + if (C.kind == CXCursor_MacroExpansion) { + ASTUnit *TU = getCursorASTUnit(C); + SourceRange Range = cxcursor::getCursorMacroExpansion(C).getSourceRange(); + return TU->mapRangeFromPreamble(Range); + } + + if (C.kind == CXCursor_MacroDefinition) { + ASTUnit *TU = getCursorASTUnit(C); + SourceRange Range = cxcursor::getCursorMacroDefinition(C)->getSourceRange(); + return TU->mapRangeFromPreamble(Range); + } + + if (C.kind == CXCursor_InclusionDirective) { + ASTUnit *TU = getCursorASTUnit(C); + SourceRange Range = cxcursor::getCursorInclusionDirective(C)->getSourceRange(); + return TU->mapRangeFromPreamble(Range); + } + + if (C.kind == CXCursor_TranslationUnit) { + ASTUnit *TU = getCursorASTUnit(C); + FileID MainID = TU->getSourceManager().getMainFileID(); + SourceLocation Start = TU->getSourceManager().getLocForStartOfFile(MainID); + SourceLocation End = TU->getSourceManager().getLocForEndOfFile(MainID); + return SourceRange(Start, End); + } + + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return SourceRange(); + + SourceRange R = D->getSourceRange(); + // FIXME: Multiple variables declared in a single declaration + // currently lack the information needed to correctly determine their + // ranges when accounting for the type-specifier. We use context + // stored in the CXCursor to determine if the VarDecl is in a DeclGroup, + // and if so, whether it is the first decl. + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!cxcursor::isFirstInDeclGroup(C)) + R.setBegin(VD->getLocation()); + } + return R; + } + return SourceRange(); +} + +/// Retrieves the "raw" cursor extent, which is then extended to include +/// the decl-specifier-seq for declarations. +static SourceRange getFullCursorExtent(CXCursor C, SourceManager &SrcMgr) { + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return SourceRange(); + + SourceRange R = D->getSourceRange(); + + // Adjust the start of the location for declarations preceded by + // declaration specifiers. + SourceLocation StartLoc; + if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) { + if (TypeSourceInfo *TI = DD->getTypeSourceInfo()) + StartLoc = TI->getTypeLoc().getBeginLoc(); + } else if (const TypedefDecl *Typedef = dyn_cast<TypedefDecl>(D)) { + if (TypeSourceInfo *TI = Typedef->getTypeSourceInfo()) + StartLoc = TI->getTypeLoc().getBeginLoc(); + } + + if (StartLoc.isValid() && R.getBegin().isValid() && + SrcMgr.isBeforeInTranslationUnit(StartLoc, R.getBegin())) + R.setBegin(StartLoc); + + // FIXME: Multiple variables declared in a single declaration + // currently lack the information needed to correctly determine their + // ranges when accounting for the type-specifier. We use context + // stored in the CXCursor to determine if the VarDecl is in a DeclGroup, + // and if so, whether it is the first decl. + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (!cxcursor::isFirstInDeclGroup(C)) + R.setBegin(VD->getLocation()); + } + + return R; + } + + return getRawCursorExtent(C); +} + +CXSourceRange clang_getCursorExtent(CXCursor C) { + SourceRange R = getRawCursorExtent(C); + if (R.isInvalid()) + return clang_getNullRange(); + + return cxloc::translateSourceRange(getCursorContext(C), R); +} + +CXCursor clang_getCursorReferenced(CXCursor C) { + if (clang_isInvalid(C.kind)) + return clang_getNullCursor(); + + CXTranslationUnit tu = getCursorTU(C); + if (clang_isDeclaration(C.kind)) { + const Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + if (const UsingDecl *Using = dyn_cast<UsingDecl>(D)) + return MakeCursorOverloadedDeclRef(Using, D->getLocation(), tu); + if (const ObjCPropertyImplDecl *PropImpl = + dyn_cast<ObjCPropertyImplDecl>(D)) + if (ObjCPropertyDecl *Property = PropImpl->getPropertyDecl()) + return MakeCXCursor(Property, tu); + + return C; + } + + if (clang_isExpression(C.kind)) { + const Expr *E = getCursorExpr(C); + const Decl *D = getDeclFromExpr(E); + if (D) { + CXCursor declCursor = MakeCXCursor(D, tu); + declCursor = getSelectorIdentifierCursor(getSelectorIdentifierIndex(C), + declCursor); + return declCursor; + } + + if (const OverloadExpr *Ovl = dyn_cast_or_null<OverloadExpr>(E)) + return MakeCursorOverloadedDeclRef(Ovl, tu); + + return clang_getNullCursor(); + } + + if (clang_isStatement(C.kind)) { + const Stmt *S = getCursorStmt(C); + if (const GotoStmt *Goto = dyn_cast_or_null<GotoStmt>(S)) + if (LabelDecl *label = Goto->getLabel()) + if (LabelStmt *labelS = label->getStmt()) + return MakeCXCursor(labelS, getCursorDecl(C), tu); + + return clang_getNullCursor(); + } + + if (C.kind == CXCursor_MacroExpansion) { + if (const MacroDefinitionRecord *Def = + getCursorMacroExpansion(C).getDefinition()) + return MakeMacroDefinitionCursor(Def, tu); + } + + if (!clang_isReference(C.kind)) + return clang_getNullCursor(); + + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: + return MakeCXCursor(getCursorObjCSuperClassRef(C).first, tu); + + case CXCursor_ObjCProtocolRef: { + const ObjCProtocolDecl *Prot = getCursorObjCProtocolRef(C).first; + if (const ObjCProtocolDecl *Def = Prot->getDefinition()) + return MakeCXCursor(Def, tu); + + return MakeCXCursor(Prot, tu); + } + + case CXCursor_ObjCClassRef: { + const ObjCInterfaceDecl *Class = getCursorObjCClassRef(C).first; + if (const ObjCInterfaceDecl *Def = Class->getDefinition()) + return MakeCXCursor(Def, tu); + + return MakeCXCursor(Class, tu); + } + + case CXCursor_TypeRef: + return MakeCXCursor(getCursorTypeRef(C).first, tu ); + + case CXCursor_TemplateRef: + return MakeCXCursor(getCursorTemplateRef(C).first, tu ); + + case CXCursor_NamespaceRef: + return MakeCXCursor(getCursorNamespaceRef(C).first, tu ); + + case CXCursor_MemberRef: + return MakeCXCursor(getCursorMemberRef(C).first, tu ); + + case CXCursor_CXXBaseSpecifier: { + const CXXBaseSpecifier *B = cxcursor::getCursorCXXBaseSpecifier(C); + return clang_getTypeDeclaration(cxtype::MakeCXType(B->getType(), + tu )); + } + + case CXCursor_LabelRef: + // FIXME: We end up faking the "parent" declaration here because we + // don't want to make CXCursor larger. + return MakeCXCursor(getCursorLabelRef(C).first, + cxtu::getASTUnit(tu)->getASTContext() + .getTranslationUnitDecl(), + tu); + + case CXCursor_OverloadedDeclRef: + return C; + + case CXCursor_VariableRef: + return MakeCXCursor(getCursorVariableRef(C).first, tu); + + default: + // We would prefer to enumerate all non-reference cursor kinds here. + llvm_unreachable("Unhandled reference cursor kind"); + } +} + +CXCursor clang_getCursorDefinition(CXCursor C) { + if (clang_isInvalid(C.kind)) + return clang_getNullCursor(); + + CXTranslationUnit TU = getCursorTU(C); + + bool WasReference = false; + if (clang_isReference(C.kind) || clang_isExpression(C.kind)) { + C = clang_getCursorReferenced(C); + WasReference = true; + } + + if (C.kind == CXCursor_MacroExpansion) + return clang_getCursorReferenced(C); + + if (!clang_isDeclaration(C.kind)) + return clang_getNullCursor(); + + const Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + + switch (D->getKind()) { + // Declaration kinds that don't really separate the notions of + // declaration and definition. + case Decl::Namespace: + case Decl::Typedef: + case Decl::TypeAlias: + case Decl::TypeAliasTemplate: + case Decl::TemplateTypeParm: + case Decl::EnumConstant: + case Decl::Field: + case Decl::Binding: + case Decl::MSProperty: + case Decl::IndirectField: + case Decl::ObjCIvar: + case Decl::ObjCAtDefsField: + case Decl::ImplicitParam: + case Decl::ParmVar: + case Decl::NonTypeTemplateParm: + case Decl::TemplateTemplateParm: + case Decl::ObjCCategoryImpl: + case Decl::ObjCImplementation: + case Decl::AccessSpec: + case Decl::LinkageSpec: + case Decl::Export: + case Decl::ObjCPropertyImpl: + case Decl::FileScopeAsm: + case Decl::StaticAssert: + case Decl::Block: + case Decl::Captured: + case Decl::OMPCapturedExpr: + case Decl::Label: // FIXME: Is this right?? + case Decl::ClassScopeFunctionSpecialization: + case Decl::CXXDeductionGuide: + case Decl::Import: + case Decl::OMPThreadPrivate: + case Decl::OMPAllocate: + case Decl::OMPDeclareReduction: + case Decl::OMPDeclareMapper: + case Decl::OMPRequires: + case Decl::ObjCTypeParam: + case Decl::BuiltinTemplate: + case Decl::PragmaComment: + case Decl::PragmaDetectMismatch: + case Decl::UsingPack: + case Decl::Concept: + case Decl::LifetimeExtendedTemporary: + case Decl::RequiresExprBody: + return C; + + // Declaration kinds that don't make any sense here, but are + // nonetheless harmless. + case Decl::Empty: + case Decl::TranslationUnit: + case Decl::ExternCContext: + break; + + // Declaration kinds for which the definition is not resolvable. + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + break; + + case Decl::UsingDirective: + return MakeCXCursor(cast<UsingDirectiveDecl>(D)->getNominatedNamespace(), + TU); + + case Decl::NamespaceAlias: + return MakeCXCursor(cast<NamespaceAliasDecl>(D)->getNamespace(), TU); + + case Decl::Enum: + case Decl::Record: + case Decl::CXXRecord: + case Decl::ClassTemplateSpecialization: + case Decl::ClassTemplatePartialSpecialization: + if (TagDecl *Def = cast<TagDecl>(D)->getDefinition()) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: { + const FunctionDecl *Def = nullptr; + if (cast<FunctionDecl>(D)->getBody(Def)) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + } + + case Decl::Var: + case Decl::VarTemplateSpecialization: + case Decl::VarTemplatePartialSpecialization: + case Decl::Decomposition: { + // Ask the variable if it has a definition. + if (const VarDecl *Def = cast<VarDecl>(D)->getDefinition()) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + } + + case Decl::FunctionTemplate: { + const FunctionDecl *Def = nullptr; + if (cast<FunctionTemplateDecl>(D)->getTemplatedDecl()->getBody(Def)) + return MakeCXCursor(Def->getDescribedFunctionTemplate(), TU); + return clang_getNullCursor(); + } + + case Decl::ClassTemplate: { + if (RecordDecl *Def = cast<ClassTemplateDecl>(D)->getTemplatedDecl() + ->getDefinition()) + return MakeCXCursor(cast<CXXRecordDecl>(Def)->getDescribedClassTemplate(), + TU); + return clang_getNullCursor(); + } + + case Decl::VarTemplate: { + if (VarDecl *Def = + cast<VarTemplateDecl>(D)->getTemplatedDecl()->getDefinition()) + return MakeCXCursor(cast<VarDecl>(Def)->getDescribedVarTemplate(), TU); + return clang_getNullCursor(); + } + + case Decl::Using: + return MakeCursorOverloadedDeclRef(cast<UsingDecl>(D), + D->getLocation(), TU); + + case Decl::UsingShadow: + case Decl::ConstructorUsingShadow: + return clang_getCursorDefinition( + MakeCXCursor(cast<UsingShadowDecl>(D)->getTargetDecl(), + TU)); + + case Decl::ObjCMethod: { + const ObjCMethodDecl *Method = cast<ObjCMethodDecl>(D); + if (Method->isThisDeclarationADefinition()) + return C; + + // Dig out the method definition in the associated + // @implementation, if we have it. + // FIXME: The ASTs should make finding the definition easier. + if (const ObjCInterfaceDecl *Class + = dyn_cast<ObjCInterfaceDecl>(Method->getDeclContext())) + if (ObjCImplementationDecl *ClassImpl = Class->getImplementation()) + if (ObjCMethodDecl *Def = ClassImpl->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + if (Def->isThisDeclarationADefinition()) + return MakeCXCursor(Def, TU); + + return clang_getNullCursor(); + } + + case Decl::ObjCCategory: + if (ObjCCategoryImplDecl *Impl + = cast<ObjCCategoryDecl>(D)->getImplementation()) + return MakeCXCursor(Impl, TU); + return clang_getNullCursor(); + + case Decl::ObjCProtocol: + if (const ObjCProtocolDecl *Def = cast<ObjCProtocolDecl>(D)->getDefinition()) + return MakeCXCursor(Def, TU); + return clang_getNullCursor(); + + case Decl::ObjCInterface: { + // There are two notions of a "definition" for an Objective-C + // class: the interface and its implementation. When we resolved a + // reference to an Objective-C class, produce the @interface as + // the definition; when we were provided with the interface, + // produce the @implementation as the definition. + const ObjCInterfaceDecl *IFace = cast<ObjCInterfaceDecl>(D); + if (WasReference) { + if (const ObjCInterfaceDecl *Def = IFace->getDefinition()) + return MakeCXCursor(Def, TU); + } else if (ObjCImplementationDecl *Impl = IFace->getImplementation()) + return MakeCXCursor(Impl, TU); + return clang_getNullCursor(); + } + + case Decl::ObjCProperty: + // FIXME: We don't really know where to find the + // ObjCPropertyImplDecls that implement this property. + return clang_getNullCursor(); + + case Decl::ObjCCompatibleAlias: + if (const ObjCInterfaceDecl *Class + = cast<ObjCCompatibleAliasDecl>(D)->getClassInterface()) + if (const ObjCInterfaceDecl *Def = Class->getDefinition()) + return MakeCXCursor(Def, TU); + + return clang_getNullCursor(); + + case Decl::Friend: + if (NamedDecl *Friend = cast<FriendDecl>(D)->getFriendDecl()) + return clang_getCursorDefinition(MakeCXCursor(Friend, TU)); + return clang_getNullCursor(); + + case Decl::FriendTemplate: + if (NamedDecl *Friend = cast<FriendTemplateDecl>(D)->getFriendDecl()) + return clang_getCursorDefinition(MakeCXCursor(Friend, TU)); + return clang_getNullCursor(); + } + + return clang_getNullCursor(); +} + +unsigned clang_isCursorDefinition(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + return clang_getCursorDefinition(C) == C; +} + +CXCursor clang_getCanonicalCursor(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return C; + + if (const Decl *D = getCursorDecl(C)) { + if (const ObjCCategoryImplDecl *CatImplD = dyn_cast<ObjCCategoryImplDecl>(D)) + if (ObjCCategoryDecl *CatD = CatImplD->getCategoryDecl()) + return MakeCXCursor(CatD, getCursorTU(C)); + + if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) + if (const ObjCInterfaceDecl *IFD = ImplD->getClassInterface()) + return MakeCXCursor(IFD, getCursorTU(C)); + + return MakeCXCursor(D->getCanonicalDecl(), getCursorTU(C)); + } + + return C; +} + +int clang_Cursor_getObjCSelectorIndex(CXCursor cursor) { + return cxcursor::getSelectorIdentifierIndexAndLoc(cursor).first; +} + +unsigned clang_getNumOverloadedDecls(CXCursor C) { + if (C.kind != CXCursor_OverloadedDeclRef) + return 0; + + OverloadedDeclRefStorage Storage = getCursorOverloadedDeclRef(C).first; + if (const OverloadExpr *E = Storage.dyn_cast<const OverloadExpr *>()) + return E->getNumDecls(); + + if (OverloadedTemplateStorage *S + = Storage.dyn_cast<OverloadedTemplateStorage*>()) + return S->size(); + + const Decl *D = Storage.get<const Decl *>(); + if (const UsingDecl *Using = dyn_cast<UsingDecl>(D)) + return Using->shadow_size(); + + return 0; +} + +CXCursor clang_getOverloadedDecl(CXCursor cursor, unsigned index) { + if (cursor.kind != CXCursor_OverloadedDeclRef) + return clang_getNullCursor(); + + if (index >= clang_getNumOverloadedDecls(cursor)) + return clang_getNullCursor(); + + CXTranslationUnit TU = getCursorTU(cursor); + OverloadedDeclRefStorage Storage = getCursorOverloadedDeclRef(cursor).first; + if (const OverloadExpr *E = Storage.dyn_cast<const OverloadExpr *>()) + return MakeCXCursor(E->decls_begin()[index], TU); + + if (OverloadedTemplateStorage *S + = Storage.dyn_cast<OverloadedTemplateStorage*>()) + return MakeCXCursor(S->begin()[index], TU); + + const Decl *D = Storage.get<const Decl *>(); + if (const UsingDecl *Using = dyn_cast<UsingDecl>(D)) { + // FIXME: This is, unfortunately, linear time. + UsingDecl::shadow_iterator Pos = Using->shadow_begin(); + std::advance(Pos, index); + return MakeCXCursor(cast<UsingShadowDecl>(*Pos)->getTargetDecl(), TU); + } + + return clang_getNullCursor(); +} + +void clang_getDefinitionSpellingAndExtent(CXCursor C, + const char **startBuf, + const char **endBuf, + unsigned *startLine, + unsigned *startColumn, + unsigned *endLine, + unsigned *endColumn) { + assert(getCursorDecl(C) && "CXCursor has null decl"); + const FunctionDecl *FD = dyn_cast<FunctionDecl>(getCursorDecl(C)); + CompoundStmt *Body = dyn_cast<CompoundStmt>(FD->getBody()); + + SourceManager &SM = FD->getASTContext().getSourceManager(); + *startBuf = SM.getCharacterData(Body->getLBracLoc()); + *endBuf = SM.getCharacterData(Body->getRBracLoc()); + *startLine = SM.getSpellingLineNumber(Body->getLBracLoc()); + *startColumn = SM.getSpellingColumnNumber(Body->getLBracLoc()); + *endLine = SM.getSpellingLineNumber(Body->getRBracLoc()); + *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc()); +} + + +CXSourceRange clang_getCursorReferenceNameRange(CXCursor C, unsigned NameFlags, + unsigned PieceIndex) { + RefNamePieces Pieces; + + switch (C.kind) { + case CXCursor_MemberRefExpr: + if (const MemberExpr *E = dyn_cast<MemberExpr>(getCursorExpr(C))) + Pieces = buildPieces(NameFlags, true, E->getMemberNameInfo(), + E->getQualifierLoc().getSourceRange()); + break; + + case CXCursor_DeclRefExpr: + if (const DeclRefExpr *E = dyn_cast<DeclRefExpr>(getCursorExpr(C))) { + SourceRange TemplateArgLoc(E->getLAngleLoc(), E->getRAngleLoc()); + Pieces = + buildPieces(NameFlags, false, E->getNameInfo(), + E->getQualifierLoc().getSourceRange(), &TemplateArgLoc); + } + break; + + case CXCursor_CallExpr: + if (const CXXOperatorCallExpr *OCE = + dyn_cast<CXXOperatorCallExpr>(getCursorExpr(C))) { + const Expr *Callee = OCE->getCallee(); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Callee)) + Callee = ICE->getSubExpr(); + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Callee)) + Pieces = buildPieces(NameFlags, false, DRE->getNameInfo(), + DRE->getQualifierLoc().getSourceRange()); + } + break; + + default: + break; + } + + if (Pieces.empty()) { + if (PieceIndex == 0) + return clang_getCursorExtent(C); + } else if (PieceIndex < Pieces.size()) { + SourceRange R = Pieces[PieceIndex]; + if (R.isValid()) + return cxloc::translateSourceRange(getCursorContext(C), R); + } + + return clang_getNullRange(); +} + +void clang_enableStackTraces(void) { + // FIXME: Provide an argv0 here so we can find llvm-symbolizer. + llvm::sys::PrintStackTraceOnErrorSignal(StringRef()); +} + +void clang_executeOnThread(void (*fn)(void*), void *user_data, + unsigned stack_size) { + llvm::llvm_execute_on_thread(fn, user_data, + stack_size == 0 + ? clang::DesiredStackSize + : llvm::Optional<unsigned>(stack_size)); +} + +//===----------------------------------------------------------------------===// +// Token-based Operations. +//===----------------------------------------------------------------------===// + +/* CXToken layout: + * int_data[0]: a CXTokenKind + * int_data[1]: starting token location + * int_data[2]: token length + * int_data[3]: reserved + * ptr_data: for identifiers and keywords, an IdentifierInfo*. + * otherwise unused. + */ +CXTokenKind clang_getTokenKind(CXToken CXTok) { + return static_cast<CXTokenKind>(CXTok.int_data[0]); +} + +CXString clang_getTokenSpelling(CXTranslationUnit TU, CXToken CXTok) { + switch (clang_getTokenKind(CXTok)) { + case CXToken_Identifier: + case CXToken_Keyword: + // We know we have an IdentifierInfo*, so use that. + return cxstring::createRef(static_cast<IdentifierInfo *>(CXTok.ptr_data) + ->getNameStart()); + + case CXToken_Literal: { + // We have stashed the starting pointer in the ptr_data field. Use it. + const char *Text = static_cast<const char *>(CXTok.ptr_data); + return cxstring::createDup(StringRef(Text, CXTok.int_data[2])); + } + + case CXToken_Punctuation: + case CXToken_Comment: + break; + } + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return cxstring::createEmpty(); + } + + // We have to find the starting buffer pointer the hard way, by + // deconstructing the source location. + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return cxstring::createEmpty(); + + SourceLocation Loc = SourceLocation::getFromRawEncoding(CXTok.int_data[1]); + std::pair<FileID, unsigned> LocInfo + = CXXUnit->getSourceManager().getDecomposedSpellingLoc(Loc); + bool Invalid = false; + StringRef Buffer + = CXXUnit->getSourceManager().getBufferData(LocInfo.first, &Invalid); + if (Invalid) + return cxstring::createEmpty(); + + return cxstring::createDup(Buffer.substr(LocInfo.second, CXTok.int_data[2])); +} + +CXSourceLocation clang_getTokenLocation(CXTranslationUnit TU, CXToken CXTok) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return clang_getNullLocation(); + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return clang_getNullLocation(); + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), + SourceLocation::getFromRawEncoding(CXTok.int_data[1])); +} + +CXSourceRange clang_getTokenExtent(CXTranslationUnit TU, CXToken CXTok) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return clang_getNullRange(); + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return clang_getNullRange(); + + return cxloc::translateSourceRange(CXXUnit->getASTContext(), + SourceLocation::getFromRawEncoding(CXTok.int_data[1])); +} + +static void getTokens(ASTUnit *CXXUnit, SourceRange Range, + SmallVectorImpl<CXToken> &CXTokens) { + SourceManager &SourceMgr = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> BeginLocInfo + = SourceMgr.getDecomposedSpellingLoc(Range.getBegin()); + std::pair<FileID, unsigned> EndLocInfo + = SourceMgr.getDecomposedSpellingLoc(Range.getEnd()); + + // Cannot tokenize across files. + if (BeginLocInfo.first != EndLocInfo.first) + return; + + // Create a lexer + bool Invalid = false; + StringRef Buffer + = SourceMgr.getBufferData(BeginLocInfo.first, &Invalid); + if (Invalid) + return; + + Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first), + CXXUnit->getASTContext().getLangOpts(), + Buffer.begin(), Buffer.data() + BeginLocInfo.second, Buffer.end()); + Lex.SetCommentRetentionState(true); + + // Lex tokens until we hit the end of the range. + const char *EffectiveBufferEnd = Buffer.data() + EndLocInfo.second; + Token Tok; + bool previousWasAt = false; + do { + // Lex the next token + Lex.LexFromRawLexer(Tok); + if (Tok.is(tok::eof)) + break; + + // Initialize the CXToken. + CXToken CXTok; + + // - Common fields + CXTok.int_data[1] = Tok.getLocation().getRawEncoding(); + CXTok.int_data[2] = Tok.getLength(); + CXTok.int_data[3] = 0; + + // - Kind-specific fields + if (Tok.isLiteral()) { + CXTok.int_data[0] = CXToken_Literal; + CXTok.ptr_data = const_cast<char *>(Tok.getLiteralData()); + } else if (Tok.is(tok::raw_identifier)) { + // Lookup the identifier to determine whether we have a keyword. + IdentifierInfo *II + = CXXUnit->getPreprocessor().LookUpIdentifierInfo(Tok); + + if ((II->getObjCKeywordID() != tok::objc_not_keyword) && previousWasAt) { + CXTok.int_data[0] = CXToken_Keyword; + } + else { + CXTok.int_data[0] = Tok.is(tok::identifier) + ? CXToken_Identifier + : CXToken_Keyword; + } + CXTok.ptr_data = II; + } else if (Tok.is(tok::comment)) { + CXTok.int_data[0] = CXToken_Comment; + CXTok.ptr_data = nullptr; + } else { + CXTok.int_data[0] = CXToken_Punctuation; + CXTok.ptr_data = nullptr; + } + CXTokens.push_back(CXTok); + previousWasAt = Tok.is(tok::at); + } while (Lex.getBufferLocation() < EffectiveBufferEnd); +} + +CXToken *clang_getToken(CXTranslationUnit TU, CXSourceLocation Location) { + LOG_FUNC_SECTION { + *Log << TU << ' ' << Location; + } + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return NULL; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return NULL; + + SourceLocation Begin = cxloc::translateSourceLocation(Location); + if (Begin.isInvalid()) + return NULL; + SourceManager &SM = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> DecomposedEnd = SM.getDecomposedLoc(Begin); + DecomposedEnd.second += Lexer::MeasureTokenLength(Begin, SM, CXXUnit->getLangOpts()); + + SourceLocation End = SM.getComposedLoc(DecomposedEnd.first, DecomposedEnd.second); + + SmallVector<CXToken, 32> CXTokens; + getTokens(CXXUnit, SourceRange(Begin, End), CXTokens); + + if (CXTokens.empty()) + return NULL; + + CXTokens.resize(1); + CXToken *Token = static_cast<CXToken *>(llvm::safe_malloc(sizeof(CXToken))); + + memmove(Token, CXTokens.data(), sizeof(CXToken)); + return Token; +} + +void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range, + CXToken **Tokens, unsigned *NumTokens) { + LOG_FUNC_SECTION { + *Log << TU << ' ' << Range; + } + + if (Tokens) + *Tokens = nullptr; + if (NumTokens) + *NumTokens = 0; + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return; + } + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit || !Tokens || !NumTokens) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SourceRange R = cxloc::translateCXSourceRange(Range); + if (R.isInvalid()) + return; + + SmallVector<CXToken, 32> CXTokens; + getTokens(CXXUnit, R, CXTokens); + + if (CXTokens.empty()) + return; + + *Tokens = static_cast<CXToken *>( + llvm::safe_malloc(sizeof(CXToken) * CXTokens.size())); + memmove(*Tokens, CXTokens.data(), sizeof(CXToken) * CXTokens.size()); + *NumTokens = CXTokens.size(); +} + +void clang_disposeTokens(CXTranslationUnit TU, + CXToken *Tokens, unsigned NumTokens) { + free(Tokens); +} + +//===----------------------------------------------------------------------===// +// Token annotation APIs. +//===----------------------------------------------------------------------===// + +static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data); +static bool AnnotateTokensPostChildrenVisitor(CXCursor cursor, + CXClientData client_data); + +namespace { +class AnnotateTokensWorker { + CXToken *Tokens; + CXCursor *Cursors; + unsigned NumTokens; + unsigned TokIdx; + unsigned PreprocessingTokIdx; + CursorVisitor AnnotateVis; + SourceManager &SrcMgr; + bool HasContextSensitiveKeywords; + + struct PostChildrenAction { + CXCursor cursor; + enum Action { Invalid, Ignore, Postpone } action; + }; + using PostChildrenActions = SmallVector<PostChildrenAction, 0>; + + struct PostChildrenInfo { + CXCursor Cursor; + SourceRange CursorRange; + unsigned BeforeReachingCursorIdx; + unsigned BeforeChildrenTokenIdx; + PostChildrenActions ChildActions; + }; + SmallVector<PostChildrenInfo, 8> PostChildrenInfos; + + CXToken &getTok(unsigned Idx) { + assert(Idx < NumTokens); + return Tokens[Idx]; + } + const CXToken &getTok(unsigned Idx) const { + assert(Idx < NumTokens); + return Tokens[Idx]; + } + bool MoreTokens() const { return TokIdx < NumTokens; } + unsigned NextToken() const { return TokIdx; } + void AdvanceToken() { ++TokIdx; } + SourceLocation GetTokenLoc(unsigned tokI) { + return SourceLocation::getFromRawEncoding(getTok(tokI).int_data[1]); + } + bool isFunctionMacroToken(unsigned tokI) const { + return getTok(tokI).int_data[3] != 0; + } + SourceLocation getFunctionMacroTokenLoc(unsigned tokI) const { + return SourceLocation::getFromRawEncoding(getTok(tokI).int_data[3]); + } + + void annotateAndAdvanceTokens(CXCursor, RangeComparisonResult, SourceRange); + bool annotateAndAdvanceFunctionMacroTokens(CXCursor, RangeComparisonResult, + SourceRange); + +public: + AnnotateTokensWorker(CXToken *tokens, CXCursor *cursors, unsigned numTokens, + CXTranslationUnit TU, SourceRange RegionOfInterest) + : Tokens(tokens), Cursors(cursors), + NumTokens(numTokens), TokIdx(0), PreprocessingTokIdx(0), + AnnotateVis(TU, + AnnotateTokensVisitor, this, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + RegionOfInterest, + /*VisitDeclsOnly=*/false, + AnnotateTokensPostChildrenVisitor), + SrcMgr(cxtu::getASTUnit(TU)->getSourceManager()), + HasContextSensitiveKeywords(false) { } + + void VisitChildren(CXCursor C) { AnnotateVis.VisitChildren(C); } + enum CXChildVisitResult Visit(CXCursor cursor, CXCursor parent); + bool IsIgnoredChildCursor(CXCursor cursor) const; + PostChildrenActions DetermineChildActions(CXCursor Cursor) const; + + bool postVisitChildren(CXCursor cursor); + void HandlePostPonedChildCursors(const PostChildrenInfo &Info); + void HandlePostPonedChildCursor(CXCursor Cursor, unsigned StartTokenIndex); + + void AnnotateTokens(); + + /// Determine whether the annotator saw any cursors that have + /// context-sensitive keywords. + bool hasContextSensitiveKeywords() const { + return HasContextSensitiveKeywords; + } + + ~AnnotateTokensWorker() { + assert(PostChildrenInfos.empty()); + } +}; +} + +void AnnotateTokensWorker::AnnotateTokens() { + // Walk the AST within the region of interest, annotating tokens + // along the way. + AnnotateVis.visitFileRegion(); +} + +bool AnnotateTokensWorker::IsIgnoredChildCursor(CXCursor cursor) const { + if (PostChildrenInfos.empty()) + return false; + + for (const auto &ChildAction : PostChildrenInfos.back().ChildActions) { + if (ChildAction.cursor == cursor && + ChildAction.action == PostChildrenAction::Ignore) { + return true; + } + } + + return false; +} + +const CXXOperatorCallExpr *GetSubscriptOrCallOperator(CXCursor Cursor) { + if (!clang_isExpression(Cursor.kind)) + return nullptr; + + const Expr *E = getCursorExpr(Cursor); + if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E)) { + const OverloadedOperatorKind Kind = OCE->getOperator(); + if (Kind == OO_Call || Kind == OO_Subscript) + return OCE; + } + + return nullptr; +} + +AnnotateTokensWorker::PostChildrenActions +AnnotateTokensWorker::DetermineChildActions(CXCursor Cursor) const { + PostChildrenActions actions; + + // The DeclRefExpr of CXXOperatorCallExpr refering to the custom operator is + // visited before the arguments to the operator call. For the Call and + // Subscript operator the range of this DeclRefExpr includes the whole call + // expression, so that all tokens in that range would be mapped to the + // operator function, including the tokens of the arguments. To avoid that, + // ensure to visit this DeclRefExpr as last node. + if (const auto *OCE = GetSubscriptOrCallOperator(Cursor)) { + const Expr *Callee = OCE->getCallee(); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Callee)) { + const Expr *SubExpr = ICE->getSubExpr(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(SubExpr)) { + const Decl *parentDecl = getCursorDecl(Cursor); + CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); + + // Visit the DeclRefExpr as last. + CXCursor cxChild = MakeCXCursor(DRE, parentDecl, TU); + actions.push_back({cxChild, PostChildrenAction::Postpone}); + + // The parent of the DeclRefExpr, an ImplicitCastExpr, has an equally + // wide range as the DeclRefExpr. We can skip visiting this entirely. + cxChild = MakeCXCursor(ICE, parentDecl, TU); + actions.push_back({cxChild, PostChildrenAction::Ignore}); + } + } + } + + return actions; +} + +static inline void updateCursorAnnotation(CXCursor &Cursor, + const CXCursor &updateC) { + if (clang_isInvalid(updateC.kind) || !clang_isInvalid(Cursor.kind)) + return; + Cursor = updateC; +} + +/// It annotates and advances tokens with a cursor until the comparison +//// between the cursor location and the source range is the same as +/// \arg compResult. +/// +/// Pass RangeBefore to annotate tokens with a cursor until a range is reached. +/// Pass RangeOverlap to annotate tokens inside a range. +void AnnotateTokensWorker::annotateAndAdvanceTokens(CXCursor updateC, + RangeComparisonResult compResult, + SourceRange range) { + while (MoreTokens()) { + const unsigned I = NextToken(); + if (isFunctionMacroToken(I)) + if (!annotateAndAdvanceFunctionMacroTokens(updateC, compResult, range)) + return; + + SourceLocation TokLoc = GetTokenLoc(I); + if (LocationCompare(SrcMgr, TokLoc, range) == compResult) { + updateCursorAnnotation(Cursors[I], updateC); + AdvanceToken(); + continue; + } + break; + } +} + +/// Special annotation handling for macro argument tokens. +/// \returns true if it advanced beyond all macro tokens, false otherwise. +bool AnnotateTokensWorker::annotateAndAdvanceFunctionMacroTokens( + CXCursor updateC, + RangeComparisonResult compResult, + SourceRange range) { + assert(MoreTokens()); + assert(isFunctionMacroToken(NextToken()) && + "Should be called only for macro arg tokens"); + + // This works differently than annotateAndAdvanceTokens; because expanded + // macro arguments can have arbitrary translation-unit source order, we do not + // advance the token index one by one until a token fails the range test. + // We only advance once past all of the macro arg tokens if all of them + // pass the range test. If one of them fails we keep the token index pointing + // at the start of the macro arg tokens so that the failing token will be + // annotated by a subsequent annotation try. + + bool atLeastOneCompFail = false; + + unsigned I = NextToken(); + for (; I < NumTokens && isFunctionMacroToken(I); ++I) { + SourceLocation TokLoc = getFunctionMacroTokenLoc(I); + if (TokLoc.isFileID()) + continue; // not macro arg token, it's parens or comma. + if (LocationCompare(SrcMgr, TokLoc, range) == compResult) { + if (clang_isInvalid(clang_getCursorKind(Cursors[I]))) + Cursors[I] = updateC; + } else + atLeastOneCompFail = true; + } + + if (atLeastOneCompFail) + return false; + + TokIdx = I; // All of the tokens were handled, advance beyond all of them. + return true; +} + +enum CXChildVisitResult +AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) { + SourceRange cursorRange = getRawCursorExtent(cursor); + if (cursorRange.isInvalid()) + return CXChildVisit_Recurse; + + if (IsIgnoredChildCursor(cursor)) + return CXChildVisit_Continue; + + if (!HasContextSensitiveKeywords) { + // Objective-C properties can have context-sensitive keywords. + if (cursor.kind == CXCursor_ObjCPropertyDecl) { + if (const ObjCPropertyDecl *Property + = dyn_cast_or_null<ObjCPropertyDecl>(getCursorDecl(cursor))) + HasContextSensitiveKeywords = Property->getPropertyAttributesAsWritten() != 0; + } + // Objective-C methods can have context-sensitive keywords. + else if (cursor.kind == CXCursor_ObjCInstanceMethodDecl || + cursor.kind == CXCursor_ObjCClassMethodDecl) { + if (const ObjCMethodDecl *Method + = dyn_cast_or_null<ObjCMethodDecl>(getCursorDecl(cursor))) { + if (Method->getObjCDeclQualifier()) + HasContextSensitiveKeywords = true; + else { + for (const auto *P : Method->parameters()) { + if (P->getObjCDeclQualifier()) { + HasContextSensitiveKeywords = true; + break; + } + } + } + } + } + // C++ methods can have context-sensitive keywords. + else if (cursor.kind == CXCursor_CXXMethod) { + if (const CXXMethodDecl *Method + = dyn_cast_or_null<CXXMethodDecl>(getCursorDecl(cursor))) { + if (Method->hasAttr<FinalAttr>() || Method->hasAttr<OverrideAttr>()) + HasContextSensitiveKeywords = true; + } + } + // C++ classes can have context-sensitive keywords. + else if (cursor.kind == CXCursor_StructDecl || + cursor.kind == CXCursor_ClassDecl || + cursor.kind == CXCursor_ClassTemplate || + cursor.kind == CXCursor_ClassTemplatePartialSpecialization) { + if (const Decl *D = getCursorDecl(cursor)) + if (D->hasAttr<FinalAttr>()) + HasContextSensitiveKeywords = true; + } + } + + // Don't override a property annotation with its getter/setter method. + if (cursor.kind == CXCursor_ObjCInstanceMethodDecl && + parent.kind == CXCursor_ObjCPropertyDecl) + return CXChildVisit_Continue; + + if (clang_isPreprocessing(cursor.kind)) { + // Items in the preprocessing record are kept separate from items in + // declarations, so we keep a separate token index. + unsigned SavedTokIdx = TokIdx; + TokIdx = PreprocessingTokIdx; + + // Skip tokens up until we catch up to the beginning of the preprocessing + // entry. + while (MoreTokens()) { + const unsigned I = NextToken(); + SourceLocation TokLoc = GetTokenLoc(I); + switch (LocationCompare(SrcMgr, TokLoc, cursorRange)) { + case RangeBefore: + AdvanceToken(); + continue; + case RangeAfter: + case RangeOverlap: + break; + } + break; + } + + // Look at all of the tokens within this range. + while (MoreTokens()) { + const unsigned I = NextToken(); + SourceLocation TokLoc = GetTokenLoc(I); + switch (LocationCompare(SrcMgr, TokLoc, cursorRange)) { + case RangeBefore: + llvm_unreachable("Infeasible"); + case RangeAfter: + break; + case RangeOverlap: + // For macro expansions, just note where the beginning of the macro + // expansion occurs. + if (cursor.kind == CXCursor_MacroExpansion) { + if (TokLoc == cursorRange.getBegin()) + Cursors[I] = cursor; + AdvanceToken(); + break; + } + // We may have already annotated macro names inside macro definitions. + if (Cursors[I].kind != CXCursor_MacroExpansion) + Cursors[I] = cursor; + AdvanceToken(); + continue; + } + break; + } + + // Save the preprocessing token index; restore the non-preprocessing + // token index. + PreprocessingTokIdx = TokIdx; + TokIdx = SavedTokIdx; + return CXChildVisit_Recurse; + } + + if (cursorRange.isInvalid()) + return CXChildVisit_Continue; + + unsigned BeforeReachingCursorIdx = NextToken(); + const enum CXCursorKind cursorK = clang_getCursorKind(cursor); + const enum CXCursorKind K = clang_getCursorKind(parent); + const CXCursor updateC = + (clang_isInvalid(K) || K == CXCursor_TranslationUnit || + // Attributes are annotated out-of-order, skip tokens until we reach it. + clang_isAttribute(cursor.kind)) + ? clang_getNullCursor() : parent; + + annotateAndAdvanceTokens(updateC, RangeBefore, cursorRange); + + // Avoid having the cursor of an expression "overwrite" the annotation of the + // variable declaration that it belongs to. + // This can happen for C++ constructor expressions whose range generally + // include the variable declaration, e.g.: + // MyCXXClass foo; // Make sure we don't annotate 'foo' as a CallExpr cursor. + if (clang_isExpression(cursorK) && MoreTokens()) { + const Expr *E = getCursorExpr(cursor); + if (const Decl *D = getCursorDecl(cursor)) { + const unsigned I = NextToken(); + if (E->getBeginLoc().isValid() && D->getLocation().isValid() && + E->getBeginLoc() == D->getLocation() && + E->getBeginLoc() == GetTokenLoc(I)) { + updateCursorAnnotation(Cursors[I], updateC); + AdvanceToken(); + } + } + } + + // Before recursing into the children keep some state that we are going + // to use in the AnnotateTokensWorker::postVisitChildren callback to do some + // extra work after the child nodes are visited. + // Note that we don't call VisitChildren here to avoid traversing statements + // code-recursively which can blow the stack. + + PostChildrenInfo Info; + Info.Cursor = cursor; + Info.CursorRange = cursorRange; + Info.BeforeReachingCursorIdx = BeforeReachingCursorIdx; + Info.BeforeChildrenTokenIdx = NextToken(); + Info.ChildActions = DetermineChildActions(cursor); + PostChildrenInfos.push_back(Info); + + return CXChildVisit_Recurse; +} + +bool AnnotateTokensWorker::postVisitChildren(CXCursor cursor) { + if (PostChildrenInfos.empty()) + return false; + const PostChildrenInfo &Info = PostChildrenInfos.back(); + if (!clang_equalCursors(Info.Cursor, cursor)) + return false; + + HandlePostPonedChildCursors(Info); + + const unsigned BeforeChildren = Info.BeforeChildrenTokenIdx; + const unsigned AfterChildren = NextToken(); + SourceRange cursorRange = Info.CursorRange; + + // Scan the tokens that are at the end of the cursor, but are not captured + // but the child cursors. + annotateAndAdvanceTokens(cursor, RangeOverlap, cursorRange); + + // Scan the tokens that are at the beginning of the cursor, but are not + // capture by the child cursors. + for (unsigned I = BeforeChildren; I != AfterChildren; ++I) { + if (!clang_isInvalid(clang_getCursorKind(Cursors[I]))) + break; + + Cursors[I] = cursor; + } + + // Attributes are annotated out-of-order, rewind TokIdx to when we first + // encountered the attribute cursor. + if (clang_isAttribute(cursor.kind)) + TokIdx = Info.BeforeReachingCursorIdx; + + PostChildrenInfos.pop_back(); + return false; +} + +void AnnotateTokensWorker::HandlePostPonedChildCursors( + const PostChildrenInfo &Info) { + for (const auto &ChildAction : Info.ChildActions) { + if (ChildAction.action == PostChildrenAction::Postpone) { + HandlePostPonedChildCursor(ChildAction.cursor, + Info.BeforeChildrenTokenIdx); + } + } +} + +void AnnotateTokensWorker::HandlePostPonedChildCursor( + CXCursor Cursor, unsigned StartTokenIndex) { + unsigned I = StartTokenIndex; + + // The bracket tokens of a Call or Subscript operator are mapped to + // CallExpr/CXXOperatorCallExpr because we skipped visiting the corresponding + // DeclRefExpr. Remap these tokens to the DeclRefExpr cursors. + for (unsigned RefNameRangeNr = 0; I < NumTokens; RefNameRangeNr++) { + const CXSourceRange CXRefNameRange = clang_getCursorReferenceNameRange( + Cursor, CXNameRange_WantQualifier, RefNameRangeNr); + if (clang_Range_isNull(CXRefNameRange)) + break; // All ranges handled. + + SourceRange RefNameRange = cxloc::translateCXSourceRange(CXRefNameRange); + while (I < NumTokens) { + const SourceLocation TokenLocation = GetTokenLoc(I); + if (!TokenLocation.isValid()) + break; + + // Adapt the end range, because LocationCompare() reports + // RangeOverlap even for the not-inclusive end location. + const SourceLocation fixedEnd = + RefNameRange.getEnd().getLocWithOffset(-1); + RefNameRange = SourceRange(RefNameRange.getBegin(), fixedEnd); + + const RangeComparisonResult ComparisonResult = + LocationCompare(SrcMgr, TokenLocation, RefNameRange); + + if (ComparisonResult == RangeOverlap) { + Cursors[I++] = Cursor; + } else if (ComparisonResult == RangeBefore) { + ++I; // Not relevant token, check next one. + } else if (ComparisonResult == RangeAfter) { + break; // All tokens updated for current range, check next. + } + } + } +} + +static enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + return static_cast<AnnotateTokensWorker*>(client_data)->Visit(cursor, parent); +} + +static bool AnnotateTokensPostChildrenVisitor(CXCursor cursor, + CXClientData client_data) { + return static_cast<AnnotateTokensWorker*>(client_data)-> + postVisitChildren(cursor); +} + +namespace { + +/// Uses the macro expansions in the preprocessing record to find +/// and mark tokens that are macro arguments. This info is used by the +/// AnnotateTokensWorker. +class MarkMacroArgTokensVisitor { + SourceManager &SM; + CXToken *Tokens; + unsigned NumTokens; + unsigned CurIdx; + +public: + MarkMacroArgTokensVisitor(SourceManager &SM, + CXToken *tokens, unsigned numTokens) + : SM(SM), Tokens(tokens), NumTokens(numTokens), CurIdx(0) { } + + CXChildVisitResult visit(CXCursor cursor, CXCursor parent) { + if (cursor.kind != CXCursor_MacroExpansion) + return CXChildVisit_Continue; + + SourceRange macroRange = getCursorMacroExpansion(cursor).getSourceRange(); + if (macroRange.getBegin() == macroRange.getEnd()) + return CXChildVisit_Continue; // it's not a function macro. + + for (; CurIdx < NumTokens; ++CurIdx) { + if (!SM.isBeforeInTranslationUnit(getTokenLoc(CurIdx), + macroRange.getBegin())) + break; + } + + if (CurIdx == NumTokens) + return CXChildVisit_Break; + + for (; CurIdx < NumTokens; ++CurIdx) { + SourceLocation tokLoc = getTokenLoc(CurIdx); + if (!SM.isBeforeInTranslationUnit(tokLoc, macroRange.getEnd())) + break; + + setFunctionMacroTokenLoc(CurIdx, SM.getMacroArgExpandedLocation(tokLoc)); + } + + if (CurIdx == NumTokens) + return CXChildVisit_Break; + + return CXChildVisit_Continue; + } + +private: + CXToken &getTok(unsigned Idx) { + assert(Idx < NumTokens); + return Tokens[Idx]; + } + const CXToken &getTok(unsigned Idx) const { + assert(Idx < NumTokens); + return Tokens[Idx]; + } + + SourceLocation getTokenLoc(unsigned tokI) { + return SourceLocation::getFromRawEncoding(getTok(tokI).int_data[1]); + } + + void setFunctionMacroTokenLoc(unsigned tokI, SourceLocation loc) { + // The third field is reserved and currently not used. Use it here + // to mark macro arg expanded tokens with their expanded locations. + getTok(tokI).int_data[3] = loc.getRawEncoding(); + } +}; + +} // end anonymous namespace + +static CXChildVisitResult +MarkMacroArgTokensVisitorDelegate(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + return static_cast<MarkMacroArgTokensVisitor*>(client_data)->visit(cursor, + parent); +} + +/// Used by \c annotatePreprocessorTokens. +/// \returns true if lexing was finished, false otherwise. +static bool lexNext(Lexer &Lex, Token &Tok, + unsigned &NextIdx, unsigned NumTokens) { + if (NextIdx >= NumTokens) + return true; + + ++NextIdx; + Lex.LexFromRawLexer(Tok); + return Tok.is(tok::eof); +} + +static void annotatePreprocessorTokens(CXTranslationUnit TU, + SourceRange RegionOfInterest, + CXCursor *Cursors, + CXToken *Tokens, + unsigned NumTokens) { + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + + Preprocessor &PP = CXXUnit->getPreprocessor(); + SourceManager &SourceMgr = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> BeginLocInfo + = SourceMgr.getDecomposedSpellingLoc(RegionOfInterest.getBegin()); + std::pair<FileID, unsigned> EndLocInfo + = SourceMgr.getDecomposedSpellingLoc(RegionOfInterest.getEnd()); + + if (BeginLocInfo.first != EndLocInfo.first) + return; + + StringRef Buffer; + bool Invalid = false; + Buffer = SourceMgr.getBufferData(BeginLocInfo.first, &Invalid); + if (Buffer.empty() || Invalid) + return; + + Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first), + CXXUnit->getASTContext().getLangOpts(), + Buffer.begin(), Buffer.data() + BeginLocInfo.second, + Buffer.end()); + Lex.SetCommentRetentionState(true); + + unsigned NextIdx = 0; + // Lex tokens in raw mode until we hit the end of the range, to avoid + // entering #includes or expanding macros. + while (true) { + Token Tok; + if (lexNext(Lex, Tok, NextIdx, NumTokens)) + break; + unsigned TokIdx = NextIdx-1; + assert(Tok.getLocation() == + SourceLocation::getFromRawEncoding(Tokens[TokIdx].int_data[1])); + + reprocess: + if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) { + // We have found a preprocessing directive. Annotate the tokens + // appropriately. + // + // FIXME: Some simple tests here could identify macro definitions and + // #undefs, to provide specific cursor kinds for those. + + SourceLocation BeginLoc = Tok.getLocation(); + if (lexNext(Lex, Tok, NextIdx, NumTokens)) + break; + + MacroInfo *MI = nullptr; + if (Tok.is(tok::raw_identifier) && Tok.getRawIdentifier() == "define") { + if (lexNext(Lex, Tok, NextIdx, NumTokens)) + break; + + if (Tok.is(tok::raw_identifier)) { + IdentifierInfo &II = + PP.getIdentifierTable().get(Tok.getRawIdentifier()); + SourceLocation MappedTokLoc = + CXXUnit->mapLocationToPreamble(Tok.getLocation()); + MI = getMacroInfo(II, MappedTokLoc, TU); + } + } + + bool finished = false; + do { + if (lexNext(Lex, Tok, NextIdx, NumTokens)) { + finished = true; + break; + } + // If we are in a macro definition, check if the token was ever a + // macro name and annotate it if that's the case. + if (MI) { + SourceLocation SaveLoc = Tok.getLocation(); + Tok.setLocation(CXXUnit->mapLocationToPreamble(SaveLoc)); + MacroDefinitionRecord *MacroDef = + checkForMacroInMacroDefinition(MI, Tok, TU); + Tok.setLocation(SaveLoc); + if (MacroDef) + Cursors[NextIdx - 1] = + MakeMacroExpansionCursor(MacroDef, Tok.getLocation(), TU); + } + } while (!Tok.isAtStartOfLine()); + + unsigned LastIdx = finished ? NextIdx-1 : NextIdx-2; + assert(TokIdx <= LastIdx); + SourceLocation EndLoc = + SourceLocation::getFromRawEncoding(Tokens[LastIdx].int_data[1]); + CXCursor Cursor = + MakePreprocessingDirectiveCursor(SourceRange(BeginLoc, EndLoc), TU); + + for (; TokIdx <= LastIdx; ++TokIdx) + updateCursorAnnotation(Cursors[TokIdx], Cursor); + + if (finished) + break; + goto reprocess; + } + } +} + +// This gets run a separate thread to avoid stack blowout. +static void clang_annotateTokensImpl(CXTranslationUnit TU, ASTUnit *CXXUnit, + CXToken *Tokens, unsigned NumTokens, + CXCursor *Cursors) { + CIndexer *CXXIdx = TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) + setThreadBackgroundPriority(); + + // Determine the region of interest, which contains all of the tokens. + SourceRange RegionOfInterest; + RegionOfInterest.setBegin( + cxloc::translateSourceLocation(clang_getTokenLocation(TU, Tokens[0]))); + RegionOfInterest.setEnd( + cxloc::translateSourceLocation(clang_getTokenLocation(TU, + Tokens[NumTokens-1]))); + + // Relex the tokens within the source range to look for preprocessing + // directives. + annotatePreprocessorTokens(TU, RegionOfInterest, Cursors, Tokens, NumTokens); + + // If begin location points inside a macro argument, set it to the expansion + // location so we can have the full context when annotating semantically. + { + SourceManager &SM = CXXUnit->getSourceManager(); + SourceLocation Loc = + SM.getMacroArgExpandedLocation(RegionOfInterest.getBegin()); + if (Loc.isMacroID()) + RegionOfInterest.setBegin(SM.getExpansionLoc(Loc)); + } + + if (CXXUnit->getPreprocessor().getPreprocessingRecord()) { + // Search and mark tokens that are macro argument expansions. + MarkMacroArgTokensVisitor Visitor(CXXUnit->getSourceManager(), + Tokens, NumTokens); + CursorVisitor MacroArgMarker(TU, + MarkMacroArgTokensVisitorDelegate, &Visitor, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + RegionOfInterest); + MacroArgMarker.visitPreprocessedEntitiesInRegion(); + } + + // Annotate all of the source locations in the region of interest that map to + // a specific cursor. + AnnotateTokensWorker W(Tokens, Cursors, NumTokens, TU, RegionOfInterest); + + // FIXME: We use a ridiculous stack size here because the data-recursion + // algorithm uses a large stack frame than the non-data recursive version, + // and AnnotationTokensWorker currently transforms the data-recursion + // algorithm back into a traditional recursion by explicitly calling + // VisitChildren(). We will need to remove this explicit recursive call. + W.AnnotateTokens(); + + // If we ran into any entities that involve context-sensitive keywords, + // take another pass through the tokens to mark them as such. + if (W.hasContextSensitiveKeywords()) { + for (unsigned I = 0; I != NumTokens; ++I) { + if (clang_getTokenKind(Tokens[I]) != CXToken_Identifier) + continue; + + if (Cursors[I].kind == CXCursor_ObjCPropertyDecl) { + IdentifierInfo *II = static_cast<IdentifierInfo *>(Tokens[I].ptr_data); + if (const ObjCPropertyDecl *Property + = dyn_cast_or_null<ObjCPropertyDecl>(getCursorDecl(Cursors[I]))) { + if (Property->getPropertyAttributesAsWritten() != 0 && + llvm::StringSwitch<bool>(II->getName()) + .Case("readonly", true) + .Case("assign", true) + .Case("unsafe_unretained", true) + .Case("readwrite", true) + .Case("retain", true) + .Case("copy", true) + .Case("nonatomic", true) + .Case("atomic", true) + .Case("getter", true) + .Case("setter", true) + .Case("strong", true) + .Case("weak", true) + .Case("class", true) + .Default(false)) + Tokens[I].int_data[0] = CXToken_Keyword; + } + continue; + } + + if (Cursors[I].kind == CXCursor_ObjCInstanceMethodDecl || + Cursors[I].kind == CXCursor_ObjCClassMethodDecl) { + IdentifierInfo *II = static_cast<IdentifierInfo *>(Tokens[I].ptr_data); + if (llvm::StringSwitch<bool>(II->getName()) + .Case("in", true) + .Case("out", true) + .Case("inout", true) + .Case("oneway", true) + .Case("bycopy", true) + .Case("byref", true) + .Default(false)) + Tokens[I].int_data[0] = CXToken_Keyword; + continue; + } + + if (Cursors[I].kind == CXCursor_CXXFinalAttr || + Cursors[I].kind == CXCursor_CXXOverrideAttr) { + Tokens[I].int_data[0] = CXToken_Keyword; + continue; + } + } + } +} + +void clang_annotateTokens(CXTranslationUnit TU, + CXToken *Tokens, unsigned NumTokens, + CXCursor *Cursors) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return; + } + if (NumTokens == 0 || !Tokens || !Cursors) { + LOG_FUNC_SECTION { *Log << "<null input>"; } + return; + } + + LOG_FUNC_SECTION { + *Log << TU << ' '; + CXSourceLocation bloc = clang_getTokenLocation(TU, Tokens[0]); + CXSourceLocation eloc = clang_getTokenLocation(TU, Tokens[NumTokens-1]); + *Log << clang_getRange(bloc, eloc); + } + + // Any token we don't specifically annotate will have a NULL cursor. + CXCursor C = clang_getNullCursor(); + for (unsigned I = 0; I != NumTokens; ++I) + Cursors[I] = C; + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + auto AnnotateTokensImpl = [=]() { + clang_annotateTokensImpl(TU, CXXUnit, Tokens, NumTokens, Cursors); + }; + llvm::CrashRecoveryContext CRC; + if (!RunSafely(CRC, AnnotateTokensImpl, GetSafetyThreadStackSize() * 2)) { + fprintf(stderr, "libclang: crash detected while annotating tokens\n"); + } +} + +//===----------------------------------------------------------------------===// +// Operations for querying linkage of a cursor. +//===----------------------------------------------------------------------===// + +CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { + if (!clang_isDeclaration(cursor.kind)) + return CXLinkage_Invalid; + + const Decl *D = cxcursor::getCursorDecl(cursor); + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) + switch (ND->getLinkageInternal()) { + case NoLinkage: + case VisibleNoLinkage: return CXLinkage_NoLinkage; + case ModuleInternalLinkage: + case InternalLinkage: return CXLinkage_Internal; + case UniqueExternalLinkage: return CXLinkage_UniqueExternal; + case ModuleLinkage: + case ExternalLinkage: return CXLinkage_External; + }; + + return CXLinkage_Invalid; +} + +//===----------------------------------------------------------------------===// +// Operations for querying visibility of a cursor. +//===----------------------------------------------------------------------===// + +CXVisibilityKind clang_getCursorVisibility(CXCursor cursor) { + if (!clang_isDeclaration(cursor.kind)) + return CXVisibility_Invalid; + + const Decl *D = cxcursor::getCursorDecl(cursor); + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) + switch (ND->getVisibility()) { + case HiddenVisibility: return CXVisibility_Hidden; + case ProtectedVisibility: return CXVisibility_Protected; + case DefaultVisibility: return CXVisibility_Default; + }; + + return CXVisibility_Invalid; +} + +//===----------------------------------------------------------------------===// +// Operations for querying language of a cursor. +//===----------------------------------------------------------------------===// + +static CXLanguageKind getDeclLanguage(const Decl *D) { + if (!D) + return CXLanguage_C; + + switch (D->getKind()) { + default: + break; + case Decl::ImplicitParam: + case Decl::ObjCAtDefsField: + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCCompatibleAlias: + case Decl::ObjCImplementation: + case Decl::ObjCInterface: + case Decl::ObjCIvar: + case Decl::ObjCMethod: + case Decl::ObjCProperty: + case Decl::ObjCPropertyImpl: + case Decl::ObjCProtocol: + case Decl::ObjCTypeParam: + return CXLanguage_ObjC; + case Decl::CXXConstructor: + case Decl::CXXConversion: + case Decl::CXXDestructor: + case Decl::CXXMethod: + case Decl::CXXRecord: + case Decl::ClassTemplate: + case Decl::ClassTemplatePartialSpecialization: + case Decl::ClassTemplateSpecialization: + case Decl::Friend: + case Decl::FriendTemplate: + case Decl::FunctionTemplate: + case Decl::LinkageSpec: + case Decl::Namespace: + case Decl::NamespaceAlias: + case Decl::NonTypeTemplateParm: + case Decl::StaticAssert: + case Decl::TemplateTemplateParm: + case Decl::TemplateTypeParm: + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + case Decl::Using: + case Decl::UsingDirective: + case Decl::UsingShadow: + return CXLanguage_CPlusPlus; + } + + return CXLanguage_C; +} + +static CXAvailabilityKind getCursorAvailabilityForDecl(const Decl *D) { + if (isa<FunctionDecl>(D) && cast<FunctionDecl>(D)->isDeleted()) + return CXAvailability_NotAvailable; + + switch (D->getAvailability()) { + case AR_Available: + case AR_NotYetIntroduced: + if (const EnumConstantDecl *EnumConst = dyn_cast<EnumConstantDecl>(D)) + return getCursorAvailabilityForDecl( + cast<Decl>(EnumConst->getDeclContext())); + return CXAvailability_Available; + + case AR_Deprecated: + return CXAvailability_Deprecated; + + case AR_Unavailable: + return CXAvailability_NotAvailable; + } + + llvm_unreachable("Unknown availability kind!"); +} + +enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + if (const Decl *D = cxcursor::getCursorDecl(cursor)) + return getCursorAvailabilityForDecl(D); + + return CXAvailability_Available; +} + +static CXVersion convertVersion(VersionTuple In) { + CXVersion Out = { -1, -1, -1 }; + if (In.empty()) + return Out; + + Out.Major = In.getMajor(); + + Optional<unsigned> Minor = In.getMinor(); + if (Minor.hasValue()) + Out.Minor = *Minor; + else + return Out; + + Optional<unsigned> Subminor = In.getSubminor(); + if (Subminor.hasValue()) + Out.Subminor = *Subminor; + + return Out; +} + +static void getCursorPlatformAvailabilityForDecl( + const Decl *D, int *always_deprecated, CXString *deprecated_message, + int *always_unavailable, CXString *unavailable_message, + SmallVectorImpl<AvailabilityAttr *> &AvailabilityAttrs) { + bool HadAvailAttr = false; + for (auto A : D->attrs()) { + if (DeprecatedAttr *Deprecated = dyn_cast<DeprecatedAttr>(A)) { + HadAvailAttr = true; + if (always_deprecated) + *always_deprecated = 1; + if (deprecated_message) { + clang_disposeString(*deprecated_message); + *deprecated_message = cxstring::createDup(Deprecated->getMessage()); + } + continue; + } + + if (UnavailableAttr *Unavailable = dyn_cast<UnavailableAttr>(A)) { + HadAvailAttr = true; + if (always_unavailable) + *always_unavailable = 1; + if (unavailable_message) { + clang_disposeString(*unavailable_message); + *unavailable_message = cxstring::createDup(Unavailable->getMessage()); + } + continue; + } + + if (AvailabilityAttr *Avail = dyn_cast<AvailabilityAttr>(A)) { + AvailabilityAttrs.push_back(Avail); + HadAvailAttr = true; + } + } + + if (!HadAvailAttr) + if (const EnumConstantDecl *EnumConst = dyn_cast<EnumConstantDecl>(D)) + return getCursorPlatformAvailabilityForDecl( + cast<Decl>(EnumConst->getDeclContext()), always_deprecated, + deprecated_message, always_unavailable, unavailable_message, + AvailabilityAttrs); + + if (AvailabilityAttrs.empty()) + return; + + llvm::sort(AvailabilityAttrs, + [](AvailabilityAttr *LHS, AvailabilityAttr *RHS) { + return LHS->getPlatform()->getName() < + RHS->getPlatform()->getName(); + }); + ASTContext &Ctx = D->getASTContext(); + auto It = std::unique( + AvailabilityAttrs.begin(), AvailabilityAttrs.end(), + [&Ctx](AvailabilityAttr *LHS, AvailabilityAttr *RHS) { + if (LHS->getPlatform() != RHS->getPlatform()) + return false; + + if (LHS->getIntroduced() == RHS->getIntroduced() && + LHS->getDeprecated() == RHS->getDeprecated() && + LHS->getObsoleted() == RHS->getObsoleted() && + LHS->getMessage() == RHS->getMessage() && + LHS->getReplacement() == RHS->getReplacement()) + return true; + + if ((!LHS->getIntroduced().empty() && !RHS->getIntroduced().empty()) || + (!LHS->getDeprecated().empty() && !RHS->getDeprecated().empty()) || + (!LHS->getObsoleted().empty() && !RHS->getObsoleted().empty())) + return false; + + if (LHS->getIntroduced().empty() && !RHS->getIntroduced().empty()) + LHS->setIntroduced(Ctx, RHS->getIntroduced()); + + if (LHS->getDeprecated().empty() && !RHS->getDeprecated().empty()) { + LHS->setDeprecated(Ctx, RHS->getDeprecated()); + if (LHS->getMessage().empty()) + LHS->setMessage(Ctx, RHS->getMessage()); + if (LHS->getReplacement().empty()) + LHS->setReplacement(Ctx, RHS->getReplacement()); + } + + if (LHS->getObsoleted().empty() && !RHS->getObsoleted().empty()) { + LHS->setObsoleted(Ctx, RHS->getObsoleted()); + if (LHS->getMessage().empty()) + LHS->setMessage(Ctx, RHS->getMessage()); + if (LHS->getReplacement().empty()) + LHS->setReplacement(Ctx, RHS->getReplacement()); + } + + return true; + }); + AvailabilityAttrs.erase(It, AvailabilityAttrs.end()); +} + +int clang_getCursorPlatformAvailability(CXCursor cursor, int *always_deprecated, + CXString *deprecated_message, + int *always_unavailable, + CXString *unavailable_message, + CXPlatformAvailability *availability, + int availability_size) { + if (always_deprecated) + *always_deprecated = 0; + if (deprecated_message) + *deprecated_message = cxstring::createEmpty(); + if (always_unavailable) + *always_unavailable = 0; + if (unavailable_message) + *unavailable_message = cxstring::createEmpty(); + + if (!clang_isDeclaration(cursor.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(cursor); + if (!D) + return 0; + + SmallVector<AvailabilityAttr *, 8> AvailabilityAttrs; + getCursorPlatformAvailabilityForDecl(D, always_deprecated, deprecated_message, + always_unavailable, unavailable_message, + AvailabilityAttrs); + for (const auto &Avail : + llvm::enumerate(llvm::makeArrayRef(AvailabilityAttrs) + .take_front(availability_size))) { + availability[Avail.index()].Platform = + cxstring::createDup(Avail.value()->getPlatform()->getName()); + availability[Avail.index()].Introduced = + convertVersion(Avail.value()->getIntroduced()); + availability[Avail.index()].Deprecated = + convertVersion(Avail.value()->getDeprecated()); + availability[Avail.index()].Obsoleted = + convertVersion(Avail.value()->getObsoleted()); + availability[Avail.index()].Unavailable = Avail.value()->getUnavailable(); + availability[Avail.index()].Message = + cxstring::createDup(Avail.value()->getMessage()); + } + + return AvailabilityAttrs.size(); +} + +void clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability) { + clang_disposeString(availability->Platform); + clang_disposeString(availability->Message); +} + +CXLanguageKind clang_getCursorLanguage(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + return getDeclLanguage(cxcursor::getCursorDecl(cursor)); + + return CXLanguage_Invalid; +} + +CXTLSKind clang_getCursorTLSKind(CXCursor cursor) { + const Decl *D = cxcursor::getCursorDecl(cursor); + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + switch (VD->getTLSKind()) { + case VarDecl::TLS_None: + return CXTLS_None; + case VarDecl::TLS_Dynamic: + return CXTLS_Dynamic; + case VarDecl::TLS_Static: + return CXTLS_Static; + } + } + + return CXTLS_None; +} + + /// If the given cursor is the "templated" declaration + /// describing a class or function template, return the class or + /// function template. +static const Decl *maybeGetTemplateCursor(const Decl *D) { + if (!D) + return nullptr; + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FunctionTemplateDecl *FunTmpl = FD->getDescribedFunctionTemplate()) + return FunTmpl; + + if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) + if (ClassTemplateDecl *ClassTmpl = RD->getDescribedClassTemplate()) + return ClassTmpl; + + return D; +} + + +enum CX_StorageClass clang_Cursor_getStorageClass(CXCursor C) { + StorageClass sc = SC_None; + const Decl *D = getCursorDecl(C); + if (D) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + sc = FD->getStorageClass(); + } else if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { + sc = VD->getStorageClass(); + } else { + return CX_SC_Invalid; + } + } else { + return CX_SC_Invalid; + } + switch (sc) { + case SC_None: + return CX_SC_None; + case SC_Extern: + return CX_SC_Extern; + case SC_Static: + return CX_SC_Static; + case SC_PrivateExtern: + return CX_SC_PrivateExtern; + case SC_Auto: + return CX_SC_Auto; + case SC_Register: + return CX_SC_Register; + } + llvm_unreachable("Unhandled storage class!"); +} + +CXCursor clang_getCursorSemanticParent(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) { + if (const Decl *D = getCursorDecl(cursor)) { + const DeclContext *DC = D->getDeclContext(); + if (!DC) + return clang_getNullCursor(); + + return MakeCXCursor(maybeGetTemplateCursor(cast<Decl>(DC)), + getCursorTU(cursor)); + } + } + + if (clang_isStatement(cursor.kind) || clang_isExpression(cursor.kind)) { + if (const Decl *D = getCursorDecl(cursor)) + return MakeCXCursor(D, getCursorTU(cursor)); + } + + return clang_getNullCursor(); +} + +CXCursor clang_getCursorLexicalParent(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) { + if (const Decl *D = getCursorDecl(cursor)) { + const DeclContext *DC = D->getLexicalDeclContext(); + if (!DC) + return clang_getNullCursor(); + + return MakeCXCursor(maybeGetTemplateCursor(cast<Decl>(DC)), + getCursorTU(cursor)); + } + } + + // FIXME: Note that we can't easily compute the lexical context of a + // statement or expression, so we return nothing. + return clang_getNullCursor(); +} + +CXFile clang_getIncludedFile(CXCursor cursor) { + if (cursor.kind != CXCursor_InclusionDirective) + return nullptr; + + const InclusionDirective *ID = getCursorInclusionDirective(cursor); + return const_cast<FileEntry *>(ID->getFile()); +} + +unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C, unsigned reserved) { + if (C.kind != CXCursor_ObjCPropertyDecl) + return CXObjCPropertyAttr_noattr; + + unsigned Result = CXObjCPropertyAttr_noattr; + const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(getCursorDecl(C)); + ObjCPropertyDecl::PropertyAttributeKind Attr = + PD->getPropertyAttributesAsWritten(); + +#define SET_CXOBJCPROP_ATTR(A) \ + if (Attr & ObjCPropertyDecl::OBJC_PR_##A) \ + Result |= CXObjCPropertyAttr_##A + SET_CXOBJCPROP_ATTR(readonly); + SET_CXOBJCPROP_ATTR(getter); + SET_CXOBJCPROP_ATTR(assign); + SET_CXOBJCPROP_ATTR(readwrite); + SET_CXOBJCPROP_ATTR(retain); + SET_CXOBJCPROP_ATTR(copy); + SET_CXOBJCPROP_ATTR(nonatomic); + SET_CXOBJCPROP_ATTR(setter); + SET_CXOBJCPROP_ATTR(atomic); + SET_CXOBJCPROP_ATTR(weak); + SET_CXOBJCPROP_ATTR(strong); + SET_CXOBJCPROP_ATTR(unsafe_unretained); + SET_CXOBJCPROP_ATTR(class); +#undef SET_CXOBJCPROP_ATTR + + return Result; +} + +CXString clang_Cursor_getObjCPropertyGetterName(CXCursor C) { + if (C.kind != CXCursor_ObjCPropertyDecl) + return cxstring::createNull(); + + const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(getCursorDecl(C)); + Selector sel = PD->getGetterName(); + if (sel.isNull()) + return cxstring::createNull(); + + return cxstring::createDup(sel.getAsString()); +} + +CXString clang_Cursor_getObjCPropertySetterName(CXCursor C) { + if (C.kind != CXCursor_ObjCPropertyDecl) + return cxstring::createNull(); + + const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(getCursorDecl(C)); + Selector sel = PD->getSetterName(); + if (sel.isNull()) + return cxstring::createNull(); + + return cxstring::createDup(sel.getAsString()); +} + +unsigned clang_Cursor_getObjCDeclQualifiers(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return CXObjCDeclQualifier_None; + + Decl::ObjCDeclQualifier QT = Decl::OBJC_TQ_None; + const Decl *D = getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + QT = MD->getObjCDeclQualifier(); + else if (const ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D)) + QT = PD->getObjCDeclQualifier(); + if (QT == Decl::OBJC_TQ_None) + return CXObjCDeclQualifier_None; + + unsigned Result = CXObjCDeclQualifier_None; + if (QT & Decl::OBJC_TQ_In) Result |= CXObjCDeclQualifier_In; + if (QT & Decl::OBJC_TQ_Inout) Result |= CXObjCDeclQualifier_Inout; + if (QT & Decl::OBJC_TQ_Out) Result |= CXObjCDeclQualifier_Out; + if (QT & Decl::OBJC_TQ_Bycopy) Result |= CXObjCDeclQualifier_Bycopy; + if (QT & Decl::OBJC_TQ_Byref) Result |= CXObjCDeclQualifier_Byref; + if (QT & Decl::OBJC_TQ_Oneway) Result |= CXObjCDeclQualifier_Oneway; + + return Result; +} + +unsigned clang_Cursor_isObjCOptional(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = getCursorDecl(C); + if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) + return PD->getPropertyImplementation() == ObjCPropertyDecl::Optional; + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->getImplementationControl() == ObjCMethodDecl::Optional; + + return 0; +} + +unsigned clang_Cursor_isVariadic(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = getCursorDecl(C); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + return FD->isVariadic(); + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->isVariadic(); + + return 0; +} + +unsigned clang_Cursor_isExternalSymbol(CXCursor C, + CXString *language, CXString *definedIn, + unsigned *isGenerated) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = getCursorDecl(C); + + if (auto *attr = D->getExternalSourceSymbolAttr()) { + if (language) + *language = cxstring::createDup(attr->getLanguage()); + if (definedIn) + *definedIn = cxstring::createDup(attr->getDefinedIn()); + if (isGenerated) + *isGenerated = attr->getGeneratedDeclaration(); + return 1; + } + return 0; +} + +CXSourceRange clang_Cursor_getCommentRange(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return clang_getNullRange(); + + const Decl *D = getCursorDecl(C); + ASTContext &Context = getCursorContext(C); + const RawComment *RC = Context.getRawCommentForAnyRedecl(D); + if (!RC) + return clang_getNullRange(); + + return cxloc::translateSourceRange(Context, RC->getSourceRange()); +} + +CXString clang_Cursor_getRawCommentText(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return cxstring::createNull(); + + const Decl *D = getCursorDecl(C); + ASTContext &Context = getCursorContext(C); + const RawComment *RC = Context.getRawCommentForAnyRedecl(D); + StringRef RawText = RC ? RC->getRawText(Context.getSourceManager()) : + StringRef(); + + // Don't duplicate the string because RawText points directly into source + // code. + return cxstring::createRef(RawText); +} + +CXString clang_Cursor_getBriefCommentText(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return cxstring::createNull(); + + const Decl *D = getCursorDecl(C); + const ASTContext &Context = getCursorContext(C); + const RawComment *RC = Context.getRawCommentForAnyRedecl(D); + + if (RC) { + StringRef BriefText = RC->getBriefText(Context); + + // Don't duplicate the string because RawComment ensures that this memory + // will not go away. + return cxstring::createRef(BriefText); + } + + return cxstring::createNull(); +} + +CXModule clang_Cursor_getModule(CXCursor C) { + if (C.kind == CXCursor_ModuleImportDecl) { + if (const ImportDecl *ImportD = + dyn_cast_or_null<ImportDecl>(getCursorDecl(C))) + return ImportD->getImportedModule(); + } + + return nullptr; +} + +CXModule clang_getModuleForFile(CXTranslationUnit TU, CXFile File) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return nullptr; + } + if (!File) + return nullptr; + FileEntry *FE = static_cast<FileEntry *>(File); + + ASTUnit &Unit = *cxtu::getASTUnit(TU); + HeaderSearch &HS = Unit.getPreprocessor().getHeaderSearchInfo(); + ModuleMap::KnownHeader Header = HS.findModuleForHeader(FE); + + return Header.getModule(); +} + +CXFile clang_Module_getASTFile(CXModule CXMod) { + if (!CXMod) + return nullptr; + Module *Mod = static_cast<Module*>(CXMod); + return const_cast<FileEntry *>(Mod->getASTFile()); +} + +CXModule clang_Module_getParent(CXModule CXMod) { + if (!CXMod) + return nullptr; + Module *Mod = static_cast<Module*>(CXMod); + return Mod->Parent; +} + +CXString clang_Module_getName(CXModule CXMod) { + if (!CXMod) + return cxstring::createEmpty(); + Module *Mod = static_cast<Module*>(CXMod); + return cxstring::createDup(Mod->Name); +} + +CXString clang_Module_getFullName(CXModule CXMod) { + if (!CXMod) + return cxstring::createEmpty(); + Module *Mod = static_cast<Module*>(CXMod); + return cxstring::createDup(Mod->getFullModuleName()); +} + +int clang_Module_isSystem(CXModule CXMod) { + if (!CXMod) + return 0; + Module *Mod = static_cast<Module*>(CXMod); + return Mod->IsSystem; +} + +unsigned clang_Module_getNumTopLevelHeaders(CXTranslationUnit TU, + CXModule CXMod) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return 0; + } + if (!CXMod) + return 0; + Module *Mod = static_cast<Module*>(CXMod); + FileManager &FileMgr = cxtu::getASTUnit(TU)->getFileManager(); + ArrayRef<const FileEntry *> TopHeaders = Mod->getTopHeaders(FileMgr); + return TopHeaders.size(); +} + +CXFile clang_Module_getTopLevelHeader(CXTranslationUnit TU, + CXModule CXMod, unsigned Index) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return nullptr; + } + if (!CXMod) + return nullptr; + Module *Mod = static_cast<Module*>(CXMod); + FileManager &FileMgr = cxtu::getASTUnit(TU)->getFileManager(); + + ArrayRef<const FileEntry *> TopHeaders = Mod->getTopHeaders(FileMgr); + if (Index < TopHeaders.size()) + return const_cast<FileEntry *>(TopHeaders[Index]); + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// C++ AST instrospection. +//===----------------------------------------------------------------------===// + +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; + + if (const auto D = cxcursor::getCursorDecl(C)) + if (const auto FD = dyn_cast_or_null<FieldDecl>(D)) + return FD->isMutable() ? 1 : 0; + return 0; +} + +unsigned clang_CXXMethod_isPureVirtual(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->isVirtual() && Method->isPure()) ? 1 : 0; +} + +unsigned clang_CXXMethod_isConst(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->getMethodQualifiers().hasConst()) ? 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; + + const Decl *D = cxcursor::getCursorDecl(C); + const CXXMethodDecl *Method = + D ? dyn_cast_or_null<CXXMethodDecl>(D->getAsFunction()) : nullptr; + return (Method && Method->isStatic()) ? 1 : 0; +} + +unsigned clang_CXXMethod_isVirtual(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->isVirtual()) ? 1 : 0; +} + +unsigned clang_CXXRecord_isAbstract(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const auto *D = cxcursor::getCursorDecl(C); + const auto *RD = dyn_cast_or_null<CXXRecordDecl>(D); + if (RD) + RD = RD->getDefinition(); + return (RD && RD->isAbstract()) ? 1 : 0; +} + +unsigned clang_EnumDecl_isScoped(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + const Decl *D = cxcursor::getCursorDecl(C); + auto *Enum = dyn_cast_or_null<EnumDecl>(D); + return (Enum && Enum->isScoped()) ? 1 : 0; +} + +//===----------------------------------------------------------------------===// +// Attribute introspection. +//===----------------------------------------------------------------------===// + +CXType clang_getIBOutletCollectionType(CXCursor C) { + if (C.kind != CXCursor_IBOutletCollectionAttr) + return cxtype::MakeCXType(QualType(), cxcursor::getCursorTU(C)); + + const IBOutletCollectionAttr *A = + cast<IBOutletCollectionAttr>(cxcursor::getCursorAttr(C)); + + return cxtype::MakeCXType(A->getInterface(), cxcursor::getCursorTU(C)); +} + +//===----------------------------------------------------------------------===// +// Inspecting memory usage. +//===----------------------------------------------------------------------===// + +typedef std::vector<CXTUResourceUsageEntry> MemUsageEntries; + +static inline void createCXTUResourceUsageEntry(MemUsageEntries &entries, + enum CXTUResourceUsageKind k, + unsigned long amount) { + CXTUResourceUsageEntry entry = { k, amount }; + entries.push_back(entry); +} + +const char *clang_getTUResourceUsageName(CXTUResourceUsageKind kind) { + const char *str = ""; + switch (kind) { + case CXTUResourceUsage_AST: + str = "ASTContext: expressions, declarations, and types"; + break; + case CXTUResourceUsage_Identifiers: + str = "ASTContext: identifiers"; + break; + case CXTUResourceUsage_Selectors: + str = "ASTContext: selectors"; + break; + case CXTUResourceUsage_GlobalCompletionResults: + str = "Code completion: cached global results"; + break; + case CXTUResourceUsage_SourceManagerContentCache: + str = "SourceManager: content cache allocator"; + break; + case CXTUResourceUsage_AST_SideTables: + str = "ASTContext: side tables"; + break; + case CXTUResourceUsage_SourceManager_Membuffer_Malloc: + str = "SourceManager: malloc'ed memory buffers"; + break; + case CXTUResourceUsage_SourceManager_Membuffer_MMap: + str = "SourceManager: mmap'ed memory buffers"; + break; + case CXTUResourceUsage_ExternalASTSource_Membuffer_Malloc: + str = "ExternalASTSource: malloc'ed memory buffers"; + break; + case CXTUResourceUsage_ExternalASTSource_Membuffer_MMap: + str = "ExternalASTSource: mmap'ed memory buffers"; + break; + case CXTUResourceUsage_Preprocessor: + str = "Preprocessor: malloc'ed memory"; + break; + case CXTUResourceUsage_PreprocessingRecord: + str = "Preprocessor: PreprocessingRecord"; + break; + case CXTUResourceUsage_SourceManager_DataStructures: + str = "SourceManager: data structures and tables"; + break; + case CXTUResourceUsage_Preprocessor_HeaderSearch: + str = "Preprocessor: header search tables"; + break; + } + return str; +} + +CXTUResourceUsage clang_getCXTUResourceUsage(CXTranslationUnit TU) { + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + CXTUResourceUsage usage = { (void*) nullptr, 0, nullptr }; + return usage; + } + + ASTUnit *astUnit = cxtu::getASTUnit(TU); + std::unique_ptr<MemUsageEntries> entries(new MemUsageEntries()); + ASTContext &astContext = astUnit->getASTContext(); + + // How much memory is used by AST nodes and types? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_AST, + (unsigned long) astContext.getASTAllocatedMemory()); + + // How much memory is used by identifiers? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_Identifiers, + (unsigned long) astContext.Idents.getAllocator().getTotalMemory()); + + // How much memory is used for selectors? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_Selectors, + (unsigned long) astContext.Selectors.getTotalMemory()); + + // How much memory is used by ASTContext's side tables? + createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_AST_SideTables, + (unsigned long) astContext.getSideTableAllocatedMemory()); + + // How much memory is used for caching global code completion results? + unsigned long completionBytes = 0; + if (GlobalCodeCompletionAllocator *completionAllocator = + astUnit->getCachedCompletionAllocator().get()) { + completionBytes = completionAllocator->getTotalMemory(); + } + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_GlobalCompletionResults, + completionBytes); + + // How much memory is being used by SourceManager's content cache? + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManagerContentCache, + (unsigned long) astContext.getSourceManager().getContentCacheSize()); + + // How much memory is being used by the MemoryBuffer's in SourceManager? + const SourceManager::MemoryBufferSizes &srcBufs = + astUnit->getSourceManager().getMemoryBufferSizes(); + + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManager_Membuffer_Malloc, + (unsigned long) srcBufs.malloc_bytes); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManager_Membuffer_MMap, + (unsigned long) srcBufs.mmap_bytes); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_SourceManager_DataStructures, + (unsigned long) astContext.getSourceManager() + .getDataStructureSizes()); + + // How much memory is being used by the ExternalASTSource? + if (ExternalASTSource *esrc = astContext.getExternalSource()) { + const ExternalASTSource::MemoryBufferSizes &sizes = + esrc->getMemoryBufferSizes(); + + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_ExternalASTSource_Membuffer_Malloc, + (unsigned long) sizes.malloc_bytes); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_ExternalASTSource_Membuffer_MMap, + (unsigned long) sizes.mmap_bytes); + } + + // How much memory is being used by the Preprocessor? + Preprocessor &pp = astUnit->getPreprocessor(); + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_Preprocessor, + pp.getTotalMemory()); + + if (PreprocessingRecord *pRec = pp.getPreprocessingRecord()) { + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_PreprocessingRecord, + pRec->getTotalMemory()); + } + + createCXTUResourceUsageEntry(*entries, + CXTUResourceUsage_Preprocessor_HeaderSearch, + pp.getHeaderSearchInfo().getTotalMemory()); + + CXTUResourceUsage usage = { (void*) entries.get(), + (unsigned) entries->size(), + !entries->empty() ? &(*entries)[0] : nullptr }; + (void)entries.release(); + return usage; +} + +void clang_disposeCXTUResourceUsage(CXTUResourceUsage usage) { + if (usage.data) + delete (MemUsageEntries*) usage.data; +} + +CXSourceRangeList *clang_getSkippedRanges(CXTranslationUnit TU, CXFile file) { + CXSourceRangeList *skipped = new CXSourceRangeList; + skipped->count = 0; + skipped->ranges = nullptr; + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return skipped; + } + + if (!file) + return skipped; + + ASTUnit *astUnit = cxtu::getASTUnit(TU); + PreprocessingRecord *ppRec = astUnit->getPreprocessor().getPreprocessingRecord(); + if (!ppRec) + return skipped; + + ASTContext &Ctx = astUnit->getASTContext(); + SourceManager &sm = Ctx.getSourceManager(); + FileEntry *fileEntry = static_cast<FileEntry *>(file); + FileID wantedFileID = sm.translateFile(fileEntry); + bool isMainFile = wantedFileID == sm.getMainFileID(); + + const std::vector<SourceRange> &SkippedRanges = ppRec->getSkippedRanges(); + std::vector<SourceRange> wantedRanges; + for (std::vector<SourceRange>::const_iterator i = SkippedRanges.begin(), ei = SkippedRanges.end(); + i != ei; ++i) { + if (sm.getFileID(i->getBegin()) == wantedFileID || sm.getFileID(i->getEnd()) == wantedFileID) + wantedRanges.push_back(*i); + else if (isMainFile && (astUnit->isInPreambleFileID(i->getBegin()) || astUnit->isInPreambleFileID(i->getEnd()))) + wantedRanges.push_back(*i); + } + + skipped->count = wantedRanges.size(); + skipped->ranges = new CXSourceRange[skipped->count]; + for (unsigned i = 0, ei = skipped->count; i != ei; ++i) + skipped->ranges[i] = cxloc::translateSourceRange(Ctx, wantedRanges[i]); + + return skipped; +} + +CXSourceRangeList *clang_getAllSkippedRanges(CXTranslationUnit TU) { + CXSourceRangeList *skipped = new CXSourceRangeList; + skipped->count = 0; + skipped->ranges = nullptr; + + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return skipped; + } + + ASTUnit *astUnit = cxtu::getASTUnit(TU); + PreprocessingRecord *ppRec = astUnit->getPreprocessor().getPreprocessingRecord(); + if (!ppRec) + return skipped; + + ASTContext &Ctx = astUnit->getASTContext(); + + const std::vector<SourceRange> &SkippedRanges = ppRec->getSkippedRanges(); + + skipped->count = SkippedRanges.size(); + skipped->ranges = new CXSourceRange[skipped->count]; + for (unsigned i = 0, ei = skipped->count; i != ei; ++i) + skipped->ranges[i] = cxloc::translateSourceRange(Ctx, SkippedRanges[i]); + + return skipped; +} + +void clang_disposeSourceRangeList(CXSourceRangeList *ranges) { + if (ranges) { + delete[] ranges->ranges; + delete ranges; + } +} + +void clang::PrintLibclangResourceUsage(CXTranslationUnit TU) { + CXTUResourceUsage Usage = clang_getCXTUResourceUsage(TU); + for (unsigned I = 0; I != Usage.numEntries; ++I) + fprintf(stderr, " %s: %lu\n", + clang_getTUResourceUsageName(Usage.entries[I].kind), + Usage.entries[I].amount); + + clang_disposeCXTUResourceUsage(Usage); +} + +//===----------------------------------------------------------------------===// +// Misc. utility functions. +//===----------------------------------------------------------------------===// + +/// Default to using our desired 8 MB stack size on "safety" threads. +static unsigned SafetyStackThreadSize = DesiredStackSize; + +namespace clang { + +bool RunSafely(llvm::CrashRecoveryContext &CRC, llvm::function_ref<void()> Fn, + unsigned Size) { + if (!Size) + Size = GetSafetyThreadStackSize(); + if (Size && !getenv("LIBCLANG_NOTHREADS")) + return CRC.RunSafelyOnThread(Fn, Size); + return CRC.RunSafely(Fn); +} + +unsigned GetSafetyThreadStackSize() { + return SafetyStackThreadSize; +} + +void SetSafetyThreadStackSize(unsigned Value) { + SafetyStackThreadSize = Value; +} + +} + +void clang::setThreadBackgroundPriority() { + if (getenv("LIBCLANG_BGPRIO_DISABLE")) + return; + +#if LLVM_ENABLE_THREADS + llvm::set_thread_priority(llvm::ThreadPriority::Background); +#endif +} + +void cxindex::printDiagsToStderr(ASTUnit *Unit) { + if (!Unit) + return; + + for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(), + DEnd = Unit->stored_diag_end(); + D != DEnd; ++D) { + CXStoredDiagnostic Diag(*D, Unit->getLangOpts()); + CXString Msg = clang_formatDiagnostic(&Diag, + clang_defaultDiagnosticDisplayOptions()); + fprintf(stderr, "%s\n", clang_getCString(Msg)); + clang_disposeString(Msg); + } +#ifdef _WIN32 + // On Windows, force a flush, since there may be multiple copies of + // stderr and stdout in the file system, all with different buffers + // but writing to the same device. + fflush(stderr); +#endif +} + +MacroInfo *cxindex::getMacroInfo(const IdentifierInfo &II, + SourceLocation MacroDefLoc, + CXTranslationUnit TU){ + if (MacroDefLoc.isInvalid() || !TU) + return nullptr; + if (!II.hadMacroDefinition()) + return nullptr; + + ASTUnit *Unit = cxtu::getASTUnit(TU); + Preprocessor &PP = Unit->getPreprocessor(); + MacroDirective *MD = PP.getLocalMacroDirectiveHistory(&II); + if (MD) { + for (MacroDirective::DefInfo + Def = MD->getDefinition(); Def; Def = Def.getPreviousDefinition()) { + if (MacroDefLoc == Def.getMacroInfo()->getDefinitionLoc()) + return Def.getMacroInfo(); + } + } + + return nullptr; +} + +const MacroInfo *cxindex::getMacroInfo(const MacroDefinitionRecord *MacroDef, + CXTranslationUnit TU) { + if (!MacroDef || !TU) + return nullptr; + const IdentifierInfo *II = MacroDef->getName(); + if (!II) + return nullptr; + + return getMacroInfo(*II, MacroDef->getLocation(), TU); +} + +MacroDefinitionRecord * +cxindex::checkForMacroInMacroDefinition(const MacroInfo *MI, const Token &Tok, + CXTranslationUnit TU) { + if (!MI || !TU) + return nullptr; + if (Tok.isNot(tok::raw_identifier)) + return nullptr; + + if (MI->getNumTokens() == 0) + return nullptr; + SourceRange DefRange(MI->getReplacementToken(0).getLocation(), + MI->getDefinitionEndLoc()); + ASTUnit *Unit = cxtu::getASTUnit(TU); + + // Check that the token is inside the definition and not its argument list. + SourceManager &SM = Unit->getSourceManager(); + if (SM.isBeforeInTranslationUnit(Tok.getLocation(), DefRange.getBegin())) + return nullptr; + if (SM.isBeforeInTranslationUnit(DefRange.getEnd(), Tok.getLocation())) + return nullptr; + + Preprocessor &PP = Unit->getPreprocessor(); + PreprocessingRecord *PPRec = PP.getPreprocessingRecord(); + if (!PPRec) + return nullptr; + + IdentifierInfo &II = PP.getIdentifierTable().get(Tok.getRawIdentifier()); + if (!II.hadMacroDefinition()) + return nullptr; + + // Check that the identifier is not one of the macro arguments. + if (std::find(MI->param_begin(), MI->param_end(), &II) != MI->param_end()) + return nullptr; + + MacroDirective *InnerMD = PP.getLocalMacroDirectiveHistory(&II); + if (!InnerMD) + return nullptr; + + return PPRec->findMacroDefinition(InnerMD->getMacroInfo()); +} + +MacroDefinitionRecord * +cxindex::checkForMacroInMacroDefinition(const MacroInfo *MI, SourceLocation Loc, + CXTranslationUnit TU) { + if (Loc.isInvalid() || !MI || !TU) + return nullptr; + + if (MI->getNumTokens() == 0) + return nullptr; + ASTUnit *Unit = cxtu::getASTUnit(TU); + Preprocessor &PP = Unit->getPreprocessor(); + if (!PP.getPreprocessingRecord()) + return nullptr; + Loc = Unit->getSourceManager().getSpellingLoc(Loc); + Token Tok; + if (PP.getRawToken(Loc, Tok)) + return nullptr; + + return checkForMacroInMacroDefinition(MI, Tok, TU); +} + +CXString clang_getClangVersion() { + return cxstring::createDup(getClangFullVersion()); +} + +Logger &cxindex::Logger::operator<<(CXTranslationUnit TU) { + if (TU) { + if (ASTUnit *Unit = cxtu::getASTUnit(TU)) { + LogOS << '<' << Unit->getMainFileName() << '>'; + if (Unit->isMainFileAST()) + LogOS << " (" << Unit->getASTFileName() << ')'; + return *this; + } + } else { + LogOS << "<NULL TU>"; + } + return *this; +} + +Logger &cxindex::Logger::operator<<(const FileEntry *FE) { + *this << FE->getName(); + return *this; +} + +Logger &cxindex::Logger::operator<<(CXCursor cursor) { + CXString cursorName = clang_getCursorDisplayName(cursor); + *this << cursorName << "@" << clang_getCursorLocation(cursor); + clang_disposeString(cursorName); + return *this; +} + +Logger &cxindex::Logger::operator<<(CXSourceLocation Loc) { + CXFile File; + unsigned Line, Column; + clang_getFileLocation(Loc, &File, &Line, &Column, nullptr); + CXString FileName = clang_getFileName(File); + *this << llvm::format("(%s:%d:%d)", clang_getCString(FileName), Line, Column); + clang_disposeString(FileName); + return *this; +} + +Logger &cxindex::Logger::operator<<(CXSourceRange range) { + CXSourceLocation BLoc = clang_getRangeStart(range); + CXSourceLocation ELoc = clang_getRangeEnd(range); + + CXFile BFile; + unsigned BLine, BColumn; + clang_getFileLocation(BLoc, &BFile, &BLine, &BColumn, nullptr); + + CXFile EFile; + unsigned ELine, EColumn; + clang_getFileLocation(ELoc, &EFile, &ELine, &EColumn, nullptr); + + CXString BFileName = clang_getFileName(BFile); + if (BFile == EFile) { + *this << llvm::format("[%s %d:%d-%d:%d]", clang_getCString(BFileName), + BLine, BColumn, ELine, EColumn); + } else { + CXString EFileName = clang_getFileName(EFile); + *this << llvm::format("[%s:%d:%d - ", clang_getCString(BFileName), + BLine, BColumn) + << llvm::format("%s:%d:%d]", clang_getCString(EFileName), + ELine, EColumn); + clang_disposeString(EFileName); + } + clang_disposeString(BFileName); + return *this; +} + +Logger &cxindex::Logger::operator<<(CXString Str) { + *this << clang_getCString(Str); + return *this; +} + +Logger &cxindex::Logger::operator<<(const llvm::format_object_base &Fmt) { + LogOS << Fmt; + return *this; +} + +static llvm::ManagedStatic<std::mutex> LoggingMutex; + +cxindex::Logger::~Logger() { + std::lock_guard<std::mutex> L(*LoggingMutex); + + static llvm::TimeRecord sBeginTR = llvm::TimeRecord::getCurrentTime(); + + raw_ostream &OS = llvm::errs(); + OS << "[libclang:" << Name << ':'; + +#ifdef USE_DARWIN_THREADS + // TODO: Portability. + mach_port_t tid = pthread_mach_thread_np(pthread_self()); + OS << tid << ':'; +#endif + + llvm::TimeRecord TR = llvm::TimeRecord::getCurrentTime(); + OS << llvm::format("%7.4f] ", TR.getWallTime() - sBeginTR.getWallTime()); + OS << Msg << '\n'; + + if (Trace) { + llvm::sys::PrintStackTrace(OS); + 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; + +// This anchor is used to force the linker to link the clang-include-fixer +// plugin. +extern volatile int ClangIncludeFixerPluginAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED ClangIncludeFixerPluginAnchorDestination = + ClangIncludeFixerPluginAnchorSource; +#endif diff --git a/gnu/llvm/clang/tools/libclang/CIndexCXX.cpp b/gnu/llvm/clang/tools/libclang/CIndexCXX.cpp new file mode 100644 index 00000000000..a06fe7ba4af --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexCXX.cpp @@ -0,0 +1,122 @@ +//===- CIndexCXX.cpp - Clang-C Source Indexing Library --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the libclang support for C++ cursors. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "CXType.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" + +using namespace clang; +using namespace clang::cxcursor; + +unsigned clang_isVirtualBase(CXCursor C) { + if (C.kind != CXCursor_CXXBaseSpecifier) + return 0; + + const CXXBaseSpecifier *B = getCursorCXXBaseSpecifier(C); + return B->isVirtual(); +} + +enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor C) { + AccessSpecifier spec = AS_none; + + if (C.kind == CXCursor_CXXAccessSpecifier || clang_isDeclaration(C.kind)) + spec = getCursorDecl(C)->getAccess(); + else if (C.kind == CXCursor_CXXBaseSpecifier) + spec = getCursorCXXBaseSpecifier(C)->getAccessSpecifier(); + else + return CX_CXXInvalidAccessSpecifier; + + switch (spec) { + case AS_public: return CX_CXXPublic; + case AS_protected: return CX_CXXProtected; + case AS_private: return CX_CXXPrivate; + case AS_none: return CX_CXXInvalidAccessSpecifier; + } + + llvm_unreachable("Invalid AccessSpecifier!"); +} + +enum CXCursorKind clang_getTemplateCursorKind(CXCursor C) { + using namespace clang::cxcursor; + + switch (C.kind) { + case CXCursor_ClassTemplate: + case CXCursor_FunctionTemplate: + if (const TemplateDecl *Template + = dyn_cast_or_null<TemplateDecl>(getCursorDecl(C))) + return MakeCXCursor(Template->getTemplatedDecl(), getCursorTU(C)).kind; + break; + + case CXCursor_ClassTemplatePartialSpecialization: + if (const ClassTemplateSpecializationDecl *PartialSpec + = dyn_cast_or_null<ClassTemplatePartialSpecializationDecl>( + getCursorDecl(C))) { + switch (PartialSpec->getTagKind()) { + case TTK_Interface: + case TTK_Struct: return CXCursor_StructDecl; + case TTK_Class: return CXCursor_ClassDecl; + case TTK_Union: return CXCursor_UnionDecl; + case TTK_Enum: return CXCursor_NoDeclFound; + } + } + break; + + default: + break; + } + + return CXCursor_NoDeclFound; +} + +CXCursor clang_getSpecializedCursorTemplate(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return clang_getNullCursor(); + + const Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + + Decl *Template = nullptr; + if (const CXXRecordDecl *CXXRecord = dyn_cast<CXXRecordDecl>(D)) { + if (const ClassTemplatePartialSpecializationDecl *PartialSpec + = dyn_cast<ClassTemplatePartialSpecializationDecl>(CXXRecord)) + Template = PartialSpec->getSpecializedTemplate(); + else if (const ClassTemplateSpecializationDecl *ClassSpec + = dyn_cast<ClassTemplateSpecializationDecl>(CXXRecord)) { + llvm::PointerUnion<ClassTemplateDecl *, + ClassTemplatePartialSpecializationDecl *> Result + = ClassSpec->getSpecializedTemplateOrPartial(); + if (Result.is<ClassTemplateDecl *>()) + Template = Result.get<ClassTemplateDecl *>(); + else + Template = Result.get<ClassTemplatePartialSpecializationDecl *>(); + + } else + Template = CXXRecord->getInstantiatedFromMemberClass(); + } else if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) { + Template = Function->getPrimaryTemplate(); + if (!Template) + Template = Function->getInstantiatedFromMemberFunction(); + } else if (const VarDecl *Var = dyn_cast<VarDecl>(D)) { + if (Var->isStaticDataMember()) + Template = Var->getInstantiatedFromStaticDataMember(); + } else if (const RedeclarableTemplateDecl *Tmpl + = dyn_cast<RedeclarableTemplateDecl>(D)) + Template = Tmpl->getInstantiatedFromMemberTemplate(); + + if (!Template) + return clang_getNullCursor(); + + return MakeCXCursor(Template, getCursorTU(C)); +} diff --git a/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp b/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp new file mode 100644 index 00000000000..1311f66ce0b --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexCodeCompletion.cpp @@ -0,0 +1,1043 @@ +//===- CIndexCodeCompletion.cpp - Code Completion API hooks ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Clang-C Source Indexing library hooks for +// code completion. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CIndexDiagnostic.h" +#include "CLog.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <atomic> +#include <cstdio> +#include <cstdlib> +#include <string> + + +#ifdef UDP_CODE_COMPLETION_LOGGER +#include "clang/Basic/Version.h" +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +using namespace clang; +using namespace clang::cxindex; + +enum CXCompletionChunkKind +clang_getCompletionChunkKind(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return CXCompletionChunk_Text; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + return CXCompletionChunk_TypedText; + case CodeCompletionString::CK_Text: + return CXCompletionChunk_Text; + case CodeCompletionString::CK_Optional: + return CXCompletionChunk_Optional; + case CodeCompletionString::CK_Placeholder: + return CXCompletionChunk_Placeholder; + case CodeCompletionString::CK_Informative: + return CXCompletionChunk_Informative; + case CodeCompletionString::CK_ResultType: + return CXCompletionChunk_ResultType; + case CodeCompletionString::CK_CurrentParameter: + return CXCompletionChunk_CurrentParameter; + case CodeCompletionString::CK_LeftParen: + return CXCompletionChunk_LeftParen; + case CodeCompletionString::CK_RightParen: + return CXCompletionChunk_RightParen; + case CodeCompletionString::CK_LeftBracket: + return CXCompletionChunk_LeftBracket; + case CodeCompletionString::CK_RightBracket: + return CXCompletionChunk_RightBracket; + case CodeCompletionString::CK_LeftBrace: + return CXCompletionChunk_LeftBrace; + case CodeCompletionString::CK_RightBrace: + return CXCompletionChunk_RightBrace; + case CodeCompletionString::CK_LeftAngle: + return CXCompletionChunk_LeftAngle; + case CodeCompletionString::CK_RightAngle: + return CXCompletionChunk_RightAngle; + case CodeCompletionString::CK_Comma: + return CXCompletionChunk_Comma; + case CodeCompletionString::CK_Colon: + return CXCompletionChunk_Colon; + case CodeCompletionString::CK_SemiColon: + return CXCompletionChunk_SemiColon; + case CodeCompletionString::CK_Equal: + return CXCompletionChunk_Equal; + case CodeCompletionString::CK_HorizontalSpace: + return CXCompletionChunk_HorizontalSpace; + case CodeCompletionString::CK_VerticalSpace: + return CXCompletionChunk_VerticalSpace; + } + + llvm_unreachable("Invalid CompletionKind!"); +} + +CXString clang_getCompletionChunkText(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return cxstring::createNull(); + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + case CodeCompletionString::CK_ResultType: + case CodeCompletionString::CK_Colon: + case CodeCompletionString::CK_SemiColon: + case CodeCompletionString::CK_Equal: + case CodeCompletionString::CK_HorizontalSpace: + case CodeCompletionString::CK_VerticalSpace: + return cxstring::createRef((*CCStr)[chunk_number].Text); + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return cxstring::createEmpty(); + } + + llvm_unreachable("Invalid CodeCompletionString Kind!"); +} + + +CXCompletionString +clang_getCompletionChunkCompletionString(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return nullptr; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + case CodeCompletionString::CK_ResultType: + case CodeCompletionString::CK_Colon: + case CodeCompletionString::CK_SemiColon: + case CodeCompletionString::CK_Equal: + case CodeCompletionString::CK_HorizontalSpace: + case CodeCompletionString::CK_VerticalSpace: + return nullptr; + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return (*CCStr)[chunk_number].Optional; + } + + llvm_unreachable("Invalid CompletionKind!"); +} + +unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? CCStr->size() : 0; +} + +unsigned clang_getCompletionPriority(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? CCStr->getPriority() : unsigned(CCP_Unlikely); +} + +enum CXAvailabilityKind +clang_getCompletionAvailability(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? static_cast<CXAvailabilityKind>(CCStr->getAvailability()) + : CXAvailability_Available; +} + +unsigned clang_getCompletionNumAnnotations(CXCompletionString completion_string) +{ + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr ? CCStr->getAnnotationCount() : 0; +} + +CXString clang_getCompletionAnnotation(CXCompletionString completion_string, + unsigned annotation_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr ? cxstring::createRef(CCStr->getAnnotation(annotation_number)) + : cxstring::createNull(); +} + +CXString +clang_getCompletionParent(CXCompletionString completion_string, + CXCursorKind *kind) { + if (kind) + *kind = CXCursor_NotImplemented; + + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr) + return cxstring::createNull(); + + return cxstring::createRef(CCStr->getParentContextName()); +} + +CXString +clang_getCompletionBriefComment(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + + if (!CCStr) + return cxstring::createNull(); + + return cxstring::createRef(CCStr->getBriefComment()); +} + +namespace { + +/// The CXCodeCompleteResults structure we allocate internally; +/// the client only sees the initial CXCodeCompleteResults structure. +/// +/// Normally, clients of CXString shouldn't care whether or not a CXString is +/// managed by a pool or by explicitly malloc'ed memory. But +/// AllocatedCXCodeCompleteResults outlives the CXTranslationUnit, so we can +/// not rely on the StringPool in the TU. +struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { + AllocatedCXCodeCompleteResults(IntrusiveRefCntPtr<FileManager> FileMgr); + ~AllocatedCXCodeCompleteResults(); + + /// Diagnostics produced while performing code completion. + SmallVector<StoredDiagnostic, 8> Diagnostics; + + /// Allocated API-exposed wrappters for Diagnostics. + SmallVector<CXStoredDiagnostic *, 8> DiagnosticsWrappers; + + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + + /// Diag object + IntrusiveRefCntPtr<DiagnosticsEngine> Diag; + + /// Language options used to adjust source locations. + LangOptions LangOpts; + + /// File manager, used for diagnostics. + IntrusiveRefCntPtr<FileManager> FileMgr; + + /// Source manager, used for diagnostics. + IntrusiveRefCntPtr<SourceManager> SourceMgr; + + /// Temporary buffers that will be deleted once we have finished with + /// the code-completion results. + SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers; + + /// Allocator used to store globally cached code-completion results. + std::shared_ptr<clang::GlobalCodeCompletionAllocator> + CachedCompletionAllocator; + + /// Allocator used to store code completion results. + std::shared_ptr<clang::GlobalCodeCompletionAllocator> CodeCompletionAllocator; + + /// Context under which completion occurred. + enum clang::CodeCompletionContext::Kind ContextKind; + + /// A bitfield representing the acceptable completions for the + /// current context. + unsigned long long Contexts; + + /// The kind of the container for the current context for completions. + enum CXCursorKind ContainerKind; + + /// The USR of the container for the current context for completions. + std::string ContainerUSR; + + /// a boolean value indicating whether there is complete information + /// about the container + unsigned ContainerIsIncomplete; + + /// A string containing the Objective-C selector entered thus far for a + /// message send. + std::string Selector; + + /// Vector of fix-its for each completion result that *must* be applied + /// before that result for the corresponding completion item. + std::vector<std::vector<FixItHint>> FixItsVector; +}; + +} // end anonymous namespace + +unsigned clang_getCompletionNumFixIts(CXCodeCompleteResults *results, + unsigned completion_index) { + AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results; + + if (!allocated_results || allocated_results->FixItsVector.size() <= completion_index) + return 0; + + return static_cast<unsigned>(allocated_results->FixItsVector[completion_index].size()); +} + +CXString clang_getCompletionFixIt(CXCodeCompleteResults *results, + unsigned completion_index, + unsigned fixit_index, + CXSourceRange *replacement_range) { + AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results; + + if (!allocated_results || allocated_results->FixItsVector.size() <= completion_index) { + if (replacement_range) + *replacement_range = clang_getNullRange(); + return cxstring::createNull(); + } + + ArrayRef<FixItHint> FixIts = allocated_results->FixItsVector[completion_index]; + if (FixIts.size() <= fixit_index) { + if (replacement_range) + *replacement_range = clang_getNullRange(); + return cxstring::createNull(); + } + + const FixItHint &FixIt = FixIts[fixit_index]; + if (replacement_range) { + *replacement_range = cxloc::translateSourceRange( + *allocated_results->SourceMgr, allocated_results->LangOpts, + FixIt.RemoveRange); + } + + return cxstring::createRef(FixIt.CodeToInsert.c_str()); +} + +/// Tracks the number of code-completion result objects that are +/// currently active. +/// +/// Used for debugging purposes only. +static std::atomic<unsigned> CodeCompletionResultObjects; + +AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults( + IntrusiveRefCntPtr<FileManager> FileMgr) + : CXCodeCompleteResults(), DiagOpts(new DiagnosticOptions), + Diag(new DiagnosticsEngine( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts)), + FileMgr(std::move(FileMgr)), + SourceMgr(new SourceManager(*Diag, *this->FileMgr)), + CodeCompletionAllocator( + std::make_shared<clang::GlobalCodeCompletionAllocator>()), + Contexts(CXCompletionContext_Unknown), + ContainerKind(CXCursor_InvalidCode), ContainerIsIncomplete(1) { + if (getenv("LIBCLANG_OBJTRACKING")) + fprintf(stderr, "+++ %u completion results\n", + ++CodeCompletionResultObjects); +} + +AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { + llvm::DeleteContainerPointers(DiagnosticsWrappers); + delete [] Results; + + for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I) + delete TemporaryBuffers[I]; + + if (getenv("LIBCLANG_OBJTRACKING")) + fprintf(stderr, "--- %u completion results\n", + --CodeCompletionResultObjects); +} + +static unsigned long long getContextsForContextKind( + enum CodeCompletionContext::Kind kind, + Sema &S) { + unsigned long long contexts = 0; + switch (kind) { + case CodeCompletionContext::CCC_OtherWithMacros: { + //We can allow macros here, but we don't know what else is permissible + //So we'll say the only thing permissible are macros + contexts = CXCompletionContext_MacroName; + break; + } + case CodeCompletionContext::CCC_TopLevel: + case CodeCompletionContext::CCC_ObjCIvarList: + case CodeCompletionContext::CCC_ClassStructUnion: + case CodeCompletionContext::CCC_Type: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_Statement: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_AnyValue; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_Expression: { + contexts = CXCompletionContext_AnyValue; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_ObjCMessageReceiver: { + contexts = CXCompletionContext_ObjCObjectValue | + CXCompletionContext_ObjCSelectorValue | + CXCompletionContext_ObjCInterface; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_CXXClassTypeValue | + CXCompletionContext_AnyType | + CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_DotMemberAccess: { + contexts = CXCompletionContext_DotMemberAccess; + break; + } + case CodeCompletionContext::CCC_ArrowMemberAccess: { + contexts = CXCompletionContext_ArrowMemberAccess; + break; + } + case CodeCompletionContext::CCC_ObjCPropertyAccess: { + contexts = CXCompletionContext_ObjCPropertyAccess; + break; + } + case CodeCompletionContext::CCC_EnumTag: { + contexts = CXCompletionContext_EnumTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_UnionTag: { + contexts = CXCompletionContext_UnionTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_ClassOrStructTag: { + contexts = CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_ObjCProtocolName: { + contexts = CXCompletionContext_ObjCProtocol; + break; + } + case CodeCompletionContext::CCC_Namespace: { + contexts = CXCompletionContext_Namespace; + break; + } + case CodeCompletionContext::CCC_SymbolOrNewName: + case CodeCompletionContext::CCC_Symbol: { + contexts = CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_MacroNameUse: { + contexts = CXCompletionContext_MacroName; + break; + } + case CodeCompletionContext::CCC_NaturalLanguage: { + contexts = CXCompletionContext_NaturalLanguage; + break; + } + case CodeCompletionContext::CCC_IncludedFile: { + contexts = CXCompletionContext_IncludedFile; + break; + } + case CodeCompletionContext::CCC_SelectorName: { + contexts = CXCompletionContext_ObjCSelectorName; + break; + } + case CodeCompletionContext::CCC_ParenthesizedExpression: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_AnyValue; + if (S.getLangOpts().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_ObjCInstanceMessage: { + contexts = CXCompletionContext_ObjCInstanceMessage; + break; + } + case CodeCompletionContext::CCC_ObjCClassMessage: { + contexts = CXCompletionContext_ObjCClassMessage; + break; + } + case CodeCompletionContext::CCC_ObjCInterfaceName: { + contexts = CXCompletionContext_ObjCInterface; + break; + } + case CodeCompletionContext::CCC_ObjCCategoryName: { + contexts = CXCompletionContext_ObjCCategory; + break; + } + case CodeCompletionContext::CCC_Other: + case CodeCompletionContext::CCC_ObjCInterface: + case CodeCompletionContext::CCC_ObjCImplementation: + case CodeCompletionContext::CCC_NewName: + case CodeCompletionContext::CCC_MacroName: + case CodeCompletionContext::CCC_PreprocessorExpression: + case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_TypeQualifiers: { + //Only Clang results should be accepted, so we'll set all of the other + //context bits to 0 (i.e. the empty set) + contexts = CXCompletionContext_Unexposed; + break; + } + case CodeCompletionContext::CCC_Recovery: { + //We don't know what the current context is, so we'll return unknown + //This is the equivalent of setting all of the other context bits + contexts = CXCompletionContext_Unknown; + break; + } + } + return contexts; +} + +namespace { + class CaptureCompletionResults : public CodeCompleteConsumer { + AllocatedCXCodeCompleteResults &AllocatedResults; + CodeCompletionTUInfo CCTUInfo; + SmallVector<CXCompletionResult, 16> StoredResults; + CXTranslationUnit *TU; + public: + CaptureCompletionResults(const CodeCompleteOptions &Opts, + AllocatedCXCodeCompleteResults &Results, + CXTranslationUnit *TranslationUnit) + : CodeCompleteConsumer(Opts), AllocatedResults(Results), + CCTUInfo(Results.CodeCompletionAllocator), TU(TranslationUnit) {} + ~CaptureCompletionResults() override { Finish(); } + + void ProcessCodeCompleteResults(Sema &S, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) override { + StoredResults.reserve(StoredResults.size() + NumResults); + if (includeFixIts()) + AllocatedResults.FixItsVector.reserve(NumResults); + for (unsigned I = 0; I != NumResults; ++I) { + CodeCompletionString *StoredCompletion + = Results[I].CreateCodeCompletionString(S, Context, getAllocator(), + getCodeCompletionTUInfo(), + includeBriefComments()); + + CXCompletionResult R; + R.CursorKind = Results[I].CursorKind; + R.CompletionString = StoredCompletion; + StoredResults.push_back(R); + if (includeFixIts()) + AllocatedResults.FixItsVector.emplace_back(std::move(Results[I].FixIts)); + } + + enum CodeCompletionContext::Kind contextKind = Context.getKind(); + + AllocatedResults.ContextKind = contextKind; + AllocatedResults.Contexts = getContextsForContextKind(contextKind, S); + + AllocatedResults.Selector = ""; + ArrayRef<IdentifierInfo *> SelIdents = Context.getSelIdents(); + for (ArrayRef<IdentifierInfo *>::iterator I = SelIdents.begin(), + E = SelIdents.end(); + I != E; ++I) { + if (IdentifierInfo *selIdent = *I) + AllocatedResults.Selector += selIdent->getName(); + AllocatedResults.Selector += ":"; + } + + QualType baseType = Context.getBaseType(); + NamedDecl *D = nullptr; + + if (!baseType.isNull()) { + // Get the declaration for a class/struct/union/enum type + if (const TagType *Tag = baseType->getAs<TagType>()) + D = Tag->getDecl(); + // Get the @interface declaration for a (possibly-qualified) Objective-C + // object pointer type, e.g., NSString* + else if (const ObjCObjectPointerType *ObjPtr = + baseType->getAs<ObjCObjectPointerType>()) + D = ObjPtr->getInterfaceDecl(); + // Get the @interface declaration for an Objective-C object type + else if (const ObjCObjectType *Obj = baseType->getAs<ObjCObjectType>()) + D = Obj->getInterface(); + // Get the class for a C++ injected-class-name + else if (const InjectedClassNameType *Injected = + baseType->getAs<InjectedClassNameType>()) + D = Injected->getDecl(); + } + + if (D != nullptr) { + CXCursor cursor = cxcursor::MakeCXCursor(D, *TU); + + AllocatedResults.ContainerKind = clang_getCursorKind(cursor); + + CXString CursorUSR = clang_getCursorUSR(cursor); + AllocatedResults.ContainerUSR = clang_getCString(CursorUSR); + clang_disposeString(CursorUSR); + + const Type *type = baseType.getTypePtrOrNull(); + if (type) { + AllocatedResults.ContainerIsIncomplete = type->isIncompleteType(); + } + else { + AllocatedResults.ContainerIsIncomplete = 1; + } + } + else { + AllocatedResults.ContainerKind = CXCursor_InvalidCode; + AllocatedResults.ContainerUSR.clear(); + AllocatedResults.ContainerIsIncomplete = 1; + } + } + + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates, + SourceLocation OpenParLoc) override { + StoredResults.reserve(StoredResults.size() + NumCandidates); + for (unsigned I = 0; I != NumCandidates; ++I) { + CodeCompletionString *StoredCompletion + = Candidates[I].CreateSignatureString(CurrentArg, S, getAllocator(), + getCodeCompletionTUInfo(), + includeBriefComments()); + + CXCompletionResult R; + R.CursorKind = CXCursor_OverloadCandidate; + R.CompletionString = StoredCompletion; + StoredResults.push_back(R); + } + } + + CodeCompletionAllocator &getAllocator() override { + return *AllocatedResults.CodeCompletionAllocator; + } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo;} + + private: + void Finish() { + AllocatedResults.Results = new CXCompletionResult [StoredResults.size()]; + AllocatedResults.NumResults = StoredResults.size(); + std::memcpy(AllocatedResults.Results, StoredResults.data(), + StoredResults.size() * sizeof(CXCompletionResult)); + StoredResults.clear(); + } + }; +} + +static CXCodeCompleteResults * +clang_codeCompleteAt_Impl(CXTranslationUnit TU, const char *complete_filename, + unsigned complete_line, unsigned complete_column, + ArrayRef<CXUnsavedFile> unsaved_files, + unsigned options) { + bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments; + bool SkipPreamble = options & CXCodeComplete_SkipPreamble; + bool IncludeFixIts = options & CXCodeComplete_IncludeCompletionsWithFixIts; + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); +#endif +#endif + bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != nullptr; + + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return nullptr; + } + + ASTUnit *AST = cxtu::getASTUnit(TU); + if (!AST) + return nullptr; + + CIndexer *CXXIdx = TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) + setThreadBackgroundPriority(); + + ASTUnit::ConcurrencyCheck Check(*AST); + + // Perform the remapping of source files. + SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; + + for (auto &UF : unsaved_files) { + std::unique_ptr<llvm::MemoryBuffer> MB = + llvm::MemoryBuffer::getMemBufferCopy(getContents(UF), UF.Filename); + RemappedFiles.push_back(std::make_pair(UF.Filename, MB.release())); + } + + if (EnableLogging) { + // FIXME: Add logging. + } + + // Parse the resulting source file to find code-completion results. + AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults( + &AST->getFileManager()); + Results->Results = nullptr; + Results->NumResults = 0; + + // Create a code-completion consumer to capture the results. + CodeCompleteOptions Opts; + Opts.IncludeBriefComments = IncludeBriefComments; + Opts.LoadExternal = !SkipPreamble; + Opts.IncludeFixIts = IncludeFixIts; + CaptureCompletionResults Capture(Opts, *Results, &TU); + + // Perform completion. + std::vector<const char *> CArgs; + for (const auto &Arg : TU->Arguments) + CArgs.push_back(Arg.c_str()); + std::string CompletionInvocation = + llvm::formatv("-code-completion-at={0}:{1}:{2}", complete_filename, + complete_line, complete_column) + .str(); + LibclangInvocationReporter InvocationReporter( + *CXXIdx, LibclangInvocationReporter::OperationKind::CompletionOperation, + TU->ParsingOptions, CArgs, CompletionInvocation, unsaved_files); + AST->CodeComplete(complete_filename, complete_line, complete_column, + RemappedFiles, (options & CXCodeComplete_IncludeMacros), + (options & CXCodeComplete_IncludeCodePatterns), + IncludeBriefComments, Capture, + CXXIdx->getPCHContainerOperations(), *Results->Diag, + Results->LangOpts, *Results->SourceMgr, *Results->FileMgr, + Results->Diagnostics, Results->TemporaryBuffers); + + Results->DiagnosticsWrappers.resize(Results->Diagnostics.size()); + + // Keep a reference to the allocator used for cached global completions, so + // that we can be sure that the memory used by our code completion strings + // doesn't get freed due to subsequent reparses (while the code completion + // results are still active). + Results->CachedCompletionAllocator = AST->getCachedCompletionAllocator(); + + + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); + SmallString<256> LogResult; + llvm::raw_svector_ostream os(LogResult); + + // Figure out the language and whether or not it uses PCH. + const char *lang = 0; + bool usesPCH = false; + + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + if (*I == 0) + continue; + if (strcmp(*I, "-x") == 0) { + if (I + 1 != E) { + lang = *(++I); + continue; + } + } + else if (strcmp(*I, "-include") == 0) { + if (I+1 != E) { + const char *arg = *(++I); + SmallString<512> pchName; + { + llvm::raw_svector_ostream os(pchName); + os << arg << ".pth"; + } + pchName.push_back('\0'); + llvm::sys::fs::file_status stat_results; + if (!llvm::sys::fs::status(pchName, stat_results)) + usesPCH = true; + continue; + } + } + } + + os << "{ "; + os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); + os << ", \"numRes\": " << Results->NumResults; + os << ", \"diags\": " << Results->Diagnostics.size(); + os << ", \"pch\": " << (usesPCH ? "true" : "false"); + os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"'; + const char *name = getlogin(); + os << ", \"user\": \"" << (name ? name : "unknown") << '"'; + os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; + os << " }"; + + StringRef res = os.str(); + if (res.size() > 0) { + do { + // Setup the UDP socket. + struct sockaddr_in servaddr; + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); + if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, + &servaddr.sin_addr) <= 0) + break; + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + break; + + sendto(sockfd, res.data(), res.size(), 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + close(sockfd); + } + while (false); + } +#endif +#endif + return Results; +} + +CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + unsigned options) { + LOG_FUNC_SECTION { + *Log << TU << ' ' + << complete_filename << ':' << complete_line << ':' << complete_column; + } + + if (num_unsaved_files && !unsaved_files) + return nullptr; + + CXCodeCompleteResults *result; + auto CodeCompleteAtImpl = [=, &result]() { + result = clang_codeCompleteAt_Impl( + TU, complete_filename, complete_line, complete_column, + llvm::makeArrayRef(unsaved_files, num_unsaved_files), options); + }; + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, CodeCompleteAtImpl)) { + fprintf(stderr, "libclang: crash detected in code completion\n"); + cxtu::getASTUnit(TU)->setUnsafeToFree(true); + return nullptr; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) + PrintLibclangResourceUsage(TU); + + return result; +} + +unsigned clang_defaultCodeCompleteOptions(void) { + return CXCodeComplete_IncludeMacros; +} + +void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { + if (!ResultsIn) + return; + + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + delete Results; +} + +unsigned +clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results) + return 0; + + return Results->Diagnostics.size(); +} + +CXDiagnostic +clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, + unsigned Index) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results || Index >= Results->Diagnostics.size()) + return nullptr; + + CXStoredDiagnostic *Diag = Results->DiagnosticsWrappers[Index]; + if (!Diag) + Results->DiagnosticsWrappers[Index] = Diag = + new CXStoredDiagnostic(Results->Diagnostics[Index], Results->LangOpts); + return Diag; +} + +unsigned long long +clang_codeCompleteGetContexts(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results) + return 0; + + return Results->Contexts; +} + +enum CXCursorKind clang_codeCompleteGetContainerKind( + CXCodeCompleteResults *ResultsIn, + unsigned *IsIncomplete) { + AllocatedCXCodeCompleteResults *Results = + static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn); + if (!Results) + return CXCursor_InvalidCode; + + if (IsIncomplete != nullptr) { + *IsIncomplete = Results->ContainerIsIncomplete; + } + + return Results->ContainerKind; +} + +CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results = + static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn); + if (!Results) + return cxstring::createEmpty(); + + return cxstring::createRef(Results->ContainerUSR.c_str()); +} + + +CXString clang_codeCompleteGetObjCSelector(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results = + static_cast<AllocatedCXCodeCompleteResults *>(ResultsIn); + if (!Results) + return cxstring::createEmpty(); + + return cxstring::createDup(Results->Selector); +} + +/// Simple utility function that appends a \p New string to the given +/// \p Old string, using the \p Buffer for storage. +/// +/// \param Old The string to which we are appending. This parameter will be +/// updated to reflect the complete string. +/// +/// +/// \param New The string to append to \p Old. +/// +/// \param Buffer A buffer that stores the actual, concatenated string. It will +/// be used if the old string is already-non-empty. +static void AppendToString(StringRef &Old, StringRef New, + SmallString<256> &Buffer) { + if (Old.empty()) { + Old = New; + return; + } + + if (Buffer.empty()) + Buffer.append(Old.begin(), Old.end()); + Buffer.append(New.begin(), New.end()); + Old = Buffer.str(); +} + +/// Get the typed-text blocks from the given code-completion string +/// and return them as a single string. +/// +/// \param String The code-completion string whose typed-text blocks will be +/// concatenated. +/// +/// \param Buffer A buffer used for storage of the completed name. +static StringRef GetTypedName(CodeCompletionString *String, + SmallString<256> &Buffer) { + StringRef Result; + for (CodeCompletionString::iterator C = String->begin(), CEnd = String->end(); + C != CEnd; ++C) { + if (C->Kind == CodeCompletionString::CK_TypedText) + AppendToString(Result, C->Text, Buffer); + } + + return Result; +} + +namespace { + struct OrderCompletionResults { + bool operator()(const CXCompletionResult &XR, + const CXCompletionResult &YR) const { + CodeCompletionString *X + = (CodeCompletionString *)XR.CompletionString; + CodeCompletionString *Y + = (CodeCompletionString *)YR.CompletionString; + + SmallString<256> XBuffer; + StringRef XText = GetTypedName(X, XBuffer); + SmallString<256> YBuffer; + StringRef YText = GetTypedName(Y, YBuffer); + + if (XText.empty() || YText.empty()) + return !XText.empty(); + + int result = XText.compare_lower(YText); + if (result < 0) + return true; + if (result > 0) + return false; + + result = XText.compare(YText); + return result < 0; + } + }; +} + +void clang_sortCodeCompletionResults(CXCompletionResult *Results, + unsigned NumResults) { + std::stable_sort(Results, Results + NumResults, OrderCompletionResults()); +} diff --git a/gnu/llvm/clang/tools/libclang/CIndexDiagnostic.cpp b/gnu/llvm/clang/tools/libclang/CIndexDiagnostic.cpp new file mode 100644 index 00000000000..624e9262150 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexDiagnostic.cpp @@ -0,0 +1,470 @@ +//===- CIndexDiagnostic.cpp - Diagnostics C Interface ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements the diagnostic functions of the Clang C interface. +// +//===----------------------------------------------------------------------===// + +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXSourceLocation.h" +#include "CXString.h" + +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cxloc; +using namespace clang::cxdiag; +using namespace llvm; + +CXDiagnosticSetImpl::~CXDiagnosticSetImpl() {} + +void +CXDiagnosticSetImpl::appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D) { + Diagnostics.push_back(std::move(D)); +} + +CXDiagnosticImpl::~CXDiagnosticImpl() {} + +namespace { +class CXDiagnosticCustomNoteImpl : public CXDiagnosticImpl { + std::string Message; + CXSourceLocation Loc; +public: + CXDiagnosticCustomNoteImpl(StringRef Msg, CXSourceLocation L) + : CXDiagnosticImpl(CustomNoteDiagnosticKind), + Message(Msg), Loc(L) {} + + ~CXDiagnosticCustomNoteImpl() override {} + + CXDiagnosticSeverity getSeverity() const override { + return CXDiagnostic_Note; + } + + CXSourceLocation getLocation() const override { + return Loc; + } + + CXString getSpelling() const override { + return cxstring::createRef(Message.c_str()); + } + + CXString getDiagnosticOption(CXString *Disable) const override { + if (Disable) + *Disable = cxstring::createEmpty(); + return cxstring::createEmpty(); + } + + unsigned getCategory() const override { return 0; } + CXString getCategoryText() const override { return cxstring::createEmpty(); } + + unsigned getNumRanges() const override { return 0; } + CXSourceRange getRange(unsigned Range) const override { + return clang_getNullRange(); + } + unsigned getNumFixIts() const override { return 0; } + CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const override { + if (ReplacementRange) + *ReplacementRange = clang_getNullRange(); + return cxstring::createEmpty(); + } +}; + +class CXDiagnosticRenderer : public DiagnosticNoteRenderer { +public: + CXDiagnosticRenderer(const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts, + CXDiagnosticSetImpl *mainSet) + : DiagnosticNoteRenderer(LangOpts, DiagOpts), + CurrentSet(mainSet), MainSet(mainSet) {} + + ~CXDiagnosticRenderer() override {} + + void beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) override { + + const StoredDiagnostic *SD = D.dyn_cast<const StoredDiagnostic*>(); + if (!SD) + return; + + if (Level != DiagnosticsEngine::Note) + CurrentSet = MainSet; + + auto Owner = std::make_unique<CXStoredDiagnostic>(*SD, LangOpts); + CXStoredDiagnostic &CD = *Owner; + CurrentSet->appendDiagnostic(std::move(Owner)); + + if (Level != DiagnosticsEngine::Note) + CurrentSet = &CD.getChildDiagnostics(); + } + + void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, StringRef Message, + ArrayRef<CharSourceRange> Ranges, + DiagOrStoredDiag D) override { + if (!D.isNull()) + return; + + CXSourceLocation L; + if (Loc.hasManager()) + L = translateSourceLocation(Loc.getManager(), LangOpts, Loc); + else + L = clang_getNullLocation(); + CurrentSet->appendDiagnostic( + std::make_unique<CXDiagnosticCustomNoteImpl>(Message, L)); + } + + void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) override {} + + void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints) override {} + + void emitNote(FullSourceLoc Loc, StringRef Message) override { + CXSourceLocation L; + if (Loc.hasManager()) + L = translateSourceLocation(Loc.getManager(), LangOpts, Loc); + else + L = clang_getNullLocation(); + CurrentSet->appendDiagnostic( + std::make_unique<CXDiagnosticCustomNoteImpl>(Message, L)); + } + + CXDiagnosticSetImpl *CurrentSet; + CXDiagnosticSetImpl *MainSet; +}; +} + +CXDiagnosticSetImpl *cxdiag::lazyCreateDiags(CXTranslationUnit TU, + bool checkIfChanged) { + ASTUnit *AU = cxtu::getASTUnit(TU); + + if (TU->Diagnostics && checkIfChanged) { + // In normal use, ASTUnit's diagnostics should not change unless we reparse. + // Currently they can only change by using the internal testing flag + // '-error-on-deserialized-decl' which will error during deserialization of + // a declaration. What will happen is: + // + // -c-index-test gets a CXTranslationUnit + // -checks the diagnostics, the diagnostics set is lazily created, + // no errors are reported + // -later does an operation, like annotation of tokens, that triggers + // -error-on-deserialized-decl, that will emit a diagnostic error, + // that ASTUnit will catch and add to its stored diagnostics vector. + // -c-index-test wants to check whether an error occurred after performing + // the operation but can only query the lazily created set. + // + // We check here if a new diagnostic was appended since the last time the + // diagnostic set was created, in which case we reset it. + + CXDiagnosticSetImpl * + Set = static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); + if (AU->stored_diag_size() != Set->getNumDiagnostics()) { + // Diagnostics in the ASTUnit were updated, reset the associated + // diagnostics. + delete Set; + TU->Diagnostics = nullptr; + } + } + + if (!TU->Diagnostics) { + CXDiagnosticSetImpl *Set = new CXDiagnosticSetImpl(); + TU->Diagnostics = Set; + IntrusiveRefCntPtr<DiagnosticOptions> DOpts = new DiagnosticOptions; + CXDiagnosticRenderer Renderer(AU->getASTContext().getLangOpts(), + &*DOpts, Set); + + for (ASTUnit::stored_diag_iterator it = AU->stored_diag_begin(), + ei = AU->stored_diag_end(); it != ei; ++it) { + Renderer.emitStoredDiagnostic(*it); + } + } + return static_cast<CXDiagnosticSetImpl*>(TU->Diagnostics); +} + +//----------------------------------------------------------------------------- +// C Interface Routines +//----------------------------------------------------------------------------- +unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) { + if (cxtu::isNotUsableTU(Unit)) { + LOG_BAD_TU(Unit); + return 0; + } + if (!cxtu::getASTUnit(Unit)) + return 0; + return lazyCreateDiags(Unit, /*checkIfChanged=*/true)->getNumDiagnostics(); +} + +CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) { + if (cxtu::isNotUsableTU(Unit)) { + LOG_BAD_TU(Unit); + return nullptr; + } + + CXDiagnosticSet D = clang_getDiagnosticSetFromTU(Unit); + if (!D) + return nullptr; + + CXDiagnosticSetImpl *Diags = static_cast<CXDiagnosticSetImpl*>(D); + if (Index >= Diags->getNumDiagnostics()) + return nullptr; + + return Diags->getDiagnostic(Index); +} + +CXDiagnosticSet clang_getDiagnosticSetFromTU(CXTranslationUnit Unit) { + if (cxtu::isNotUsableTU(Unit)) { + LOG_BAD_TU(Unit); + return nullptr; + } + if (!cxtu::getASTUnit(Unit)) + return nullptr; + return static_cast<CXDiagnostic>(lazyCreateDiags(Unit)); +} + +void clang_disposeDiagnostic(CXDiagnostic Diagnostic) { + // No-op. Kept as a legacy API. CXDiagnostics are now managed + // by the enclosing CXDiagnosticSet. +} + +CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { + if (!Diagnostic) + return cxstring::createEmpty(); + + CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic); + + SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + + if (Options & CXDiagnostic_DisplaySourceLocation) { + // Print source location (file:line), along with optional column + // and source ranges. + CXFile File; + unsigned Line, Column; + clang_getSpellingLocation(clang_getDiagnosticLocation(Diagnostic), + &File, &Line, &Column, nullptr); + if (File) { + CXString FName = clang_getFileName(File); + Out << clang_getCString(FName) << ":" << Line << ":"; + clang_disposeString(FName); + if (Options & CXDiagnostic_DisplayColumn) + Out << Column << ":"; + + if (Options & CXDiagnostic_DisplaySourceRanges) { + unsigned N = clang_getDiagnosticNumRanges(Diagnostic); + bool PrintedRange = false; + for (unsigned I = 0; I != N; ++I) { + CXFile StartFile, EndFile; + CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I); + + unsigned StartLine, StartColumn, EndLine, EndColumn; + clang_getSpellingLocation(clang_getRangeStart(Range), + &StartFile, &StartLine, &StartColumn, + nullptr); + clang_getSpellingLocation(clang_getRangeEnd(Range), + &EndFile, &EndLine, &EndColumn, nullptr); + + if (StartFile != EndFile || StartFile != File) + continue; + + Out << "{" << StartLine << ":" << StartColumn << "-" + << EndLine << ":" << EndColumn << "}"; + PrintedRange = true; + } + if (PrintedRange) + Out << ":"; + } + + Out << " "; + } + } + + /* Print warning/error/etc. */ + switch (Severity) { + case CXDiagnostic_Ignored: llvm_unreachable("impossible"); + case CXDiagnostic_Note: Out << "note: "; break; + case CXDiagnostic_Warning: Out << "warning: "; break; + case CXDiagnostic_Error: Out << "error: "; break; + case CXDiagnostic_Fatal: Out << "fatal error: "; break; + } + + CXString Text = clang_getDiagnosticSpelling(Diagnostic); + if (clang_getCString(Text)) + Out << clang_getCString(Text); + else + Out << "<no diagnostic text>"; + clang_disposeString(Text); + + if (Options & (CXDiagnostic_DisplayOption | CXDiagnostic_DisplayCategoryId | + CXDiagnostic_DisplayCategoryName)) { + bool NeedBracket = true; + bool NeedComma = false; + + if (Options & CXDiagnostic_DisplayOption) { + CXString OptionName = clang_getDiagnosticOption(Diagnostic, nullptr); + if (const char *OptionText = clang_getCString(OptionName)) { + if (OptionText[0]) { + Out << " [" << OptionText; + NeedBracket = false; + NeedComma = true; + } + } + clang_disposeString(OptionName); + } + + if (Options & (CXDiagnostic_DisplayCategoryId | + CXDiagnostic_DisplayCategoryName)) { + if (unsigned CategoryID = clang_getDiagnosticCategory(Diagnostic)) { + if (Options & CXDiagnostic_DisplayCategoryId) { + if (NeedBracket) + Out << " ["; + if (NeedComma) + Out << ", "; + Out << CategoryID; + NeedBracket = false; + NeedComma = true; + } + + if (Options & CXDiagnostic_DisplayCategoryName) { + CXString CategoryName = clang_getDiagnosticCategoryText(Diagnostic); + if (NeedBracket) + Out << " ["; + if (NeedComma) + Out << ", "; + Out << clang_getCString(CategoryName); + NeedBracket = false; + NeedComma = true; + clang_disposeString(CategoryName); + } + } + } + + (void) NeedComma; // Silence dead store warning. + if (!NeedBracket) + Out << "]"; + } + + return cxstring::createDup(Out.str()); +} + +unsigned clang_defaultDiagnosticDisplayOptions() { + return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn | + CXDiagnostic_DisplayOption; +} + +enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag)) + return D->getSeverity(); + return CXDiagnostic_Ignored; +} + +CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl*>(Diag)) + return D->getLocation(); + return clang_getNullLocation(); +} + +CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getSpelling(); + return cxstring::createEmpty(); +} + +CXString clang_getDiagnosticOption(CXDiagnostic Diag, CXString *Disable) { + if (Disable) + *Disable = cxstring::createEmpty(); + + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getDiagnosticOption(Disable); + + return cxstring::createEmpty(); +} + +unsigned clang_getDiagnosticCategory(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getCategory(); + return 0; +} + +CXString clang_getDiagnosticCategoryName(unsigned Category) { + // Kept for backward compatibility. + return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(Category)); +} + +CXString clang_getDiagnosticCategoryText(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getCategoryText(); + return cxstring::createEmpty(); +} + +unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getNumRanges(); + return 0; +} + +CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) { + CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag); + if (!D || Range >= D->getNumRanges()) + return clang_getNullRange(); + return D->getRange(Range); +} + +unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) + return D->getNumFixIts(); + return 0; +} + +CXString clang_getDiagnosticFixIt(CXDiagnostic Diag, unsigned FixIt, + CXSourceRange *ReplacementRange) { + CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag); + if (!D || FixIt >= D->getNumFixIts()) { + if (ReplacementRange) + *ReplacementRange = clang_getNullRange(); + return cxstring::createEmpty(); + } + return D->getFixIt(FixIt, ReplacementRange); +} + +void clang_disposeDiagnosticSet(CXDiagnosticSet Diags) { + if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl *>(Diags)) { + if (D->isExternallyManaged()) + delete D; + } +} + +CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags, + unsigned Index) { + if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags)) + if (Index < D->getNumDiagnostics()) + return D->getDiagnostic(Index); + return nullptr; +} + +CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic Diag) { + if (CXDiagnosticImpl *D = static_cast<CXDiagnosticImpl *>(Diag)) { + CXDiagnosticSetImpl &ChildDiags = D->getChildDiagnostics(); + return ChildDiags.empty() ? nullptr : (CXDiagnosticSet) &ChildDiags; + } + return nullptr; +} + +unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags) { + if (CXDiagnosticSetImpl *D = static_cast<CXDiagnosticSetImpl*>(Diags)) + return D->getNumDiagnostics(); + return 0; +} diff --git a/gnu/llvm/clang/tools/libclang/CIndexDiagnostic.h b/gnu/llvm/clang/tools/libclang/CIndexDiagnostic.h new file mode 100644 index 00000000000..25589bb5747 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexDiagnostic.h @@ -0,0 +1,165 @@ +/*===-- CIndexDiagnostic.h - Diagnostics C Interface ------------*- C++ -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements the diagnostic functions of the Clang C interface. *| +|* *| +\*===----------------------------------------------------------------------===*/ +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CINDEXDIAGNOSTIC_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CINDEXDIAGNOSTIC_H + +#include "clang-c/Index.h" +#include <memory> +#include <vector> +#include <assert.h> + +namespace clang { + +class LangOptions; +class StoredDiagnostic; +class CXDiagnosticImpl; + +class CXDiagnosticSetImpl { + std::vector<std::unique_ptr<CXDiagnosticImpl>> Diagnostics; + const bool IsExternallyManaged; +public: + CXDiagnosticSetImpl(bool isManaged = false) + : IsExternallyManaged(isManaged) {} + + virtual ~CXDiagnosticSetImpl(); + + size_t getNumDiagnostics() const { + return Diagnostics.size(); + } + + CXDiagnosticImpl *getDiagnostic(unsigned i) const { + assert(i < getNumDiagnostics()); + return Diagnostics[i].get(); + } + + void appendDiagnostic(std::unique_ptr<CXDiagnosticImpl> D); + + bool empty() const { + return Diagnostics.empty(); + } + + bool isExternallyManaged() const { return IsExternallyManaged; } +}; + +class CXDiagnosticImpl { +public: + enum Kind { StoredDiagnosticKind, LoadedDiagnosticKind, + CustomNoteDiagnosticKind }; + + virtual ~CXDiagnosticImpl(); + + /// Return the severity of the diagnostic. + virtual CXDiagnosticSeverity getSeverity() const = 0; + + /// Return the location of the diagnostic. + virtual CXSourceLocation getLocation() const = 0; + + /// Return the spelling of the diagnostic. + virtual CXString getSpelling() const = 0; + + /// Return the text for the diagnostic option. + virtual CXString getDiagnosticOption(CXString *Disable) const = 0; + + /// Return the category of the diagnostic. + virtual unsigned getCategory() const = 0; + + /// Return the category string of the diagnostic. + virtual CXString getCategoryText() const = 0; + + /// Return the number of source ranges for the diagnostic. + virtual unsigned getNumRanges() const = 0; + + /// Return the source ranges for the diagnostic. + virtual CXSourceRange getRange(unsigned Range) const = 0; + + /// Return the number of FixIts. + virtual unsigned getNumFixIts() const = 0; + + /// Return the FixIt information (source range and inserted text). + virtual CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const = 0; + + Kind getKind() const { return K; } + + CXDiagnosticSetImpl &getChildDiagnostics() { + return ChildDiags; + } + +protected: + CXDiagnosticImpl(Kind k) : K(k) {} + CXDiagnosticSetImpl ChildDiags; + + void append(std::unique_ptr<CXDiagnosticImpl> D) { + ChildDiags.appendDiagnostic(std::move(D)); + } + +private: + Kind K; +}; + +/// The storage behind a CXDiagnostic +struct CXStoredDiagnostic : public CXDiagnosticImpl { + const StoredDiagnostic &Diag; + const LangOptions &LangOpts; + + CXStoredDiagnostic(const StoredDiagnostic &Diag, + const LangOptions &LangOpts) + : CXDiagnosticImpl(StoredDiagnosticKind), + Diag(Diag), LangOpts(LangOpts) { } + + ~CXStoredDiagnostic() override {} + + /// Return the severity of the diagnostic. + CXDiagnosticSeverity getSeverity() const override; + + /// Return the location of the diagnostic. + CXSourceLocation getLocation() const override; + + /// Return the spelling of the diagnostic. + CXString getSpelling() const override; + + /// Return the text for the diagnostic option. + CXString getDiagnosticOption(CXString *Disable) const override; + + /// Return the category of the diagnostic. + unsigned getCategory() const override; + + /// Return the category string of the diagnostic. + CXString getCategoryText() const override; + + /// Return the number of source ranges for the diagnostic. + unsigned getNumRanges() const override; + + /// Return the source ranges for the diagnostic. + CXSourceRange getRange(unsigned Range) const override; + + /// Return the number of FixIts. + unsigned getNumFixIts() const override; + + /// Return the FixIt information (source range and inserted text). + CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const override; + + static bool classof(const CXDiagnosticImpl *D) { + return D->getKind() == StoredDiagnosticKind; + } +}; + +namespace cxdiag { +CXDiagnosticSetImpl *lazyCreateDiags(CXTranslationUnit TU, + bool checkIfChanged = false); +} // end namespace cxdiag + +} // end namespace clang + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp b/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp new file mode 100644 index 00000000000..04699ccb01a --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexHigh.cpp @@ -0,0 +1,534 @@ +//===- CIndexHigh.cpp - Higher level API functions ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "CursorVisitor.h" +#include "CLog.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXTranslationUnit.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Frontend/ASTUnit.h" +#include "llvm/Support/Compiler.h" + +using namespace clang; +using namespace cxcursor; +using namespace cxindex; + +static void getTopOverriddenMethods(CXTranslationUnit TU, + const Decl *D, + SmallVectorImpl<const Decl *> &Methods) { + if (!D) + return; + if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D)) + return; + + SmallVector<CXCursor, 8> Overridden; + cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden); + + if (Overridden.empty()) { + Methods.push_back(D->getCanonicalDecl()); + return; + } + + for (SmallVectorImpl<CXCursor>::iterator + I = Overridden.begin(), E = Overridden.end(); I != E; ++I) + getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); +} + +namespace { + +struct FindFileIdRefVisitData { + CXTranslationUnit TU; + FileID FID; + const Decl *Dcl; + int SelectorIdIdx; + CXCursorAndRangeVisitor visitor; + + typedef SmallVector<const Decl *, 8> TopMethodsTy; + TopMethodsTy TopMethods; + + FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, + const Decl *D, int selectorIdIdx, + CXCursorAndRangeVisitor visitor) + : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) { + Dcl = getCanonical(D); + getTopOverriddenMethods(TU, Dcl, TopMethods); + } + + ASTContext &getASTContext() const { + return cxtu::getASTUnit(TU)->getASTContext(); + } + + /// We are looking to find all semantically relevant identifiers, + /// so the definition of "canonical" here is different than in the AST, e.g. + /// + /// \code + /// class C { + /// C() {} + /// }; + /// \endcode + /// + /// we consider the canonical decl of the constructor decl to be the class + /// itself, so both 'C' can be highlighted. + const Decl *getCanonical(const Decl *D) const { + if (!D) + return nullptr; + + D = D->getCanonicalDecl(); + + if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) { + if (ImplD->getClassInterface()) + return getCanonical(ImplD->getClassInterface()); + + } else if (const CXXConstructorDecl *CXXCtorD = + dyn_cast<CXXConstructorDecl>(D)) { + return getCanonical(CXXCtorD->getParent()); + } + + return D; + } + + bool isHit(const Decl *D) const { + if (!D) + return false; + + D = getCanonical(D); + if (D == Dcl) + return true; + + if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) + return isOverriddingMethod(D); + + return false; + } + +private: + bool isOverriddingMethod(const Decl *D) const { + if (llvm::find(TopMethods, D) != TopMethods.end()) + return true; + + TopMethodsTy methods; + getTopOverriddenMethods(TU, D, methods); + for (TopMethodsTy::iterator + I = methods.begin(), E = methods.end(); I != E; ++I) { + if (llvm::find(TopMethods, *I) != TopMethods.end()) + return true; + } + + return false; + } +}; + +} // end anonymous namespace. + +/// For a macro \arg Loc, returns the file spelling location and sets +/// to \arg isMacroArg whether the spelling resides inside a macro definition or +/// a macro argument. +static SourceLocation getFileSpellingLoc(SourceManager &SM, + SourceLocation Loc, + bool &isMacroArg) { + assert(Loc.isMacroID()); + SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc); + if (SpellLoc.isMacroID()) + return getFileSpellingLoc(SM, SpellLoc, isMacroArg); + + isMacroArg = SM.isMacroArgExpansion(Loc); + return SpellLoc; +} + +static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + CXCursor declCursor = clang_getCursorReferenced(cursor); + if (!clang_isDeclaration(declCursor.kind)) + return CXChildVisit_Recurse; + + const Decl *D = cxcursor::getCursorDecl(declCursor); + if (!D) + return CXChildVisit_Continue; + + FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data; + if (data->isHit(D)) { + cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor); + + // We are looking for identifiers to highlight so for objc methods (and + // not a parameter) we can only highlight the selector identifiers. + if ((cursor.kind == CXCursor_ObjCClassMethodDecl || + cursor.kind == CXCursor_ObjCInstanceMethodDecl) && + cxcursor::getSelectorIdentifierIndex(cursor) == -1) + return CXChildVisit_Recurse; + + if (clang_isExpression(cursor.kind)) { + if (cursor.kind == CXCursor_DeclRefExpr || + cursor.kind == CXCursor_MemberRefExpr) { + // continue.. + + } else if (cursor.kind == CXCursor_ObjCMessageExpr && + cxcursor::getSelectorIdentifierIndex(cursor) != -1) { + // continue.. + + } else + return CXChildVisit_Recurse; + } + + SourceLocation + Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); + SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor); + if (SelIdLoc.isValid()) + Loc = SelIdLoc; + + ASTContext &Ctx = data->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + bool isInMacroDef = false; + if (Loc.isMacroID()) { + bool isMacroArg; + Loc = getFileSpellingLoc(SM, Loc, isMacroArg); + isInMacroDef = !isMacroArg; + } + + // We are looking for identifiers in a specific file. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + if (LocInfo.first != data->FID) + return CXChildVisit_Recurse; + + if (isInMacroDef) { + // FIXME: For a macro definition make sure that all expansions + // of it expand to the same reference before allowing to point to it. + return CXChildVisit_Recurse; + } + + if (data->visitor.visit(data->visitor.context, cursor, + cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) + return CXChildVisit_Break; + } + return CXChildVisit_Recurse; +} + +static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, + const FileEntry *File, + CXCursorAndRangeVisitor Visitor) { + assert(clang_isDeclaration(declCursor.kind)); + SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); + + FileID FID = SM.translateFile(File); + const Decl *Dcl = cxcursor::getCursorDecl(declCursor); + if (!Dcl) + return false; + + FindFileIdRefVisitData data(TU, FID, Dcl, + cxcursor::getSelectorIdentifierIndex(declCursor), + Visitor); + + if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) { + return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), + findFileIdRefVisit, &data); + } + + SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); + CursorVisitor FindIdRefsVisitor(TU, + findFileIdRefVisit, &data, + /*VisitPreprocessorLast=*/true, + /*VisitIncludedEntities=*/false, + Range, + /*VisitDeclsOnly=*/true); + return FindIdRefsVisitor.visitFileRegion(); +} + +namespace { + +struct FindFileMacroRefVisitData { + ASTUnit &Unit; + const FileEntry *File; + const IdentifierInfo *Macro; + CXCursorAndRangeVisitor visitor; + + FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File, + const IdentifierInfo *Macro, + CXCursorAndRangeVisitor visitor) + : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { } + + ASTContext &getASTContext() const { + return Unit.getASTContext(); + } +}; + +} // anonymous namespace + +static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + const IdentifierInfo *Macro = nullptr; + if (cursor.kind == CXCursor_MacroDefinition) + Macro = getCursorMacroDefinition(cursor)->getName(); + else if (cursor.kind == CXCursor_MacroExpansion) + Macro = getCursorMacroExpansion(cursor).getName(); + if (!Macro) + return CXChildVisit_Continue; + + FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data; + if (data->Macro != Macro) + return CXChildVisit_Continue; + + SourceLocation + Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); + + ASTContext &Ctx = data->getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + bool isInMacroDef = false; + if (Loc.isMacroID()) { + bool isMacroArg; + Loc = getFileSpellingLoc(SM, Loc, isMacroArg); + isInMacroDef = !isMacroArg; + } + + // We are looking for identifiers in a specific file. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + if (SM.getFileEntryForID(LocInfo.first) != data->File) + return CXChildVisit_Continue; + + if (isInMacroDef) { + // FIXME: For a macro definition make sure that all expansions + // of it expand to the same reference before allowing to point to it. + return CXChildVisit_Continue; + } + + if (data->visitor.visit(data->visitor.context, cursor, + cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) + return CXChildVisit_Break; + return CXChildVisit_Continue; +} + +static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor, + const FileEntry *File, + CXCursorAndRangeVisitor Visitor) { + if (Cursor.kind != CXCursor_MacroDefinition && + Cursor.kind != CXCursor_MacroExpansion) + return false; + + ASTUnit *Unit = cxtu::getASTUnit(TU); + SourceManager &SM = Unit->getSourceManager(); + + FileID FID = SM.translateFile(File); + const IdentifierInfo *Macro = nullptr; + if (Cursor.kind == CXCursor_MacroDefinition) + Macro = getCursorMacroDefinition(Cursor)->getName(); + else + Macro = getCursorMacroExpansion(Cursor).getName(); + if (!Macro) + return false; + + FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor); + + SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); + CursorVisitor FindMacroRefsVisitor(TU, + findFileMacroRefVisit, &data, + /*VisitPreprocessorLast=*/false, + /*VisitIncludedEntities=*/false, + Range); + return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion(); +} + +namespace { + +struct FindFileIncludesVisitor { + ASTUnit &Unit; + const FileEntry *File; + CXCursorAndRangeVisitor visitor; + + FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File, + CXCursorAndRangeVisitor visitor) + : Unit(Unit), File(File), visitor(visitor) { } + + ASTContext &getASTContext() const { + return Unit.getASTContext(); + } + + enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) { + if (cursor.kind != CXCursor_InclusionDirective) + return CXChildVisit_Continue; + + SourceLocation + Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor)); + + ASTContext &Ctx = getASTContext(); + SourceManager &SM = Ctx.getSourceManager(); + + // We are looking for includes in a specific file. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + if (SM.getFileEntryForID(LocInfo.first) != File) + return CXChildVisit_Continue; + + if (visitor.visit(visitor.context, cursor, + cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break) + return CXChildVisit_Break; + return CXChildVisit_Continue; + } + + static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent, + CXClientData client_data) { + return static_cast<FindFileIncludesVisitor*>(client_data)-> + visit(cursor, parent); + } +}; + +} // anonymous namespace + +static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File, + CXCursorAndRangeVisitor Visitor) { + assert(TU && File && Visitor.visit); + + ASTUnit *Unit = cxtu::getASTUnit(TU); + SourceManager &SM = Unit->getSourceManager(); + + FileID FID = SM.translateFile(File); + + FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor); + + SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID)); + CursorVisitor InclusionCursorsVisitor(TU, + FindFileIncludesVisitor::visit, + &IncludesVisitor, + /*VisitPreprocessorLast=*/false, + /*VisitIncludedEntities=*/false, + Range); + return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion(); +} + + +//===----------------------------------------------------------------------===// +// libclang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { + +CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file, + CXCursorAndRangeVisitor visitor) { + LogRef Log = Logger::make(__func__); + + if (clang_Cursor_isNull(cursor)) { + if (Log) + *Log << "Null cursor"; + return CXResult_Invalid; + } + if (cursor.kind == CXCursor_NoDeclFound) { + if (Log) + *Log << "Got CXCursor_NoDeclFound"; + return CXResult_Invalid; + } + if (!file) { + if (Log) + *Log << "Null file"; + return CXResult_Invalid; + } + if (!visitor.visit) { + if (Log) + *Log << "Null visitor"; + return CXResult_Invalid; + } + + if (Log) + *Log << cursor << " @" << static_cast<const FileEntry *>(file); + + ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); + if (!CXXUnit) + return CXResult_Invalid; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + if (cursor.kind == CXCursor_MacroDefinition || + cursor.kind == CXCursor_MacroExpansion) { + if (findMacroRefsInFile(cxcursor::getCursorTU(cursor), + cursor, + static_cast<const FileEntry *>(file), + visitor)) + return CXResult_VisitBreak; + return CXResult_Success; + } + + // We are interested in semantics of identifiers so for C++ constructor exprs + // prefer type references, e.g.: + // + // return MyStruct(); + // + // for 'MyStruct' we'll have a cursor pointing at the constructor decl but + // we are actually interested in the type declaration. + cursor = cxcursor::getTypeRefCursor(cursor); + + CXCursor refCursor = clang_getCursorReferenced(cursor); + + if (!clang_isDeclaration(refCursor.kind)) { + if (Log) + *Log << "cursor is not referencing a declaration"; + return CXResult_Invalid; + } + + if (findIdRefsInFile(cxcursor::getCursorTU(cursor), + refCursor, + static_cast<const FileEntry *>(file), + visitor)) + return CXResult_VisitBreak; + return CXResult_Success; +} + +CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file, + CXCursorAndRangeVisitor visitor) { + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXResult_Invalid; + } + + LogRef Log = Logger::make(__func__); + if (!file) { + if (Log) + *Log << "Null file"; + return CXResult_Invalid; + } + if (!visitor.visit) { + if (Log) + *Log << "Null visitor"; + return CXResult_Invalid; + } + + if (Log) + *Log << TU << " @" << static_cast<const FileEntry *>(file); + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + if (!CXXUnit) + return CXResult_Invalid; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor)) + return CXResult_VisitBreak; + return CXResult_Success; +} + +static enum CXVisitorResult _visitCursorAndRange(void *context, + CXCursor cursor, + CXSourceRange range) { + CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; + return INVOKE_BLOCK2(block, cursor, range); +} + +CXResult clang_findReferencesInFileWithBlock(CXCursor cursor, + CXFile file, + CXCursorAndRangeVisitorBlock block) { + CXCursorAndRangeVisitor visitor = { block, + block ? _visitCursorAndRange : nullptr }; + return clang_findReferencesInFile(cursor, file, visitor); +} + +CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU, + CXFile file, + CXCursorAndRangeVisitorBlock block) { + CXCursorAndRangeVisitor visitor = { block, + block ? _visitCursorAndRange : nullptr }; + return clang_findIncludesInFile(TU, file, visitor); +} + +} // end: extern "C" diff --git a/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp b/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp new file mode 100644 index 00000000000..f1c5b53c5ef --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexInclusionStack.cpp @@ -0,0 +1,96 @@ +//===- CIndexInclusionStack.cpp - Clang-C Source Indexing Library ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines a callback mechanism for clients to get the inclusion +// stack from a translation unit. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXSourceLocation.h" +#include "CXTranslationUnit.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" +using namespace clang; + +static void getInclusions(const SrcMgr::SLocEntry &(SourceManager::*Getter)(unsigned, bool*) const, unsigned n, + CXTranslationUnit TU, CXInclusionVisitor CB, + CXClientData clientData) +{ + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + SourceManager &SM = CXXUnit->getSourceManager(); + ASTContext &Ctx = CXXUnit->getASTContext(); + SmallVector<CXSourceLocation, 10> InclusionStack; + const bool HasPreamble = SM.getPreambleFileID().isValid(); + + for (unsigned i = 0 ; i < n ; ++i) { + bool Invalid = false; + const SrcMgr::SLocEntry &SL = (SM.*Getter)(i, &Invalid); + + if (!SL.isFile() || Invalid) + continue; + + const SrcMgr::FileInfo &FI = SL.getFile(); + if (!FI.getContentCache()->OrigEntry) + continue; + + // If this is the main file, and there is a preamble, skip this SLoc. The + // inclusions of the preamble already showed it. + SourceLocation L = FI.getIncludeLoc(); + if (HasPreamble && CXXUnit->isInMainFileID(L)) + continue; + + // Build the inclusion stack. + InclusionStack.clear(); + while (L.isValid()) { + PresumedLoc PLoc = SM.getPresumedLoc(L); + InclusionStack.push_back(cxloc::translateSourceLocation(Ctx, L)); + L = PLoc.isValid()? PLoc.getIncludeLoc() : SourceLocation(); + } + + // If there is a preamble, the last entry is the "inclusion" of that + // preamble into the main file, which has the bogus entry of main.c:1:1 + if (HasPreamble && !InclusionStack.empty()) + InclusionStack.pop_back(); + + // Callback to the client. + // FIXME: We should have a function to construct CXFiles. + CB(static_cast<CXFile>( + const_cast<FileEntry *>(FI.getContentCache()->OrigEntry)), + InclusionStack.data(), InclusionStack.size(), clientData); + } +} + + +void clang_getInclusions(CXTranslationUnit TU, CXInclusionVisitor CB, + CXClientData clientData) { + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return; + } + + SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager(); + const unsigned n = SM.local_sloc_entry_size(); + + // In the case where all the SLocEntries are in an external source, traverse + // those SLocEntries as well. This is the case where we are looking + // at the inclusion stack of an AST/PCH file. Also, if we are not looking at + // a AST/PCH file, but this file has a pre-compiled preamble, we also need + // to look in that file. + if (n == 1 || SM.getPreambleFileID().isValid()) { + getInclusions(&SourceManager::getLoadedSLocEntry, + SM.loaded_sloc_entry_size(), TU, CB, clientData); + } + + // Not a PCH/AST file. Note, if there is a preamble, it could still be that + // there are #includes in this file (e.g. for any include after the first + // declaration). + if (n != 1) + getInclusions(&SourceManager::getLocalSLocEntry, n, TU, CB, clientData); + +} diff --git a/gnu/llvm/clang/tools/libclang/CIndexUSRs.cpp b/gnu/llvm/clang/tools/libclang/CIndexUSRs.cpp new file mode 100644 index 00000000000..75bb3b01299 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexUSRs.cpp @@ -0,0 +1,139 @@ +//===- CIndexUSRs.cpp - Clang-C Source Indexing Library -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the generation and use of USRs from CXEntities. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Index/USRGeneration.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::index; + +//===----------------------------------------------------------------------===// +// API hooks. +//===----------------------------------------------------------------------===// + +static inline StringRef extractUSRSuffix(StringRef s) { + return s.startswith("c:") ? s.substr(2) : ""; +} + +bool cxcursor::getDeclCursorUSR(const Decl *D, SmallVectorImpl<char> &Buf) { + return generateUSRForDecl(D, Buf); +} + +CXString clang_getCursorUSR(CXCursor C) { + const CXCursorKind &K = clang_getCursorKind(C); + + if (clang_isDeclaration(K)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return cxstring::createEmpty(); + + CXTranslationUnit TU = cxcursor::getCursorTU(C); + if (!TU) + return cxstring::createEmpty(); + + cxstring::CXStringBuf *buf = cxstring::getCXStringBuf(TU); + if (!buf) + return cxstring::createEmpty(); + + bool Ignore = cxcursor::getDeclCursorUSR(D, buf->Data); + if (Ignore) { + buf->dispose(); + return cxstring::createEmpty(); + } + + // Return the C-string, but don't make a copy since it is already in + // the string buffer. + buf->Data.push_back('\0'); + return createCXString(buf); + } + + if (K == CXCursor_MacroDefinition) { + CXTranslationUnit TU = cxcursor::getCursorTU(C); + if (!TU) + return cxstring::createEmpty(); + + cxstring::CXStringBuf *buf = cxstring::getCXStringBuf(TU); + if (!buf) + return cxstring::createEmpty(); + + bool Ignore = generateUSRForMacro(cxcursor::getCursorMacroDefinition(C), + cxtu::getASTUnit(TU)->getSourceManager(), + buf->Data); + if (Ignore) { + buf->dispose(); + return cxstring::createEmpty(); + } + + // Return the C-string, but don't make a copy since it is already in + // the string buffer. + buf->Data.push_back('\0'); + return createCXString(buf); + } + + return cxstring::createEmpty(); +} + +CXString clang_constructUSR_ObjCIvar(const char *name, CXString classUSR) { + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + OS << extractUSRSuffix(clang_getCString(classUSR)); + generateUSRForObjCIvar(name, OS); + return cxstring::createDup(OS.str()); +} + +CXString clang_constructUSR_ObjCMethod(const char *name, + unsigned isInstanceMethod, + CXString classUSR) { + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + OS << extractUSRSuffix(clang_getCString(classUSR)); + generateUSRForObjCMethod(name, isInstanceMethod, OS); + return cxstring::createDup(OS.str()); +} + +CXString clang_constructUSR_ObjCClass(const char *name) { + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + generateUSRForObjCClass(name, OS); + return cxstring::createDup(OS.str()); +} + +CXString clang_constructUSR_ObjCProtocol(const char *name) { + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + generateUSRForObjCProtocol(name, OS); + return cxstring::createDup(OS.str()); +} + +CXString clang_constructUSR_ObjCCategory(const char *class_name, + const char *category_name) { + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + generateUSRForObjCCategory(class_name, category_name, OS); + return cxstring::createDup(OS.str()); +} + +CXString clang_constructUSR_ObjCProperty(const char *property, + CXString classUSR) { + SmallString<128> Buf(getUSRSpacePrefix()); + llvm::raw_svector_ostream OS(Buf); + OS << extractUSRSuffix(clang_getCString(classUSR)); + generateUSRForObjCProperty(property, /*isClassProp=*/false, OS); + return cxstring::createDup(OS.str()); +} diff --git a/gnu/llvm/clang/tools/libclang/CIndexer.cpp b/gnu/llvm/clang/tools/libclang/CIndexer.cpp new file mode 100644 index 00000000000..f7840925ad5 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexer.cpp @@ -0,0 +1,219 @@ +//===- CIndexer.cpp - Clang-C Source Indexing Library ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Clang-C Source Indexing library. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXString.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/Version.h" +#include "clang/Driver/Driver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/YAMLParser.h" +#include <cstdio> +#include <mutex> + +#ifdef __CYGWIN__ +#include <cygwin/version.h> +#include <sys/cygwin.h> +#define _WIN32 1 +#endif + +#ifdef _WIN32 +#include <windows.h> +#elif defined(_AIX) +#include <errno.h> +#include <sys/ldr.h> +#else +#include <dlfcn.h> +#endif + +using namespace clang; + +#ifdef _AIX +namespace clang { +namespace { + +template <typename LibClangPathType> +void getClangResourcesPathImplAIX(LibClangPathType &LibClangPath) { + int PrevErrno = errno; + + size_t BufSize = 2048u; + std::unique_ptr<char[]> Buf; + while (true) { + Buf = std::make_unique<char []>(BufSize); + errno = 0; + int Ret = loadquery(L_GETXINFO, Buf.get(), (unsigned int)BufSize); + if (Ret != -1) + break; // loadquery() was successful. + if (errno != ENOMEM) + llvm_unreachable("Encountered an unexpected loadquery() failure"); + + // errno == ENOMEM; try to allocate more memory. + if ((BufSize & ~((-1u) >> 1u)) != 0u) + llvm::report_fatal_error("BufSize needed for loadquery() too large"); + + Buf.release(); + BufSize <<= 1u; + } + + // Extract the function entry point from the function descriptor. + uint64_t EntryAddr = + reinterpret_cast<uintptr_t &>(clang_createTranslationUnit); + + // Loop to locate the function entry point in the loadquery() results. + ld_xinfo *CurInfo = reinterpret_cast<ld_xinfo *>(Buf.get()); + while (true) { + uint64_t CurTextStart = (uint64_t)CurInfo->ldinfo_textorg; + uint64_t CurTextEnd = CurTextStart + CurInfo->ldinfo_textsize; + if (CurTextStart <= EntryAddr && EntryAddr < CurTextEnd) + break; // Successfully located. + + if (CurInfo->ldinfo_next == 0u) + llvm::report_fatal_error("Cannot locate entry point in " + "the loadquery() results"); + CurInfo = reinterpret_cast<ld_xinfo *>(reinterpret_cast<char *>(CurInfo) + + CurInfo->ldinfo_next); + } + + LibClangPath += reinterpret_cast<char *>(CurInfo) + CurInfo->ldinfo_filename; + errno = PrevErrno; +} + +} // end anonymous namespace +} // end namespace clang +#endif + +const std::string &CIndexer::getClangResourcesPath() { + // Did we already compute the path? + if (!ResourcesPath.empty()) + return ResourcesPath; + + SmallString<128> LibClangPath; + + // Find the location where this library lives (libclang.dylib). +#ifdef _WIN32 + MEMORY_BASIC_INFORMATION mbi; + char path[MAX_PATH]; + VirtualQuery((void *)(uintptr_t)clang_createTranslationUnit, &mbi, + sizeof(mbi)); + GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, path, MAX_PATH); + +#ifdef __CYGWIN__ + char w32path[MAX_PATH]; + strcpy(w32path, path); +#if CYGWIN_VERSION_API_MAJOR > 0 || CYGWIN_VERSION_API_MINOR >= 181 + cygwin_conv_path(CCP_WIN_A_TO_POSIX, w32path, path, MAX_PATH); +#else + cygwin_conv_to_full_posix_path(w32path, path); +#endif +#endif + + LibClangPath += path; +#elif defined(_AIX) + getClangResourcesPathImplAIX(LibClangPath); +#else + // This silly cast below avoids a C++ warning. + Dl_info info; + if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) == 0) + llvm_unreachable("Call to dladdr() failed"); + + // We now have the CIndex directory, locate clang relative to it. + LibClangPath += info.dli_fname; +#endif + + // Cache our result. + ResourcesPath = driver::Driver::GetResourcesPath(LibClangPath); + return ResourcesPath; +} + +StringRef CIndexer::getClangToolchainPath() { + if (!ToolchainPath.empty()) + return ToolchainPath; + StringRef ResourcePath = getClangResourcesPath(); + ToolchainPath = llvm::sys::path::parent_path( + llvm::sys::path::parent_path(llvm::sys::path::parent_path(ResourcePath))); + return ToolchainPath; +} + +LibclangInvocationReporter::LibclangInvocationReporter( + CIndexer &Idx, OperationKind Op, unsigned ParseOptions, + llvm::ArrayRef<const char *> Args, + llvm::ArrayRef<std::string> InvocationArgs, + llvm::ArrayRef<CXUnsavedFile> UnsavedFiles) { + StringRef Path = Idx.getInvocationEmissionPath(); + if (Path.empty()) + return; + + // Create a temporary file for the invocation log. + SmallString<256> TempPath; + TempPath = Path; + llvm::sys::path::append(TempPath, "libclang-%%%%%%%%%%%%"); + int FD; + if (llvm::sys::fs::createUniqueFile(TempPath, FD, TempPath)) + return; + File = std::string(TempPath.begin(), TempPath.end()); + llvm::raw_fd_ostream OS(FD, /*ShouldClose=*/true); + + // Write out the information about the invocation to it. + auto WriteStringKey = [&OS](StringRef Key, StringRef Value) { + OS << R"(")" << Key << R"(":")"; + OS << llvm::yaml::escape(Value) << '"'; + }; + OS << '{'; + WriteStringKey("toolchain", Idx.getClangToolchainPath()); + OS << ','; + WriteStringKey("libclang.operation", + Op == OperationKind::ParseOperation ? "parse" : "complete"); + OS << ','; + OS << R"("libclang.opts":)" << ParseOptions; + OS << ','; + OS << R"("args":[)"; + for (const auto &I : llvm::enumerate(Args)) { + if (I.index()) + OS << ','; + OS << '"' << llvm::yaml::escape(I.value()) << '"'; + } + if (!InvocationArgs.empty()) { + OS << R"(],"invocation-args":[)"; + for (const auto &I : llvm::enumerate(InvocationArgs)) { + if (I.index()) + OS << ','; + OS << '"' << llvm::yaml::escape(I.value()) << '"'; + } + } + if (!UnsavedFiles.empty()) { + OS << R"(],"unsaved_file_hashes":[)"; + for (const auto &UF : llvm::enumerate(UnsavedFiles)) { + if (UF.index()) + OS << ','; + OS << '{'; + WriteStringKey("name", UF.value().Filename); + OS << ','; + llvm::MD5 Hash; + Hash.update(getContents(UF.value())); + llvm::MD5::MD5Result Result; + Hash.final(Result); + SmallString<32> Digest = Result.digest(); + WriteStringKey("md5", Digest); + OS << '}'; + } + } + OS << "]}"; +} + +LibclangInvocationReporter::~LibclangInvocationReporter() { + if (!File.empty()) + llvm::sys::fs::remove(File); +} diff --git a/gnu/llvm/clang/tools/libclang/CIndexer.h b/gnu/llvm/clang/tools/libclang/CIndexer.h new file mode 100644 index 00000000000..8a926fd2d9e --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CIndexer.h @@ -0,0 +1,153 @@ +//===- CIndexer.h - Clang-C Source Indexing Library -------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines CIndexer, a subclass of Indexer that provides extra +// functionality needed by the CIndex library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CINDEXER_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CINDEXER_H + +#include "clang-c/Index.h" +#include "clang/Frontend/PCHContainerOperations.h" +#include "llvm/ADT/STLExtras.h" +#include <utility> + +namespace llvm { + class CrashRecoveryContext; +} + +namespace clang { +class ASTUnit; +class MacroInfo; +class MacroDefinitionRecord; +class SourceLocation; +class Token; +class IdentifierInfo; + +class CIndexer { + bool OnlyLocalDecls; + bool DisplayDiagnostics; + unsigned Options; // CXGlobalOptFlags. + + std::string ResourcesPath; + std::shared_ptr<PCHContainerOperations> PCHContainerOps; + + std::string ToolchainPath; + + std::string InvocationEmissionPath; + +public: + CIndexer(std::shared_ptr<PCHContainerOperations> PCHContainerOps = + std::make_shared<PCHContainerOperations>()) + : OnlyLocalDecls(false), DisplayDiagnostics(false), + Options(CXGlobalOpt_None), PCHContainerOps(std::move(PCHContainerOps)) { + } + + /// Whether we only want to see "local" declarations (that did not + /// come from a previous precompiled header). If false, we want to see all + /// declarations. + bool getOnlyLocalDecls() const { return OnlyLocalDecls; } + void setOnlyLocalDecls(bool Local = true) { OnlyLocalDecls = Local; } + + bool getDisplayDiagnostics() const { return DisplayDiagnostics; } + void setDisplayDiagnostics(bool Display = true) { + DisplayDiagnostics = Display; + } + + std::shared_ptr<PCHContainerOperations> getPCHContainerOperations() const { + return PCHContainerOps; + } + + unsigned getCXGlobalOptFlags() const { return Options; } + void setCXGlobalOptFlags(unsigned options) { Options = options; } + + bool isOptEnabled(CXGlobalOptFlags opt) const { + return Options & opt; + } + + /// Get the path of the clang resource files. + const std::string &getClangResourcesPath(); + + StringRef getClangToolchainPath(); + + void setInvocationEmissionPath(StringRef Str) { + InvocationEmissionPath = Str; + } + + StringRef getInvocationEmissionPath() const { return InvocationEmissionPath; } +}; + +/// Logs information about a particular libclang operation like parsing to +/// a new file in the invocation emission path. +class LibclangInvocationReporter { +public: + enum class OperationKind { ParseOperation, CompletionOperation }; + + LibclangInvocationReporter(CIndexer &Idx, OperationKind Op, + unsigned ParseOptions, + llvm::ArrayRef<const char *> Args, + llvm::ArrayRef<std::string> InvocationArgs, + llvm::ArrayRef<CXUnsavedFile> UnsavedFiles); + ~LibclangInvocationReporter(); + +private: + std::string File; +}; + + /// Return the current size to request for "safety". + unsigned GetSafetyThreadStackSize(); + + /// Set the current size to request for "safety" (or 0, if safety + /// threads should not be used). + void SetSafetyThreadStackSize(unsigned Value); + + /// Execution the given code "safely", using crash recovery or safety + /// threads when possible. + /// + /// \return False if a crash was detected. + bool RunSafely(llvm::CrashRecoveryContext &CRC, llvm::function_ref<void()> Fn, + unsigned Size = 0); + + /// Set the thread priority to background. + /// FIXME: Move to llvm/Support. + void setThreadBackgroundPriority(); + + /// Print libclang's resource usage to standard error. + void PrintLibclangResourceUsage(CXTranslationUnit TU); + + namespace cxindex { + void printDiagsToStderr(ASTUnit *Unit); + + /// If \c MacroDefLoc points at a macro definition with \c II as + /// its name, this retrieves its MacroInfo. + MacroInfo *getMacroInfo(const IdentifierInfo &II, + SourceLocation MacroDefLoc, CXTranslationUnit TU); + + /// Retrieves the corresponding MacroInfo of a MacroDefinitionRecord. + const MacroInfo *getMacroInfo(const MacroDefinitionRecord *MacroDef, + CXTranslationUnit TU); + + /// If \c Loc resides inside the definition of \c MI and it points at + /// an identifier that has ever been a macro name, this returns the latest + /// MacroDefinitionRecord for that name, otherwise it returns NULL. + MacroDefinitionRecord *checkForMacroInMacroDefinition(const MacroInfo *MI, + SourceLocation Loc, + CXTranslationUnit TU); + + /// If \c Tok resides inside the definition of \c MI and it points at + /// an identifier that has ever been a macro name, this returns the latest + /// MacroDefinitionRecord for that name, otherwise it returns NULL. + MacroDefinitionRecord *checkForMacroInMacroDefinition(const MacroInfo *MI, + const Token &Tok, + CXTranslationUnit TU); + } + } + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CLog.h b/gnu/llvm/clang/tools/libclang/CLog.h new file mode 100644 index 00000000000..c25dbc03a52 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CLog.h @@ -0,0 +1,102 @@ +//===- CLog.h - Logging Interface -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CLOG_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CLOG_H + +#include "clang-c/Index.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" +#include <string> + +namespace llvm { +class format_object_base; +} + +namespace clang { + class FileEntry; + +namespace cxindex { + +class Logger; +typedef IntrusiveRefCntPtr<Logger> LogRef; + +/// Collects logging output and writes it to stderr when it's destructed. +/// Common use case: +/// \code +/// if (LogRef Log = Logger::make(__func__)) { +/// *Log << "stuff"; +/// } +/// \endcode +class Logger : public RefCountedBase<Logger> { + std::string Name; + bool Trace; + SmallString<64> Msg; + llvm::raw_svector_ostream LogOS; +public: + static const char *getEnvVar() { + static const char *sCachedVar = ::getenv("LIBCLANG_LOGGING"); + return sCachedVar; + } + static bool isLoggingEnabled() { return getEnvVar() != nullptr; } + static bool isStackTracingEnabled() { + if (const char *EnvOpt = Logger::getEnvVar()) + return llvm::StringRef(EnvOpt) == "2"; + return false; + } + static LogRef make(llvm::StringRef name, + bool trace = isStackTracingEnabled()) { + if (isLoggingEnabled()) + return new Logger(name, trace); + return nullptr; + } + + explicit Logger(llvm::StringRef name, bool trace) + : Name(name), Trace(trace), LogOS(Msg) { } + ~Logger(); + + Logger &operator<<(CXTranslationUnit); + Logger &operator<<(const FileEntry *FE); + Logger &operator<<(CXCursor cursor); + Logger &operator<<(CXSourceLocation); + Logger &operator<<(CXSourceRange); + Logger &operator<<(CXString); + Logger &operator<<(llvm::StringRef Str) { LogOS << Str; return *this; } + Logger &operator<<(const char *Str) { + if (Str) + LogOS << Str; + return *this; + } + Logger &operator<<(unsigned long N) { LogOS << N; return *this; } + Logger &operator<<(long N) { LogOS << N ; return *this; } + Logger &operator<<(unsigned int N) { LogOS << N; return *this; } + Logger &operator<<(int N) { LogOS << N; return *this; } + Logger &operator<<(char C) { LogOS << C; return *this; } + Logger &operator<<(unsigned char C) { LogOS << C; return *this; } + Logger &operator<<(signed char C) { LogOS << C; return *this; } + Logger &operator<<(const llvm::format_object_base &Fmt); +}; + +} +} + +/// Macros to automate common uses of Logger. Like this: +/// \code +/// LOG_FUNC_SECTION { +/// *Log << "blah"; +/// } +/// \endcode +#define LOG_SECTION(NAME) \ + if (clang::cxindex::LogRef Log = clang::cxindex::Logger::make(NAME)) +#define LOG_FUNC_SECTION LOG_SECTION(__func__) + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CMakeLists.txt b/gnu/llvm/clang/tools/libclang/CMakeLists.txt new file mode 100644 index 00000000000..bd0c945a5e1 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CMakeLists.txt @@ -0,0 +1,184 @@ +set(SOURCES + ARCMigrate.cpp + BuildSystem.cpp + CIndex.cpp + CIndexCXX.cpp + CIndexCodeCompletion.cpp + CIndexDiagnostic.cpp + CIndexHigh.cpp + CIndexInclusionStack.cpp + CIndexUSRs.cpp + CIndexer.cpp + CXComment.cpp + CXCursor.cpp + CXIndexDataConsumer.cpp + CXCompilationDatabase.cpp + CXLoadedDiagnostic.cpp + CXSourceLocation.cpp + CXStoredDiagnostic.cpp + CXString.cpp + CXType.cpp + Indexing.cpp + FatalErrorHandler.cpp + + ADDITIONAL_HEADERS + CIndexDiagnostic.h + CIndexer.h + CXCursor.h + CXLoadedDiagnostic.h + CXSourceLocation.h + CXString.h + CXTranslationUnit.h + CXType.h + Index_Internal.h + ../../include/clang-c/Index.h + ) + +set(LIBS + clangAST + clangBasic + clangDriver + clangFrontend + clangIndex + clangLex + clangSema + clangSerialization + clangTooling + LLVMSupport +) + +if (CLANG_ENABLE_ARCMT) + list(APPEND LIBS clangARCMigrate) +endif () + +if (TARGET clangTidyPlugin) + add_definitions(-DCLANG_TOOL_EXTRA_BUILD) + list(APPEND LIBS clangTidyPlugin) + list(APPEND LIBS clangIncludeFixerPlugin) + if(LLVM_ENABLE_MODULES) + list(APPEND LLVM_COMPILE_FLAGS "-fmodules-ignore-macro=CLANG_TOOL_EXTRA_BUILD") + endif() +endif () + +find_library(DL_LIBRARY_PATH dl) +if (DL_LIBRARY_PATH) + list(APPEND LIBS dl) +endif() + +option(LIBCLANG_BUILD_STATIC + "Build libclang as a static library (in addition to a shared one)" OFF) + +set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/libclang.exports) + +if(MSVC) + # Avoid LNK4197 by not specifying libclang.exports here. + # Each functions is exported as "dllexport" in include/clang-c. + # KB835326 + set(LLVM_EXPORTED_SYMBOL_FILE) +endif() + +if(LLVM_ENABLE_PIC OR WIN32) + set(ENABLE_SHARED SHARED) +endif() + +if((NOT LLVM_ENABLE_PIC OR LIBCLANG_BUILD_STATIC) AND NOT WIN32) + set(ENABLE_STATIC STATIC) +endif() + +if(WIN32) + set(output_name "libclang") +else() + set(output_name "clang") +endif() + +# libclang requires headers which need _ALL_SOURCE to build on AIX +if (UNIX AND ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + remove_definitions("-D_XOPEN_SOURCE=700") +endif() + +add_clang_library(libclang ${ENABLE_SHARED} ${ENABLE_STATIC} INSTALL_WITH_TOOLCHAIN + OUTPUT_NAME ${output_name} + ${SOURCES} + + DEPENDS + ClangDriverOptions + clang-resource-headers + + LINK_LIBS + ${LIBS} + + LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + Core + Support + ) + +if(ENABLE_SHARED) + if(WIN32) + set_target_properties(libclang + PROPERTIES + VERSION ${LIBCLANG_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + elseif(APPLE) + set(LIBCLANG_LINK_FLAGS " -Wl,-compatibility_version -Wl,1") + set(LIBCLANG_LINK_FLAGS "${LIBCLANG_LINK_FLAGS} -Wl,-current_version -Wl,${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + + set_property(TARGET libclang APPEND_STRING PROPERTY + LINK_FLAGS ${LIBCLANG_LINK_FLAGS}) + else() + set_target_properties(libclang + PROPERTIES + VERSION ${LIBCLANG_LIBRARY_VERSION} + DEFINE_SYMBOL _CINDEX_LIB_) + # FIXME: _CINDEX_LIB_ affects dllexport/dllimport on Win32. + if(LLVM_ENABLE_MODULES AND NOT WIN32) + target_compile_options(libclang PRIVATE + "-fmodules-ignore-macro=_CINDEX_LIB_" + ) + endif() + endif() +endif() + +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 + ) + +# LLVM_DISTRIBUTION_COMPONENTS requires that each component have both a +# component and an install-component target, so add a dummy libclang-headers +# target to allow using it in LLVM_DISTRIBUTION_COMPONENTS. +add_custom_target(libclang-headers) +set_target_properties(libclang-headers PROPERTIES FOLDER "Misc") + +if (NOT LLVM_ENABLE_IDE) + add_llvm_install_targets(install-libclang-headers + COMPONENT libclang-headers) +endif() + +# Create a target to install the python bindings to make them easier to +# distribute. Since the bindings are over libclang, which is installed +# unbundled to the clang version, follow suit. +foreach(PythonVersion ${CLANG_PYTHON_BINDINGS_VERSIONS}) + install(DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR}/../../bindings/python/clang + COMPONENT + libclang-python-bindings + DESTINATION + "lib${LLVM_LIBDIR_SUFFIX}/python${PythonVersion}/site-packages") +endforeach() +if(NOT LLVM_ENABLE_IDE) + add_custom_target(libclang-python-bindings) + add_llvm_install_targets(install-libclang-python-bindings + COMPONENT + libclang-python-bindings) +endif() + diff --git a/gnu/llvm/clang/tools/libclang/CXComment.cpp b/gnu/llvm/clang/tools/libclang/CXComment.cpp new file mode 100644 index 00000000000..bafaeb0fd00 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXComment.cpp @@ -0,0 +1,408 @@ +//===- CXComment.cpp - libclang APIs for manipulating CXComments ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines all libclang APIs related to walking comment AST. +// +//===----------------------------------------------------------------------===// + +#include "CXComment.h" +#include "CXCursor.h" +#include "CXString.h" +#include "clang-c/Documentation.h" +#include "clang-c/Index.h" +#include "clang/AST/Decl.h" +#include "clang/Index/CommentToXML.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" +#include <climits> + +using namespace clang; +using namespace clang::comments; +using namespace clang::cxcomment; + +CXComment clang_Cursor_getParsedComment(CXCursor C) { + using namespace clang::cxcursor; + + if (!clang_isDeclaration(C.kind)) + return createCXComment(nullptr, nullptr); + + const Decl *D = getCursorDecl(C); + const ASTContext &Context = getCursorContext(C); + const FullComment *FC = Context.getCommentForDecl(D, /*PP=*/nullptr); + + return createCXComment(FC, getCursorTU(C)); +} + +enum CXCommentKind clang_Comment_getKind(CXComment CXC) { + const Comment *C = getASTNode(CXC); + if (!C) + return CXComment_Null; + + switch (C->getCommentKind()) { + case Comment::NoCommentKind: + return CXComment_Null; + + case Comment::TextCommentKind: + return CXComment_Text; + + case Comment::InlineCommandCommentKind: + return CXComment_InlineCommand; + + case Comment::HTMLStartTagCommentKind: + return CXComment_HTMLStartTag; + + case Comment::HTMLEndTagCommentKind: + return CXComment_HTMLEndTag; + + case Comment::ParagraphCommentKind: + return CXComment_Paragraph; + + case Comment::BlockCommandCommentKind: + return CXComment_BlockCommand; + + case Comment::ParamCommandCommentKind: + return CXComment_ParamCommand; + + case Comment::TParamCommandCommentKind: + return CXComment_TParamCommand; + + case Comment::VerbatimBlockCommentKind: + return CXComment_VerbatimBlockCommand; + + case Comment::VerbatimBlockLineCommentKind: + return CXComment_VerbatimBlockLine; + + case Comment::VerbatimLineCommentKind: + return CXComment_VerbatimLine; + + case Comment::FullCommentKind: + return CXComment_FullComment; + } + llvm_unreachable("unknown CommentKind"); +} + +unsigned clang_Comment_getNumChildren(CXComment CXC) { + const Comment *C = getASTNode(CXC); + if (!C) + return 0; + + return C->child_count(); +} + +CXComment clang_Comment_getChild(CXComment CXC, unsigned ChildIdx) { + const Comment *C = getASTNode(CXC); + if (!C || ChildIdx >= C->child_count()) + return createCXComment(nullptr, nullptr); + + return createCXComment(*(C->child_begin() + ChildIdx), CXC.TranslationUnit); +} + +unsigned clang_Comment_isWhitespace(CXComment CXC) { + const Comment *C = getASTNode(CXC); + if (!C) + return false; + + if (const TextComment *TC = dyn_cast<TextComment>(C)) + return TC->isWhitespace(); + + if (const ParagraphComment *PC = dyn_cast<ParagraphComment>(C)) + return PC->isWhitespace(); + + return false; +} + +unsigned clang_InlineContentComment_hasTrailingNewline(CXComment CXC) { + const InlineContentComment *ICC = getASTNodeAs<InlineContentComment>(CXC); + if (!ICC) + return false; + + return ICC->hasTrailingNewline(); +} + +CXString clang_TextComment_getText(CXComment CXC) { + const TextComment *TC = getASTNodeAs<TextComment>(CXC); + if (!TC) + return cxstring::createNull(); + + return cxstring::createRef(TC->getText()); +} + +CXString clang_InlineCommandComment_getCommandName(CXComment CXC) { + const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); + if (!ICC) + return cxstring::createNull(); + + const CommandTraits &Traits = getCommandTraits(CXC); + return cxstring::createRef(ICC->getCommandName(Traits)); +} + +enum CXCommentInlineCommandRenderKind +clang_InlineCommandComment_getRenderKind(CXComment CXC) { + const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); + if (!ICC) + return CXCommentInlineCommandRenderKind_Normal; + + switch (ICC->getRenderKind()) { + case InlineCommandComment::RenderNormal: + return CXCommentInlineCommandRenderKind_Normal; + + case InlineCommandComment::RenderBold: + return CXCommentInlineCommandRenderKind_Bold; + + case InlineCommandComment::RenderMonospaced: + return CXCommentInlineCommandRenderKind_Monospaced; + + case InlineCommandComment::RenderEmphasized: + return CXCommentInlineCommandRenderKind_Emphasized; + + case InlineCommandComment::RenderAnchor: + return CXCommentInlineCommandRenderKind_Anchor; + } + llvm_unreachable("unknown InlineCommandComment::RenderKind"); +} + +unsigned clang_InlineCommandComment_getNumArgs(CXComment CXC) { + const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); + if (!ICC) + return 0; + + return ICC->getNumArgs(); +} + +CXString clang_InlineCommandComment_getArgText(CXComment CXC, + unsigned ArgIdx) { + const InlineCommandComment *ICC = getASTNodeAs<InlineCommandComment>(CXC); + if (!ICC || ArgIdx >= ICC->getNumArgs()) + return cxstring::createNull(); + + return cxstring::createRef(ICC->getArgText(ArgIdx)); +} + +CXString clang_HTMLTagComment_getTagName(CXComment CXC) { + const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); + if (!HTC) + return cxstring::createNull(); + + return cxstring::createRef(HTC->getTagName()); +} + +unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment CXC) { + const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); + if (!HST) + return false; + + return HST->isSelfClosing(); +} + +unsigned clang_HTMLStartTag_getNumAttrs(CXComment CXC) { + const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); + if (!HST) + return 0; + + return HST->getNumAttrs(); +} + +CXString clang_HTMLStartTag_getAttrName(CXComment CXC, unsigned AttrIdx) { + const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); + if (!HST || AttrIdx >= HST->getNumAttrs()) + return cxstring::createNull(); + + return cxstring::createRef(HST->getAttr(AttrIdx).Name); +} + +CXString clang_HTMLStartTag_getAttrValue(CXComment CXC, unsigned AttrIdx) { + const HTMLStartTagComment *HST = getASTNodeAs<HTMLStartTagComment>(CXC); + if (!HST || AttrIdx >= HST->getNumAttrs()) + return cxstring::createNull(); + + return cxstring::createRef(HST->getAttr(AttrIdx).Value); +} + +CXString clang_BlockCommandComment_getCommandName(CXComment CXC) { + const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); + if (!BCC) + return cxstring::createNull(); + + const CommandTraits &Traits = getCommandTraits(CXC); + return cxstring::createRef(BCC->getCommandName(Traits)); +} + +unsigned clang_BlockCommandComment_getNumArgs(CXComment CXC) { + const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); + if (!BCC) + return 0; + + return BCC->getNumArgs(); +} + +CXString clang_BlockCommandComment_getArgText(CXComment CXC, + unsigned ArgIdx) { + const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); + if (!BCC || ArgIdx >= BCC->getNumArgs()) + return cxstring::createNull(); + + return cxstring::createRef(BCC->getArgText(ArgIdx)); +} + +CXComment clang_BlockCommandComment_getParagraph(CXComment CXC) { + const BlockCommandComment *BCC = getASTNodeAs<BlockCommandComment>(CXC); + if (!BCC) + return createCXComment(nullptr, nullptr); + + return createCXComment(BCC->getParagraph(), CXC.TranslationUnit); +} + +CXString clang_ParamCommandComment_getParamName(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); + if (!PCC || !PCC->hasParamName()) + return cxstring::createNull(); + + return cxstring::createRef(PCC->getParamNameAsWritten()); +} + +unsigned clang_ParamCommandComment_isParamIndexValid(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); + if (!PCC) + return false; + + return PCC->isParamIndexValid(); +} + +unsigned clang_ParamCommandComment_getParamIndex(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); + if (!PCC || !PCC->isParamIndexValid() || PCC->isVarArgParam()) + return ParamCommandComment::InvalidParamIndex; + + return PCC->getParamIndex(); +} + +unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); + if (!PCC) + return false; + + return PCC->isDirectionExplicit(); +} + +enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( + CXComment CXC) { + const ParamCommandComment *PCC = getASTNodeAs<ParamCommandComment>(CXC); + if (!PCC) + return CXCommentParamPassDirection_In; + + switch (PCC->getDirection()) { + case ParamCommandComment::In: + return CXCommentParamPassDirection_In; + + case ParamCommandComment::Out: + return CXCommentParamPassDirection_Out; + + case ParamCommandComment::InOut: + return CXCommentParamPassDirection_InOut; + } + llvm_unreachable("unknown ParamCommandComment::PassDirection"); +} + +CXString clang_TParamCommandComment_getParamName(CXComment CXC) { + const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); + if (!TPCC || !TPCC->hasParamName()) + return cxstring::createNull(); + + return cxstring::createRef(TPCC->getParamNameAsWritten()); +} + +unsigned clang_TParamCommandComment_isParamPositionValid(CXComment CXC) { + const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); + if (!TPCC) + return false; + + return TPCC->isPositionValid(); +} + +unsigned clang_TParamCommandComment_getDepth(CXComment CXC) { + const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); + if (!TPCC || !TPCC->isPositionValid()) + return 0; + + return TPCC->getDepth(); +} + +unsigned clang_TParamCommandComment_getIndex(CXComment CXC, unsigned Depth) { + const TParamCommandComment *TPCC = getASTNodeAs<TParamCommandComment>(CXC); + if (!TPCC || !TPCC->isPositionValid() || Depth >= TPCC->getDepth()) + return 0; + + return TPCC->getIndex(Depth); +} + +CXString clang_VerbatimBlockLineComment_getText(CXComment CXC) { + const VerbatimBlockLineComment *VBL = + getASTNodeAs<VerbatimBlockLineComment>(CXC); + if (!VBL) + return cxstring::createNull(); + + return cxstring::createRef(VBL->getText()); +} + +CXString clang_VerbatimLineComment_getText(CXComment CXC) { + const VerbatimLineComment *VLC = getASTNodeAs<VerbatimLineComment>(CXC); + if (!VLC) + return cxstring::createNull(); + + return cxstring::createRef(VLC->getText()); +} + +//===----------------------------------------------------------------------===// +// Converting comments to XML. +//===----------------------------------------------------------------------===// + +CXString clang_HTMLTagComment_getAsString(CXComment CXC) { + const HTMLTagComment *HTC = getASTNodeAs<HTMLTagComment>(CXC); + if (!HTC) + return cxstring::createNull(); + + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new clang::index::CommentToXMLConverter(); + + SmallString<128> Text; + TU->CommentToXML->convertHTMLTagNodeToText( + HTC, Text, cxtu::getASTUnit(TU)->getASTContext()); + return cxstring::createDup(Text.str()); +} + +CXString clang_FullComment_getAsHTML(CXComment CXC) { + const FullComment *FC = getASTNodeAs<FullComment>(CXC); + if (!FC) + return cxstring::createNull(); + + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new clang::index::CommentToXMLConverter(); + + SmallString<1024> HTML; + TU->CommentToXML + ->convertCommentToHTML(FC, HTML, cxtu::getASTUnit(TU)->getASTContext()); + return cxstring::createDup(HTML.str()); +} + +CXString clang_FullComment_getAsXML(CXComment CXC) { + const FullComment *FC = getASTNodeAs<FullComment>(CXC); + if (!FC) + return cxstring::createNull(); + + CXTranslationUnit TU = CXC.TranslationUnit; + if (!TU->CommentToXML) + TU->CommentToXML = new clang::index::CommentToXMLConverter(); + + SmallString<1024> XML; + TU->CommentToXML + ->convertCommentToXML(FC, XML, cxtu::getASTUnit(TU)->getASTContext()); + return cxstring::createDup(XML.str()); +} + diff --git a/gnu/llvm/clang/tools/libclang/CXComment.h b/gnu/llvm/clang/tools/libclang/CXComment.h new file mode 100644 index 00000000000..30be06419cc --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXComment.h @@ -0,0 +1,63 @@ +//===- CXComment.h - Routines for manipulating CXComments -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXComments. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXCOMMENT_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXCOMMENT_H + +#include "CXTranslationUnit.h" +#include "clang-c/Documentation.h" +#include "clang-c/Index.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Comment.h" +#include "clang/Frontend/ASTUnit.h" + +namespace clang { +namespace comments { + class CommandTraits; +} + +namespace cxcomment { + +static inline CXComment createCXComment(const comments::Comment *C, + CXTranslationUnit TU) { + CXComment Result; + Result.ASTNode = C; + Result.TranslationUnit = TU; + return Result; +} + +static inline const comments::Comment *getASTNode(CXComment CXC) { + return static_cast<const comments::Comment *>(CXC.ASTNode); +} + +template<typename T> +static inline const T *getASTNodeAs(CXComment CXC) { + const comments::Comment *C = getASTNode(CXC); + if (!C) + return nullptr; + + return dyn_cast<T>(C); +} + +static inline ASTContext &getASTContext(CXComment CXC) { + return cxtu::getASTUnit(CXC.TranslationUnit)->getASTContext(); +} + +static inline comments::CommandTraits &getCommandTraits(CXComment CXC) { + return getASTContext(CXC).getCommentCommandTraits(); +} + +} // end namespace cxcomment +} // end namespace clang + +#endif + diff --git a/gnu/llvm/clang/tools/libclang/CXCompilationDatabase.cpp b/gnu/llvm/clang/tools/libclang/CXCompilationDatabase.cpp new file mode 100644 index 00000000000..2ca532659d3 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXCompilationDatabase.cpp @@ -0,0 +1,167 @@ +#include "clang-c/CXCompilationDatabase.h" +#include "CXString.h" +#include "clang/Tooling/CompilationDatabase.h" +#include <cstdio> + +using namespace clang; +using namespace clang::tooling; + +// FIXME: do something more useful with the error message +CXCompilationDatabase +clang_CompilationDatabase_fromDirectory(const char *BuildDir, + CXCompilationDatabase_Error *ErrorCode) +{ + std::string ErrorMsg; + CXCompilationDatabase_Error Err = CXCompilationDatabase_NoError; + + std::unique_ptr<CompilationDatabase> db = + CompilationDatabase::loadFromDirectory(BuildDir, ErrorMsg); + + if (!db) { + fprintf(stderr, "LIBCLANG TOOLING ERROR: %s\n", ErrorMsg.c_str()); + Err = CXCompilationDatabase_CanNotLoadDatabase; + } + + if (ErrorCode) + *ErrorCode = Err; + + return db.release(); +} + +void +clang_CompilationDatabase_dispose(CXCompilationDatabase CDb) +{ + delete static_cast<CompilationDatabase *>(CDb); +} + +struct AllocatedCXCompileCommands +{ + std::vector<CompileCommand> CCmd; + + AllocatedCXCompileCommands(std::vector<CompileCommand> Cmd) + : CCmd(std::move(Cmd)) {} +}; + +CXCompileCommands +clang_CompilationDatabase_getCompileCommands(CXCompilationDatabase CDb, + const char *CompleteFileName) +{ + if (CompilationDatabase *db = static_cast<CompilationDatabase *>(CDb)) { + std::vector<CompileCommand> CCmd(db->getCompileCommands(CompleteFileName)); + if (!CCmd.empty()) + return new AllocatedCXCompileCommands(std::move(CCmd)); + } + + return nullptr; +} + +CXCompileCommands +clang_CompilationDatabase_getAllCompileCommands(CXCompilationDatabase CDb) { + if (CompilationDatabase *db = static_cast<CompilationDatabase *>(CDb)) { + std::vector<CompileCommand> CCmd(db->getAllCompileCommands()); + if (!CCmd.empty()) + return new AllocatedCXCompileCommands(std::move(CCmd)); + } + + return nullptr; +} + +void +clang_CompileCommands_dispose(CXCompileCommands Cmds) +{ + delete static_cast<AllocatedCXCompileCommands *>(Cmds); +} + +unsigned +clang_CompileCommands_getSize(CXCompileCommands Cmds) +{ + if (!Cmds) + return 0; + + AllocatedCXCompileCommands *ACC = + static_cast<AllocatedCXCompileCommands *>(Cmds); + + return ACC->CCmd.size(); +} + +CXCompileCommand +clang_CompileCommands_getCommand(CXCompileCommands Cmds, unsigned I) +{ + if (!Cmds) + return nullptr; + + AllocatedCXCompileCommands *ACC = + static_cast<AllocatedCXCompileCommands *>(Cmds); + + if (I >= ACC->CCmd.size()) + return nullptr; + + return &ACC->CCmd[I]; +} + +CXString +clang_CompileCommand_getDirectory(CXCompileCommand CCmd) +{ + if (!CCmd) + return cxstring::createNull(); + + CompileCommand *cmd = static_cast<CompileCommand *>(CCmd); + return cxstring::createRef(cmd->Directory.c_str()); +} + +CXString +clang_CompileCommand_getFilename(CXCompileCommand CCmd) +{ + if (!CCmd) + return cxstring::createNull(); + + CompileCommand *cmd = static_cast<CompileCommand *>(CCmd); + return cxstring::createRef(cmd->Filename.c_str()); +} + +unsigned +clang_CompileCommand_getNumArgs(CXCompileCommand CCmd) +{ + if (!CCmd) + return 0; + + return static_cast<CompileCommand *>(CCmd)->CommandLine.size(); +} + +CXString +clang_CompileCommand_getArg(CXCompileCommand CCmd, unsigned Arg) +{ + if (!CCmd) + return cxstring::createNull(); + + CompileCommand *Cmd = static_cast<CompileCommand *>(CCmd); + + if (Arg >= Cmd->CommandLine.size()) + return cxstring::createNull(); + + return cxstring::createRef(Cmd->CommandLine[Arg].c_str()); +} + +unsigned +clang_CompileCommand_getNumMappedSources(CXCompileCommand CCmd) +{ + // Left here for backward compatibility. No mapped sources exists in the C++ + // backend anymore. + return 0; +} + +CXString +clang_CompileCommand_getMappedSourcePath(CXCompileCommand CCmd, unsigned I) +{ + // Left here for backward compatibility. No mapped sources exists in the C++ + // backend anymore. + return cxstring::createNull(); +} + +CXString +clang_CompileCommand_getMappedSourceContent(CXCompileCommand CCmd, unsigned I) +{ + // Left here for backward compatibility. No mapped sources exists in the C++ + // backend anymore. + return cxstring::createNull(); +} diff --git a/gnu/llvm/clang/tools/libclang/CXCursor.cpp b/gnu/llvm/clang/tools/libclang/CXCursor.cpp new file mode 100644 index 00000000000..04b713c68b8 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXCursor.cpp @@ -0,0 +1,1634 @@ +//===- CXCursor.cpp - Routines for manipulating CXCursors -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. It should be the +// only file that has internal knowledge of the encoding of the data in +// CXCursor. +// +//===----------------------------------------------------------------------===// + +#include "CXTranslationUnit.h" +#include "CXCursor.h" +#include "CXString.h" +#include "CXType.h" +#include "clang-c/Index.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Frontend/ASTUnit.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace cxcursor; + +CXCursor cxcursor::MakeCXCursorInvalid(CXCursorKind K, CXTranslationUnit TU) { + assert(K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid); + CXCursor C = { K, 0, { nullptr, nullptr, TU } }; + return C; +} + +static CXCursorKind GetCursorKind(const Attr *A) { + assert(A && "Invalid arguments!"); + switch (A->getKind()) { + default: break; + case attr::IBAction: return CXCursor_IBActionAttr; + case attr::IBOutlet: return CXCursor_IBOutletAttr; + case attr::IBOutletCollection: return CXCursor_IBOutletCollectionAttr; + case attr::Final: return CXCursor_CXXFinalAttr; + case attr::Override: return CXCursor_CXXOverrideAttr; + case attr::Annotate: return CXCursor_AnnotateAttr; + case attr::AsmLabel: return CXCursor_AsmLabelAttr; + case attr::Packed: return CXCursor_PackedAttr; + case attr::Pure: return CXCursor_PureAttr; + case attr::Const: return CXCursor_ConstAttr; + case attr::NoDuplicate: return CXCursor_NoDuplicateAttr; + case attr::CUDAConstant: return CXCursor_CUDAConstantAttr; + case attr::CUDADevice: return CXCursor_CUDADeviceAttr; + case attr::CUDAGlobal: return CXCursor_CUDAGlobalAttr; + case attr::CUDAHost: return CXCursor_CUDAHostAttr; + case attr::CUDAShared: return CXCursor_CUDASharedAttr; + case attr::Visibility: return CXCursor_VisibilityAttr; + case attr::DLLExport: return CXCursor_DLLExport; + case attr::DLLImport: return CXCursor_DLLImport; + case attr::NSReturnsRetained: return CXCursor_NSReturnsRetained; + case attr::NSReturnsNotRetained: return CXCursor_NSReturnsNotRetained; + case attr::NSReturnsAutoreleased: return CXCursor_NSReturnsAutoreleased; + case attr::NSConsumesSelf: return CXCursor_NSConsumesSelf; + case attr::NSConsumed: return CXCursor_NSConsumed; + case attr::ObjCException: return CXCursor_ObjCException; + case attr::ObjCNSObject: return CXCursor_ObjCNSObject; + case attr::ObjCIndependentClass: return CXCursor_ObjCIndependentClass; + case attr::ObjCPreciseLifetime: return CXCursor_ObjCPreciseLifetime; + case attr::ObjCReturnsInnerPointer: return CXCursor_ObjCReturnsInnerPointer; + case attr::ObjCRequiresSuper: return CXCursor_ObjCRequiresSuper; + case attr::ObjCRootClass: return CXCursor_ObjCRootClass; + case attr::ObjCSubclassingRestricted: return CXCursor_ObjCSubclassingRestricted; + case attr::ObjCExplicitProtocolImpl: return CXCursor_ObjCExplicitProtocolImpl; + case attr::ObjCDesignatedInitializer: return CXCursor_ObjCDesignatedInitializer; + case attr::ObjCRuntimeVisible: return CXCursor_ObjCRuntimeVisible; + case attr::ObjCBoxable: return CXCursor_ObjCBoxable; + case attr::FlagEnum: return CXCursor_FlagEnum; + case attr::Convergent: return CXCursor_ConvergentAttr; + case attr::WarnUnused: return CXCursor_WarnUnusedAttr; + case attr::WarnUnusedResult: return CXCursor_WarnUnusedResultAttr; + case attr::Aligned: return CXCursor_AlignedAttr; + } + + return CXCursor_UnexposedAttr; +} + +CXCursor cxcursor::MakeCXCursor(const Attr *A, const Decl *Parent, + CXTranslationUnit TU) { + assert(A && Parent && TU && "Invalid arguments!"); + CXCursor C = { GetCursorKind(A), 0, { Parent, A, TU } }; + return C; +} + +CXCursor cxcursor::MakeCXCursor(const Decl *D, CXTranslationUnit TU, + SourceRange RegionOfInterest, + bool FirstInDeclGroup) { + assert(D && TU && "Invalid arguments!"); + + CXCursorKind K = getCursorKindForDecl(D); + + if (K == CXCursor_ObjCClassMethodDecl || + K == CXCursor_ObjCInstanceMethodDecl) { + int SelectorIdIndex = -1; + // Check if cursor points to a selector id. + if (RegionOfInterest.isValid() && + RegionOfInterest.getBegin() == RegionOfInterest.getEnd()) { + SmallVector<SourceLocation, 16> SelLocs; + cast<ObjCMethodDecl>(D)->getSelectorLocs(SelLocs); + SmallVectorImpl<SourceLocation>::iterator I = + llvm::find(SelLocs, RegionOfInterest.getBegin()); + if (I != SelLocs.end()) + SelectorIdIndex = I - SelLocs.begin(); + } + CXCursor C = { K, SelectorIdIndex, + { D, (void*)(intptr_t) (FirstInDeclGroup ? 1 : 0), TU }}; + return C; + } + + CXCursor C = { K, 0, { D, (void*)(intptr_t) (FirstInDeclGroup ? 1 : 0), TU }}; + return C; +} + +CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, + CXTranslationUnit TU, + SourceRange RegionOfInterest) { + assert(S && TU && "Invalid arguments!"); + CXCursorKind K = CXCursor_NotImplemented; + + switch (S->getStmtClass()) { + case Stmt::NoStmtClass: + break; + + case Stmt::CaseStmtClass: + K = CXCursor_CaseStmt; + break; + + case Stmt::DefaultStmtClass: + K = CXCursor_DefaultStmt; + break; + + case Stmt::IfStmtClass: + K = CXCursor_IfStmt; + break; + + case Stmt::SwitchStmtClass: + K = CXCursor_SwitchStmt; + break; + + case Stmt::WhileStmtClass: + K = CXCursor_WhileStmt; + break; + + case Stmt::DoStmtClass: + K = CXCursor_DoStmt; + break; + + case Stmt::ForStmtClass: + K = CXCursor_ForStmt; + break; + + case Stmt::GotoStmtClass: + K = CXCursor_GotoStmt; + break; + + case Stmt::IndirectGotoStmtClass: + K = CXCursor_IndirectGotoStmt; + break; + + case Stmt::ContinueStmtClass: + K = CXCursor_ContinueStmt; + break; + + case Stmt::BreakStmtClass: + K = CXCursor_BreakStmt; + break; + + case Stmt::ReturnStmtClass: + K = CXCursor_ReturnStmt; + break; + + case Stmt::GCCAsmStmtClass: + K = CXCursor_GCCAsmStmt; + break; + + case Stmt::MSAsmStmtClass: + K = CXCursor_MSAsmStmt; + break; + + case Stmt::ObjCAtTryStmtClass: + K = CXCursor_ObjCAtTryStmt; + break; + + case Stmt::ObjCAtCatchStmtClass: + K = CXCursor_ObjCAtCatchStmt; + break; + + case Stmt::ObjCAtFinallyStmtClass: + K = CXCursor_ObjCAtFinallyStmt; + break; + + case Stmt::ObjCAtThrowStmtClass: + K = CXCursor_ObjCAtThrowStmt; + break; + + case Stmt::ObjCAtSynchronizedStmtClass: + K = CXCursor_ObjCAtSynchronizedStmt; + break; + + case Stmt::ObjCAutoreleasePoolStmtClass: + K = CXCursor_ObjCAutoreleasePoolStmt; + break; + + case Stmt::ObjCForCollectionStmtClass: + K = CXCursor_ObjCForCollectionStmt; + break; + + case Stmt::CXXCatchStmtClass: + K = CXCursor_CXXCatchStmt; + break; + + case Stmt::CXXTryStmtClass: + K = CXCursor_CXXTryStmt; + break; + + case Stmt::CXXForRangeStmtClass: + K = CXCursor_CXXForRangeStmt; + break; + + case Stmt::SEHTryStmtClass: + K = CXCursor_SEHTryStmt; + break; + + case Stmt::SEHExceptStmtClass: + K = CXCursor_SEHExceptStmt; + break; + + case Stmt::SEHFinallyStmtClass: + K = CXCursor_SEHFinallyStmt; + break; + + case Stmt::SEHLeaveStmtClass: + K = CXCursor_SEHLeaveStmt; + break; + + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoreturnStmtClass: + K = CXCursor_UnexposedStmt; + break; + + case Stmt::ArrayTypeTraitExprClass: + case Stmt::AsTypeExprClass: + case Stmt::AtomicExprClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::TypeTraitExprClass: + case Stmt::CoawaitExprClass: + case Stmt::ConceptSpecializationExprClass: + case Stmt::RequiresExprClass: + case Stmt::DependentCoawaitExprClass: + case Stmt::CoyieldExprClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: + case Stmt::CXXFoldExprClass: + case Stmt::CXXRewrittenBinaryOperatorClass: + case Stmt::CXXStdInitializerListExprClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::CXXUuidofExprClass: + case Stmt::ChooseExprClass: + case Stmt::DesignatedInitExprClass: + case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitLoopExprClass: + case Stmt::ArrayInitIndexExprClass: + case Stmt::ExprWithCleanupsClass: + case Stmt::ExpressionTraitExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::ImplicitCastExprClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::NoInitExprClass: + case Stmt::MaterializeTemporaryExprClass: + case Stmt::ObjCIndirectCopyRestoreExprClass: + case Stmt::OffsetOfExprClass: + case Stmt::ParenListExprClass: + case Stmt::PredefinedExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::SourceLocExprClass: + case Stmt::ConvertVectorExprClass: + case Stmt::VAArgExprClass: + case Stmt::ObjCArrayLiteralClass: + case Stmt::ObjCDictionaryLiteralClass: + case Stmt::ObjCBoxedExprClass: + case Stmt::ObjCSubscriptRefExprClass: + K = CXCursor_UnexposedExpr; + break; + + case Stmt::OpaqueValueExprClass: + if (Expr *Src = cast<OpaqueValueExpr>(S)->getSourceExpr()) + return MakeCXCursor(Src, Parent, TU, RegionOfInterest); + K = CXCursor_UnexposedExpr; + break; + + case Stmt::PseudoObjectExprClass: + return MakeCXCursor(cast<PseudoObjectExpr>(S)->getSyntacticForm(), + Parent, TU, RegionOfInterest); + + case Stmt::CompoundStmtClass: + K = CXCursor_CompoundStmt; + break; + + case Stmt::NullStmtClass: + K = CXCursor_NullStmt; + break; + + case Stmt::LabelStmtClass: + K = CXCursor_LabelStmt; + break; + + case Stmt::AttributedStmtClass: + K = CXCursor_UnexposedStmt; + break; + + case Stmt::DeclStmtClass: + K = CXCursor_DeclStmt; + break; + + case Stmt::CapturedStmtClass: + K = CXCursor_UnexposedStmt; + break; + + case Stmt::IntegerLiteralClass: + K = CXCursor_IntegerLiteral; + break; + + case Stmt::FixedPointLiteralClass: + K = CXCursor_FixedPointLiteral; + break; + + case Stmt::FloatingLiteralClass: + K = CXCursor_FloatingLiteral; + break; + + case Stmt::ImaginaryLiteralClass: + K = CXCursor_ImaginaryLiteral; + break; + + case Stmt::StringLiteralClass: + K = CXCursor_StringLiteral; + break; + + case Stmt::CharacterLiteralClass: + K = CXCursor_CharacterLiteral; + break; + + case Stmt::ConstantExprClass: + return MakeCXCursor(cast<ConstantExpr>(S)->getSubExpr(), + Parent, TU, RegionOfInterest); + + case Stmt::ParenExprClass: + K = CXCursor_ParenExpr; + break; + + case Stmt::UnaryOperatorClass: + K = CXCursor_UnaryOperator; + break; + + case Stmt::UnaryExprOrTypeTraitExprClass: + case Stmt::CXXNoexceptExprClass: + K = CXCursor_UnaryExpr; + break; + + case Stmt::MSPropertySubscriptExprClass: + case Stmt::ArraySubscriptExprClass: + K = CXCursor_ArraySubscriptExpr; + break; + + case Stmt::OMPArraySectionExprClass: + K = CXCursor_OMPArraySectionExpr; + break; + + case Stmt::BinaryOperatorClass: + K = CXCursor_BinaryOperator; + break; + + case Stmt::CompoundAssignOperatorClass: + K = CXCursor_CompoundAssignOperator; + break; + + case Stmt::ConditionalOperatorClass: + K = CXCursor_ConditionalOperator; + break; + + case Stmt::CStyleCastExprClass: + K = CXCursor_CStyleCastExpr; + break; + + case Stmt::CompoundLiteralExprClass: + K = CXCursor_CompoundLiteralExpr; + break; + + case Stmt::InitListExprClass: + K = CXCursor_InitListExpr; + break; + + case Stmt::AddrLabelExprClass: + K = CXCursor_AddrLabelExpr; + break; + + case Stmt::StmtExprClass: + K = CXCursor_StmtExpr; + break; + + case Stmt::GenericSelectionExprClass: + K = CXCursor_GenericSelectionExpr; + break; + + case Stmt::GNUNullExprClass: + K = CXCursor_GNUNullExpr; + break; + + case Stmt::CXXStaticCastExprClass: + K = CXCursor_CXXStaticCastExpr; + break; + + case Stmt::CXXDynamicCastExprClass: + K = CXCursor_CXXDynamicCastExpr; + break; + + case Stmt::CXXReinterpretCastExprClass: + K = CXCursor_CXXReinterpretCastExpr; + break; + + case Stmt::CXXConstCastExprClass: + K = CXCursor_CXXConstCastExpr; + break; + + case Stmt::CXXFunctionalCastExprClass: + K = CXCursor_CXXFunctionalCastExpr; + break; + + case Stmt::CXXTypeidExprClass: + K = CXCursor_CXXTypeidExpr; + break; + + case Stmt::CXXBoolLiteralExprClass: + K = CXCursor_CXXBoolLiteralExpr; + break; + + case Stmt::CXXNullPtrLiteralExprClass: + K = CXCursor_CXXNullPtrLiteralExpr; + break; + + case Stmt::CXXThisExprClass: + K = CXCursor_CXXThisExpr; + break; + + case Stmt::CXXThrowExprClass: + K = CXCursor_CXXThrowExpr; + break; + + case Stmt::CXXNewExprClass: + K = CXCursor_CXXNewExpr; + break; + + case Stmt::CXXDeleteExprClass: + K = CXCursor_CXXDeleteExpr; + break; + + case Stmt::ObjCStringLiteralClass: + K = CXCursor_ObjCStringLiteral; + break; + + case Stmt::ObjCEncodeExprClass: + K = CXCursor_ObjCEncodeExpr; + break; + + case Stmt::ObjCSelectorExprClass: + K = CXCursor_ObjCSelectorExpr; + break; + + case Stmt::ObjCProtocolExprClass: + K = CXCursor_ObjCProtocolExpr; + break; + + case Stmt::ObjCBoolLiteralExprClass: + K = CXCursor_ObjCBoolLiteralExpr; + break; + + case Stmt::ObjCAvailabilityCheckExprClass: + K = CXCursor_ObjCAvailabilityCheckExpr; + break; + + case Stmt::ObjCBridgedCastExprClass: + K = CXCursor_ObjCBridgedCastExpr; + break; + + case Stmt::BlockExprClass: + K = CXCursor_BlockExpr; + break; + + case Stmt::PackExpansionExprClass: + K = CXCursor_PackExpansionExpr; + break; + + case Stmt::SizeOfPackExprClass: + K = CXCursor_SizeOfPackExpr; + break; + + case Stmt::DeclRefExprClass: + if (const ImplicitParamDecl *IPD = + dyn_cast_or_null<ImplicitParamDecl>(cast<DeclRefExpr>(S)->getDecl())) { + if (const ObjCMethodDecl *MD = + dyn_cast<ObjCMethodDecl>(IPD->getDeclContext())) { + if (MD->getSelfDecl() == IPD) { + K = CXCursor_ObjCSelfExpr; + break; + } + } + } + + K = CXCursor_DeclRefExpr; + break; + + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: + case Stmt::SubstNonTypeTemplateParmPackExprClass: + case Stmt::FunctionParmPackExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::TypoExprClass: // A typo could actually be a DeclRef or a MemberRef + K = CXCursor_DeclRefExpr; + break; + + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::MemberExprClass: + case Stmt::MSPropertyRefExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCIvarRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + case Stmt::UnresolvedMemberExprClass: + K = CXCursor_MemberRefExpr; + break; + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::CUDAKernelCallExprClass: + case Stmt::CXXConstructExprClass: + case Stmt::CXXInheritedCtorInitExprClass: + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::UserDefinedLiteralClass: + K = CXCursor_CallExpr; + break; + + case Stmt::LambdaExprClass: + K = CXCursor_LambdaExpr; + break; + + case Stmt::ObjCMessageExprClass: { + K = CXCursor_ObjCMessageExpr; + int SelectorIdIndex = -1; + // Check if cursor points to a selector id. + if (RegionOfInterest.isValid() && + RegionOfInterest.getBegin() == RegionOfInterest.getEnd()) { + SmallVector<SourceLocation, 16> SelLocs; + cast<ObjCMessageExpr>(S)->getSelectorLocs(SelLocs); + SmallVectorImpl<SourceLocation>::iterator I = + llvm::find(SelLocs, RegionOfInterest.getBegin()); + if (I != SelLocs.end()) + SelectorIdIndex = I - SelLocs.begin(); + } + CXCursor C = { K, 0, { Parent, S, TU } }; + return getSelectorIdentifierCursor(SelectorIdIndex, C); + } + + case Stmt::MSDependentExistsStmtClass: + K = CXCursor_UnexposedStmt; + break; + case Stmt::OMPParallelDirectiveClass: + K = CXCursor_OMPParallelDirective; + break; + case Stmt::OMPSimdDirectiveClass: + K = CXCursor_OMPSimdDirective; + break; + case Stmt::OMPForDirectiveClass: + K = CXCursor_OMPForDirective; + break; + case Stmt::OMPForSimdDirectiveClass: + K = CXCursor_OMPForSimdDirective; + break; + case Stmt::OMPSectionsDirectiveClass: + K = CXCursor_OMPSectionsDirective; + break; + case Stmt::OMPSectionDirectiveClass: + K = CXCursor_OMPSectionDirective; + break; + case Stmt::OMPSingleDirectiveClass: + K = CXCursor_OMPSingleDirective; + break; + case Stmt::OMPMasterDirectiveClass: + K = CXCursor_OMPMasterDirective; + break; + case Stmt::OMPCriticalDirectiveClass: + K = CXCursor_OMPCriticalDirective; + break; + case Stmt::OMPParallelForDirectiveClass: + K = CXCursor_OMPParallelForDirective; + break; + case Stmt::OMPParallelForSimdDirectiveClass: + K = CXCursor_OMPParallelForSimdDirective; + break; + case Stmt::OMPParallelMasterDirectiveClass: + K = CXCursor_OMPParallelMasterDirective; + break; + case Stmt::OMPParallelSectionsDirectiveClass: + K = CXCursor_OMPParallelSectionsDirective; + break; + case Stmt::OMPTaskDirectiveClass: + K = CXCursor_OMPTaskDirective; + break; + case Stmt::OMPTaskyieldDirectiveClass: + K = CXCursor_OMPTaskyieldDirective; + break; + case Stmt::OMPBarrierDirectiveClass: + K = CXCursor_OMPBarrierDirective; + break; + case Stmt::OMPTaskwaitDirectiveClass: + K = CXCursor_OMPTaskwaitDirective; + break; + case Stmt::OMPTaskgroupDirectiveClass: + K = CXCursor_OMPTaskgroupDirective; + break; + case Stmt::OMPFlushDirectiveClass: + K = CXCursor_OMPFlushDirective; + break; + case Stmt::OMPOrderedDirectiveClass: + K = CXCursor_OMPOrderedDirective; + break; + case Stmt::OMPAtomicDirectiveClass: + K = CXCursor_OMPAtomicDirective; + break; + case Stmt::OMPTargetDirectiveClass: + K = CXCursor_OMPTargetDirective; + break; + 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; + case Stmt::OMPCancellationPointDirectiveClass: + K = CXCursor_OMPCancellationPointDirective; + break; + case Stmt::OMPCancelDirectiveClass: + K = CXCursor_OMPCancelDirective; + break; + case Stmt::OMPTaskLoopDirectiveClass: + K = CXCursor_OMPTaskLoopDirective; + break; + case Stmt::OMPTaskLoopSimdDirectiveClass: + K = CXCursor_OMPTaskLoopSimdDirective; + break; + case Stmt::OMPMasterTaskLoopDirectiveClass: + K = CXCursor_OMPMasterTaskLoopDirective; + break; + case Stmt::OMPMasterTaskLoopSimdDirectiveClass: + K = CXCursor_OMPMasterTaskLoopSimdDirective; + break; + case Stmt::OMPParallelMasterTaskLoopDirectiveClass: + K = CXCursor_OMPParallelMasterTaskLoopDirective; + break; + case Stmt::OMPParallelMasterTaskLoopSimdDirectiveClass: + K = CXCursor_OMPParallelMasterTaskLoopSimdDirective; + break; + 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; + case Stmt::OMPTargetSimdDirectiveClass: + K = CXCursor_OMPTargetSimdDirective; + break; + case Stmt::OMPTeamsDistributeDirectiveClass: + K = CXCursor_OMPTeamsDistributeDirective; + break; + case Stmt::OMPTeamsDistributeSimdDirectiveClass: + K = CXCursor_OMPTeamsDistributeSimdDirective; + break; + case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + K = CXCursor_OMPTeamsDistributeParallelForSimdDirective; + break; + case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + K = CXCursor_OMPTeamsDistributeParallelForDirective; + break; + case Stmt::OMPTargetTeamsDirectiveClass: + K = CXCursor_OMPTargetTeamsDirective; + break; + case Stmt::OMPTargetTeamsDistributeDirectiveClass: + K = CXCursor_OMPTargetTeamsDistributeDirective; + break; + case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: + K = CXCursor_OMPTargetTeamsDistributeParallelForDirective; + break; + case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: + K = CXCursor_OMPTargetTeamsDistributeParallelForSimdDirective; + break; + case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + K = CXCursor_OMPTargetTeamsDistributeSimdDirective; + break; + case Stmt::BuiltinBitCastExprClass: + K = CXCursor_BuiltinBitCastExpr; + } + + CXCursor C = { K, 0, { Parent, S, TU } }; + return C; +} + +CXCursor cxcursor::MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Super && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_ObjCSuperClassRef, 0, { Super, RawLoc, TU } }; + return C; +} + +std::pair<const ObjCInterfaceDecl *, SourceLocation> +cxcursor::getCursorObjCSuperClassRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCSuperClassRef); + return std::make_pair(static_cast<const ObjCInterfaceDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorObjCProtocolRef(const ObjCProtocolDecl *Proto, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Proto && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_ObjCProtocolRef, 0, { Proto, RawLoc, TU } }; + return C; +} + +std::pair<const ObjCProtocolDecl *, SourceLocation> +cxcursor::getCursorObjCProtocolRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCProtocolRef); + return std::make_pair(static_cast<const ObjCProtocolDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorObjCClassRef(const ObjCInterfaceDecl *Class, + SourceLocation Loc, + CXTranslationUnit TU) { + // 'Class' can be null for invalid code. + if (!Class) + return MakeCXCursorInvalid(CXCursor_InvalidCode); + assert(TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_ObjCClassRef, 0, { Class, RawLoc, TU } }; + return C; +} + +std::pair<const ObjCInterfaceDecl *, SourceLocation> +cxcursor::getCursorObjCClassRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCClassRef); + return std::make_pair(static_cast<const ObjCInterfaceDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorTypeRef(const TypeDecl *Type, SourceLocation Loc, + CXTranslationUnit TU) { + assert(Type && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_TypeRef, 0, { Type, RawLoc, TU } }; + return C; +} + +std::pair<const TypeDecl *, SourceLocation> +cxcursor::getCursorTypeRef(CXCursor C) { + assert(C.kind == CXCursor_TypeRef); + return std::make_pair(static_cast<const TypeDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorTemplateRef(const TemplateDecl *Template, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Template && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_TemplateRef, 0, { Template, RawLoc, TU } }; + return C; +} + +std::pair<const TemplateDecl *, SourceLocation> +cxcursor::getCursorTemplateRef(CXCursor C) { + assert(C.kind == CXCursor_TemplateRef); + return std::make_pair(static_cast<const TemplateDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorNamespaceRef(const NamedDecl *NS, + SourceLocation Loc, + CXTranslationUnit TU) { + + assert(NS && (isa<NamespaceDecl>(NS) || isa<NamespaceAliasDecl>(NS)) && TU && + "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_NamespaceRef, 0, { NS, RawLoc, TU } }; + return C; +} + +std::pair<const NamedDecl *, SourceLocation> +cxcursor::getCursorNamespaceRef(CXCursor C) { + assert(C.kind == CXCursor_NamespaceRef); + return std::make_pair(static_cast<const NamedDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorVariableRef(const VarDecl *Var, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Var && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_VariableRef, 0, { Var, RawLoc, TU } }; + return C; +} + +std::pair<const VarDecl *, SourceLocation> +cxcursor::getCursorVariableRef(CXCursor C) { + assert(C.kind == CXCursor_VariableRef); + return std::make_pair(static_cast<const VarDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorMemberRef(const FieldDecl *Field, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Field && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_MemberRef, 0, { Field, RawLoc, TU } }; + return C; +} + +std::pair<const FieldDecl *, SourceLocation> +cxcursor::getCursorMemberRef(CXCursor C) { + assert(C.kind == CXCursor_MemberRef); + return std::make_pair(static_cast<const FieldDecl *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorCXXBaseSpecifier(const CXXBaseSpecifier *B, + CXTranslationUnit TU){ + CXCursor C = { CXCursor_CXXBaseSpecifier, 0, { B, nullptr, TU } }; + return C; +} + +const CXXBaseSpecifier *cxcursor::getCursorCXXBaseSpecifier(CXCursor C) { + assert(C.kind == CXCursor_CXXBaseSpecifier); + return static_cast<const CXXBaseSpecifier*>(C.data[0]); +} + +CXCursor cxcursor::MakePreprocessingDirectiveCursor(SourceRange Range, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_PreprocessingDirective, 0, + { Range.getBegin().getPtrEncoding(), + Range.getEnd().getPtrEncoding(), + TU } + }; + return C; +} + +SourceRange cxcursor::getCursorPreprocessingDirective(CXCursor C) { + assert(C.kind == CXCursor_PreprocessingDirective); + SourceRange Range(SourceLocation::getFromPtrEncoding(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); + ASTUnit *TU = getCursorASTUnit(C); + return TU->mapRangeFromPreamble(Range); +} + +CXCursor cxcursor::MakeMacroDefinitionCursor(const MacroDefinitionRecord *MI, + CXTranslationUnit TU) { + CXCursor C = {CXCursor_MacroDefinition, 0, {MI, nullptr, TU}}; + return C; +} + +const MacroDefinitionRecord *cxcursor::getCursorMacroDefinition(CXCursor C) { + assert(C.kind == CXCursor_MacroDefinition); + return static_cast<const MacroDefinitionRecord *>(C.data[0]); +} + +CXCursor cxcursor::MakeMacroExpansionCursor(MacroExpansion *MI, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_MacroExpansion, 0, { MI, nullptr, TU } }; + return C; +} + +CXCursor cxcursor::MakeMacroExpansionCursor(MacroDefinitionRecord *MI, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Loc.isValid()); + CXCursor C = {CXCursor_MacroExpansion, 0, {MI, Loc.getPtrEncoding(), TU}}; + return C; +} + +const IdentifierInfo *cxcursor::MacroExpansionCursor::getName() const { + if (isPseudo()) + return getAsMacroDefinition()->getName(); + return getAsMacroExpansion()->getName(); +} +const MacroDefinitionRecord * +cxcursor::MacroExpansionCursor::getDefinition() const { + if (isPseudo()) + return getAsMacroDefinition(); + return getAsMacroExpansion()->getDefinition(); +} +SourceRange cxcursor::MacroExpansionCursor::getSourceRange() const { + if (isPseudo()) + return getPseudoLoc(); + return getAsMacroExpansion()->getSourceRange(); +} + +CXCursor cxcursor::MakeInclusionDirectiveCursor(InclusionDirective *ID, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_InclusionDirective, 0, { ID, nullptr, TU } }; + return C; +} + +const InclusionDirective *cxcursor::getCursorInclusionDirective(CXCursor C) { + assert(C.kind == CXCursor_InclusionDirective); + return static_cast<const InclusionDirective *>(C.data[0]); +} + +CXCursor cxcursor::MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, + CXTranslationUnit TU) { + + assert(Label && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + CXCursor C = { CXCursor_LabelRef, 0, { Label, RawLoc, TU } }; + return C; +} + +std::pair<const LabelStmt *, SourceLocation> +cxcursor::getCursorLabelRef(CXCursor C) { + assert(C.kind == CXCursor_LabelRef); + return std::make_pair(static_cast<const LabelStmt *>(C.data[0]), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +CXCursor cxcursor::MakeCursorOverloadedDeclRef(const OverloadExpr *E, + CXTranslationUnit TU) { + assert(E && TU && "Invalid arguments!"); + OverloadedDeclRefStorage Storage(E); + void *RawLoc = E->getNameLoc().getPtrEncoding(); + CXCursor C = { + CXCursor_OverloadedDeclRef, 0, + { Storage.getOpaqueValue(), RawLoc, TU } + }; + return C; +} + +CXCursor cxcursor::MakeCursorOverloadedDeclRef(const Decl *D, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(D && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + OverloadedDeclRefStorage Storage(D); + CXCursor C = { + CXCursor_OverloadedDeclRef, 0, + { Storage.getOpaqueValue(), RawLoc, TU } + }; + return C; +} + +CXCursor cxcursor::MakeCursorOverloadedDeclRef(TemplateName Name, + SourceLocation Loc, + CXTranslationUnit TU) { + assert(Name.getAsOverloadedTemplate() && TU && "Invalid arguments!"); + void *RawLoc = Loc.getPtrEncoding(); + OverloadedDeclRefStorage Storage(Name.getAsOverloadedTemplate()); + CXCursor C = { + CXCursor_OverloadedDeclRef, 0, + { Storage.getOpaqueValue(), RawLoc, TU } + }; + return C; +} + +std::pair<cxcursor::OverloadedDeclRefStorage, SourceLocation> +cxcursor::getCursorOverloadedDeclRef(CXCursor C) { + assert(C.kind == CXCursor_OverloadedDeclRef); + return std::make_pair(OverloadedDeclRefStorage::getFromOpaqueValue( + const_cast<void *>(C.data[0])), + SourceLocation::getFromPtrEncoding(C.data[1])); +} + +const Decl *cxcursor::getCursorDecl(CXCursor Cursor) { + return static_cast<const Decl *>(Cursor.data[0]); +} + +const Expr *cxcursor::getCursorExpr(CXCursor Cursor) { + return dyn_cast_or_null<Expr>(getCursorStmt(Cursor)); +} + +const Stmt *cxcursor::getCursorStmt(CXCursor Cursor) { + if (Cursor.kind == CXCursor_ObjCSuperClassRef || + Cursor.kind == CXCursor_ObjCProtocolRef || + Cursor.kind == CXCursor_ObjCClassRef) + return nullptr; + + return static_cast<const Stmt *>(Cursor.data[1]); +} + +const Attr *cxcursor::getCursorAttr(CXCursor Cursor) { + return static_cast<const Attr *>(Cursor.data[1]); +} + +ASTContext &cxcursor::getCursorContext(CXCursor Cursor) { + return getCursorASTUnit(Cursor)->getASTContext(); +} + +ASTUnit *cxcursor::getCursorASTUnit(CXCursor Cursor) { + CXTranslationUnit TU = getCursorTU(Cursor); + if (!TU) + return nullptr; + return cxtu::getASTUnit(TU); +} + +CXTranslationUnit cxcursor::getCursorTU(CXCursor Cursor) { + return static_cast<CXTranslationUnit>(const_cast<void*>(Cursor.data[2])); +} + +void cxcursor::getOverriddenCursors(CXCursor cursor, + SmallVectorImpl<CXCursor> &overridden) { + assert(clang_isDeclaration(cursor.kind)); + const NamedDecl *D = dyn_cast_or_null<NamedDecl>(getCursorDecl(cursor)); + if (!D) + return; + + CXTranslationUnit TU = getCursorTU(cursor); + SmallVector<const NamedDecl *, 8> OverDecls; + D->getASTContext().getOverriddenMethods(D, OverDecls); + + for (SmallVectorImpl<const NamedDecl *>::iterator + I = OverDecls.begin(), E = OverDecls.end(); I != E; ++I) { + overridden.push_back(MakeCXCursor(*I, TU)); + } +} + +std::pair<int, SourceLocation> +cxcursor::getSelectorIdentifierIndexAndLoc(CXCursor cursor) { + if (cursor.kind == CXCursor_ObjCMessageExpr) { + if (cursor.xdata != -1) + return std::make_pair(cursor.xdata, + cast<ObjCMessageExpr>(getCursorExpr(cursor)) + ->getSelectorLoc(cursor.xdata)); + } else if (cursor.kind == CXCursor_ObjCClassMethodDecl || + cursor.kind == CXCursor_ObjCInstanceMethodDecl) { + if (cursor.xdata != -1) + return std::make_pair(cursor.xdata, + cast<ObjCMethodDecl>(getCursorDecl(cursor)) + ->getSelectorLoc(cursor.xdata)); + } + + return std::make_pair(-1, SourceLocation()); +} + +CXCursor cxcursor::getSelectorIdentifierCursor(int SelIdx, CXCursor cursor) { + CXCursor newCursor = cursor; + + if (cursor.kind == CXCursor_ObjCMessageExpr) { + if (SelIdx == -1 || + unsigned(SelIdx) >= cast<ObjCMessageExpr>(getCursorExpr(cursor)) + ->getNumSelectorLocs()) + newCursor.xdata = -1; + else + newCursor.xdata = SelIdx; + } else if (cursor.kind == CXCursor_ObjCClassMethodDecl || + cursor.kind == CXCursor_ObjCInstanceMethodDecl) { + if (SelIdx == -1 || + unsigned(SelIdx) >= cast<ObjCMethodDecl>(getCursorDecl(cursor)) + ->getNumSelectorLocs()) + newCursor.xdata = -1; + else + newCursor.xdata = SelIdx; + } + + return newCursor; +} + +CXCursor cxcursor::getTypeRefCursor(CXCursor cursor) { + if (cursor.kind != CXCursor_CallExpr) + return cursor; + + if (cursor.xdata == 0) + return cursor; + + const Expr *E = getCursorExpr(cursor); + TypeSourceInfo *Type = nullptr; + if (const CXXUnresolvedConstructExpr * + UnCtor = dyn_cast<CXXUnresolvedConstructExpr>(E)) { + Type = UnCtor->getTypeSourceInfo(); + } else if (const CXXTemporaryObjectExpr *Tmp = + dyn_cast<CXXTemporaryObjectExpr>(E)){ + Type = Tmp->getTypeSourceInfo(); + } + + if (!Type) + return cursor; + + CXTranslationUnit TU = getCursorTU(cursor); + QualType Ty = Type->getType(); + TypeLoc TL = Type->getTypeLoc(); + SourceLocation Loc = TL.getBeginLoc(); + + if (const ElaboratedType *ElabT = Ty->getAs<ElaboratedType>()) { + Ty = ElabT->getNamedType(); + ElaboratedTypeLoc ElabTL = TL.castAs<ElaboratedTypeLoc>(); + Loc = ElabTL.getNamedTypeLoc().getBeginLoc(); + } + + if (const TypedefType *Typedef = Ty->getAs<TypedefType>()) + return MakeCursorTypeRef(Typedef->getDecl(), Loc, TU); + if (const TagType *Tag = Ty->getAs<TagType>()) + return MakeCursorTypeRef(Tag->getDecl(), Loc, TU); + if (const TemplateTypeParmType *TemplP = Ty->getAs<TemplateTypeParmType>()) + return MakeCursorTypeRef(TemplP->getDecl(), Loc, TU); + + return cursor; +} + +bool cxcursor::operator==(CXCursor X, CXCursor Y) { + return X.kind == Y.kind && X.data[0] == Y.data[0] && X.data[1] == Y.data[1] && + X.data[2] == Y.data[2]; +} + +// FIXME: Remove once we can model DeclGroups and their appropriate ranges +// properly in the ASTs. +bool cxcursor::isFirstInDeclGroup(CXCursor C) { + assert(clang_isDeclaration(C.kind)); + return ((uintptr_t) (C.data[1])) != 0; +} + +//===----------------------------------------------------------------------===// +// libclang CXCursor APIs +//===----------------------------------------------------------------------===// + +int clang_Cursor_isNull(CXCursor cursor) { + return clang_equalCursors(cursor, clang_getNullCursor()); +} + +CXTranslationUnit clang_Cursor_getTranslationUnit(CXCursor cursor) { + return getCursorTU(cursor); +} + +int clang_Cursor_getNumArguments(CXCursor C) { + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D)) + return MD->param_size(); + if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) + return FD->param_size(); + } + + if (clang_isExpression(C.kind)) { + const Expr *E = cxcursor::getCursorExpr(C); + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + return CE->getNumArgs(); + } + if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) { + return CE->getNumArgs(); + } + } + + return -1; +} + +CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i) { + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D)) { + if (i < MD->param_size()) + return cxcursor::MakeCXCursor(MD->parameters()[i], + cxcursor::getCursorTU(C)); + } else if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) { + if (i < FD->param_size()) + return cxcursor::MakeCXCursor(FD->parameters()[i], + cxcursor::getCursorTU(C)); + } + } + + if (clang_isExpression(C.kind)) { + const Expr *E = cxcursor::getCursorExpr(C); + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + if (i < CE->getNumArgs()) { + return cxcursor::MakeCXCursor(CE->getArg(i), + getCursorDecl(C), + cxcursor::getCursorTU(C)); + } + } + if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E)) { + if (i < CE->getNumArgs()) { + return cxcursor::MakeCXCursor(CE->getArg(i), + getCursorDecl(C), + cxcursor::getCursorTU(C)); + } + } + } + + return clang_getNullCursor(); +} + +int clang_Cursor_getNumTemplateArguments(CXCursor C) { + if (clang_getCursorKind(C) != CXCursor_FunctionDecl) { + return -1; + } + + const FunctionDecl *FD = llvm::dyn_cast_or_null<clang::FunctionDecl>( + getCursorDecl(C)); + if (!FD) { + return -1; + } + + const FunctionTemplateSpecializationInfo* SpecInfo = + FD->getTemplateSpecializationInfo(); + if (!SpecInfo) { + return -1; + } + + return SpecInfo->TemplateArguments->size(); +} + +enum CXGetTemplateArgumentStatus { + /** The operation completed successfully */ + CXGetTemplateArgumentStatus_Success = 0, + + /** The specified cursor did not represent a FunctionDecl. */ + CXGetTemplateArgumentStatus_CursorNotFunctionDecl = -1, + + /** The specified cursor was not castable to a FunctionDecl. */ + CXGetTemplateArgumentStatus_BadFunctionDeclCast = -2, + + /** A NULL FunctionTemplateSpecializationInfo was retrieved. */ + CXGetTemplateArgumentStatus_NullTemplSpecInfo = -3, + + /** An invalid (OOB) argument index was specified */ + CXGetTemplateArgumentStatus_InvalidIndex = -4 +}; + +static int clang_Cursor_getTemplateArgument( + CXCursor C, unsigned I, TemplateArgument *TA) { + if (clang_getCursorKind(C) != CXCursor_FunctionDecl) { + return CXGetTemplateArgumentStatus_CursorNotFunctionDecl; + } + + const FunctionDecl *FD = llvm::dyn_cast_or_null<clang::FunctionDecl>( + getCursorDecl(C)); + if (!FD) { + return CXGetTemplateArgumentStatus_BadFunctionDeclCast; + } + + const FunctionTemplateSpecializationInfo* SpecInfo = + FD->getTemplateSpecializationInfo(); + if (!SpecInfo) { + return CXGetTemplateArgumentStatus_NullTemplSpecInfo; + } + + if (I >= SpecInfo->TemplateArguments->size()) { + return CXGetTemplateArgumentStatus_InvalidIndex; + } + + *TA = SpecInfo->TemplateArguments->get(I); + return 0; +} + +enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C, + unsigned I) { + TemplateArgument TA; + if (clang_Cursor_getTemplateArgument(C, I, &TA)) { + return CXTemplateArgumentKind_Invalid; + } + + switch (TA.getKind()) { + case TemplateArgument::Null: return CXTemplateArgumentKind_Null; + case TemplateArgument::Type: return CXTemplateArgumentKind_Type; + case TemplateArgument::Declaration: + return CXTemplateArgumentKind_Declaration; + case TemplateArgument::NullPtr: return CXTemplateArgumentKind_NullPtr; + case TemplateArgument::Integral: return CXTemplateArgumentKind_Integral; + case TemplateArgument::Template: return CXTemplateArgumentKind_Template; + case TemplateArgument::TemplateExpansion: + return CXTemplateArgumentKind_TemplateExpansion; + case TemplateArgument::Expression: return CXTemplateArgumentKind_Expression; + case TemplateArgument::Pack: return CXTemplateArgumentKind_Pack; + } + + return CXTemplateArgumentKind_Invalid; +} + +CXType clang_Cursor_getTemplateArgumentType(CXCursor C, unsigned I) { + TemplateArgument TA; + if (clang_Cursor_getTemplateArgument(C, I, &TA) != + CXGetTemplateArgumentStatus_Success) { + return cxtype::MakeCXType(QualType(), getCursorTU(C)); + } + + if (TA.getKind() != TemplateArgument::Type) { + return cxtype::MakeCXType(QualType(), getCursorTU(C)); + } + + return cxtype::MakeCXType(TA.getAsType(), getCursorTU(C)); +} + +long long clang_Cursor_getTemplateArgumentValue(CXCursor C, unsigned I) { + TemplateArgument TA; + if (clang_Cursor_getTemplateArgument(C, I, &TA) != + CXGetTemplateArgumentStatus_Success) { + assert(0 && "Unable to retrieve TemplateArgument"); + return 0; + } + + if (TA.getKind() != TemplateArgument::Integral) { + assert(0 && "Passed template argument is not Integral"); + return 0; + } + + return TA.getAsIntegral().getSExtValue(); +} + +unsigned long long clang_Cursor_getTemplateArgumentUnsignedValue(CXCursor C, + unsigned I) { + TemplateArgument TA; + if (clang_Cursor_getTemplateArgument(C, I, &TA) != + CXGetTemplateArgumentStatus_Success) { + assert(0 && "Unable to retrieve TemplateArgument"); + return 0; + } + + if (TA.getKind() != TemplateArgument::Integral) { + assert(0 && "Passed template argument is not Integral"); + return 0; + } + + return TA.getAsIntegral().getZExtValue(); +} + +//===----------------------------------------------------------------------===// +// CXCursorSet. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap<CXCursor, unsigned> CXCursorSet_Impl; + +static inline CXCursorSet packCXCursorSet(CXCursorSet_Impl *setImpl) { + return (CXCursorSet) setImpl; +} +static inline CXCursorSet_Impl *unpackCXCursorSet(CXCursorSet set) { + return (CXCursorSet_Impl*) set; +} +namespace llvm { +template<> struct DenseMapInfo<CXCursor> { +public: + static inline CXCursor getEmptyKey() { + return MakeCXCursorInvalid(CXCursor_InvalidFile); + } + static inline CXCursor getTombstoneKey() { + return MakeCXCursorInvalid(CXCursor_NoDeclFound); + } + static inline unsigned getHashValue(const CXCursor &cursor) { + return llvm::DenseMapInfo<std::pair<const void *, const void *> > + ::getHashValue(std::make_pair(cursor.data[0], cursor.data[1])); + } + static inline bool isEqual(const CXCursor &x, const CXCursor &y) { + return x.kind == y.kind && + x.data[0] == y.data[0] && + x.data[1] == y.data[1]; + } +}; +} + +CXCursorSet clang_createCXCursorSet() { + return packCXCursorSet(new CXCursorSet_Impl()); +} + +void clang_disposeCXCursorSet(CXCursorSet set) { + delete unpackCXCursorSet(set); +} + +unsigned clang_CXCursorSet_contains(CXCursorSet set, CXCursor cursor) { + CXCursorSet_Impl *setImpl = unpackCXCursorSet(set); + if (!setImpl) + return 0; + return setImpl->find(cursor) != setImpl->end(); +} + +unsigned clang_CXCursorSet_insert(CXCursorSet set, CXCursor cursor) { + // Do not insert invalid cursors into the set. + if (cursor.kind >= CXCursor_FirstInvalid && + cursor.kind <= CXCursor_LastInvalid) + return 1; + + CXCursorSet_Impl *setImpl = unpackCXCursorSet(set); + if (!setImpl) + return 1; + unsigned &entry = (*setImpl)[cursor]; + unsigned flag = entry == 0 ? 1 : 0; + entry = 1; + return flag; +} + +CXCompletionString clang_getCursorCompletionString(CXCursor cursor) { + enum CXCursorKind kind = clang_getCursorKind(cursor); + if (clang_isDeclaration(kind)) { + const Decl *decl = getCursorDecl(cursor); + if (const NamedDecl *namedDecl = dyn_cast_or_null<NamedDecl>(decl)) { + ASTUnit *unit = getCursorASTUnit(cursor); + CodeCompletionResult Result(namedDecl, CCP_Declaration); + CodeCompletionString *String + = Result.CreateCodeCompletionString(unit->getASTContext(), + unit->getPreprocessor(), + CodeCompletionContext::CCC_Other, + unit->getCodeCompletionTUInfo().getAllocator(), + unit->getCodeCompletionTUInfo(), + true); + return String; + } + } else if (kind == CXCursor_MacroDefinition) { + const MacroDefinitionRecord *definition = getCursorMacroDefinition(cursor); + const IdentifierInfo *Macro = definition->getName(); + ASTUnit *unit = getCursorASTUnit(cursor); + CodeCompletionResult Result( + Macro, + unit->getPreprocessor().getMacroDefinition(Macro).getMacroInfo()); + CodeCompletionString *String = Result.CreateCodeCompletionString( + unit->getASTContext(), unit->getPreprocessor(), + CodeCompletionContext::CCC_Other, + unit->getCodeCompletionTUInfo().getAllocator(), + unit->getCodeCompletionTUInfo(), false); + return String; + } + return nullptr; +} + +namespace { + struct OverridenCursorsPool { + typedef SmallVector<CXCursor, 2> CursorVec; + std::vector<CursorVec*> AllCursors; + std::vector<CursorVec*> AvailableCursors; + + ~OverridenCursorsPool() { + for (std::vector<CursorVec*>::iterator I = AllCursors.begin(), + E = AllCursors.end(); I != E; ++I) { + delete *I; + } + } + }; +} + +void *cxcursor::createOverridenCXCursorsPool() { + return new OverridenCursorsPool(); +} + +void cxcursor::disposeOverridenCXCursorsPool(void *pool) { + delete static_cast<OverridenCursorsPool*>(pool); +} + +void clang_getOverriddenCursors(CXCursor cursor, + CXCursor **overridden, + unsigned *num_overridden) { + if (overridden) + *overridden = nullptr; + if (num_overridden) + *num_overridden = 0; + + CXTranslationUnit TU = cxcursor::getCursorTU(cursor); + + if (!overridden || !num_overridden || !TU) + return; + + if (!clang_isDeclaration(cursor.kind)) + return; + + OverridenCursorsPool &pool = + *static_cast<OverridenCursorsPool*>(TU->OverridenCursorsPool); + + OverridenCursorsPool::CursorVec *Vec = nullptr; + + if (!pool.AvailableCursors.empty()) { + Vec = pool.AvailableCursors.back(); + pool.AvailableCursors.pop_back(); + } + else { + Vec = new OverridenCursorsPool::CursorVec(); + pool.AllCursors.push_back(Vec); + } + + // Clear out the vector, but don't free the memory contents. This + // reduces malloc() traffic. + Vec->clear(); + + // Use the first entry to contain a back reference to the vector. + // This is a complete hack. + CXCursor backRefCursor = MakeCXCursorInvalid(CXCursor_InvalidFile, TU); + backRefCursor.data[0] = Vec; + assert(cxcursor::getCursorTU(backRefCursor) == TU); + Vec->push_back(backRefCursor); + + // Get the overridden cursors. + cxcursor::getOverriddenCursors(cursor, *Vec); + + // Did we get any overridden cursors? If not, return Vec to the pool + // of available cursor vectors. + if (Vec->size() == 1) { + pool.AvailableCursors.push_back(Vec); + return; + } + + // Now tell the caller about the overridden cursors. + assert(Vec->size() > 1); + *overridden = &((*Vec)[1]); + *num_overridden = Vec->size() - 1; +} + +void clang_disposeOverriddenCursors(CXCursor *overridden) { + if (!overridden) + return; + + // Use pointer arithmetic to get back the first faux entry + // which has a back-reference to the TU and the vector. + --overridden; + OverridenCursorsPool::CursorVec *Vec = + static_cast<OverridenCursorsPool::CursorVec *>( + const_cast<void *>(overridden->data[0])); + CXTranslationUnit TU = getCursorTU(*overridden); + + assert(Vec && TU); + + OverridenCursorsPool &pool = + *static_cast<OverridenCursorsPool*>(TU->OverridenCursorsPool); + + pool.AvailableCursors.push_back(Vec); +} + +int clang_Cursor_isDynamicCall(CXCursor C) { + const Expr *E = nullptr; + if (clang_isExpression(C.kind)) + E = getCursorExpr(C); + if (!E) + return 0; + + if (const ObjCMessageExpr *MsgE = dyn_cast<ObjCMessageExpr>(E)) { + if (MsgE->getReceiverKind() != ObjCMessageExpr::Instance) + return false; + if (auto *RecE = dyn_cast<ObjCMessageExpr>( + MsgE->getInstanceReceiver()->IgnoreParenCasts())) { + if (RecE->getMethodFamily() == OMF_alloc) + return false; + } + return true; + } + + if (auto *PropRefE = dyn_cast<ObjCPropertyRefExpr>(E)) { + return !PropRefE->isSuperReceiver(); + } + + const MemberExpr *ME = nullptr; + if (isa<MemberExpr>(E)) + ME = cast<MemberExpr>(E); + else if (const CallExpr *CE = dyn_cast<CallExpr>(E)) + ME = dyn_cast_or_null<MemberExpr>(CE->getCallee()); + + if (ME) { + if (const CXXMethodDecl * + MD = dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl())) + return MD->isVirtual() && + ME->performsVirtualDispatch( + cxcursor::getCursorContext(C).getLangOpts()); + } + + return 0; +} + +CXType clang_Cursor_getReceiverType(CXCursor C) { + CXTranslationUnit TU = cxcursor::getCursorTU(C); + const Expr *E = nullptr; + if (clang_isExpression(C.kind)) + E = getCursorExpr(C); + + if (const ObjCMessageExpr *MsgE = dyn_cast_or_null<ObjCMessageExpr>(E)) + return cxtype::MakeCXType(MsgE->getReceiverType(), TU); + + if (auto *PropRefE = dyn_cast<ObjCPropertyRefExpr>(E)) { + return cxtype::MakeCXType( + PropRefE->getReceiverType(cxcursor::getCursorContext(C)), TU); + } + + const MemberExpr *ME = nullptr; + if (isa<MemberExpr>(E)) + ME = cast<MemberExpr>(E); + else if (const CallExpr *CE = dyn_cast<CallExpr>(E)) + ME = dyn_cast_or_null<MemberExpr>(CE->getCallee()); + + if (ME) { + if (dyn_cast_or_null<CXXMethodDecl>(ME->getMemberDecl())) { + auto receiverTy = ME->getBase()->IgnoreImpCasts()->getType(); + return cxtype::MakeCXType(receiverTy, TU); + } + } + + return cxtype::MakeCXType(QualType(), TU); +} diff --git a/gnu/llvm/clang/tools/libclang/CXCursor.h b/gnu/llvm/clang/tools/libclang/CXCursor.h new file mode 100644 index 00000000000..78e597b2bbe --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXCursor.h @@ -0,0 +1,297 @@ +//===- CXCursor.h - Routines for manipulating CXCursors -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXCURSOR_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXCURSOR_H + +#include "clang-c/Index.h" +#include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerUnion.h" +#include <utility> + +namespace clang { + +class ASTContext; +class ASTUnit; +class Attr; +class CXXBaseSpecifier; +class Decl; +class Expr; +class FieldDecl; +class InclusionDirective; +class LabelStmt; +class MacroDefinitionRecord; +class MacroExpansion; +class NamedDecl; +class ObjCInterfaceDecl; +class ObjCProtocolDecl; +class OverloadedTemplateStorage; +class OverloadExpr; +class Stmt; +class TemplateDecl; +class TemplateName; +class TypeDecl; +class VarDecl; +class IdentifierInfo; + +namespace cxcursor { + +CXCursor getCursor(CXTranslationUnit, SourceLocation); + +CXCursor MakeCXCursor(const clang::Attr *A, const clang::Decl *Parent, + CXTranslationUnit TU); +CXCursor MakeCXCursor(const clang::Decl *D, CXTranslationUnit TU, + SourceRange RegionOfInterest = SourceRange(), + bool FirstInDeclGroup = true); +CXCursor MakeCXCursor(const clang::Stmt *S, const clang::Decl *Parent, + CXTranslationUnit TU, + SourceRange RegionOfInterest = SourceRange()); +CXCursor MakeCXCursorInvalid(CXCursorKind K, CXTranslationUnit TU = nullptr); + +/// Create an Objective-C superclass reference at the given location. +CXCursor MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, + SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack an ObjCSuperClassRef cursor into the interface it references +/// and optionally the location where the reference occurred. +std::pair<const ObjCInterfaceDecl *, SourceLocation> + getCursorObjCSuperClassRef(CXCursor C); + +/// Create an Objective-C protocol reference at the given location. +CXCursor MakeCursorObjCProtocolRef(const ObjCProtocolDecl *Proto, + SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack an ObjCProtocolRef cursor into the protocol it references +/// and optionally the location where the reference occurred. +std::pair<const ObjCProtocolDecl *, SourceLocation> + getCursorObjCProtocolRef(CXCursor C); + +/// Create an Objective-C class reference at the given location. +CXCursor MakeCursorObjCClassRef(const ObjCInterfaceDecl *Class, + SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack an ObjCClassRef cursor into the class it references +/// and optionally the location where the reference occurred. +std::pair<const ObjCInterfaceDecl *, SourceLocation> + getCursorObjCClassRef(CXCursor C); + +/// Create a type reference at the given location. +CXCursor MakeCursorTypeRef(const TypeDecl *Type, SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack a TypeRef cursor into the class it references +/// and optionally the location where the reference occurred. +std::pair<const TypeDecl *, SourceLocation> getCursorTypeRef(CXCursor C); + +/// Create a reference to a template at the given location. +CXCursor MakeCursorTemplateRef(const TemplateDecl *Template, SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack a TemplateRef cursor into the template it references and +/// the location where the reference occurred. +std::pair<const TemplateDecl *, SourceLocation> + getCursorTemplateRef(CXCursor C); + +/// Create a reference to a namespace or namespace alias at the given +/// location. +CXCursor MakeCursorNamespaceRef(const NamedDecl *NS, SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack a NamespaceRef cursor into the namespace or namespace alias +/// it references and the location where the reference occurred. +std::pair<const NamedDecl *, SourceLocation> getCursorNamespaceRef(CXCursor C); + +/// Create a reference to a variable at the given location. +CXCursor MakeCursorVariableRef(const VarDecl *Var, SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack a VariableRef cursor into the variable it references and the +/// location where the where the reference occurred. +std::pair<const VarDecl *, SourceLocation> getCursorVariableRef(CXCursor C); + +/// Create a reference to a field at the given location. +CXCursor MakeCursorMemberRef(const FieldDecl *Field, SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack a MemberRef cursor into the field it references and the +/// location where the reference occurred. +std::pair<const FieldDecl *, SourceLocation> getCursorMemberRef(CXCursor C); + +/// Create a CXX base specifier cursor. +CXCursor MakeCursorCXXBaseSpecifier(const CXXBaseSpecifier *B, + CXTranslationUnit TU); + +/// Unpack a CXXBaseSpecifier cursor into a CXXBaseSpecifier. +const CXXBaseSpecifier *getCursorCXXBaseSpecifier(CXCursor C); + +/// Create a preprocessing directive cursor. +CXCursor MakePreprocessingDirectiveCursor(SourceRange Range, + CXTranslationUnit TU); + +/// Unpack a given preprocessing directive to retrieve its source range. +SourceRange getCursorPreprocessingDirective(CXCursor C); + +/// Create a macro definition cursor. +CXCursor MakeMacroDefinitionCursor(const MacroDefinitionRecord *, + CXTranslationUnit TU); + +/// Unpack a given macro definition cursor to retrieve its +/// source range. +const MacroDefinitionRecord *getCursorMacroDefinition(CXCursor C); + +/// Create a macro expansion cursor. +CXCursor MakeMacroExpansionCursor(MacroExpansion *, CXTranslationUnit TU); + +/// Create a "pseudo" macro expansion cursor, using a macro definition +/// and a source location. +CXCursor MakeMacroExpansionCursor(MacroDefinitionRecord *, SourceLocation Loc, + CXTranslationUnit TU); + +/// Wraps a macro expansion cursor and provides a common interface +/// for a normal macro expansion cursor or a "pseudo" one. +/// +/// "Pseudo" macro expansion cursors (essentially a macro definition along with +/// a source location) are created in special cases, for example they can be +/// created for identifiers inside macro definitions, if these identifiers are +/// macro names. +class MacroExpansionCursor { + CXCursor C; + + bool isPseudo() const { return C.data[1] != nullptr; } + const MacroDefinitionRecord *getAsMacroDefinition() const { + assert(isPseudo()); + return static_cast<const MacroDefinitionRecord *>(C.data[0]); + } + const MacroExpansion *getAsMacroExpansion() const { + assert(!isPseudo()); + return static_cast<const MacroExpansion *>(C.data[0]); + } + SourceLocation getPseudoLoc() const { + assert(isPseudo()); + return SourceLocation::getFromPtrEncoding(C.data[1]); + } + +public: + MacroExpansionCursor(CXCursor C) : C(C) { + assert(C.kind == CXCursor_MacroExpansion); + } + + const IdentifierInfo *getName() const; + const MacroDefinitionRecord *getDefinition() const; + SourceRange getSourceRange() const; +}; + +/// Unpack a given macro expansion cursor to retrieve its info. +static inline MacroExpansionCursor getCursorMacroExpansion(CXCursor C) { + return C; +} + +/// Create an inclusion directive cursor. +CXCursor MakeInclusionDirectiveCursor(InclusionDirective *, + CXTranslationUnit TU); + +/// Unpack a given inclusion directive cursor to retrieve its +/// source range. +const InclusionDirective *getCursorInclusionDirective(CXCursor C); + +/// Create a label reference at the given location. +CXCursor MakeCursorLabelRef(LabelStmt *Label, SourceLocation Loc, + CXTranslationUnit TU); + +/// Unpack a label reference into the label statement it refers to and +/// the location of the reference. +std::pair<const LabelStmt *, SourceLocation> getCursorLabelRef(CXCursor C); + +/// Create a overloaded declaration reference cursor for an expression. +CXCursor MakeCursorOverloadedDeclRef(const OverloadExpr *E, + CXTranslationUnit TU); + +/// Create a overloaded declaration reference cursor for a declaration. +CXCursor MakeCursorOverloadedDeclRef(const Decl *D, SourceLocation Location, + CXTranslationUnit TU); + +/// Create a overloaded declaration reference cursor for a template name. +CXCursor MakeCursorOverloadedDeclRef(TemplateName Template, + SourceLocation Location, + CXTranslationUnit TU); + +/// Internal storage for an overloaded declaration reference cursor; +typedef llvm::PointerUnion<const OverloadExpr *, const Decl *, + OverloadedTemplateStorage *> + OverloadedDeclRefStorage; + +/// Unpack an overloaded declaration reference into an expression, +/// declaration, or template name along with the source location. +std::pair<OverloadedDeclRefStorage, SourceLocation> + getCursorOverloadedDeclRef(CXCursor C); + +const Decl *getCursorDecl(CXCursor Cursor); +const Expr *getCursorExpr(CXCursor Cursor); +const Stmt *getCursorStmt(CXCursor Cursor); +const Attr *getCursorAttr(CXCursor Cursor); + +ASTContext &getCursorContext(CXCursor Cursor); +ASTUnit *getCursorASTUnit(CXCursor Cursor); +CXTranslationUnit getCursorTU(CXCursor Cursor); + +void getOverriddenCursors(CXCursor cursor, + SmallVectorImpl<CXCursor> &overridden); + +/// Create an opaque pool used for fast generation of overridden +/// CXCursor arrays. +void *createOverridenCXCursorsPool(); + +/// Dispose of the overridden CXCursors pool. +void disposeOverridenCXCursorsPool(void *pool); + +/// Returns a index/location pair for a selector identifier if the cursor +/// points to one. +std::pair<int, SourceLocation> getSelectorIdentifierIndexAndLoc(CXCursor); +static inline int getSelectorIdentifierIndex(CXCursor cursor) { + return getSelectorIdentifierIndexAndLoc(cursor).first; +} +static inline SourceLocation getSelectorIdentifierLoc(CXCursor cursor) { + return getSelectorIdentifierIndexAndLoc(cursor).second; +} + +CXCursor getSelectorIdentifierCursor(int SelIdx, CXCursor cursor); + +static inline CXCursor getTypeRefedCallExprCursor(CXCursor cursor) { + CXCursor newCursor = cursor; + if (cursor.kind == CXCursor_CallExpr) + newCursor.xdata = 1; + return newCursor; +} + +CXCursor getTypeRefCursor(CXCursor cursor); + +/// Generate a USR for \arg D and put it in \arg Buf. +/// \returns true if no USR was computed or the result should be ignored, +/// false otherwise. +bool getDeclCursorUSR(const Decl *D, SmallVectorImpl<char> &Buf); + +bool operator==(CXCursor X, CXCursor Y); + +inline bool operator!=(CXCursor X, CXCursor Y) { + return !(X == Y); +} + +/// Return true if the cursor represents a declaration that is the +/// first in a declaration group. +bool isFirstInDeclGroup(CXCursor C); + +}} // end namespace: clang::cxcursor + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp new file mode 100644 index 00000000000..ad871228ccd --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.cpp @@ -0,0 +1,1310 @@ +//===- CXIndexDataConsumer.cpp - Index data consumer for libclang----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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; + +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, DeclLoc); + 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; + } +}; + +CXSymbolRole getSymbolRole(SymbolRoleSet Role) { + // CXSymbolRole mirrors low 9 bits of clang::index::SymbolRole. + return CXSymbolRole(static_cast<uint32_t>(Role) & ((1 << 9) - 1)); +} +} + +bool CXIndexDataConsumer::handleDeclOccurrence( + const Decl *D, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) { + Loc = getASTContext().getSourceManager().getFileLoc(Loc); + + 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; + } + CXSymbolRole CXRole = getSymbolRole(Roles); + + 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, CXRole); + + } 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::handleModuleOccurrence(const ImportDecl *ImportD, + const Module *Mod, + SymbolRoleSet Roles, + SourceLocation Loc) { + if (Roles & (SymbolRoleSet)SymbolRole::Declaration) + IndexingDeclVisitor(*this, SourceLocation(), nullptr).Visit(ImportD); + return !shouldAbort(); +} + +void CXIndexDataConsumer::finish() { + indexDiagnostics(); +} + + +CXIndexDataConsumer::ObjCProtocolListInfo::ObjCProtocolListInfo( + const ObjCProtocolList &ProtList, + CXIndexDataConsumer &IdxCtx, + ScratchAlloc &SA) { + ObjCInterfaceDecl::protocol_loc_iterator LI = ProtList.loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator + I = ProtList.begin(), E = ProtList.end(); I != E; ++I, ++LI) { + SourceLocation Loc = *LI; + ObjCProtocolDecl *PD = *I; + ProtEntities.push_back(EntityInfo()); + IdxCtx.getEntityInfo(PD, ProtEntities.back(), SA); + CXIdxObjCProtocolRefInfo ProtInfo = { nullptr, + MakeCursorObjCProtocolRef(PD, Loc, IdxCtx.CXTU), + IdxCtx.getIndexLoc(Loc) }; + ProtInfos.push_back(ProtInfo); + + if (IdxCtx.shouldSuppressRefs()) + IdxCtx.markEntityOccurrenceInFile(PD, Loc); + } + + for (unsigned i = 0, e = ProtInfos.size(); i != e; ++i) + ProtInfos[i].protocol = &ProtEntities[i]; + + for (unsigned i = 0, e = ProtInfos.size(); i != e; ++i) + Prots.push_back(&ProtInfos[i]); +} + + +IBOutletCollectionInfo::IBOutletCollectionInfo( + const IBOutletCollectionInfo &other) + : AttrInfo(CXIdxAttr_IBOutletCollection, other.cursor, other.loc, other.A) { + + IBCollInfo.attrInfo = this; + IBCollInfo.classCursor = other.IBCollInfo.classCursor; + IBCollInfo.classLoc = other.IBCollInfo.classLoc; + if (other.IBCollInfo.objcClass) { + ClassInfo = other.ClassInfo; + IBCollInfo.objcClass = &ClassInfo; + } else + IBCollInfo.objcClass = nullptr; +} + +AttrListInfo::AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx) + : SA(IdxCtx), ref_cnt(0) { + + if (!D->hasAttrs()) + return; + + for (const auto *A : D->attrs()) { + CXCursor C = MakeCXCursor(A, D, IdxCtx.CXTU); + CXIdxLoc Loc = IdxCtx.getIndexLoc(A->getLocation()); + switch (C.kind) { + default: + Attrs.push_back(AttrInfo(CXIdxAttr_Unexposed, C, Loc, A)); + break; + case CXCursor_IBActionAttr: + Attrs.push_back(AttrInfo(CXIdxAttr_IBAction, C, Loc, A)); + break; + case CXCursor_IBOutletAttr: + Attrs.push_back(AttrInfo(CXIdxAttr_IBOutlet, C, Loc, A)); + break; + case CXCursor_IBOutletCollectionAttr: + IBCollAttrs.push_back(IBOutletCollectionInfo(C, Loc, A)); + break; + } + } + + for (unsigned i = 0, e = IBCollAttrs.size(); i != e; ++i) { + IBOutletCollectionInfo &IBInfo = IBCollAttrs[i]; + CXAttrs.push_back(&IBInfo); + + const IBOutletCollectionAttr * + IBAttr = cast<IBOutletCollectionAttr>(IBInfo.A); + SourceLocation InterfaceLocStart = + IBAttr->getInterfaceLoc()->getTypeLoc().getBeginLoc(); + IBInfo.IBCollInfo.attrInfo = &IBInfo; + IBInfo.IBCollInfo.classLoc = IdxCtx.getIndexLoc(InterfaceLocStart); + IBInfo.IBCollInfo.objcClass = nullptr; + IBInfo.IBCollInfo.classCursor = clang_getNullCursor(); + QualType Ty = IBAttr->getInterface(); + if (const ObjCObjectType *ObjectTy = Ty->getAs<ObjCObjectType>()) { + if (const ObjCInterfaceDecl *InterD = ObjectTy->getInterface()) { + IdxCtx.getEntityInfo(InterD, IBInfo.ClassInfo, SA); + IBInfo.IBCollInfo.objcClass = &IBInfo.ClassInfo; + IBInfo.IBCollInfo.classCursor = + MakeCursorObjCClassRef(InterD, InterfaceLocStart, IdxCtx.CXTU); + } + } + } + + for (unsigned i = 0, e = Attrs.size(); i != e; ++i) + CXAttrs.push_back(&Attrs[i]); +} + +IntrusiveRefCntPtr<AttrListInfo> +AttrListInfo::create(const Decl *D, CXIndexDataConsumer &IdxCtx) { + ScratchAlloc SA(IdxCtx); + AttrListInfo *attrs = SA.allocate<AttrListInfo>(); + return new (attrs) AttrListInfo(D, IdxCtx); +} + +CXIndexDataConsumer::CXXBasesListInfo::CXXBasesListInfo(const CXXRecordDecl *D, + CXIndexDataConsumer &IdxCtx, + ScratchAlloc &SA) { + for (const auto &Base : D->bases()) { + BaseEntities.push_back(EntityInfo()); + const NamedDecl *BaseD = nullptr; + QualType T = Base.getType(); + SourceLocation Loc = getBaseLoc(Base); + + if (const TypedefType *TDT = T->getAs<TypedefType>()) { + BaseD = TDT->getDecl(); + } else if (const TemplateSpecializationType * + TST = T->getAs<TemplateSpecializationType>()) { + BaseD = TST->getTemplateName().getAsTemplateDecl(); + } else if (const RecordType *RT = T->getAs<RecordType>()) { + BaseD = RT->getDecl(); + } + + if (BaseD) + IdxCtx.getEntityInfo(BaseD, BaseEntities.back(), SA); + CXIdxBaseClassInfo BaseInfo = { nullptr, + MakeCursorCXXBaseSpecifier(&Base, IdxCtx.CXTU), + IdxCtx.getIndexLoc(Loc) }; + BaseInfos.push_back(BaseInfo); + } + + for (unsigned i = 0, e = BaseInfos.size(); i != e; ++i) { + if (BaseEntities[i].name && BaseEntities[i].USR) + BaseInfos[i].base = &BaseEntities[i]; + } + + for (unsigned i = 0, e = BaseInfos.size(); i != e; ++i) + CXBases.push_back(&BaseInfos[i]); +} + +SourceLocation CXIndexDataConsumer::CXXBasesListInfo::getBaseLoc( + const CXXBaseSpecifier &Base) const { + SourceLocation Loc = Base.getSourceRange().getBegin(); + TypeLoc TL; + if (Base.getTypeSourceInfo()) + TL = Base.getTypeSourceInfo()->getTypeLoc(); + if (TL.isNull()) + return Loc; + + if (QualifiedTypeLoc QL = TL.getAs<QualifiedTypeLoc>()) + TL = QL.getUnqualifiedLoc(); + + if (ElaboratedTypeLoc EL = TL.getAs<ElaboratedTypeLoc>()) + return EL.getNamedTypeLoc().getBeginLoc(); + if (DependentNameTypeLoc DL = TL.getAs<DependentNameTypeLoc>()) + return DL.getNameLoc(); + if (DependentTemplateSpecializationTypeLoc DTL = + TL.getAs<DependentTemplateSpecializationTypeLoc>()) + return DTL.getTemplateNameLoc(); + + return Loc; +} + +const char *ScratchAlloc::toCStr(StringRef Str) { + if (Str.empty()) + return ""; + if (Str.data()[Str.size()] == '\0') + return Str.data(); + return copyCStr(Str); +} + +const char *ScratchAlloc::copyCStr(StringRef Str) { + char *buf = IdxCtx.StrScratch.Allocate<char>(Str.size() + 1); + std::uninitialized_copy(Str.begin(), Str.end(), buf); + buf[Str.size()] = '\0'; + return buf; +} + +void CXIndexDataConsumer::setASTContext(ASTContext &ctx) { + Ctx = &ctx; + cxtu::getASTUnit(CXTU)->setASTContext(&ctx); +} + +void CXIndexDataConsumer::setPreprocessor(std::shared_ptr<Preprocessor> PP) { + cxtu::getASTUnit(CXTU)->setPreprocessor(std::move(PP)); +} + +bool CXIndexDataConsumer::isFunctionLocalDecl(const Decl *D) { + assert(D); + + if (!D->getParentFunctionOrMethod()) + return false; + + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + switch (ND->getFormalLinkage()) { + case NoLinkage: + case InternalLinkage: + return true; + case VisibleNoLinkage: + case ModuleInternalLinkage: + case UniqueExternalLinkage: + llvm_unreachable("Not a sema linkage"); + case ModuleLinkage: + case ExternalLinkage: + return false; + } + } + + return true; +} + +bool CXIndexDataConsumer::shouldAbort() { + if (!CB.abortQuery) + return false; + return CB.abortQuery(ClientData, nullptr); +} + +void CXIndexDataConsumer::enteredMainFile(const FileEntry *File) { + if (File && CB.enteredMainFile) { + CXIdxClientFile idxFile = + CB.enteredMainFile(ClientData, + static_cast<CXFile>(const_cast<FileEntry *>(File)), + nullptr); + FileMap[File] = idxFile; + } +} + +void CXIndexDataConsumer::ppIncludedFile(SourceLocation hashLoc, + StringRef filename, + const FileEntry *File, + bool isImport, bool isAngled, + bool isModuleImport) { + if (!CB.ppIncludedFile) + return; + + ScratchAlloc SA(*this); + CXIdxIncludedFileInfo Info = { getIndexLoc(hashLoc), + SA.toCStr(filename), + static_cast<CXFile>( + const_cast<FileEntry *>(File)), + isImport, isAngled, isModuleImport }; + CXIdxClientFile idxFile = CB.ppIncludedFile(ClientData, &Info); + FileMap[File] = idxFile; +} + +void CXIndexDataConsumer::importedModule(const ImportDecl *ImportD) { + if (!CB.importedASTFile) + return; + + Module *Mod = ImportD->getImportedModule(); + if (!Mod) + return; + + // If the imported module is part of the top-level module that we're + // indexing, it doesn't correspond to an imported AST file. + // FIXME: This assumes that AST files and top-level modules directly + // correspond, which is unlikely to remain true forever. + if (Module *SrcMod = ImportD->getImportedOwningModule()) + if (SrcMod->getTopLevelModule() == Mod->getTopLevelModule()) + return; + + CXIdxImportedASTFileInfo Info = { + static_cast<CXFile>( + const_cast<FileEntry *>(Mod->getASTFile())), + Mod, + getIndexLoc(ImportD->getLocation()), + ImportD->isImplicit() + }; + CXIdxClientASTFile astFile = CB.importedASTFile(ClientData, &Info); + (void)astFile; +} + +void CXIndexDataConsumer::importedPCH(const FileEntry *File) { + if (!CB.importedASTFile) + return; + + CXIdxImportedASTFileInfo Info = { + static_cast<CXFile>( + const_cast<FileEntry *>(File)), + /*module=*/nullptr, + getIndexLoc(SourceLocation()), + /*isImplicit=*/false + }; + CXIdxClientASTFile astFile = CB.importedASTFile(ClientData, &Info); + (void)astFile; +} + +void CXIndexDataConsumer::startedTranslationUnit() { + CXIdxClientContainer idxCont = nullptr; + if (CB.startedTranslationUnit) + idxCont = CB.startedTranslationUnit(ClientData, nullptr); + addContainerInMap(Ctx->getTranslationUnitDecl(), idxCont); +} + +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 CXIndexDataConsumer::handleDecl(const NamedDecl *D, + SourceLocation Loc, CXCursor Cursor, + DeclInfo &DInfo, + const DeclContext *LexicalDC, + const DeclContext *SemaDC) { + if (!CB.indexDeclaration || !D) + return false; + if (D->isImplicit() && shouldIgnoreIfImplicit(D)) + return false; + + ScratchAlloc SA(*this); + getEntityInfo(D, DInfo.EntInfo, SA); + if ((!shouldIndexFunctionLocalSymbols() && !DInfo.EntInfo.USR) + || Loc.isInvalid()) + return false; + + if (!LexicalDC) + LexicalDC = D->getLexicalDeclContext(); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(D, Loc); + + DInfo.entityInfo = &DInfo.EntInfo; + DInfo.cursor = Cursor; + DInfo.loc = getIndexLoc(Loc); + DInfo.isImplicit = D->isImplicit(); + + DInfo.attributes = DInfo.EntInfo.attributes; + DInfo.numAttributes = DInfo.EntInfo.numAttributes; + + if (!SemaDC) + SemaDC = D->getDeclContext(); + getContainerInfo(SemaDC, DInfo.SemanticContainer); + DInfo.semanticContainer = &DInfo.SemanticContainer; + + if (LexicalDC == SemaDC) { + DInfo.lexicalContainer = &DInfo.SemanticContainer; + } else if (isTemplateImplicitInstantiation(D)) { + // Implicit instantiations have the lexical context of where they were + // instantiated first. We choose instead the semantic context because: + // 1) at the time that we see the instantiation we have not seen the + // function where it occurred yet. + // 2) the lexical context of the first instantiation is not useful + // information anyway. + DInfo.lexicalContainer = &DInfo.SemanticContainer; + } else { + getContainerInfo(LexicalDC, DInfo.LexicalContainer); + DInfo.lexicalContainer = &DInfo.LexicalContainer; + } + + if (DInfo.isContainer) { + getContainerInfo(getEntityContainer(D), DInfo.DeclAsContainer); + DInfo.declAsContainer = &DInfo.DeclAsContainer; + } + + CB.indexDeclaration(ClientData, &DInfo); + return true; +} + +bool CXIndexDataConsumer::handleObjCContainer(const ObjCContainerDecl *D, + SourceLocation Loc, CXCursor Cursor, + ObjCContainerDeclInfo &ContDInfo) { + ContDInfo.ObjCContDeclInfo.declInfo = &ContDInfo; + return handleDecl(D, Loc, Cursor, ContDInfo); +} + +bool CXIndexDataConsumer::handleFunction(const FunctionDecl *D) { + bool isDef = D->isThisDeclarationADefinition(); + bool isContainer = isDef; + bool isSkipped = false; + if (D->hasSkippedBody()) { + isSkipped = true; + isDef = true; + isContainer = false; + } + + DeclInfo DInfo(!D->isFirstDecl(), isDef, isContainer); + if (isSkipped) + DInfo.flags |= CXIdxDeclFlag_Skipped; + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleVar(const VarDecl *D) { + DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleField(const FieldDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleEnumerator(const EnumConstantDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/false, /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleTagDecl(const TagDecl *D) { + if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(D)) + return handleCXXRecordDecl(CXXRD, D); + + DeclInfo DInfo(!D->isFirstDecl(), D->isThisDeclarationADefinition(), + D->isThisDeclarationADefinition()); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleTypedefName(const TypedefNameDecl *D) { + DeclInfo DInfo(!D->isFirstDecl(), /*isDefinition=*/true, + /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +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())) + return false; // already occurred. + + // FIXME: This seems like the wrong definition for redeclaration. + bool isRedeclaration = D->hasDefinition() || D->getPreviousDecl(); + ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/true, isRedeclaration, + /*isImplementation=*/false); + return handleObjCContainer(D, D->getLocation(), + MakeCursorObjCClassRef(D, D->getLocation(), + CXTU), + ContDInfo); + } + + ScratchAlloc SA(*this); + + CXIdxBaseClassInfo BaseClass; + EntityInfo BaseEntity; + BaseClass.cursor = clang_getNullCursor(); + if (ObjCInterfaceDecl *SuperD = D->getSuperClass()) { + getEntityInfo(SuperD, BaseEntity, SA); + SourceLocation SuperLoc = D->getSuperClassLoc(); + BaseClass.base = &BaseEntity; + BaseClass.cursor = MakeCursorObjCSuperClassRef(SuperD, SuperLoc, CXTU); + BaseClass.loc = getIndexLoc(SuperLoc); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(SuperD, SuperLoc); + } + + ObjCProtocolList EmptyProtoList; + ObjCProtocolListInfo ProtInfo(D->isThisDeclarationADefinition() + ? D->getReferencedProtocols() + : EmptyProtoList, + *this, SA); + + ObjCInterfaceDeclInfo InterInfo(D); + InterInfo.ObjCProtoListInfo = ProtInfo.getListInfo(); + InterInfo.ObjCInterDeclInfo.containerInfo = &InterInfo.ObjCContDeclInfo; + InterInfo.ObjCInterDeclInfo.superInfo = D->getSuperClass() ? &BaseClass + : nullptr; + InterInfo.ObjCInterDeclInfo.protocols = &InterInfo.ObjCProtoListInfo; + + return handleObjCContainer(D, D->getLocation(), getCursor(D), InterInfo); +} + +bool CXIndexDataConsumer::handleObjCImplementation( + const ObjCImplementationDecl *D) { + ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/false, + /*isRedeclaration=*/true, + /*isImplementation=*/true); + return handleObjCContainer(D, D->getLocation(), getCursor(D), ContDInfo); +} + +bool CXIndexDataConsumer::handleObjCProtocol(const ObjCProtocolDecl *D) { + if (!D->isThisDeclarationADefinition()) { + if (shouldSuppressRefs() && markEntityOccurrenceInFile(D, D->getLocation())) + return false; // already occurred. + + // FIXME: This seems like the wrong definition for redeclaration. + bool isRedeclaration = D->hasDefinition() || D->getPreviousDecl(); + ObjCContainerDeclInfo ContDInfo(/*isForwardRef=*/true, + isRedeclaration, + /*isImplementation=*/false); + return handleObjCContainer(D, D->getLocation(), + MakeCursorObjCProtocolRef(D, D->getLocation(), + CXTU), + ContDInfo); + } + + ScratchAlloc SA(*this); + ObjCProtocolList EmptyProtoList; + ObjCProtocolListInfo ProtListInfo(D->isThisDeclarationADefinition() + ? D->getReferencedProtocols() + : EmptyProtoList, + *this, SA); + + ObjCProtocolDeclInfo ProtInfo(D); + ProtInfo.ObjCProtoRefListInfo = ProtListInfo.getListInfo(); + + return handleObjCContainer(D, D->getLocation(), getCursor(D), ProtInfo); +} + +bool CXIndexDataConsumer::handleObjCCategory(const ObjCCategoryDecl *D) { + ScratchAlloc SA(*this); + + ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/false); + EntityInfo ClassEntity; + const ObjCInterfaceDecl *IFaceD = D->getClassInterface(); + SourceLocation ClassLoc = D->getLocation(); + SourceLocation CategoryLoc = D->IsClassExtension() ? ClassLoc + : D->getCategoryNameLoc(); + getEntityInfo(IFaceD, ClassEntity, SA); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(IFaceD, ClassLoc); + + ObjCProtocolListInfo ProtInfo(D->getReferencedProtocols(), *this, SA); + + CatDInfo.ObjCCatDeclInfo.containerInfo = &CatDInfo.ObjCContDeclInfo; + if (IFaceD) { + CatDInfo.ObjCCatDeclInfo.objcClass = &ClassEntity; + CatDInfo.ObjCCatDeclInfo.classCursor = + MakeCursorObjCClassRef(IFaceD, ClassLoc, CXTU); + } else { + CatDInfo.ObjCCatDeclInfo.objcClass = nullptr; + CatDInfo.ObjCCatDeclInfo.classCursor = clang_getNullCursor(); + } + CatDInfo.ObjCCatDeclInfo.classLoc = getIndexLoc(ClassLoc); + CatDInfo.ObjCProtoListInfo = ProtInfo.getListInfo(); + CatDInfo.ObjCCatDeclInfo.protocols = &CatDInfo.ObjCProtoListInfo; + + return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); +} + +bool CXIndexDataConsumer::handleObjCCategoryImpl(const ObjCCategoryImplDecl *D) { + ScratchAlloc SA(*this); + + const ObjCCategoryDecl *CatD = D->getCategoryDecl(); + ObjCCategoryDeclInfo CatDInfo(/*isImplementation=*/true); + EntityInfo ClassEntity; + const ObjCInterfaceDecl *IFaceD = CatD->getClassInterface(); + SourceLocation ClassLoc = D->getLocation(); + SourceLocation CategoryLoc = D->getCategoryNameLoc(); + getEntityInfo(IFaceD, ClassEntity, SA); + + if (shouldSuppressRefs()) + markEntityOccurrenceInFile(IFaceD, ClassLoc); + + CatDInfo.ObjCCatDeclInfo.containerInfo = &CatDInfo.ObjCContDeclInfo; + if (IFaceD) { + CatDInfo.ObjCCatDeclInfo.objcClass = &ClassEntity; + CatDInfo.ObjCCatDeclInfo.classCursor = + MakeCursorObjCClassRef(IFaceD, ClassLoc, CXTU); + } else { + CatDInfo.ObjCCatDeclInfo.objcClass = nullptr; + CatDInfo.ObjCCatDeclInfo.classCursor = clang_getNullCursor(); + } + CatDInfo.ObjCCatDeclInfo.classLoc = getIndexLoc(ClassLoc); + CatDInfo.ObjCCatDeclInfo.protocols = nullptr; + + return handleObjCContainer(D, CategoryLoc, getCursor(D), CatDInfo); +} + +bool CXIndexDataConsumer::handleObjCMethod(const ObjCMethodDecl *D, + SourceLocation Loc) { + bool isDef = D->isThisDeclarationADefinition(); + bool isContainer = isDef; + bool isSkipped = false; + if (D->hasSkippedBody()) { + isSkipped = true; + isDef = true; + isContainer = false; + } + + DeclInfo DInfo(!D->isCanonicalDecl(), isDef, isContainer); + if (isSkipped) + DInfo.flags |= CXIdxDeclFlag_Skipped; + return handleDecl(D, Loc, getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleSynthesizedObjCProperty( + const ObjCPropertyImplDecl *D) { + ObjCPropertyDecl *PD = D->getPropertyDecl(); + auto *DC = D->getDeclContext(); + return handleReference(PD, D->getLocation(), getCursor(D), + dyn_cast<NamedDecl>(DC), DC); +} + +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, D->getDeclContext()); +} + +bool CXIndexDataConsumer::handleObjCProperty(const ObjCPropertyDecl *D) { + ScratchAlloc SA(*this); + + ObjCPropertyDeclInfo DInfo; + EntityInfo GetterEntity; + EntityInfo SetterEntity; + + DInfo.ObjCPropDeclInfo.declInfo = &DInfo; + + if (ObjCMethodDecl *Getter = D->getGetterMethodDecl()) { + getEntityInfo(Getter, GetterEntity, SA); + DInfo.ObjCPropDeclInfo.getter = &GetterEntity; + } else { + DInfo.ObjCPropDeclInfo.getter = nullptr; + } + if (ObjCMethodDecl *Setter = D->getSetterMethodDecl()) { + getEntityInfo(Setter, SetterEntity, SA); + DInfo.ObjCPropDeclInfo.setter = &SetterEntity; + } else { + DInfo.ObjCPropDeclInfo.setter = nullptr; + } + + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleNamespace(const NamespaceDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/!D->isOriginalNamespace(), + /*isDefinition=*/true, + /*isContainer=*/true); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleClassTemplate(const ClassTemplateDecl *D) { + return handleCXXRecordDecl(D->getTemplatedDecl(), 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 CXIndexDataConsumer::handleTypeAliasTemplate(const TypeAliasTemplateDecl *D) { + DeclInfo DInfo(/*isRedeclaration=*/!D->isCanonicalDecl(), + /*isDefinition=*/true, /*isContainer=*/false); + return handleDecl(D, D->getLocation(), getCursor(D), DInfo); +} + +bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, + CXCursor Cursor, + const NamedDecl *Parent, + const DeclContext *DC, + const Expr *E, + CXIdxEntityRefKind Kind, + CXSymbolRole Role) { + if (!CB.indexEntityReference) + return false; + + if (!D || !DC) + return false; + if (Loc.isInvalid()) + return false; + if (!shouldIndexFunctionLocalSymbols() && isFunctionLocalDecl(D)) + return false; + if (isNotFromSourceFile(D->getLocation())) + return false; + if (D->isImplicit() && shouldIgnoreIfImplicit(D)) + return false; + + if (shouldSuppressRefs()) { + if (markEntityOccurrenceInFile(D, Loc)) + return false; // already occurred. + } + + ScratchAlloc SA(*this); + EntityInfo RefEntity, ParentEntity; + getEntityInfo(D, RefEntity, SA); + if (!RefEntity.USR) + return false; + + getEntityInfo(Parent, ParentEntity, SA); + + ContainerInfo Container; + getContainerInfo(DC, Container); + + CXIdxEntityRefInfo Info = { Kind, + Cursor, + getIndexLoc(Loc), + &RefEntity, + Parent ? &ParentEntity : nullptr, + &Container, + Role }; + CB.indexEntityReference(ClientData, &Info); + return true; +} + +bool CXIndexDataConsumer::isNotFromSourceFile(SourceLocation Loc) const { + if (Loc.isInvalid()) + return true; + SourceManager &SM = Ctx->getSourceManager(); + SourceLocation FileLoc = SM.getFileLoc(Loc); + FileID FID = SM.getFileID(FileLoc); + return SM.getFileEntryForID(FID) == nullptr; +} + +void CXIndexDataConsumer::addContainerInMap(const DeclContext *DC, + CXIdxClientContainer container) { + if (!DC) + return; + + ContainerMapTy::iterator I = ContainerMap.find(DC); + if (I == ContainerMap.end()) { + if (container) + ContainerMap[DC] = container; + return; + } + // Allow changing the container of a previously seen DeclContext so we + // can handle invalid user code, like a function re-definition. + if (container) + I->second = container; + else + ContainerMap.erase(I); +} + +CXIdxClientEntity CXIndexDataConsumer::getClientEntity(const Decl *D) const { + if (!D) + return nullptr; + EntityMapTy::const_iterator I = EntityMap.find(D); + if (I == EntityMap.end()) + return nullptr; + return I->second; +} + +void CXIndexDataConsumer::setClientEntity(const Decl *D, CXIdxClientEntity client) { + if (!D) + return; + EntityMap[D] = client; +} + +bool CXIndexDataConsumer::handleCXXRecordDecl(const CXXRecordDecl *RD, + const NamedDecl *OrigD) { + if (RD->isThisDeclarationADefinition()) { + ScratchAlloc SA(*this); + CXXClassDeclInfo CXXDInfo(/*isRedeclaration=*/!OrigD->isCanonicalDecl(), + /*isDefinition=*/RD->isThisDeclarationADefinition()); + CXXBasesListInfo BaseList(RD, *this, SA); + CXXDInfo.CXXClassInfo.declInfo = &CXXDInfo; + CXXDInfo.CXXClassInfo.bases = BaseList.getBases(); + CXXDInfo.CXXClassInfo.numBases = BaseList.getNumBases(); + + if (shouldSuppressRefs()) { + // Go through bases and mark them as referenced. + for (unsigned i = 0, e = BaseList.getNumBases(); i != e; ++i) { + const CXIdxBaseClassInfo *baseInfo = BaseList.getBases()[i]; + if (baseInfo->base) { + const NamedDecl *BaseD = BaseList.BaseEntities[i].Dcl; + SourceLocation + Loc = SourceLocation::getFromRawEncoding(baseInfo->loc.int_data); + markEntityOccurrenceInFile(BaseD, Loc); + } + } + } + + return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), CXXDInfo); + } + + DeclInfo DInfo(/*isRedeclaration=*/!OrigD->isCanonicalDecl(), + /*isDefinition=*/RD->isThisDeclarationADefinition(), + /*isContainer=*/RD->isThisDeclarationADefinition()); + return handleDecl(OrigD, OrigD->getLocation(), getCursor(OrigD), DInfo); +} + +bool CXIndexDataConsumer::markEntityOccurrenceInFile(const NamedDecl *D, + SourceLocation Loc) { + if (!D || Loc.isInvalid()) + return true; + + SourceManager &SM = Ctx->getSourceManager(); + D = getEntityDecl(D); + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SM.getFileLoc(Loc)); + FileID FID = LocInfo.first; + if (FID.isInvalid()) + return true; + + const FileEntry *FE = SM.getFileEntryForID(FID); + if (!FE) + return true; + RefFileOccurrence RefOccur(FE, D); + std::pair<llvm::DenseSet<RefFileOccurrence>::iterator, bool> + res = RefFileOccurrences.insert(RefOccur); + return !res.second; // already in map +} + +const NamedDecl *CXIndexDataConsumer::getEntityDecl(const NamedDecl *D) const { + assert(D); + D = cast<NamedDecl>(D->getCanonicalDecl()); + + if (const ObjCImplementationDecl * + ImplD = dyn_cast<ObjCImplementationDecl>(D)) { + return getEntityDecl(ImplD->getClassInterface()); + + } else if (const ObjCCategoryImplDecl * + CatImplD = dyn_cast<ObjCCategoryImplDecl>(D)) { + return getEntityDecl(CatImplD->getCategoryDecl()); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FunctionTemplateDecl *TemplD = FD->getDescribedFunctionTemplate()) + return getEntityDecl(TemplD); + } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { + if (ClassTemplateDecl *TemplD = RD->getDescribedClassTemplate()) + return getEntityDecl(TemplD); + } + + return D; +} + +const DeclContext * +CXIndexDataConsumer::getEntityContainer(const Decl *D) const { + const DeclContext *DC = dyn_cast<DeclContext>(D); + if (DC) + return DC; + + if (const ClassTemplateDecl *ClassTempl = dyn_cast<ClassTemplateDecl>(D)) { + DC = ClassTempl->getTemplatedDecl(); + } else if (const FunctionTemplateDecl * + FuncTempl = dyn_cast<FunctionTemplateDecl>(D)) { + DC = FuncTempl->getTemplatedDecl(); + } + + return DC; +} + +CXIdxClientContainer +CXIndexDataConsumer::getClientContainerForDC(const DeclContext *DC) const { + if (!DC) + return nullptr; + + ContainerMapTy::const_iterator I = ContainerMap.find(DC); + if (I == ContainerMap.end()) + return nullptr; + + return I->second; +} + +CXIdxClientFile CXIndexDataConsumer::getIndexFile(const FileEntry *File) { + if (!File) + return nullptr; + + FileMapTy::iterator FI = FileMap.find(File); + if (FI != FileMap.end()) + return FI->second; + + return nullptr; +} + +CXIdxLoc CXIndexDataConsumer::getIndexLoc(SourceLocation Loc) const { + CXIdxLoc idxLoc = { {nullptr, nullptr}, 0 }; + if (Loc.isInvalid()) + return idxLoc; + + idxLoc.ptr_data[0] = const_cast<CXIndexDataConsumer *>(this); + idxLoc.int_data = Loc.getRawEncoding(); + return idxLoc; +} + +void CXIndexDataConsumer::translateLoc(SourceLocation Loc, + CXIdxClientFile *indexFile, CXFile *file, + unsigned *line, unsigned *column, + unsigned *offset) { + if (Loc.isInvalid()) + return; + + SourceManager &SM = Ctx->getSourceManager(); + Loc = SM.getFileLoc(Loc); + + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isInvalid()) + return; + + const FileEntry *FE = SM.getFileEntryForID(FID); + if (indexFile) + *indexFile = getIndexFile(FE); + if (file) + *file = const_cast<FileEntry *>(FE); + if (line) + *line = SM.getLineNumber(FID, FileOffset); + if (column) + *column = SM.getColumnNumber(FID, FileOffset); + if (offset) + *offset = FileOffset; +} + +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage L); +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolProperties(SymbolPropertySet K); +static CXIdxEntityLanguage getEntityLangFromSymbolLang(SymbolLanguage L); + +void CXIndexDataConsumer::getEntityInfo(const NamedDecl *D, + EntityInfo &EntityInfo, + ScratchAlloc &SA) { + if (!D) + return; + + D = getEntityDecl(D); + EntityInfo.cursor = getCursor(D); + EntityInfo.Dcl = D; + EntityInfo.IndexCtx = this; + + SymbolInfo SymInfo = getSymbolInfo(D); + EntityInfo.kind = getEntityKindFromSymbolKind(SymInfo.Kind, SymInfo.Lang); + EntityInfo.templateKind = getEntityKindFromSymbolProperties(SymInfo.Properties); + EntityInfo.lang = getEntityLangFromSymbolLang(SymInfo.Lang); + + if (D->hasAttrs()) { + EntityInfo.AttrList = AttrListInfo::create(D, *this); + EntityInfo.attributes = EntityInfo.AttrList->getAttrs(); + EntityInfo.numAttributes = EntityInfo.AttrList->getNumAttrs(); + } + + if (EntityInfo.kind == CXIdxEntity_Unexposed) + return; + + if (IdentifierInfo *II = D->getIdentifier()) { + EntityInfo.name = SA.toCStr(II->getName()); + + } else if (isa<TagDecl>(D) || isa<FieldDecl>(D) || isa<NamespaceDecl>(D)) { + EntityInfo.name = nullptr; // anonymous tag/field/namespace. + + } else { + SmallString<256> StrBuf; + { + llvm::raw_svector_ostream OS(StrBuf); + D->printName(OS); + } + EntityInfo.name = SA.copyCStr(StrBuf.str()); + } + + { + SmallString<512> StrBuf; + bool Ignore = getDeclCursorUSR(D, StrBuf); + if (Ignore) { + EntityInfo.USR = nullptr; + } else { + EntityInfo.USR = SA.copyCStr(StrBuf.str()); + } + } +} + +void CXIndexDataConsumer::getContainerInfo(const DeclContext *DC, + ContainerInfo &ContInfo) { + ContInfo.cursor = getCursor(cast<Decl>(DC)); + ContInfo.DC = DC; + ContInfo.IndexCtx = this; +} + +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)) + return MakeCursorObjCClassRef(ID, Loc, CXTU); + if (const ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) + return MakeCursorObjCProtocolRef(PD, Loc, CXTU); + if (const TemplateDecl *Template = dyn_cast<TemplateDecl>(D)) + return MakeCursorTemplateRef(Template, Loc, CXTU); + if (const NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(D)) + return MakeCursorNamespaceRef(Namespace, Loc, CXTU); + if (const NamespaceAliasDecl *Namespace = dyn_cast<NamespaceAliasDecl>(D)) + return MakeCursorNamespaceRef(Namespace, Loc, CXTU); + if (const FieldDecl *Field = dyn_cast<FieldDecl>(D)) + return MakeCursorMemberRef(Field, Loc, CXTU); + if (const VarDecl *Var = dyn_cast<VarDecl>(D)) + return MakeCursorVariableRef(Var, Loc, CXTU); + + return clang_getNullCursor(); +} + +bool CXIndexDataConsumer::shouldIgnoreIfImplicit(const Decl *D) { + if (isa<ObjCInterfaceDecl>(D)) + return false; + if (isa<ObjCCategoryDecl>(D)) + return false; + if (isa<ObjCIvarDecl>(D)) + return false; + if (isa<ObjCMethodDecl>(D)) + return false; + if (isa<ImportDecl>(D)) + return false; + return true; +} + +bool CXIndexDataConsumer::isTemplateImplicitInstantiation(const Decl *D) { + if (const ClassTemplateSpecializationDecl * + SD = dyn_cast<ClassTemplateSpecializationDecl>(D)) { + return SD->getSpecializationKind() == TSK_ImplicitInstantiation; + } + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + return FD->getTemplateSpecializationKind() == TSK_ImplicitInstantiation; + } + return false; +} + +static CXIdxEntityKind getEntityKindFromSymbolKind(SymbolKind K, SymbolLanguage Lang) { + switch (K) { + case SymbolKind::Unknown: + case SymbolKind::Module: + case SymbolKind::Macro: + case SymbolKind::ClassProperty: + case SymbolKind::Using: + 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; + case SymbolKind::Parameter: return CXIdxEntity_Variable; + } + llvm_unreachable("invalid symbol kind"); +} + +static CXIdxEntityCXXTemplateKind +getEntityKindFromSymbolProperties(SymbolPropertySet K) { + if (K & (SymbolPropertySet)SymbolProperty::TemplatePartialSpecialization) + return CXIdxEntity_TemplatePartialSpecialization; + if (K & (SymbolPropertySet)SymbolProperty::TemplateSpecialization) + return CXIdxEntity_TemplateSpecialization; + if (K & (SymbolPropertySet)SymbolProperty::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; + case SymbolLanguage::Swift: return CXIdxEntityLang_Swift; + } + llvm_unreachable("invalid symbol language"); +} diff --git a/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h new file mode 100644 index 00000000000..ace9d59bf04 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXIndexDataConsumer.h @@ -0,0 +1,501 @@ +//===- CXIndexDataConsumer.h - Index data consumer for libclang--*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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" + +namespace clang { + class FileEntry; + class MSPropertyDecl; + class ObjCPropertyDecl; + class ClassTemplateDecl; + class FunctionTemplateDecl; + class TypeAliasTemplateDecl; + class ClassTemplateSpecializationDecl; + +namespace cxindex { + class CXIndexDataConsumer; + class AttrListInfo; + +class ScratchAlloc { + CXIndexDataConsumer &IdxCtx; + +public: + explicit ScratchAlloc(CXIndexDataConsumer &indexCtx); + ScratchAlloc(const ScratchAlloc &SA); + + ~ScratchAlloc(); + + const char *toCStr(StringRef Str); + const char *copyCStr(StringRef Str); + + template <typename T> + T *allocate(); +}; + +struct EntityInfo : public CXIdxEntityInfo { + const NamedDecl *Dcl; + CXIndexDataConsumer *IndexCtx; + IntrusiveRefCntPtr<AttrListInfo> AttrList; + + EntityInfo() { + name = USR = nullptr; + attributes = nullptr; + numAttributes = 0; + } +}; + +struct ContainerInfo : public CXIdxContainerInfo { + const DeclContext *DC; + CXIndexDataConsumer *IndexCtx; +}; + +struct DeclInfo : public CXIdxDeclInfo { + enum DInfoKind { + Info_Decl, + + Info_ObjCContainer, + Info_ObjCInterface, + Info_ObjCProtocol, + Info_ObjCCategory, + + Info_ObjCProperty, + + Info_CXXClass + }; + + DInfoKind Kind; + + EntityInfo EntInfo; + ContainerInfo SemanticContainer; + ContainerInfo LexicalContainer; + ContainerInfo DeclAsContainer; + + DeclInfo(bool isRedeclaration, bool isDefinition, bool isContainer) + : Kind(Info_Decl) { + this->isRedeclaration = isRedeclaration; + this->isDefinition = isDefinition; + this->isContainer = isContainer; + attributes = nullptr; + numAttributes = 0; + declAsContainer = semanticContainer = lexicalContainer = nullptr; + flags = 0; + } + DeclInfo(DInfoKind K, + bool isRedeclaration, bool isDefinition, bool isContainer) + : Kind(K) { + this->isRedeclaration = isRedeclaration; + this->isDefinition = isDefinition; + this->isContainer = isContainer; + attributes = nullptr; + numAttributes = 0; + declAsContainer = semanticContainer = lexicalContainer = nullptr; + flags = 0; + } +}; + +struct ObjCContainerDeclInfo : public DeclInfo { + CXIdxObjCContainerDeclInfo ObjCContDeclInfo; + + ObjCContainerDeclInfo(bool isForwardRef, + bool isRedeclaration, + bool isImplementation) + : DeclInfo(Info_ObjCContainer, isRedeclaration, + /*isDefinition=*/!isForwardRef, /*isContainer=*/!isForwardRef) { + init(isForwardRef, isImplementation); + } + ObjCContainerDeclInfo(DInfoKind K, + bool isForwardRef, + bool isRedeclaration, + bool isImplementation) + : DeclInfo(K, isRedeclaration, /*isDefinition=*/!isForwardRef, + /*isContainer=*/!isForwardRef) { + init(isForwardRef, isImplementation); + } + + static bool classof(const DeclInfo *D) { + return Info_ObjCContainer <= D->Kind && D->Kind <= Info_ObjCCategory; + } + +private: + void init(bool isForwardRef, bool isImplementation) { + if (isForwardRef) + ObjCContDeclInfo.kind = CXIdxObjCContainer_ForwardRef; + else if (isImplementation) + ObjCContDeclInfo.kind = CXIdxObjCContainer_Implementation; + else + ObjCContDeclInfo.kind = CXIdxObjCContainer_Interface; + } +}; + +struct ObjCInterfaceDeclInfo : public ObjCContainerDeclInfo { + CXIdxObjCInterfaceDeclInfo ObjCInterDeclInfo; + CXIdxObjCProtocolRefListInfo ObjCProtoListInfo; + + ObjCInterfaceDeclInfo(const ObjCInterfaceDecl *D) + : ObjCContainerDeclInfo(Info_ObjCInterface, + /*isForwardRef=*/false, + /*isRedeclaration=*/D->getPreviousDecl() != nullptr, + /*isImplementation=*/false) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCInterface; + } +}; + +struct ObjCProtocolDeclInfo : public ObjCContainerDeclInfo { + CXIdxObjCProtocolRefListInfo ObjCProtoRefListInfo; + + ObjCProtocolDeclInfo(const ObjCProtocolDecl *D) + : ObjCContainerDeclInfo(Info_ObjCProtocol, + /*isForwardRef=*/false, + /*isRedeclaration=*/D->getPreviousDecl(), + /*isImplementation=*/false) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCProtocol; + } +}; + +struct ObjCCategoryDeclInfo : public ObjCContainerDeclInfo { + CXIdxObjCCategoryDeclInfo ObjCCatDeclInfo; + CXIdxObjCProtocolRefListInfo ObjCProtoListInfo; + + explicit ObjCCategoryDeclInfo(bool isImplementation) + : ObjCContainerDeclInfo(Info_ObjCCategory, + /*isForwardRef=*/false, + /*isRedeclaration=*/isImplementation, + /*isImplementation=*/isImplementation) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCCategory; + } +}; + +struct ObjCPropertyDeclInfo : public DeclInfo { + CXIdxObjCPropertyDeclInfo ObjCPropDeclInfo; + + ObjCPropertyDeclInfo() + : DeclInfo(Info_ObjCProperty, + /*isRedeclaration=*/false, /*isDefinition=*/false, + /*isContainer=*/false) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_ObjCProperty; + } +}; + +struct CXXClassDeclInfo : public DeclInfo { + CXIdxCXXClassDeclInfo CXXClassInfo; + + CXXClassDeclInfo(bool isRedeclaration, bool isDefinition) + : DeclInfo(Info_CXXClass, isRedeclaration, isDefinition, isDefinition) { } + + static bool classof(const DeclInfo *D) { + return D->Kind == Info_CXXClass; + } +}; + +struct AttrInfo : public CXIdxAttrInfo { + const Attr *A; + + AttrInfo(CXIdxAttrKind Kind, CXCursor C, CXIdxLoc Loc, const Attr *A) { + kind = Kind; + cursor = C; + loc = Loc; + this->A = A; + } +}; + +struct IBOutletCollectionInfo : public AttrInfo { + EntityInfo ClassInfo; + CXIdxIBOutletCollectionAttrInfo IBCollInfo; + + IBOutletCollectionInfo(CXCursor C, CXIdxLoc Loc, const Attr *A) : + AttrInfo(CXIdxAttr_IBOutletCollection, C, Loc, A) { + assert(C.kind == CXCursor_IBOutletCollectionAttr); + IBCollInfo.objcClass = nullptr; + } + + IBOutletCollectionInfo(const IBOutletCollectionInfo &other); + + static bool classof(const AttrInfo *A) { + return A->kind == CXIdxAttr_IBOutletCollection; + } +}; + +class AttrListInfo { + ScratchAlloc SA; + + SmallVector<AttrInfo, 2> Attrs; + SmallVector<IBOutletCollectionInfo, 2> IBCollAttrs; + SmallVector<CXIdxAttrInfo *, 2> CXAttrs; + unsigned ref_cnt; + + AttrListInfo(const AttrListInfo &) = delete; + void operator=(const AttrListInfo &) = delete; +public: + AttrListInfo(const Decl *D, CXIndexDataConsumer &IdxCtx); + + static IntrusiveRefCntPtr<AttrListInfo> create(const Decl *D, + CXIndexDataConsumer &IdxCtx); + + const CXIdxAttrInfo *const *getAttrs() const { + if (CXAttrs.empty()) + return nullptr; + return CXAttrs.data(); + } + unsigned getNumAttrs() const { return (unsigned)CXAttrs.size(); } + + /// Retain/Release only useful when we allocate a AttrListInfo from the + /// BumpPtrAllocator, and not from the stack; so that we keep a pointer + // in the EntityInfo + void Retain() { ++ref_cnt; } + void Release() { + assert (ref_cnt > 0 && "Reference count is already zero."); + if (--ref_cnt == 0) { + // Memory is allocated from a BumpPtrAllocator, no need to delete it. + this->~AttrListInfo(); + } + } +}; + +class CXIndexDataConsumer : public index::IndexDataConsumer { + ASTContext *Ctx; + CXClientData ClientData; + IndexerCallbacks &CB; + unsigned IndexOptions; + CXTranslationUnit CXTU; + + typedef llvm::DenseMap<const FileEntry *, CXIdxClientFile> FileMapTy; + typedef llvm::DenseMap<const DeclContext *, CXIdxClientContainer> + ContainerMapTy; + typedef llvm::DenseMap<const Decl *, CXIdxClientEntity> EntityMapTy; + + FileMapTy FileMap; + ContainerMapTy ContainerMap; + EntityMapTy EntityMap; + + typedef std::pair<const FileEntry *, const Decl *> RefFileOccurrence; + llvm::DenseSet<RefFileOccurrence> RefFileOccurrences; + + llvm::BumpPtrAllocator StrScratch; + unsigned StrAdapterCount; + friend class ScratchAlloc; + + struct ObjCProtocolListInfo { + SmallVector<CXIdxObjCProtocolRefInfo, 4> ProtInfos; + SmallVector<EntityInfo, 4> ProtEntities; + SmallVector<CXIdxObjCProtocolRefInfo *, 4> Prots; + + CXIdxObjCProtocolRefListInfo getListInfo() const { + CXIdxObjCProtocolRefListInfo Info = { Prots.data(), + (unsigned)Prots.size() }; + return Info; + } + + ObjCProtocolListInfo(const ObjCProtocolList &ProtList, + CXIndexDataConsumer &IdxCtx, + ScratchAlloc &SA); + }; + + struct CXXBasesListInfo { + SmallVector<CXIdxBaseClassInfo, 4> BaseInfos; + SmallVector<EntityInfo, 4> BaseEntities; + SmallVector<CXIdxBaseClassInfo *, 4> CXBases; + + const CXIdxBaseClassInfo *const *getBases() const { + return CXBases.data(); + } + unsigned getNumBases() const { return (unsigned)CXBases.size(); } + + CXXBasesListInfo(const CXXRecordDecl *D, + CXIndexDataConsumer &IdxCtx, ScratchAlloc &SA); + + private: + SourceLocation getBaseLoc(const CXXBaseSpecifier &Base) const; + }; + + friend class AttrListInfo; + +public: + 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(std::shared_ptr<Preprocessor> PP) override; + + bool shouldSuppressRefs() const { + return IndexOptions & CXIndexOpt_SuppressRedundantRefs; + } + + bool shouldIndexFunctionLocalSymbols() const { + return IndexOptions & CXIndexOpt_IndexFunctionLocalSymbols; + } + + bool shouldIndexImplicitTemplateInsts() const { + return IndexOptions & CXIndexOpt_IndexImplicitTemplateInstantiations; + } + + static bool isFunctionLocalDecl(const Decl *D); + + bool shouldAbort(); + + bool hasDiagnosticCallback() const { return CB.diagnostic; } + + void enteredMainFile(const FileEntry *File); + + void ppIncludedFile(SourceLocation hashLoc, + StringRef filename, const FileEntry *File, + bool isImport, bool isAngled, bool isModuleImport); + + void importedModule(const ImportDecl *ImportD); + void importedPCH(const FileEntry *File); + + void startedTranslationUnit(); + + void indexDiagnostics(); + + void handleDiagnosticSet(CXDiagnosticSet CXDiagSet); + + bool handleFunction(const FunctionDecl *FD); + + bool handleVar(const VarDecl *D); + + bool handleField(const FieldDecl *D); + + bool handleEnumerator(const EnumConstantDecl *D); + + bool handleTagDecl(const TagDecl *D); + + bool handleTypedefName(const TypedefNameDecl *D); + + bool handleObjCInterface(const ObjCInterfaceDecl *D); + bool handleObjCImplementation(const ObjCImplementationDecl *D); + + bool handleObjCProtocol(const ObjCProtocolDecl *D); + + bool handleObjCCategory(const ObjCCategoryDecl *D); + bool handleObjCCategoryImpl(const ObjCCategoryImplDecl *D); + + bool handleObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc); + + bool handleSynthesizedObjCProperty(const ObjCPropertyImplDecl *D); + bool handleSynthesizedObjCMethod(const ObjCMethodDecl *D, SourceLocation Loc, + const DeclContext *LexicalDC); + + bool handleObjCProperty(const ObjCPropertyDecl *D); + + bool handleNamespace(const NamespaceDecl *D); + + bool handleClassTemplate(const ClassTemplateDecl *D); + bool handleFunctionTemplate(const FunctionTemplateDecl *D); + bool handleTypeAliasTemplate(const TypeAliasTemplateDecl *D); + + bool handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, + const NamedDecl *Parent, + const DeclContext *DC, + const Expr *E = nullptr, + CXIdxEntityRefKind Kind = CXIdxEntityRef_Direct, + CXSymbolRole Role = CXSymbolRole_None); + + bool isNotFromSourceFile(SourceLocation Loc) const; + + void translateLoc(SourceLocation Loc, CXIdxClientFile *indexFile, CXFile *file, + unsigned *line, unsigned *column, unsigned *offset); + + CXIdxClientContainer getClientContainerForDC(const DeclContext *DC) const; + void addContainerInMap(const DeclContext *DC, CXIdxClientContainer container); + + CXIdxClientEntity getClientEntity(const Decl *D) const; + void setClientEntity(const Decl *D, CXIdxClientEntity client); + + static bool isTemplateImplicitInstantiation(const Decl *D); + +private: + bool handleDeclOccurrence(const Decl *D, index::SymbolRoleSet Roles, + ArrayRef<index::SymbolRelation> Relations, + SourceLocation Loc, ASTNodeInfo ASTNode) override; + + bool handleModuleOccurrence(const ImportDecl *ImportD, const Module *Mod, + index::SymbolRoleSet Roles, + SourceLocation Loc) override; + + void finish() override; + + bool handleDecl(const NamedDecl *D, + SourceLocation Loc, CXCursor Cursor, + DeclInfo &DInfo, + const DeclContext *LexicalDC = nullptr, + const DeclContext *SemaDC = nullptr); + + bool handleObjCContainer(const ObjCContainerDecl *D, + SourceLocation Loc, CXCursor Cursor, + ObjCContainerDeclInfo &ContDInfo); + + bool handleCXXRecordDecl(const CXXRecordDecl *RD, const NamedDecl *OrigD); + + bool markEntityOccurrenceInFile(const NamedDecl *D, SourceLocation Loc); + + const NamedDecl *getEntityDecl(const NamedDecl *D) const; + + const DeclContext *getEntityContainer(const Decl *D) const; + + CXIdxClientFile getIndexFile(const FileEntry *File); + + CXIdxLoc getIndexLoc(SourceLocation Loc) const; + + void getEntityInfo(const NamedDecl *D, + EntityInfo &EntityInfo, + ScratchAlloc &SA); + + void getContainerInfo(const DeclContext *DC, ContainerInfo &ContInfo); + + CXCursor getCursor(const Decl *D) { + return cxcursor::MakeCXCursor(D, CXTU); + } + + CXCursor getRefCursor(const NamedDecl *D, SourceLocation Loc); + + static bool shouldIgnoreIfImplicit(const Decl *D); +}; + +inline ScratchAlloc::ScratchAlloc(CXIndexDataConsumer &idxCtx) : IdxCtx(idxCtx) { + ++IdxCtx.StrAdapterCount; +} +inline ScratchAlloc::ScratchAlloc(const ScratchAlloc &SA) : IdxCtx(SA.IdxCtx) { + ++IdxCtx.StrAdapterCount; +} + +inline ScratchAlloc::~ScratchAlloc() { + --IdxCtx.StrAdapterCount; + if (IdxCtx.StrAdapterCount == 0) + IdxCtx.StrScratch.Reset(); +} + +template <typename T> +inline T *ScratchAlloc::allocate() { + return IdxCtx.StrScratch.Allocate<T>(); +} + +}} // end clang::cxindex + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp b/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp new file mode 100644 index 00000000000..b3dcf977b92 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.cpp @@ -0,0 +1,394 @@ +//===-- CXLoadedDiagnostic.cpp - Handling of persisent diags ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements handling of persisent diagnostics. +// +//===----------------------------------------------------------------------===// + +#include "CXLoadedDiagnostic.h" +#include "CXString.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/SerializedDiagnosticReader.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Extend CXDiagnosticSetImpl which contains strings for diagnostics. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap<unsigned, const char *> Strings; + +namespace { +class CXLoadedDiagnosticSetImpl : public CXDiagnosticSetImpl { +public: + CXLoadedDiagnosticSetImpl() : CXDiagnosticSetImpl(true), FakeFiles(FO) {} + ~CXLoadedDiagnosticSetImpl() override {} + + llvm::BumpPtrAllocator Alloc; + Strings Categories; + Strings WarningFlags; + Strings FileNames; + + FileSystemOptions FO; + FileManager FakeFiles; + llvm::DenseMap<unsigned, const FileEntry *> Files; + + /// Copy the string into our own allocator. + const char *copyString(StringRef Blob) { + char *mem = Alloc.Allocate<char>(Blob.size() + 1); + memcpy(mem, Blob.data(), Blob.size()); + mem[Blob.size()] = '\0'; + return mem; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Cleanup. +//===----------------------------------------------------------------------===// + +CXLoadedDiagnostic::~CXLoadedDiagnostic() {} + +//===----------------------------------------------------------------------===// +// Public CXLoadedDiagnostic methods. +//===----------------------------------------------------------------------===// + +CXDiagnosticSeverity CXLoadedDiagnostic::getSeverity() const { + // FIXME: Fail more softly if the diagnostic level is unknown? + auto severityAsLevel = static_cast<serialized_diags::Level>(severity); + assert(severity == static_cast<unsigned>(severityAsLevel) && + "unknown serialized diagnostic level"); + + switch (severityAsLevel) { +#define CASE(X) case serialized_diags::X: return CXDiagnostic_##X; + CASE(Ignored) + CASE(Note) + CASE(Warning) + CASE(Error) + CASE(Fatal) +#undef CASE + // The 'Remark' level isn't represented in the stable API. + case serialized_diags::Remark: return CXDiagnostic_Warning; + } + + llvm_unreachable("Invalid diagnostic level"); +} + +static CXSourceLocation makeLocation(const CXLoadedDiagnostic::Location *DLoc) { + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + uintptr_t V = (uintptr_t) DLoc; + V |= 0x1; + CXSourceLocation Loc = { { (void*) V, nullptr }, 0 }; + return Loc; +} + +CXSourceLocation CXLoadedDiagnostic::getLocation() const { + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + return makeLocation(&DiagLoc); +} + +CXString CXLoadedDiagnostic::getSpelling() const { + return cxstring::createRef(Spelling); +} + +CXString CXLoadedDiagnostic::getDiagnosticOption(CXString *Disable) const { + if (DiagOption.empty()) + return cxstring::createEmpty(); + + // FIXME: possibly refactor with logic in CXStoredDiagnostic. + if (Disable) + *Disable = cxstring::createDup((Twine("-Wno-") + DiagOption).str()); + return cxstring::createDup((Twine("-W") + DiagOption).str()); +} + +unsigned CXLoadedDiagnostic::getCategory() const { + return category; +} + +CXString CXLoadedDiagnostic::getCategoryText() const { + return cxstring::createDup(CategoryText); +} + +unsigned CXLoadedDiagnostic::getNumRanges() const { + return Ranges.size(); +} + +CXSourceRange CXLoadedDiagnostic::getRange(unsigned Range) const { + assert(Range < Ranges.size()); + return Ranges[Range]; +} + +unsigned CXLoadedDiagnostic::getNumFixIts() const { + return FixIts.size(); +} + +CXString CXLoadedDiagnostic::getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const { + assert(FixIt < FixIts.size()); + if (ReplacementRange) + *ReplacementRange = FixIts[FixIt].first; + return cxstring::createRef(FixIts[FixIt].second); +} + +void CXLoadedDiagnostic::decodeLocation(CXSourceLocation location, + CXFile *file, + unsigned int *line, + unsigned int *column, + unsigned int *offset) { + + + // CXSourceLocation consists of the following fields: + // + // void *ptr_data[2]; + // unsigned int_data; + // + // The lowest bit of ptr_data[0] is always set to 1 to indicate this + // is a persistent diagnostic. + // + // For now, do the unoptimized approach and store the data in a side + // data structure. We can optimize this case later. + + uintptr_t V = (uintptr_t) location.ptr_data[0]; + assert((V & 0x1) == 1); + V &= ~(uintptr_t)1; + + const Location &Loc = *((Location*)V); + + if (file) + *file = Loc.file; + if (line) + *line = Loc.line; + if (column) + *column = Loc.column; + if (offset) + *offset = Loc.offset; +} + +//===----------------------------------------------------------------------===// +// Deserialize diagnostics. +//===----------------------------------------------------------------------===// + +namespace { +class DiagLoader : serialized_diags::SerializedDiagnosticReader { + enum CXLoadDiag_Error *error; + CXString *errorString; + std::unique_ptr<CXLoadedDiagnosticSetImpl> TopDiags; + SmallVector<std::unique_ptr<CXLoadedDiagnostic>, 8> CurrentDiags; + + std::error_code reportBad(enum CXLoadDiag_Error code, llvm::StringRef err) { + if (error) + *error = code; + if (errorString) + *errorString = cxstring::createDup(err); + return serialized_diags::SDError::HandlerFailed; + } + + std::error_code reportInvalidFile(llvm::StringRef err) { + return reportBad(CXLoadDiag_InvalidFile, err); + } + + std::error_code readRange(const serialized_diags::Location &SDStart, + const serialized_diags::Location &SDEnd, + CXSourceRange &SR); + + std::error_code readLocation(const serialized_diags::Location &SDLoc, + CXLoadedDiagnostic::Location &LoadedLoc); + +protected: + std::error_code visitStartOfDiagnostic() override; + std::error_code visitEndOfDiagnostic() override; + + std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; + + std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; + + std::error_code visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) override; + + std::error_code visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) override; + + std::error_code visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) override; + + std::error_code + visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) override; + +public: + DiagLoader(enum CXLoadDiag_Error *e, CXString *es) + : SerializedDiagnosticReader(), error(e), errorString(es) { + if (error) + *error = CXLoadDiag_None; + if (errorString) + *errorString = cxstring::createEmpty(); + } + + CXDiagnosticSet load(const char *file); +}; +} // end anonymous namespace + +CXDiagnosticSet DiagLoader::load(const char *file) { + TopDiags = std::make_unique<CXLoadedDiagnosticSetImpl>(); + + std::error_code EC = readDiagnostics(file); + if (EC) { + switch (EC.value()) { + case static_cast<int>(serialized_diags::SDError::HandlerFailed): + // We've already reported the problem. + break; + case static_cast<int>(serialized_diags::SDError::CouldNotLoad): + reportBad(CXLoadDiag_CannotLoad, EC.message()); + break; + default: + reportInvalidFile(EC.message()); + break; + } + return nullptr; + } + + return (CXDiagnosticSet)TopDiags.release(); +} + +std::error_code +DiagLoader::readLocation(const serialized_diags::Location &SDLoc, + CXLoadedDiagnostic::Location &LoadedLoc) { + unsigned FileID = SDLoc.FileID; + if (FileID == 0) + LoadedLoc.file = nullptr; + else { + LoadedLoc.file = const_cast<FileEntry *>(TopDiags->Files[FileID]); + if (!LoadedLoc.file) + return reportInvalidFile("Corrupted file entry in source location"); + } + LoadedLoc.line = SDLoc.Line; + LoadedLoc.column = SDLoc.Col; + LoadedLoc.offset = SDLoc.Offset; + return std::error_code(); +} + +std::error_code +DiagLoader::readRange(const serialized_diags::Location &SDStart, + const serialized_diags::Location &SDEnd, + CXSourceRange &SR) { + CXLoadedDiagnostic::Location *Start, *End; + Start = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>(); + End = TopDiags->Alloc.Allocate<CXLoadedDiagnostic::Location>(); + + std::error_code EC; + if ((EC = readLocation(SDStart, *Start))) + return EC; + if ((EC = readLocation(SDEnd, *End))) + return EC; + + CXSourceLocation startLoc = makeLocation(Start); + CXSourceLocation endLoc = makeLocation(End); + SR = clang_getRange(startLoc, endLoc); + return std::error_code(); +} + +std::error_code DiagLoader::visitStartOfDiagnostic() { + CurrentDiags.push_back(std::make_unique<CXLoadedDiagnostic>()); + return std::error_code(); +} + +std::error_code DiagLoader::visitEndOfDiagnostic() { + auto D = CurrentDiags.pop_back_val(); + if (CurrentDiags.empty()) + TopDiags->appendDiagnostic(std::move(D)); + else + CurrentDiags.back()->getChildDiagnostics().appendDiagnostic(std::move(D)); + return std::error_code(); +} + +std::error_code DiagLoader::visitCategoryRecord(unsigned ID, StringRef Name) { + // FIXME: Why do we care about long strings? + if (Name.size() > 65536) + return reportInvalidFile("Out-of-bounds string in category"); + TopDiags->Categories[ID] = TopDiags->copyString(Name); + return std::error_code(); +} + +std::error_code DiagLoader::visitDiagFlagRecord(unsigned ID, StringRef Name) { + // FIXME: Why do we care about long strings? + if (Name.size() > 65536) + return reportInvalidFile("Out-of-bounds string in warning flag"); + TopDiags->WarningFlags[ID] = TopDiags->copyString(Name); + return std::error_code(); +} + +std::error_code DiagLoader::visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) { + // FIXME: Why do we care about long strings? + if (Name.size() > 65536) + return reportInvalidFile("Out-of-bounds string in filename"); + TopDiags->FileNames[ID] = TopDiags->copyString(Name); + TopDiags->Files[ID] = + TopDiags->FakeFiles.getVirtualFile(Name, Size, Timestamp); + return std::error_code(); +} + +std::error_code +DiagLoader::visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) { + CXSourceRange SR; + if (std::error_code EC = readRange(Start, End, SR)) + return EC; + CurrentDiags.back()->Ranges.push_back(SR); + return std::error_code(); +} + +std::error_code +DiagLoader::visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) { + CXSourceRange SR; + if (std::error_code EC = readRange(Start, End, SR)) + return EC; + // FIXME: Why do we care about long strings? + if (CodeToInsert.size() > 65536) + return reportInvalidFile("Out-of-bounds string in FIXIT"); + CurrentDiags.back()->FixIts.push_back( + std::make_pair(SR, TopDiags->copyString(CodeToInsert))); + return std::error_code(); +} + +std::error_code DiagLoader::visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) { + CXLoadedDiagnostic &D = *CurrentDiags.back(); + D.severity = Severity; + if (std::error_code EC = readLocation(Location, D.DiagLoc)) + return EC; + D.category = Category; + D.DiagOption = Flag ? TopDiags->WarningFlags[Flag] : ""; + D.CategoryText = Category ? TopDiags->Categories[Category] : ""; + D.Spelling = TopDiags->copyString(Message); + return std::error_code(); +} + +CXDiagnosticSet clang_loadDiagnostics(const char *file, + enum CXLoadDiag_Error *error, + CXString *errorString) { + DiagLoader L(error, errorString); + return L.load(file); +} diff --git a/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.h b/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.h new file mode 100644 index 00000000000..93995d7bb79 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXLoadedDiagnostic.h @@ -0,0 +1,93 @@ +/*===-- CXLoadedDiagnostic.h - Handling of persisent diags ------*- C++ -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements handling of persisent diagnostics. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXLOADEDDIAGNOSTIC_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXLOADEDDIAGNOSTIC_H + +#include "CIndexDiagnostic.h" +#include "llvm/ADT/StringRef.h" +#include "clang/Basic/LLVM.h" +#include <vector> + +namespace clang { +class CXLoadedDiagnostic : public CXDiagnosticImpl { +public: + CXLoadedDiagnostic() : CXDiagnosticImpl(LoadedDiagnosticKind), + severity(0), category(0) {} + + ~CXLoadedDiagnostic() override; + + /// Return the severity of the diagnostic. + CXDiagnosticSeverity getSeverity() const override; + + /// Return the location of the diagnostic. + CXSourceLocation getLocation() const override; + + /// Return the spelling of the diagnostic. + CXString getSpelling() const override; + + /// Return the text for the diagnostic option. + CXString getDiagnosticOption(CXString *Disable) const override; + + /// Return the category of the diagnostic. + unsigned getCategory() const override; + + /// Return the category string of the diagnostic. + CXString getCategoryText() const override; + + /// Return the number of source ranges for the diagnostic. + unsigned getNumRanges() const override; + + /// Return the source ranges for the diagnostic. + CXSourceRange getRange(unsigned Range) const override; + + /// Return the number of FixIts. + unsigned getNumFixIts() const override; + + /// Return the FixIt information (source range and inserted text). + CXString getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const override; + + static bool classof(const CXDiagnosticImpl *D) { + return D->getKind() == LoadedDiagnosticKind; + } + + /// Decode the CXSourceLocation into file, line, column, and offset. + static void decodeLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset); + + struct Location { + CXFile file; + unsigned line; + unsigned column; + unsigned offset; + + Location() : line(0), column(0), offset(0) {} + }; + + Location DiagLoc; + + std::vector<CXSourceRange> Ranges; + std::vector<std::pair<CXSourceRange, const char *> > FixIts; + const char *Spelling; + llvm::StringRef DiagOption; + llvm::StringRef CategoryText; + unsigned severity; + unsigned category; +}; +} + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CXSourceLocation.cpp b/gnu/llvm/clang/tools/libclang/CXSourceLocation.cpp new file mode 100644 index 00000000000..a253dadb3b2 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXSourceLocation.cpp @@ -0,0 +1,373 @@ +//===- CXSourceLocation.cpp - CXSourceLocations APIs ------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXSourceLocations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "CIndexer.h" +#include "CLog.h" +#include "CXLoadedDiagnostic.h" +#include "CXSourceLocation.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Format.h" + +using namespace clang; +using namespace clang::cxindex; + +//===----------------------------------------------------------------------===// +// Internal predicates on CXSourceLocations. +//===----------------------------------------------------------------------===// + +static bool isASTUnitSourceLocation(const CXSourceLocation &L) { + // If the lowest bit is clear then the first ptr_data entry is a SourceManager + // pointer, or the CXSourceLocation is a null location. + return ((uintptr_t)L.ptr_data[0] & 0x1) == 0; +} + +//===----------------------------------------------------------------------===// +// Basic construction and comparison of CXSourceLocations and CXSourceRanges. +//===----------------------------------------------------------------------===// + +CXSourceLocation clang_getNullLocation() { + CXSourceLocation Result = { { nullptr, nullptr }, 0 }; + return Result; +} + +unsigned clang_equalLocations(CXSourceLocation loc1, CXSourceLocation loc2) { + return (loc1.ptr_data[0] == loc2.ptr_data[0] && + loc1.ptr_data[1] == loc2.ptr_data[1] && + loc1.int_data == loc2.int_data); +} + +CXSourceRange clang_getNullRange() { + CXSourceRange Result = { { nullptr, nullptr }, 0, 0 }; + return Result; +} + +CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { + if (!isASTUnitSourceLocation(begin)) { + if (isASTUnitSourceLocation(end)) + return clang_getNullRange(); + CXSourceRange Result = { { begin.ptr_data[0], end.ptr_data[0] }, 0, 0 }; + return Result; + } + + if (begin.ptr_data[0] != end.ptr_data[0] || + begin.ptr_data[1] != end.ptr_data[1]) + return clang_getNullRange(); + + CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] }, + begin.int_data, end.int_data }; + + return Result; +} + +unsigned clang_equalRanges(CXSourceRange range1, CXSourceRange range2) { + return range1.ptr_data[0] == range2.ptr_data[0] + && range1.ptr_data[1] == range2.ptr_data[1] + && range1.begin_int_data == range2.begin_int_data + && range1.end_int_data == range2.end_int_data; +} + +int clang_Range_isNull(CXSourceRange range) { + return clang_equalRanges(range, clang_getNullRange()); +} + + +CXSourceLocation clang_getRangeStart(CXSourceRange range) { + // Special decoding for CXSourceLocations for CXLoadedDiagnostics. + if ((uintptr_t)range.ptr_data[0] & 0x1) { + CXSourceLocation Result = { { range.ptr_data[0], nullptr }, 0 }; + return Result; + } + + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, + range.begin_int_data }; + return Result; +} + +CXSourceLocation clang_getRangeEnd(CXSourceRange range) { + // Special decoding for CXSourceLocations for CXLoadedDiagnostics. + if ((uintptr_t)range.ptr_data[0] & 0x1) { + CXSourceLocation Result = { { range.ptr_data[1], nullptr }, 0 }; + return Result; + } + + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, + range.end_int_data }; + return Result; +} + +//===----------------------------------------------------------------------===// +// Getting CXSourceLocations and CXSourceRanges from a translation unit. +//===----------------------------------------------------------------------===// + +CXSourceLocation clang_getLocation(CXTranslationUnit TU, + CXFile file, + unsigned line, + unsigned column) { + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return clang_getNullLocation(); + } + if (!file) + return clang_getNullLocation(); + if (line == 0 || column == 0) + return clang_getNullLocation(); + + LogRef Log = Logger::make(__func__); + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + const FileEntry *File = static_cast<const FileEntry *>(file); + SourceLocation SLoc = CXXUnit->getLocation(File, line, column); + if (SLoc.isInvalid()) { + if (Log) + *Log << llvm::format("(\"%s\", %d, %d) = invalid", + File->getName().str().c_str(), line, column); + return clang_getNullLocation(); + } + + CXSourceLocation CXLoc = + cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); + if (Log) + *Log << llvm::format("(\"%s\", %d, %d) = ", File->getName().str().c_str(), + line, column) + << CXLoc; + + return CXLoc; +} + +CXSourceLocation clang_getLocationForOffset(CXTranslationUnit TU, + CXFile file, + unsigned offset) { + if (cxtu::isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return clang_getNullLocation(); + } + if (!file) + return clang_getNullLocation(); + + ASTUnit *CXXUnit = cxtu::getASTUnit(TU); + + SourceLocation SLoc + = CXXUnit->getLocation(static_cast<const FileEntry *>(file), offset); + + if (SLoc.isInvalid()) + return clang_getNullLocation(); + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); +} + +//===----------------------------------------------------------------------===// +// Routines for expanding and manipulating CXSourceLocations, regardless +// of their origin. +//===----------------------------------------------------------------------===// + +static void createNullLocation(CXFile *file, unsigned *line, + unsigned *column, unsigned *offset) { + if (file) + *file = nullptr; + if (line) + *line = 0; + if (column) + *column = 0; + if (offset) + *offset = 0; +} + +static void createNullLocation(CXString *filename, unsigned *line, + unsigned *column, unsigned *offset = nullptr) { + if (filename) + *filename = cxstring::createEmpty(); + if (line) + *line = 0; + if (column) + *column = 0; + if (offset) + *offset = 0; +} + +int clang_Location_isInSystemHeader(CXSourceLocation location) { + const SourceLocation Loc = + SourceLocation::getFromRawEncoding(location.int_data); + if (Loc.isInvalid()) + return 0; + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + return SM.isInSystemHeader(Loc); +} + +int clang_Location_isFromMainFile(CXSourceLocation location) { + const SourceLocation Loc = + SourceLocation::getFromRawEncoding(location.int_data); + if (Loc.isInvalid()) + return 0; + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + return SM.isWrittenInMainFile(Loc); +} + +void clang_getExpansionLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, column, offset); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) { + createNullLocation(file, line, column, offset); + return; + } + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + + // Check that the FileID is invalid on the expansion location. + // This can manifest in invalid code. + FileID fileID = SM.getFileID(ExpansionLoc); + bool Invalid = false; + const SrcMgr::SLocEntry &sloc = SM.getSLocEntry(fileID, &Invalid); + if (Invalid || !sloc.isFile()) { + createNullLocation(file, line, column, offset); + return; + } + + if (file) + *file = const_cast<FileEntry *>(SM.getFileEntryForSLocEntry(sloc)); + if (line) + *line = SM.getExpansionLineNumber(ExpansionLoc); + if (column) + *column = SM.getExpansionColumnNumber(ExpansionLoc); + if (offset) + *offset = SM.getDecomposedLoc(ExpansionLoc).second; +} + +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. + createNullLocation(filename, line, column); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) { + createNullLocation(filename, line, column); + return; + } + + const SourceManager &SM = + *static_cast<const SourceManager *>(location.ptr_data[0]); + PresumedLoc PreLoc = SM.getPresumedLoc(Loc); + if (PreLoc.isInvalid()) { + createNullLocation(filename, line, column); + return; + } + + if (filename) *filename = cxstring::createRef(PreLoc.getFilename()); + if (line) *line = PreLoc.getLine(); + if (column) *column = PreLoc.getColumn(); +} + +void clang_getInstantiationLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + // Redirect to new API. + clang_getExpansionLocation(location, file, line, column, offset); +} + +void clang_getSpellingLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, + column, offset); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) + return createNullLocation(file, line, column, offset); + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + // FIXME: This should call SourceManager::getSpellingLoc(). + SourceLocation SpellLoc = SM.getFileLoc(Loc); + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(SpellLoc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isInvalid()) + return createNullLocation(file, line, column, offset); + + if (file) + *file = const_cast<FileEntry *>(SM.getFileEntryForID(FID)); + if (line) + *line = SM.getLineNumber(FID, FileOffset); + if (column) + *column = SM.getColumnNumber(FID, FileOffset); + if (offset) + *offset = FileOffset; +} + +void clang_getFileLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + if (!isASTUnitSourceLocation(location)) { + CXLoadedDiagnostic::decodeLocation(location, file, line, + column, offset); + return; + } + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) + return createNullLocation(file, line, column, offset); + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + SourceLocation FileLoc = SM.getFileLoc(Loc); + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(FileLoc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + if (FID.isInvalid()) + return createNullLocation(file, line, column, offset); + + if (file) + *file = const_cast<FileEntry *>(SM.getFileEntryForID(FID)); + if (line) + *line = SM.getLineNumber(FID, FileOffset); + if (column) + *column = SM.getColumnNumber(FID, FileOffset); + if (offset) + *offset = FileOffset; +} diff --git a/gnu/llvm/clang/tools/libclang/CXSourceLocation.h b/gnu/llvm/clang/tools/libclang/CXSourceLocation.h new file mode 100644 index 00000000000..6702d0cf979 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXSourceLocation.h @@ -0,0 +1,77 @@ +//===- CXSourceLocation.h - CXSourceLocations Utilities ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXSourceLocations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXSOURCELOCATION_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXSOURCELOCATION_H + +#include "clang-c/Index.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" + +namespace clang { + +class SourceManager; + +namespace cxloc { + +/// Translate a Clang source location into a CIndex source location. +static inline CXSourceLocation +translateSourceLocation(const SourceManager &SM, const LangOptions &LangOpts, + SourceLocation Loc) { + if (Loc.isInvalid()) + clang_getNullLocation(); + + CXSourceLocation Result = { { &SM, &LangOpts, }, + Loc.getRawEncoding() }; + return Result; +} + +/// Translate a Clang source location into a CIndex source location. +static inline CXSourceLocation translateSourceLocation(ASTContext &Context, + SourceLocation Loc) { + return translateSourceLocation(Context.getSourceManager(), + Context.getLangOpts(), + Loc); +} + +/// Translate a Clang source range into a CIndex source range. +/// +/// Clang internally represents ranges where the end location points to the +/// start of the token at the end. However, for external clients it is more +/// useful to have a CXSourceRange be a proper half-open interval. This routine +/// does the appropriate translation. +CXSourceRange translateSourceRange(const SourceManager &SM, + const LangOptions &LangOpts, + const CharSourceRange &R); + +/// Translate a Clang source range into a CIndex source range. +static inline CXSourceRange translateSourceRange(ASTContext &Context, + SourceRange R) { + return translateSourceRange(Context.getSourceManager(), + Context.getLangOpts(), + CharSourceRange::getTokenRange(R)); +} + +static inline SourceLocation translateSourceLocation(CXSourceLocation L) { + return SourceLocation::getFromRawEncoding(L.int_data); +} + +static inline SourceRange translateCXSourceRange(CXSourceRange R) { + return SourceRange(SourceLocation::getFromRawEncoding(R.begin_int_data), + SourceLocation::getFromRawEncoding(R.end_int_data)); +} + + +}} // end namespace: clang::cxloc + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CXStoredDiagnostic.cpp b/gnu/llvm/clang/tools/libclang/CXStoredDiagnostic.cpp new file mode 100644 index 00000000000..c4c24876e70 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXStoredDiagnostic.cpp @@ -0,0 +1,111 @@ +//===- CXStoredDiagnostic.cpp - Diagnostics C Interface -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements part of the diagnostic functions of the Clang C interface. +// +//===----------------------------------------------------------------------===// + +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CXTranslationUnit.h" +#include "CXSourceLocation.h" +#include "CXString.h" + +#include "clang/Basic/DiagnosticIDs.h" +#include "clang/Frontend/ASTUnit.h" +#include "llvm/ADT/Twine.h" + +using namespace clang; +using namespace clang::cxloc; + +CXDiagnosticSeverity CXStoredDiagnostic::getSeverity() const { + switch (Diag.getLevel()) { + case DiagnosticsEngine::Ignored: return CXDiagnostic_Ignored; + case DiagnosticsEngine::Note: return CXDiagnostic_Note; + case DiagnosticsEngine::Remark: + // The 'Remark' level isn't represented in the stable API. + case DiagnosticsEngine::Warning: return CXDiagnostic_Warning; + case DiagnosticsEngine::Error: return CXDiagnostic_Error; + case DiagnosticsEngine::Fatal: return CXDiagnostic_Fatal; + } + + llvm_unreachable("Invalid diagnostic level"); +} + +CXSourceLocation CXStoredDiagnostic::getLocation() const { + if (Diag.getLocation().isInvalid()) + return clang_getNullLocation(); + + return translateSourceLocation(Diag.getLocation().getManager(), + LangOpts, Diag.getLocation()); +} + +CXString CXStoredDiagnostic::getSpelling() const { + return cxstring::createRef(Diag.getMessage()); +} + +CXString CXStoredDiagnostic::getDiagnosticOption(CXString *Disable) const { + unsigned ID = Diag.getID(); + StringRef Option = DiagnosticIDs::getWarningOptionForDiag(ID); + if (!Option.empty()) { + if (Disable) + *Disable = cxstring::createDup((Twine("-Wno-") + Option).str()); + return cxstring::createDup((Twine("-W") + Option).str()); + } + + if (ID == diag::fatal_too_many_errors) { + if (Disable) + *Disable = cxstring::createRef("-ferror-limit=0"); + return cxstring::createRef("-ferror-limit="); + } + + return cxstring::createEmpty(); +} + +unsigned CXStoredDiagnostic::getCategory() const { + return DiagnosticIDs::getCategoryNumberForDiag(Diag.getID()); +} + +CXString CXStoredDiagnostic::getCategoryText() const { + unsigned catID = DiagnosticIDs::getCategoryNumberForDiag(Diag.getID()); + return cxstring::createRef(DiagnosticIDs::getCategoryNameFromID(catID)); +} + +unsigned CXStoredDiagnostic::getNumRanges() const { + if (Diag.getLocation().isInvalid()) + return 0; + + return Diag.range_size(); +} + +CXSourceRange CXStoredDiagnostic::getRange(unsigned int Range) const { + assert(Diag.getLocation().isValid()); + return translateSourceRange(Diag.getLocation().getManager(), + LangOpts, + Diag.range_begin()[Range]); +} + +unsigned CXStoredDiagnostic::getNumFixIts() const { + if (Diag.getLocation().isInvalid()) + return 0; + return Diag.fixit_size(); +} + +CXString CXStoredDiagnostic::getFixIt(unsigned FixIt, + CXSourceRange *ReplacementRange) const { + const FixItHint &Hint = Diag.fixit_begin()[FixIt]; + if (ReplacementRange) { + // Create a range that covers the entire replacement (or + // removal) range, adjusting the end of the range to point to + // the end of the token. + *ReplacementRange = translateSourceRange(Diag.getLocation().getManager(), + LangOpts, Hint.RemoveRange); + } + return cxstring::createDup(Hint.CodeToInsert); +} + diff --git a/gnu/llvm/clang/tools/libclang/CXString.cpp b/gnu/llvm/clang/tools/libclang/CXString.cpp new file mode 100644 index 00000000000..2754795f4a6 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXString.cpp @@ -0,0 +1,191 @@ +//===- CXString.cpp - Routines for manipulating CXStrings -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXStrings. It should be the +// only file that has internal knowledge of the encoding of the data in +// CXStrings. +// +//===----------------------------------------------------------------------===// + +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang-c/Index.h" +#include "clang/Frontend/ASTUnit.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; + +/// Describes the kind of underlying data in CXString. +enum CXStringFlag { + /// CXString contains a 'const char *' that it doesn't own. + CXS_Unmanaged, + + /// CXString contains a 'const char *' that it allocated with malloc(). + CXS_Malloc, + + /// CXString contains a CXStringBuf that needs to be returned to the + /// CXStringPool. + CXS_StringBuf +}; + +namespace clang { +namespace cxstring { + +//===----------------------------------------------------------------------===// +// Basic generation of CXStrings. +//===----------------------------------------------------------------------===// + +CXString createEmpty() { + CXString Str; + Str.data = ""; + Str.private_flags = CXS_Unmanaged; + return Str; +} + +CXString createNull() { + CXString Str; + Str.data = nullptr; + Str.private_flags = CXS_Unmanaged; + return Str; +} + +CXString createRef(const char *String) { + if (String && String[0] == '\0') + return createEmpty(); + + CXString Str; + Str.data = String; + Str.private_flags = CXS_Unmanaged; + return Str; +} + +CXString createDup(const char *String) { + if (!String) + return createNull(); + + if (String[0] == '\0') + return createEmpty(); + + CXString Str; + Str.data = strdup(String); + Str.private_flags = CXS_Malloc; + return Str; +} + +CXString createRef(StringRef String) { + // If the string is not nul-terminated, we have to make a copy. + + // FIXME: This is doing a one past end read, and should be removed! For memory + // we don't manage, the API string can become unterminated at any time outside + // our control. + + if (!String.empty() && String.data()[String.size()] != 0) + return createDup(String); + + CXString Result; + Result.data = String.data(); + Result.private_flags = (unsigned) CXS_Unmanaged; + return Result; +} + +CXString createDup(StringRef String) { + CXString Result; + char *Spelling = static_cast<char *>(llvm::safe_malloc(String.size() + 1)); + memmove(Spelling, String.data(), String.size()); + Spelling[String.size()] = 0; + Result.data = Spelling; + Result.private_flags = (unsigned) CXS_Malloc; + return Result; +} + +CXString createCXString(CXStringBuf *buf) { + CXString Str; + Str.data = buf; + Str.private_flags = (unsigned) CXS_StringBuf; + return Str; +} + +CXStringSet *createSet(const std::vector<std::string> &Strings) { + CXStringSet *Set = new CXStringSet; + Set->Count = Strings.size(); + Set->Strings = new CXString[Set->Count]; + for (unsigned SI = 0, SE = Set->Count; SI < SE; ++SI) + Set->Strings[SI] = createDup(Strings[SI]); + return Set; +} + + +//===----------------------------------------------------------------------===// +// String pools. +//===----------------------------------------------------------------------===// + +CXStringPool::~CXStringPool() { + for (std::vector<CXStringBuf *>::iterator I = Pool.begin(), E = Pool.end(); + I != E; ++I) { + delete *I; + } +} + +CXStringBuf *CXStringPool::getCXStringBuf(CXTranslationUnit TU) { + if (Pool.empty()) + return new CXStringBuf(TU); + + CXStringBuf *Buf = Pool.back(); + Buf->Data.clear(); + Pool.pop_back(); + return Buf; +} + +CXStringBuf *getCXStringBuf(CXTranslationUnit TU) { + return TU->StringPool->getCXStringBuf(TU); +} + +void CXStringBuf::dispose() { + TU->StringPool->Pool.push_back(this); +} + +bool isManagedByPool(CXString str) { + return ((CXStringFlag) str.private_flags) == CXS_StringBuf; +} + +} // end namespace cxstring +} // end namespace clang + +//===----------------------------------------------------------------------===// +// libClang public APIs. +//===----------------------------------------------------------------------===// + +const char *clang_getCString(CXString string) { + if (string.private_flags == (unsigned) CXS_StringBuf) { + return static_cast<const cxstring::CXStringBuf *>(string.data)->Data.data(); + } + return static_cast<const char *>(string.data); +} + +void clang_disposeString(CXString string) { + switch ((CXStringFlag) string.private_flags) { + case CXS_Unmanaged: + break; + case CXS_Malloc: + if (string.data) + free(const_cast<void *>(string.data)); + break; + case CXS_StringBuf: + static_cast<cxstring::CXStringBuf *>( + const_cast<void *>(string.data))->dispose(); + break; + } +} + +void clang_disposeStringSet(CXStringSet *set) { + for (unsigned SI = 0, SE = set->Count; SI < SE; ++SI) + clang_disposeString(set->Strings[SI]); + delete[] set->Strings; + delete set; +} + diff --git a/gnu/llvm/clang/tools/libclang/CXString.h b/gnu/llvm/clang/tools/libclang/CXString.h new file mode 100644 index 00000000000..809bdec3d67 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXString.h @@ -0,0 +1,108 @@ +//===- CXString.h - Routines for manipulating CXStrings -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXStrings. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXSTRING_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXSTRING_H + +#include "clang-c/Index.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Compiler.h" +#include <string> +#include <vector> + +namespace clang { +namespace cxstring { + +struct CXStringBuf; + +/// Create a CXString object for an empty "" string. +CXString createEmpty(); + +/// Create a CXString object for an NULL string. +/// +/// A NULL string should be used as an "invalid" value in case of errors. +CXString createNull(); + +/// Create a CXString object from a nul-terminated C string. New +/// CXString may contain a pointer to \p String. +/// +/// \p String should not be changed by the caller afterwards. +CXString createRef(const char *String); + +/// Create a CXString object from a nul-terminated C string. New +/// CXString will contain a copy of \p String. +/// +/// \p String can be changed or freed by the caller. +CXString createDup(const char *String); + +/// Create a CXString object from a StringRef. New CXString may +/// contain a pointer to the undrelying data of \p String. +/// +/// \p String should not be changed by the caller afterwards. +CXString createRef(StringRef String); + +/// Create a CXString object from a StringRef. New CXString will +/// contain a copy of \p String. +/// +/// \p String can be changed or freed by the caller. +CXString createDup(StringRef String); + +// Usually std::string is intended to be used as backing storage for CXString. +// In this case, call \c createRef(String.c_str()). +// +// If you need to make a copy, call \c createDup(StringRef(String)). +CXString createRef(std::string String) = delete; + +/// Create a CXString object that is backed by a string buffer. +CXString createCXString(CXStringBuf *buf); + +CXStringSet *createSet(const std::vector<std::string> &Strings); + +/// A string pool used for fast allocation/deallocation of strings. +class CXStringPool { +public: + ~CXStringPool(); + + CXStringBuf *getCXStringBuf(CXTranslationUnit TU); + +private: + std::vector<CXStringBuf *> Pool; + + friend struct CXStringBuf; +}; + +struct CXStringBuf { + SmallString<128> Data; + CXTranslationUnit TU; + + CXStringBuf(CXTranslationUnit TU) : TU(TU) {} + + /// Return this buffer to the pool. + void dispose(); +}; + +CXStringBuf *getCXStringBuf(CXTranslationUnit TU); + +/// Returns true if the CXString data is managed by a pool. +bool isManagedByPool(CXString str); + +} + +static inline StringRef getContents(const CXUnsavedFile &UF) { + return StringRef(UF.Contents, UF.Length); +} +} + +#endif + diff --git a/gnu/llvm/clang/tools/libclang/CXTranslationUnit.h b/gnu/llvm/clang/tools/libclang/CXTranslationUnit.h new file mode 100644 index 00000000000..3424bf2997f --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXTranslationUnit.h @@ -0,0 +1,89 @@ +//===- CXTranslationUnit.h - Routines for manipulating CXTranslationUnits -===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXTranslationUnits. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXTRANSLATIONUNIT_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXTRANSLATIONUNIT_H + +#include "CLog.h" +#include "CXString.h" +#include "clang-c/Index.h" + +namespace clang { + class ASTUnit; + class CIndexer; +namespace index { +class CommentToXMLConverter; +} // namespace index +} // namespace clang + +struct CXTranslationUnitImpl { + clang::CIndexer *CIdx; + clang::ASTUnit *TheASTUnit; + clang::cxstring::CXStringPool *StringPool; + void *Diagnostics; + void *OverridenCursorsPool; + clang::index::CommentToXMLConverter *CommentToXML; + unsigned ParsingOptions; + std::vector<std::string> Arguments; +}; + +struct CXTargetInfoImpl { + CXTranslationUnit TranslationUnit; +}; + +namespace clang { +namespace cxtu { + +CXTranslationUnitImpl *MakeCXTranslationUnit(CIndexer *CIdx, + std::unique_ptr<ASTUnit> AU); + +static inline ASTUnit *getASTUnit(CXTranslationUnit TU) { + if (!TU) + return nullptr; + return TU->TheASTUnit; +} + +/// \returns true if the ASTUnit has a diagnostic about the AST file being +/// corrupted. +bool isASTReadError(ASTUnit *AU); + +static inline bool isNotUsableTU(CXTranslationUnit TU) { + return !TU; +} + +#define LOG_BAD_TU(TU) \ + do { \ + LOG_FUNC_SECTION { \ + *Log << "called with a bad TU: " << TU; \ + } \ + } while(false) + +class CXTUOwner { + CXTranslationUnitImpl *TU; + +public: + CXTUOwner(CXTranslationUnitImpl *tu) : TU(tu) { } + ~CXTUOwner(); + + CXTranslationUnitImpl *getTU() const { return TU; } + + CXTranslationUnitImpl *takeTU() { + CXTranslationUnitImpl *retTU = TU; + TU = nullptr; + return retTU; + } +}; + + +}} // end namespace clang::cxtu + +#endif diff --git a/gnu/llvm/clang/tools/libclang/CXType.cpp b/gnu/llvm/clang/tools/libclang/CXType.cpp new file mode 100644 index 00000000000..acecf87d0cd --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXType.cpp @@ -0,0 +1,1320 @@ +//===- CXType.cpp - Implements 'CXTypes' aspect of libclang ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--------------------------------------------------------------------===// +// +// This file implements the 'CXTypes' API hooks in the Clang-C library. +// +//===--------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "CXType.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/AddressSpaces.h" +#include "clang/Frontend/ASTUnit.h" + +using namespace clang; + +static CXTypeKind GetBuiltinTypeKind(const BuiltinType *BT) { +#define BTCASE(K) case BuiltinType::K: return CXType_##K + switch (BT->getKind()) { + BTCASE(Void); + BTCASE(Bool); + BTCASE(Char_U); + BTCASE(UChar); + BTCASE(Char16); + BTCASE(Char32); + BTCASE(UShort); + BTCASE(UInt); + BTCASE(ULong); + BTCASE(ULongLong); + BTCASE(UInt128); + BTCASE(Char_S); + BTCASE(SChar); + case BuiltinType::WChar_S: return CXType_WChar; + case BuiltinType::WChar_U: return CXType_WChar; + BTCASE(Short); + BTCASE(Int); + BTCASE(Long); + BTCASE(LongLong); + BTCASE(Int128); + BTCASE(Half); + BTCASE(Float); + BTCASE(Double); + BTCASE(LongDouble); + BTCASE(ShortAccum); + BTCASE(Accum); + BTCASE(LongAccum); + BTCASE(UShortAccum); + BTCASE(UAccum); + BTCASE(ULongAccum); + BTCASE(Float16); + BTCASE(Float128); + BTCASE(NullPtr); + BTCASE(Overload); + BTCASE(Dependent); + BTCASE(ObjCId); + BTCASE(ObjCClass); + BTCASE(ObjCSel); +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) BTCASE(Id); +#include "clang/Basic/OpenCLImageTypes.def" +#undef IMAGE_TYPE +#define EXT_OPAQUE_TYPE(ExtType, Id, Ext) BTCASE(Id); +#include "clang/Basic/OpenCLExtensionTypes.def" + BTCASE(OCLSampler); + BTCASE(OCLEvent); + BTCASE(OCLQueue); + BTCASE(OCLReserveID); + default: + return CXType_Unexposed; + } +#undef BTCASE +} + +static CXTypeKind GetTypeKind(QualType T) { + const Type *TP = T.getTypePtrOrNull(); + if (!TP) + return CXType_Invalid; + +#define TKCASE(K) case Type::K: return CXType_##K + switch (TP->getTypeClass()) { + case Type::Builtin: + return GetBuiltinTypeKind(cast<BuiltinType>(TP)); + TKCASE(Complex); + TKCASE(Pointer); + TKCASE(BlockPointer); + TKCASE(LValueReference); + TKCASE(RValueReference); + TKCASE(Record); + TKCASE(Enum); + TKCASE(Typedef); + TKCASE(ObjCInterface); + TKCASE(ObjCObject); + TKCASE(ObjCObjectPointer); + TKCASE(ObjCTypeParam); + TKCASE(FunctionNoProto); + TKCASE(FunctionProto); + TKCASE(ConstantArray); + TKCASE(IncompleteArray); + TKCASE(VariableArray); + TKCASE(DependentSizedArray); + TKCASE(Vector); + TKCASE(ExtVector); + TKCASE(MemberPointer); + TKCASE(Auto); + TKCASE(Elaborated); + TKCASE(Pipe); + TKCASE(Attributed); + default: + return CXType_Unexposed; + } +#undef TKCASE +} + + +CXType cxtype::MakeCXType(QualType T, CXTranslationUnit TU) { + CXTypeKind TK = CXType_Invalid; + + if (TU && !T.isNull()) { + // Handle attributed types as the original type + if (auto *ATT = T->getAs<AttributedType>()) { + if (!(TU->ParsingOptions & CXTranslationUnit_IncludeAttributedTypes)) { + // Return the equivalent type which represents the canonically + // equivalent type. + return MakeCXType(ATT->getEquivalentType(), TU); + } + } + // Handle paren types as the original type + if (auto *PTT = T->getAs<ParenType>()) { + return MakeCXType(PTT->getInnerType(), TU); + } + + ASTContext &Ctx = cxtu::getASTUnit(TU)->getASTContext(); + if (Ctx.getLangOpts().ObjC) { + QualType UnqualT = T.getUnqualifiedType(); + if (Ctx.isObjCIdType(UnqualT)) + TK = CXType_ObjCId; + else if (Ctx.isObjCClassType(UnqualT)) + TK = CXType_ObjCClass; + else if (Ctx.isObjCSelType(UnqualT)) + TK = CXType_ObjCSel; + } + + /* Handle decayed types as the original type */ + if (const DecayedType *DT = T->getAs<DecayedType>()) { + return MakeCXType(DT->getOriginalType(), TU); + } + } + if (TK == CXType_Invalid) + TK = GetTypeKind(T); + + CXType CT = { TK, { TK == CXType_Invalid ? nullptr + : T.getAsOpaquePtr(), TU } }; + return CT; +} + +using cxtype::MakeCXType; + +static inline QualType GetQualType(CXType CT) { + return QualType::getFromOpaquePtr(CT.data[0]); +} + +static inline CXTranslationUnit GetTU(CXType CT) { + return static_cast<CXTranslationUnit>(CT.data[1]); +} + +static Optional<ArrayRef<TemplateArgument>> +GetTemplateArguments(QualType Type) { + assert(!Type.isNull()); + if (const auto *Specialization = Type->getAs<TemplateSpecializationType>()) + return Specialization->template_arguments(); + + if (const auto *RecordDecl = Type->getAsCXXRecordDecl()) { + const auto *TemplateDecl = + dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl); + if (TemplateDecl) + return TemplateDecl->getTemplateArgs().asArray(); + } + + return None; +} + +static Optional<QualType> TemplateArgumentToQualType(const TemplateArgument &A) { + if (A.getKind() == TemplateArgument::Type) + return A.getAsType(); + return None; +} + +static Optional<QualType> +FindTemplateArgumentTypeAt(ArrayRef<TemplateArgument> TA, unsigned index) { + unsigned current = 0; + for (const auto &A : TA) { + if (A.getKind() == TemplateArgument::Pack) { + if (index < current + A.pack_size()) + return TemplateArgumentToQualType(A.getPackAsArray()[index - current]); + current += A.pack_size(); + continue; + } + if (current == index) + return TemplateArgumentToQualType(A); + current++; + } + return None; +} + +CXType clang_getCursorType(CXCursor C) { + using namespace cxcursor; + + CXTranslationUnit TU = cxcursor::getCursorTU(C); + if (!TU) + return MakeCXType(QualType(), TU); + + ASTContext &Context = cxtu::getASTUnit(TU)->getASTContext(); + if (clang_isExpression(C.kind)) { + QualType T = cxcursor::getCursorExpr(C)->getType(); + return MakeCXType(T, TU); + } + + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (!D) + return MakeCXType(QualType(), TU); + + if (const TypeDecl *TD = dyn_cast<TypeDecl>(D)) + return MakeCXType(Context.getTypeDeclType(TD), TU); + if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) + return MakeCXType(Context.getObjCInterfaceType(ID), TU); + if (const DeclaratorDecl *DD = dyn_cast<DeclaratorDecl>(D)) + return MakeCXType(DD->getType(), TU); + if (const ValueDecl *VD = dyn_cast<ValueDecl>(D)) + return MakeCXType(VD->getType(), TU); + if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) + return MakeCXType(PD->getType(), TU); + if (const FunctionTemplateDecl *FTD = dyn_cast<FunctionTemplateDecl>(D)) + return MakeCXType(FTD->getTemplatedDecl()->getType(), TU); + return MakeCXType(QualType(), TU); + } + + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + QualType T + = Context.getObjCInterfaceType(getCursorObjCSuperClassRef(C).first); + return MakeCXType(T, TU); + } + + case CXCursor_ObjCClassRef: { + QualType T = Context.getObjCInterfaceType(getCursorObjCClassRef(C).first); + return MakeCXType(T, TU); + } + + case CXCursor_TypeRef: { + QualType T = Context.getTypeDeclType(getCursorTypeRef(C).first); + return MakeCXType(T, TU); + + } + + case CXCursor_CXXBaseSpecifier: + return cxtype::MakeCXType(getCursorCXXBaseSpecifier(C)->getType(), TU); + + case CXCursor_MemberRef: + return cxtype::MakeCXType(getCursorMemberRef(C).first->getType(), TU); + + case CXCursor_VariableRef: + return cxtype::MakeCXType(getCursorVariableRef(C).first->getType(), TU); + + case CXCursor_ObjCProtocolRef: + case CXCursor_TemplateRef: + case CXCursor_NamespaceRef: + case CXCursor_OverloadedDeclRef: + default: + break; + } + + return MakeCXType(QualType(), TU); + } + + return MakeCXType(QualType(), TU); +} + +CXString clang_getTypeSpelling(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return cxstring::createEmpty(); + + CXTranslationUnit TU = GetTU(CT); + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + PrintingPolicy PP(cxtu::getASTUnit(TU)->getASTContext().getLangOpts()); + + T.print(OS, PP); + + return cxstring::createDup(OS.str()); +} + +CXType clang_getTypedefDeclUnderlyingType(CXCursor C) { + using namespace cxcursor; + CXTranslationUnit TU = cxcursor::getCursorTU(C); + + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + + if (const TypedefNameDecl *TD = dyn_cast_or_null<TypedefNameDecl>(D)) { + QualType T = TD->getUnderlyingType(); + return MakeCXType(T, TU); + } + + return MakeCXType(QualType(), TU); + } + + return MakeCXType(QualType(), TU); +} + +CXType clang_getEnumDeclIntegerType(CXCursor C) { + using namespace cxcursor; + CXTranslationUnit TU = cxcursor::getCursorTU(C); + + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + + if (const EnumDecl *TD = dyn_cast_or_null<EnumDecl>(D)) { + QualType T = TD->getIntegerType(); + return MakeCXType(T, TU); + } + + return MakeCXType(QualType(), TU); + } + + return MakeCXType(QualType(), TU); +} + +long long clang_getEnumConstantDeclValue(CXCursor C) { + using namespace cxcursor; + + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + + if (const EnumConstantDecl *TD = dyn_cast_or_null<EnumConstantDecl>(D)) { + return TD->getInitVal().getSExtValue(); + } + + return LLONG_MIN; + } + + return LLONG_MIN; +} + +unsigned long long clang_getEnumConstantDeclUnsignedValue(CXCursor C) { + using namespace cxcursor; + + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + + if (const EnumConstantDecl *TD = dyn_cast_or_null<EnumConstantDecl>(D)) { + return TD->getInitVal().getZExtValue(); + } + + return ULLONG_MAX; + } + + return ULLONG_MAX; +} + +int clang_getFieldDeclBitWidth(CXCursor C) { + using namespace cxcursor; + + if (clang_isDeclaration(C.kind)) { + const Decl *D = getCursorDecl(C); + + if (const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(D)) { + if (FD->isBitField()) + return FD->getBitWidthValue(getCursorContext(C)); + } + } + + return -1; +} + +CXType clang_getCanonicalType(CXType CT) { + if (CT.kind == CXType_Invalid) + return CT; + + QualType T = GetQualType(CT); + CXTranslationUnit TU = GetTU(CT); + + if (T.isNull()) + return MakeCXType(QualType(), GetTU(CT)); + + return MakeCXType(cxtu::getASTUnit(TU)->getASTContext() + .getCanonicalType(T), + TU); +} + +unsigned clang_isConstQualifiedType(CXType CT) { + QualType T = GetQualType(CT); + return T.isLocalConstQualified(); +} + +unsigned clang_isVolatileQualifiedType(CXType CT) { + QualType T = GetQualType(CT); + return T.isLocalVolatileQualified(); +} + +unsigned clang_isRestrictQualifiedType(CXType CT) { + QualType T = GetQualType(CT); + return T.isLocalRestrictQualified(); +} + +unsigned clang_getAddressSpace(CXType CT) { + QualType T = GetQualType(CT); + + // For non language-specific address space, use separate helper function. + if (T.getAddressSpace() >= LangAS::FirstTargetAddressSpace) { + return T.getQualifiers().getAddressSpaceAttributePrintValue(); + } + // FIXME: this function returns either a LangAS or a target AS + // Those values can overlap which makes this function rather unpredictable + // for any caller + return (unsigned)T.getAddressSpace(); +} + +CXString clang_getTypedefName(CXType CT) { + QualType T = GetQualType(CT); + const TypedefType *TT = T->getAs<TypedefType>(); + if (TT) { + TypedefNameDecl *TD = TT->getDecl(); + if (TD) + return cxstring::createDup(TD->getNameAsString().c_str()); + } + return cxstring::createEmpty(); +} + +CXType clang_getPointeeType(CXType CT) { + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (!TP) + return MakeCXType(QualType(), GetTU(CT)); + +try_again: + switch (TP->getTypeClass()) { + case Type::Pointer: + T = cast<PointerType>(TP)->getPointeeType(); + break; + case Type::BlockPointer: + T = cast<BlockPointerType>(TP)->getPointeeType(); + break; + case Type::LValueReference: + case Type::RValueReference: + T = cast<ReferenceType>(TP)->getPointeeType(); + break; + case Type::ObjCObjectPointer: + T = cast<ObjCObjectPointerType>(TP)->getPointeeType(); + break; + case Type::MemberPointer: + T = cast<MemberPointerType>(TP)->getPointeeType(); + break; + case Type::Auto: + case Type::DeducedTemplateSpecialization: + TP = cast<DeducedType>(TP)->getDeducedType().getTypePtrOrNull(); + if (TP) + goto try_again; + break; + default: + T = QualType(); + break; + } + return MakeCXType(T, GetTU(CT)); +} + +CXCursor clang_getTypeDeclaration(CXType CT) { + if (CT.kind == CXType_Invalid) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (!TP) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + Decl *D = nullptr; + +try_again: + switch (TP->getTypeClass()) { + case Type::Typedef: + D = cast<TypedefType>(TP)->getDecl(); + break; + case Type::ObjCObject: + D = cast<ObjCObjectType>(TP)->getInterface(); + break; + case Type::ObjCInterface: + D = cast<ObjCInterfaceType>(TP)->getDecl(); + break; + case Type::Record: + case Type::Enum: + D = cast<TagType>(TP)->getDecl(); + break; + case Type::TemplateSpecialization: + if (const RecordType *Record = TP->getAs<RecordType>()) + D = Record->getDecl(); + else + D = cast<TemplateSpecializationType>(TP)->getTemplateName() + .getAsTemplateDecl(); + break; + + case Type::Auto: + case Type::DeducedTemplateSpecialization: + TP = cast<DeducedType>(TP)->getDeducedType().getTypePtrOrNull(); + if (TP) + goto try_again; + break; + + case Type::InjectedClassName: + D = cast<InjectedClassNameType>(TP)->getDecl(); + break; + + // FIXME: Template type parameters! + + case Type::Elaborated: + TP = cast<ElaboratedType>(TP)->getNamedType().getTypePtrOrNull(); + goto try_again; + + default: + break; + } + + if (!D) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + return cxcursor::MakeCXCursor(D, GetTU(CT)); +} + +CXString clang_getTypeKindSpelling(enum CXTypeKind K) { + const char *s = nullptr; +#define TKIND(X) case CXType_##X: s = "" #X ""; break + switch (K) { + TKIND(Invalid); + TKIND(Unexposed); + TKIND(Void); + TKIND(Bool); + TKIND(Char_U); + TKIND(UChar); + TKIND(Char16); + TKIND(Char32); + TKIND(UShort); + TKIND(UInt); + TKIND(ULong); + TKIND(ULongLong); + TKIND(UInt128); + TKIND(Char_S); + TKIND(SChar); + case CXType_WChar: s = "WChar"; break; + TKIND(Short); + TKIND(Int); + TKIND(Long); + TKIND(LongLong); + TKIND(Int128); + TKIND(Half); + TKIND(Float); + TKIND(Double); + TKIND(LongDouble); + TKIND(ShortAccum); + TKIND(Accum); + TKIND(LongAccum); + TKIND(UShortAccum); + TKIND(UAccum); + TKIND(ULongAccum); + TKIND(Float16); + TKIND(Float128); + TKIND(NullPtr); + TKIND(Overload); + TKIND(Dependent); + TKIND(ObjCId); + TKIND(ObjCClass); + TKIND(ObjCSel); + TKIND(Complex); + TKIND(Pointer); + TKIND(BlockPointer); + TKIND(LValueReference); + TKIND(RValueReference); + TKIND(Record); + TKIND(Enum); + TKIND(Typedef); + TKIND(ObjCInterface); + TKIND(ObjCObject); + TKIND(ObjCObjectPointer); + TKIND(ObjCTypeParam); + TKIND(FunctionNoProto); + TKIND(FunctionProto); + TKIND(ConstantArray); + TKIND(IncompleteArray); + TKIND(VariableArray); + TKIND(DependentSizedArray); + TKIND(Vector); + TKIND(ExtVector); + TKIND(MemberPointer); + TKIND(Auto); + TKIND(Elaborated); + TKIND(Pipe); + TKIND(Attributed); +#define IMAGE_TYPE(ImgType, Id, SingletonId, Access, Suffix) TKIND(Id); +#include "clang/Basic/OpenCLImageTypes.def" +#undef IMAGE_TYPE +#define EXT_OPAQUE_TYPE(ExtTYpe, Id, Ext) TKIND(Id); +#include "clang/Basic/OpenCLExtensionTypes.def" + TKIND(OCLSampler); + TKIND(OCLEvent); + TKIND(OCLQueue); + TKIND(OCLReserveID); + } +#undef TKIND + return cxstring::createRef(s); +} + +unsigned clang_equalTypes(CXType A, CXType B) { + return A.data[0] == B.data[0] && A.data[1] == B.data[1]; +} + +unsigned clang_isFunctionTypeVariadic(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return 0; + + if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) + return (unsigned)FD->isVariadic(); + + if (T->getAs<FunctionNoProtoType>()) + return 1; + + return 0; +} + +CXCallingConv clang_getFunctionTypeCallingConv(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return CXCallingConv_Invalid; + + if (const FunctionType *FD = T->getAs<FunctionType>()) { +#define TCALLINGCONV(X) case CC_##X: return CXCallingConv_##X + switch (FD->getCallConv()) { + TCALLINGCONV(C); + TCALLINGCONV(X86StdCall); + TCALLINGCONV(X86FastCall); + TCALLINGCONV(X86ThisCall); + TCALLINGCONV(X86Pascal); + TCALLINGCONV(X86RegCall); + TCALLINGCONV(X86VectorCall); + TCALLINGCONV(AArch64VectorCall); + TCALLINGCONV(Win64); + TCALLINGCONV(X86_64SysV); + TCALLINGCONV(AAPCS); + TCALLINGCONV(AAPCS_VFP); + TCALLINGCONV(IntelOclBicc); + TCALLINGCONV(Swift); + TCALLINGCONV(PreserveMost); + TCALLINGCONV(PreserveAll); + case CC_SpirFunction: return CXCallingConv_Unexposed; + case CC_OpenCLKernel: return CXCallingConv_Unexposed; + break; + } +#undef TCALLINGCONV + } + + return CXCallingConv_Invalid; +} + +int clang_getNumArgTypes(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return -1; + + if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) { + return FD->getNumParams(); + } + + if (T->getAs<FunctionNoProtoType>()) { + return 0; + } + + return -1; +} + +CXType clang_getArgType(CXType X, unsigned i) { + QualType T = GetQualType(X); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(X)); + + if (const FunctionProtoType *FD = T->getAs<FunctionProtoType>()) { + unsigned numParams = FD->getNumParams(); + if (i >= numParams) + return MakeCXType(QualType(), GetTU(X)); + + return MakeCXType(FD->getParamType(i), GetTU(X)); + } + + return MakeCXType(QualType(), GetTU(X)); +} + +CXType clang_getResultType(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(X)); + + if (const FunctionType *FD = T->getAs<FunctionType>()) + return MakeCXType(FD->getReturnType(), GetTU(X)); + + return MakeCXType(QualType(), GetTU(X)); +} + +CXType clang_getCursorResultType(CXCursor C) { + if (clang_isDeclaration(C.kind)) { + const Decl *D = cxcursor::getCursorDecl(C); + if (const ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(D)) + return MakeCXType(MD->getReturnType(), cxcursor::getCursorTU(C)); + + return clang_getResultType(clang_getCursorType(C)); + } + + return MakeCXType(QualType(), cxcursor::getCursorTU(C)); +} + +// FIXME: We should expose the canThrow(...) result instead of the EST. +static CXCursor_ExceptionSpecificationKind +getExternalExceptionSpecificationKind(ExceptionSpecificationType EST) { + switch (EST) { + case EST_None: + return CXCursor_ExceptionSpecificationKind_None; + case EST_DynamicNone: + return CXCursor_ExceptionSpecificationKind_DynamicNone; + case EST_Dynamic: + return CXCursor_ExceptionSpecificationKind_Dynamic; + case EST_MSAny: + return CXCursor_ExceptionSpecificationKind_MSAny; + case EST_BasicNoexcept: + return CXCursor_ExceptionSpecificationKind_BasicNoexcept; + case EST_NoThrow: + return CXCursor_ExceptionSpecificationKind_NoThrow; + case EST_NoexceptFalse: + case EST_NoexceptTrue: + case EST_DependentNoexcept: + return CXCursor_ExceptionSpecificationKind_ComputedNoexcept; + case EST_Unevaluated: + return CXCursor_ExceptionSpecificationKind_Unevaluated; + case EST_Uninstantiated: + return CXCursor_ExceptionSpecificationKind_Uninstantiated; + case EST_Unparsed: + return CXCursor_ExceptionSpecificationKind_Unparsed; + } + llvm_unreachable("invalid EST value"); +} + +int clang_getExceptionSpecificationType(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return -1; + + if (const auto *FD = T->getAs<FunctionProtoType>()) + return getExternalExceptionSpecificationKind(FD->getExceptionSpecType()); + + return -1; +} + +int clang_getCursorExceptionSpecificationType(CXCursor C) { + if (clang_isDeclaration(C.kind)) + return clang_getExceptionSpecificationType(clang_getCursorType(C)); + + return -1; +} + +unsigned clang_isPODType(CXType X) { + QualType T = GetQualType(X); + if (T.isNull()) + return 0; + + CXTranslationUnit TU = GetTU(X); + + return T.isPODType(cxtu::getASTUnit(TU)->getASTContext()) ? 1 : 0; +} + +CXType clang_getElementType(CXType CT) { + QualType ET = QualType(); + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + ET = cast<ConstantArrayType> (TP)->getElementType(); + break; + case Type::IncompleteArray: + ET = cast<IncompleteArrayType> (TP)->getElementType(); + break; + case Type::VariableArray: + ET = cast<VariableArrayType> (TP)->getElementType(); + break; + case Type::DependentSizedArray: + ET = cast<DependentSizedArrayType> (TP)->getElementType(); + break; + case Type::Vector: + ET = cast<VectorType> (TP)->getElementType(); + break; + case Type::ExtVector: + ET = cast<ExtVectorType>(TP)->getElementType(); + break; + case Type::Complex: + ET = cast<ComplexType> (TP)->getElementType(); + break; + default: + break; + } + } + return MakeCXType(ET, GetTU(CT)); +} + +long long clang_getNumElements(CXType CT) { + long long result = -1; + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + result = cast<ConstantArrayType> (TP)->getSize().getSExtValue(); + break; + case Type::Vector: + result = cast<VectorType> (TP)->getNumElements(); + break; + case Type::ExtVector: + result = cast<ExtVectorType>(TP)->getNumElements(); + break; + default: + break; + } + } + return result; +} + +CXType clang_getArrayElementType(CXType CT) { + QualType ET = QualType(); + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + ET = cast<ConstantArrayType> (TP)->getElementType(); + break; + case Type::IncompleteArray: + ET = cast<IncompleteArrayType> (TP)->getElementType(); + break; + case Type::VariableArray: + ET = cast<VariableArrayType> (TP)->getElementType(); + break; + case Type::DependentSizedArray: + ET = cast<DependentSizedArrayType> (TP)->getElementType(); + break; + default: + break; + } + } + return MakeCXType(ET, GetTU(CT)); +} + +long long clang_getArraySize(CXType CT) { + long long result = -1; + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP) { + switch (TP->getTypeClass()) { + case Type::ConstantArray: + result = cast<ConstantArrayType> (TP)->getSize().getSExtValue(); + break; + default: + break; + } + } + return result; +} + +static bool isIncompleteTypeWithAlignment(QualType QT) { + return QT->isIncompleteArrayType() || !QT->isIncompleteType(); +} + +long long clang_Type_getAlignOf(CXType T) { + if (T.kind == CXType_Invalid) + return CXTypeLayoutError_Invalid; + ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext(); + QualType QT = GetQualType(T); + // [expr.alignof] p1: return size_t value for complete object type, reference + // or array. + // [expr.alignof] p3: if reference type, return size of referenced type + if (QT->isReferenceType()) + QT = QT.getNonReferenceType(); + if (!isIncompleteTypeWithAlignment(QT)) + return CXTypeLayoutError_Incomplete; + if (QT->isDependentType()) + return CXTypeLayoutError_Dependent; + if (const auto *Deduced = dyn_cast<DeducedType>(QT)) + if (Deduced->getDeducedType().isNull()) + return CXTypeLayoutError_Undeduced; + // Exceptions by GCC extension - see ASTContext.cpp:1313 getTypeInfoImpl + // if (QT->isFunctionType()) return 4; // Bug #15511 - should be 1 + // if (QT->isVoidType()) return 1; + return Ctx.getTypeAlignInChars(QT).getQuantity(); +} + +CXType clang_Type_getClassType(CXType CT) { + QualType ET = QualType(); + QualType T = GetQualType(CT); + const Type *TP = T.getTypePtrOrNull(); + + if (TP && TP->getTypeClass() == Type::MemberPointer) { + ET = QualType(cast<MemberPointerType> (TP)->getClass(), 0); + } + return MakeCXType(ET, GetTU(CT)); +} + +long long clang_Type_getSizeOf(CXType T) { + if (T.kind == CXType_Invalid) + return CXTypeLayoutError_Invalid; + ASTContext &Ctx = cxtu::getASTUnit(GetTU(T))->getASTContext(); + QualType QT = GetQualType(T); + // [expr.sizeof] p2: if reference type, return size of referenced type + if (QT->isReferenceType()) + QT = QT.getNonReferenceType(); + // [expr.sizeof] p1: return -1 on: func, incomplete, bitfield, incomplete + // enumeration + // Note: We get the cxtype, not the cxcursor, so we can't call + // FieldDecl->isBitField() + // [expr.sizeof] p3: pointer ok, function not ok. + // [gcc extension] lib/AST/ExprConstant.cpp:1372 HandleSizeof : vla == error + if (QT->isIncompleteType()) + return CXTypeLayoutError_Incomplete; + if (QT->isDependentType()) + return CXTypeLayoutError_Dependent; + if (!QT->isConstantSizeType()) + return CXTypeLayoutError_NotConstantSize; + if (const auto *Deduced = dyn_cast<DeducedType>(QT)) + if (Deduced->getDeducedType().isNull()) + return CXTypeLayoutError_Undeduced; + // [gcc extension] lib/AST/ExprConstant.cpp:1372 + // HandleSizeof : {voidtype,functype} == 1 + // not handled by ASTContext.cpp:1313 getTypeInfoImpl + if (QT->isVoidType() || QT->isFunctionType()) + return 1; + return Ctx.getTypeSizeInChars(QT).getQuantity(); +} + +static bool isTypeIncompleteForLayout(QualType QT) { + return QT->isIncompleteType() && !QT->isIncompleteArrayType(); +} + +static long long visitRecordForValidation(const RecordDecl *RD) { + for (const auto *I : RD->fields()){ + QualType FQT = I->getType(); + if (isTypeIncompleteForLayout(FQT)) + return CXTypeLayoutError_Incomplete; + if (FQT->isDependentType()) + return CXTypeLayoutError_Dependent; + // recurse + if (const RecordType *ChildType = I->getType()->getAs<RecordType>()) { + if (const RecordDecl *Child = ChildType->getDecl()) { + long long ret = visitRecordForValidation(Child); + if (ret < 0) + return ret; + } + } + // else try next field + } + return 0; +} + +static long long validateFieldParentType(CXCursor PC, CXType PT){ + if (clang_isInvalid(PC.kind)) + return CXTypeLayoutError_Invalid; + const RecordDecl *RD = + dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC)); + // validate parent declaration + if (!RD || RD->isInvalidDecl()) + return CXTypeLayoutError_Invalid; + RD = RD->getDefinition(); + if (!RD) + return CXTypeLayoutError_Incomplete; + if (RD->isInvalidDecl()) + return CXTypeLayoutError_Invalid; + // validate parent type + QualType RT = GetQualType(PT); + if (RT->isIncompleteType()) + return CXTypeLayoutError_Incomplete; + if (RT->isDependentType()) + return CXTypeLayoutError_Dependent; + // We recurse into all record fields to detect incomplete and dependent types. + long long Error = visitRecordForValidation(RD); + if (Error < 0) + return Error; + return 0; +} + +long long clang_Type_getOffsetOf(CXType PT, const char *S) { + // check that PT is not incomplete/dependent + CXCursor PC = clang_getTypeDeclaration(PT); + long long Error = validateFieldParentType(PC,PT); + if (Error < 0) + return Error; + if (!S) + return CXTypeLayoutError_InvalidFieldName; + // lookup field + ASTContext &Ctx = cxtu::getASTUnit(GetTU(PT))->getASTContext(); + IdentifierInfo *II = &Ctx.Idents.get(S); + DeclarationName FieldName(II); + const RecordDecl *RD = + dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC)); + // verified in validateFieldParentType + RD = RD->getDefinition(); + RecordDecl::lookup_result Res = RD->lookup(FieldName); + // If a field of the parent record is incomplete, lookup will fail. + // and we would return InvalidFieldName instead of Incomplete. + // But this erroneous results does protects again a hidden assertion failure + // in the RecordLayoutBuilder + if (Res.size() != 1) + return CXTypeLayoutError_InvalidFieldName; + if (const FieldDecl *FD = dyn_cast<FieldDecl>(Res.front())) + return Ctx.getFieldOffset(FD); + if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(Res.front())) + return Ctx.getFieldOffset(IFD); + // we don't want any other Decl Type. + return CXTypeLayoutError_InvalidFieldName; +} + +CXType clang_Type_getModifiedType(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(CT)); + + if (auto *ATT = T->getAs<AttributedType>()) + return MakeCXType(ATT->getModifiedType(), GetTU(CT)); + + return MakeCXType(QualType(), GetTU(CT)); +} + +long long clang_Cursor_getOffsetOfField(CXCursor C) { + if (clang_isDeclaration(C.kind)) { + // we need to validate the parent type + CXCursor PC = clang_getCursorSemanticParent(C); + CXType PT = clang_getCursorType(PC); + long long Error = validateFieldParentType(PC,PT); + if (Error < 0) + return Error; + // proceed with the offset calculation + const Decl *D = cxcursor::getCursorDecl(C); + ASTContext &Ctx = cxcursor::getCursorContext(C); + if (const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(D)) + return Ctx.getFieldOffset(FD); + if (const IndirectFieldDecl *IFD = dyn_cast_or_null<IndirectFieldDecl>(D)) + return Ctx.getFieldOffset(IFD); + } + return -1; +} + +enum CXRefQualifierKind clang_Type_getCXXRefQualifier(CXType T) { + QualType QT = GetQualType(T); + if (QT.isNull()) + return CXRefQualifier_None; + const FunctionProtoType *FD = QT->getAs<FunctionProtoType>(); + if (!FD) + return CXRefQualifier_None; + switch (FD->getRefQualifier()) { + case RQ_None: + return CXRefQualifier_None; + case RQ_LValue: + return CXRefQualifier_LValue; + case RQ_RValue: + return CXRefQualifier_RValue; + } + return CXRefQualifier_None; +} + +unsigned clang_Cursor_isBitField(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + const FieldDecl *FD = dyn_cast_or_null<FieldDecl>(cxcursor::getCursorDecl(C)); + if (!FD) + return 0; + return FD->isBitField(); +} + +CXString clang_getDeclObjCTypeEncoding(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return cxstring::createEmpty(); + + const Decl *D = cxcursor::getCursorDecl(C); + ASTContext &Ctx = cxcursor::getCursorContext(C); + std::string encoding; + + if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) { + encoding = Ctx.getObjCEncodingForMethodDecl(OMD); + } else if (const ObjCPropertyDecl *OPD = dyn_cast<ObjCPropertyDecl>(D)) + encoding = Ctx.getObjCEncodingForPropertyDecl(OPD, nullptr); + else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + encoding = Ctx.getObjCEncodingForFunctionDecl(FD); + else { + QualType Ty; + if (const TypeDecl *TD = dyn_cast<TypeDecl>(D)) + Ty = Ctx.getTypeDeclType(TD); + if (const ValueDecl *VD = dyn_cast<ValueDecl>(D)) + Ty = VD->getType(); + else return cxstring::createRef("?"); + Ctx.getObjCEncodingForType(Ty, encoding); + } + + return cxstring::createDup(encoding); +} + +static unsigned GetTemplateArgumentArraySize(ArrayRef<TemplateArgument> TA) { + unsigned size = TA.size(); + for (const auto &Arg : TA) + if (Arg.getKind() == TemplateArgument::Pack) + size += Arg.pack_size() - 1; + return size; +} + +int clang_Type_getNumTemplateArguments(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return -1; + + auto TA = GetTemplateArguments(T); + if (!TA) + return -1; + + return GetTemplateArgumentArraySize(TA.getValue()); +} + +CXType clang_Type_getTemplateArgumentAsType(CXType CT, unsigned index) { + QualType T = GetQualType(CT); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(CT)); + + auto TA = GetTemplateArguments(T); + if (!TA) + return MakeCXType(QualType(), GetTU(CT)); + + Optional<QualType> QT = FindTemplateArgumentTypeAt(TA.getValue(), index); + return MakeCXType(QT.getValueOr(QualType()), GetTU(CT)); +} + +CXType clang_Type_getObjCObjectBaseType(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(CT)); + + const ObjCObjectType *OT = dyn_cast<ObjCObjectType>(T); + if (!OT) + return MakeCXType(QualType(), GetTU(CT)); + + return MakeCXType(OT->getBaseType(), GetTU(CT)); +} + +unsigned clang_Type_getNumObjCProtocolRefs(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return 0; + + const ObjCObjectType *OT = dyn_cast<ObjCObjectType>(T); + if (!OT) + return 0; + + return OT->getNumProtocols(); +} + +CXCursor clang_Type_getObjCProtocolDecl(CXType CT, unsigned i) { + QualType T = GetQualType(CT); + if (T.isNull()) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + const ObjCObjectType *OT = dyn_cast<ObjCObjectType>(T); + if (!OT) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + const ObjCProtocolDecl *PD = OT->getProtocol(i); + if (!PD) + return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); + + return cxcursor::MakeCXCursor(PD, GetTU(CT)); +} + +unsigned clang_Type_getNumObjCTypeArgs(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return 0; + + const ObjCObjectType *OT = dyn_cast<ObjCObjectType>(T); + if (!OT) + return 0; + + return OT->getTypeArgs().size(); +} + +CXType clang_Type_getObjCTypeArg(CXType CT, unsigned i) { + QualType T = GetQualType(CT); + if (T.isNull()) + return MakeCXType(QualType(), GetTU(CT)); + + const ObjCObjectType *OT = dyn_cast<ObjCObjectType>(T); + if (!OT) + return MakeCXType(QualType(), GetTU(CT)); + + const ArrayRef<QualType> TA = OT->getTypeArgs(); + if ((size_t)i >= TA.size()) + return MakeCXType(QualType(), GetTU(CT)); + + return MakeCXType(TA[i], GetTU(CT)); +} + +unsigned clang_Type_visitFields(CXType PT, + CXFieldVisitor visitor, + CXClientData client_data){ + CXCursor PC = clang_getTypeDeclaration(PT); + if (clang_isInvalid(PC.kind)) + return false; + const RecordDecl *RD = + dyn_cast_or_null<RecordDecl>(cxcursor::getCursorDecl(PC)); + if (!RD || RD->isInvalidDecl()) + return false; + RD = RD->getDefinition(); + if (!RD || RD->isInvalidDecl()) + return false; + + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I){ + const FieldDecl *FD = dyn_cast_or_null<FieldDecl>((*I)); + // Callback to the client. + switch (visitor(cxcursor::MakeCXCursor(FD, GetTU(PT)), client_data)){ + case CXVisit_Break: + return true; + case CXVisit_Continue: + break; + } + } + return true; +} + +unsigned clang_Cursor_isAnonymous(CXCursor C){ + if (!clang_isDeclaration(C.kind)) + return 0; + const Decl *D = cxcursor::getCursorDecl(C); + if (const NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(D)) { + return ND->isAnonymousNamespace(); + } else if (const TagDecl *TD = dyn_cast_or_null<TagDecl>(D)) { + return TD->getTypedefNameForAnonDecl() == nullptr && + TD->getIdentifier() == nullptr; + } + + return 0; +} + +unsigned clang_Cursor_isAnonymousRecordDecl(CXCursor C){ + if (!clang_isDeclaration(C.kind)) + return 0; + const Decl *D = cxcursor::getCursorDecl(C); + if (const RecordDecl *FD = dyn_cast_or_null<RecordDecl>(D)) + return FD->isAnonymousStructOrUnion(); + return 0; +} + +unsigned clang_Cursor_isInlineNamespace(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + const Decl *D = cxcursor::getCursorDecl(C); + const NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(D); + return ND ? ND->isInline() : 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)); +} + +unsigned clang_Type_isTransparentTagTypedef(CXType TT){ + QualType T = GetQualType(TT); + if (auto *TT = dyn_cast_or_null<TypedefType>(T.getTypePtrOrNull())) { + if (auto *D = TT->getDecl()) + return D->isTransparentTag(); + } + return false; +} + +enum CXTypeNullabilityKind clang_Type_getNullability(CXType CT) { + QualType T = GetQualType(CT); + if (T.isNull()) + return CXTypeNullability_Invalid; + + ASTContext &Ctx = cxtu::getASTUnit(GetTU(CT))->getASTContext(); + if (auto nullability = T->getNullability(Ctx)) { + switch (*nullability) { + case NullabilityKind::NonNull: + return CXTypeNullability_NonNull; + case NullabilityKind::Nullable: + return CXTypeNullability_Nullable; + case NullabilityKind::Unspecified: + return CXTypeNullability_Unspecified; + } + } + return CXTypeNullability_Invalid; +} diff --git a/gnu/llvm/clang/tools/libclang/CXType.h b/gnu/llvm/clang/tools/libclang/CXType.h new file mode 100644 index 00000000000..1d458086c09 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CXType.h @@ -0,0 +1,28 @@ +//===- CXTypes.h - Routines for manipulating CXTypes ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CXTYPE_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CXTYPE_H + +#include "clang-c/Index.h" +#include "clang/AST/Type.h" + +namespace clang { + +class ASTUnit; + +namespace cxtype { + +CXType MakeCXType(QualType T, CXTranslationUnit TU); + +}} // end namespace clang::cxtype +#endif diff --git a/gnu/llvm/clang/tools/libclang/CursorVisitor.h b/gnu/llvm/clang/tools/libclang/CursorVisitor.h new file mode 100644 index 00000000000..b0afa5a0b59 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/CursorVisitor.h @@ -0,0 +1,277 @@ +//===- CursorVisitor.h - CursorVisitor interface ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_CURSORVISITOR_H +#define LLVM_CLANG_TOOLS_LIBCLANG_CURSORVISITOR_H + +#include "CXCursor.h" +#include "CXTranslationUnit.h" +#include "Index_Internal.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/TypeLocVisitor.h" + +namespace clang { + class PreprocessingRecord; + class ASTUnit; + +namespace cxcursor { + +class VisitorJob { +public: + enum Kind { DeclVisitKind, StmtVisitKind, MemberExprPartsKind, + TypeLocVisitKind, OverloadExprPartsKind, + DeclRefExprPartsKind, LabelRefVisitKind, + ExplicitTemplateArgsVisitKind, + NestedNameSpecifierLocVisitKind, + DeclarationNameInfoVisitKind, + MemberRefVisitKind, SizeOfPackExprPartsKind, + LambdaExprPartsKind, PostChildrenVisitKind }; +protected: + const void *data[3]; + CXCursor parent; + Kind K; + VisitorJob(CXCursor C, Kind k, const void *d1, const void *d2 = nullptr, + const void *d3 = nullptr) + : parent(C), K(k) { + data[0] = d1; + data[1] = d2; + data[2] = d3; + } +public: + Kind getKind() const { return K; } + const CXCursor &getParent() const { return parent; } +}; + +typedef SmallVector<VisitorJob, 10> VisitorWorkList; + +// Cursor visitor. +class CursorVisitor : public DeclVisitor<CursorVisitor, bool>, + public TypeLocVisitor<CursorVisitor, bool> +{ +public: + /// Callback called after child nodes of a cursor have been visited. + /// Return true to break visitation or false to continue. + typedef bool (*PostChildrenVisitorTy)(CXCursor cursor, + CXClientData client_data); + +private: + /// The translation unit we are traversing. + CXTranslationUnit TU; + ASTUnit *AU; + + /// The parent cursor whose children we are traversing. + CXCursor Parent; + + /// The declaration that serves at the parent of any statement or + /// expression nodes. + const Decl *StmtParent; + + /// The visitor function. + CXCursorVisitor Visitor; + + PostChildrenVisitorTy PostChildrenVisitor; + + /// The opaque client data, to be passed along to the visitor. + CXClientData ClientData; + + /// Whether we should visit the preprocessing record entries last, + /// after visiting other declarations. + bool VisitPreprocessorLast; + + /// Whether we should visit declarations or preprocessing record + /// entries that are #included inside the \arg RegionOfInterest. + bool VisitIncludedEntities; + + /// When valid, a source range to which the cursor should restrict + /// its search. + SourceRange RegionOfInterest; + + /// Whether we should only visit declarations and not preprocessing + /// record entries. + bool VisitDeclsOnly; + + // FIXME: Eventually remove. This part of a hack to support proper + // iteration over all Decls contained lexically within an ObjC container. + DeclContext::decl_iterator *DI_current; + DeclContext::decl_iterator DE_current; + SmallVectorImpl<Decl *>::iterator *FileDI_current; + SmallVectorImpl<Decl *>::iterator FileDE_current; + + // Cache of pre-allocated worklists for data-recursion walk of Stmts. + SmallVector<VisitorWorkList*, 5> WorkListFreeList; + SmallVector<VisitorWorkList*, 5> WorkListCache; + + using DeclVisitor<CursorVisitor, bool>::Visit; + using TypeLocVisitor<CursorVisitor, bool>::Visit; + + /// Determine whether this particular source range comes before, comes + /// after, or overlaps the region of interest. + /// + /// \param R a half-open source range retrieved from the abstract syntax tree. + RangeComparisonResult CompareRegionOfInterest(SourceRange R); + + bool visitDeclsFromFileRegion(FileID File, unsigned Offset, unsigned Length); + + class SetParentRAII { + CXCursor &Parent; + const Decl *&StmtParent; + CXCursor OldParent; + + public: + SetParentRAII(CXCursor &Parent, const Decl *&StmtParent, + CXCursor NewParent) + : Parent(Parent), StmtParent(StmtParent), OldParent(Parent) + { + Parent = NewParent; + if (clang_isDeclaration(Parent.kind)) + StmtParent = getCursorDecl(Parent); + } + + ~SetParentRAII() { + Parent = OldParent; + if (clang_isDeclaration(Parent.kind)) + StmtParent = getCursorDecl(Parent); + } + }; + +public: + CursorVisitor(CXTranslationUnit TU, CXCursorVisitor Visitor, + CXClientData ClientData, + bool VisitPreprocessorLast, + bool VisitIncludedPreprocessingEntries = false, + SourceRange RegionOfInterest = SourceRange(), + bool VisitDeclsOnly = false, + PostChildrenVisitorTy PostChildrenVisitor = nullptr) + : TU(TU), AU(cxtu::getASTUnit(TU)), + Visitor(Visitor), PostChildrenVisitor(PostChildrenVisitor), + ClientData(ClientData), + VisitPreprocessorLast(VisitPreprocessorLast), + VisitIncludedEntities(VisitIncludedPreprocessingEntries), + RegionOfInterest(RegionOfInterest), + VisitDeclsOnly(VisitDeclsOnly), + DI_current(nullptr), FileDI_current(nullptr) + { + Parent.kind = CXCursor_NoDeclFound; + Parent.data[0] = nullptr; + Parent.data[1] = nullptr; + Parent.data[2] = nullptr; + StmtParent = nullptr; + } + + ~CursorVisitor() { + // Free the pre-allocated worklists for data-recursion. + for (SmallVectorImpl<VisitorWorkList*>::iterator + I = WorkListCache.begin(), E = WorkListCache.end(); I != E; ++I) { + delete *I; + } + } + + ASTUnit *getASTUnit() const { return AU; } + CXTranslationUnit getTU() const { return TU; } + + bool Visit(CXCursor Cursor, bool CheckedRegionOfInterest = false); + + /// Visit declarations and preprocessed entities for the file region + /// designated by \see RegionOfInterest. + bool visitFileRegion(); + + bool visitPreprocessedEntitiesInRegion(); + + bool shouldVisitIncludedEntities() const { + return VisitIncludedEntities; + } + + template<typename InputIterator> + bool visitPreprocessedEntities(InputIterator First, InputIterator Last, + PreprocessingRecord &PPRec, + FileID FID = FileID()); + + bool VisitChildren(CXCursor Parent); + + // Declaration visitors + bool VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D); + bool VisitTypeAliasDecl(TypeAliasDecl *D); + bool VisitAttributes(Decl *D); + bool VisitBlockDecl(BlockDecl *B); + bool VisitCXXRecordDecl(CXXRecordDecl *D); + Optional<bool> shouldVisitCursor(CXCursor C); + bool VisitDeclContext(DeclContext *DC); + bool VisitTranslationUnitDecl(TranslationUnitDecl *D); + bool VisitTypedefDecl(TypedefDecl *D); + bool VisitTagDecl(TagDecl *D); + bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *D); + bool VisitClassTemplatePartialSpecializationDecl( + ClassTemplatePartialSpecializationDecl *D); + bool VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D); + bool VisitEnumConstantDecl(EnumConstantDecl *D); + bool VisitDeclaratorDecl(DeclaratorDecl *DD); + bool VisitFunctionDecl(FunctionDecl *ND); + bool VisitFieldDecl(FieldDecl *D); + bool VisitVarDecl(VarDecl *); + bool VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D); + bool VisitFunctionTemplateDecl(FunctionTemplateDecl *D); + bool VisitClassTemplateDecl(ClassTemplateDecl *D); + bool VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D); + bool VisitObjCTypeParamDecl(ObjCTypeParamDecl *D); + bool VisitObjCMethodDecl(ObjCMethodDecl *ND); + bool VisitObjCContainerDecl(ObjCContainerDecl *D); + bool VisitObjCCategoryDecl(ObjCCategoryDecl *ND); + bool VisitObjCProtocolDecl(ObjCProtocolDecl *PID); + bool VisitObjCPropertyDecl(ObjCPropertyDecl *PD); + bool VisitObjCTypeParamList(ObjCTypeParamList *typeParamList); + bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); + bool VisitObjCImplDecl(ObjCImplDecl *D); + bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D); + bool VisitObjCImplementationDecl(ObjCImplementationDecl *D); + // FIXME: ObjCCompatibleAliasDecl requires aliased-class locations. + bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *PD); + bool VisitLinkageSpecDecl(LinkageSpecDecl *D); + bool VisitNamespaceDecl(NamespaceDecl *D); + bool VisitNamespaceAliasDecl(NamespaceAliasDecl *D); + bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D); + bool VisitUsingDecl(UsingDecl *D); + bool VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D); + bool VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D); + bool VisitStaticAssertDecl(StaticAssertDecl *D); + bool VisitFriendDecl(FriendDecl *D); + + // Name visitor + bool VisitDeclarationNameInfo(DeclarationNameInfo Name); + bool VisitNestedNameSpecifier(NestedNameSpecifier *NNS, SourceRange Range); + bool VisitNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS); + + // Template visitors + bool VisitTemplateParameters(const TemplateParameterList *Params); + bool VisitTemplateName(TemplateName Name, SourceLocation Loc); + bool VisitTemplateArgumentLoc(const TemplateArgumentLoc &TAL); + + // Type visitors +#define ABSTRACT_TYPELOC(CLASS, PARENT) +#define TYPELOC(CLASS, PARENT) \ + bool Visit##CLASS##TypeLoc(CLASS##TypeLoc TyLoc); +#include "clang/AST/TypeLocNodes.def" + + bool VisitTagTypeLoc(TagTypeLoc TL); + bool VisitArrayTypeLoc(ArrayTypeLoc TL); + bool VisitFunctionTypeLoc(FunctionTypeLoc TL, bool SkipResultType = false); + + // Data-recursive visitor functions. + bool IsInRegionOfInterest(CXCursor C); + 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); +}; + +} +} + +#endif + diff --git a/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp b/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp new file mode 100644 index 00000000000..eab17f3dfac --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/FatalErrorHandler.cpp @@ -0,0 +1,30 @@ +/*===-- clang-c/FatalErrorHandler.cpp - Fatal Error Handling ------*- C -*-===*\ +|* *| +|* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| +|* Exceptions. *| +|* See https://llvm.org/LICENSE.txt for license information. *| +|* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#include "clang-c/FatalErrorHandler.h" +#include "llvm/Support/ErrorHandling.h" + +static void aborting_fatal_error_handler(void *, const std::string &reason, + bool) { + // Write the result out to stderr avoiding errs() because raw_ostreams can + // call report_fatal_error. + fprintf(stderr, "LIBCLANG FATAL ERROR: %s\n", reason.c_str()); + ::abort(); +} + +extern "C" { +void clang_install_aborting_llvm_fatal_error_handler(void) { + llvm::remove_fatal_error_handler(); + llvm::install_fatal_error_handler(aborting_fatal_error_handler, nullptr); +} + +void clang_uninstall_llvm_fatal_error_handler(void) { + llvm::remove_fatal_error_handler(); +} +}
\ No newline at end of file diff --git a/gnu/llvm/clang/tools/libclang/Index_Internal.h b/gnu/llvm/clang/tools/libclang/Index_Internal.h new file mode 100644 index 00000000000..d28438770e7 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/Index_Internal.h @@ -0,0 +1,54 @@ +//===- CXString.h - Routines for manipulating CXStrings -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXStrings. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_LIBCLANG_INDEX_INTERNAL_H +#define LLVM_CLANG_TOOLS_LIBCLANG_INDEX_INTERNAL_H + +#include "clang-c/Index.h" + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(blocks) + +#define INVOKE_BLOCK2(block, arg1, arg2) block(arg1, arg2) + +#else +// If we are compiled with a compiler that doesn't have native blocks support, +// define and call the block manually. + +#define INVOKE_BLOCK2(block, arg1, arg2) block->invoke(block, arg1, arg2) + +typedef struct _CXCursorAndRangeVisitorBlock { + void *isa; + int flags; + int reserved; + enum CXVisitorResult (*invoke)(_CXCursorAndRangeVisitorBlock *, + CXCursor, CXSourceRange); +} *CXCursorAndRangeVisitorBlock; + +#endif // !__has_feature(blocks) + +/// The result of comparing two source ranges. +enum RangeComparisonResult { + /// Either the ranges overlap or one of the ranges is invalid. + RangeOverlap, + + /// The first range ends before the second range starts. + RangeBefore, + + /// The first range starts after the second range ends. + RangeAfter +}; + +#endif diff --git a/gnu/llvm/clang/tools/libclang/Indexing.cpp b/gnu/llvm/clang/tools/libclang/Indexing.cpp new file mode 100644 index 00000000000..75b83d73d19 --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/Indexing.cpp @@ -0,0 +1,998 @@ +//===- Indexing.cpp - Higher level API functions --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/MultiplexConsumer.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" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include <cstdio> +#include <mutex> +#include <utility> + +using namespace clang; +using namespace clang::index; +using namespace cxtu; +using namespace cxindex; + +namespace { + +//===----------------------------------------------------------------------===// +// Skip Parsed Bodies +//===----------------------------------------------------------------------===// + +/// 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. +/// +/// As an example of different regions separated by preprocessor directives: +/// +/// \code +/// #1 +/// #ifdef BLAH +/// #2 +/// #ifdef CAKE +/// #3 +/// #endif +/// #2 +/// #endif +/// #1 +/// \endcode +/// +/// There are 3 regions, with non-consecutive parts: +/// #1 is identified as the beginning of the file +/// #2 is identified as the location of "#ifdef BLAH" +/// #3 is identified as the location of "#ifdef CAKE" +/// +class PPRegion { + llvm::sys::fs::UniqueID UniqueID; + time_t ModTime; + unsigned Offset; +public: + PPRegion() : UniqueID(0, 0), ModTime(), Offset() {} + PPRegion(llvm::sys::fs::UniqueID UniqueID, unsigned offset, time_t modTime) + : UniqueID(UniqueID), ModTime(modTime), Offset(offset) {} + + const llvm::sys::fs::UniqueID &getUniqueID() const { return UniqueID; } + unsigned getOffset() const { return Offset; } + time_t getModTime() const { return ModTime; } + + bool isInvalid() const { return *this == PPRegion(); } + + friend bool operator==(const PPRegion &lhs, const PPRegion &rhs) { + return lhs.UniqueID == rhs.UniqueID && lhs.Offset == rhs.Offset && + lhs.ModTime == rhs.ModTime; + } +}; + +} // end anonymous namespace + +namespace llvm { + + template <> + struct DenseMapInfo<PPRegion> { + static inline PPRegion getEmptyKey() { + return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-1), 0); + } + static inline PPRegion getTombstoneKey() { + return PPRegion(llvm::sys::fs::UniqueID(0, 0), unsigned(-2), 0); + } + + static unsigned getHashValue(const PPRegion &S) { + llvm::FoldingSetNodeID ID; + const llvm::sys::fs::UniqueID &UniqueID = S.getUniqueID(); + ID.AddInteger(UniqueID.getFile()); + ID.AddInteger(UniqueID.getDevice()); + ID.AddInteger(S.getOffset()); + ID.AddInteger(S.getModTime()); + return ID.ComputeHash(); + } + + static bool isEqual(const PPRegion &LHS, const PPRegion &RHS) { + return LHS == RHS; + } + }; +} + +namespace { + +/// Keeps track of function bodies that have already been parsed. +/// +/// Is thread-safe. +class ThreadSafeParsedRegions { + mutable std::mutex Mutex; + llvm::DenseSet<PPRegion> ParsedRegions; + +public: + ~ThreadSafeParsedRegions() = default; + + llvm::DenseSet<PPRegion> getParsedRegions() const { + std::lock_guard<std::mutex> MG(Mutex); + return ParsedRegions; + } + + void addParsedRegions(ArrayRef<PPRegion> Regions) { + std::lock_guard<std::mutex> MG(Mutex); + ParsedRegions.insert(Regions.begin(), Regions.end()); + } +}; + +/// Provides information whether source locations have already been parsed in +/// another FrontendAction. +/// +/// Is NOT thread-safe. +class ParsedSrcLocationsTracker { + ThreadSafeParsedRegions &ParsedRegionsStorage; + PPConditionalDirectiveRecord &PPRec; + Preprocessor &PP; + + /// Snapshot of the shared state at the point when this instance was + /// constructed. + llvm::DenseSet<PPRegion> ParsedRegionsSnapshot; + /// Regions that were queried during this instance lifetime. + SmallVector<PPRegion, 32> NewParsedRegions; + + /// Caching the last queried region. + PPRegion LastRegion; + bool LastIsParsed; + +public: + /// Creates snapshot of \p ParsedRegionsStorage. + ParsedSrcLocationsTracker(ThreadSafeParsedRegions &ParsedRegionsStorage, + PPConditionalDirectiveRecord &ppRec, + Preprocessor &pp) + : ParsedRegionsStorage(ParsedRegionsStorage), PPRec(ppRec), PP(pp), + ParsedRegionsSnapshot(ParsedRegionsStorage.getParsedRegions()) {} + + /// \returns true iff \p Loc has already been parsed. + /// + /// Can provide false-negative in case the location was parsed after this + /// instance had been constructed. + bool hasAlredyBeenParsed(SourceLocation Loc, FileID FID, + const FileEntry *FE) { + assert(FE); + PPRegion region = getRegion(Loc, FID, FE); + if (region.isInvalid()) + return false; + + // Check common case, consecutive functions in the same region. + if (LastRegion == region) + return LastIsParsed; + + LastRegion = region; + // Source locations can't be revisited during single TU parsing. + // That means if we hit the same region again, it's a different location in + // the same region and so the "is parsed" value from the snapshot is still + // correct. + LastIsParsed = ParsedRegionsSnapshot.count(region); + if (!LastIsParsed) + NewParsedRegions.emplace_back(std::move(region)); + return LastIsParsed; + } + + /// Updates ParsedRegionsStorage with newly parsed regions. + void syncWithStorage() { + ParsedRegionsStorage.addParsedRegions(NewParsedRegions); + } + +private: + PPRegion getRegion(SourceLocation Loc, FileID FID, const FileEntry *FE) { + assert(FE); + auto Bail = [this, FE]() { + if (isParsedOnceInclude(FE)) { + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, 0, FE->getModificationTime()); + } + return PPRegion(); + }; + + SourceLocation RegionLoc = PPRec.findConditionalDirectiveRegionLoc(Loc); + assert(RegionLoc.isFileID()); + if (RegionLoc.isInvalid()) + return Bail(); + + FileID RegionFID; + unsigned RegionOffset; + std::tie(RegionFID, RegionOffset) = + PPRec.getSourceManager().getDecomposedLoc(RegionLoc); + + if (RegionFID != FID) + return Bail(); + + const llvm::sys::fs::UniqueID &ID = FE->getUniqueID(); + return PPRegion(ID, RegionOffset, FE->getModificationTime()); + } + + bool isParsedOnceInclude(const FileEntry *FE) { + return PP.getHeaderSearchInfo().isFileMultipleIncludeGuarded(FE); + } +}; + +//===----------------------------------------------------------------------===// +// IndexPPCallbacks +//===----------------------------------------------------------------------===// + +class IndexPPCallbacks : public PPCallbacks { + Preprocessor &PP; + CXIndexDataConsumer &DataConsumer; + bool IsMainFileEntered; + +public: + IndexPPCallbacks(Preprocessor &PP, CXIndexDataConsumer &dataConsumer) + : PP(PP), DataConsumer(dataConsumer), IsMainFileEntered(false) { } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, FileID PrevFID) override { + if (IsMainFileEntered) + return; + + SourceManager &SM = PP.getSourceManager(); + SourceLocation MainFileLoc = SM.getLocForStartOfFile(SM.getMainFileID()); + + if (Loc == MainFileLoc && Reason == PPCallbacks::EnterFile) { + IsMainFileEntered = true; + DataConsumer.enteredMainFile(SM.getFileEntryForID(SM.getMainFileID())); + } + } + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { + bool isImport = (IncludeTok.is(tok::identifier) && + IncludeTok.getIdentifierInfo()->getPPKeywordID() == tok::pp_import); + DataConsumer.ppIncludedFile(HashLoc, FileName, File, isImport, IsAngled, + Imported); + } + + /// MacroDefined - This hook is called whenever a macro definition is seen. + void MacroDefined(const Token &Id, const MacroDirective *MD) override {} + + /// MacroUndefined - This hook is called whenever a macro #undef is seen. + /// MI is released immediately following this callback. + void MacroUndefined(const Token &MacroNameTok, + const MacroDefinition &MD, + const MacroDirective *UD) override {} + + /// MacroExpands - This is called by when a macro invocation is found. + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override {} + + /// SourceRangeSkipped - This hook is called when a source range is skipped. + /// \param Range The SourceRange that was skipped. The range begins at the + /// #if/#else directive and ends after the #endif/#else directive. + void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override { + } +}; + +//===----------------------------------------------------------------------===// +// IndexingConsumer +//===----------------------------------------------------------------------===// + +class IndexingConsumer : public ASTConsumer { + CXIndexDataConsumer &DataConsumer; + +public: + IndexingConsumer(CXIndexDataConsumer &dataConsumer, + ParsedSrcLocationsTracker *parsedLocsTracker) + : DataConsumer(dataConsumer) {} + + void Initialize(ASTContext &Context) override { + DataConsumer.setASTContext(Context); + DataConsumer.startedTranslationUnit(); + } + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + return !DataConsumer.shouldAbort(); + } +}; + +//===----------------------------------------------------------------------===// +// CaptureDiagnosticConsumer +//===----------------------------------------------------------------------===// + +class CaptureDiagnosticConsumer : public DiagnosticConsumer { + SmallVector<StoredDiagnostic, 4> Errors; +public: + + void HandleDiagnostic(DiagnosticsEngine::Level level, + const Diagnostic &Info) override { + if (level >= DiagnosticsEngine::Error) + Errors.push_back(StoredDiagnostic(level, Info)); + } +}; + +//===----------------------------------------------------------------------===// +// IndexingFrontendAction +//===----------------------------------------------------------------------===// + +class IndexingFrontendAction : public ASTFrontendAction { + std::shared_ptr<CXIndexDataConsumer> DataConsumer; + IndexingOptions Opts; + + ThreadSafeParsedRegions *SKData; + std::unique_ptr<ParsedSrcLocationsTracker> ParsedLocsTracker; + +public: + IndexingFrontendAction(std::shared_ptr<CXIndexDataConsumer> dataConsumer, + const IndexingOptions &Opts, + ThreadSafeParsedRegions *skData) + : DataConsumer(std::move(dataConsumer)), Opts(Opts), SKData(skData) {} + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + + if (!PPOpts.ImplicitPCHInclude.empty()) { + auto File = CI.getFileManager().getFile(PPOpts.ImplicitPCHInclude); + if (File) + DataConsumer->importedPCH(*File); + } + + DataConsumer->setASTContext(CI.getASTContext()); + Preprocessor &PP = CI.getPreprocessor(); + PP.addPPCallbacks(std::make_unique<IndexPPCallbacks>(PP, *DataConsumer)); + DataConsumer->setPreprocessor(CI.getPreprocessorPtr()); + + if (SKData) { + auto *PPRec = new PPConditionalDirectiveRecord(PP.getSourceManager()); + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); + ParsedLocsTracker = + std::make_unique<ParsedSrcLocationsTracker>(*SKData, *PPRec, PP); + } + + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + Consumers.push_back(std::make_unique<IndexingConsumer>( + *DataConsumer, ParsedLocsTracker.get())); + Consumers.push_back(createIndexingASTConsumer( + DataConsumer, Opts, CI.getPreprocessorPtr(), + [this](const Decl *D) { return this->shouldSkipFunctionBody(D); })); + return std::make_unique<MultiplexConsumer>(std::move(Consumers)); + } + + bool shouldSkipFunctionBody(const Decl *D) { + if (!ParsedLocsTracker) { + // Always skip bodies. + return true; + } + + const SourceManager &SM = D->getASTContext().getSourceManager(); + SourceLocation Loc = D->getLocation(); + if (Loc.isMacroID()) + return false; + if (SM.isInSystemHeader(Loc)) + return true; // always skip bodies from system headers. + + FileID FID; + unsigned Offset; + std::tie(FID, Offset) = SM.getDecomposedLoc(Loc); + // Don't skip bodies from main files; this may be revisited. + if (SM.getMainFileID() == FID) + return false; + const FileEntry *FE = SM.getFileEntryForID(FID); + if (!FE) + return false; + + return ParsedLocsTracker->hasAlredyBeenParsed(Loc, FID, FE); + } + + TranslationUnitKind getTranslationUnitKind() override { + if (DataConsumer->shouldIndexImplicitTemplateInsts()) + return TU_Complete; + else + return TU_Prefix; + } + bool hasCodeCompletionSupport() const override { return false; } + + void EndSourceFileAction() override { + if (ParsedLocsTracker) + ParsedLocsTracker->syncWithStorage(); + } +}; + +//===----------------------------------------------------------------------===// +// clang_indexSourceFileUnit Implementation +//===----------------------------------------------------------------------===// + +static IndexingOptions getIndexingOptionsFromCXOptions(unsigned index_options) { + IndexingOptions IdxOpts; + if (index_options & CXIndexOpt_IndexFunctionLocalSymbols) + IdxOpts.IndexFunctionLocals = true; + if (index_options & CXIndexOpt_IndexImplicitTemplateInstantiations) + IdxOpts.IndexImplicitInstantiation = true; + return IdxOpts; +} + +struct IndexSessionData { + CXIndex CIdx; + std::unique_ptr<ThreadSafeParsedRegions> SkipBodyData = + std::make_unique<ThreadSafeParsedRegions>(); + + explicit IndexSessionData(CXIndex cIdx) : CIdx(cIdx) {} +}; + +} // anonymous namespace + +static CXErrorCode clang_indexSourceFile_Impl( + CXIndexAction cxIdxAction, CXClientData client_data, + IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size, + unsigned index_options, const char *source_filename, + const char *const *command_line_args, int num_command_line_args, + ArrayRef<CXUnsavedFile> unsaved_files, CXTranslationUnit *out_TU, + unsigned TU_options) { + if (out_TU) + *out_TU = nullptr; + bool requestedToGetTU = (out_TU != nullptr); + + if (!cxIdxAction) { + return CXError_InvalidArguments; + } + if (!client_index_callbacks || index_callbacks_size == 0) { + return CXError_InvalidArguments; + } + + IndexerCallbacks CB; + memset(&CB, 0, sizeof(CB)); + unsigned ClientCBSize = index_callbacks_size < sizeof(CB) + ? index_callbacks_size : sizeof(CB); + memcpy(&CB, client_index_callbacks, ClientCBSize); + + IndexSessionData *IdxSession = static_cast<IndexSessionData *>(cxIdxAction); + CIndexer *CXXIdx = static_cast<CIndexer *>(IdxSession->CIdx); + + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + CaptureDiagsKind CaptureDiagnostics = CaptureDiagsKind::All; + if (TU_options & CXTranslationUnit_IgnoreNonErrorsFromIncludedFiles) + CaptureDiagnostics = CaptureDiagsKind::AllWithoutNonErrorsFromIncludes; + if (Logger::isLoggingEnabled()) + CaptureDiagnostics = CaptureDiagsKind::None; + + CaptureDiagnosticConsumer *CaptureDiag = nullptr; + if (CaptureDiagnostics != CaptureDiagsKind::None) + CaptureDiag = new CaptureDiagnosticConsumer(); + + // Configure the diagnostics. + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions, + CaptureDiag, + /*ShouldOwnClient=*/true)); + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.get()); + + std::unique_ptr<std::vector<const char *>> Args( + new std::vector<const char *>()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<std::vector<const char*> > + ArgsCleanup(Args.get()); + + Args->insert(Args->end(), command_line_args, + command_line_args + num_command_line_args); + + // The 'source_filename' argument is optional. If the caller does not + // specify it then it is assumed that the source file is specified + // in the actual argument list. + // Put the source file after command_line_args otherwise if '-x' flag is + // present it will be unused. + if (source_filename) + Args->push_back(source_filename); + + std::shared_ptr<CompilerInvocation> CInvok = + createInvocationFromCommandLine(*Args, Diags); + + if (!CInvok) + return CXError_Failure; + + // Recover resources if we crash before exiting this function. + llvm::CrashRecoveryContextCleanupRegistrar< + std::shared_ptr<CompilerInvocation>, + llvm::CrashRecoveryContextDestructorCleanup< + std::shared_ptr<CompilerInvocation>>> + CInvokCleanup(&CInvok); + + if (CInvok->getFrontendOpts().Inputs.empty()) + return CXError_Failure; + + typedef SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 8> MemBufferOwner; + std::unique_ptr<MemBufferOwner> BufOwner(new MemBufferOwner); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<MemBufferOwner> BufOwnerCleanup( + BufOwner.get()); + + for (auto &UF : unsaved_files) { + std::unique_ptr<llvm::MemoryBuffer> MB = + llvm::MemoryBuffer::getMemBufferCopy(getContents(UF), UF.Filename); + CInvok->getPreprocessorOpts().addRemappedFile(UF.Filename, MB.get()); + BufOwner->push_back(std::move(MB)); + } + + // Since libclang is primarily used by batch tools dealing with + // (often very broken) source code, where spell-checking can have a + // significant negative impact on performance (particularly when + // precompiled headers are involved), we disable it. + CInvok->getLangOpts()->SpellChecking = false; + + if (index_options & CXIndexOpt_SuppressWarnings) + CInvok->getDiagnosticOpts().IgnoreWarnings = true; + + // Make sure to use the raw module format. + CInvok->getHeaderSearchOpts().ModuleFormat = + CXXIdx->getPCHContainerOperations()->getRawReader().getFormat(); + + auto Unit = ASTUnit::create(CInvok, Diags, CaptureDiagnostics, + /*UserFilesAreVolatile=*/true); + if (!Unit) + return CXError_InvalidArguments; + + auto *UPtr = Unit.get(); + std::unique_ptr<CXTUOwner> CXTU( + new CXTUOwner(MakeCXTranslationUnit(CXXIdx, std::move(Unit)))); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CXTUOwner> + CXTUCleanup(CXTU.get()); + + // Enable the skip-parsed-bodies optimization only for C++; this may be + // revisited. + bool SkipBodies = (index_options & CXIndexOpt_SkipParsedBodiesInSession) && + CInvok->getLangOpts()->CPlusPlus; + if (SkipBodies) + CInvok->getFrontendOpts().SkipFunctionBodies = true; + + auto DataConsumer = + std::make_shared<CXIndexDataConsumer>(client_data, CB, index_options, + CXTU->getTU()); + auto IndexAction = std::make_unique<IndexingFrontendAction>( + DataConsumer, getIndexingOptionsFromCXOptions(index_options), + SkipBodies ? IdxSession->SkipBodyData.get() : nullptr); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<FrontendAction> + IndexActionCleanup(IndexAction.get()); + + bool Persistent = requestedToGetTU; + bool OnlyLocalDecls = false; + bool PrecompilePreamble = false; + bool CreatePreambleOnFirstParse = false; + bool CacheCodeCompletionResults = false; + PreprocessorOptions &PPOpts = CInvok->getPreprocessorOpts(); + PPOpts.AllowPCHWithCompilerErrors = true; + + if (requestedToGetTU) { + OnlyLocalDecls = CXXIdx->getOnlyLocalDecls(); + PrecompilePreamble = TU_options & CXTranslationUnit_PrecompiledPreamble; + CreatePreambleOnFirstParse = + TU_options & CXTranslationUnit_CreatePreambleOnFirstParse; + // FIXME: Add a flag for modules. + CacheCodeCompletionResults + = TU_options & CXTranslationUnit_CacheCompletionResults; + } + + if (TU_options & CXTranslationUnit_DetailedPreprocessingRecord) { + PPOpts.DetailedRecord = true; + } + + if (!requestedToGetTU && !CInvok->getLangOpts()->Modules) + PPOpts.DetailedRecord = false; + + // Unless the user specified that they want the preamble on the first parse + // set it up to be created on the first reparse. This makes the first parse + // faster, trading for a slower (first) reparse. + unsigned PrecompilePreambleAfterNParses = + !PrecompilePreamble ? 0 : 2 - CreatePreambleOnFirstParse; + DiagnosticErrorTrap DiagTrap(*Diags); + bool Success = ASTUnit::LoadFromCompilerInvocationAction( + std::move(CInvok), CXXIdx->getPCHContainerOperations(), Diags, + IndexAction.get(), UPtr, Persistent, CXXIdx->getClangResourcesPath(), + OnlyLocalDecls, CaptureDiagnostics, PrecompilePreambleAfterNParses, + CacheCodeCompletionResults, + /*IncludeBriefCommentsInCodeCompletion=*/false, + /*UserFilesAreVolatile=*/true); + if (DiagTrap.hasErrorOccurred() && CXXIdx->getDisplayDiagnostics()) + printDiagsToStderr(UPtr); + + if (isASTReadError(UPtr)) + return CXError_ASTReadError; + + if (!Success) + return CXError_Failure; + + if (out_TU) + *out_TU = CXTU->takeTU(); + + return CXError_Success; +} + +//===----------------------------------------------------------------------===// +// clang_indexTranslationUnit Implementation +//===----------------------------------------------------------------------===// + +static void indexPreprocessingRecord(ASTUnit &Unit, CXIndexDataConsumer &IdxCtx) { + Preprocessor &PP = Unit.getPreprocessor(); + if (!PP.getPreprocessingRecord()) + return; + + // FIXME: Only deserialize inclusion directives. + + bool isModuleFile = Unit.isModuleFile(); + for (PreprocessedEntity *PPE : Unit.getLocalPreprocessingEntities()) { + if (InclusionDirective *ID = dyn_cast<InclusionDirective>(PPE)) { + SourceLocation Loc = ID->getSourceRange().getBegin(); + // Modules have synthetic main files as input, give an invalid location + // if the location points to such a file. + if (isModuleFile && Unit.isInMainFileID(Loc)) + Loc = SourceLocation(); + IdxCtx.ppIncludedFile(Loc, ID->getFileName(), + ID->getFile(), + ID->getKind() == InclusionDirective::Import, + !ID->wasInQuotes(), ID->importedModule()); + } + } +} + +static CXErrorCode clang_indexTranslationUnit_Impl( + CXIndexAction idxAction, CXClientData client_data, + IndexerCallbacks *client_index_callbacks, unsigned index_callbacks_size, + unsigned index_options, CXTranslationUnit TU) { + // Check arguments. + if (isNotUsableTU(TU)) { + LOG_BAD_TU(TU); + return CXError_InvalidArguments; + } + if (!client_index_callbacks || index_callbacks_size == 0) { + return CXError_InvalidArguments; + } + + CIndexer *CXXIdx = TU->CIdx; + if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForIndexing)) + setThreadBackgroundPriority(); + + IndexerCallbacks CB; + memset(&CB, 0, sizeof(CB)); + unsigned ClientCBSize = index_callbacks_size < sizeof(CB) + ? index_callbacks_size : sizeof(CB); + memcpy(&CB, client_index_callbacks, ClientCBSize); + + CXIndexDataConsumer DataConsumer(client_data, CB, index_options, TU); + + ASTUnit *Unit = cxtu::getASTUnit(TU); + if (!Unit) + return CXError_Failure; + + ASTUnit::ConcurrencyCheck Check(*Unit); + + if (const FileEntry *PCHFile = Unit->getPCHFile()) + DataConsumer.importedPCH(PCHFile); + + FileManager &FileMgr = Unit->getFileManager(); + + if (Unit->getOriginalSourceFileName().empty()) + DataConsumer.enteredMainFile(nullptr); + else if (auto MainFile = FileMgr.getFile(Unit->getOriginalSourceFileName())) + DataConsumer.enteredMainFile(*MainFile); + else + DataConsumer.enteredMainFile(nullptr); + + DataConsumer.setASTContext(Unit->getASTContext()); + DataConsumer.startedTranslationUnit(); + + indexPreprocessingRecord(*Unit, DataConsumer); + indexASTUnit(*Unit, DataConsumer, getIndexingOptionsFromCXOptions(index_options)); + DataConsumer.indexDiagnostics(); + + return CXError_Success; +} + +//===----------------------------------------------------------------------===// +// libclang public APIs. +//===----------------------------------------------------------------------===// + +int clang_index_isEntityObjCContainerKind(CXIdxEntityKind K) { + return CXIdxEntity_ObjCClass <= K && K <= CXIdxEntity_ObjCCategory; +} + +const CXIdxObjCContainerDeclInfo * +clang_index_getObjCContainerDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return nullptr; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCContainerDeclInfo * + ContInfo = dyn_cast<ObjCContainerDeclInfo>(DI)) + return &ContInfo->ObjCContDeclInfo; + + return nullptr; +} + +const CXIdxObjCInterfaceDeclInfo * +clang_index_getObjCInterfaceDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return nullptr; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCInterfaceDeclInfo * + InterInfo = dyn_cast<ObjCInterfaceDeclInfo>(DI)) + return &InterInfo->ObjCInterDeclInfo; + + return nullptr; +} + +const CXIdxObjCCategoryDeclInfo * +clang_index_getObjCCategoryDeclInfo(const CXIdxDeclInfo *DInfo){ + if (!DInfo) + return nullptr; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCCategoryDeclInfo * + CatInfo = dyn_cast<ObjCCategoryDeclInfo>(DI)) + return &CatInfo->ObjCCatDeclInfo; + + return nullptr; +} + +const CXIdxObjCProtocolRefListInfo * +clang_index_getObjCProtocolRefListInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return nullptr; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + + if (const ObjCInterfaceDeclInfo * + InterInfo = dyn_cast<ObjCInterfaceDeclInfo>(DI)) + return InterInfo->ObjCInterDeclInfo.protocols; + + if (const ObjCProtocolDeclInfo * + ProtInfo = dyn_cast<ObjCProtocolDeclInfo>(DI)) + return &ProtInfo->ObjCProtoRefListInfo; + + if (const ObjCCategoryDeclInfo *CatInfo = dyn_cast<ObjCCategoryDeclInfo>(DI)) + return CatInfo->ObjCCatDeclInfo.protocols; + + return nullptr; +} + +const CXIdxObjCPropertyDeclInfo * +clang_index_getObjCPropertyDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return nullptr; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const ObjCPropertyDeclInfo *PropInfo = dyn_cast<ObjCPropertyDeclInfo>(DI)) + return &PropInfo->ObjCPropDeclInfo; + + return nullptr; +} + +const CXIdxIBOutletCollectionAttrInfo * +clang_index_getIBOutletCollectionAttrInfo(const CXIdxAttrInfo *AInfo) { + if (!AInfo) + return nullptr; + + const AttrInfo *DI = static_cast<const AttrInfo *>(AInfo); + if (const IBOutletCollectionInfo * + IBInfo = dyn_cast<IBOutletCollectionInfo>(DI)) + return &IBInfo->IBCollInfo; + + return nullptr; +} + +const CXIdxCXXClassDeclInfo * +clang_index_getCXXClassDeclInfo(const CXIdxDeclInfo *DInfo) { + if (!DInfo) + return nullptr; + + const DeclInfo *DI = static_cast<const DeclInfo *>(DInfo); + if (const CXXClassDeclInfo *ClassInfo = dyn_cast<CXXClassDeclInfo>(DI)) + return &ClassInfo->CXXClassInfo; + + return nullptr; +} + +CXIdxClientContainer +clang_index_getClientContainer(const CXIdxContainerInfo *info) { + if (!info) + return nullptr; + const ContainerInfo *Container = static_cast<const ContainerInfo *>(info); + return Container->IndexCtx->getClientContainerForDC(Container->DC); +} + +void clang_index_setClientContainer(const CXIdxContainerInfo *info, + CXIdxClientContainer client) { + if (!info) + return; + const ContainerInfo *Container = static_cast<const ContainerInfo *>(info); + Container->IndexCtx->addContainerInMap(Container->DC, client); +} + +CXIdxClientEntity clang_index_getClientEntity(const CXIdxEntityInfo *info) { + if (!info) + return nullptr; + const EntityInfo *Entity = static_cast<const EntityInfo *>(info); + return Entity->IndexCtx->getClientEntity(Entity->Dcl); +} + +void clang_index_setClientEntity(const CXIdxEntityInfo *info, + CXIdxClientEntity client) { + if (!info) + return; + const EntityInfo *Entity = static_cast<const EntityInfo *>(info); + Entity->IndexCtx->setClientEntity(Entity->Dcl, client); +} + +CXIndexAction clang_IndexAction_create(CXIndex CIdx) { + return new IndexSessionData(CIdx); +} + +void clang_IndexAction_dispose(CXIndexAction idxAction) { + if (idxAction) + delete static_cast<IndexSessionData *>(idxAction); +} + +int clang_indexSourceFile(CXIndexAction idxAction, + CXClientData client_data, + IndexerCallbacks *index_callbacks, + unsigned index_callbacks_size, + unsigned index_options, + const char *source_filename, + const char * const *command_line_args, + int num_command_line_args, + struct CXUnsavedFile *unsaved_files, + unsigned num_unsaved_files, + CXTranslationUnit *out_TU, + unsigned TU_options) { + SmallVector<const char *, 4> Args; + Args.push_back("clang"); + Args.append(command_line_args, command_line_args + num_command_line_args); + return clang_indexSourceFileFullArgv( + idxAction, client_data, index_callbacks, index_callbacks_size, + index_options, source_filename, Args.data(), Args.size(), unsaved_files, + num_unsaved_files, out_TU, TU_options); +} + +int clang_indexSourceFileFullArgv( + CXIndexAction idxAction, CXClientData client_data, + IndexerCallbacks *index_callbacks, unsigned index_callbacks_size, + unsigned index_options, const char *source_filename, + const char *const *command_line_args, int num_command_line_args, + struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files, + CXTranslationUnit *out_TU, unsigned TU_options) { + LOG_FUNC_SECTION { + *Log << source_filename << ": "; + for (int i = 0; i != num_command_line_args; ++i) + *Log << command_line_args[i] << " "; + } + + if (num_unsaved_files && !unsaved_files) + return CXError_InvalidArguments; + + CXErrorCode result = CXError_Failure; + auto IndexSourceFileImpl = [=, &result]() { + result = clang_indexSourceFile_Impl( + idxAction, client_data, index_callbacks, index_callbacks_size, + index_options, source_filename, command_line_args, + num_command_line_args, + llvm::makeArrayRef(unsaved_files, num_unsaved_files), out_TU, + TU_options); + }; + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, IndexSourceFileImpl)) { + fprintf(stderr, "libclang: crash detected during indexing source file: {\n"); + fprintf(stderr, " 'source_filename' : '%s'\n", source_filename); + fprintf(stderr, " 'command_line_args' : ["); + for (int i = 0; i != num_command_line_args; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "'%s'", command_line_args[i]); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'unsaved_files' : ["); + for (unsigned i = 0; i != num_unsaved_files; ++i) { + if (i) + fprintf(stderr, ", "); + fprintf(stderr, "('%s', '...', %ld)", unsaved_files[i].Filename, + unsaved_files[i].Length); + } + fprintf(stderr, "],\n"); + fprintf(stderr, " 'options' : %d,\n", TU_options); + fprintf(stderr, "}\n"); + + return 1; + } else if (getenv("LIBCLANG_RESOURCE_USAGE")) { + if (out_TU) + PrintLibclangResourceUsage(*out_TU); + } + + return result; +} + +int clang_indexTranslationUnit(CXIndexAction idxAction, + CXClientData client_data, + IndexerCallbacks *index_callbacks, + unsigned index_callbacks_size, + unsigned index_options, + CXTranslationUnit TU) { + LOG_FUNC_SECTION { + *Log << TU; + } + + CXErrorCode result; + auto IndexTranslationUnitImpl = [=, &result]() { + result = clang_indexTranslationUnit_Impl( + idxAction, client_data, index_callbacks, index_callbacks_size, + index_options, TU); + }; + + llvm::CrashRecoveryContext CRC; + + if (!RunSafely(CRC, IndexTranslationUnitImpl)) { + fprintf(stderr, "libclang: crash detected during indexing TU\n"); + + return 1; + } + + return result; +} + +void clang_indexLoc_getFileLocation(CXIdxLoc location, + CXIdxClientFile *indexFile, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + if (indexFile) *indexFile = nullptr; + if (file) *file = nullptr; + if (line) *line = 0; + if (column) *column = 0; + if (offset) *offset = 0; + + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + if (!location.ptr_data[0] || Loc.isInvalid()) + return; + + CXIndexDataConsumer &DataConsumer = + *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]); + DataConsumer.translateLoc(Loc, indexFile, file, line, column, offset); +} + +CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc location) { + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + if (!location.ptr_data[0] || Loc.isInvalid()) + return clang_getNullLocation(); + + CXIndexDataConsumer &DataConsumer = + *static_cast<CXIndexDataConsumer*>(location.ptr_data[0]); + return cxloc::translateSourceLocation(DataConsumer.getASTContext(), Loc); +} diff --git a/gnu/llvm/clang/tools/libclang/libclang.exports b/gnu/llvm/clang/tools/libclang/libclang.exports new file mode 100644 index 00000000000..9408c02083f --- /dev/null +++ b/gnu/llvm/clang/tools/libclang/libclang.exports @@ -0,0 +1,383 @@ +clang_CXCursorSet_contains +clang_CXCursorSet_insert +clang_CXIndex_getGlobalOptions +clang_CXIndex_setGlobalOptions +clang_CXIndex_setInvocationEmissionPathOption +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 +clang_CXXMethod_isVirtual +clang_CXXRecord_isAbstract +clang_EnumDecl_isScoped +clang_Cursor_getArgument +clang_Cursor_getNumTemplateArguments +clang_Cursor_getTemplateArgumentKind +clang_Cursor_getTemplateArgumentType +clang_Cursor_getTemplateArgumentValue +clang_Cursor_getTemplateArgumentUnsignedValue +clang_Cursor_getBriefCommentText +clang_Cursor_getCommentRange +clang_Cursor_getCXXManglings +clang_Cursor_getMangling +clang_Cursor_getObjCManglings +clang_Cursor_getParsedComment +clang_Cursor_getRawCommentText +clang_Cursor_getNumArguments +clang_Cursor_getObjCDeclQualifiers +clang_Cursor_getObjCPropertyAttributes +clang_Cursor_getObjCPropertyGetterName +clang_Cursor_getObjCPropertySetterName +clang_Cursor_getObjCSelectorIndex +clang_Cursor_getOffsetOfField +clang_Cursor_getSpellingNameRange +clang_Cursor_getTranslationUnit +clang_Cursor_getReceiverType +clang_Cursor_isAnonymous +clang_Cursor_isAnonymousRecordDecl +clang_Cursor_isBitField +clang_Cursor_isDynamicCall +clang_Cursor_isExternalSymbol +clang_Cursor_isInlineNamespace +clang_Cursor_isNull +clang_Cursor_isObjCOptional +clang_Cursor_isVariadic +clang_Cursor_getModule +clang_Cursor_getStorageClass +clang_File_isEqual +clang_File_tryGetRealPathName +clang_Module_getASTFile +clang_Module_getParent +clang_Module_getName +clang_Module_getFullName +clang_Module_getNumTopLevelHeaders +clang_Module_getTopLevelHeader +clang_Module_isSystem +clang_IndexAction_create +clang_IndexAction_dispose +clang_Range_isNull +clang_Comment_getKind +clang_Comment_getNumChildren +clang_Comment_getChild +clang_Comment_isWhitespace +clang_InlineContentComment_hasTrailingNewline +clang_TextComment_getText +clang_InlineCommandComment_getCommandName +clang_InlineCommandComment_getRenderKind +clang_InlineCommandComment_getNumArgs +clang_InlineCommandComment_getArgText +clang_HTMLTagComment_getTagName +clang_HTMLStartTagComment_isSelfClosing +clang_HTMLStartTag_getNumAttrs +clang_HTMLStartTag_getAttrName +clang_HTMLStartTag_getAttrValue +clang_BlockCommandComment_getCommandName +clang_BlockCommandComment_getNumArgs +clang_BlockCommandComment_getArgText +clang_BlockCommandComment_getParagraph +clang_ParamCommandComment_getParamName +clang_ParamCommandComment_isParamIndexValid +clang_ParamCommandComment_getParamIndex +clang_ParamCommandComment_isDirectionExplicit +clang_ParamCommandComment_getDirection +clang_TParamCommandComment_getParamName +clang_TParamCommandComment_isParamPositionValid +clang_TParamCommandComment_getDepth +clang_TParamCommandComment_getIndex +clang_TargetInfo_dispose +clang_TargetInfo_getPointerWidth +clang_TargetInfo_getTriple +clang_Type_getAlignOf +clang_Type_getClassType +clang_Type_getSizeOf +clang_Type_getOffsetOf +clang_Type_getNumTemplateArguments +clang_Type_getTemplateArgumentAsType +clang_Type_getCXXRefQualifier +clang_Type_visitFields +clang_Type_getNamedType +clang_Type_isTransparentTagTypedef +clang_Type_getObjCObjectBaseType +clang_Type_getNumObjCProtocolRefs +clang_Type_getObjCProtocolDecl +clang_Type_getNumObjCTypeArgs +clang_Type_getObjCTypeArg +clang_Type_getModifiedType +clang_Type_getNullability +clang_VerbatimBlockLineComment_getText +clang_VerbatimLineComment_getText +clang_HTMLTagComment_getAsString +clang_FullComment_getAsHTML +clang_FullComment_getAsXML +clang_annotateTokens +clang_codeCompleteAt +clang_codeCompleteGetContainerKind +clang_codeCompleteGetContainerUSR +clang_codeCompleteGetContexts +clang_codeCompleteGetDiagnostic +clang_codeCompleteGetNumDiagnostics +clang_codeCompleteGetObjCSelector +clang_constructUSR_ObjCCategory +clang_constructUSR_ObjCClass +clang_constructUSR_ObjCIvar +clang_constructUSR_ObjCMethod +clang_constructUSR_ObjCProperty +clang_constructUSR_ObjCProtocol +clang_createCXCursorSet +clang_createIndex +clang_createTranslationUnit +clang_createTranslationUnit2 +clang_createTranslationUnitFromSourceFile +clang_defaultCodeCompleteOptions +clang_defaultDiagnosticDisplayOptions +clang_defaultEditingTranslationUnitOptions +clang_defaultReparseOptions +clang_defaultSaveOptions +clang_disposeCXCursorSet +clang_disposeCXTUResourceUsage +clang_disposeCodeCompleteResults +clang_disposeDiagnostic +clang_disposeDiagnosticSet +clang_disposeIndex +clang_disposeOverriddenCursors +clang_disposeCXPlatformAvailability +clang_disposeSourceRangeList +clang_disposeString +clang_disposeStringSet +clang_disposeTokens +clang_disposeTranslationUnit +clang_enableStackTraces +clang_equalCursors +clang_equalLocations +clang_equalRanges +clang_equalTypes +clang_executeOnThread +clang_findIncludesInFile +clang_findIncludesInFileWithBlock +clang_findReferencesInFile +clang_findReferencesInFileWithBlock +clang_formatDiagnostic +clang_free +clang_getAddressSpace +clang_getAllSkippedRanges +clang_getArgType +clang_getArrayElementType +clang_getArraySize +clang_getBuildSessionTimestamp +clang_getCString +clang_getCXTUResourceUsage +clang_getCXXAccessSpecifier +clang_getCanonicalCursor +clang_getCanonicalType +clang_getChildDiagnostics +clang_getClangVersion +clang_getCompletionAnnotation +clang_getCompletionAvailability +clang_getCompletionBriefComment +clang_getCompletionChunkCompletionString +clang_getCompletionChunkKind +clang_getCompletionChunkText +clang_getCompletionNumFixIts +clang_getCompletionFixIt +clang_getCompletionNumAnnotations +clang_getCompletionParent +clang_getCompletionPriority +clang_getCursor +clang_getCursorAvailability +clang_getCursorCompletionString +clang_getCursorDefinition +clang_getCursorDisplayName +clang_getCursorPrintingPolicy +clang_getCursorPrettyPrinted +clang_getCursorExtent +clang_getCursorExceptionSpecificationType +clang_getCursorKind +clang_getCursorKindSpelling +clang_getCursorLanguage +clang_getCursorLexicalParent +clang_getCursorLinkage +clang_getCursorLocation +clang_getCursorPlatformAvailability +clang_getCursorReferenceNameRange +clang_getCursorReferenced +clang_getCursorResultType +clang_getCursorSemanticParent +clang_getCursorSpelling +clang_getCursorTLSKind +clang_getCursorType +clang_getCursorUSR +clang_getCursorVisibility +clang_getDeclObjCTypeEncoding +clang_getDefinitionSpellingAndExtent +clang_getDiagnostic +clang_getDiagnosticCategory +clang_getDiagnosticCategoryName +clang_getDiagnosticCategoryText +clang_getDiagnosticFixIt +clang_getDiagnosticInSet +clang_getDiagnosticLocation +clang_getDiagnosticNumFixIts +clang_getDiagnosticNumRanges +clang_getDiagnosticOption +clang_getDiagnosticRange +clang_getDiagnosticSetFromTU +clang_getDiagnosticSeverity +clang_getDiagnosticSpelling +clang_getElementType +clang_getEnumConstantDeclUnsignedValue +clang_getEnumConstantDeclValue +clang_getEnumDeclIntegerType +clang_getExceptionSpecificationType +clang_getFieldDeclBitWidth +clang_getExpansionLocation +clang_getFile +clang_getFileContents +clang_getFileLocation +clang_getFileName +clang_getFileTime +clang_getFileUniqueID +clang_getFunctionTypeCallingConv +clang_getIBOutletCollectionType +clang_getIncludedFile +clang_getInclusions +clang_getInstantiationLocation +clang_getLocation +clang_getLocationForOffset +clang_getModuleForFile +clang_getNullCursor +clang_getNullLocation +clang_getNullRange +clang_getNumArgTypes +clang_getNumCompletionChunks +clang_getNumDiagnostics +clang_getNumDiagnosticsInSet +clang_getNumElements +clang_getNumOverloadedDecls +clang_getOverloadedDecl +clang_getOverriddenCursors +clang_getPointeeType +clang_getPresumedLocation +clang_getRange +clang_getRangeEnd +clang_getRangeStart +clang_getRemappings +clang_getRemappingsFromFileList +clang_getResultType +clang_getSkippedRanges +clang_getSpecializedCursorTemplate +clang_getSpellingLocation +clang_getTUResourceUsageName +clang_getTemplateCursorKind +clang_getToken +clang_getTokenExtent +clang_getTokenKind +clang_getTokenLocation +clang_getTokenSpelling +clang_getTranslationUnitCursor +clang_getTranslationUnitSpelling +clang_getTranslationUnitTargetInfo +clang_getTypeDeclaration +clang_getTypeKindSpelling +clang_getTypeSpelling +clang_getTypedefDeclUnderlyingType +clang_getTypedefName +clang_hashCursor +clang_indexLoc_getCXSourceLocation +clang_indexLoc_getFileLocation +clang_indexSourceFile +clang_indexSourceFileFullArgv +clang_indexTranslationUnit +clang_index_getCXXClassDeclInfo +clang_index_getClientContainer +clang_index_getClientEntity +clang_index_getIBOutletCollectionAttrInfo +clang_index_getObjCCategoryDeclInfo +clang_index_getObjCContainerDeclInfo +clang_index_getObjCInterfaceDeclInfo +clang_index_getObjCPropertyDeclInfo +clang_index_getObjCProtocolRefListInfo +clang_index_isEntityObjCContainerKind +clang_index_setClientContainer +clang_index_setClientEntity +clang_isAttribute +clang_isConstQualifiedType +clang_isCursorDefinition +clang_isDeclaration +clang_isInvalidDeclaration +clang_isExpression +clang_isFileMultipleIncludeGuarded +clang_isFunctionTypeVariadic +clang_isInvalid +clang_isPODType +clang_isPreprocessing +clang_isReference +clang_isRestrictQualifiedType +clang_isStatement +clang_isTranslationUnit +clang_isUnexposed +clang_isVirtualBase +clang_isVolatileQualifiedType +clang_loadDiagnostics +clang_Location_isInSystemHeader +clang_Location_isFromMainFile +clang_parseTranslationUnit +clang_parseTranslationUnit2 +clang_parseTranslationUnit2FullArgv +clang_remap_dispose +clang_remap_getFilenames +clang_remap_getNumFiles +clang_reparseTranslationUnit +clang_saveTranslationUnit +clang_suspendTranslationUnit +clang_sortCodeCompletionResults +clang_toggleCrashRecovery +clang_tokenize +clang_CompilationDatabase_fromDirectory +clang_CompilationDatabase_dispose +clang_CompilationDatabase_getCompileCommands +clang_CompilationDatabase_getAllCompileCommands +clang_CompileCommands_dispose +clang_CompileCommands_getSize +clang_CompileCommands_getCommand +clang_CompileCommand_getDirectory +clang_CompileCommand_getFilename +clang_CompileCommand_getMappedSourceContent +clang_CompileCommand_getMappedSourcePath +clang_CompileCommand_getNumArgs +clang_CompileCommand_getArg +clang_visitChildren +clang_visitChildrenWithBlock +clang_ModuleMapDescriptor_create +clang_ModuleMapDescriptor_dispose +clang_ModuleMapDescriptor_setFrameworkModuleName +clang_ModuleMapDescriptor_setUmbrellaHeader +clang_ModuleMapDescriptor_writeToBuffer +clang_VirtualFileOverlay_addFileMapping +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_getAsLongLong +clang_EvalResult_getAsUnsigned +clang_EvalResult_isUnsignedInt +clang_EvalResult_getAsDouble +clang_EvalResult_getAsStr +clang_EvalResult_dispose +clang_PrintingPolicy_getProperty +clang_PrintingPolicy_setProperty +clang_PrintingPolicy_dispose +clang_install_aborting_llvm_fatal_error_handler +clang_uninstall_llvm_fatal_error_handler diff --git a/gnu/llvm/clang/tools/scan-build-py/README.md b/gnu/llvm/clang/tools/scan-build-py/README.md new file mode 100644 index 00000000000..0f89b6fa43d --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/README.md @@ -0,0 +1,146 @@ +scan-build +========== + +A package designed to wrap a build so that all calls to gcc/clang are +intercepted and logged into a [compilation database][1] and/or piped to +the clang static analyzer. Includes intercept-build tool, which logs +the build, as well as scan-build tool, which logs the build and runs +the clang static analyzer on it. + +Portability +----------- + +Should be working on UNIX operating systems. + +- It has been tested on FreeBSD, GNU/Linux and OS X. +- Prepared to work on windows, but need help to make it. + + +Prerequisites +------------- + +1. **python** interpreter (version 2.7, 3.2, 3.3, 3.4, 3.5). + + +How to use +---------- + +To run the Clang static analyzer against a project goes like this: + + $ scan-build <your build command> + +To generate a compilation database file goes like this: + + $ intercept-build <your build command> + +To run the Clang static analyzer against a project with compilation database +goes like this: + + $ analyze-build + +Use `--help` to know more about the commands. + + +How to use the experimental Cross Translation Unit analysis +----------------------------------------------------------- + +To run the CTU analysis, a compilation database file has to be created: + + $ intercept-build <your build command> + +To run the Clang Static Analyzer against a compilation database +with CTU analysis enabled, execute: + + $ analyze-build --ctu + +For CTU analysis an additional (external definition) collection-phase is required. +For debugging purposes, it is possible to separately execute the collection +and the analysis phase. By doing this, the intermediate files used for +the analysis are kept on the disk in `./ctu-dir`. + + # Collect and store the data required by the CTU analysis + $ analyze-build --ctu-collect-only + + # Analyze using the previously collected data + $ analyze-build --ctu-analyze-only + +Use `--help` to get more information about the commands. + + +Limitations +----------- + +Generally speaking, the `intercept-build` and `analyze-build` tools together +does the same job as `scan-build` does. So, you can expect the same output +from this line as simple `scan-build` would do: + + $ intercept-build <your build command> && analyze-build + +The major difference is how and when the analyzer is run. The `scan-build` +tool has three distinct model to run the analyzer: + +1. Use compiler wrappers to make actions. + The compiler wrappers does run the real compiler and the analyzer. + This is the default behaviour, can be enforced with `--override-compiler` + flag. + +2. Use special library to intercept compiler calls during the build process. + The analyzer run against each modules after the build finished. + Use `--intercept-first` flag to get this model. + +3. Use compiler wrappers to intercept compiler calls during the build process. + The analyzer run against each modules after the build finished. + Use `--intercept-first` and `--override-compiler` flags together to get + this model. + +The 1. and 3. are using compiler wrappers, which works only if the build +process respects the `CC` and `CXX` environment variables. (Some build +process can override these variable as command line parameter only. This case +you need to pass the compiler wrappers manually. eg.: `intercept-build +--override-compiler make CC=intercept-cc CXX=intercept-c++ all` where the +original build command would have been `make all` only.) + +The 1. runs the analyzer right after the real compilation. So, if the build +process removes removes intermediate modules (generated sources) the analyzer +output still kept. + +The 2. and 3. generate the compilation database first, and filters out those +modules which are not exists. So, it's suitable for incremental analysis during +the development. + +The 2. mode is available only on FreeBSD and Linux. Where library preload +is available from the dynamic loader. Not supported on OS X (unless System +Integrity Protection feature is turned off). + +`intercept-build` command uses only the 2. and 3. mode to generate the +compilation database. `analyze-build` does only run the analyzer against the +captured compiler calls. + + +Known problems +-------------- + +Because it uses `LD_PRELOAD` or `DYLD_INSERT_LIBRARIES` environment variables, +it does not append to it, but overrides it. So builds which are using these +variables might not work. (I don't know any build tool which does that, but +please let me know if you do.) + + +Problem reports +--------------- + +If you find a bug in this documentation or elsewhere in the program or would +like to propose an improvement, please use the project's [issue tracker][3]. +Please describing the bug and where you found it. If you have a suggestion +how to fix it, include that as well. Patches are also welcome. + + +License +------- + +The project is licensed under Apache-2.0 with LLVM exceptions. +See LICENSE.TXT for details. + + [1]: http://clang.llvm.org/docs/JSONCompilationDatabase.html + [2]: https://pypi.python.org/pypi/scan-build + [3]: https://llvm.org/bugs/enter_bug.cgi?product=clang diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build new file mode 100755 index 00000000000..6c285874a20 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-build @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import multiprocessing +multiprocessing.freeze_support() + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_build +sys.exit(analyze_build()) diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/analyze-c++ b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-c++ new file mode 100755 index 00000000000..564e2abf554 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-c++ @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_compiler_wrapper +sys.exit(analyze_compiler_wrapper()) diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/analyze-cc b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-cc new file mode 100755 index 00000000000..564e2abf554 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/analyze-cc @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import analyze_compiler_wrapper +sys.exit(analyze_compiler_wrapper()) diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build new file mode 100755 index 00000000000..23f5104782c --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-build @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import multiprocessing +multiprocessing.freeze_support() + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.intercept import intercept_build +sys.exit(intercept_build()) diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/intercept-c++ b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-c++ new file mode 100755 index 00000000000..4230c8035d2 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-c++ @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.intercept import intercept_compiler_wrapper +sys.exit(intercept_compiler_wrapper()) diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/intercept-cc b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-cc new file mode 100755 index 00000000000..4230c8035d2 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/intercept-cc @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.intercept import intercept_compiler_wrapper +sys.exit(intercept_compiler_wrapper()) diff --git a/gnu/llvm/clang/tools/scan-build-py/bin/scan-build b/gnu/llvm/clang/tools/scan-build-py/bin/scan-build new file mode 100755 index 00000000000..156da064a2b --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/bin/scan-build @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import multiprocessing +multiprocessing.freeze_support() + +import sys +import os.path +this_dir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.dirname(this_dir)) + +from libscanbuild.analyze import scan_build +sys.exit(scan_build()) diff --git a/gnu/llvm/clang/tools/scan-build-py/libear/__init__.py b/gnu/llvm/clang/tools/scan-build-py/libear/__init__.py new file mode 100644 index 00000000000..0dfe8c11eba --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libear/__init__.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module compiles the intercept library. """ + +import sys +import os +import os.path +import re +import tempfile +import shutil +import contextlib +import logging + +__all__ = ['build_libear'] + + +def build_libear(compiler, dst_dir): + """ Returns the full path to the 'libear' library. """ + + try: + src_dir = os.path.dirname(os.path.realpath(__file__)) + toolset = make_toolset(src_dir) + toolset.set_compiler(compiler) + toolset.set_language_standard('c99') + toolset.add_definitions(['-D_GNU_SOURCE']) + + configure = do_configure(toolset) + configure.check_function_exists('execve', 'HAVE_EXECVE') + configure.check_function_exists('execv', 'HAVE_EXECV') + configure.check_function_exists('execvpe', 'HAVE_EXECVPE') + configure.check_function_exists('execvp', 'HAVE_EXECVP') + configure.check_function_exists('execvP', 'HAVE_EXECVP2') + configure.check_function_exists('exect', 'HAVE_EXECT') + configure.check_function_exists('execl', 'HAVE_EXECL') + configure.check_function_exists('execlp', 'HAVE_EXECLP') + configure.check_function_exists('execle', 'HAVE_EXECLE') + configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN') + configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP') + configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h', + 'HAVE_NSGETENVIRON') + configure.write_by_template( + os.path.join(src_dir, 'config.h.in'), + os.path.join(dst_dir, 'config.h')) + + target = create_shared_library('ear', toolset) + target.add_include(dst_dir) + target.add_sources('ear.c') + target.link_against(toolset.dl_libraries()) + target.link_against(['pthread']) + target.build_release(dst_dir) + + return os.path.join(dst_dir, target.name) + + except Exception: + logging.info("Could not build interception library.", exc_info=True) + return None + + +def execute(cmd, *args, **kwargs): + """ Make subprocess execution silent. """ + + import subprocess + kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) + return subprocess.check_call(cmd, *args, **kwargs) + + +@contextlib.contextmanager +def TemporaryDirectory(**kwargs): + name = tempfile.mkdtemp(**kwargs) + try: + yield name + finally: + shutil.rmtree(name) + + +class Toolset(object): + """ Abstract class to represent different toolset. """ + + def __init__(self, src_dir): + self.src_dir = src_dir + self.compiler = None + self.c_flags = [] + + def set_compiler(self, compiler): + """ part of public interface """ + self.compiler = compiler + + def set_language_standard(self, standard): + """ part of public interface """ + self.c_flags.append('-std=' + standard) + + def add_definitions(self, defines): + """ part of public interface """ + self.c_flags.extend(defines) + + def dl_libraries(self): + raise NotImplementedError() + + def shared_library_name(self, name): + raise NotImplementedError() + + def shared_library_c_flags(self, release): + extra = ['-DNDEBUG', '-O3'] if release else [] + return extra + ['-fPIC'] + self.c_flags + + def shared_library_ld_flags(self, release, name): + raise NotImplementedError() + + +class DarwinToolset(Toolset): + def __init__(self, src_dir): + Toolset.__init__(self, src_dir) + + def dl_libraries(self): + return [] + + def shared_library_name(self, name): + return 'lib' + name + '.dylib' + + def shared_library_ld_flags(self, release, name): + extra = ['-dead_strip'] if release else [] + return extra + ['-dynamiclib', '-install_name', '@rpath/' + name] + + +class UnixToolset(Toolset): + def __init__(self, src_dir): + Toolset.__init__(self, src_dir) + + def dl_libraries(self): + return [] + + def shared_library_name(self, name): + return 'lib' + name + '.so' + + def shared_library_ld_flags(self, release, name): + extra = [] if release else [] + return extra + ['-shared', '-Wl,-soname,' + name] + + +class LinuxToolset(UnixToolset): + def __init__(self, src_dir): + UnixToolset.__init__(self, src_dir) + + def dl_libraries(self): + return ['dl'] + + +def make_toolset(src_dir): + platform = sys.platform + if platform in {'win32', 'cygwin'}: + raise RuntimeError('not implemented on this platform') + elif platform == 'darwin': + return DarwinToolset(src_dir) + elif platform in {'linux', 'linux2'}: + return LinuxToolset(src_dir) + else: + return UnixToolset(src_dir) + + +class Configure(object): + def __init__(self, toolset): + self.ctx = toolset + self.results = {'APPLE': sys.platform == 'darwin'} + + def _try_to_compile_and_link(self, source): + try: + with TemporaryDirectory() as work_dir: + src_file = 'check.c' + with open(os.path.join(work_dir, src_file), 'w') as handle: + handle.write(source) + + execute([self.ctx.compiler, src_file] + self.ctx.c_flags, + cwd=work_dir) + return True + except Exception: + return False + + def check_function_exists(self, function, name): + template = "int FUNCTION(); int main() { return FUNCTION(); }" + source = template.replace("FUNCTION", function) + + logging.debug('Checking function %s', function) + found = self._try_to_compile_and_link(source) + logging.debug('Checking function %s -- %s', function, + 'found' if found else 'not found') + self.results.update({name: found}) + + def check_symbol_exists(self, symbol, include, name): + template = """#include <INCLUDE> + int main() { return ((int*)(&SYMBOL))[0]; }""" + source = template.replace('INCLUDE', include).replace("SYMBOL", symbol) + + logging.debug('Checking symbol %s', symbol) + found = self._try_to_compile_and_link(source) + logging.debug('Checking symbol %s -- %s', symbol, + 'found' if found else 'not found') + self.results.update({name: found}) + + def write_by_template(self, template, output): + def transform(line, definitions): + + pattern = re.compile(r'^#cmakedefine\s+(\S+)') + m = pattern.match(line) + if m: + key = m.group(1) + if key not in definitions or not definitions[key]: + return '/* #undef {0} */{1}'.format(key, os.linesep) + else: + return '#define {0}{1}'.format(key, os.linesep) + return line + + with open(template, 'r') as src_handle: + logging.debug('Writing config to %s', output) + with open(output, 'w') as dst_handle: + for line in src_handle: + dst_handle.write(transform(line, self.results)) + + +def do_configure(toolset): + return Configure(toolset) + + +class SharedLibrary(object): + def __init__(self, name, toolset): + self.name = toolset.shared_library_name(name) + self.ctx = toolset + self.inc = [] + self.src = [] + self.lib = [] + + def add_include(self, directory): + self.inc.extend(['-I', directory]) + + def add_sources(self, source): + self.src.append(source) + + def link_against(self, libraries): + self.lib.extend(['-l' + lib for lib in libraries]) + + def build_release(self, directory): + for src in self.src: + logging.debug('Compiling %s', src) + execute( + [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src), + '-o', src + '.o'] + self.inc + + self.ctx.shared_library_c_flags(True), + cwd=directory) + logging.debug('Linking %s', self.name) + execute( + [self.ctx.compiler] + [src + '.o' for src in self.src] + + ['-o', self.name] + self.lib + + self.ctx.shared_library_ld_flags(True, self.name), + cwd=directory) + + +def create_shared_library(name, toolset): + return SharedLibrary(name, toolset) diff --git a/gnu/llvm/clang/tools/scan-build-py/libear/config.h.in b/gnu/llvm/clang/tools/scan-build-py/libear/config.h.in new file mode 100644 index 00000000000..6ca1b9580b4 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libear/config.h.in @@ -0,0 +1,22 @@ +/* -*- coding: utf-8 -*- +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#pragma once + +#cmakedefine HAVE_EXECVE +#cmakedefine HAVE_EXECV +#cmakedefine HAVE_EXECVPE +#cmakedefine HAVE_EXECVP +#cmakedefine HAVE_EXECVP2 +#cmakedefine HAVE_EXECT +#cmakedefine HAVE_EXECL +#cmakedefine HAVE_EXECLP +#cmakedefine HAVE_EXECLE +#cmakedefine HAVE_POSIX_SPAWN +#cmakedefine HAVE_POSIX_SPAWNP +#cmakedefine HAVE_NSGETENVIRON + +#cmakedefine APPLE diff --git a/gnu/llvm/clang/tools/scan-build-py/libear/ear.c b/gnu/llvm/clang/tools/scan-build-py/libear/ear.c new file mode 100644 index 00000000000..21c57684745 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libear/ear.c @@ -0,0 +1,604 @@ +/* -*- coding: utf-8 -*- +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +/** + * This file implements a shared library. This library can be pre-loaded by + * the dynamic linker of the Operating System (OS). It implements a few function + * related to process creation. By pre-load this library the executed process + * uses these functions instead of those from the standard library. + * + * The idea here is to inject a logic before call the real methods. The logic is + * to dump the call into a file. To call the real method this library is doing + * the job of the dynamic linker. + * + * The only input for the log writing is about the destination directory. + * This is passed as environment variable. + */ + +#include "config.h" + +#include <stddef.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <dlfcn.h> +#include <pthread.h> + +#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP +#include <spawn.h> +#endif + +#if defined HAVE_NSGETENVIRON +# include <crt_externs.h> +#else +extern char **environ; +#endif + +#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR" +#ifdef APPLE +# define ENV_FLAT "DYLD_FORCE_FLAT_NAMESPACE" +# define ENV_PRELOAD "DYLD_INSERT_LIBRARIES" +# define ENV_SIZE 3 +#else +# define ENV_PRELOAD "LD_PRELOAD" +# define ENV_SIZE 2 +#endif + +#define DLSYM(TYPE_, VAR_, SYMBOL_) \ + union { \ + void *from; \ + TYPE_ to; \ + } cast; \ + if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) { \ + perror("bear: dlsym"); \ + exit(EXIT_FAILURE); \ + } \ + TYPE_ const VAR_ = cast.to; + + +typedef char const * bear_env_t[ENV_SIZE]; + +static int bear_capture_env_t(bear_env_t *env); +static int bear_reset_env_t(bear_env_t *env); +static void bear_release_env_t(bear_env_t *env); +static char const **bear_update_environment(char *const envp[], bear_env_t *env); +static char const **bear_update_environ(char const **in, char const *key, char const *value); +static char **bear_get_environment(); +static void bear_report_call(char const *fun, char const *const argv[]); +static char const **bear_strings_build(char const *arg, va_list *ap); +static char const **bear_strings_copy(char const **const in); +static char const **bear_strings_append(char const **in, char const *e); +static size_t bear_strings_length(char const *const *in); +static void bear_strings_release(char const **); + + +static bear_env_t env_names = + { ENV_OUTPUT + , ENV_PRELOAD +#ifdef ENV_FLAT + , ENV_FLAT +#endif + }; + +static bear_env_t initial_env = + { 0 + , 0 +#ifdef ENV_FLAT + , 0 +#endif + }; + +static int initialized = 0; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static void on_load(void) __attribute__((constructor)); +static void on_unload(void) __attribute__((destructor)); + + +#ifdef HAVE_EXECVE +static int call_execve(const char *path, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_EXECVP +static int call_execvp(const char *file, char *const argv[]); +#endif +#ifdef HAVE_EXECVPE +static int call_execvpe(const char *file, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_EXECVP2 +static int call_execvP(const char *file, const char *search_path, + char *const argv[]); +#endif +#ifdef HAVE_EXECT +static int call_exect(const char *path, char *const argv[], + char *const envp[]); +#endif +#ifdef HAVE_POSIX_SPAWN +static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]); +#endif +#ifdef HAVE_POSIX_SPAWNP +static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]); +#endif + + +/* Initialization method to Captures the relevant environment variables. + */ + +static void on_load(void) { + pthread_mutex_lock(&mutex); + if (!initialized) + initialized = bear_capture_env_t(&initial_env); + pthread_mutex_unlock(&mutex); +} + +static void on_unload(void) { + pthread_mutex_lock(&mutex); + bear_release_env_t(&initial_env); + initialized = 0; + pthread_mutex_unlock(&mutex); +} + + +/* These are the methods we are try to hijack. + */ + +#ifdef HAVE_EXECVE +int execve(const char *path, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execve(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECV +#ifndef HAVE_EXECVE +#error can not implement execv without execve +#endif +int execv(const char *path, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + char * const * envp = bear_get_environment(); + return call_execve(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECVPE +int execvpe(const char *file, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvpe(file, argv, envp); +} +#endif + +#ifdef HAVE_EXECVP +int execvp(const char *file, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvp(file, argv); +} +#endif + +#ifdef HAVE_EXECVP2 +int execvP(const char *file, const char *search_path, char *const argv[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_execvP(file, search_path, argv); +} +#endif + +#ifdef HAVE_EXECT +int exect(const char *path, char *const argv[], char *const envp[]) { + bear_report_call(__func__, (char const *const *)argv); + return call_exect(path, argv, envp); +} +#endif + +#ifdef HAVE_EXECL +# ifndef HAVE_EXECVE +# error can not implement execl without execve +# endif +int execl(const char *path, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + char * const * envp = bear_get_environment(); + int const result = call_execve(path, (char *const *)argv, envp); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_EXECLP +# ifndef HAVE_EXECVP +# error can not implement execlp without execvp +# endif +int execlp(const char *file, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + int const result = call_execvp(file, (char *const *)argv); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_EXECLE +# ifndef HAVE_EXECVE +# error can not implement execle without execve +# endif +// int execle(const char *path, const char *arg, ..., char * const envp[]); +int execle(const char *path, const char *arg, ...) { + va_list args; + va_start(args, arg); + char const **argv = bear_strings_build(arg, &args); + char const **envp = va_arg(args, char const **); + va_end(args); + + bear_report_call(__func__, (char const *const *)argv); + int const result = + call_execve(path, (char *const *)argv, (char *const *)envp); + + bear_strings_release(argv); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWN +int posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], char *const envp[restrict]) { + bear_report_call(__func__, (char const *const *)argv); + return call_posix_spawn(pid, path, file_actions, attrp, argv, envp); +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +int posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], char *const envp[restrict]) { + bear_report_call(__func__, (char const *const *)argv); + return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp); +} +#endif + +/* These are the methods which forward the call to the standard implementation. + */ + +#ifdef HAVE_EXECVE +static int call_execve(const char *path, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "execve"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(path, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_EXECVPE +static int call_execvpe(const char *file, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "execvpe"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(file, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_EXECVP +static int call_execvp(const char *file, char *const argv[]) { + typedef int (*func)(const char *file, char *const argv[]); + + DLSYM(func, fp, "execvp"); + + bear_env_t current_env; + bear_capture_env_t(¤t_env); + bear_reset_env_t(&initial_env); + int const result = (*fp)(file, argv); + bear_reset_env_t(¤t_env); + bear_release_env_t(¤t_env); + + return result; +} +#endif + +#ifdef HAVE_EXECVP2 +static int call_execvP(const char *file, const char *search_path, + char *const argv[]) { + typedef int (*func)(const char *, const char *, char *const *); + + DLSYM(func, fp, "execvP"); + + bear_env_t current_env; + bear_capture_env_t(¤t_env); + bear_reset_env_t(&initial_env); + int const result = (*fp)(file, search_path, argv); + bear_reset_env_t(¤t_env); + bear_release_env_t(¤t_env); + + return result; +} +#endif + +#ifdef HAVE_EXECT +static int call_exect(const char *path, char *const argv[], + char *const envp[]) { + typedef int (*func)(const char *, char *const *, char *const *); + + DLSYM(func, fp, "exect"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = (*fp)(path, argv, (char *const *)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWN +static int call_posix_spawn(pid_t *restrict pid, const char *restrict path, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]) { + typedef int (*func)(pid_t *restrict, const char *restrict, + const posix_spawn_file_actions_t *, + const posix_spawnattr_t *restrict, + char *const *restrict, char *const *restrict); + + DLSYM(func, fp, "posix_spawn"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = + (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file, + const posix_spawn_file_actions_t *file_actions, + const posix_spawnattr_t *restrict attrp, + char *const argv[restrict], + char *const envp[restrict]) { + typedef int (*func)(pid_t *restrict, const char *restrict, + const posix_spawn_file_actions_t *, + const posix_spawnattr_t *restrict, + char *const *restrict, char *const *restrict); + + DLSYM(func, fp, "posix_spawnp"); + + char const **const menvp = bear_update_environment(envp, &initial_env); + int const result = + (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp); + bear_strings_release(menvp); + return result; +} +#endif + +/* this method is to write log about the process creation. */ + +static void bear_report_call(char const *fun, char const *const argv[]) { + static int const GS = 0x1d; + static int const RS = 0x1e; + static int const US = 0x1f; + + if (!initialized) + return; + + pthread_mutex_lock(&mutex); + const char *cwd = getcwd(NULL, 0); + if (0 == cwd) { + perror("bear: getcwd"); + exit(EXIT_FAILURE); + } + char const * const out_dir = initial_env[0]; + size_t const path_max_length = strlen(out_dir) + 32; + char filename[path_max_length]; + if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) { + perror("bear: snprintf"); + exit(EXIT_FAILURE); + } + FILE * fd = fopen(filename, "a+"); + if (0 == fd) { + perror("bear: fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "%d%c", getpid(), RS); + fprintf(fd, "%d%c", getppid(), RS); + fprintf(fd, "%s%c", fun, RS); + fprintf(fd, "%s%c", cwd, RS); + size_t const argc = bear_strings_length(argv); + for (size_t it = 0; it < argc; ++it) { + fprintf(fd, "%s%c", argv[it], US); + } + fprintf(fd, "%c", GS); + if (fclose(fd)) { + perror("bear: fclose"); + exit(EXIT_FAILURE); + } + free((void *)cwd); + pthread_mutex_unlock(&mutex); +} + +/* update environment assure that chilren processes will copy the desired + * behaviour */ + +static int bear_capture_env_t(bear_env_t *env) { + int status = 1; + for (size_t it = 0; it < ENV_SIZE; ++it) { + char const * const env_value = getenv(env_names[it]); + char const * const env_copy = (env_value) ? strdup(env_value) : env_value; + (*env)[it] = env_copy; + status &= (env_copy) ? 1 : 0; + } + return status; +} + +static int bear_reset_env_t(bear_env_t *env) { + int status = 1; + for (size_t it = 0; it < ENV_SIZE; ++it) { + if ((*env)[it]) { + setenv(env_names[it], (*env)[it], 1); + } else { + unsetenv(env_names[it]); + } + } + return status; +} + +static void bear_release_env_t(bear_env_t *env) { + for (size_t it = 0; it < ENV_SIZE; ++it) { + free((void *)(*env)[it]); + (*env)[it] = 0; + } +} + +static char const **bear_update_environment(char *const envp[], bear_env_t *env) { + char const **result = bear_strings_copy((char const **)envp); + for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it) + result = bear_update_environ(result, env_names[it], (*env)[it]); + return result; +} + +static char const **bear_update_environ(char const *envs[], char const *key, char const * const value) { + // find the key if it's there + size_t const key_length = strlen(key); + char const **it = envs; + for (; (it) && (*it); ++it) { + if ((0 == strncmp(*it, key, key_length)) && + (strlen(*it) > key_length) && ('=' == (*it)[key_length])) + break; + } + // allocate a environment entry + size_t const value_length = strlen(value); + size_t const env_length = key_length + value_length + 2; + char *env = malloc(env_length); + if (0 == env) { + perror("bear: malloc [in env_update]"); + exit(EXIT_FAILURE); + } + if (-1 == snprintf(env, env_length, "%s=%s", key, value)) { + perror("bear: snprintf"); + exit(EXIT_FAILURE); + } + // replace or append the environment entry + if (it && *it) { + free((void *)*it); + *it = env; + return envs; + } + return bear_strings_append(envs, env); +} + +static char **bear_get_environment() { +#if defined HAVE_NSGETENVIRON + return *_NSGetEnviron(); +#else + return environ; +#endif +} + +/* util methods to deal with string arrays. environment and process arguments + * are both represented as string arrays. */ + +static char const **bear_strings_build(char const *const arg, va_list *args) { + char const **result = 0; + size_t size = 0; + for (char const *it = arg; it; it = va_arg(*args, char const *)) { + result = realloc(result, (size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + char const *copy = strdup(it); + if (0 == copy) { + perror("bear: strdup"); + exit(EXIT_FAILURE); + } + result[size++] = copy; + } + result = realloc(result, (size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + result[size++] = 0; + + return result; +} + +static char const **bear_strings_copy(char const **const in) { + size_t const size = bear_strings_length(in); + + char const **const result = malloc((size + 1) * sizeof(char const *)); + if (0 == result) { + perror("bear: malloc"); + exit(EXIT_FAILURE); + } + + char const **out_it = result; + for (char const *const *in_it = in; (in_it) && (*in_it); + ++in_it, ++out_it) { + *out_it = strdup(*in_it); + if (0 == *out_it) { + perror("bear: strdup"); + exit(EXIT_FAILURE); + } + } + *out_it = 0; + return result; +} + +static char const **bear_strings_append(char const **const in, + char const *const e) { + size_t size = bear_strings_length(in); + char const **result = realloc(in, (size + 2) * sizeof(char const *)); + if (0 == result) { + perror("bear: realloc"); + exit(EXIT_FAILURE); + } + result[size++] = e; + result[size++] = 0; + return result; +} + +static size_t bear_strings_length(char const *const *const in) { + size_t result = 0; + for (char const *const *it = in; (it) && (*it); ++it) + ++result; + return result; +} + +static void bear_strings_release(char const **in) { + for (char const *const *it = in; (it) && (*it); ++it) { + free((void *)*it); + } + free((void *)in); +} diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/__init__.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/__init__.py new file mode 100644 index 00000000000..2e432816509 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/__init__.py @@ -0,0 +1,207 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module is a collection of methods commonly used in this project. """ +import collections +import functools +import json +import logging +import os +import os.path +import re +import shlex +import subprocess +import sys + +ENVIRONMENT_KEY = 'INTERCEPT_BUILD' + +Execution = collections.namedtuple('Execution', ['pid', 'cwd', 'cmd']) + +CtuConfig = collections.namedtuple('CtuConfig', ['collect', 'analyze', 'dir', + 'extdef_map_cmd']) + + +def duplicate_check(method): + """ Predicate to detect duplicated entries. + + Unique hash method can be use to detect duplicates. Entries are + represented as dictionaries, which has no default hash method. + This implementation uses a set datatype to store the unique hash values. + + This method returns a method which can detect the duplicate values. """ + + def predicate(entry): + entry_hash = predicate.unique(entry) + if entry_hash not in predicate.state: + predicate.state.add(entry_hash) + return False + return True + + predicate.unique = method + predicate.state = set() + return predicate + + +def run_build(command, *args, **kwargs): + """ Run and report build command execution + + :param command: array of tokens + :return: exit code of the process + """ + environment = kwargs.get('env', os.environ) + logging.debug('run build %s, in environment: %s', command, environment) + exit_code = subprocess.call(command, *args, **kwargs) + logging.debug('build finished with exit code: %d', exit_code) + return exit_code + + +def run_command(command, cwd=None): + """ Run a given command and report the execution. + + :param command: array of tokens + :param cwd: the working directory where the command will be executed + :return: output of the command + """ + def decode_when_needed(result): + """ check_output returns bytes or string depend on python version """ + return result.decode('utf-8') if isinstance(result, bytes) else result + + try: + directory = os.path.abspath(cwd) if cwd else os.getcwd() + logging.debug('exec command %s in %s', command, directory) + output = subprocess.check_output(command, + cwd=directory, + stderr=subprocess.STDOUT) + return decode_when_needed(output).splitlines() + except subprocess.CalledProcessError as ex: + ex.output = decode_when_needed(ex.output).splitlines() + raise ex + + +def reconfigure_logging(verbose_level): + """ Reconfigure logging level and format based on the verbose flag. + + :param verbose_level: number of `-v` flags received by the command + :return: no return value + """ + # Exit when nothing to do. + if verbose_level == 0: + return + + root = logging.getLogger() + # Tune logging level. + level = logging.WARNING - min(logging.WARNING, (10 * verbose_level)) + root.setLevel(level) + # Be verbose with messages. + if verbose_level <= 3: + fmt_string = '%(name)s: %(levelname)s: %(message)s' + else: + fmt_string = '%(name)s: %(levelname)s: %(funcName)s: %(message)s' + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(logging.Formatter(fmt=fmt_string)) + root.handlers = [handler] + + +def command_entry_point(function): + """ Decorator for command entry methods. + + The decorator initialize/shutdown logging and guard on programming + errors (catch exceptions). + + The decorated method can have arbitrary parameters, the return value will + be the exit code of the process. """ + + @functools.wraps(function) + def wrapper(*args, **kwargs): + """ Do housekeeping tasks and execute the wrapped method. """ + + try: + logging.basicConfig(format='%(name)s: %(message)s', + level=logging.WARNING, + stream=sys.stdout) + # This hack to get the executable name as %(name). + logging.getLogger().name = os.path.basename(sys.argv[0]) + return function(*args, **kwargs) + except KeyboardInterrupt: + logging.warning('Keyboard interrupt') + return 130 # Signal received exit code for bash. + except Exception: + logging.exception('Internal error.') + if logging.getLogger().isEnabledFor(logging.DEBUG): + logging.error("Please report this bug and attach the output " + "to the bug report") + else: + logging.error("Please run this command again and turn on " + "verbose mode (add '-vvvv' as argument).") + return 64 # Some non used exit code for internal errors. + finally: + logging.shutdown() + + return wrapper + + +def compiler_wrapper(function): + """ Implements compiler wrapper base functionality. + + A compiler wrapper executes the real compiler, then implement some + functionality, then returns with the real compiler exit code. + + :param function: the extra functionality what the wrapper want to + do on top of the compiler call. If it throws exception, it will be + caught and logged. + :return: the exit code of the real compiler. + + The :param function: will receive the following arguments: + + :param result: the exit code of the compilation. + :param execution: the command executed by the wrapper. """ + + def is_cxx_compiler(): + """ Find out was it a C++ compiler call. Compiler wrapper names + contain the compiler type. C++ compiler wrappers ends with `c++`, + but might have `.exe` extension on windows. """ + + wrapper_command = os.path.basename(sys.argv[0]) + return re.match(r'(.+)c\+\+(.*)', wrapper_command) + + def run_compiler(executable): + """ Execute compilation with the real compiler. """ + + command = executable + sys.argv[1:] + logging.debug('compilation: %s', command) + result = subprocess.call(command) + logging.debug('compilation exit code: %d', result) + return result + + # Get relevant parameters from environment. + parameters = json.loads(os.environ[ENVIRONMENT_KEY]) + reconfigure_logging(parameters['verbose']) + # Execute the requested compilation. Do crash if anything goes wrong. + cxx = is_cxx_compiler() + compiler = parameters['cxx'] if cxx else parameters['cc'] + result = run_compiler(compiler) + # Call the wrapped method and ignore it's return value. + try: + call = Execution( + pid=os.getpid(), + cwd=os.getcwd(), + cmd=['c++' if cxx else 'cc'] + sys.argv[1:]) + function(result, call) + except: + logging.exception('Compiler wrapper failed complete.') + finally: + # Always return the real compiler exit code. + return result + + +def wrapper_environment(args): + """ Set up environment for interpose compiler wrapper.""" + + return { + ENVIRONMENT_KEY: json.dumps({ + 'verbose': args.verbose, + 'cc': shlex.split(args.cc), + 'cxx': shlex.split(args.cxx) + }) + } diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/analyze.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/analyze.py new file mode 100644 index 00000000000..ffdf7f7c7d6 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/analyze.py @@ -0,0 +1,792 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module implements the 'scan-build' command API. + +To run the static analyzer against a build is done in multiple steps: + + -- Intercept: capture the compilation command during the build, + -- Analyze: run the analyzer against the captured commands, + -- Report: create a cover report from the analyzer outputs. """ + +import re +import os +import os.path +import json +import logging +import multiprocessing +import tempfile +import functools +import subprocess +import contextlib +import datetime +import shutil +import glob +from collections import defaultdict + +from libscanbuild import command_entry_point, compiler_wrapper, \ + wrapper_environment, run_build, run_command, CtuConfig +from libscanbuild.arguments import parse_args_for_scan_build, \ + parse_args_for_analyze_build +from libscanbuild.intercept import capture +from libscanbuild.report import document +from libscanbuild.compilation import split_command, classify_source, \ + compiler_language +from libscanbuild.clang import get_version, get_arguments, get_triple_arch, \ + ClangErrorException +from libscanbuild.shell import decode + +__all__ = ['scan_build', 'analyze_build', 'analyze_compiler_wrapper'] + +COMPILER_WRAPPER_CC = 'analyze-cc' +COMPILER_WRAPPER_CXX = 'analyze-c++' + +CTU_EXTDEF_MAP_FILENAME = 'externalDefMap.txt' +CTU_TEMP_DEFMAP_FOLDER = 'tmpExternalDefMaps' + + +@command_entry_point +def scan_build(): + """ Entry point for scan-build command. """ + + args = parse_args_for_scan_build() + # will re-assign the report directory as new output + with report_directory(args.output, args.keep_empty) as args.output: + # Run against a build command. there are cases, when analyzer run + # is not required. But we need to set up everything for the + # wrappers, because 'configure' needs to capture the CC/CXX values + # for the Makefile. + if args.intercept_first: + # Run build command with intercept module. + exit_code = capture(args) + # Run the analyzer against the captured commands. + if need_analyzer(args.build): + govern_analyzer_runs(args) + else: + # Run build command and analyzer with compiler wrappers. + environment = setup_environment(args) + exit_code = run_build(args.build, env=environment) + # Cover report generation and bug counting. + number_of_bugs = document(args) + # Set exit status as it was requested. + return number_of_bugs if args.status_bugs else exit_code + + +@command_entry_point +def analyze_build(): + """ Entry point for analyze-build command. """ + + args = parse_args_for_analyze_build() + # will re-assign the report directory as new output + with report_directory(args.output, args.keep_empty) as args.output: + # Run the analyzer against a compilation db. + govern_analyzer_runs(args) + # Cover report generation and bug counting. + number_of_bugs = document(args) + # Set exit status as it was requested. + return number_of_bugs if args.status_bugs else 0 + + +def need_analyzer(args): + """ Check the intent of the build command. + + When static analyzer run against project configure step, it should be + silent and no need to run the analyzer or generate report. + + To run `scan-build` against the configure step might be necessary, + when compiler wrappers are used. That's the moment when build setup + check the compiler and capture the location for the build process. """ + + return len(args) and not re.search(r'configure|autogen', args[0]) + + +def prefix_with(constant, pieces): + """ From a sequence create another sequence where every second element + is from the original sequence and the odd elements are the prefix. + + eg.: prefix_with(0, [1,2,3]) creates [0, 1, 0, 2, 0, 3] """ + + return [elem for piece in pieces for elem in [constant, piece]] + + +def get_ctu_config_from_args(args): + """ CTU configuration is created from the chosen phases and dir. """ + + return ( + CtuConfig(collect=args.ctu_phases.collect, + analyze=args.ctu_phases.analyze, + dir=args.ctu_dir, + extdef_map_cmd=args.extdef_map_cmd) + if hasattr(args, 'ctu_phases') and hasattr(args.ctu_phases, 'dir') + else CtuConfig(collect=False, analyze=False, dir='', extdef_map_cmd='')) + + +def get_ctu_config_from_json(ctu_conf_json): + """ CTU configuration is created from the chosen phases and dir. """ + + ctu_config = json.loads(ctu_conf_json) + # Recover namedtuple from json when coming from analyze-cc or analyze-c++ + return CtuConfig(collect=ctu_config[0], + analyze=ctu_config[1], + dir=ctu_config[2], + extdef_map_cmd=ctu_config[3]) + + +def create_global_ctu_extdef_map(extdef_map_lines): + """ Takes iterator of individual external definition maps and creates a + global map keeping only unique names. We leave conflicting names out of + CTU. + + :param extdef_map_lines: Contains the id of a definition (mangled name) and + the originating source (the corresponding AST file) name. + :type extdef_map_lines: Iterator of str. + :returns: Mangled name - AST file pairs. + :rtype: List of (str, str) tuples. + """ + + mangled_to_asts = defaultdict(set) + + for line in extdef_map_lines: + mangled_name, ast_file = line.strip().split(' ', 1) + mangled_to_asts[mangled_name].add(ast_file) + + mangled_ast_pairs = [] + + for mangled_name, ast_files in mangled_to_asts.items(): + if len(ast_files) == 1: + mangled_ast_pairs.append((mangled_name, next(iter(ast_files)))) + + return mangled_ast_pairs + + +def merge_ctu_extdef_maps(ctudir): + """ Merge individual external definition maps into a global one. + + As the collect phase runs parallel on multiple threads, all compilation + units are separately mapped into a temporary file in CTU_TEMP_DEFMAP_FOLDER. + These definition maps contain the mangled names and the source + (AST generated from the source) which had their definition. + These files should be merged at the end into a global map file: + CTU_EXTDEF_MAP_FILENAME.""" + + def generate_extdef_map_lines(extdefmap_dir): + """ Iterate over all lines of input files in a determined order. """ + + files = glob.glob(os.path.join(extdefmap_dir, '*')) + files.sort() + for filename in files: + with open(filename, 'r') as in_file: + for line in in_file: + yield line + + def write_global_map(arch, mangled_ast_pairs): + """ Write (mangled name, ast file) pairs into final file. """ + + extern_defs_map_file = os.path.join(ctudir, arch, + CTU_EXTDEF_MAP_FILENAME) + with open(extern_defs_map_file, 'w') as out_file: + for mangled_name, ast_file in mangled_ast_pairs: + out_file.write('%s %s\n' % (mangled_name, ast_file)) + + triple_arches = glob.glob(os.path.join(ctudir, '*')) + for triple_path in triple_arches: + if os.path.isdir(triple_path): + triple_arch = os.path.basename(triple_path) + extdefmap_dir = os.path.join(ctudir, triple_arch, + CTU_TEMP_DEFMAP_FOLDER) + + extdef_map_lines = generate_extdef_map_lines(extdefmap_dir) + mangled_ast_pairs = create_global_ctu_extdef_map(extdef_map_lines) + write_global_map(triple_arch, mangled_ast_pairs) + + # Remove all temporary files + shutil.rmtree(extdefmap_dir, ignore_errors=True) + + +def run_analyzer_parallel(args): + """ Runs the analyzer against the given compilation database. """ + + def exclude(filename): + """ Return true when any excluded directory prefix the filename. """ + return any(re.match(r'^' + directory, filename) + for directory in args.excludes) + + consts = { + 'clang': args.clang, + 'output_dir': args.output, + 'output_format': args.output_format, + 'output_failures': args.output_failures, + 'direct_args': analyzer_params(args), + 'force_debug': args.force_debug, + 'ctu': get_ctu_config_from_args(args) + } + + logging.debug('run analyzer against compilation database') + with open(args.cdb, 'r') as handle: + generator = (dict(cmd, **consts) + for cmd in json.load(handle) if not exclude(cmd['file'])) + # when verbose output requested execute sequentially + pool = multiprocessing.Pool(1 if args.verbose > 2 else None) + for current in pool.imap_unordered(run, generator): + if current is not None: + # display error message from the static analyzer + for line in current['error_output']: + logging.info(line.rstrip()) + pool.close() + pool.join() + + +def govern_analyzer_runs(args): + """ Governs multiple runs in CTU mode or runs once in normal mode. """ + + ctu_config = get_ctu_config_from_args(args) + # If we do a CTU collect (1st phase) we remove all previous collection + # data first. + if ctu_config.collect: + shutil.rmtree(ctu_config.dir, ignore_errors=True) + + # If the user asked for a collect (1st) and analyze (2nd) phase, we do an + # all-in-one run where we deliberately remove collection data before and + # also after the run. If the user asks only for a single phase data is + # left so multiple analyze runs can use the same data gathered by a single + # collection run. + if ctu_config.collect and ctu_config.analyze: + # CTU strings are coming from args.ctu_dir and extdef_map_cmd, + # so we can leave it empty + args.ctu_phases = CtuConfig(collect=True, analyze=False, + dir='', extdef_map_cmd='') + run_analyzer_parallel(args) + merge_ctu_extdef_maps(ctu_config.dir) + args.ctu_phases = CtuConfig(collect=False, analyze=True, + dir='', extdef_map_cmd='') + run_analyzer_parallel(args) + shutil.rmtree(ctu_config.dir, ignore_errors=True) + else: + # Single runs (collect or analyze) are launched from here. + run_analyzer_parallel(args) + if ctu_config.collect: + merge_ctu_extdef_maps(ctu_config.dir) + + +def setup_environment(args): + """ Set up environment for build command to interpose compiler wrapper. """ + + environment = dict(os.environ) + environment.update(wrapper_environment(args)) + environment.update({ + 'CC': COMPILER_WRAPPER_CC, + 'CXX': COMPILER_WRAPPER_CXX, + 'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '', + 'ANALYZE_BUILD_REPORT_DIR': args.output, + 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, + 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', + 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)), + 'ANALYZE_BUILD_FORCE_DEBUG': 'yes' if args.force_debug else '', + 'ANALYZE_BUILD_CTU': json.dumps(get_ctu_config_from_args(args)) + }) + return environment + + +@command_entry_point +def analyze_compiler_wrapper(): + """ Entry point for `analyze-cc` and `analyze-c++` compiler wrappers. """ + + return compiler_wrapper(analyze_compiler_wrapper_impl) + + +def analyze_compiler_wrapper_impl(result, execution): + """ Implements analyzer compiler wrapper functionality. """ + + # don't run analyzer when compilation fails. or when it's not requested. + if result or not os.getenv('ANALYZE_BUILD_CLANG'): + return + + # check is it a compilation? + compilation = split_command(execution.cmd) + if compilation is None: + return + # collect the needed parameters from environment, crash when missing + 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': execution.cwd, + 'command': [execution.cmd[0], '-c'] + compilation.flags, + 'ctu': get_ctu_config_from_json(os.getenv('ANALYZE_BUILD_CTU')) + } + # call static analyzer against the compilation + for source in compilation.files: + parameters.update({'file': source}) + logging.debug('analyzer parameters %s', 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()) + + +@contextlib.contextmanager +def report_directory(hint, keep): + """ Responsible for the report directory. + + hint -- could specify the parent directory of the output directory. + keep -- a boolean value to keep or delete the empty report directory. """ + + stamp_format = 'scan-build-%Y-%m-%d-%H-%M-%S-%f-' + stamp = datetime.datetime.now().strftime(stamp_format) + parent_dir = os.path.abspath(hint) + if not os.path.exists(parent_dir): + os.makedirs(parent_dir) + name = tempfile.mkdtemp(prefix=stamp, dir=parent_dir) + + logging.info('Report directory created: %s', name) + + try: + yield name + finally: + if os.listdir(name): + msg = "Run 'scan-view %s' to examine bug reports." + keep = True + else: + if keep: + msg = "Report directory '%s' contains no report, but kept." + else: + msg = "Removing directory '%s' because it contains no report." + logging.warning(msg, name) + + if not keep: + os.rmdir(name) + + +def analyzer_params(args): + """ A group of command line arguments can mapped to command + line arguments of the analyzer. This method generates those. """ + + result = [] + + 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)) + if args.internal_stats: + result.append('-analyzer-stats') + if args.analyze_headers: + result.append('-analyzer-opt-analyze-headers') + if args.stats: + result.append('-analyzer-checker=debug.Stats') + if args.maxloop: + result.extend(['-analyzer-max-loop', str(args.maxloop)]) + if args.output_format: + result.append('-analyzer-output={0}'.format(args.output_format)) + if args.analyzer_config: + result.extend(['-analyzer-config', args.analyzer_config]) + if args.verbose >= 4: + result.append('-analyzer-display-progress') + if args.plugins: + result.extend(prefix_with('-load', args.plugins)) + if args.enable_checker: + checkers = ','.join(args.enable_checker) + result.extend(['-analyzer-checker', checkers]) + if args.disable_checker: + checkers = ','.join(args.disable_checker) + result.extend(['-analyzer-disable-checker', checkers]) + + return prefix_with('-Xclang', result) + + +def require(required): + """ Decorator for checking the required values in state. + + It checks the required attributes in the passed state and stop when + any of those is missing. """ + + def decorator(function): + @functools.wraps(function) + def wrapper(*args, **kwargs): + for key in required: + if key not in args[0]: + raise KeyError('{0} not passed to {1}'.format( + key, function.__name__)) + + return function(*args, **kwargs) + + return wrapper + + return decorator + + +@require(['command', # entry from compilation database + 'directory', # entry from compilation database + 'file', # entry from compilation database + 'clang', # clang executable name (and path) + 'direct_args', # arguments from command line + 'force_debug', # kill non debug macros + 'output_dir', # where generated report files shall go + 'output_format', # it's 'plist', 'html', both or plist-multi-file + 'output_failures', # generate crash reports or not + 'ctu']) # ctu control options +def run(opts): + """ Entry point to run (or not) static analyzer against a single entry + of the compilation database. + + This complex task is decomposed into smaller methods which are calling + each other in chain. If the analysis is not possible the given method + just return and break the chain. + + The passed parameter is a python dictionary. Each method first check + that the needed parameters received. (This is done by the 'require' + decorator. It's like an 'assert' to check the contract between the + caller and the called method.) """ + + try: + command = opts.pop('command') + command = command if isinstance(command, list) else decode(command) + logging.debug("Run analyzer against '%s'", command) + opts.update(classify_parameters(command)) + + return arch_check(opts) + except Exception: + logging.error("Problem occurred during analysis.", exc_info=1) + return None + + +@require(['clang', 'directory', 'flags', 'file', 'output_dir', 'language', + 'error_output', 'exit_code']) +def report_failure(opts): + """ Create report when analyzer failed. + + The major report is the preprocessor output. The output filename generated + randomly. The compiler output also captured into '.stderr.txt' file. + And some more execution context also saved into '.info.txt' file. """ + + def extension(): + """ Generate preprocessor file extension. """ + + mapping = {'objective-c++': '.mii', 'objective-c': '.mi', 'c++': '.ii'} + return mapping.get(opts['language'], '.i') + + def destination(): + """ Creates failures directory if not exits yet. """ + + failures_dir = os.path.join(opts['output_dir'], 'failures') + if not os.path.isdir(failures_dir): + os.makedirs(failures_dir) + return failures_dir + + # Classify error type: when Clang terminated by a signal it's a 'Crash'. + # (python subprocess Popen.returncode is negative when child terminated + # by signal.) Everything else is 'Other Error'. + error = 'crash' if opts['exit_code'] < 0 else 'other_error' + # Create preprocessor output file name. (This is blindly following the + # Perl implementation.) + (handle, name) = tempfile.mkstemp(suffix=extension(), + prefix='clang_' + error + '_', + dir=destination()) + os.close(handle) + # Execute Clang again, but run the syntax check only. + cwd = opts['directory'] + cmd = [opts['clang'], '-fsyntax-only', '-E'] + opts['flags'] + \ + [opts['file'], '-o', name] + try: + cmd = get_arguments(cmd, cwd) + run_command(cmd, cwd=cwd) + except subprocess.CalledProcessError: + pass + except ClangErrorException: + pass + # write general information about the crash + with open(name + '.info.txt', 'w') as handle: + handle.write(opts['file'] + os.linesep) + handle.write(error.title().replace('_', ' ') + os.linesep) + handle.write(' '.join(cmd) + os.linesep) + handle.write(' '.join(os.uname()) + os.linesep) + handle.write(get_version(opts['clang'])) + handle.close() + # write the captured output too + with open(name + '.stderr.txt', 'w') as handle: + handle.writelines(opts['error_output']) + handle.close() + + +@require(['clang', 'directory', 'flags', 'direct_args', 'file', 'output_dir', + 'output_format']) +def run_analyzer(opts, continuation=report_failure): + """ It assembles the analysis command line and executes it. Capture the + output of the analysis and returns with it. If failure reports are + requested, it calls the continuation to generate it. """ + + def target(): + """ Creates output file name for reports. """ + if opts['output_format'] in { + 'plist', + 'plist-html', + 'plist-multi-file'}: + (handle, name) = tempfile.mkstemp(prefix='report-', + suffix='.plist', + dir=opts['output_dir']) + os.close(handle) + return name + return opts['output_dir'] + + try: + cwd = opts['directory'] + cmd = get_arguments([opts['clang'], '--analyze'] + + opts['direct_args'] + opts['flags'] + + [opts['file'], '-o', target()], + cwd) + output = run_command(cmd, cwd=cwd) + return {'error_output': output, 'exit_code': 0} + except subprocess.CalledProcessError as ex: + result = {'error_output': ex.output, 'exit_code': ex.returncode} + if opts.get('output_failures', False): + opts.update(result) + continuation(opts) + return result + except ClangErrorException as ex: + result = {'error_output': ex.error, 'exit_code': 0} + if opts.get('output_failures', False): + opts.update(result) + continuation(opts) + return result + + +def extdef_map_list_src_to_ast(extdef_src_list): + """ Turns textual external definition map list with source files into an + external definition map list with ast files. """ + + extdef_ast_list = [] + for extdef_src_txt in extdef_src_list: + mangled_name, path = extdef_src_txt.split(" ", 1) + # Normalize path on windows as well + path = os.path.splitdrive(path)[1] + # Make relative path out of absolute + path = path[1:] if path[0] == os.sep else path + ast_path = os.path.join("ast", path + ".ast") + extdef_ast_list.append(mangled_name + " " + ast_path) + return extdef_ast_list + + +@require(['clang', 'directory', 'flags', 'direct_args', 'file', 'ctu']) +def ctu_collect_phase(opts): + """ Preprocess source by generating all data needed by CTU analysis. """ + + def generate_ast(triple_arch): + """ Generates ASTs for the current compilation command. """ + + args = opts['direct_args'] + opts['flags'] + ast_joined_path = os.path.join(opts['ctu'].dir, triple_arch, 'ast', + os.path.realpath(opts['file'])[1:] + + '.ast') + ast_path = os.path.abspath(ast_joined_path) + ast_dir = os.path.dirname(ast_path) + if not os.path.isdir(ast_dir): + try: + os.makedirs(ast_dir) + except OSError: + # In case an other process already created it. + pass + ast_command = [opts['clang'], '-emit-ast'] + ast_command.extend(args) + ast_command.append('-w') + ast_command.append(opts['file']) + ast_command.append('-o') + ast_command.append(ast_path) + logging.debug("Generating AST using '%s'", ast_command) + run_command(ast_command, cwd=opts['directory']) + + def map_extdefs(triple_arch): + """ Generate external definition map file for the current source. """ + + args = opts['direct_args'] + opts['flags'] + extdefmap_command = [opts['ctu'].extdef_map_cmd] + extdefmap_command.append(opts['file']) + extdefmap_command.append('--') + extdefmap_command.extend(args) + logging.debug("Generating external definition map using '%s'", + extdefmap_command) + extdef_src_list = run_command(extdefmap_command, cwd=opts['directory']) + extdef_ast_list = extdef_map_list_src_to_ast(extdef_src_list) + extern_defs_map_folder = os.path.join(opts['ctu'].dir, triple_arch, + CTU_TEMP_DEFMAP_FOLDER) + if not os.path.isdir(extern_defs_map_folder): + try: + os.makedirs(extern_defs_map_folder) + except OSError: + # In case an other process already created it. + pass + if extdef_ast_list: + with tempfile.NamedTemporaryFile(mode='w', + dir=extern_defs_map_folder, + delete=False) as out_file: + out_file.write("\n".join(extdef_ast_list) + "\n") + + cwd = opts['directory'] + cmd = [opts['clang'], '--analyze'] + opts['direct_args'] + opts['flags'] \ + + [opts['file']] + triple_arch = get_triple_arch(cmd, cwd) + generate_ast(triple_arch) + map_extdefs(triple_arch) + + +@require(['ctu']) +def dispatch_ctu(opts, continuation=run_analyzer): + """ Execute only one phase of 2 phases of CTU if needed. """ + + ctu_config = opts['ctu'] + + if ctu_config.collect or ctu_config.analyze: + assert ctu_config.collect != ctu_config.analyze + if ctu_config.collect: + return ctu_collect_phase(opts) + if ctu_config.analyze: + cwd = opts['directory'] + cmd = [opts['clang'], '--analyze'] + opts['direct_args'] \ + + opts['flags'] + [opts['file']] + triarch = get_triple_arch(cmd, cwd) + ctu_options = ['ctu-dir=' + os.path.join(ctu_config.dir, triarch), + 'experimental-enable-naive-ctu-analysis=true'] + analyzer_options = prefix_with('-analyzer-config', ctu_options) + direct_options = prefix_with('-Xanalyzer', analyzer_options) + opts['direct_args'].extend(direct_options) + + return continuation(opts) + + +@require(['flags', 'force_debug']) +def filter_debug_flags(opts, continuation=dispatch_ctu): + """ Filter out nondebug macros when requested. """ + + if opts.pop('force_debug'): + # lazy implementation just append an undefine macro at the end + opts.update({'flags': opts['flags'] + ['-UNDEBUG']}) + + return continuation(opts) + + +@require(['language', 'compiler', 'file', 'flags']) +def language_check(opts, continuation=filter_debug_flags): + """ Find out the language from command line parameters or file name + extension. The decision also influenced by the compiler invocation. """ + + accepted = frozenset({ + 'c', 'c++', 'objective-c', 'objective-c++', 'c-cpp-output', + 'c++-cpp-output', 'objective-c-cpp-output' + }) + + # language can be given as a parameter... + language = opts.pop('language') + compiler = opts.pop('compiler') + # ... or find out from source file extension + if language is None and compiler is not None: + language = classify_source(opts['file'], compiler == 'c') + + if language is None: + logging.debug('skip analysis, language not known') + return None + elif language not in accepted: + logging.debug('skip analysis, language not supported') + return None + else: + logging.debug('analysis, language: %s', language) + opts.update({'language': language, + 'flags': ['-x', language] + opts['flags']}) + return continuation(opts) + + +@require(['arch_list', 'flags']) +def arch_check(opts, continuation=language_check): + """ Do run analyzer through one of the given architectures. """ + + disabled = frozenset({'ppc', 'ppc64'}) + + received_list = opts.pop('arch_list') + if received_list: + # filter out disabled architectures and -arch switches + filtered_list = [a for a in received_list if a not in disabled] + if filtered_list: + # There should be only one arch given (or the same multiple + # times). If there are multiple arch are given and are not + # the same, those should not change the pre-processing step. + # But that's the only pass we have before run the analyzer. + current = filtered_list.pop() + logging.debug('analysis, on arch: %s', current) + + opts.update({'flags': ['-arch', current] + opts['flags']}) + return continuation(opts) + else: + logging.debug('skip analysis, found not supported arch') + return None + else: + logging.debug('analysis, on default arch') + return continuation(opts) + + +# To have good results from static analyzer certain compiler options shall be +# omitted. The compiler flag filtering only affects the static analyzer run. +# +# Keys are the option name, value number of options to skip +IGNORED_FLAGS = { + '-c': 0, # compile option will be overwritten + '-fsyntax-only': 0, # static analyzer option will be overwritten + '-o': 1, # will set up own output file + # flags below are inherited from the perl implementation. + '-g': 0, + '-save-temps': 0, + '-install_name': 1, + '-exported_symbols_list': 1, + '-current_version': 1, + '-compatibility_version': 1, + '-init': 1, + '-e': 1, + '-seg1addr': 1, + '-bundle_loader': 1, + '-multiply_defined': 1, + '-sectorder': 3, + '--param': 1, + '--serialize-diagnostics': 1 +} + + +def classify_parameters(command): + """ Prepare compiler flags (filters some and add others) and take out + language (-x) and architecture (-arch) flags for future processing. """ + + result = { + 'flags': [], # the filtered compiler flags + 'arch_list': [], # list of architecture flags + 'language': None, # compilation language, None, if not specified + 'compiler': compiler_language(command) # 'c' or 'c++' + } + + # iterate on the compile options + args = iter(command[1:]) + for arg in args: + # take arch flags into a separate basket + if arg == '-arch': + result['arch_list'].append(next(args)) + # take language + elif arg == '-x': + result['language'] = next(args) + # parameters which looks source file are not flags + elif re.match(r'^[^-].+', arg) and classify_source(arg): + pass + # ignore some flags + elif arg in IGNORED_FLAGS: + count = IGNORED_FLAGS[arg] + for _ in range(count): + next(args) + # we don't care about extra warnings, but we should suppress ones + # that we don't want to see. + elif re.match(r'^-W.+', arg) and not re.match(r'^-Wno-.+', arg): + pass + # and consider everything else as compilation flag. + else: + result['flags'].append(arg) + + return result diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/arguments.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/arguments.py new file mode 100644 index 00000000000..e258a410033 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/arguments.py @@ -0,0 +1,501 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module parses and validates arguments for command-line interfaces. + +It uses argparse module to create the command line parser. (This library is +in the standard python library since 3.2 and backported to 2.7, but not +earlier.) + +It also implements basic validation methods, related to the command. +Validations are mostly calling specific help methods, or mangling values. +""" +from __future__ import absolute_import, division, print_function + +import os +import sys +import argparse +import logging +import tempfile +from libscanbuild import reconfigure_logging, CtuConfig +from libscanbuild.clang import get_checkers, is_ctu_capable + +__all__ = ['parse_args_for_intercept_build', 'parse_args_for_analyze_build', + 'parse_args_for_scan_build'] + + +def parse_args_for_intercept_build(): + """ Parse and validate command-line arguments for intercept-build. """ + + parser = create_intercept_parser() + args = parser.parse_args() + + reconfigure_logging(args.verbose) + logging.debug('Raw arguments %s', sys.argv) + + # short validation logic + if not args.build: + parser.error(message='missing build command') + + logging.debug('Parsed arguments: %s', args) + return args + + +def parse_args_for_analyze_build(): + """ Parse and validate command-line arguments for analyze-build. """ + + from_build_command = False + parser = create_analyze_parser(from_build_command) + args = parser.parse_args() + + reconfigure_logging(args.verbose) + logging.debug('Raw arguments %s', sys.argv) + + normalize_args_for_analyze(args, from_build_command) + validate_args_for_analyze(parser, args, from_build_command) + logging.debug('Parsed arguments: %s', args) + return args + + +def parse_args_for_scan_build(): + """ Parse and validate command-line arguments for scan-build. """ + + from_build_command = True + parser = create_analyze_parser(from_build_command) + args = parser.parse_args() + + reconfigure_logging(args.verbose) + logging.debug('Raw arguments %s', sys.argv) + + normalize_args_for_analyze(args, from_build_command) + validate_args_for_analyze(parser, args, from_build_command) + logging.debug('Parsed arguments: %s', args) + return args + + +def normalize_args_for_analyze(args, from_build_command): + """ Normalize parsed arguments for analyze-build and scan-build. + + :param args: Parsed argument object. (Will be mutated.) + :param from_build_command: Boolean value tells is the command suppose + to run the analyzer against a build command or a compilation db. """ + + # make plugins always a list. (it might be None when not specified.) + if args.plugins is None: + args.plugins = [] + + # make exclude directory list unique and absolute. + uniq_excludes = set(os.path.abspath(entry) for entry in args.excludes) + args.excludes = list(uniq_excludes) + + # because shared codes for all tools, some common used methods are + # expecting some argument to be present. so, instead of query the args + # object about the presence of the flag, we fake it here. to make those + # methods more readable. (it's an arguable choice, took it only for those + # which have good default value.) + if from_build_command: + # add cdb parameter invisibly to make report module working. + args.cdb = 'compile_commands.json' + + # Make ctu_dir an abspath as it is needed inside clang + if not from_build_command and hasattr(args, 'ctu_phases') \ + and hasattr(args.ctu_phases, 'dir'): + args.ctu_dir = os.path.abspath(args.ctu_dir) + + +def validate_args_for_analyze(parser, args, from_build_command): + """ Command line parsing is done by the argparse module, but semantic + validation still needs to be done. This method is doing it for + analyze-build and scan-build commands. + + :param parser: The command line parser object. + :param args: Parsed argument object. + :param from_build_command: Boolean value tells is the command suppose + to run the analyzer against a build command or a compilation db. + :return: No return value, but this call might throw when validation + fails. """ + + if args.help_checkers_verbose: + print_checkers(get_checkers(args.clang, args.plugins)) + parser.exit(status=0) + elif args.help_checkers: + print_active_checkers(get_checkers(args.clang, args.plugins)) + parser.exit(status=0) + elif from_build_command and not args.build: + parser.error(message='missing build command') + elif not from_build_command and not os.path.exists(args.cdb): + parser.error(message='compilation database is missing') + + # If the user wants CTU mode + if not from_build_command and hasattr(args, 'ctu_phases') \ + and hasattr(args.ctu_phases, 'dir'): + # If CTU analyze_only, the input directory should exist + if args.ctu_phases.analyze and not args.ctu_phases.collect \ + and not os.path.exists(args.ctu_dir): + parser.error(message='missing CTU directory') + # Check CTU capability via checking clang-extdef-mapping + if not is_ctu_capable(args.extdef_map_cmd): + parser.error(message="""This version of clang does not support CTU + functionality or clang-extdef-mapping command not found.""") + + +def create_intercept_parser(): + """ Creates a parser for command-line arguments to 'intercept'. """ + + parser = create_default_parser() + parser_add_cdb(parser) + + parser_add_prefer_wrapper(parser) + parser_add_compilers(parser) + + advanced = parser.add_argument_group('advanced options') + group = advanced.add_mutually_exclusive_group() + group.add_argument( + '--append', + action='store_true', + help="""Extend existing compilation database with new entries. + Duplicate entries are detected and not present in the final output. + The output is not continuously updated, it's done when the build + command finished. """) + + parser.add_argument( + dest='build', nargs=argparse.REMAINDER, help="""Command to run.""") + return parser + + +def create_analyze_parser(from_build_command): + """ Creates a parser for command-line arguments to 'analyze'. """ + + parser = create_default_parser() + + if from_build_command: + parser_add_prefer_wrapper(parser) + parser_add_compilers(parser) + + parser.add_argument( + '--intercept-first', + action='store_true', + help="""Run the build commands first, intercept compiler + calls and then run the static analyzer afterwards. + Generally speaking it has better coverage on build commands. + With '--override-compiler' it use compiler wrapper, but does + not run the analyzer till the build is finished.""") + else: + parser_add_cdb(parser) + + parser.add_argument( + '--status-bugs', + action='store_true', + help="""The exit status of '%(prog)s' is the same as the executed + build command. This option ignores the build exit status and sets to + be non zero if it found potential bugs or zero otherwise.""") + parser.add_argument( + '--exclude', + metavar='<directory>', + dest='excludes', + action='append', + default=[], + help="""Do not run static analyzer against files found in this + directory. (You can specify this option multiple times.) + Could be useful when project contains 3rd party libraries.""") + + output = parser.add_argument_group('output control options') + output.add_argument( + '--output', + '-o', + metavar='<path>', + default=tempfile.gettempdir(), + help="""Specifies the output directory for analyzer reports. + Subdirectory will be created if default directory is targeted.""") + output.add_argument( + '--keep-empty', + action='store_true', + help="""Don't remove the build results directory even if no issues + were reported.""") + output.add_argument( + '--html-title', + metavar='<title>', + help="""Specify the title used on generated HTML pages. + If not specified, a default title will be used.""") + format_group = output.add_mutually_exclusive_group() + format_group.add_argument( + '--plist', + '-plist', + dest='output_format', + const='plist', + default='html', + action='store_const', + help="""Cause the results as a set of .plist files.""") + format_group.add_argument( + '--plist-html', + '-plist-html', + dest='output_format', + const='plist-html', + default='html', + action='store_const', + help="""Cause the results as a set of .html and .plist files.""") + format_group.add_argument( + '--plist-multi-file', + '-plist-multi-file', + dest='output_format', + const='plist-multi-file', + default='html', + action='store_const', + help="""Cause the results as a set of .plist files with extra + information on related files.""") + + advanced = parser.add_argument_group('advanced options') + advanced.add_argument( + '--use-analyzer', + metavar='<path>', + dest='clang', + default='clang', + help="""'%(prog)s' uses the 'clang' executable relative to itself for + static analysis. One can override this behavior with this option by + using the 'clang' packaged with Xcode (on OS X) or from the PATH.""") + advanced.add_argument( + '--no-failure-reports', + '-no-failure-reports', + dest='output_failures', + action='store_false', + help="""Do not create a 'failures' subdirectory that includes analyzer + crash reports and preprocessed source files.""") + parser.add_argument( + '--analyze-headers', + action='store_true', + help="""Also analyze functions in #included files. By default, such + functions are skipped unless they are called by functions within the + main source file.""") + advanced.add_argument( + '--stats', + '-stats', + action='store_true', + help="""Generates visitation statistics for the project.""") + advanced.add_argument( + '--internal-stats', + action='store_true', + help="""Generate internal analyzer statistics.""") + advanced.add_argument( + '--maxloop', + '-maxloop', + metavar='<loop count>', + type=int, + help="""Specify the number of times a block can be visited before + giving up. Increase for more comprehensive coverage at a cost of + speed.""") + advanced.add_argument( + '--store', + '-store', + metavar='<model>', + dest='store_model', + choices=['region', 'basic'], + help="""Specify the store model used by the analyzer. 'region' + specifies a field- sensitive store model. 'basic' which is far less + precise but can more quickly analyze code. 'basic' was the default + store model for checker-0.221 and earlier.""") + advanced.add_argument( + '--constraints', + '-constraints', + metavar='<model>', + dest='constraints_model', + choices=['range', 'basic'], + help="""Specify the constraint engine used by the analyzer. Specifying + 'basic' uses a simpler, less powerful constraint model used by + checker-0.160 and earlier.""") + advanced.add_argument( + '--analyzer-config', + '-analyzer-config', + metavar='<options>', + help="""Provide options to pass through to the analyzer's + -analyzer-config flag. Several options are separated with comma: + 'key1=val1,key2=val2' + + Available options: + stable-report-filename=true or false (default) + + Switch the page naming to: + report-<filename>-<function/method name>-<id>.html + instead of report-XXXXXX.html""") + 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( + '--load-plugin', + '-load-plugin', + metavar='<plugin library>', + dest='plugins', + action='append', + help="""Loading external checkers using the clang plugin interface.""") + plugins.add_argument( + '--enable-checker', + '-enable-checker', + metavar='<checker name>', + action=AppendCommaSeparated, + help="""Enable specific checker.""") + plugins.add_argument( + '--disable-checker', + '-disable-checker', + metavar='<checker name>', + action=AppendCommaSeparated, + help="""Disable specific checker.""") + plugins.add_argument( + '--help-checkers', + action='store_true', + help="""A default group of checkers is run unless explicitly disabled. + Exactly which checkers constitute the default group is a function of + the operating system in use. These can be printed with this flag.""") + plugins.add_argument( + '--help-checkers-verbose', + action='store_true', + help="""Print all available checkers and mark the enabled ones.""") + + if from_build_command: + parser.add_argument( + dest='build', nargs=argparse.REMAINDER, help="""Command to run.""") + else: + ctu = parser.add_argument_group('cross translation unit analysis') + ctu_mutex_group = ctu.add_mutually_exclusive_group() + ctu_mutex_group.add_argument( + '--ctu', + action='store_const', + const=CtuConfig(collect=True, analyze=True, + dir='', extdef_map_cmd=''), + dest='ctu_phases', + help="""Perform cross translation unit (ctu) analysis (both collect + and analyze phases) using default <ctu-dir> for temporary output. + At the end of the analysis, the temporary directory is removed.""") + ctu.add_argument( + '--ctu-dir', + metavar='<ctu-dir>', + dest='ctu_dir', + default='ctu-dir', + help="""Defines the temporary directory used between ctu + phases.""") + ctu_mutex_group.add_argument( + '--ctu-collect-only', + action='store_const', + const=CtuConfig(collect=True, analyze=False, + dir='', extdef_map_cmd=''), + dest='ctu_phases', + help="""Perform only the collect phase of ctu. + Keep <ctu-dir> for further use.""") + ctu_mutex_group.add_argument( + '--ctu-analyze-only', + action='store_const', + const=CtuConfig(collect=False, analyze=True, + dir='', extdef_map_cmd=''), + dest='ctu_phases', + help="""Perform only the analyze phase of ctu. <ctu-dir> should be + present and will not be removed after analysis.""") + ctu.add_argument( + '--use-extdef-map-cmd', + metavar='<path>', + dest='extdef_map_cmd', + default='clang-extdef-mapping', + help="""'%(prog)s' uses the 'clang-extdef-mapping' executable + relative to itself for generating external definition maps for + static analysis. One can override this behavior with this option + by using the 'clang-extdef-mapping' packaged with Xcode (on OS X) + or from the PATH.""") + return parser + + +def create_default_parser(): + """ Creates command line parser for all build wrapper commands. """ + + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument( + '--verbose', + '-v', + action='count', + default=0, + help="""Enable verbose output from '%(prog)s'. A second, third and + fourth flags increases verbosity.""") + return parser + + +def parser_add_cdb(parser): + parser.add_argument( + '--cdb', + metavar='<file>', + default="compile_commands.json", + help="""The JSON compilation database.""") + + +def parser_add_prefer_wrapper(parser): + parser.add_argument( + '--override-compiler', + action='store_true', + help="""Always resort to the compiler wrapper even when better + intercept methods are available.""") + + +def parser_add_compilers(parser): + parser.add_argument( + '--use-cc', + metavar='<path>', + dest='cc', + default=os.getenv('CC', 'cc'), + help="""When '%(prog)s' analyzes a project by interposing a compiler + wrapper, which executes a real compiler for compilation and do other + tasks (record the compiler invocation). Because of this interposing, + '%(prog)s' does not know what compiler your project normally uses. + Instead, it simply overrides the CC environment variable, and guesses + your default compiler. + + If you need '%(prog)s' to use a specific compiler for *compilation* + then you can use this option to specify a path to that compiler.""") + parser.add_argument( + '--use-c++', + metavar='<path>', + dest='cxx', + default=os.getenv('CXX', 'c++'), + help="""This is the same as "--use-cc" but for C++ code.""") + + +class AppendCommaSeparated(argparse.Action): + """ argparse Action class to support multiple comma separated lists. """ + + def __call__(self, __parser, namespace, values, __option_string): + # getattr(obj, attr, default) does not really returns default but none + if getattr(namespace, self.dest, None) is None: + setattr(namespace, self.dest, []) + # once it's fixed we can use as expected + actual = getattr(namespace, self.dest) + actual.extend(values.split(',')) + setattr(namespace, self.dest, actual) + + +def print_active_checkers(checkers): + """ Print active checkers to stdout. """ + + for name in sorted(name for name, (_, active) in checkers.items() + if active): + print(name) + + +def print_checkers(checkers): + """ Print verbose checker help to stdout. """ + + print('') + print('available checkers:') + print('') + for name in sorted(checkers.keys()): + description, active = checkers[name] + prefix = '+' if active else ' ' + if len(name) > 30: + print(' {0} {1}'.format(prefix, name)) + print(' ' * 35 + description) + else: + print(' {0} {1: <30} {2}'.format(prefix, name, description)) + print('') + print('NOTE: "+" indicates that an analysis is enabled by default.') + print('') diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/clang.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/clang.py new file mode 100644 index 00000000000..4f02cb20d3f --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/clang.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module is responsible for the Clang executable. + +Since Clang command line interface is so rich, but this project is using only +a subset of that, it makes sense to create a function specific wrapper. """ + +import subprocess +import re +from libscanbuild import run_command +from libscanbuild.shell import decode + +__all__ = ['get_version', 'get_arguments', 'get_checkers', 'is_ctu_capable', + 'get_triple_arch'] + +# regex for activated checker +ACTIVE_CHECKER_PATTERN = re.compile(r'^-analyzer-checker=(.*)$') + + +class ClangErrorException(Exception): + def __init__(self, error): + self.error = error + + +def get_version(clang): + """ Returns the compiler version as string. + + :param clang: the compiler we are using + :return: the version string printed to stderr """ + + output = run_command([clang, '-v']) + # the relevant version info is in the first line + return output[0] + + +def get_arguments(command, cwd): + """ Capture Clang invocation. + + :param command: the compilation command + :param cwd: the current working directory + :return: the detailed front-end invocation command """ + + cmd = command[:] + cmd.insert(1, '-###') + cmd.append('-fno-color-diagnostics') + + output = run_command(cmd, cwd=cwd) + # The relevant information is in the last line of the output. + # Don't check if finding last line fails, would throw exception anyway. + last_line = output[-1] + if re.search(r'clang(.*): error:', last_line): + raise ClangErrorException(last_line) + return decode(last_line) + + +def get_active_checkers(clang, plugins): + """ Get the active checker list. + + :param clang: the compiler we are using + :param plugins: list of plugins which was requested by the user + :return: list of checker names which are active + + To get the default checkers we execute Clang to print how this + compilation would be called. And take out the enabled checker from the + arguments. For input file we specify stdin and pass only language + information. """ + + def get_active_checkers_for(language): + """ Returns a list of active checkers for the given language. """ + + load_args = [arg + for plugin in plugins + for arg in ['-Xclang', '-load', '-Xclang', plugin]] + cmd = [clang, '--analyze'] + load_args + ['-x', language, '-'] + return [ACTIVE_CHECKER_PATTERN.match(arg).group(1) + for arg in get_arguments(cmd, '.') + if ACTIVE_CHECKER_PATTERN.match(arg)] + + result = set() + for language in ['c', 'c++', 'objective-c', 'objective-c++']: + result.update(get_active_checkers_for(language)) + return frozenset(result) + + +def is_active(checkers): + """ Returns a method, which classifies the checker active or not, + based on the received checker name list. """ + + def predicate(checker): + """ Returns True if the given checker is active. """ + + return any(pattern.match(checker) for pattern in predicate.patterns) + + predicate.patterns = [re.compile(r'^' + a + r'(\.|$)') for a in checkers] + return predicate + + +def parse_checkers(stream): + """ Parse clang -analyzer-checker-help output. + + Below the line 'CHECKERS:' are there the name description pairs. + Many of them are in one line, but some long named checker has the + name and the description in separate lines. + + The checker name is always prefixed with two space character. The + name contains no whitespaces. Then followed by newline (if it's + too long) or other space characters comes the description of the + checker. The description ends with a newline character. + + :param stream: list of lines to parse + :return: generator of tuples + + (<checker name>, <checker description>) """ + + lines = iter(stream) + # find checkers header + for line in lines: + if re.match(r'^CHECKERS:', line): + break + # find entries + state = None + for line in lines: + if state and not re.match(r'^\s\s\S', line): + yield (state, line.strip()) + state = None + elif re.match(r'^\s\s\S+$', line.rstrip()): + state = line.strip() + else: + pattern = re.compile(r'^\s\s(?P<key>\S*)\s*(?P<value>.*)') + match = pattern.match(line.rstrip()) + if match: + current = match.groupdict() + yield (current['key'], current['value']) + + +def get_checkers(clang, plugins): + """ Get all the available checkers from default and from the plugins. + + :param clang: the compiler we are using + :param plugins: list of plugins which was requested by the user + :return: a dictionary of all available checkers and its status + + {<checker name>: (<checker description>, <is active by default>)} """ + + load = [elem for plugin in plugins for elem in ['-load', plugin]] + cmd = [clang, '-cc1'] + load + ['-analyzer-checker-help'] + + lines = run_command(cmd) + + is_active_checker = is_active(get_active_checkers(clang, plugins)) + + checkers = { + name: (description, is_active_checker(name)) + for name, description in parse_checkers(lines) + } + if not checkers: + raise Exception('Could not query Clang for available checkers.') + + return checkers + + +def is_ctu_capable(extdef_map_cmd): + """ Detects if the current (or given) clang and external definition mapping + executables are CTU compatible. """ + + try: + run_command([extdef_map_cmd, '-version']) + except (OSError, subprocess.CalledProcessError): + return False + return True + + +def get_triple_arch(command, cwd): + """Returns the architecture part of the target triple for the given + compilation command. """ + + cmd = get_arguments(command, cwd) + try: + separator = cmd.index("-triple") + return cmd[separator + 1] + except (IndexError, ValueError): + return "" diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/compilation.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/compilation.py new file mode 100644 index 00000000000..38ce634fbeb --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/compilation.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" 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/gnu/llvm/clang/tools/scan-build-py/libscanbuild/intercept.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/intercept.py new file mode 100644 index 00000000000..70f3233f5e8 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/intercept.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module is responsible to capture the compiler invocation of any +build process. The result of that should be a compilation database. + +This implementation is using the LD_PRELOAD or DYLD_INSERT_LIBRARIES +mechanisms provided by the dynamic linker. The related library is implemented +in C language and can be found under 'libear' directory. + +The 'libear' library is capturing all child process creation and logging the +relevant information about it into separate files in a specified directory. +The parameter of this process is the output directory name, where the report +files shall be placed. This parameter is passed as an environment variable. + +The module also implements compiler wrappers to intercept the compiler calls. + +The module implements the build command execution and the post-processing of +the output files, which will condensates into a compilation database. """ + +import sys +import os +import os.path +import re +import itertools +import json +import glob +import logging +from libear import build_libear, TemporaryDirectory +from libscanbuild import command_entry_point, compiler_wrapper, \ + wrapper_environment, run_command, run_build +from libscanbuild import duplicate_check +from libscanbuild.compilation import split_command +from libscanbuild.arguments import parse_args_for_intercept_build +from libscanbuild.shell import encode, decode + +__all__ = ['capture', 'intercept_build', 'intercept_compiler_wrapper'] + +GS = chr(0x1d) +RS = chr(0x1e) +US = chr(0x1f) + +COMPILER_WRAPPER_CC = 'intercept-cc' +COMPILER_WRAPPER_CXX = 'intercept-c++' +TRACE_FILE_EXTENSION = '.cmd' # same as in ear.c +WRAPPER_ONLY_PLATFORMS = frozenset({'win32', 'cygwin'}) + + +@command_entry_point +def intercept_build(): + """ Entry point for 'intercept-build' command. """ + + args = parse_args_for_intercept_build() + return capture(args) + + +def capture(args): + """ The entry point of build command interception. """ + + def post_processing(commands): + """ To make a compilation database, it needs to filter out commands + which are not compiler calls. Needs to find the source file name + 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 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, + format_entry(command) for command in commands) + # read entries from previous run + 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) + if os.path.exists(entry['file']) and not duplicate(entry)) + + with TemporaryDirectory(prefix='intercept-') as tmp_dir: + # run the build command + environment = setup_environment(args, tmp_dir) + exit_code = run_build(args.build, env=environment) + # read the intercepted exec calls + 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 + entries = post_processing(exec_traces) + # dump the compilation database + with open(args.cdb, 'w+') as handle: + json.dump(list(entries), handle, sort_keys=True, indent=4) + return exit_code + + +def setup_environment(args, destination): + """ Sets up the environment for the build command. + + It sets the required environment variables and execute the given command. + The exec calls will be logged by the 'libear' preloaded library or by the + 'wrapper' programs. """ + + c_compiler = args.cc if 'cc' in args else 'cc' + cxx_compiler = args.cxx if 'cxx' in args else 'c++' + + libear_path = None if args.override_compiler or is_preload_disabled( + sys.platform) else build_libear(c_compiler, destination) + + environment = dict(os.environ) + environment.update({'INTERCEPT_BUILD_TARGET_DIR': destination}) + + if not libear_path: + logging.debug('intercept gonna use compiler wrappers') + environment.update(wrapper_environment(args)) + environment.update({ + 'CC': COMPILER_WRAPPER_CC, + 'CXX': COMPILER_WRAPPER_CXX + }) + elif sys.platform == 'darwin': + logging.debug('intercept gonna preload libear on OSX') + environment.update({ + 'DYLD_INSERT_LIBRARIES': libear_path, + 'DYLD_FORCE_FLAT_NAMESPACE': '1' + }) + else: + logging.debug('intercept gonna preload libear on UNIX') + environment.update({'LD_PRELOAD': libear_path}) + + return environment + + +@command_entry_point +def intercept_compiler_wrapper(): + """ Entry point for `intercept-cc` and `intercept-c++`. """ + + return compiler_wrapper(intercept_compiler_wrapper_impl) + + +def intercept_compiler_wrapper_impl(_, execution): + """ Implement intercept compiler wrapper functionality. + + It does generate execution report into target directory. + The target directory name is from environment variables. """ + + message_prefix = 'execution report might be incomplete: %s' + + target_dir = os.getenv('INTERCEPT_BUILD_TARGET_DIR') + if not target_dir: + logging.warning(message_prefix, 'missing target directory') + return + # write current execution info to the pid file + try: + target_file_name = str(os.getpid()) + TRACE_FILE_EXTENSION + target_file = os.path.join(target_dir, target_file_name) + logging.debug('writing execution report to: %s', target_file) + write_exec_trace(target_file, execution) + except IOError: + logging.warning(message_prefix, 'io problem') + + +def write_exec_trace(filename, entry): + """ Write execution report file. + + This method shall be sync with the execution report writer in interception + library. The entry in the file is a JSON objects. + + :param filename: path to the output execution trace file, + :param entry: the Execution object to append to that file. """ + + with open(filename, 'ab') as handler: + pid = str(entry.pid) + command = US.join(entry.cmd) + US + content = RS.join([pid, pid, 'wrapper', entry.cwd, command]) + GS + handler.write(content.encode('utf-8')) + + +def parse_exec_trace(filename): + """ Parse the file generated by the 'libear' preloaded library. + + Given filename points to a file which contains the basic report + generated by the interception library or wrapper command. A single + report file _might_ contain multiple process creation info. """ + + logging.debug('parse exec trace file: %s', filename) + with open(filename, 'r') as handler: + content = handler.read() + for group in filter(bool, content.split(GS)): + records = group.split(RS) + yield { + 'pid': records[0], + 'ppid': records[1], + 'function': records[2], + 'directory': records[3], + 'command': records[4].split(US)[:-1] + } + + +def format_entry(exec_trace): + """ Generate the desired fields for compilation database entries. """ + + def abspath(cwd, name): + """ Create normalized absolute path from input filename. """ + fullname = name if os.path.isabs(name) else os.path.join(cwd, name) + return os.path.normpath(fullname) + + 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': exec_trace['directory'], + 'command': encode(command), + 'file': abspath(exec_trace['directory'], source) + } + + +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 + Darwin by checking whether (1) there is a binary called 'csrutil' in + the path and, if so, (2) whether the output of executing 'csrutil status' + contains 'System Integrity Protection status: enabled'. + + :param platform: name of the platform (returned by sys.platform), + :return: True if library preload will fail by the dynamic linker. """ + + if platform in WRAPPER_ONLY_PLATFORMS: + return True + elif platform == 'darwin': + command = ['csrutil', 'status'] + pattern = re.compile(r'System Integrity Protection status:\s+enabled') + try: + return any(pattern.match(line) for line in run_command(command)) + except: + return False + else: + return False + + +def entry_hash(entry): + """ Implement unique hash method for compilation database entries. """ + + # For faster lookup in set filename is reverted + filename = entry['file'][::-1] + # For faster lookup in set directory is reverted + directory = entry['directory'][::-1] + # On OS X the 'cc' and 'c++' compilers are wrappers for + # 'clang' therefore both call would be logged. To avoid + # this the hash does not contain the first word of the + # command. + command = ' '.join(decode(entry['command'])[1:]) + + return '<>'.join([filename, directory, command]) diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/report.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/report.py new file mode 100644 index 00000000000..8bd6385fce6 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/report.py @@ -0,0 +1,505 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module is responsible to generate 'index.html' for the report. + +The input for this step is the output directory, where individual reports +could be found. It parses those reports and generates 'index.html'. """ + +import re +import os +import os.path +import sys +import shutil +import plistlib +import glob +import json +import logging +import datetime +from libscanbuild import duplicate_check +from libscanbuild.clang import get_version + +__all__ = ['document'] + + +def document(args): + """ Generates cover report and returns the number of bugs/crashes. """ + + html_reports_available = args.output_format in {'html', 'plist-html'} + + logging.debug('count crashes and bugs') + crash_count = sum(1 for _ in read_crashes(args.output)) + bug_counter = create_counters() + for bug in read_bugs(args.output, html_reports_available): + bug_counter(bug) + result = crash_count + bug_counter.total + + if html_reports_available and result: + use_cdb = os.path.exists(args.cdb) + + logging.debug('generate index.html file') + # common prefix for source files to have sorter path + prefix = commonprefix_from(args.cdb) if use_cdb else os.getcwd() + # assemble the cover from multiple fragments + fragments = [] + try: + if bug_counter.total: + fragments.append(bug_summary(args.output, bug_counter)) + fragments.append(bug_report(args.output, prefix)) + if crash_count: + fragments.append(crash_report(args.output, prefix)) + assemble_cover(args, prefix, fragments) + # copy additional files to the report + copy_resource_files(args.output) + if use_cdb: + shutil.copy(args.cdb, args.output) + finally: + for fragment in fragments: + os.remove(fragment) + return result + + +def assemble_cover(args, prefix, fragments): + """ Put together the fragments into a final report. """ + + import getpass + import socket + + if args.html_title is None: + args.html_title = os.path.basename(prefix) + ' - analyzer results' + + with open(os.path.join(args.output, 'index.html'), 'w') as handle: + indent = 0 + handle.write(reindent(""" + |<!DOCTYPE html> + |<html> + | <head> + | <title>{html_title}</title> + | <link type="text/css" rel="stylesheet" href="scanview.css"/> + | <script type='text/javascript' src="sorttable.js"></script> + | <script type='text/javascript' src='selectable.js'></script> + | </head>""", indent).format(html_title=args.html_title)) + handle.write(comment('SUMMARYENDHEAD')) + handle.write(reindent(""" + | <body> + | <h1>{html_title}</h1> + | <table> + | <tr><th>User:</th><td>{user_name}@{host_name}</td></tr> + | <tr><th>Working Directory:</th><td>{current_dir}</td></tr> + | <tr><th>Command Line:</th><td>{cmd_args}</td></tr> + | <tr><th>Clang Version:</th><td>{clang_version}</td></tr> + | <tr><th>Date:</th><td>{date}</td></tr> + | </table>""", indent).format(html_title=args.html_title, + user_name=getpass.getuser(), + host_name=socket.gethostname(), + current_dir=prefix, + cmd_args=' '.join(sys.argv), + clang_version=get_version(args.clang), + date=datetime.datetime.today( + ).strftime('%c'))) + for fragment in fragments: + # copy the content of fragments + with open(fragment, 'r') as input_handle: + shutil.copyfileobj(input_handle, handle) + handle.write(reindent(""" + | </body> + |</html>""", indent)) + + +def bug_summary(output_dir, bug_counter): + """ Bug summary is a HTML table to give a better overview of the bugs. """ + + name = os.path.join(output_dir, 'summary.html.fragment') + with open(name, 'w') as handle: + indent = 4 + handle.write(reindent(""" + |<h2>Bug Summary</h2> + |<table> + | <thead> + | <tr> + | <td>Bug Type</td> + | <td>Quantity</td> + | <td class="sorttable_nosort">Display?</td> + | </tr> + | </thead> + | <tbody>""", indent)) + handle.write(reindent(""" + | <tr style="font-weight:bold"> + | <td class="SUMM_DESC">All Bugs</td> + | <td class="Q">{0}</td> + | <td> + | <center> + | <input checked type="checkbox" id="AllBugsCheck" + | onClick="CopyCheckedStateToCheckButtons(this);"/> + | </center> + | </td> + | </tr>""", indent).format(bug_counter.total)) + for category, types in bug_counter.categories.items(): + handle.write(reindent(""" + | <tr> + | <th>{0}</th><th colspan=2></th> + | </tr>""", indent).format(category)) + for bug_type in types.values(): + handle.write(reindent(""" + | <tr> + | <td class="SUMM_DESC">{bug_type}</td> + | <td class="Q">{bug_count}</td> + | <td> + | <center> + | <input checked type="checkbox" + | onClick="ToggleDisplay(this,'{bug_type_class}');"/> + | </center> + | </td> + | </tr>""", indent).format(**bug_type)) + handle.write(reindent(""" + | </tbody> + |</table>""", indent)) + handle.write(comment('SUMMARYBUGEND')) + return name + + +def bug_report(output_dir, prefix): + """ Creates a fragment from the analyzer reports. """ + + pretty = prettify_bug(prefix, output_dir) + bugs = (pretty(bug) for bug in read_bugs(output_dir, True)) + + name = os.path.join(output_dir, 'bugs.html.fragment') + with open(name, 'w') as handle: + indent = 4 + handle.write(reindent(""" + |<h2>Reports</h2> + |<table class="sortable" style="table-layout:automatic"> + | <thead> + | <tr> + | <td>Bug Group</td> + | <td class="sorttable_sorted"> + | Bug Type + | <span id="sorttable_sortfwdind"> ▾</span> + | </td> + | <td>File</td> + | <td>Function/Method</td> + | <td class="Q">Line</td> + | <td class="Q">Path Length</td> + | <td class="sorttable_nosort"></td> + | </tr> + | </thead> + | <tbody>""", indent)) + handle.write(comment('REPORTBUGCOL')) + for current in bugs: + handle.write(reindent(""" + | <tr class="{bug_type_class}"> + | <td class="DESC">{bug_category}</td> + | <td class="DESC">{bug_type}</td> + | <td>{bug_file}</td> + | <td class="DESC">{bug_function}</td> + | <td class="Q">{bug_line}</td> + | <td class="Q">{bug_path_length}</td> + | <td><a href="{report_file}#EndPath">View Report</a></td> + | </tr>""", indent).format(**current)) + handle.write(comment('REPORTBUG', {'id': current['report_file']})) + handle.write(reindent(""" + | </tbody> + |</table>""", indent)) + handle.write(comment('REPORTBUGEND')) + return name + + +def crash_report(output_dir, prefix): + """ Creates a fragment from the compiler crashes. """ + + pretty = prettify_crash(prefix, output_dir) + crashes = (pretty(crash) for crash in read_crashes(output_dir)) + + name = os.path.join(output_dir, 'crashes.html.fragment') + with open(name, 'w') as handle: + indent = 4 + handle.write(reindent(""" + |<h2>Analyzer Failures</h2> + |<p>The analyzer had problems processing the following files:</p> + |<table> + | <thead> + | <tr> + | <td>Problem</td> + | <td>Source File</td> + | <td>Preprocessed File</td> + | <td>STDERR Output</td> + | </tr> + | </thead> + | <tbody>""", indent)) + for current in crashes: + handle.write(reindent(""" + | <tr> + | <td>{problem}</td> + | <td>{source}</td> + | <td><a href="{file}">preprocessor output</a></td> + | <td><a href="{stderr}">analyzer std err</a></td> + | </tr>""", indent).format(**current)) + handle.write(comment('REPORTPROBLEM', current)) + handle.write(reindent(""" + | </tbody> + |</table>""", indent)) + handle.write(comment('REPORTCRASHES')) + return name + + +def read_crashes(output_dir): + """ Generate a unique sequence of crashes from given output directory. """ + + return (parse_crash(filename) + for filename in glob.iglob(os.path.join(output_dir, 'failures', + '*.info.txt'))) + + +def read_bugs(output_dir, html): + # type: (str, bool) -> Generator[Dict[str, Any], None, None] + """ Generate a unique sequence of bugs from given output directory. + + Duplicates can be in a project if the same module was compiled multiple + times with different compiler options. These would be better to show in + the final report (cover) only once. """ + + def empty(file_name): + return os.stat(file_name).st_size == 0 + + duplicate = duplicate_check( + lambda bug: '{bug_line}.{bug_path_length}:{bug_file}'.format(**bug)) + + # get the right parser for the job. + parser = parse_bug_html if html else parse_bug_plist + # get the input files, which are not empty. + pattern = os.path.join(output_dir, '*.html' if html else '*.plist') + bug_files = (file for file in glob.iglob(pattern) if not empty(file)) + + for bug_file in bug_files: + for bug in parser(bug_file): + if not duplicate(bug): + yield bug + + +def parse_bug_plist(filename): + """ Returns the generator of bugs from a single .plist file. """ + + content = plistlib.readPlist(filename) + files = content.get('files') + for bug in content.get('diagnostics', []): + if len(files) <= int(bug['location']['file']): + logging.warning('Parsing bug from "%s" failed', filename) + continue + + yield { + 'result': filename, + 'bug_type': bug['type'], + 'bug_category': bug['category'], + 'bug_line': int(bug['location']['line']), + 'bug_path_length': int(bug['location']['col']), + 'bug_file': files[int(bug['location']['file'])] + } + + +def parse_bug_html(filename): + """ Parse out the bug information from HTML output. """ + + patterns = [re.compile(r'<!-- BUGTYPE (?P<bug_type>.*) -->$'), + re.compile(r'<!-- BUGFILE (?P<bug_file>.*) -->$'), + re.compile(r'<!-- BUGPATHLENGTH (?P<bug_path_length>.*) -->$'), + re.compile(r'<!-- BUGLINE (?P<bug_line>.*) -->$'), + re.compile(r'<!-- BUGCATEGORY (?P<bug_category>.*) -->$'), + re.compile(r'<!-- BUGDESC (?P<bug_description>.*) -->$'), + re.compile(r'<!-- FUNCTIONNAME (?P<bug_function>.*) -->$')] + endsign = re.compile(r'<!-- BUGMETAEND -->') + + bug = { + 'report_file': filename, + 'bug_function': 'n/a', # compatibility with < clang-3.5 + 'bug_category': 'Other', + 'bug_line': 0, + 'bug_path_length': 1 + } + + with open(filename) as handler: + for line in handler.readlines(): + # do not read the file further + if endsign.match(line): + break + # search for the right lines + for regex in patterns: + match = regex.match(line.strip()) + if match: + bug.update(match.groupdict()) + break + + encode_value(bug, 'bug_line', int) + encode_value(bug, 'bug_path_length', int) + + yield bug + + +def parse_crash(filename): + """ Parse out the crash information from the report file. """ + + match = re.match(r'(.*)\.info\.txt', filename) + name = match.group(1) if match else None + with open(filename, mode='rb') as handler: + # this is a workaround to fix windows read '\r\n' as new lines. + lines = [line.decode().rstrip() for line in handler.readlines()] + return { + 'source': lines[0], + 'problem': lines[1], + 'file': name, + 'info': name + '.info.txt', + 'stderr': name + '.stderr.txt' + } + + +def category_type_name(bug): + """ Create a new bug attribute from bug by category and type. + + The result will be used as CSS class selector in the final report. """ + + def smash(key): + """ Make value ready to be HTML attribute value. """ + + return bug.get(key, '').lower().replace(' ', '_').replace("'", '') + + return escape('bt_' + smash('bug_category') + '_' + smash('bug_type')) + + +def create_counters(): + """ Create counters for bug statistics. + + Two entries are maintained: 'total' is an integer, represents the + number of bugs. The 'categories' is a two level categorisation of bug + counters. The first level is 'bug category' the second is 'bug type'. + Each entry in this classification is a dictionary of 'count', 'type' + and 'label'. """ + + def predicate(bug): + bug_category = bug['bug_category'] + bug_type = bug['bug_type'] + current_category = predicate.categories.get(bug_category, dict()) + current_type = current_category.get(bug_type, { + 'bug_type': bug_type, + 'bug_type_class': category_type_name(bug), + 'bug_count': 0 + }) + current_type.update({'bug_count': current_type['bug_count'] + 1}) + current_category.update({bug_type: current_type}) + predicate.categories.update({bug_category: current_category}) + predicate.total += 1 + + predicate.total = 0 + predicate.categories = dict() + return predicate + + +def prettify_bug(prefix, output_dir): + def predicate(bug): + """ Make safe this values to embed into HTML. """ + + bug['bug_type_class'] = category_type_name(bug) + + encode_value(bug, 'bug_file', lambda x: escape(chop(prefix, x))) + encode_value(bug, 'bug_category', escape) + encode_value(bug, 'bug_type', escape) + encode_value(bug, 'report_file', lambda x: escape(chop(output_dir, x))) + return bug + + return predicate + + +def prettify_crash(prefix, output_dir): + def predicate(crash): + """ Make safe this values to embed into HTML. """ + + encode_value(crash, 'source', lambda x: escape(chop(prefix, x))) + encode_value(crash, 'problem', escape) + encode_value(crash, 'file', lambda x: escape(chop(output_dir, x))) + encode_value(crash, 'info', lambda x: escape(chop(output_dir, x))) + encode_value(crash, 'stderr', lambda x: escape(chop(output_dir, x))) + return crash + + return predicate + + +def copy_resource_files(output_dir): + """ Copy the javascript and css files to the report directory. """ + + this_dir = os.path.dirname(os.path.realpath(__file__)) + for resource in os.listdir(os.path.join(this_dir, 'resources')): + shutil.copy(os.path.join(this_dir, 'resources', resource), output_dir) + + +def encode_value(container, key, encode): + """ Run 'encode' on 'container[key]' value and update it. """ + + if key in container: + value = encode(container[key]) + container.update({key: value}) + + +def chop(prefix, filename): + """ Create 'filename' from '/prefix/filename' """ + + return filename if not len(prefix) else os.path.relpath(filename, prefix) + + +def escape(text): + """ Paranoid HTML escape method. (Python version independent) """ + + escape_table = { + '&': '&', + '"': '"', + "'": ''', + '>': '>', + '<': '<' + } + return ''.join(escape_table.get(c, c) for c in text) + + +def reindent(text, indent): + """ Utility function to format html output and keep indentation. """ + + result = '' + for line in text.splitlines(): + if len(line.strip()): + result += ' ' * indent + line.split('|')[1] + os.linesep + return result + + +def comment(name, opts=dict()): + """ Utility function to format meta information as comment. """ + + attributes = '' + for key, value in opts.items(): + attributes += ' {0}="{1}"'.format(key, value) + + return '<!-- {0}{1} -->{2}'.format(name, attributes, os.linesep) + + +def commonprefix_from(filename): + """ Create file prefix from a compilation database entries. """ + + with open(filename, 'r') as handle: + return commonprefix(item['file'] for item in json.load(handle)) + + +def commonprefix(files): + """ Fixed version of os.path.commonprefix. + + :param files: list of file names. + :return: the longest path prefix that is a prefix of all files. """ + result = None + for current in files: + if result is not None: + result = os.path.commonprefix([result, current]) + else: + result = current + + if result is None: + return '' + elif not os.path.isdir(result): + return os.path.dirname(result) + else: + return os.path.abspath(result) diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/scanview.css b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/scanview.css new file mode 100644 index 00000000000..cf8a5a6ad47 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/scanview.css @@ -0,0 +1,62 @@ +body { color:#000000; background-color:#ffffff } +body { font-family: Helvetica, sans-serif; font-size:9pt } +h1 { font-size: 14pt; } +h2 { font-size: 12pt; } +table { font-size:9pt } +table { border-spacing: 0px; border: 1px solid black } +th, table thead { + background-color:#eee; color:#666666; + font-weight: bold; cursor: default; + text-align:center; + font-weight: bold; font-family: Verdana; + white-space:nowrap; +} +.W { font-size:0px } +th, td { padding:5px; padding-left:8px; text-align:left } +td.SUMM_DESC { padding-left:12px } +td.DESC { white-space:pre } +td.Q { text-align:right } +td { text-align:left } +tbody.scrollContent { overflow:auto } + +table.form_group { + background-color: #ccc; + border: 1px solid #333; + padding: 2px; +} + +table.form_inner_group { + background-color: #ccc; + border: 1px solid #333; + padding: 0px; +} + +table.form { + background-color: #999; + border: 1px solid #333; + padding: 2px; +} + +td.form_label { + text-align: right; + vertical-align: top; +} +/* For one line entires */ +td.form_clabel { + text-align: right; + vertical-align: center; +} +td.form_value { + text-align: left; + vertical-align: top; +} +td.form_submit { + text-align: right; + vertical-align: top; +} + +h1.SubmitFail { + color: #f00; +} +h1.SubmitOk { +} diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/selectable.js b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/selectable.js new file mode 100644 index 00000000000..53f6a8da13d --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/selectable.js @@ -0,0 +1,47 @@ +function SetDisplay(RowClass, DisplayVal) +{ + var Rows = document.getElementsByTagName("tr"); + for ( var i = 0 ; i < Rows.length; ++i ) { + if (Rows[i].className == RowClass) { + Rows[i].style.display = DisplayVal; + } + } +} + +function CopyCheckedStateToCheckButtons(SummaryCheckButton) { + var Inputs = document.getElementsByTagName("input"); + for ( var i = 0 ; i < Inputs.length; ++i ) { + if (Inputs[i].type == "checkbox") { + if(Inputs[i] != SummaryCheckButton) { + Inputs[i].checked = SummaryCheckButton.checked; + Inputs[i].onclick(); + } + } + } +} + +function returnObjById( id ) { + if (document.getElementById) + var returnVar = document.getElementById(id); + else if (document.all) + var returnVar = document.all[id]; + else if (document.layers) + var returnVar = document.layers[id]; + return returnVar; +} + +var NumUnchecked = 0; + +function ToggleDisplay(CheckButton, ClassName) { + if (CheckButton.checked) { + SetDisplay(ClassName, ""); + if (--NumUnchecked == 0) { + returnObjById("AllBugsCheck").checked = true; + } + } + else { + SetDisplay(ClassName, "none"); + NumUnchecked++; + returnObjById("AllBugsCheck").checked = false; + } +} diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/sorttable.js b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/sorttable.js new file mode 100644 index 00000000000..32faa078d89 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/resources/sorttable.js @@ -0,0 +1,492 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backward compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=0; i<headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j<rows.length; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i=0; i<table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i=newrows.length-1; i>=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + sorttable.init(); // call the onload handler + } + }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff + var _timer = setInterval(function() { + if (/loaded|complete/.test(document.readyState)) { + sorttable.init(); // call the onload handler + } + }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; diff --git a/gnu/llvm/clang/tools/scan-build-py/libscanbuild/shell.py b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/shell.py new file mode 100644 index 00000000000..f9c08dfef2b --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/libscanbuild/shell.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" This module implements basic shell escaping/unescaping methods. """ + +import re +import shlex + +__all__ = ['encode', 'decode'] + + +def encode(command): + """ Takes a command as list and returns a string. """ + + def needs_quote(word): + """ Returns true if arguments needs to be protected by quotes. + + Previous implementation was shlex.split method, but that's not good + for this job. Currently is running through the string with a basic + state checking. """ + + reserved = {' ', '$', '%', '&', '(', ')', '[', ']', '{', '}', '*', '|', + '<', '>', '@', '?', '!'} + state = 0 + for current in word: + if state == 0 and current in reserved: + return True + elif state == 0 and current == '\\': + state = 1 + elif state == 1 and current in reserved | {'\\'}: + state = 0 + elif state == 0 and current == '"': + state = 2 + elif state == 2 and current == '"': + state = 0 + elif state == 0 and current == "'": + state = 3 + elif state == 3 and current == "'": + state = 0 + return state != 0 + + def escape(word): + """ Do protect argument if that's needed. """ + + table = {'\\': '\\\\', '"': '\\"'} + escaped = ''.join([table.get(c, c) for c in word]) + + return '"' + escaped + '"' if needs_quote(word) else escaped + + return " ".join([escape(arg) for arg in command]) + + +def decode(string): + """ Takes a command string and returns as a list. """ + + def unescape(arg): + """ Gets rid of the escaping characters. """ + + if len(arg) >= 2 and arg[0] == arg[-1] and arg[0] == '"': + arg = arg[1:-1] + return re.sub(r'\\(["\\])', r'\1', arg) + return re.sub(r'\\([\\ $%&\(\)\[\]\{\}\*|<>@?!])', r'\1', arg) + + return [unescape(arg) for arg in shlex.split(string)] diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/__init__.py b/gnu/llvm/clang/tools/scan-build-py/tests/__init__.py new file mode 100644 index 00000000000..9efd1602265 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/__init__.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import unittest + +import tests.unit +import tests.functional.cases + + +def suite(): + loader = unittest.TestLoader() + suite = unittest.TestSuite() + suite.addTests(loader.loadTestsFromModule(tests.unit)) + suite.addTests(loader.loadTestsFromModule(tests.functional.cases)) + return suite diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/__init__.py b/gnu/llvm/clang/tools/scan-build-py/tests/functional/__init__.py new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/__init__.py diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/__init__.py b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/__init__.py new file mode 100644 index 00000000000..7ac3456f98a --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/__init__.py @@ -0,0 +1,70 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import re +import os.path +import subprocess + + +def load_tests(loader, suite, pattern): + from . import test_from_cdb + suite.addTests(loader.loadTestsFromModule(test_from_cdb)) + from . import test_from_cmd + suite.addTests(loader.loadTestsFromModule(test_from_cmd)) + from . import test_create_cdb + suite.addTests(loader.loadTestsFromModule(test_create_cdb)) + from . import test_exec_anatomy + suite.addTests(loader.loadTestsFromModule(test_exec_anatomy)) + return suite + + +def make_args(target): + this_dir, _ = os.path.split(__file__) + path = os.path.normpath(os.path.join(this_dir, '..', 'src')) + return ['make', 'SRCDIR={}'.format(path), 'OBJDIR={}'.format(target), '-f', + os.path.join(path, 'build', 'Makefile')] + + +def silent_call(cmd, *args, **kwargs): + kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) + return subprocess.call(cmd, *args, **kwargs) + + +def silent_check_call(cmd, *args, **kwargs): + kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}) + return subprocess.check_call(cmd, *args, **kwargs) + + +def call_and_report(analyzer_cmd, build_cmd): + child = subprocess.Popen(analyzer_cmd + ['-v'] + build_cmd, + universal_newlines=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + pattern = re.compile('Report directory created: (.+)') + directory = None + for line in child.stdout.readlines(): + match = pattern.search(line) + if match and match.lastindex == 1: + directory = match.group(1) + break + child.stdout.close() + child.wait() + + return (child.returncode, directory) + + +def check_call_and_report(analyzer_cmd, build_cmd): + exit_code, result = call_and_report(analyzer_cmd, build_cmd) + if exit_code != 0: + raise subprocess.CalledProcessError( + exit_code, analyzer_cmd + build_cmd, None) + else: + return result + + +def create_empty_file(filename): + with open(filename, 'a') as handle: + pass diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_create_cdb.py b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_create_cdb.py new file mode 100644 index 00000000000..692a489b611 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_create_cdb.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +from . import make_args, silent_check_call, silent_call, create_empty_file +import unittest + +import os.path +import json + + +class CompilationDatabaseTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, args): + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + args + silent_check_call( + ['intercept-build', '--cdb', result] + make) + return result + + @staticmethod + def count_entries(filename): + with open(filename, 'r') as handler: + content = json.load(handler) + return len(content) + + def test_successful_build(self): + 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 libear.TemporaryDirectory() as tmpdir: + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + ['build_regular'] + silent_check_call(['intercept-build', '--cdb', result, + '--override-compiler'] + make) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + @unittest.skipIf(os.getenv('TRAVIS'), 'ubuntu make return -11') + def test_successful_build_parallel(self): + 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 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, + 'env', '-'] + make) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + def test_successful_build_all_in_one(self): + 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 libear.TemporaryDirectory() as tmpdir: + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + ['build_broken'] + silent_call( + ['intercept-build', '--cdb', result] + make) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(2, self.count_entries(result)) + + +class ExitCodeTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, target): + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + [target] + return silent_call( + ['intercept-build', '--cdb', result] + make) + + def test_successful_build(self): + with libear.TemporaryDirectory() as tmpdir: + exitcode = self.run_intercept(tmpdir, 'build_clean') + self.assertFalse(exitcode) + + def test_not_successful_build(self): + with libear.TemporaryDirectory() as tmpdir: + exitcode = self.run_intercept(tmpdir, 'build_broken') + self.assertTrue(exitcode) + + +class ResumeFeatureTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, target, args): + result = os.path.join(tmpdir, 'cdb.json') + make = make_args(tmpdir) + [target] + silent_check_call( + ['intercept-build', '--cdb', result] + args + make) + return result + + @staticmethod + def count_entries(filename): + with open(filename, 'r') as handler: + content = json.load(handler) + return len(content) + + def test_overwrite_existing_cdb(self): + 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', []) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(2, self.count_entries(result)) + + def test_append_to_existing_cdb(self): + 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']) + self.assertTrue(os.path.isfile(result)) + self.assertEqual(5, self.count_entries(result)) + + +class ResultFormatingTest(unittest.TestCase): + @staticmethod + def run_intercept(tmpdir, command): + result = os.path.join(tmpdir, 'cdb.json') + silent_check_call( + ['intercept-build', '--cdb', result] + command, + cwd=tmpdir) + with open(result, 'r') as handler: + content = json.load(handler) + return content + + def assert_creates_number_of_entries(self, command, count): + with libear.TemporaryDirectory() as tmpdir: + filename = os.path.join(tmpdir, 'test.c') + create_empty_file(filename) + command.append(filename) + cmd = ['sh', '-c', ' '.join(command)] + cdb = self.run_intercept(tmpdir, cmd) + self.assertEqual(count, len(cdb)) + + def test_filter_preprocessor_only_calls(self): + self.assert_creates_number_of_entries(['cc', '-c'], 1) + self.assert_creates_number_of_entries(['cc', '-c', '-E'], 0) + self.assert_creates_number_of_entries(['cc', '-c', '-M'], 0) + self.assert_creates_number_of_entries(['cc', '-c', '-MM'], 0) + + def assert_command_creates_entry(self, command, expected): + with libear.TemporaryDirectory() as tmpdir: + filename = os.path.join(tmpdir, command[-1]) + create_empty_file(filename) + cmd = ['sh', '-c', ' '.join(command)] + cdb = self.run_intercept(tmpdir, cmd) + self.assertEqual(' '.join(expected), cdb[0]['command']) + + def test_filter_preprocessor_flags(self): + self.assert_command_creates_entry( + ['cc', '-c', '-MD', 'test.c'], + ['cc', '-c', 'test.c']) + self.assert_command_creates_entry( + ['cc', '-c', '-MMD', 'test.c'], + ['cc', '-c', 'test.c']) + self.assert_command_creates_entry( + ['cc', '-c', '-MD', '-MF', 'test.d', 'test.c'], + ['cc', '-c', 'test.c']) + + def test_pass_language_flag(self): + self.assert_command_creates_entry( + ['cc', '-c', '-x', 'c', 'test.c'], + ['cc', '-c', '-x', 'c', 'test.c']) + self.assert_command_creates_entry( + ['cc', '-c', 'test.c'], + ['cc', '-c', 'test.c']) + + def test_pass_arch_flags(self): + self.assert_command_creates_entry( + ['clang', '-c', 'test.c'], + ['cc', '-c', 'test.c']) + self.assert_command_creates_entry( + ['clang', '-c', '-arch', 'i386', 'test.c'], + ['cc', '-c', '-arch', 'i386', 'test.c']) + self.assert_command_creates_entry( + ['clang', '-c', '-arch', 'i386', '-arch', 'armv7l', 'test.c'], + ['cc', '-c', '-arch', 'i386', '-arch', 'armv7l', 'test.c']) diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py new file mode 100644 index 00000000000..b0fec3db0c9 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_exec_anatomy.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +import unittest + +import os.path +import subprocess +import json + + +def run(source_dir, target_dir): + def execute(cmd): + return subprocess.check_call(cmd, + cwd=target_dir, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + execute(['cmake', source_dir]) + execute(['make']) + + result_file = os.path.join(target_dir, 'result.json') + expected_file = os.path.join(target_dir, 'expected.json') + execute(['intercept-build', '--cdb', result_file, './exec', + expected_file]) + return (expected_file, result_file) + + +class ExecAnatomyTest(unittest.TestCase): + def assertEqualJson(self, expected, result): + def read_json(filename): + with open(filename) as handler: + return json.load(handler) + + lhs = read_json(expected) + rhs = read_json(result) + for item in lhs: + self.assertTrue(rhs.count(item)) + for item in rhs: + self.assertTrue(lhs.count(item)) + + def test_all_exec_calls(self): + this_dir, _ = os.path.split(__file__) + source_dir = os.path.normpath(os.path.join(this_dir, '..', 'exec')) + with libear.TemporaryDirectory() as tmp_dir: + expected, result = run(source_dir, tmp_dir) + self.assertEqualJson(expected, result) diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_from_cdb.py b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_from_cdb.py new file mode 100644 index 00000000000..7af3eea4dd5 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_from_cdb.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +from . import call_and_report +import unittest + +import os.path +import string +import glob + + +def prepare_cdb(name, target_dir): + target_file = 'build_{0}.json'.format(name) + this_dir, _ = os.path.split(__file__) + path = os.path.normpath(os.path.join(this_dir, '..', 'src')) + source_dir = os.path.join(path, 'compilation_database') + source_file = os.path.join(source_dir, target_file + '.in') + target_file = os.path.join(target_dir, 'compile_commands.json') + with open(source_file, 'r') as in_handle: + with open(target_file, 'w') as out_handle: + for line in in_handle: + temp = string.Template(line) + out_handle.write(temp.substitute(path=path)) + return target_file + + +def run_analyzer(directory, cdb, args): + cmd = ['analyze-build', '--cdb', cdb, '--output', directory] \ + + args + return call_and_report(cmd, []) + + +class OutputDirectoryTest(unittest.TestCase): + def test_regular_keeps_report_dir(self): + 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 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 libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--keep-empty']) + self.assertTrue(os.path.isdir(reportdir)) + + +class ExitCodeTest(unittest.TestCase): + def test_regular_does_not_set_exit_code(self): + 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 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 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 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 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 libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('clean', tmpdir) + exit_code, __ = run_analyzer( + tmpdir, cdb, ['--status-bugs', '--plist']) + self.assertFalse(exit_code) + + +class OutputFormatTest(unittest.TestCase): + @staticmethod + def get_html_count(directory): + return len(glob.glob(os.path.join(directory, 'report-*.html'))) + + @staticmethod + def get_plist_count(directory): + return len(glob.glob(os.path.join(directory, 'report-*.plist'))) + + def test_default_creates_html_report(self): + with libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, []) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertEqual(self.get_html_count(reportdir), 2) + self.assertEqual(self.get_plist_count(reportdir), 0) + + def test_plist_and_html_creates_html_report(self): + with libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist-html']) + self.assertTrue( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertEqual(self.get_html_count(reportdir), 2) + self.assertEqual(self.get_plist_count(reportdir), 5) + + def test_plist_does_not_creates_html_report(self): + with libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('regular', tmpdir) + exit_code, reportdir = run_analyzer(tmpdir, cdb, ['--plist']) + self.assertFalse( + os.path.exists(os.path.join(reportdir, 'index.html'))) + self.assertEqual(self.get_html_count(reportdir), 0) + self.assertEqual(self.get_plist_count(reportdir), 5) + + +class FailureReportTest(unittest.TestCase): + def test_broken_creates_failure_reports(self): + 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 libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('broken', tmpdir) + exit_code, reportdir = run_analyzer( + tmpdir, cdb, ['--no-failure-reports']) + self.assertFalse( + os.path.isdir(os.path.join(reportdir, 'failures'))) + + +class TitleTest(unittest.TestCase): + def assertTitleEqual(self, directory, expected): + import re + patterns = [ + re.compile(r'<title>(?P<page>.*)</title>'), + re.compile(r'<h1>(?P<head>.*)</h1>') + ] + result = dict() + + index = os.path.join(directory, 'index.html') + with open(index, 'r') as handler: + for line in handler.readlines(): + for regex in patterns: + match = regex.match(line.strip()) + if match: + result.update(match.groupdict()) + break + self.assertEqual(result['page'], result['head']) + self.assertEqual(result['page'], expected) + + def test_default_title_in_report(self): + 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 libear.TemporaryDirectory() as tmpdir: + cdb = prepare_cdb('broken', tmpdir) + exit_code, reportdir = run_analyzer( + tmpdir, cdb, ['--html-title', 'this is the title']) + self.assertTitleEqual(reportdir, 'this is the title') diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_from_cmd.py b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_from_cmd.py new file mode 100644 index 00000000000..62e369c7c61 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/cases/test_from_cmd.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +from . import make_args, check_call_and_report, create_empty_file +import unittest + +import os +import os.path +import glob + + +class OutputDirectoryTest(unittest.TestCase): + + @staticmethod + def run_analyzer(outdir, args, cmd): + return check_call_and_report( + ['scan-build', '--intercept-first', '-o', outdir] + args, + cmd) + + def test_regular_keeps_report_dir(self): + 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 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 libear.TemporaryDirectory() as tmpdir: + make = make_args(tmpdir) + ['build_clean'] + outdir = self.run_analyzer(tmpdir, ['--keep-empty'], make) + self.assertTrue(os.path.isdir(outdir)) + + +class RunAnalyzerTest(unittest.TestCase): + + @staticmethod + def get_plist_count(directory): + return len(glob.glob(os.path.join(directory, 'report-*.plist'))) + + def test_interposition_works(self): + with libear.TemporaryDirectory() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler'], + make) + + self.assertTrue(os.path.isdir(outdir)) + self.assertEqual(self.get_plist_count(outdir), 5) + + def test_intercept_wrapper_works(self): + with libear.TemporaryDirectory() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--intercept-first', + '--override-compiler'], + make) + + self.assertTrue(os.path.isdir(outdir)) + self.assertEqual(self.get_plist_count(outdir), 5) + + def test_intercept_library_works(self): + with libear.TemporaryDirectory() as tmpdir: + make = make_args(tmpdir) + ['build_regular'] + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--intercept-first'], + make) + + self.assertTrue(os.path.isdir(outdir)) + self.assertEqual(self.get_plist_count(outdir), 5) + + @staticmethod + def compile_empty_source_file(target_dir, is_cxx): + compiler = '$CXX' if is_cxx else '$CC' + src_file_name = 'test.cxx' if is_cxx else 'test.c' + src_file = os.path.join(target_dir, src_file_name) + obj_file = os.path.join(target_dir, 'test.o') + create_empty_file(src_file) + command = ' '.join([compiler, '-c', src_file, '-o', obj_file]) + return ['sh', '-c', command] + + def test_interposition_cc_works(self): + 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 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 libear.TemporaryDirectory() as tmpdir: + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler', + '--intercept-first'], + self.compile_empty_source_file(tmpdir, False)) + self.assertEqual(self.get_plist_count(outdir), 1) + + def test_intercept_cxx_works(self): + with libear.TemporaryDirectory() as tmpdir: + outdir = check_call_and_report( + ['scan-build', '--plist', '-o', tmpdir, '--override-compiler', + '--intercept-first'], + self.compile_empty_source_file(tmpdir, True)) + self.assertEqual(self.get_plist_count(outdir), 1) diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/CMakeLists.txt b/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/CMakeLists.txt new file mode 100644 index 00000000000..42ee1d11db8 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/CMakeLists.txt @@ -0,0 +1,32 @@ +project(exec C) + +cmake_minimum_required(VERSION 3.4.3) + +include(CheckCCompilerFlag) +check_c_compiler_flag("-std=c99" C99_SUPPORTED) +if (C99_SUPPORTED) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") +endif() + +include(CheckFunctionExists) +include(CheckSymbolExists) + +add_definitions(-D_GNU_SOURCE) +list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + +check_function_exists(execve HAVE_EXECVE) +check_function_exists(execv HAVE_EXECV) +check_function_exists(execvpe HAVE_EXECVPE) +check_function_exists(execvp HAVE_EXECVP) +check_function_exists(execvP HAVE_EXECVP2) +check_function_exists(exect HAVE_EXECT) +check_function_exists(execl HAVE_EXECL) +check_function_exists(execlp HAVE_EXECLP) +check_function_exists(execle HAVE_EXECLE) +check_function_exists(posix_spawn HAVE_POSIX_SPAWN) +check_function_exists(posix_spawnp HAVE_POSIX_SPAWNP) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(exec main.c) diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/config.h.in b/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/config.h.in new file mode 100644 index 00000000000..2f07d0cbea9 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/config.h.in @@ -0,0 +1,19 @@ +/* -*- coding: utf-8 -*- +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#pragma once + +#cmakedefine HAVE_EXECVE +#cmakedefine HAVE_EXECV +#cmakedefine HAVE_EXECVPE +#cmakedefine HAVE_EXECVP +#cmakedefine HAVE_EXECVP2 +#cmakedefine HAVE_EXECT +#cmakedefine HAVE_EXECL +#cmakedefine HAVE_EXECLP +#cmakedefine HAVE_EXECLE +#cmakedefine HAVE_POSIX_SPAWN +#cmakedefine HAVE_POSIX_SPAWNP diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/main.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/main.c new file mode 100644 index 00000000000..0f5e01fb35c --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/exec/main.c @@ -0,0 +1,306 @@ +/* -*- coding: utf-8 -*- +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +*/ + +#include "config.h" + +#include <sys/wait.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <paths.h> + +#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP +#include <spawn.h> +#endif + +// ..:: environment access fixer - begin ::.. +#ifdef HAVE_NSGETENVIRON +#include <crt_externs.h> +#else +extern char **environ; +#endif + +char **get_environ() { +#ifdef HAVE_NSGETENVIRON + return *_NSGetEnviron(); +#else + return environ; +#endif +} +// ..:: environment access fixer - end ::.. + +// ..:: test fixtures - begin ::.. +static char const *cwd = NULL; +static FILE *fd = NULL; +static int need_comma = 0; + +void expected_out_open(const char *expected) { + cwd = getcwd(NULL, 0); + fd = fopen(expected, "w"); + if (!fd) { + perror("fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "[\n"); + need_comma = 0; +} + +void expected_out_close() { + fprintf(fd, "]\n"); + fclose(fd); + fd = NULL; + + free((void *)cwd); + cwd = NULL; +} + +void expected_out(const char *file) { + if (need_comma) + fprintf(fd, ",\n"); + else + need_comma = 1; + + fprintf(fd, "{\n"); + fprintf(fd, " \"directory\": \"%s\",\n", cwd); + fprintf(fd, " \"command\": \"cc -c %s\",\n", file); + fprintf(fd, " \"file\": \"%s/%s\"\n", cwd, file); + fprintf(fd, "}\n"); +} + +void create_source(char *file) { + FILE *fd = fopen(file, "w"); + if (!fd) { + perror("fopen"); + exit(EXIT_FAILURE); + } + fprintf(fd, "typedef int score;\n"); + fclose(fd); +} + +typedef void (*exec_fun)(); + +void wait_for(pid_t child) { + int status; + if (-1 == waitpid(child, &status, 0)) { + perror("wait"); + exit(EXIT_FAILURE); + } + if (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE) { + fprintf(stderr, "children process has non zero exit code\n"); + exit(EXIT_FAILURE); + } +} + +#define FORK(FUNC) \ + { \ + pid_t child = fork(); \ + if (-1 == child) { \ + perror("fork"); \ + exit(EXIT_FAILURE); \ + } else if (0 == child) { \ + FUNC fprintf(stderr, "children process failed to exec\n"); \ + exit(EXIT_FAILURE); \ + } else { \ + wait_for(child); \ + } \ + } +// ..:: test fixtures - end ::.. + +#ifdef HAVE_EXECV +void call_execv() { + char *const file = "execv.c"; + char *const compiler = "/usr/bin/cc"; + char *const argv[] = {"cc", "-c", file, 0}; + + expected_out(file); + create_source(file); + + FORK(execv(compiler, argv);) +} +#endif + +#ifdef HAVE_EXECVE +void call_execve() { + char *const file = "execve.c"; + char *const compiler = "/usr/bin/cc"; + char *const argv[] = {compiler, "-c", file, 0}; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(execve(compiler, argv, envp);) +} +#endif + +#ifdef HAVE_EXECVP +void call_execvp() { + char *const file = "execvp.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + FORK(execvp(compiler, argv);) +} +#endif + +#ifdef HAVE_EXECVP2 +void call_execvP() { + char *const file = "execv_p.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + FORK(execvP(compiler, _PATH_DEFPATH, argv);) +} +#endif + +#ifdef HAVE_EXECVPE +void call_execvpe() { + char *const file = "execvpe.c"; + char *const compiler = "cc"; + char *const argv[] = {"/usr/bin/cc", "-c", file, 0}; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(execvpe(compiler, argv, envp);) +} +#endif + +#ifdef HAVE_EXECT +void call_exect() { + char *const file = "exect.c"; + char *const compiler = "/usr/bin/cc"; + char *const argv[] = {compiler, "-c", file, 0}; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(exect(compiler, argv, envp);) +} +#endif + +#ifdef HAVE_EXECL +void call_execl() { + char *const file = "execl.c"; + char *const compiler = "/usr/bin/cc"; + + expected_out(file); + create_source(file); + + FORK(execl(compiler, "cc", "-c", file, (char *)0);) +} +#endif + +#ifdef HAVE_EXECLP +void call_execlp() { + char *const file = "execlp.c"; + char *const compiler = "cc"; + + expected_out(file); + create_source(file); + + FORK(execlp(compiler, compiler, "-c", file, (char *)0);) +} +#endif + +#ifdef HAVE_EXECLE +void call_execle() { + char *const file = "execle.c"; + char *const compiler = "/usr/bin/cc"; + char *const envp[] = {"THIS=THAT", 0}; + + expected_out(file); + create_source(file); + + FORK(execle(compiler, compiler, "-c", file, (char *)0, envp);) +} +#endif + +#ifdef HAVE_POSIX_SPAWN +void call_posix_spawn() { + char *const file = "posix_spawn.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + pid_t child; + if (0 != posix_spawn(&child, "/usr/bin/cc", 0, 0, argv, get_environ())) { + perror("posix_spawn"); + exit(EXIT_FAILURE); + } + wait_for(child); +} +#endif + +#ifdef HAVE_POSIX_SPAWNP +void call_posix_spawnp() { + char *const file = "posix_spawnp.c"; + char *const compiler = "cc"; + char *const argv[] = {compiler, "-c", file, 0}; + + expected_out(file); + create_source(file); + + pid_t child; + if (0 != posix_spawnp(&child, "cc", 0, 0, argv, get_environ())) { + perror("posix_spawnp"); + exit(EXIT_FAILURE); + } + wait_for(child); +} +#endif + +int main(int argc, char *const argv[]) { + if (argc != 2) + exit(EXIT_FAILURE); + + expected_out_open(argv[1]); +#ifdef HAVE_EXECV + call_execv(); +#endif +#ifdef HAVE_EXECVE + call_execve(); +#endif +#ifdef HAVE_EXECVP + call_execvp(); +#endif +#ifdef HAVE_EXECVP2 + call_execvP(); +#endif +#ifdef HAVE_EXECVPE + call_execvpe(); +#endif +#ifdef HAVE_EXECT + call_exect(); +#endif +#ifdef HAVE_EXECL + call_execl(); +#endif +#ifdef HAVE_EXECLP + call_execlp(); +#endif +#ifdef HAVE_EXECLE + call_execle(); +#endif +#ifdef HAVE_POSIX_SPAWN + call_posix_spawn(); +#endif +#ifdef HAVE_POSIX_SPAWNP + call_posix_spawnp(); +#endif + expected_out_close(); + return 0; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/broken-one.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/broken-one.c new file mode 100644 index 00000000000..f0550238132 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/broken-one.c @@ -0,0 +1,6 @@ +#include <notexisting.hpp> + +int value(int in) +{ + return 2 * in; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/broken-two.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/broken-two.c new file mode 100644 index 00000000000..7b4c12ff5c3 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/broken-two.c @@ -0,0 +1 @@ +int test() { ; diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/build/Makefile b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/build/Makefile new file mode 100644 index 00000000000..a8c0aafd0e5 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/build/Makefile @@ -0,0 +1,42 @@ +SRCDIR := .. +OBJDIR := . + +CFLAGS = -Wall -DDEBUG -Dvariable="value with space" -I $(SRCDIR)/include +LDFLAGS = +PROGRAM = $(OBJDIR)/prg + +$(OBJDIR)/main.o: $(SRCDIR)/main.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/main.c + +$(OBJDIR)/clean-one.o: $(SRCDIR)/clean-one.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/clean-one.c + +$(OBJDIR)/clean-two.o: $(SRCDIR)/clean-two.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/clean-two.c + +$(OBJDIR)/emit-one.o: $(SRCDIR)/emit-one.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/emit-one.c + +$(OBJDIR)/emit-two.o: $(SRCDIR)/emit-two.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/emit-two.c + +$(OBJDIR)/broken-one.o: $(SRCDIR)/broken-one.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/broken-one.c + +$(OBJDIR)/broken-two.o: $(SRCDIR)/broken-two.c + $(CC) $(CFLAGS) -c -o $@ $(SRCDIR)/broken-two.c + +$(PROGRAM): $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o $(OBJDIR)/emit-one.o $(OBJDIR)/emit-two.o + $(CC) $(LDFLAGS) -o $@ $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o $(OBJDIR)/emit-one.o $(OBJDIR)/emit-two.o + +build_regular: $(PROGRAM) + +build_clean: $(OBJDIR)/main.o $(OBJDIR)/clean-one.o $(OBJDIR)/clean-two.o + +build_broken: $(OBJDIR)/main.o $(OBJDIR)/broken-one.o $(OBJDIR)/broken-two.o + +build_all_in_one: $(SRCDIR)/main.c $(SRCDIR)/clean-one.c $(SRCDIR)/clean-two.c $(SRCDIR)/emit-one.c $(SRCDIR)/emit-two.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROGRAM) $(SRCDIR)/main.c $(SRCDIR)/clean-one.c $(SRCDIR)/clean-two.c $(SRCDIR)/emit-one.c $(SRCDIR)/emit-two.c + +clean: + rm -f $(PROGRAM) $(OBJDIR)/*.o diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/clean-one.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/clean-one.c new file mode 100644 index 00000000000..08c5f33609b --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/clean-one.c @@ -0,0 +1,13 @@ +#include <clean-one.h> + +int do_nothing_loop() +{ + int i = 32; + int idx = 0; + + for (idx = i; idx > 0; --idx) + { + i += idx; + } + return i; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/clean-two.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/clean-two.c new file mode 100644 index 00000000000..73bc288627d --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/clean-two.c @@ -0,0 +1,11 @@ +#include <clean-one.h> + +#include <stdlib.h> + +unsigned int another_method() +{ + unsigned int const size = do_nothing_loop(); + unsigned int const square = size * size; + + return square; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_broken.json.in b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_broken.json.in new file mode 100644 index 00000000000..104a4191cb1 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_broken.json.in @@ -0,0 +1,43 @@ +[ +{ + "directory": "${path}", + "command": "g++ -c -o main.o main.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/main.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o broken-one.o broken-one.c -Wall -DDEBUG \"-Dvariable=value with space\"", + "file": "${path}/broken-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o broken-two.o broken-two.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/broken-two.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o clean-one.o clean-one.c -Wall -DDEBUG \"-Dvariable=value with space\" -Iinclude", + "file": "${path}/clean-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o clean-two.o clean-two.c -Wall -DDEBUG -Dvariable=value -I ./include", + "file": "${path}/clean-two.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o emit-one.o emit-one.c -Wall -DDEBUG \"-Dvariable=value with space\"", + "file": "${path}/emit-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o emit-two.o emit-two.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/emit-two.c" +} +] diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_clean.json.in b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_clean.json.in new file mode 100644 index 00000000000..aa4dcde8e5e --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_clean.json.in @@ -0,0 +1,19 @@ +[ +{ + "directory": "${path}", + "command": "g++ -c -o main.o main.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/main.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o clean-one.o clean-one.c -Wall -DDEBUG \"-Dvariable=value with space\" -Iinclude", + "file": "${path}/clean-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o clean-two.o clean-two.c -Wall -DDEBUG -Dvariable=value -I ./include", + "file": "${path}/clean-two.c" +} +] diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_regular.json.in b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_regular.json.in new file mode 100644 index 00000000000..0200c1d8624 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/compilation_database/build_regular.json.in @@ -0,0 +1,31 @@ +[ +{ + "directory": "${path}", + "command": "g++ -c -o main.o main.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/main.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o clean-one.o clean-one.c -Wall -DDEBUG \"-Dvariable=value with space\" -Iinclude", + "file": "${path}/clean-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o clean-two.o clean-two.c -Wall -DDEBUG -Dvariable=value -I ./include", + "file": "${path}/clean-two.c" +} +, +{ + "directory": "${path}", + "command": "cc -c -o emit-one.o emit-one.c -Wall -DDEBUG \"-Dvariable=value with space\"", + "file": "${path}/emit-one.c" +} +, +{ + "directory": "${path}", + "command": "g++ -c -o emit-two.o emit-two.c -Wall -DDEBUG -Dvariable=value", + "file": "${path}/emit-two.c" +} +] diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/emit-one.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/emit-one.c new file mode 100644 index 00000000000..6cbd9cea72b --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/emit-one.c @@ -0,0 +1,23 @@ +#include <assert.h> + +int div(int numerator, int denominator) +{ + return numerator / denominator; +} + +void div_test() +{ + int i = 0; + for (i = 0; i < 2; ++i) + assert(div(2 * i, i) == 2); +} + +int do_nothing() +{ + unsigned int i = 0; + + int k = 100; + int j = k + 1; + + return j; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/emit-two.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/emit-two.c new file mode 100644 index 00000000000..faea77167f4 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/emit-two.c @@ -0,0 +1,13 @@ + +int bad_guy(int * i) +{ + *i = 9; + return *i; +} + +void bad_guy_test() +{ + int * ptr = 0; + + bad_guy(ptr); +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/include/clean-one.h b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/include/clean-one.h new file mode 100644 index 00000000000..695dbd04c65 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/include/clean-one.h @@ -0,0 +1,6 @@ +#ifndef CLEAN_ONE_H +#define CLEAN_ONE_H + +int do_nothing_loop(); + +#endif diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/main.c b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/main.c new file mode 100644 index 00000000000..905869dfa38 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/functional/src/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/unit/__init__.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/__init__.py new file mode 100644 index 00000000000..83a04743e6a --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from . import test_libear +from . import test_compilation +from . import test_clang +from . import test_report +from . import test_analyze +from . import test_intercept +from . import test_shell + + +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_report)) + suite.addTests(loader.loadTestsFromModule(test_analyze)) + suite.addTests(loader.loadTestsFromModule(test_intercept)) + suite.addTests(loader.loadTestsFromModule(test_shell)) + return suite diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_analyze.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_analyze.py new file mode 100644 index 00000000000..4b6f5d05211 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_analyze.py @@ -0,0 +1,414 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import unittest +import re +import os +import os.path +import libear +import libscanbuild.analyze as sut + + +class ReportDirectoryTest(unittest.TestCase): + + # Test that successive report directory names ascend in lexicographic + # order. This is required so that report directories from two runs of + # scan-build can be easily matched up to compare results. + def test_directory_name_comparison(self): + with libear.TemporaryDirectory() as tmpdir, \ + sut.report_directory(tmpdir, False) as report_dir1, \ + sut.report_directory(tmpdir, False) as report_dir2, \ + sut.report_directory(tmpdir, False) as report_dir3: + self.assertLess(report_dir1, report_dir2) + self.assertLess(report_dir2, report_dir3) + + +class FilteringFlagsTest(unittest.TestCase): + + def test_language_captured(self): + def test(flags): + cmd = ['clang', '-c', 'source.c'] + flags + opts = sut.classify_parameters(cmd) + return opts['language'] + + self.assertEqual(None, test([])) + self.assertEqual('c', test(['-x', 'c'])) + self.assertEqual('cpp', test(['-x', 'cpp'])) + + def test_arch(self): + def test(flags): + cmd = ['clang', '-c', 'source.c'] + flags + opts = sut.classify_parameters(cmd) + return opts['arch_list'] + + self.assertEqual([], test([])) + self.assertEqual(['mips'], test(['-arch', 'mips'])) + self.assertEqual(['mips', 'i386'], + test(['-arch', 'mips', '-arch', 'i386'])) + + def assertFlagsChanged(self, expected, flags): + cmd = ['clang', '-c', 'source.c'] + flags + opts = sut.classify_parameters(cmd) + self.assertEqual(expected, opts['flags']) + + def assertFlagsUnchanged(self, flags): + self.assertFlagsChanged(flags, flags) + + def assertFlagsFiltered(self, flags): + self.assertFlagsChanged([], flags) + + def test_optimalizations_pass(self): + self.assertFlagsUnchanged(['-O']) + self.assertFlagsUnchanged(['-O1']) + self.assertFlagsUnchanged(['-Os']) + self.assertFlagsUnchanged(['-O2']) + self.assertFlagsUnchanged(['-O3']) + + def test_include_pass(self): + self.assertFlagsUnchanged([]) + self.assertFlagsUnchanged(['-include', '/usr/local/include']) + self.assertFlagsUnchanged(['-I.']) + self.assertFlagsUnchanged(['-I', '.']) + self.assertFlagsUnchanged(['-I/usr/local/include']) + self.assertFlagsUnchanged(['-I', '/usr/local/include']) + self.assertFlagsUnchanged(['-I/opt', '-I', '/opt/otp/include']) + self.assertFlagsUnchanged(['-isystem', '/path']) + self.assertFlagsUnchanged(['-isystem=/path']) + + def test_define_pass(self): + self.assertFlagsUnchanged(['-DNDEBUG']) + self.assertFlagsUnchanged(['-UNDEBUG']) + self.assertFlagsUnchanged(['-Dvar1=val1', '-Dvar2=val2']) + self.assertFlagsUnchanged(['-Dvar="val ues"']) + + def test_output_filtered(self): + self.assertFlagsFiltered(['-o', 'source.o']) + + def test_some_warning_filtered(self): + self.assertFlagsFiltered(['-Wall']) + self.assertFlagsFiltered(['-Wnoexcept']) + self.assertFlagsFiltered(['-Wreorder', '-Wunused', '-Wundef']) + self.assertFlagsUnchanged(['-Wno-reorder', '-Wno-unused']) + + def test_compile_only_flags_pass(self): + self.assertFlagsUnchanged(['-std=C99']) + self.assertFlagsUnchanged(['-nostdinc']) + self.assertFlagsUnchanged(['-isystem', '/image/debian']) + self.assertFlagsUnchanged(['-iprefix', '/usr/local']) + self.assertFlagsUnchanged(['-iquote=me']) + self.assertFlagsUnchanged(['-iquote', 'me']) + + def test_compile_and_link_flags_pass(self): + self.assertFlagsUnchanged(['-fsinged-char']) + self.assertFlagsUnchanged(['-fPIC']) + self.assertFlagsUnchanged(['-stdlib=libc++']) + self.assertFlagsUnchanged(['--sysroot', '/']) + self.assertFlagsUnchanged(['-isysroot', '/']) + + def test_some_flags_filtered(self): + self.assertFlagsFiltered(['-g']) + self.assertFlagsFiltered(['-fsyntax-only']) + self.assertFlagsFiltered(['-save-temps']) + self.assertFlagsFiltered(['-init', 'my_init']) + self.assertFlagsFiltered(['-sectorder', 'a', 'b', 'c']) + + +class Spy(object): + def __init__(self): + self.arg = None + self.success = 0 + + def call(self, params): + self.arg = params + return self.success + + +class RunAnalyzerTest(unittest.TestCase): + + @staticmethod + def run_analyzer(content, failures_report): + with libear.TemporaryDirectory() as tmpdir: + filename = os.path.join(tmpdir, 'test.cpp') + with open(filename, 'w') as handle: + handle.write(content) + + opts = { + 'clang': 'clang', + 'directory': os.getcwd(), + 'flags': [], + 'direct_args': [], + 'file': filename, + 'output_dir': tmpdir, + 'output_format': 'plist', + 'output_failures': failures_report + } + spy = Spy() + result = sut.run_analyzer(opts, spy.call) + return (result, spy.arg) + + def test_run_analyzer(self): + content = "int div(int n, int d) { return n / d; }" + (result, fwds) = RunAnalyzerTest.run_analyzer(content, False) + self.assertEqual(None, fwds) + self.assertEqual(0, result['exit_code']) + + def test_run_analyzer_crash(self): + content = "int div(int n, int d) { return n / d }" + (result, fwds) = RunAnalyzerTest.run_analyzer(content, False) + self.assertEqual(None, fwds) + self.assertEqual(1, result['exit_code']) + + def test_run_analyzer_crash_and_forwarded(self): + content = "int div(int n, int d) { return n / d }" + (_, fwds) = RunAnalyzerTest.run_analyzer(content, True) + self.assertEqual(1, fwds['exit_code']) + self.assertTrue(len(fwds['error_output']) > 0) + + +class ReportFailureTest(unittest.TestCase): + + def assertUnderFailures(self, path): + self.assertEqual('failures', os.path.basename(os.path.dirname(path))) + + def test_report_failure_create_files(self): + with libear.TemporaryDirectory() as tmpdir: + # create input file + filename = os.path.join(tmpdir, 'test.c') + with open(filename, 'w') as handle: + handle.write('int main() { return 0') + uname_msg = ' '.join(os.uname()) + os.linesep + error_msg = 'this is my error output' + # execute test + opts = { + 'clang': 'clang', + 'directory': os.getcwd(), + 'flags': [], + 'file': filename, + 'output_dir': tmpdir, + 'language': 'c', + 'error_type': 'other_error', + 'error_output': error_msg, + 'exit_code': 13 + } + sut.report_failure(opts) + # verify the result + result = dict() + pp_file = None + for root, _, files in os.walk(tmpdir): + keys = [os.path.join(root, name) for name in files] + for key in keys: + with open(key, 'r') as handle: + result[key] = handle.readlines() + if re.match(r'^(.*/)+clang(.*)\.i$', key): + pp_file = key + + # prepocessor file generated + self.assertUnderFailures(pp_file) + # info file generated and content dumped + info_file = pp_file + '.info.txt' + self.assertTrue(info_file in result) + self.assertEqual('Other Error\n', result[info_file][1]) + self.assertEqual(uname_msg, result[info_file][3]) + # error file generated and content dumped + error_file = pp_file + '.stderr.txt' + self.assertTrue(error_file in result) + self.assertEqual([error_msg], result[error_file]) + + +class AnalyzerTest(unittest.TestCase): + + def test_nodebug_macros_appended(self): + def test(flags): + spy = Spy() + opts = {'flags': flags, 'force_debug': True} + self.assertEqual(spy.success, + sut.filter_debug_flags(opts, spy.call)) + return spy.arg['flags'] + + self.assertEqual(['-UNDEBUG'], test([])) + self.assertEqual(['-DNDEBUG', '-UNDEBUG'], test(['-DNDEBUG'])) + self.assertEqual(['-DSomething', '-UNDEBUG'], test(['-DSomething'])) + + def test_set_language_fall_through(self): + def language(expected, input): + spy = Spy() + input.update({'compiler': 'c', 'file': 'test.c'}) + self.assertEqual(spy.success, sut.language_check(input, spy.call)) + self.assertEqual(expected, spy.arg['language']) + + language('c', {'language': 'c', 'flags': []}) + language('c++', {'language': 'c++', 'flags': []}) + + def test_set_language_stops_on_not_supported(self): + spy = Spy() + input = { + 'compiler': 'c', + 'flags': [], + 'file': 'test.java', + 'language': 'java' + } + self.assertIsNone(sut.language_check(input, spy.call)) + self.assertIsNone(spy.arg) + + def test_set_language_sets_flags(self): + def flags(expected, input): + spy = Spy() + input.update({'compiler': 'c', 'file': 'test.c'}) + self.assertEqual(spy.success, sut.language_check(input, spy.call)) + self.assertEqual(expected, spy.arg['flags']) + + flags(['-x', 'c'], {'language': 'c', 'flags': []}) + flags(['-x', 'c++'], {'language': 'c++', 'flags': []}) + + def test_set_language_from_filename(self): + def language(expected, input): + spy = Spy() + input.update({'language': None, 'flags': []}) + self.assertEqual(spy.success, sut.language_check(input, spy.call)) + self.assertEqual(expected, spy.arg['language']) + + language('c', {'file': 'file.c', 'compiler': 'c'}) + language('c++', {'file': 'file.c', 'compiler': 'c++'}) + language('c++', {'file': 'file.cxx', 'compiler': 'c'}) + language('c++', {'file': 'file.cxx', 'compiler': 'c++'}) + language('c++', {'file': 'file.cpp', 'compiler': 'c++'}) + language('c-cpp-output', {'file': 'file.i', 'compiler': 'c'}) + language('c++-cpp-output', {'file': 'file.i', 'compiler': 'c++'}) + + def test_arch_loop_sets_flags(self): + def flags(archs): + spy = Spy() + input = {'flags': [], 'arch_list': archs} + sut.arch_check(input, spy.call) + return spy.arg['flags'] + + self.assertEqual([], flags([])) + self.assertEqual(['-arch', 'i386'], flags(['i386'])) + self.assertEqual(['-arch', 'i386'], flags(['i386', 'ppc'])) + self.assertEqual(['-arch', 'sparc'], flags(['i386', 'sparc'])) + + def test_arch_loop_stops_on_not_supported(self): + def stop(archs): + spy = Spy() + input = {'flags': [], 'arch_list': archs} + self.assertIsNone(sut.arch_check(input, spy.call)) + self.assertIsNone(spy.arg) + + stop(['ppc']) + stop(['ppc64']) + + +@sut.require([]) +def method_without_expecteds(opts): + return 0 + + +@sut.require(['this', 'that']) +def method_with_expecteds(opts): + return 0 + + +@sut.require([]) +def method_exception_from_inside(opts): + raise Exception('here is one') + + +class RequireDecoratorTest(unittest.TestCase): + + def test_method_without_expecteds(self): + self.assertEqual(method_without_expecteds(dict()), 0) + self.assertEqual(method_without_expecteds({}), 0) + self.assertEqual(method_without_expecteds({'this': 2}), 0) + self.assertEqual(method_without_expecteds({'that': 3}), 0) + + def test_method_with_expecteds(self): + self.assertRaises(KeyError, method_with_expecteds, dict()) + self.assertRaises(KeyError, method_with_expecteds, {}) + self.assertRaises(KeyError, method_with_expecteds, {'this': 2}) + self.assertRaises(KeyError, method_with_expecteds, {'that': 3}) + self.assertEqual(method_with_expecteds({'this': 0, 'that': 3}), 0) + + def test_method_exception_not_caught(self): + self.assertRaises(Exception, method_exception_from_inside, dict()) + + +class PrefixWithTest(unittest.TestCase): + + def test_gives_empty_on_empty(self): + res = sut.prefix_with(0, []) + self.assertFalse(res) + + def test_interleaves_prefix(self): + res = sut.prefix_with(0, [1, 2, 3]) + self.assertListEqual([0, 1, 0, 2, 0, 3], res) + + +class MergeCtuMapTest(unittest.TestCase): + + def test_no_map_gives_empty(self): + pairs = sut.create_global_ctu_extdef_map([]) + self.assertFalse(pairs) + + def test_multiple_maps_merged(self): + concat_map = ['c:@F@fun1#I# ast/fun1.c.ast', + 'c:@F@fun2#I# ast/fun2.c.ast', + 'c:@F@fun3#I# ast/fun3.c.ast'] + pairs = sut.create_global_ctu_extdef_map(concat_map) + self.assertTrue(('c:@F@fun1#I#', 'ast/fun1.c.ast') in pairs) + self.assertTrue(('c:@F@fun2#I#', 'ast/fun2.c.ast') in pairs) + self.assertTrue(('c:@F@fun3#I#', 'ast/fun3.c.ast') in pairs) + self.assertEqual(3, len(pairs)) + + def test_not_unique_func_left_out(self): + concat_map = ['c:@F@fun1#I# ast/fun1.c.ast', + 'c:@F@fun2#I# ast/fun2.c.ast', + 'c:@F@fun1#I# ast/fun7.c.ast'] + pairs = sut.create_global_ctu_extdef_map(concat_map) + self.assertFalse(('c:@F@fun1#I#', 'ast/fun1.c.ast') in pairs) + self.assertFalse(('c:@F@fun1#I#', 'ast/fun7.c.ast') in pairs) + self.assertTrue(('c:@F@fun2#I#', 'ast/fun2.c.ast') in pairs) + self.assertEqual(1, len(pairs)) + + def test_duplicates_are_kept(self): + concat_map = ['c:@F@fun1#I# ast/fun1.c.ast', + 'c:@F@fun2#I# ast/fun2.c.ast', + 'c:@F@fun1#I# ast/fun1.c.ast'] + pairs = sut.create_global_ctu_extdef_map(concat_map) + self.assertTrue(('c:@F@fun1#I#', 'ast/fun1.c.ast') in pairs) + self.assertTrue(('c:@F@fun2#I#', 'ast/fun2.c.ast') in pairs) + self.assertEqual(2, len(pairs)) + + def test_space_handled_in_source(self): + concat_map = ['c:@F@fun1#I# ast/f un.c.ast'] + pairs = sut.create_global_ctu_extdef_map(concat_map) + self.assertTrue(('c:@F@fun1#I#', 'ast/f un.c.ast') in pairs) + self.assertEqual(1, len(pairs)) + + +class ExtdefMapSrcToAstTest(unittest.TestCase): + + def test_empty_gives_empty(self): + fun_ast_lst = sut.extdef_map_list_src_to_ast([]) + self.assertFalse(fun_ast_lst) + + def test_sources_to_asts(self): + fun_src_lst = ['c:@F@f1#I# ' + os.path.join(os.sep + 'path', 'f1.c'), + 'c:@F@f2#I# ' + os.path.join(os.sep + 'path', 'f2.c')] + fun_ast_lst = sut.extdef_map_list_src_to_ast(fun_src_lst) + self.assertTrue('c:@F@f1#I# ' + + os.path.join('ast', 'path', 'f1.c.ast') + in fun_ast_lst) + self.assertTrue('c:@F@f2#I# ' + + os.path.join('ast', 'path', 'f2.c.ast') + in fun_ast_lst) + self.assertEqual(2, len(fun_ast_lst)) + + def test_spaces_handled(self): + fun_src_lst = ['c:@F@f1#I# ' + os.path.join(os.sep + 'path', 'f 1.c')] + fun_ast_lst = sut.extdef_map_list_src_to_ast(fun_src_lst) + self.assertTrue('c:@F@f1#I# ' + + os.path.join('ast', 'path', 'f 1.c.ast') + in fun_ast_lst) + self.assertEqual(1, len(fun_ast_lst)) diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_clang.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_clang.py new file mode 100644 index 00000000000..80ce61a1fab --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_clang.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +import libscanbuild.clang as sut +import unittest +import os.path +import sys + + +class ClangGetVersion(unittest.TestCase): + def test_get_version_is_not_empty(self): + self.assertTrue(sut.get_version('clang')) + + def test_get_version_throws(self): + with self.assertRaises(OSError): + sut.get_version('notexists') + + +class ClangGetArgumentsTest(unittest.TestCase): + def test_get_clang_arguments(self): + with libear.TemporaryDirectory() as tmpdir: + filename = os.path.join(tmpdir, 'test.c') + with open(filename, 'w') as handle: + handle.write('') + + result = sut.get_arguments( + ['clang', '-c', filename, '-DNDEBUG', '-Dvar="this is it"'], + tmpdir) + + self.assertTrue('NDEBUG' in result) + self.assertTrue('var="this is it"' in result) + + def test_get_clang_arguments_fails(self): + with self.assertRaises(Exception): + sut.get_arguments(['clang', '-x', 'c', 'notexist.c'], '.') + + def test_get_clang_arguments_fails_badly(self): + with self.assertRaises(OSError): + sut.get_arguments(['notexist'], '.') + + +class ClangGetCheckersTest(unittest.TestCase): + def test_get_checkers(self): + # this test is only to see is not crashing + result = sut.get_checkers('clang', []) + self.assertTrue(len(result)) + # do check result types + string_type = unicode if sys.version_info < (3,) else str + for key, value in result.items(): + self.assertEqual(string_type, type(key)) + self.assertEqual(string_type, type(value[0])) + self.assertEqual(bool, type(value[1])) + + def test_get_active_checkers(self): + # this test is only to see is not crashing + result = sut.get_active_checkers('clang', []) + self.assertTrue(len(result)) + # do check result types + for value in result: + self.assertEqual(str, type(value)) + + def test_is_active(self): + test = sut.is_active(['a', 'b.b', 'c.c.c']) + + self.assertTrue(test('a')) + self.assertTrue(test('a.b')) + self.assertTrue(test('b.b')) + self.assertTrue(test('b.b.c')) + self.assertTrue(test('c.c.c.p')) + + self.assertFalse(test('ab')) + self.assertFalse(test('ba')) + self.assertFalse(test('bb')) + self.assertFalse(test('c.c')) + self.assertFalse(test('b')) + self.assertFalse(test('d')) + + def test_parse_checkers(self): + lines = [ + 'OVERVIEW: Clang Static Analyzer Checkers List', + '', + 'CHECKERS:', + ' checker.one Checker One description', + ' checker.two', + ' Checker Two description'] + result = dict(sut.parse_checkers(lines)) + self.assertTrue('checker.one' in result) + self.assertEqual('Checker One description', result.get('checker.one')) + self.assertTrue('checker.two' in result) + self.assertEqual('Checker Two description', result.get('checker.two')) + + +class ClangIsCtuCapableTest(unittest.TestCase): + def test_ctu_not_found(self): + is_ctu = sut.is_ctu_capable('not-found-clang-extdef-mapping') + self.assertFalse(is_ctu) + + +class ClangGetTripleArchTest(unittest.TestCase): + def test_arch_is_not_empty(self): + arch = sut.get_triple_arch(['clang', '-E', '-'], '.') + self.assertTrue(len(arch) > 0) diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_compilation.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_compilation.py new file mode 100644 index 00000000000..e8ad3d8c99f --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_compilation.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +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/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_intercept.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_intercept.py new file mode 100644 index 00000000000..5473b88d833 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_intercept.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +import libscanbuild.intercept as sut +import unittest +import os.path + + +class InterceptUtilTest(unittest.TestCase): + + def test_format_entry_filters_action(self): + def test(command): + 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'])) + + def test_format_entry_normalize_filename(self): + parent = os.path.join(os.sep, 'home', 'me') + current = os.path.join(parent, 'project') + + def test(filename): + trace = {'directory': current, 'command': ['cc', '-c', filename]} + return list(sut.format_entry(trace))[0]['file'] + + 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): + content = """#!/usr/bin/env sh + echo 'sa-la-la-la' + echo 'la-la-la' + echo '{0}' + echo 'sa-la-la-la' + echo 'la-la-la' + """.format(message) + lines = [line.strip() for line in content.split('\n')] + with open(filename, 'w') as handle: + handle.write('\n'.join(lines)) + handle.close() + os.chmod(filename, 0x1ff) + + def create_csrutil(dest_dir, status): + filename = os.path.join(dest_dir, 'csrutil') + message = 'System Integrity Protection status: {0}'.format(status) + return create_status_report(filename, message) + + def create_sestatus(dest_dir, status): + filename = os.path.join(dest_dir, 'sestatus') + message = 'SELinux status:\t{0}'.format(status) + return create_status_report(filename, message) + + ENABLED = 'enabled' + DISABLED = 'disabled' + + OSX = 'darwin' + + with libear.TemporaryDirectory() as tmpdir: + saved = os.environ['PATH'] + try: + os.environ['PATH'] = tmpdir + ':' + saved + + create_csrutil(tmpdir, ENABLED) + self.assertTrue(sut.is_preload_disabled(OSX)) + + create_csrutil(tmpdir, DISABLED) + self.assertFalse(sut.is_preload_disabled(OSX)) + finally: + os.environ['PATH'] = saved + + saved = os.environ['PATH'] + try: + os.environ['PATH'] = '' + # shall be false when it's not in the path + self.assertFalse(sut.is_preload_disabled(OSX)) + + self.assertFalse(sut.is_preload_disabled('unix')) + finally: + os.environ['PATH'] = saved diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_libear.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_libear.py new file mode 100644 index 00000000000..933da50242f --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_libear.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +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/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_report.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_report.py new file mode 100644 index 00000000000..60ec0d855ff --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_report.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libear +import libscanbuild.report as sut +import unittest +import os +import os.path + + +def run_bug_parse(content): + with libear.TemporaryDirectory() as tmpdir: + file_name = os.path.join(tmpdir, 'test.html') + with open(file_name, 'w') as handle: + handle.writelines(content) + for bug in sut.parse_bug_html(file_name): + return bug + + +def run_crash_parse(content, preproc): + with libear.TemporaryDirectory() as tmpdir: + file_name = os.path.join(tmpdir, preproc + '.info.txt') + with open(file_name, 'w') as handle: + handle.writelines(content) + return sut.parse_crash(file_name) + + +class ParseFileTest(unittest.TestCase): + + def test_parse_bug(self): + content = [ + "some header\n", + "<!-- BUGDESC Division by zero -->\n", + "<!-- BUGTYPE Division by zero -->\n", + "<!-- BUGCATEGORY Logic error -->\n", + "<!-- BUGFILE xx -->\n", + "<!-- BUGLINE 5 -->\n", + "<!-- BUGCOLUMN 22 -->\n", + "<!-- BUGPATHLENGTH 4 -->\n", + "<!-- BUGMETAEND -->\n", + "<!-- REPORTHEADER -->\n", + "some tails\n"] + result = run_bug_parse(content) + self.assertEqual(result['bug_category'], 'Logic error') + self.assertEqual(result['bug_path_length'], 4) + self.assertEqual(result['bug_line'], 5) + self.assertEqual(result['bug_description'], 'Division by zero') + self.assertEqual(result['bug_type'], 'Division by zero') + self.assertEqual(result['bug_file'], 'xx') + + def test_parse_bug_empty(self): + content = [] + result = run_bug_parse(content) + self.assertEqual(result['bug_category'], 'Other') + self.assertEqual(result['bug_path_length'], 1) + self.assertEqual(result['bug_line'], 0) + + def test_parse_crash(self): + content = [ + "/some/path/file.c\n", + "Some very serious Error\n", + "bla\n", + "bla-bla\n"] + result = run_crash_parse(content, 'file.i') + self.assertEqual(result['source'], content[0].rstrip()) + self.assertEqual(result['problem'], content[1].rstrip()) + self.assertEqual(os.path.basename(result['file']), + 'file.i') + self.assertEqual(os.path.basename(result['info']), + 'file.i.info.txt') + self.assertEqual(os.path.basename(result['stderr']), + 'file.i.stderr.txt') + + def test_parse_real_crash(self): + import libscanbuild.analyze as sut2 + import re + 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 = { + '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 + for root, _, files in os.walk(tmpdir): + keys = [os.path.join(root, name) for name in files] + for key in keys: + if re.match(r'^(.*/)+clang(.*)\.i$', key): + pp_file = key + self.assertIsNot(pp_file, None) + # read the failure report back + result = sut.parse_crash(pp_file + '.info.txt') + self.assertEqual(result['source'], filename) + self.assertEqual(result['problem'], 'Other Error') + self.assertEqual(result['file'], pp_file) + self.assertEqual(result['info'], pp_file + '.info.txt') + self.assertEqual(result['stderr'], pp_file + '.stderr.txt') + + +class ReportMethodTest(unittest.TestCase): + + def test_chop(self): + self.assertEqual('file', sut.chop('/prefix', '/prefix/file')) + self.assertEqual('file', sut.chop('/prefix/', '/prefix/file')) + self.assertEqual('lib/file', sut.chop('/prefix/', '/prefix/lib/file')) + self.assertEqual('/prefix/file', sut.chop('', '/prefix/file')) + + def test_chop_when_cwd(self): + self.assertEqual('../src/file', sut.chop('/cwd', '/src/file')) + self.assertEqual('../src/file', sut.chop('/prefix/cwd', + '/prefix/src/file')) + + +class GetPrefixFromCompilationDatabaseTest(unittest.TestCase): + + def test_with_different_filenames(self): + self.assertEqual( + sut.commonprefix(['/tmp/a.c', '/tmp/b.c']), '/tmp') + + def test_with_different_dirnames(self): + self.assertEqual( + sut.commonprefix(['/tmp/abs/a.c', '/tmp/ack/b.c']), '/tmp') + + def test_no_common_prefix(self): + self.assertEqual( + sut.commonprefix(['/tmp/abs/a.c', '/usr/ack/b.c']), '/') + + def test_with_single_file(self): + self.assertEqual( + sut.commonprefix(['/tmp/a.c']), '/tmp') + + def test_empty(self): + self.assertEqual( + sut.commonprefix([]), '') diff --git a/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_shell.py b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_shell.py new file mode 100644 index 00000000000..6ffbb8782a9 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build-py/tests/unit/test_shell.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import libscanbuild.shell as sut +import unittest + + +class ShellTest(unittest.TestCase): + + def test_encode_decode_are_same(self): + def test(value): + self.assertEqual(sut.encode(sut.decode(value)), value) + + test("") + test("clang") + test("clang this and that") + + def test_decode_encode_are_same(self): + def test(value): + self.assertEqual(sut.decode(sut.encode(value)), value) + + test([]) + test(['clang']) + test(['clang', 'this', 'and', 'that']) + test(['clang', 'this and', 'that']) + test(['clang', "it's me", 'again']) + test(['clang', 'some "words" are', 'quoted']) + + def test_encode(self): + self.assertEqual(sut.encode(['clang', "it's me", 'again']), + 'clang "it\'s me" again') + self.assertEqual(sut.encode(['clang', "it(s me", 'again)']), + 'clang "it(s me" "again)"') + self.assertEqual(sut.encode(['clang', 'redirect > it']), + 'clang "redirect > it"') + self.assertEqual(sut.encode(['clang', '-DKEY="VALUE"']), + 'clang -DKEY=\\"VALUE\\"') + self.assertEqual(sut.encode(['clang', '-DKEY="value with spaces"']), + 'clang -DKEY=\\"value with spaces\\"') diff --git a/gnu/llvm/clang/tools/scan-build/CMakeLists.txt b/gnu/llvm/clang/tools/scan-build/CMakeLists.txt new file mode 100644 index 00000000000..28241245fcb --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/CMakeLists.txt @@ -0,0 +1,99 @@ +option(CLANG_INSTALL_SCANBUILD "Install the scan-build tool" ON) + +include(GNUInstallDirs) + +if (WIN32 AND NOT CYGWIN) + set(BinFiles + scan-build + scan-build.bat) + set(LibexecFiles + ccc-analyzer + c++-analyzer + ccc-analyzer.bat + c++-analyzer.bat) +else() + set(BinFiles + scan-build) + set(LibexecFiles + ccc-analyzer + c++-analyzer) + if (APPLE) + list(APPEND BinFiles + set-xcode-analyzer) + endif() +endif() + +set(ManPages + scan-build.1) + +set(ShareFiles + scanview.css + sorttable.js) + + +if(CLANG_INSTALL_SCANBUILD) + foreach(BinFile ${BinFiles}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/${BinFile} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile} + ${CMAKE_BINARY_DIR}/bin/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile}) + install(PROGRAMS bin/${BinFile} + DESTINATION bin + COMPONENT scan-build) + endforeach() + + foreach(LibexecFile ${LibexecFiles}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/libexec/${LibexecFile} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/libexec + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${LibexecFile} + ${CMAKE_BINARY_DIR}/libexec/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libexec/${LibexecFile}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/libexec/${LibexecFile}) + install(PROGRAMS libexec/${LibexecFile} + DESTINATION libexec + COMPONENT scan-build) + endforeach() + + foreach(ManPage ${ManPages}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/${ManPage} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1 + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/man/${ManPage} + ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/man/${ManPage}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_MANDIR}/man1/${ManPage}) + install(PROGRAMS man/${ManPage} + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + COMPONENT scan-build) + endforeach() + + foreach(ShareFile ${ShareFiles}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/share/scan-build/${ShareFile} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/share/scan-build + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/share/scan-build/${ShareFile} + ${CMAKE_BINARY_DIR}/share/scan-build/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/share/scan-build/${ShareFile}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/share/scan-build/${ShareFile}) + install(FILES share/scan-build/${ShareFile} + DESTINATION share/scan-build + COMPONENT scan-build) + endforeach() + + add_custom_target(scan-build ALL DEPENDS ${Depends}) + set_target_properties(scan-build PROPERTIES FOLDER "Misc") + + if(NOT LLVM_ENABLE_IDE) + add_llvm_install_targets("install-scan-build" + DEPENDS scan-build + COMPONENT scan-build) + endif() +endif() diff --git a/gnu/llvm/clang/tools/scan-build/bin/scan-build b/gnu/llvm/clang/tools/scan-build/bin/scan-build new file mode 100755 index 00000000000..1574b10f205 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/bin/scan-build @@ -0,0 +1,1963 @@ +#!/usr/bin/env perl +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +##===----------------------------------------------------------------------===## +# +# A script designed to wrap a build so that all calls to gcc are intercepted +# and piped to the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin qw($RealBin); +use Digest::MD5; +use File::Basename; +use File::Find; +use File::Copy qw(copy); +use File::Path qw( rmtree mkpath ); +use Term::ANSIColor; +use Term::ANSIColor qw(:constants); +use Cwd qw/ getcwd abs_path /; +use Sys::Hostname; +use Hash::Util qw(lock_keys); + +my $Prog = "scan-build"; +my $BuildName; +my $BuildDate; + +my $TERM = $ENV{'TERM'}; +my $UseColor = (defined $TERM and $TERM =~ 'xterm-.*color' and -t STDOUT + and defined $ENV{'SCAN_BUILD_COLOR'}); + +# Portability: getpwuid is not implemented for Win32 (see Perl language +# reference, perlport), use getlogin instead. +my $UserName = HtmlEscape(getlogin() || getpwuid($<) || 'unknown'); +my $HostName = HtmlEscape(hostname() || 'unknown'); +my $CurrentDir = HtmlEscape(getcwd()); + +my $CmdArgs; + +my $Date = localtime(); + +# Command-line/config arguments. +my %Options = ( + Verbose => 0, # Verbose output from this script. + AnalyzeHeaders => 0, + OutputDir => undef, # Parent directory to store HTML files. + HtmlTitle => basename($CurrentDir)." - scan-build results", + IgnoreErrors => 0, # Ignore build errors. + KeepCC => 0, # Do not override CC and CXX make variables + ViewResults => 0, # View results when the build terminates. + ExitStatusFoundBugs => 0, # Exit status reflects whether bugs were found + ShowDescription => 0, # Display the description of the defect in the list + KeepEmpty => 0, # Don't remove output directory even with 0 results. + EnableCheckers => {}, + DisableCheckers => {}, + SilenceCheckers => {}, + Excludes => [], + UseCC => undef, # C compiler to use for compilation. + UseCXX => undef, # C++ compiler to use for compilation. + AnalyzerTarget => undef, + StoreModel => undef, + ConstraintsModel => undef, + InternalStats => undef, + OutputFormat => "html", + ConfigOptions => [], # Options to pass through to the analyzer's -analyzer-config flag. + ReportFailures => undef, + AnalyzerStats => 0, + MaxLoop => 0, + PluginsToLoad => [], + AnalyzerDiscoveryMethod => undef, + OverrideCompiler => 0, # The flag corresponding to the --override-compiler command line option. + ForceAnalyzeDebugCode => 0 +); +lock_keys(%Options); + +##----------------------------------------------------------------------------## +# Diagnostics +##----------------------------------------------------------------------------## + +sub Diag { + if ($UseColor) { + print BOLD, MAGENTA "$Prog: @_"; + print RESET; + } + else { + print "$Prog: @_"; + } +} + +sub ErrorDiag { + if ($UseColor) { + print STDERR BOLD, RED "$Prog: "; + print STDERR RESET, RED @_; + print STDERR RESET; + } else { + print STDERR "$Prog: @_"; + } +} + +sub DiagCrashes { + my $Dir = shift; + Diag ("The analyzer encountered problems on some source files.\n"); + Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n"); + Diag ("Please consider submitting a bug report using these files:\n"); + Diag (" http://clang-analyzer.llvm.org/filing_bugs.html\n") +} + +sub DieDiag { + if ($UseColor) { + print STDERR BOLD, RED "$Prog: "; + print STDERR RESET, RED @_; + print STDERR RESET; + } + else { + print STDERR "$Prog: ", @_; + } + exit 1; +} + +##----------------------------------------------------------------------------## +# Print default checker names +##----------------------------------------------------------------------------## + +if (grep /^--help-checkers$/, @ARGV) { + my @options = qx($0 -h); + foreach (@options) { + next unless /^ \+/; + s/^\s*//; + my ($sign, $name, @text) = split ' ', $_; + print $name, $/ if $sign eq '+'; + } + exit 0; +} + +##----------------------------------------------------------------------------## +# Declaration of Clang options. Populated later. +##----------------------------------------------------------------------------## + +my $Clang; +my $ClangSB; +my $ClangCXX; +my $ClangVersion; + +##----------------------------------------------------------------------------## +# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. +##----------------------------------------------------------------------------## + +sub GetHTMLRunDir { + die "Not enough arguments." if (@_ == 0); + my $Dir = shift @_; + my $TmpMode = 0; + if (!defined $Dir) { + $Dir = $ENV{'TMPDIR'} || $ENV{'TEMP'} || $ENV{'TMP'} || "/tmp"; + $TmpMode = 1; + } + + # Chop off any trailing '/' characters. + while ($Dir =~ /\/$/) { chop $Dir; } + + # Get current date and time. + my @CurrentTime = localtime(); + my $year = $CurrentTime[5] + 1900; + my $day = $CurrentTime[3]; + my $month = $CurrentTime[4] + 1; + my $hour = $CurrentTime[2]; + my $min = $CurrentTime[1]; + my $sec = $CurrentTime[0]; + + my $TimeString = sprintf("%02d%02d%02d", $hour, $min, $sec); + my $DateString = sprintf("%d-%02d-%02d-%s-$$", + $year, $month, $day, $TimeString); + + # Determine the run number. + my $RunNumber; + + if (-d $Dir) { + if (! -r $Dir) { + DieDiag("directory '$Dir' exists but is not readable.\n"); + } + # Iterate over all files in the specified directory. + my $max = 0; + opendir(DIR, $Dir); + my @FILES = grep { -d "$Dir/$_" } readdir(DIR); + closedir(DIR); + + foreach my $f (@FILES) { + # Strip the prefix '$Prog-' if we are dumping files to /tmp. + if ($TmpMode) { + next if (!($f =~ /^$Prog-(.+)/)); + $f = $1; + } + + my @x = split/-/, $f; + next if (scalar(@x) != 4); + next if ($x[0] != $year); + next if ($x[1] != $month); + next if ($x[2] != $day); + next if ($x[3] != $TimeString); + next if ($x[4] != $$); + + if ($x[5] > $max) { + $max = $x[5]; + } + } + + $RunNumber = $max + 1; + } + else { + + if (-x $Dir) { + DieDiag("'$Dir' exists but is not a directory.\n"); + } + + if ($TmpMode) { + DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n"); + } + + # $Dir does not exist. It will be automatically created by the + # clang driver. Set the run number to 1. + + $RunNumber = 1; + } + + die "RunNumber must be defined!" if (!defined $RunNumber); + + # Append the run number. + my $NewDir; + if ($TmpMode) { + $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; + } + else { + $NewDir = "$Dir/$DateString-$RunNumber"; + } + + # Make sure that the directory does not exist in order to avoid hijack. + if (-e $NewDir) { + DieDiag("The directory '$NewDir' already exists.\n"); + } + + mkpath($NewDir); + return $NewDir; +} + +sub SetHtmlEnv { + + die "Wrong number of arguments." if (scalar(@_) != 2); + + my $Args = shift; + my $Dir = shift; + + die "No build command." if (scalar(@$Args) == 0); + + my $Cmd = $$Args[0]; + + if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) { + return; + } + + if ($Options{Verbose}) { + Diag("Emitting reports for this run to '$Dir'.\n"); + } + + $ENV{'CCC_ANALYZER_HTML'} = $Dir; +} + +##----------------------------------------------------------------------------## +# ComputeDigest - Compute a digest of the specified file. +##----------------------------------------------------------------------------## + +sub ComputeDigest { + my $FName = shift; + DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); + + # Use Digest::MD5. We don't have to be cryptographically secure. We're + # just looking for duplicate files that come from a non-malicious source. + # We use Digest::MD5 because it is a standard Perl module that should + # come bundled on most systems. + open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); + binmode FILE; + my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; + close(FILE); + + # Return the digest. + return $Result; +} + +##----------------------------------------------------------------------------## +# UpdatePrefix - Compute the common prefix of files. +##----------------------------------------------------------------------------## + +my $Prefix; + +sub UpdatePrefix { + my $x = shift; + my $y = basename($x); + $x =~ s/\Q$y\E$//; + + if (!defined $Prefix) { + $Prefix = $x; + return; + } + + chop $Prefix while (!($x =~ /^\Q$Prefix/)); +} + +sub GetPrefix { + return $Prefix; +} + +##----------------------------------------------------------------------------## +# UpdateInFilePath - Update the path in the report file. +##----------------------------------------------------------------------------## + +sub UpdateInFilePath { + my $fname = shift; + my $regex = shift; + my $newtext = shift; + + open (RIN, $fname) or die "cannot open $fname"; + open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; + + while (<RIN>) { + s/$regex/$newtext/; + print ROUT $_; + } + + close (ROUT); + close (RIN); + rename("$fname.tmp", $fname) +} + +##----------------------------------------------------------------------------## +# AddStatLine - Decode and insert a statistics line into the database. +##----------------------------------------------------------------------------## + +sub AddStatLine { + my $Line = shift; + my $Stats = shift; + my $File = shift; + + print $Line . "\n"; + + my $Regex = qr/(.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable + \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList: + \ (yes|no)/x; + + if ($Line !~ $Regex) { + return; + } + + # Create a hash of the interesting fields + my $Row = { + Filename => $File, + Function => $1, + Total => $2, + Unreachable => $3, + Aborted => $4, + Empty => $5 + }; + + # Add them to the stats array + push @$Stats, $Row; +} + +##----------------------------------------------------------------------------## +# ScanFile - Scan a report file for various identifying attributes. +##----------------------------------------------------------------------------## + +# Sometimes a source file is scanned more than once, and thus produces +# multiple error reports. We use a cache to solve this problem. + +my %AlreadyScanned; + +sub ScanFile { + + my $Index = shift; + my $Dir = shift; + my $FName = shift; + my $Stats = shift; + + # Compute a digest for the report file. Determine if we have already + # scanned a file that looks just like it. + + my $digest = ComputeDigest("$Dir/$FName"); + + if (defined $AlreadyScanned{$digest}) { + # Redundant file. Remove it. + unlink("$Dir/$FName"); + return; + } + + $AlreadyScanned{$digest} = 1; + + # At this point the report file is not world readable. Make it happen. + chmod(0644, "$Dir/$FName"); + + # Scan the report file for tags. + open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); + + my $BugType = ""; + my $BugFile = ""; + my $BugFunction = ""; + my $BugCategory = ""; + my $BugDescription = ""; + my $BugPathLength = 1; + my $BugLine = 0; + + while (<IN>) { + last if (/<!-- BUGMETAEND -->/); + + if (/<!-- BUGTYPE (.*) -->$/) { + $BugType = $1; + } + elsif (/<!-- BUGFILE (.*) -->$/) { + $BugFile = abs_path($1); + if (!defined $BugFile) { + # The file no longer exists: use the original path. + $BugFile = $1; + } + + # Get just the path + my $p = dirname($BugFile); + # Check if the path is found in the list of exclude + if (grep { $p =~ m/$_/ } @{$Options{Excludes}}) { + if ($Options{Verbose}) { + Diag("File '$BugFile' deleted: part of an ignored directory.\n"); + } + + # File in an ignored directory. Remove it + unlink("$Dir/$FName"); + return; + } + + UpdatePrefix($BugFile); + } + elsif (/<!-- BUGPATHLENGTH (.*) -->$/) { + $BugPathLength = $1; + } + elsif (/<!-- BUGLINE (.*) -->$/) { + $BugLine = $1; + } + elsif (/<!-- BUGCATEGORY (.*) -->$/) { + $BugCategory = $1; + } + elsif (/<!-- BUGDESC (.*) -->$/) { + $BugDescription = $1; + } + elsif (/<!-- FUNCTIONNAME (.*) -->$/) { + $BugFunction = $1; + } + + } + + + close(IN); + + if (!defined $BugCategory) { + $BugCategory = "Other"; + } + + # Don't add internal statistics to the bug reports + if ($BugCategory =~ /statistics/i) { + AddStatLine($BugDescription, $Stats, $BugFile); + return; + } + + push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugFunction, $BugLine, + $BugPathLength ]; + + if ($Options{ShowDescription}) { + push @{ $Index->[-1] }, $BugDescription + } +} + +##----------------------------------------------------------------------------## +# CopyFiles - Copy resource files to target directory. +##----------------------------------------------------------------------------## + +sub CopyFiles { + + my $Dir = shift; + + my $JS = Cwd::realpath("$RealBin/../share/scan-build/sorttable.js"); + + DieDiag("Cannot find 'sorttable.js'.\n") + if (! -r $JS); + + copy($JS, "$Dir"); + + DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") + if (! -r "$Dir/sorttable.js"); + + my $CSS = Cwd::realpath("$RealBin/../share/scan-build/scanview.css"); + + DieDiag("Cannot find 'scanview.css'.\n") + if (! -r $CSS); + + copy($CSS, "$Dir"); + + DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") + if (! -r $CSS); +} + +##----------------------------------------------------------------------------## +# CalcStats - Calculates visitation statistics and returns the string. +##----------------------------------------------------------------------------## + +sub CalcStats { + my $Stats = shift; + + my $TotalBlocks = 0; + my $UnreachedBlocks = 0; + my $TotalFunctions = scalar(@$Stats); + my $BlockAborted = 0; + my $WorkListAborted = 0; + my $Aborted = 0; + + # Calculate the unique files + my $FilesHash = {}; + + foreach my $Row (@$Stats) { + $FilesHash->{$Row->{Filename}} = 1; + $TotalBlocks += $Row->{Total}; + $UnreachedBlocks += $Row->{Unreachable}; + $BlockAborted++ if $Row->{Aborted} eq 'yes'; + $WorkListAborted++ if $Row->{Empty} eq 'no'; + $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no'; + } + + my $TotalFiles = scalar(keys(%$FilesHash)); + + # Calculations + my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100); + my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions + * 100); + my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted / + $TotalFunctions * 100); + my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks + * 100); + + my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions" + . " in $TotalFiles files\n" + . "$Aborted functions aborted early ($PercentAborted%)\n" + . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n" + . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n" + . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n"; + + return $StatsString; +} + +##----------------------------------------------------------------------------## +# Postprocess - Postprocess the results of an analysis scan. +##----------------------------------------------------------------------------## + +my @filesFound; +my $baseDir; +sub FileWanted { + my $baseDirRegEx = quotemeta $baseDir; + my $file = $File::Find::name; + + # The name of the file is generated by clang binary (HTMLDiagnostics.cpp) + if ($file =~ /report-.*\.html$/) { + my $relative_file = $file; + $relative_file =~ s/$baseDirRegEx//g; + push @filesFound, $relative_file; + } +} + +sub Postprocess { + + my $Dir = shift; + my $BaseDir = shift; + my $AnalyzerStats = shift; + my $KeepEmpty = shift; + + die "No directory specified." if (!defined $Dir); + + if (! -d $Dir) { + Diag("No bugs found.\n"); + return 0; + } + + $baseDir = $Dir . "/"; + find({ wanted => \&FileWanted, follow => 0}, $Dir); + + if (scalar(@filesFound) == 0 and ! -e "$Dir/failures") { + if (! $KeepEmpty) { + Diag("Removing directory '$Dir' because it contains no reports.\n"); + rmtree($Dir) or die "Cannot rmtree '$Dir' : $!"; + } + Diag("No bugs found.\n"); + return 0; + } + + # Scan each report file and build an index. + my @Index; + my @Stats; + foreach my $file (@filesFound) { ScanFile(\@Index, $Dir, $file, \@Stats); } + + # Scan the failures directory and use the information in the .info files + # to update the common prefix directory. + my @failures; + my @attributes_ignored; + if (-d "$Dir/failures") { + opendir(DIR, "$Dir/failures"); + @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR); + closedir(DIR); + opendir(DIR, "$Dir/failures"); + @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR); + closedir(DIR); + foreach my $file (@failures) { + open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n"); + my $Path = <IN>; + if (defined $Path) { UpdatePrefix($Path); } + close IN; + } + } + + # Generate an index.html file. + my $FName = "$Dir/index.html"; + open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); + + # Print out the header. + +print OUT <<ENDTEXT; +<html> +<head> +<title>${Options{HtmlTitle}}</title> +<link type="text/css" rel="stylesheet" href="scanview.css"/> +<script src="sorttable.js"></script> +<script language='javascript' type="text/javascript"> +function SetDisplay(RowClass, DisplayVal) +{ + var Rows = document.getElementsByTagName("tr"); + for ( var i = 0 ; i < Rows.length; ++i ) { + if (Rows[i].className == RowClass) { + Rows[i].style.display = DisplayVal; + } + } +} + +function CopyCheckedStateToCheckButtons(SummaryCheckButton) { + var Inputs = document.getElementsByTagName("input"); + for ( var i = 0 ; i < Inputs.length; ++i ) { + if (Inputs[i].type == "checkbox") { + if(Inputs[i] != SummaryCheckButton) { + Inputs[i].checked = SummaryCheckButton.checked; + Inputs[i].onclick(); + } + } + } +} + +function returnObjById( id ) { + if (document.getElementById) + var returnVar = document.getElementById(id); + else if (document.all) + var returnVar = document.all[id]; + else if (document.layers) + var returnVar = document.layers[id]; + return returnVar; +} + +var NumUnchecked = 0; + +function ToggleDisplay(CheckButton, ClassName) { + if (CheckButton.checked) { + SetDisplay(ClassName, ""); + if (--NumUnchecked == 0) { + returnObjById("AllBugsCheck").checked = true; + } + } + else { + SetDisplay(ClassName, "none"); + NumUnchecked++; + returnObjById("AllBugsCheck").checked = false; + } +} +</script> +<!-- SUMMARYENDHEAD --> +</head> +<body> +<h1>${Options{HtmlTitle}}</h1> + +<table> +<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr> +<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr> +<tr><th>Command Line:</th><td>${CmdArgs}</td></tr> +<tr><th>Clang Version:</th><td>${ClangVersion}</td></tr> +<tr><th>Date:</th><td>${Date}</td></tr> +ENDTEXT + +print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n" + if (defined($BuildName) && defined($BuildDate)); + +print OUT <<ENDTEXT; +</table> +ENDTEXT + + if (scalar(@filesFound)) { + # Print out the summary table. + my %Totals; + + for my $row ( @Index ) { + my $bug_type = ($row->[2]); + my $bug_category = ($row->[1]); + my $key = "$bug_category:$bug_type"; + + if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } + else { $Totals{$key}->[0]++; } + } + + print OUT "<h2>Bug Summary</h2>"; + + if (defined $BuildName) { + print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n" + } + + my $TotalBugs = scalar(@Index); +print OUT <<ENDTEXT; +<table> +<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead> +<tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr> +ENDTEXT + + my $last_category; + + for my $key ( + sort { + my $x = $Totals{$a}; + my $y = $Totals{$b}; + my $res = $x->[1] cmp $y->[1]; + $res = $x->[2] cmp $y->[2] if ($res == 0); + $res + } keys %Totals ) + { + my $val = $Totals{$key}; + my $category = $val->[1]; + if (!defined $last_category or $last_category ne $category) { + $last_category = $category; + print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n"; + } + my $x = lc $key; + $x =~ s/[ ,'":\/()]+/_/g; + print OUT "<tr><td class=\"SUMM_DESC\">"; + print OUT $val->[2]; + print OUT "</td><td class=\"Q\">"; + print OUT $val->[0]; + print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n"; + } + + # Print out the table of errors. + +print OUT <<ENDTEXT; +</table> +<h2>Reports</h2> + +<table class="sortable" style="table-layout:automatic"> +<thead><tr> + <td>Bug Group</td> + <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind"> ▾</span></td> + <td>File</td> + <td>Function/Method</td> + <td class="Q">Line</td> + <td class="Q">Path Length</td> +ENDTEXT + +if ($Options{ShowDescription}) { +print OUT <<ENDTEXT; + <td class="Q">Description</td> +ENDTEXT +} + +print OUT <<ENDTEXT; + <td class="sorttable_nosort"></td> + <!-- REPORTBUGCOL --> +</tr></thead> +<tbody> +ENDTEXT + + my $prefix = GetPrefix(); + my $regex; + my $InFileRegex; + my $InFilePrefix = "File:</td><td>"; + + if (defined $prefix) { + $regex = qr/^\Q$prefix\E/is; + $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; + } + + for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { + my $x = "$row->[1]:$row->[2]"; + $x = lc $x; + $x =~ s/[ ,'":\/()]+/_/g; + + my $ReportFile = $row->[0]; + + print OUT "<tr class=\"bt_$x\">"; + print OUT "<td class=\"DESC\">"; + print OUT $row->[1]; # $BugCategory + print OUT "</td>"; + print OUT "<td class=\"DESC\">"; + print OUT $row->[2]; # $BugType + print OUT "</td>"; + + # Update the file prefix. + my $fname = $row->[3]; + + if (defined $regex) { + $fname =~ s/$regex//; + UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) + } + + print OUT "<td>"; + my @fname = split /\//,$fname; + if ($#fname > 0) { + while ($#fname >= 0) { + my $x = shift @fname; + print OUT $x; + if ($#fname >= 0) { + print OUT "/"; + } + } + } + else { + print OUT $fname; + } + print OUT "</td>"; + + print OUT "<td class=\"DESC\">"; + print OUT $row->[4]; # Function + print OUT "</td>"; + + # Print out the quantities. + for my $j ( 5 .. 6 ) { # Line & Path length + print OUT "<td class=\"Q\">$row->[$j]</td>"; + } + + # Print the rest of the columns. + for (my $j = 7; $j <= $#{$row}; ++$j) { + print OUT "<td>$row->[$j]</td>" + } + + # Emit the "View" link. + print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>"; + + # Emit REPORTBUG markers. + print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n"; + + # End the row. + print OUT "</tr>\n"; + } + + print OUT "</tbody>\n</table>\n\n"; + } + + if (scalar (@failures) || scalar(@attributes_ignored)) { + print OUT "<h2>Analyzer Failures</h2>\n"; + + if (scalar @attributes_ignored) { + print OUT "The analyzer's parser ignored the following attributes:<p>\n"; + print OUT "<table>\n"; + print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; + foreach my $file (sort @attributes_ignored) { + die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/)); + my $attribute = $1; + # Open the attribute file to get the first file that failed. + next if (!open (ATTR, "$Dir/failures/$file")); + my $ppfile = <ATTR>; + chomp $ppfile; + close ATTR; + next if (! -e "$Dir/failures/$ppfile"); + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$ppfile.info.txt") or + die "Cannot open $Dir/failures/$ppfile.info.txt\n"; + my $srcfile = <INFO>; + chomp $srcfile; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; + } + print OUT "</table>\n"; + } + + if (scalar @failures) { + print OUT "<p>The analyzer had problems processing the following files:</p>\n"; + print OUT "<table>\n"; + print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; + foreach my $file (sort @failures) { + $file =~ /(.+).info.txt$/; + # Get the preprocessed file. + my $ppfile = $1; + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$file") or + die "Cannot open $Dir/failures/$file\n"; + my $srcfile = <INFO>; + chomp $srcfile; + my $problem = <INFO>; + chomp $problem; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; + } + print OUT "</table>\n"; + } + print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n"; + } + + print OUT "</body></html>\n"; + close(OUT); + CopyFiles($Dir); + + # Make sure $Dir and $BaseDir are world readable/executable. + chmod(0755, $Dir); + if (defined $BaseDir) { chmod(0755, $BaseDir); } + + # Print statistics + print CalcStats(\@Stats) if $AnalyzerStats; + + my $Num = scalar(@Index); + if ($Num == 1) { + Diag("$Num bug found.\n"); + } else { + Diag("$Num bugs found.\n"); + } + if ($Num > 0 && -r "$Dir/index.html") { + Diag("Run 'scan-view $Dir' to examine bug reports.\n"); + } + + DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored); + + return $Num; +} + +##----------------------------------------------------------------------------## +# RunBuildCommand - Run the build command. +##----------------------------------------------------------------------------## + +sub AddIfNotPresent { + my $Args = shift; + my $Arg = shift; + my $found = 0; + + foreach my $k (@$Args) { + if ($k eq $Arg) { + $found = 1; + last; + } + } + + if ($found == 0) { + push @$Args, $Arg; + } +} + +sub SetEnv { + my $EnvVars = shift @_; + foreach my $var ('CC', 'CXX', 'CLANG', 'CLANG_CXX', + 'CCC_ANALYZER_ANALYSIS', 'CCC_ANALYZER_PLUGINS', + 'CCC_ANALYZER_CONFIG') { + die "$var is undefined\n" if (!defined $var); + $ENV{$var} = $EnvVars->{$var}; + } + foreach my $var ('CCC_ANALYZER_STORE_MODEL', + 'CCC_ANALYZER_CONSTRAINTS_MODEL', + 'CCC_ANALYZER_INTERNAL_STATS', + 'CCC_ANALYZER_OUTPUT_FORMAT', + 'CCC_CC', + 'CCC_CXX', + 'CCC_REPORT_FAILURES', + 'CLANG_ANALYZER_TARGET', + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') { + my $x = $EnvVars->{$var}; + if (defined $x) { $ENV{$var} = $x } + } + my $Verbose = $EnvVars->{'VERBOSE'}; + if ($Verbose >= 2) { + $ENV{'CCC_ANALYZER_VERBOSE'} = 1; + } + if ($Verbose >= 3) { + $ENV{'CCC_ANALYZER_LOG'} = 1; + } +} + +sub RunXcodebuild { + my $Args = shift; + my $IgnoreErrors = shift; + my $CCAnalyzer = shift; + my $CXXAnalyzer = shift; + my $EnvVars = shift; + + if ($IgnoreErrors) { + AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); + } + + # Detect the version of Xcode. If Xcode 4.6 or higher, use new + # in situ support for analyzer interposition without needed to override + # the compiler. + open(DETECT_XCODE, "-|", $Args->[0], "-version") or + die "error: cannot detect version of xcodebuild\n"; + + my $oldBehavior = 1; + + while(<DETECT_XCODE>) { + if (/^Xcode (.+)$/) { + my $ver = $1; + if ($ver =~ /^([0-9]+[.][0-9]+)[^0-9]?/) { + if ($1 >= 4.6) { + $oldBehavior = 0; + last; + } + } + } + } + close(DETECT_XCODE); + + # If --override-compiler is explicitly requested, resort to the old + # behavior regardless of Xcode version. + if ($Options{OverrideCompiler}) { + $oldBehavior = 1; + } + + if ($oldBehavior == 0) { + my $OutputDir = $EnvVars->{"OUTPUT_DIR"}; + my $CLANG = $EnvVars->{"CLANG"}; + my $OtherFlags = $EnvVars->{"CCC_ANALYZER_ANALYSIS"}; + push @$Args, + "RUN_CLANG_STATIC_ANALYZER=YES", + "CLANG_ANALYZER_OUTPUT=plist-html", + "CLANG_ANALYZER_EXEC=$CLANG", + "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir", + "CLANG_ANALYZER_OTHER_FLAGS=$OtherFlags"; + + return (system(@$Args) >> 8); + } + + # Default to old behavior where we insert a bogus compiler. + SetEnv($EnvVars); + + # Check if using iPhone SDK 3.0 (simulator). If so the compiler being + # used should be gcc-4.2. + if (!defined $ENV{"CCC_CC"}) { + for (my $i = 0 ; $i < scalar(@$Args); ++$i) { + if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) { + if (@$Args[$i+1] =~ /^iphonesimulator3/) { + $ENV{"CCC_CC"} = "gcc-4.2"; + $ENV{"CCC_CXX"} = "g++-4.2"; + } + } + } + } + + # Disable PCH files until clang supports them. + AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); + + # When 'CC' is set, xcodebuild uses it to do all linking, even if we are + # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' + # (via c++-analyzer) when linking such files. + $ENV{"LDPLUSPLUS"} = $CXXAnalyzer; + + return (system(@$Args) >> 8); +} + +sub RunBuildCommand { + my $Args = shift; + my $IgnoreErrors = shift; + my $KeepCC = shift; + my $Cmd = $Args->[0]; + my $CCAnalyzer = shift; + my $CXXAnalyzer = shift; + my $EnvVars = shift; + + if ($Cmd =~ /\bxcodebuild$/) { + return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $EnvVars); + } + + # Setup the environment. + SetEnv($EnvVars); + + if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?cc[^\/]*$)/ or + $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?clang[^\/]*$)/ or + $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) { + + if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) { + $ENV{"CCC_CC"} = $1; + } + + shift @$Args; + unshift @$Args, $CCAnalyzer; + } + elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or + $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or + $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or + $Cmd =~ /(.*\/?clang\+\+$)/ or + $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) { + if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) { + $ENV{"CCC_CXX"} = $1; + } + shift @$Args; + unshift @$Args, $CXXAnalyzer; + } + elsif ($Cmd eq "make" or $Cmd eq "gmake" or $Cmd eq "mingw32-make") { + if (!$KeepCC) { + AddIfNotPresent($Args, "CC=$CCAnalyzer"); + AddIfNotPresent($Args, "CXX=$CXXAnalyzer"); + } + if ($IgnoreErrors) { + AddIfNotPresent($Args,"-k"); + AddIfNotPresent($Args,"-i"); + } + } + + return (system(@$Args) >> 8); +} + +##----------------------------------------------------------------------------## +# DisplayHelp - Utility function to display all help options. +##----------------------------------------------------------------------------## + +sub DisplayHelp { + + my $ArgClangNotFoundErrMsg = shift; +print <<ENDTEXT; +USAGE: $Prog [options] <build command> [build options] + +ENDTEXT + + if (defined $BuildName) { + print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; + } + +print <<ENDTEXT; +OPTIONS: + + -analyze-headers + + 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 + created as needed to represent separate "runs" of the analyzer. If this + option is not specified, a directory is created in /tmp (TMPDIR on Mac OS X) + to store the reports. + + -h + --help + + Display this message. + + -k + --keep-going + + Add a "keep on going" option to the specified build command. This option + currently supports make and xcodebuild. This is a convenience option; one + can specify this behavior directly using build options. + + --keep-cc + + Do not override CC and CXX make variables. Useful when running make in + autoconf-based (and similar) projects where configure can add extra flags + to those variables. + + --html-title [title] + --html-title=[title] + + Specify the title used on generated HTML pages. If not specified, a default + title will be used. + + --show-description + + Display the description of defects in the list + + -sarif + + By default the output of scan-build is a set of HTML files. This option + outputs the results in SARIF format. + + -plist + + By default the output of scan-build is a set of HTML files. This option + outputs the results as a set of .plist files. + + -plist-html + + By default the output of scan-build is a set of HTML files. This option + outputs the results as a set of HTML and .plist files. + + --status-bugs + + By default, the exit status of scan-build is the same as the executed build + command. Specifying this option causes the exit status of scan-build to be 1 + if it found potential bugs and the exit status of the build itself otherwise. + + --exclude <path> + + Do not run static analyzer against files found in this + directory (You can specify this option multiple times). + Could be useful when project contains 3rd party libraries. + + --use-cc [compiler path] + --use-cc=[compiler path] + + scan-build analyzes a project by interposing a "fake compiler", which + executes a real compiler for compilation and the static analyzer for analysis. + Because of the current implementation of interposition, scan-build does not + know what compiler your project normally uses. Instead, it simply overrides + the CC environment variable, and guesses your default compiler. + + In the future, this interposition mechanism to be improved, but if you need + scan-build to use a specific compiler for *compilation* then you can use + this option to specify a path to that compiler. + + If the given compiler is a cross compiler, you may also need to provide + --analyzer-target option to properly analyze the source code because static + analyzer runs as if the code is compiled for the host machine by default. + + --use-c++ [compiler path] + --use-c++=[compiler path] + + This is the same as "--use-cc" but for C++ code. + + --analyzer-target [target triple name for analysis] + --analyzer-target=[target triple name for analysis] + + This provides target triple information to clang static analyzer. + It only changes the target for analysis but doesn't change the target of a + real compiler given by --use-cc and --use-c++ options. + + -v + + Enable verbose output from scan-build. A second and third '-v' increases + verbosity. + + -V + --view + + View analysis results in a web browser when the build completes. + +ADVANCED OPTIONS: + + -no-failure-reports + + Do not create a 'failures' subdirectory that includes analyzer crash reports + and preprocessed source files. + + -stats + + Generates visitation statistics for the project being analyzed. + + -maxloop <loop count> + + Specify the number of times a block can be visited before giving up. + Default is 4. Increase for more comprehensive coverage at a cost of speed. + + -internal-stats + + Generate internal analyzer statistics. + + --use-analyzer [Xcode|path to clang] + --use-analyzer=[Xcode|path to clang] + + scan-build uses the 'clang' executable relative to itself for static + analysis. One can override this behavior with this option by using the + 'clang' packaged with Xcode (on OS X) or from the PATH. + + --keep-empty + + Don't remove the build results directory even if no issues were reported. + + --override-compiler + Always resort to the ccc-analyzer even when better interposition methods + are available. + + -analyzer-config <options> + + Provide options to pass through to the analyzer's -analyzer-config flag. + Several options are separated with comma: 'key1=val1,key2=val2' + + Available options: + * stable-report-filename=true or false (default) + Switch the page naming to: + report-<filename>-<function/method name>-<id>.html + instead of report-XXXXXX.html + +CONTROLLING CHECKERS: + + A default group of checkers are always run unless explicitly disabled. + Checkers may be enabled/disabled using the following options: + + -enable-checker [checker name] + -disable-checker [checker name] + +LOADING CHECKERS: + + Loading external checkers using the clang plugin interface: + + -load-plugin [plugin library] +ENDTEXT + + if (defined $Clang && -x $Clang) { + # Query clang for list of checkers that are enabled. + + # create a list to load the plugins via the 'Xclang' command line + # argument + my @PluginLoadCommandline_xclang; + foreach my $param ( @{$Options{PluginsToLoad}} ) { + push ( @PluginLoadCommandline_xclang, "-Xclang" ); + push ( @PluginLoadCommandline_xclang, "-load" ); + push ( @PluginLoadCommandline_xclang, "-Xclang" ); + push ( @PluginLoadCommandline_xclang, $param ); + } + + my %EnabledCheckers; + foreach my $lang ("c", "objective-c", "objective-c++", "c++") { + my $ExecLine = join(' ', qq/"$Clang"/, @PluginLoadCommandline_xclang, "--analyze", "-x", $lang, "-", "-###", "2>&1", "|"); + open(PS, $ExecLine); + while (<PS>) { + foreach my $val (split /\s+/) { + $val =~ s/\"//g; + if ($val =~ /-analyzer-checker\=([^\s]+)/) { + $EnabledCheckers{$1} = 1; + } + } + } + } + + # Query clang for complete list of checkers. + my @PluginLoadCommandline; + foreach my $param ( @{$Options{PluginsToLoad}} ) { + push ( @PluginLoadCommandline, "-load" ); + push ( @PluginLoadCommandline, $param ); + } + + my $ExecLine = join(' ', qq/"$Clang"/, "-cc1", @PluginLoadCommandline, "-analyzer-checker-help", "2>&1", "|"); + open(PS, $ExecLine); + my $foundCheckers = 0; + while (<PS>) { + if (/CHECKERS:/) { + $foundCheckers = 1; + last; + } + } + if (!$foundCheckers) { + print " *** Could not query Clang for the list of available checkers."; + } + else { + print("\nAVAILABLE CHECKERS:\n\n"); + my $skip = 0; + while(<PS>) { + if (/experimental/) { + $skip = 1; + next; + } + if ($skip) { + next if (!/^\s\s[^\s]/); + $skip = 0; + } + s/^\s\s//; + if (/^([^\s]+)/) { + # Is the checker enabled? + my $checker = $1; + my $enabled = 0; + my $aggregate = ""; + foreach my $domain (split /\./, $checker) { + $aggregate .= $domain; + if ($EnabledCheckers{$aggregate}) { + $enabled =1; + last; + } + # append a dot, if an additional domain is added in the next iteration + $aggregate .= "."; + } + + if ($enabled) { + print " + "; + } + else { + print " "; + } + } + else { + print " "; + } + print $_; + } + print "\nNOTE: \"+\" indicates that an analysis is enabled by default.\n"; + } + close PS; + } + else { + print " *** Could not query Clang for the list of available checkers.\n"; + if (defined $ArgClangNotFoundErrMsg) { + print " *** Reason: $ArgClangNotFoundErrMsg\n"; + } + } + +print <<ENDTEXT + +BUILD OPTIONS + + You can specify any build option acceptable to the build command. + +EXAMPLE + + scan-build -o /tmp/myhtmldir make -j4 + +The above example causes analysis reports to be deposited into a subdirectory +of "/tmp/myhtmldir" and to run "make" with the "-j4" option. A different +subdirectory is created each time scan-build analyzes a project. The analyzer +should support most parallel builds, but not distributed builds. + +ENDTEXT +} + +##----------------------------------------------------------------------------## +# HtmlEscape - HTML entity encode characters that are special in HTML +##----------------------------------------------------------------------------## + +sub HtmlEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + my $tmp = $arg; + $tmp =~ s/&/&/g; + $tmp =~ s/</</g; + $tmp =~ s/>/>/g; + return $tmp; +} + +##----------------------------------------------------------------------------## +# ShellEscape - backslash escape characters that are special to the shell +##----------------------------------------------------------------------------## + +sub ShellEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + if ($arg =~ /["\s]/) { return "'" . $arg . "'"; } + return $arg; +} + +##----------------------------------------------------------------------------## +# FindXcrun - searches for the 'xcrun' executable. Returns "" if not found. +##----------------------------------------------------------------------------## + +sub FindXcrun { + my $xcrun = `which xcrun`; + chomp $xcrun; + return $xcrun; +} + +##----------------------------------------------------------------------------## +# FindClang - searches for 'clang' executable. +##----------------------------------------------------------------------------## + +sub FindClang { + if (!defined $Options{AnalyzerDiscoveryMethod}) { + $Clang = Cwd::realpath("$RealBin/bin/clang") if (-f "$RealBin/bin/clang"); + if (!defined $Clang || ! -x $Clang) { + $Clang = Cwd::realpath("$RealBin/clang") if (-f "$RealBin/clang"); + if (!defined $Clang || ! -x $Clang) { + # When an Xcode toolchain is present, look for a clang in the sibling bin + # of the parent of the bin directory. So if scan-build is at + # $TOOLCHAIN/usr/local/bin/scan-build look for clang at + # $TOOLCHAIN/usr/bin/clang. + my $has_xcode_toolchain = FindXcrun() ne ""; + if ($has_xcode_toolchain && -f "$RealBin/../../bin/clang") { + $Clang = Cwd::realpath("$RealBin/../../bin/clang"); + } + } + } + if (!defined $Clang || ! -x $Clang) { + return "error: Cannot find an executable 'clang' relative to" . + " scan-build. Consider using --use-analyzer to pick a version of" . + " 'clang' to use for static analysis.\n"; + } + } + else { + if ($Options{AnalyzerDiscoveryMethod} =~ /^[Xx]code$/) { + my $xcrun = FindXcrun(); + if ($xcrun eq "") { + return "Cannot find 'xcrun' to find 'clang' for analysis.\n"; + } + $Clang = `$xcrun -toolchain XcodeDefault -find clang`; + chomp $Clang; + if ($Clang eq "") { + return "No 'clang' executable found by 'xcrun'\n"; + } + } + else { + $Clang = $Options{AnalyzerDiscoveryMethod}; + if (!defined $Clang or not -x $Clang) { + return "Cannot find an executable clang at '$Options{AnalyzerDiscoveryMethod}'\n"; + } + } + } + return undef; +} + +##----------------------------------------------------------------------------## +# Process command-line arguments. +##----------------------------------------------------------------------------## + +my $RequestDisplayHelp = 0; +my $ForceDisplayHelp = 0; + +sub ProcessArgs { + my $Args = shift; + my $NumArgs = 0; + + while (@$Args) { + + $NumArgs++; + + # Scan for options we recognize. + + my $arg = $Args->[0]; + + if ($arg eq "-h" or $arg eq "--help") { + $RequestDisplayHelp = 1; + shift @$Args; + next; + } + + if ($arg eq '-analyze-headers') { + shift @$Args; + $Options{AnalyzeHeaders} = 1; + next; + } + + if ($arg eq "-o") { + shift @$Args; + + if (!@$Args) { + DieDiag("'-o' option requires a target directory name.\n"); + } + + # Construct an absolute path. Uses the current working directory + # as a base if the original path was not absolute. + my $OutDir = shift @$Args; + mkpath($OutDir) unless (-e $OutDir); # abs_path wants existing dir + $Options{OutputDir} = abs_path($OutDir); + + next; + } + + if ($arg =~ /^--html-title(=(.+))?$/) { + shift @$Args; + + if (!defined $2 || $2 eq '') { + if (!@$Args) { + DieDiag("'--html-title' option requires a string.\n"); + } + + $Options{HtmlTitle} = shift @$Args; + } else { + $Options{HtmlTitle} = $2; + } + + next; + } + + if ($arg eq "-k" or $arg eq "--keep-going") { + shift @$Args; + $Options{IgnoreErrors} = 1; + next; + } + + if ($arg eq "--keep-cc") { + shift @$Args; + $Options{KeepCC} = 1; + next; + } + + if ($arg =~ /^--use-cc(=(.+))?$/) { + shift @$Args; + my $cc; + + if (!defined $2 || $2 eq "") { + if (!@$Args) { + DieDiag("'--use-cc' option requires a compiler executable name.\n"); + } + $cc = shift @$Args; + } + else { + $cc = $2; + } + + $Options{UseCC} = $cc; + next; + } + + if ($arg =~ /^--use-c\+\+(=(.+))?$/) { + shift @$Args; + my $cxx; + + if (!defined $2 || $2 eq "") { + if (!@$Args) { + DieDiag("'--use-c++' option requires a compiler executable name.\n"); + } + $cxx = shift @$Args; + } + else { + $cxx = $2; + } + + $Options{UseCXX} = $cxx; + next; + } + + if ($arg =~ /^--analyzer-target(=(.+))?$/) { + shift @ARGV; + my $AnalyzerTarget; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--analyzer-target' option requires a target triple name.\n"); + } + $AnalyzerTarget = shift @ARGV; + } + else { + $AnalyzerTarget = $2; + } + + $Options{AnalyzerTarget} = $AnalyzerTarget; + next; + } + + if ($arg eq "-v") { + shift @$Args; + $Options{Verbose}++; + next; + } + + if ($arg eq "-V" or $arg eq "--view") { + shift @$Args; + $Options{ViewResults} = 1; + next; + } + + if ($arg eq "--status-bugs") { + shift @$Args; + $Options{ExitStatusFoundBugs} = 1; + next; + } + + if ($arg eq "--show-description") { + shift @$Args; + $Options{ShowDescription} = 1; + next; + } + + if ($arg eq "-store") { + shift @$Args; + $Options{StoreModel} = shift @$Args; + next; + } + + if ($arg eq "-constraints") { + shift @$Args; + $Options{ConstraintsModel} = shift @$Args; + next; + } + + if ($arg eq "-internal-stats") { + shift @$Args; + $Options{InternalStats} = 1; + next; + } + + if ($arg eq "-sarif") { + shift @$Args; + $Options{OutputFormat} = "sarif"; + next; + } + + if ($arg eq "-plist") { + shift @$Args; + $Options{OutputFormat} = "plist"; + next; + } + + if ($arg eq "-plist-html") { + shift @$Args; + $Options{OutputFormat} = "plist-html"; + next; + } + + if ($arg eq "-analyzer-config") { + shift @$Args; + push @{$Options{ConfigOptions}}, shift @$Args; + next; + } + + if ($arg eq "-no-failure-reports") { + shift @$Args; + $Options{ReportFailures} = 0; + next; + } + + if ($arg eq "-stats") { + shift @$Args; + $Options{AnalyzerStats} = 1; + next; + } + + if ($arg eq "-maxloop") { + shift @$Args; + $Options{MaxLoop} = shift @$Args; + next; + } + + if ($arg eq "-enable-checker") { + shift @$Args; + my $Checker = shift @$Args; + # Store $NumArgs to preserve the order the checkers were enabled. + $Options{EnableCheckers}{$Checker} = $NumArgs; + delete $Options{DisableCheckers}{$Checker}; + next; + } + + if ($arg eq "-disable-checker") { + shift @$Args; + my $Checker = shift @$Args; + # Store $NumArgs to preserve the order the checkers are disabled/silenced. + # See whether it is a core checker to disable. That means we do not want + # to emit a report from that checker so we have to silence it. + if (index($Checker, "core") == 0) { + $Options{SilenceCheckers}{$Checker} = $NumArgs; + } else { + $Options{DisableCheckers}{$Checker} = $NumArgs; + delete $Options{EnableCheckers}{$Checker}; + } + next; + } + + if ($arg eq "--exclude") { + shift @$Args; + my $arg = shift @$Args; + # Remove the trailing slash if any + $arg =~ s|/$||; + push @{$Options{Excludes}}, $arg; + next; + } + + if ($arg eq "-load-plugin") { + shift @$Args; + push @{$Options{PluginsToLoad}}, shift @$Args; + next; + } + + if ($arg eq "--use-analyzer") { + shift @$Args; + $Options{AnalyzerDiscoveryMethod} = shift @$Args; + next; + } + + if ($arg =~ /^--use-analyzer=(.+)$/) { + shift @$Args; + $Options{AnalyzerDiscoveryMethod} = $1; + next; + } + + if ($arg eq "--keep-empty") { + shift @$Args; + $Options{KeepEmpty} = 1; + next; + } + + if ($arg eq "--override-compiler") { + shift @$Args; + $Options{OverrideCompiler} = 1; + next; + } + + if ($arg eq "--force-analyze-debug-code") { + shift @$Args; + $Options{ForceAnalyzeDebugCode} = 1; + next; + } + + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); + + $NumArgs--; + last; + } + return $NumArgs; +} + +if (!@ARGV) { + $ForceDisplayHelp = 1 +} + +ProcessArgs(\@ARGV); +# All arguments are now shifted from @ARGV. The rest is a build command, if any. + +if (!@ARGV and !$RequestDisplayHelp) { + ErrorDiag("No build command specified.\n\n"); + $ForceDisplayHelp = 1; +} + +my $ClangNotFoundErrMsg = FindClang(); + +if ($ForceDisplayHelp || $RequestDisplayHelp) { + DisplayHelp($ClangNotFoundErrMsg); + exit $ForceDisplayHelp; +} + +DieDiag($ClangNotFoundErrMsg) if (defined $ClangNotFoundErrMsg); + +$ClangCXX = $Clang; +if ($Clang !~ /\+\+(\.exe)?$/) { + # If $Clang holds the name of the clang++ executable then we leave + # $ClangCXX and $Clang equal, otherwise construct the name of the clang++ + # executable from the clang executable name. + + # Determine operating system under which this copy of Perl was built. + my $IsWinBuild = ($^O =~/msys|cygwin|MSWin32/); + if($IsWinBuild) { + $ClangCXX =~ s/.exe$/++.exe/; + } + else { + $ClangCXX =~ s/\-\d+\.\d+$//; + $ClangCXX .= "++"; + } +} + +# Make sure to use "" to handle paths with spaces. +$ClangVersion = HtmlEscape(`"$Clang" --version`); + +# Determine where results go. +$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); + +# Determine the output directory for the HTML reports. +my $BaseDir = $Options{OutputDir}; +$Options{OutputDir} = GetHTMLRunDir($Options{OutputDir}); + +# Determine the location of ccc-analyzer. +my $AbsRealBin = Cwd::realpath($RealBin); +my $Cmd = "$AbsRealBin/../libexec/ccc-analyzer"; +my $CmdCXX = "$AbsRealBin/../libexec/c++-analyzer"; + +# Portability: use less strict but portable check -e (file exists) instead of +# non-portable -x (file is executable). On some windows ports -x just checks +# file extension to determine if a file is executable (see Perl language +# reference, perlport) +if (!defined $Cmd || ! -e $Cmd) { + $Cmd = "$AbsRealBin/ccc-analyzer"; + DieDiag("'ccc-analyzer' does not exist at '$Cmd'\n") if(! -e $Cmd); +} +if (!defined $CmdCXX || ! -e $CmdCXX) { + $CmdCXX = "$AbsRealBin/c++-analyzer"; + DieDiag("'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -e $CmdCXX); +} + +Diag("Using '$Clang' for static analysis\n"); + +SetHtmlEnv(\@ARGV, $Options{OutputDir}); + +my @AnalysesToRun; +foreach (sort { $Options{EnableCheckers}{$a} <=> $Options{EnableCheckers}{$b} } + keys %{$Options{EnableCheckers}}) { + # Push checkers in order they were enabled. + push @AnalysesToRun, "-analyzer-checker", $_; +} +foreach (sort { $Options{DisableCheckers}{$a} <=> $Options{DisableCheckers}{$b} } + keys %{$Options{DisableCheckers}}) { + # Push checkers in order they were disabled. + push @AnalysesToRun, "-analyzer-disable-checker", $_; +} +if ($Options{AnalyzeHeaders}) { push @AnalysesToRun, "-analyzer-opt-analyze-headers"; } +if ($Options{AnalyzerStats}) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; } +if ($Options{MaxLoop} > 0) { push @AnalysesToRun, "-analyzer-max-loop $Options{MaxLoop}"; } + +# Delay setting up other environment variables in case we can do true +# interposition. +my $CCC_ANALYZER_ANALYSIS = join ' ', @AnalysesToRun; +my $CCC_ANALYZER_PLUGINS = join ' ', map { "-load ".$_ } @{$Options{PluginsToLoad}}; +my $CCC_ANALYZER_CONFIG = join ' ', map { "-analyzer-config ".$_ } @{$Options{ConfigOptions}}; + +foreach (sort { $Options{SilenceCheckers}{$a} <=> $Options{SilenceCheckers}{$b} } + keys %{$Options{SilenceCheckers}}) { + # Add checkers in order they were silenced. + $CCC_ANALYZER_CONFIG = + $CCC_ANALYZER_CONFIG." -analyzer-config silence-checkers=".$_; +} + +my %EnvVars = ( + 'CC' => $Cmd, + 'CXX' => $CmdCXX, + 'CLANG' => $Clang, + 'CLANG_CXX' => $ClangCXX, + 'VERBOSE' => $Options{Verbose}, + 'CCC_ANALYZER_ANALYSIS' => $CCC_ANALYZER_ANALYSIS, + 'CCC_ANALYZER_PLUGINS' => $CCC_ANALYZER_PLUGINS, + 'CCC_ANALYZER_CONFIG' => $CCC_ANALYZER_CONFIG, + 'OUTPUT_DIR' => $Options{OutputDir}, + 'CCC_CC' => $Options{UseCC}, + 'CCC_CXX' => $Options{UseCXX}, + 'CCC_REPORT_FAILURES' => $Options{ReportFailures}, + 'CCC_ANALYZER_STORE_MODEL' => $Options{StoreModel}, + 'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel}, + 'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats}, + 'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat}, + 'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget}, + 'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode} +); + +# Run the build. +my $ExitStatus = RunBuildCommand(\@ARGV, $Options{IgnoreErrors}, $Options{KeepCC}, + $Cmd, $CmdCXX, \%EnvVars); + +if (defined $Options{OutputFormat}) { + if ($Options{OutputFormat} =~ /plist/ || + $Options{OutputFormat} =~ /sarif/) { + Diag "Analysis run complete.\n"; + Diag "Analysis results (" . + ($Options{OutputFormat} =~ /plist/ ? "plist" : "sarif") . + " files) deposited in '$Options{OutputDir}'\n"; + } + if ($Options{OutputFormat} =~ /html/) { + # Postprocess the HTML directory. + my $NumBugs = Postprocess($Options{OutputDir}, $BaseDir, + $Options{AnalyzerStats}, $Options{KeepEmpty}); + + if ($Options{ViewResults} and -r "$Options{OutputDir}/index.html") { + Diag "Analysis run complete.\n"; + Diag "Viewing analysis results in '$Options{OutputDir}' using scan-view.\n"; + my $ScanView = Cwd::realpath("$RealBin/scan-view"); + if (! -x $ScanView) { $ScanView = "scan-view"; } + if (! -x $ScanView) { $ScanView = Cwd::realpath("$RealBin/../../scan-view/bin/scan-view"); } + exec $ScanView, "$Options{OutputDir}"; + } + + if ($Options{ExitStatusFoundBugs}) { + exit 1 if ($NumBugs > 0); + exit $ExitStatus; + } + } +} + +exit $ExitStatus; diff --git a/gnu/llvm/clang/tools/scan-build/bin/scan-build.bat b/gnu/llvm/clang/tools/scan-build/bin/scan-build.bat new file mode 100644 index 00000000000..77be6746318 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/bin/scan-build.bat @@ -0,0 +1 @@ +perl -S scan-build %*
diff --git a/gnu/llvm/clang/tools/scan-build/bin/set-xcode-analyzer b/gnu/llvm/clang/tools/scan-build/bin/set-xcode-analyzer new file mode 100755 index 00000000000..c2a65c90859 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/bin/set-xcode-analyzer @@ -0,0 +1,114 @@ +#!/usr/bin/python + +# [PR 11661] Note that we hardwire to /usr/bin/python because we +# want to the use the system version of Python on Mac OS X. +# This one has the scripting bridge enabled. + +import sys +if sys.version_info < (2, 7): + print "set-xcode-analyzer requires Python 2.7 or later" + sys.exit(1) + +import os +import subprocess +import re +import tempfile +import shutil +import stat +from AppKit import * + +def FindClangSpecs(path): + print "(+) Searching for xcspec file in: ", path + for root, dirs, files in os.walk(path): + for f in files: + if f.endswith(".xcspec") and f.startswith("Clang LLVM"): + yield os.path.join(root, f) + +def ModifySpec(path, isBuiltinAnalyzer, pathToChecker): + t = tempfile.NamedTemporaryFile(delete=False) + foundAnalyzer = False + with open(path) as f: + if isBuiltinAnalyzer: + # First search for CLANG_ANALYZER_EXEC. Newer + # versions of Xcode set EXEC_PATH to be CLANG_ANALYZER_EXEC. + with open(path) as f2: + for line in f2: + if line.find("CLANG_ANALYZER_EXEC") >= 0: + pathToChecker = "$(CLANG_ANALYZER_EXEC)" + break + # Now create a new file. + for line in f: + if not foundAnalyzer: + if line.find("Static Analyzer") >= 0: + foundAnalyzer = True + else: + m = re.search(r'^(\s*ExecPath\s*=\s*")', line) + if m: + line = "".join([m.group(0), pathToChecker, '";\n']) + # Do not modify further ExecPath's later in the xcspec. + foundAnalyzer = False + t.write(line) + t.close() + print "(+) processing:", path + try: + shutil.copy(t.name, path) + os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) + except IOError, why: + print " (-) Cannot update file:", why, "\n" + except OSError, why: + print " (-) Cannot update file:", why, "\n" + os.unlink(t.name) + +def main(): + from optparse import OptionParser + parser = OptionParser('usage: %prog [options]') + parser.set_description(__doc__) + parser.add_option("--use-checker-build", dest="path", + help="Use the Clang located at the provided absolute path, e.g. /Users/foo/checker-1") + parser.add_option("--use-xcode-clang", action="store_const", + const="$(CLANG)", dest="default", + help="Use the Clang bundled with Xcode") + (options, args) = parser.parse_args() + if options.path is None and options.default is None: + parser.error("You must specify a version of Clang to use for static analysis. Specify '-h' for details") + + # determine if Xcode is running + for x in NSWorkspace.sharedWorkspace().runningApplications(): + if x.localizedName().find("Xcode") >= 0: + print "(-) You must quit Xcode first before modifying its configuration files." + sys.exit(1) + + isBuiltinAnalyzer = False + if options.path: + # Expand tildes. + path = os.path.expanduser(options.path) + if not path.endswith("clang"): + print "(+) Using Clang bundled with checker build:", path + path = os.path.join(path, "bin", "clang"); + else: + print "(+) Using Clang located at:", path + else: + print "(+) Using the Clang bundled with Xcode" + path = options.default + isBuiltinAnalyzer = True + + try: + xcode_path = subprocess.check_output(["xcode-select", "-print-path"]) + except AttributeError: + # Fall back to the default install location when using Python < 2.7.0 + xcode_path = "/Developer" + if (xcode_path.find(".app/") != -1): + # Cut off the 'Developer' dir, as the xcspec lies in another part + # of the Xcode.app subtree. + xcode_path = xcode_path.rsplit('/Developer', 1)[0] + + foundSpec = False + for x in FindClangSpecs(xcode_path): + foundSpec = True + ModifySpec(x, isBuiltinAnalyzer, path) + + if foundSpec == False: + print "(-) No compiler configuration file was found. Xcode's analyzer has not been updated." + +if __name__ == '__main__': + main() diff --git a/gnu/llvm/clang/tools/scan-build/libexec/c++-analyzer b/gnu/llvm/clang/tools/scan-build/libexec/c++-analyzer new file mode 100755 index 00000000000..dda5db9c7d9 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/libexec/c++-analyzer @@ -0,0 +1,8 @@ +#!/usr/bin/env perl + +use Cwd qw/ abs_path /; +use File::Basename qw/ dirname /; +# Add scan-build dir to the list of places where perl looks for modules. +use lib dirname(abs_path($0)); + +do 'ccc-analyzer'; diff --git a/gnu/llvm/clang/tools/scan-build/libexec/c++-analyzer.bat b/gnu/llvm/clang/tools/scan-build/libexec/c++-analyzer.bat new file mode 100644 index 00000000000..69f048a9167 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/libexec/c++-analyzer.bat @@ -0,0 +1 @@ +perl -S c++-analyzer %*
diff --git a/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer b/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer new file mode 100755 index 00000000000..800f38b5ba2 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer @@ -0,0 +1,784 @@ +#!/usr/bin/env perl +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +##===----------------------------------------------------------------------===## +# +# A script designed to interpose between the build system and gcc. It invokes +# both gcc and the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin; +use Cwd qw/ getcwd abs_path /; +use File::Temp qw/ tempfile /; +use File::Path qw / mkpath /; +use File::Basename; +use Text::ParseWords; + +##===----------------------------------------------------------------------===## +# List form 'system' with STDOUT and STDERR captured. +##===----------------------------------------------------------------------===## + +sub silent_system { + my $HtmlDir = shift; + my $Command = shift; + + # Save STDOUT and STDERR and redirect to a temporary file. + open OLDOUT, ">&", \*STDOUT; + open OLDERR, ">&", \*STDERR; + my ($TmpFH, $TmpFile) = tempfile("temp_buf_XXXXXX", + DIR => $HtmlDir, + UNLINK => 1); + open(STDOUT, ">$TmpFile"); + open(STDERR, ">&", \*STDOUT); + + # Invoke 'system', STDOUT and STDERR are output to a temporary file. + system $Command, @_; + + # Restore STDOUT and STDERR. + open STDOUT, ">&", \*OLDOUT; + open STDERR, ">&", \*OLDERR; + + return $TmpFH; +} + +##===----------------------------------------------------------------------===## +# Compiler command setup. +##===----------------------------------------------------------------------===## + +# Search in the PATH if the compiler exists +sub SearchInPath { + my $file = shift; + foreach my $dir (split (':', $ENV{PATH})) { + if (-x "$dir/$file") { + return 1; + } + } + return 0; +} + +my $Compiler; +my $Clang; +my $DefaultCCompiler; +my $DefaultCXXCompiler; +my $IsCXX; +my $AnalyzerTarget; + +# If on OSX, use xcrun to determine the SDK root. +my $UseXCRUN = 0; + +if (`uname -a` =~ m/Darwin/) { + $DefaultCCompiler = 'clang'; + $DefaultCXXCompiler = 'clang++'; + # Older versions of OSX do not have xcrun to + # query the SDK location. + if (-x "/usr/bin/xcrun") { + $UseXCRUN = 1; + } +} else { + $DefaultCCompiler = 'gcc'; + $DefaultCXXCompiler = 'g++'; +} + +if ($FindBin::Script =~ /c\+\+-analyzer/) { + $Compiler = $ENV{'CCC_CXX'}; + if (!defined $Compiler || (! -x $Compiler && ! SearchInPath($Compiler))) { $Compiler = $DefaultCXXCompiler; } + + $Clang = $ENV{'CLANG_CXX'}; + if (!defined $Clang || ! -x $Clang) { $Clang = 'clang++'; } + + $IsCXX = 1 +} +else { + $Compiler = $ENV{'CCC_CC'}; + if (!defined $Compiler || (! -x $Compiler && ! SearchInPath($Compiler))) { $Compiler = $DefaultCCompiler; } + + $Clang = $ENV{'CLANG'}; + if (!defined $Clang || ! -x $Clang) { $Clang = 'clang'; } + + $IsCXX = 0 +} + +$AnalyzerTarget = $ENV{'CLANG_ANALYZER_TARGET'}; + +##===----------------------------------------------------------------------===## +# Cleanup. +##===----------------------------------------------------------------------===## + +my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'}; +if (!defined $ReportFailures) { $ReportFailures = 1; } + +my $CleanupFile; +my $ResultFile; + +# Remove any stale files at exit. +END { + if (defined $ResultFile && -z $ResultFile) { + unlink($ResultFile); + } + if (defined $CleanupFile) { + unlink($CleanupFile); + } +} + +##----------------------------------------------------------------------------## +# Process Clang Crashes. +##----------------------------------------------------------------------------## + +sub GetPPExt { + my $Lang = shift; + if ($Lang =~ /objective-c\+\+/) { return ".mii" }; + if ($Lang =~ /objective-c/) { return ".mi"; } + if ($Lang =~ /c\+\+/) { return ".ii"; } + return ".i"; +} + +# Set this to 1 if we want to include 'parser rejects' files. +my $IncludeParserRejects = 0; +my $ParserRejects = "Parser Rejects"; +my $AttributeIgnored = "Attribute Ignored"; +my $OtherError = "Other Error"; + +sub ProcessClangFailure { + my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_; + my $Dir = "$HtmlDir/failures"; + mkpath $Dir; + + my $prefix = "clang_crash"; + if ($ErrorType eq $ParserRejects) { + $prefix = "clang_parser_rejects"; + } + elsif ($ErrorType eq $AttributeIgnored) { + $prefix = "clang_attribute_ignored"; + } + elsif ($ErrorType eq $OtherError) { + $prefix = "clang_other_error"; + } + + # Generate the preprocessed file with Clang. + my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX", + SUFFIX => GetPPExt($Lang), + DIR => $Dir); + close ($PPH); + system $Clang, @$Args, "-E", "-o", $PPFile; + + # Create the info file. + open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n"; + print OUT abs_path($file), "\n"; + print OUT "$ErrorType\n"; + print OUT "@$Args\n"; + close OUT; + `uname -a >> $PPFile.info.txt 2>&1`; + `"$Compiler" -v >> $PPFile.info.txt 2>&1`; + rename($ofile, "$PPFile.stderr.txt"); + return (basename $PPFile); +} + +##----------------------------------------------------------------------------## +# Running the analyzer. +##----------------------------------------------------------------------------## + +sub GetCCArgs { + my $HtmlDir = shift; + my $mode = shift; + my $Args = shift; + my $line; + my $OutputStream = silent_system($HtmlDir, $Clang, "-###", $mode, @$Args); + while (<$OutputStream>) { + next if (!/\s"?-cc1"?\s/); + $line = $_; + } + die "could not find clang line\n" if (!defined $line); + # Strip leading and trailing whitespace characters. + $line =~ s/^\s+|\s+$//g; + my @items = quotewords('\s+', 0, $line); + my $cmd = shift @items; + die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/)); + return \@items; +} + +sub Analyze { + my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir, + $file) = @_; + + my @Args = @$OriginalArgs; + my $Cmd; + my @CmdArgs; + my @CmdArgsSansAnalyses; + + if ($Lang =~ /header/) { + exit 0 if (!defined ($Output)); + $Cmd = 'cp'; + push @CmdArgs, $file; + # Remove the PCH extension. + $Output =~ s/[.]gch$//; + push @CmdArgs, $Output; + @CmdArgsSansAnalyses = @CmdArgs; + } + else { + $Cmd = $Clang; + + # Create arguments for doing regular parsing. + my $SyntaxArgs = GetCCArgs($HtmlDir, "-fsyntax-only", \@Args); + @CmdArgsSansAnalyses = @$SyntaxArgs; + + # Create arguments for doing static analysis. + if (defined $ResultFile) { + push @Args, '-o', $ResultFile; + } + elsif (defined $HtmlDir) { + push @Args, '-o', $HtmlDir; + } + if ($Verbose) { + push @Args, "-Xclang", "-analyzer-display-progress"; + } + + foreach my $arg (@$AnalyzeArgs) { + push @Args, "-Xclang", $arg; + } + + if (defined $AnalyzerTarget) { + push @Args, "-target", $AnalyzerTarget; + } + + my $AnalysisArgs = GetCCArgs($HtmlDir, "--analyze", \@Args); + @CmdArgs = @$AnalysisArgs; + } + + my @PrintArgs; + my $dir; + + if ($Verbose) { + $dir = getcwd(); + print STDERR "\n[LOCATION]: $dir\n"; + push @PrintArgs,"'$Cmd'"; + foreach my $arg (@CmdArgs) { + push @PrintArgs,"\'$arg\'"; + } + } + if ($Verbose == 1) { + # We MUST print to stderr. Some clients use the stdout output of + # gcc for various purposes. + print STDERR join(' ', @PrintArgs); + print STDERR "\n"; + } + elsif ($Verbose == 2) { + print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n"; + } + + # Save STDOUT and STDERR of clang to a temporary file and reroute + # all clang output to ccc-analyzer's STDERR. + # We save the output file in the 'crashes' directory if clang encounters + # any problems with the file. + my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir); + + my $OutputStream = silent_system($HtmlDir, $Cmd, @CmdArgs); + while ( <$OutputStream> ) { + print $ofh $_; + print STDERR $_; + } + my $Result = $?; + close $ofh; + + # Did the command die because of a signal? + if ($ReportFailures) { + if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) { + ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, "Crash", $ofile); + } + elsif ($Result) { + if ($IncludeParserRejects && !($file =~/conftest/)) { + ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, $ParserRejects, $ofile); + } else { + ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, $OtherError, $ofile); + } + } + else { + # Check if there were any unhandled attributes. + if (open(CHILD, $ofile)) { + my %attributes_not_handled; + + # Don't flag warnings about the following attributes that we + # know are currently not supported by Clang. + $attributes_not_handled{"cdecl"} = 1; + + my $ppfile; + while (<CHILD>) { + next if (! /warning: '([^\']+)' attribute ignored/); + + # Have we already spotted this unhandled attribute? + next if (defined $attributes_not_handled{$1}); + $attributes_not_handled{$1} = 1; + + # Get the name of the attribute file. + my $dir = "$HtmlDir/failures"; + my $afile = "$dir/attribute_ignored_$1.txt"; + + # Only create another preprocessed file if the attribute file + # doesn't exist yet. + next if (-e $afile); + + # Add this file to the list of files that contained this attribute. + # Generate a preprocessed file if we haven't already. + if (!(defined $ppfile)) { + $ppfile = ProcessClangFailure($Clang, $Lang, $file, + \@CmdArgsSansAnalyses, + $HtmlDir, $AttributeIgnored, $ofile); + } + + mkpath $dir; + open(AFILE, ">$afile"); + print AFILE "$ppfile\n"; + close(AFILE); + } + close CHILD; + } + } + } + + unlink($ofile); +} + +##----------------------------------------------------------------------------## +# Lookup tables. +##----------------------------------------------------------------------------## + +my %CompileOptionMap = ( + '-nostdinc' => 0, + '-include' => 1, + '-idirafter' => 1, + '-imacros' => 1, + '-iprefix' => 1, + '-iquote' => 1, + '-iwithprefix' => 1, + '-iwithprefixbefore' => 1 +); + +my %LinkerOptionMap = ( + '-framework' => 1, + '-fobjc-link-runtime' => 0 +); + +my %CompilerLinkerOptionMap = ( + '-Wwrite-strings' => 0, + '-ftrapv-handler' => 1, # specifically call out separated -f flag + '-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '=' + '-isysroot' => 1, + '-arch' => 1, + '-m32' => 0, + '-m64' => 0, + '-stdlib' => 0, # This is really a 1 argument, but always has '=' + '--sysroot' => 1, + '-target' => 1, + '-v' => 0, + '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '=' + '-miphoneos-version-min' => 0, # This is really a 1 argument, but always has '=' + '--target' => 0 +); + +my %IgnoredOptionMap = ( + '-MT' => 1, # Ignore these preprocessor options. + '-MF' => 1, + + '-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, + '-u' => 1, + '--serialize-diagnostics' => 1 +); + +my %LangMap = ( + 'c' => $IsCXX ? 'c++' : 'c', + 'cp' => 'c++', + 'cpp' => 'c++', + 'cxx' => 'c++', + 'txx' => 'c++', + 'cc' => 'c++', + 'C' => 'c++', + 'ii' => 'c++-cpp-output', + 'i' => $IsCXX ? 'c++-cpp-output' : 'cpp-output', + 'm' => 'objective-c', + 'mi' => 'objective-c-cpp-output', + 'mm' => 'objective-c++', + 'mii' => 'objective-c++-cpp-output', +); + +my %UniqueOptions = ( + '-isysroot' => 0 +); + +##----------------------------------------------------------------------------## +# Languages accepted. +##----------------------------------------------------------------------------## + +my %LangsAccepted = ( + "objective-c" => 1, + "c" => 1, + "c++" => 1, + "objective-c++" => 1, + "cpp-output" => 1, + "objective-c-cpp-output" => 1, + "c++-cpp-output" => 1 +); + +##----------------------------------------------------------------------------## +# Main Logic. +##----------------------------------------------------------------------------## + +my $Action = 'link'; +my @CompileOpts; +my @LinkOpts; +my @Files; +my $Lang; +my $Output; +my %Uniqued; + +# Forward arguments to gcc. +my $Status = system($Compiler,@ARGV); +if (defined $ENV{'CCC_ANALYZER_LOG'}) { + print STDERR "$Compiler @ARGV\n"; +} +if ($Status) { exit($Status >> 8); } + +# Get the analysis options. +my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; + +# Get the plugins to load. +my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'}; + +# Get the store model. +my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'}; + +# Get the constraints engine. +my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'}; + +#Get the internal stats setting. +my $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'}; + +# Get the output format. +my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'}; +if (!defined $OutputFormat) { $OutputFormat = "html"; } + +# Get the config options. +my $ConfigOptions = $ENV{'CCC_ANALYZER_CONFIG'}; + +# Determine the level of verbosity. +my $Verbose = 0; +if (defined $ENV{'CCC_ANALYZER_VERBOSE'}) { $Verbose = 1; } +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; +my $HasSDK = 0; + +# Process the arguments. +foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { + my $Arg = $ARGV[$i]; + my @ArgParts = split /=/,$Arg,2; + my $ArgKey = $ArgParts[0]; + + # Be friendly to "" in the argument list. + if (!defined($ArgKey)) { + next; + } + + # Modes ccc-analyzer supports + if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; } + elsif ($Arg eq '-c') { $Action = 'compile'; } + elsif ($Arg =~ /^-print-prog-name/) { exit 0; } + + # Specially handle duplicate cases of -arch + if ($Arg eq "-arch") { + my $arch = $ARGV[$i+1]; + # We don't want to process 'ppc' because of Clang's lack of support + # for Altivec (also some #defines won't likely be defined correctly, etc.) + if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; } + $HadArch = 1; + ++$i; + next; + } + + # On OSX/iOS, record if an SDK path was specified. This + # is innocuous for other platforms, so the check just happens. + if ($Arg =~ /^-isysroot/) { + $HasSDK = 1; + } + + # Options with possible arguments that should pass through to compiler. + if (defined $CompileOptionMap{$ArgKey}) { + my $Cnt = $CompileOptionMap{$ArgKey}; + push @CompileOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } + next; + } + # Handle the case where there isn't a space after -iquote + if ($Arg =~ /^-iquote.*/) { + push @CompileOpts,$Arg; + next; + } + + # Options with possible arguments that should pass through to linker. + if (defined $LinkerOptionMap{$ArgKey}) { + my $Cnt = $LinkerOptionMap{$ArgKey}; + push @LinkOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to both compiler + # and the linker. + if (defined $CompilerLinkerOptionMap{$ArgKey}) { + my $Cnt = $CompilerLinkerOptionMap{$ArgKey}; + + # Check if this is an option that should have a unique value, and if so + # determine if the value was checked before. + if ($UniqueOptions{$Arg}) { + if (defined $Uniqued{$Arg}) { + $i += $Cnt; + next; + } + $Uniqued{$Arg} = 1; + } + + push @CompileOpts,$Arg; + push @LinkOpts,$Arg; + + if (scalar @ArgParts == 1) { + while ($Cnt > 0) { + ++$i; --$Cnt; + push @CompileOpts, $ARGV[$i]; + push @LinkOpts, $ARGV[$i]; + } + } + next; + } + + # Ignored options. + if (defined $IgnoredOptionMap{$ArgKey}) { + my $Cnt = $IgnoredOptionMap{$ArgKey}; + while ($Cnt > 0) { + ++$i; --$Cnt; + } + next; + } + + # Compile mode flags. + if ($Arg =~ /^-(?:[DIU]|isystem)(.*)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + next; + } + + if ($Arg =~ /^-m.*/) { + push @CompileOpts,$Arg; + next; + } + + # Language. + if ($Arg eq '-x') { + $Lang = $ARGV[$i+1]; + ++$i; next; + } + + # Output file. + if ($Arg eq '-o') { + ++$i; + $Output = $ARGV[$i]; + next; + } + + # Get the link mode. + if ($Arg =~ /^-[l,L,O]/) { + if ($Arg eq '-O') { push @LinkOpts,'-O1'; } + elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } + else { push @LinkOpts,$Arg; } + + # Must pass this along for the __OPTIMIZE__ macro + if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; } + next; + } + + if ($Arg =~ /^-std=/) { + push @CompileOpts,$Arg; + next; + } + + # Get the compiler/link mode. + if ($Arg =~ /^-F(.+)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + push @LinkOpts,$Tmp; + next; + } + + # Input files. + if ($Arg eq '-filelist') { + # FIXME: Make sure we aren't walking off the end. + open(IN, $ARGV[$i+1]); + while (<IN>) { s/\015?\012//; push @Files,$_; } + close(IN); + ++$i; + next; + } + + if ($Arg =~ /^-f/) { + push @CompileOpts,$Arg; + push @LinkOpts,$Arg; + next; + } + + # Handle -Wno-. We don't care about extra warnings, but + # we should suppress ones that we don't want to see. + if ($Arg =~ /^-Wno-/) { + push @CompileOpts, $Arg; + next; + } + + # Handle -Xclang some-arg. Add both arguments to the compiler options. + if ($Arg =~ /^-Xclang$/) { + # FIXME: Check if we are going off the end. + ++$i; + push @CompileOpts, $Arg; + push @CompileOpts, $ARGV[$i]; + next; + } + + if (!($Arg =~ /^-/)) { + push @Files, $Arg; + next; + } +} + +# 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. +if (not $HasSDK and $UseXCRUN) { + my $sdk = `/usr/bin/xcrun --show-sdk-path -sdk macosx`; + chomp $sdk; + push @CompileOpts, "-isysroot", $sdk; +} + +if ($Action eq 'compile' or $Action eq 'link') { + my @Archs = keys %ArchsSeen; + # Skip the file if we don't support the architectures specified. + exit 0 if ($HadArch && scalar(@Archs) == 0); + + foreach my $file (@Files) { + # Determine the language for the file. + my $FileLang = $Lang; + + if (!defined($FileLang)) { + # Infer the language from the extension. + if ($file =~ /[.]([^.]+)$/) { + $FileLang = $LangMap{$1}; + } + } + + # FileLang still not defined? Skip the file. + next if (!defined $FileLang); + + # Language not accepted? + next if (!defined $LangsAccepted{$FileLang}); + + my @CmdArgs; + my @AnalyzeArgs; + + if ($FileLang ne 'unknown') { + push @CmdArgs, '-x', $FileLang; + } + + if (defined $StoreModel) { + push @AnalyzeArgs, "-analyzer-store=$StoreModel"; + } + + if (defined $ConstraintsModel) { + push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; + } + + if (defined $InternalStats) { + push @AnalyzeArgs, "-analyzer-stats"; + } + + if (defined $Analyses) { + push @AnalyzeArgs, split '\s+', $Analyses; + } + + if (defined $Plugins) { + push @AnalyzeArgs, split '\s+', $Plugins; + } + + if (defined $OutputFormat) { + push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat; + if ($OutputFormat =~ /plist/ || $OutputFormat =~ /sarif/) { + # Change "Output" to be a file. + my $Suffix = $OutputFormat =~ /plist/ ? ".plist" : ".sarif"; + my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => $Suffix, + DIR => $HtmlDir); + $ResultFile = $f; + # If the HtmlDir is not set, we should clean up the plist files. + if (!defined $HtmlDir || $HtmlDir eq "") { + $CleanupFile = $f; + } + } + } + if (defined $ConfigOptions) { + push @AnalyzeArgs, split '\s+', $ConfigOptions; + } + + push @CmdArgs, @CompileOpts; + push @CmdArgs, $file; + + if (scalar @Archs) { + foreach my $arch (@Archs) { + my @NewArgs; + push @NewArgs, '-arch', $arch; + push @NewArgs, @CmdArgs; + Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file); + } + } + else { + Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file); + } + } +} diff --git a/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer.bat b/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer.bat new file mode 100644 index 00000000000..2a85376eb82 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/libexec/ccc-analyzer.bat @@ -0,0 +1 @@ +perl -S ccc-analyzer %*
diff --git a/gnu/llvm/clang/tools/scan-build/man/scan-build.1 b/gnu/llvm/clang/tools/scan-build/man/scan-build.1 new file mode 100644 index 00000000000..4f3cd8d1033 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/man/scan-build.1 @@ -0,0 +1,350 @@ +.\" Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +.\" See https://llvm.org/LICENSE.txt for license information. +.\" SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +.\" $Id$ +.Dd May 25, 2012 +.Dt SCAN-BUILD 1 +.Os "clang" "3.5" +.Sh NAME +.Nm scan-build +.Nd Clang static analyzer +.Sh SYNOPSIS +.Nm +.Op Fl ohkvV +.Op Fl analyze-headers +.Op Fl enable-checker Op Ar checker_name +.Op Fl disable-checker Op Ar checker_name +.Op Fl Fl help +.Op Fl Fl help-checkers +.Op Fl Fl html-title Op Ar =title +.Op Fl Fl keep-going +.Op Fl plist +.Op Fl plist-html +.Op Fl Fl status-bugs +.Op Fl Fl use-c++ Op Ar =compiler_path +.Op Fl Fl use-cc Op Ar =compiler_path +.Op Fl Fl view +.Op Fl constraints Op Ar model +.Op Fl maxloop Ar N +.Op Fl no-failure-reports +.Op Fl stats +.Op Fl store Op Ar model +.Ar build_command +.Op build_options +.\" +.\" Sh DESCRIPTION +.Sh DESCRIPTION +.Nm +is a Perl script that invokes the Clang static analyzer. Options used by +.Nm +or by the analyzer appear first, followed by the +.Ar build_command +and any +.Ar build_options +normally used to build the target system. +.Pp +The static analyzer employs a long list of checking algorithms, see +.Sx CHECKERS . +Output can be written in standard +.Li .plist +and/or HTML format. +.Pp +The following options are supported: +.Bl -tag -width indent +.It Fl analyze-headers +Also analyze functions in #included files. +.It Fl enable-checker Ar checker_name , Fl disable-checker Ar checker_name +Enable/disable +.Ar checker_name . +See +.Sx CHECKERS . +.It Fl h , Fl Fl help +Display this message. +.It Fl Fl help-checkers +List default checkers, see +.Sx CHECKERS . +.It Fl Fl html-title Ns Op = Ns Ar title +Specify the title used on generated HTML pages. +A default title is generated if +.Ar title +is not specified. +.It Fl k , Fl Fl keep-going +Add a +.Dq keep on going +option to +.Ar build_command . +Currently supports make and xcodebuild. This is a convenience option; +one can specify this behavior directly using build options. +.It Fl o +Target directory for HTML report files. Subdirectories will be +created as needed to represent separate invocations +of the analyzer. If this option is not specified, a directory is +created in /tmp (TMPDIR on Mac OS X) to store the reports. +.It Fl plist +Output the results as a set of +.Li .plist +files. (By default the output of +.Nm +is a set of HTML files.) +.It Fl plist-html +Output the results as a set of HTML and .plist files +.It Fl Fl status-bugs +Set exit status to 1 if it found potential bugs and 0 otherwise. By +default the exit status of +.Nm +is that returned by +.Ar build_command . +.It Fl Fl use-c++ Ns Op = Ns Ar compiler_path +Guess the default compiler for your C++ and Objective-C++ code. Use this +option to specify an alternate compiler. +.It Fl Fl use-cc Ns Op = Ns Ar compiler_path +Guess the default compiler for your C and Objective-C code. Use this +option to specify an alternate compiler. +.It Fl v +Verbose output from +.Nm +and the analyzer. A second and +third +.Ar v +increases verbosity. +.It Fl V , Fl Fl view +View analysis results in a web browser when the build completes. +.It Fl constraints Op Ar model +Specify the contraint engine used by the analyzer. By default the +.Ql range +model is used. Specifying +.Ql basic +uses a simpler, less powerful constraint model used by checker-0.160 +and earlier. +.It Fl maxloop Ar N +Specify the number of times a block can be visited before giving +up. Default is 4. Increase for more comprehensive coverage at a +cost of speed. +.It Fl no-failure-reports +Do not create a +.Ql failures +subdirectory that includes analyzer crash reports and preprocessed +source files. +.It Fl stats +Generates visitation statistics for the project being analyzed. +.It Fl store Op Ar model +Specify the store model used by the analyzer. By default, the +.Ql region +store model is used. +.Ql region +specifies a field- +sensitive store model. Users can also specify +.Ql basic +which is far less precise but can more quickly analyze code. +.Ql basic +was the default store model for checker-0.221 and earlier. +.\" +.El +.Sh EXIT STATUS +.Nm +returns the value returned by +.Ar build_command +unless +.Fl Fl status-bugs +or +.Fl Fl keep-going +is used. +.\" +.\" Other sections not yet used ... +.\" .Sh ENVIRONMENT +.\" .Sh FILES +.\" .Sh DIAGNOSTICS +.\" .Sh COMPATIBILITY +.\" .Sh HISTORY +.\" .Sh BUGS +.\" +.Sh CHECKERS +The checkers listed below may be enabled/disabled using the +.Fl enable-checker +and +.Fl disable-checker +options. +A default group of checkers is run unless explicitly disabled. +Exactly which checkers constitute the default group is a function +of the operating system in use; they are listed with +.Fl Fl help-checkers . +.Bl -tag -width indent. +.It core.AdjustedReturnValue +Check to see if the return value of a function call is different than +the caller expects (e.g., from calls through function pointers). +.It core.AttributeNonNull +Check for null pointers passed as arguments to a function whose arguments are marked with the +.Ql nonnull +attribute. +.It core.CallAndMessage +Check for logical errors for function calls and Objective-C message expressions (e.g., uninitialized arguments, null function pointers). +.It core.DivideZero +Check for division by zero. +.It core.NullDereference +Check for dereferences of null pointers. +.It core.StackAddressEscape +Check that addresses to stack memory do not escape the function. +.It core.UndefinedBinaryOperatorResult +Check for undefined results of binary operators. +.It core.VLASize +Check for declarations of VLA of undefined or zero size. +.It core.builtin.BuiltinFunctions +Evaluate compiler builtin functions, e.g. +.Fn alloca . +.It core.builtin.NoReturnFunctions +Evaluate +.Ql panic +functions that are known to not return to the caller. +.It core.uninitialized.ArraySubscript +Check for uninitialized values used as array subscripts. +.It core.uninitialized.Assign +Check for assigning uninitialized values. +.It core.uninitialized.Branch +Check for uninitialized values used as branch conditions. +.It core.uninitialized.CapturedBlockVariable +Check for blocks that capture uninitialized values. +.It core.uninitialized.UndefReturn +Check for uninitialized values being returned to the caller. +.It deadcode.DeadStores +Check for values stored to variables that are never read afterwards. +.It debug.DumpCFG +Display Control-Flow Graphs. +.It debug.DumpCallGraph +Display Call Graph. +.It debug.DumpDominators +Print the dominance tree for a given Control-Flow Graph. +.It debug.DumpLiveVars +Print results of live variable analysis. +.It debug.Stats +Emit warnings with analyzer statistics. +.It debug.TaintTest +Mark tainted symbols as such. +.It debug.ViewCFG +View Control-Flow Graphs using +.Ic GraphViz . +.It debug.ViewCallGraph +View Call Graph using +.Ic GraphViz . +.It llvm.Conventions +Check code for LLVM codebase conventions. +.It osx.API +Check for proper uses of various Mac OS X APIs. +.It osx.AtomicCAS +Evaluate calls to +.Vt OSAtomic +functions. +.It osx.SecKeychainAPI +Check for proper uses of Secure Keychain APIs. +.It osx.cocoa.AtSync +Check for null pointers used as mutexes for @synchronized. +.It osx.cocoa.ClassRelease +Check for sending +.Ql retain , +.Ql release, +or +.Ql autorelease +directly to a Class. +.It osx.cocoa.IncompatibleMethodTypes +Warn about Objective-C method signatures with type incompatibilities. +.It osx.cocoa.NSAutoreleasePool +Warn for suboptimal uses of +.Vt NSAutoreleasePool +in Objective-C GC mode. +.It osx.cocoa.NSError +Check usage of NSError** parameters. +.It osx.cocoa.NilArg +Check for prohibited nil arguments to Objective-C method calls. +.It osx.cocoa.RetainCount +Check for leaks and improper reference count management. +.It osx.cocoa.SelfInit +Check that +.Ql self +is properly initialized inside an initializer method. +.It osx.cocoa.UnusedIvars +Warn about private ivars that are never used. +.It osx.cocoa.VariadicMethodTypes +Check for passing non-Objective-C types to variadic methods that expect only Objective-C types. +.It osx.coreFoundation.CFError +Check usage of CFErrorRef* parameters. +.It osx.coreFoundation.CFNumber +Check for proper uses of +.Fn CFNumberCreate . +.It osx.coreFoundation.CFRetainRelease +Check for null arguments to +.Fn CFRetain , +.Fn CFRelease , +and +.Fn CFMakeCollectable . +.It osx.coreFoundation.containers.OutOfBounds +Checks for index out-of-bounds when using the +.Vt CFArray +API. +.It osx.coreFoundation.containers.PointerSizedValues +Warns if +.Vt CFArray , +.Vt CFDictionary , +or +.Vt CFSet +are created with non-pointer-size values. +.It security.FloatLoopCounter +Warn on using a floating point value as a loop counter (CERT: FLP30-C, FLP30-CPP). +.It security.insecureAPI.UncheckedReturn +Warn on uses of functions whose return values must be always checked. +.It security.insecureAPI.getpw +Warn on uses of +.Fn getpw . +.It security.insecureAPI.gets +Warn on uses of +.Fn gets . +.It security.insecureAPI.mkstemp +Warn when +.Fn mkstemp +is passed fewer than 6 X's in the format string. +.It security.insecureAPI.mktemp +Warn on uses of +.Fn mktemp . +.It security.insecureAPI.rand +Warn on uses of +.Fn rand , +.Fn random , +and related functions. +.It security.insecureAPI.strcpy +Warn on uses of +.Fn strcpy +and +.Fn strcat . +.It security.insecureAPI.vfork +Warn on uses of +.Fn vfork . +.It unix.API +Check calls to various UNIX/Posix functions. +.It unix.Malloc +Check for memory leaks, double free, and use-after-free. +.It unix.cstring.BadSizeArg +Check the size argument passed into C string functions for common +erroneous patterns. +.It unix.cstring.NullArg +Check for null pointers being passed as arguments to C string functions. +.El +.\" +.Sh EXAMPLE +.Ic scan-build -o /tmp/myhtmldir make -j4 +.Pp +The above example causes analysis reports to be deposited into +a subdirectory of +.Pa /tmp/myhtmldir +and to run +.Ic make +with the +.Fl j4 +option. +A different subdirectory is created each time +.Nm +analyzes a project. +The analyzer should support most parallel builds, but not distributed builds. +.Sh AUTHORS +.Nm +was written by +.An "Ted Kremenek" . +Documentation contributed by +.An "James K. Lowden" Aq jklowden@schemamania.org . diff --git a/gnu/llvm/clang/tools/scan-build/share/scan-build/scanview.css b/gnu/llvm/clang/tools/scan-build/share/scan-build/scanview.css new file mode 100644 index 00000000000..cf8a5a6ad47 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/share/scan-build/scanview.css @@ -0,0 +1,62 @@ +body { color:#000000; background-color:#ffffff } +body { font-family: Helvetica, sans-serif; font-size:9pt } +h1 { font-size: 14pt; } +h2 { font-size: 12pt; } +table { font-size:9pt } +table { border-spacing: 0px; border: 1px solid black } +th, table thead { + background-color:#eee; color:#666666; + font-weight: bold; cursor: default; + text-align:center; + font-weight: bold; font-family: Verdana; + white-space:nowrap; +} +.W { font-size:0px } +th, td { padding:5px; padding-left:8px; text-align:left } +td.SUMM_DESC { padding-left:12px } +td.DESC { white-space:pre } +td.Q { text-align:right } +td { text-align:left } +tbody.scrollContent { overflow:auto } + +table.form_group { + background-color: #ccc; + border: 1px solid #333; + padding: 2px; +} + +table.form_inner_group { + background-color: #ccc; + border: 1px solid #333; + padding: 0px; +} + +table.form { + background-color: #999; + border: 1px solid #333; + padding: 2px; +} + +td.form_label { + text-align: right; + vertical-align: top; +} +/* For one line entires */ +td.form_clabel { + text-align: right; + vertical-align: center; +} +td.form_value { + text-align: left; + vertical-align: top; +} +td.form_submit { + text-align: right; + vertical-align: top; +} + +h1.SubmitFail { + color: #f00; +} +h1.SubmitOk { +} diff --git a/gnu/llvm/clang/tools/scan-build/share/scan-build/sorttable.js b/gnu/llvm/clang/tools/scan-build/share/scan-build/sorttable.js new file mode 100644 index 00000000000..32faa078d89 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-build/share/scan-build/sorttable.js @@ -0,0 +1,492 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backward compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=0; i<headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j<rows.length; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i=0; i<table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i=newrows.length-1; i>=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + sorttable.init(); // call the onload handler + } + }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff + var _timer = setInterval(function() { + if (/loaded|complete/.test(document.readyState)) { + sorttable.init(); // call the onload handler + } + }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; diff --git a/gnu/llvm/clang/tools/scan-view/CMakeLists.txt b/gnu/llvm/clang/tools/scan-view/CMakeLists.txt new file mode 100644 index 00000000000..22edb974bac --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/CMakeLists.txt @@ -0,0 +1,51 @@ +option(CLANG_INSTALL_SCANVIEW "Install the scan-view tool" ON) + +set(BinFiles + scan-view) + +set(ShareFiles + ScanView.py + Reporter.py + startfile.py + FileRadar.scpt + GetRadarVersion.scpt + bugcatcher.ico) + +if(CLANG_INSTALL_SCANVIEW) + foreach(BinFile ${BinFiles}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/bin/${BinFile} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/bin + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile} + ${CMAKE_BINARY_DIR}/bin/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bin/${BinFile}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/bin/${BinFile}) + install(PROGRAMS bin/${BinFile} + DESTINATION bin + COMPONENT scan-view) + endforeach() + + foreach(ShareFile ${ShareFiles}) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/share/scan-view/${ShareFile} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_BINARY_DIR}/share/scan-view + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/share/${ShareFile} + ${CMAKE_BINARY_DIR}/share/scan-view/ + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/share/${ShareFile}) + list(APPEND Depends ${CMAKE_BINARY_DIR}/share/scan-view/${ShareFile}) + install(FILES share/${ShareFile} + DESTINATION share/scan-view + COMPONENT scan-view) + endforeach() + + add_custom_target(scan-view ALL DEPENDS ${Depends}) + set_target_properties(scan-view PROPERTIES FOLDER "Misc") + + if(NOT LLVM_ENABLE_IDE) + add_llvm_install_targets("install-scan-view" + DEPENDS scan-view + COMPONENT scan-view) + endif() +endif() diff --git a/gnu/llvm/clang/tools/scan-view/bin/scan-view b/gnu/llvm/clang/tools/scan-view/bin/scan-view new file mode 100755 index 00000000000..6165432e7af --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/bin/scan-view @@ -0,0 +1,150 @@ +#!/usr/bin/env python + +from __future__ import print_function + +"""The clang static analyzer results viewer. +""" + +import sys +import imp +import os +import posixpath +import threading +import time +try: + from urllib.request import urlopen +except ImportError: + from urllib2 import urlopen +import webbrowser + +# How long to wait for server to start. +kSleepTimeout = .05 +kMaxSleeps = int(60 / kSleepTimeout) + +# Default server parameters + +kDefaultHost = '127.0.0.1' +kDefaultPort = 8181 +kMaxPortsToTry = 100 + +### + + +def url_is_up(url): + try: + o = urlopen(url) + except IOError: + return False + o.close() + return True + + +def start_browser(port, options): + import webbrowser + + url = 'http://%s:%d' % (options.host, port) + + # Wait for server to start... + if options.debug: + sys.stderr.write('%s: Waiting for server.' % sys.argv[0]) + sys.stderr.flush() + for i in range(kMaxSleeps): + if url_is_up(url): + break + if options.debug: + sys.stderr.write('.') + sys.stderr.flush() + time.sleep(kSleepTimeout) + else: + print('WARNING: Unable to detect that server started.', file=sys.stderr) + + if options.debug: + print('%s: Starting webbrowser...' % sys.argv[0], file=sys.stderr) + webbrowser.open(url) + + +def run(port, options, root): + # Prefer to look relative to the installed binary + share = os.path.dirname(__file__) + "/../share/scan-view" + if not os.path.isdir(share): + # Otherwise look relative to the source + share = os.path.dirname(__file__) + "/../../scan-view/share" + sys.path.append(share) + + import ScanView + try: + print('Starting scan-view at: http://%s:%d' % (options.host, + port)) + print(' Use Ctrl-C to exit.') + httpd = ScanView.create_server((options.host, port), + options, root) + httpd.serve_forever() + except KeyboardInterrupt: + pass + + +def port_is_open(port): + try: + import socketserver + except ImportError: + import SocketServer as socketserver + try: + t = socketserver.TCPServer((kDefaultHost, port), None) + except: + return False + t.server_close() + return True + + +def main(): + import argparse + parser = argparse.ArgumentParser(description="The clang static analyzer " + "results viewer.") + parser.add_argument("root", metavar="<results directory>", type=str) + parser.add_argument( + '--host', dest="host", default=kDefaultHost, type=str, + help="Host interface to listen on. (default=%s)" % kDefaultHost) + parser.add_argument('--port', dest="port", default=None, type=int, + help="Port to listen on. (default=%s)" % kDefaultPort) + parser.add_argument("--debug", dest="debug", default=0, + action="count", + help="Print additional debugging information.") + parser.add_argument("--auto-reload", dest="autoReload", default=False, + action="store_true", + help="Automatically update module for each request.") + parser.add_argument("--no-browser", dest="startBrowser", default=True, + action="store_false", + help="Don't open a webbrowser on startup.") + parser.add_argument("--allow-all-hosts", dest="onlyServeLocal", + default=True, action="store_false", + help='Allow connections from any host (access ' + 'restricted to "127.0.0.1" by default)') + args = parser.parse_args() + + # Make sure this directory is in a reasonable state to view. + if not posixpath.exists(posixpath.join(args.root, 'index.html')): + parser.error('Invalid directory, analysis results not found!') + + # Find an open port. We aren't particularly worried about race + # conditions here. Note that if the user specified a port we only + # use that one. + if args.port is not None: + port = args.port + else: + for i in range(kMaxPortsToTry): + if port_is_open(kDefaultPort + i): + port = kDefaultPort + i + break + else: + parser.error('Unable to find usable port in [%d,%d)' % + (kDefaultPort, kDefaultPort+kMaxPortsToTry)) + + # Kick off thread to wait for server and start web browser, if + # requested. + if args.startBrowser: + threading.Thread(target=start_browser, args=(port, args)).start() + + run(port, args, args.root) + +if __name__ == '__main__': + main() diff --git a/gnu/llvm/clang/tools/scan-view/share/FileRadar.scpt b/gnu/llvm/clang/tools/scan-view/share/FileRadar.scpt Binary files differnew file mode 100644 index 00000000000..1c7455285cc --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/share/FileRadar.scpt diff --git a/gnu/llvm/clang/tools/scan-view/share/GetRadarVersion.scpt b/gnu/llvm/clang/tools/scan-view/share/GetRadarVersion.scpt new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/share/GetRadarVersion.scpt diff --git a/gnu/llvm/clang/tools/scan-view/share/Reporter.py b/gnu/llvm/clang/tools/scan-view/share/Reporter.py new file mode 100644 index 00000000000..b1ff16142e2 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/share/Reporter.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Methods for reporting bugs.""" + +import subprocess, sys, os + +__all__ = ['ReportFailure', 'BugReport', 'getReporters'] + +# + +class ReportFailure(Exception): + """Generic exception for failures in bug reporting.""" + def __init__(self, value): + self.value = value + +# Collect information about a bug. + +class BugReport(object): + def __init__(self, title, description, files): + self.title = title + self.description = description + self.files = files + +# Reporter interfaces. + +import os + +import email, mimetypes, smtplib +from email import encoders +from email.message import Message +from email.mime.base import MIMEBase +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText + +#===------------------------------------------------------------------------===# +# ReporterParameter +#===------------------------------------------------------------------------===# + +class ReporterParameter(object): + def __init__(self, n): + self.name = n + def getName(self): + return self.name + def getValue(self,r,bugtype,getConfigOption): + return getConfigOption(r.getName(),self.getName()) + def saveConfigValue(self): + return True + +class TextParameter (ReporterParameter): + def getHTML(self,r,bugtype,getConfigOption): + return """\ +<tr> +<td class="form_clabel">%s:</td> +<td class="form_value"><input type="text" name="%s_%s" value="%s"></td> +</tr>"""%(self.getName(),r.getName(),self.getName(),self.getValue(r,bugtype,getConfigOption)) + +class SelectionParameter (ReporterParameter): + def __init__(self, n, values): + ReporterParameter.__init__(self,n) + self.values = values + + def getHTML(self,r,bugtype,getConfigOption): + default = self.getValue(r,bugtype,getConfigOption) + return """\ +<tr> +<td class="form_clabel">%s:</td><td class="form_value"><select name="%s_%s"> +%s +</select></td>"""%(self.getName(),r.getName(),self.getName(),'\n'.join(["""\ +<option value="%s"%s>%s</option>"""%(o[0], + o[0] == default and ' selected="selected"' or '', + o[1]) for o in self.values])) + +#===------------------------------------------------------------------------===# +# Reporters +#===------------------------------------------------------------------------===# + +class EmailReporter(object): + def getName(self): + return 'Email' + + def getParameters(self): + return [TextParameter(x) for x in ['To', 'From', 'SMTP Server', 'SMTP Port']] + + # Lifted from python email module examples. + def attachFile(self, outer, path): + # Guess the content type based on the file's extension. Encoding + # will be ignored, although we should check for simple things like + # gzip'd or compressed files. + ctype, encoding = mimetypes.guess_type(path) + if ctype is None or encoding is not None: + # No guess could be made, or the file is encoded (compressed), so + # use a generic bag-of-bits type. + ctype = 'application/octet-stream' + maintype, subtype = ctype.split('/', 1) + if maintype == 'text': + fp = open(path) + # Note: we should handle calculating the charset + msg = MIMEText(fp.read(), _subtype=subtype) + fp.close() + else: + fp = open(path, 'rb') + msg = MIMEBase(maintype, subtype) + msg.set_payload(fp.read()) + fp.close() + # Encode the payload using Base64 + encoders.encode_base64(msg) + # Set the filename parameter + msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path)) + outer.attach(msg) + + def fileReport(self, report, parameters): + mainMsg = """\ +BUG REPORT +--- +Title: %s +Description: %s +"""%(report.title, report.description) + + if not parameters.get('To'): + raise ReportFailure('No "To" address specified.') + if not parameters.get('From'): + raise ReportFailure('No "From" address specified.') + + msg = MIMEMultipart() + msg['Subject'] = 'BUG REPORT: %s'%(report.title) + # FIXME: Get config parameters + msg['To'] = parameters.get('To') + msg['From'] = parameters.get('From') + msg.preamble = mainMsg + + msg.attach(MIMEText(mainMsg, _subtype='text/plain')) + for file in report.files: + self.attachFile(msg, file) + + try: + s = smtplib.SMTP(host=parameters.get('SMTP Server'), + port=parameters.get('SMTP Port')) + s.sendmail(msg['From'], msg['To'], msg.as_string()) + s.close() + except: + raise ReportFailure('Unable to send message via SMTP.') + + return "Message sent!" + +class BugzillaReporter(object): + def getName(self): + return 'Bugzilla' + + def getParameters(self): + return [TextParameter(x) for x in ['URL','Product']] + + def fileReport(self, report, parameters): + raise NotImplementedError + + +class RadarClassificationParameter(SelectionParameter): + def __init__(self): + SelectionParameter.__init__(self,"Classification", + [['1', 'Security'], ['2', 'Crash/Hang/Data Loss'], + ['3', 'Performance'], ['4', 'UI/Usability'], + ['6', 'Serious Bug'], ['7', 'Other']]) + + def saveConfigValue(self): + return False + + def getValue(self,r,bugtype,getConfigOption): + if bugtype.find("leak") != -1: + return '3' + elif bugtype.find("dereference") != -1: + return '2' + elif bugtype.find("missing ivar release") != -1: + return '3' + else: + return '7' + +class RadarReporter(object): + @staticmethod + def isAvailable(): + # FIXME: Find this .scpt better + path = os.path.join(os.path.dirname(__file__),'../share/scan-view/GetRadarVersion.scpt') + try: + p = subprocess.Popen(['osascript',path], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + return False + data,err = p.communicate() + res = p.wait() + # FIXME: Check version? Check for no errors? + return res == 0 + + def getName(self): + return 'Radar' + + def getParameters(self): + return [ TextParameter('Component'), TextParameter('Component Version'), + RadarClassificationParameter() ] + + def fileReport(self, report, parameters): + component = parameters.get('Component', '') + componentVersion = parameters.get('Component Version', '') + classification = parameters.get('Classification', '') + personID = "" + diagnosis = "" + config = "" + + if not component.strip(): + component = 'Bugs found by clang Analyzer' + if not componentVersion.strip(): + componentVersion = 'X' + + script = os.path.join(os.path.dirname(__file__),'../share/scan-view/FileRadar.scpt') + args = ['osascript', script, component, componentVersion, classification, personID, report.title, + report.description, diagnosis, config] + [os.path.abspath(f) for f in report.files] +# print >>sys.stderr, args + try: + p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except: + raise ReportFailure("Unable to file radar (AppleScript failure).") + data, err = p.communicate() + res = p.wait() + + if res: + raise ReportFailure("Unable to file radar (AppleScript failure).") + + try: + values = eval(data) + except: + raise ReportFailure("Unable to process radar results.") + + # We expect (int: bugID, str: message) + if len(values) != 2 or not isinstance(values[0], int): + raise ReportFailure("Unable to process radar results.") + + bugID,message = values + bugID = int(bugID) + + if not bugID: + raise ReportFailure(message) + + return "Filed: <a href=\"rdar://%d/\">%d</a>"%(bugID,bugID) + +### + +def getReporters(): + reporters = [] + if RadarReporter.isAvailable(): + reporters.append(RadarReporter()) + reporters.append(EmailReporter()) + return reporters + diff --git a/gnu/llvm/clang/tools/scan-view/share/ScanView.py b/gnu/llvm/clang/tools/scan-view/share/ScanView.py new file mode 100644 index 00000000000..a6cc7692ffe --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/share/ScanView.py @@ -0,0 +1,785 @@ +from __future__ import print_function +try: + from http.server import HTTPServer, SimpleHTTPRequestHandler +except ImportError: + from BaseHTTPServer import HTTPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler +import os +import sys +try: + from urlparse import urlparse + from urllib import unquote +except ImportError: + from urllib.parse import urlparse, unquote + +import posixpath + +if sys.version_info.major >= 3: + from io import StringIO, BytesIO +else: + from io import BytesIO, BytesIO as StringIO + +import re +import shutil +import threading +import time +import socket +import itertools + +import Reporter +try: + import configparser +except ImportError: + import ConfigParser as configparser + +### +# Various patterns matched or replaced by server. + +kReportFileRE = re.compile('(.*/)?report-(.*)\\.html') + +kBugKeyValueRE = re.compile('<!-- BUG([^ ]*) (.*) -->') + +# <!-- REPORTPROBLEM file="crashes/clang_crash_ndSGF9.mi" stderr="crashes/clang_crash_ndSGF9.mi.stderr.txt" info="crashes/clang_crash_ndSGF9.mi.info" --> + +kReportCrashEntryRE = re.compile('<!-- REPORTPROBLEM (.*?)-->') +kReportCrashEntryKeyValueRE = re.compile(' ?([^=]+)="(.*?)"') + +kReportReplacements = [] + +# Add custom javascript. +kReportReplacements.append((re.compile('<!-- SUMMARYENDHEAD -->'), """\ +<script language="javascript" type="text/javascript"> +function load(url) { + if (window.XMLHttpRequest) { + req = new XMLHttpRequest(); + } else if (window.ActiveXObject) { + req = new ActiveXObject("Microsoft.XMLHTTP"); + } + if (req != undefined) { + req.open("GET", url, true); + req.send(""); + } +} +</script>""")) + +# Insert additional columns. +kReportReplacements.append((re.compile('<!-- REPORTBUGCOL -->'), + '<td></td><td></td>')) + +# Insert report bug and open file links. +kReportReplacements.append((re.compile('<!-- REPORTBUG id="report-(.*)\\.html" -->'), + ('<td class="Button"><a href="report/\\1">Report Bug</a></td>' + + '<td class="Button"><a href="javascript:load(\'open/\\1\')">Open File</a></td>'))) + +kReportReplacements.append((re.compile('<!-- REPORTHEADER -->'), + '<h3><a href="/">Summary</a> > Report %(report)s</h3>')) + +kReportReplacements.append((re.compile('<!-- REPORTSUMMARYEXTRA -->'), + '<td class="Button"><a href="report/%(report)s">Report Bug</a></td>')) + +# Insert report crashes link. + +# Disabled for the time being until we decide exactly when this should +# be enabled. Also the radar reporter needs to be fixed to report +# multiple files. + +#kReportReplacements.append((re.compile('<!-- REPORTCRASHES -->'), +# '<br>These files will automatically be attached to ' + +# 'reports filed here: <a href="report_crashes">Report Crashes</a>.')) + +### +# Other simple parameters + +kShare = posixpath.join(posixpath.dirname(__file__), '../share/scan-view') +kConfigPath = os.path.expanduser('~/.scanview.cfg') + +### + +__version__ = "0.1" + +__all__ = ["create_server"] + +class ReporterThread(threading.Thread): + def __init__(self, report, reporter, parameters, server): + threading.Thread.__init__(self) + self.report = report + self.server = server + self.reporter = reporter + self.parameters = parameters + self.success = False + self.status = None + + def run(self): + result = None + try: + if self.server.options.debug: + print("%s: SERVER: submitting bug."%(sys.argv[0],), file=sys.stderr) + self.status = self.reporter.fileReport(self.report, self.parameters) + self.success = True + time.sleep(3) + if self.server.options.debug: + print("%s: SERVER: submission complete."%(sys.argv[0],), file=sys.stderr) + except Reporter.ReportFailure as e: + self.status = e.value + except Exception as e: + s = StringIO() + import traceback + print('<b>Unhandled Exception</b><br><pre>', file=s) + traceback.print_exc(file=s) + print('</pre>', file=s) + self.status = s.getvalue() + +class ScanViewServer(HTTPServer): + def __init__(self, address, handler, root, reporters, options): + HTTPServer.__init__(self, address, handler) + self.root = root + self.reporters = reporters + self.options = options + self.halted = False + self.config = None + self.load_config() + + def load_config(self): + self.config = configparser.RawConfigParser() + + # Add defaults + self.config.add_section('ScanView') + for r in self.reporters: + self.config.add_section(r.getName()) + for p in r.getParameters(): + if p.saveConfigValue(): + self.config.set(r.getName(), p.getName(), '') + + # Ignore parse errors + try: + self.config.read([kConfigPath]) + except: + pass + + # Save on exit + import atexit + atexit.register(lambda: self.save_config()) + + def save_config(self): + # Ignore errors (only called on exit). + try: + f = open(kConfigPath,'w') + self.config.write(f) + f.close() + except: + pass + + def halt(self): + self.halted = True + if self.options.debug: + print("%s: SERVER: halting." % (sys.argv[0],), file=sys.stderr) + + def serve_forever(self): + while not self.halted: + if self.options.debug > 1: + print("%s: SERVER: waiting..." % (sys.argv[0],), file=sys.stderr) + try: + self.handle_request() + except OSError as e: + print('OSError',e.errno) + + def finish_request(self, request, client_address): + if self.options.autoReload: + import ScanView + self.RequestHandlerClass = reload(ScanView).ScanViewRequestHandler + HTTPServer.finish_request(self, request, client_address) + + def handle_error(self, request, client_address): + # Ignore socket errors + info = sys.exc_info() + if info and isinstance(info[1], socket.error): + if self.options.debug > 1: + print("%s: SERVER: ignored socket error." % (sys.argv[0],), file=sys.stderr) + return + HTTPServer.handle_error(self, request, client_address) + +# Borrowed from Quixote, with simplifications. +def parse_query(qs, fields=None): + if fields is None: + fields = {} + for chunk in (_f for _f in qs.split('&') if _f): + if '=' not in chunk: + name = chunk + value = '' + else: + name, value = chunk.split('=', 1) + name = unquote(name.replace('+', ' ')) + value = unquote(value.replace('+', ' ')) + item = fields.get(name) + if item is None: + fields[name] = [value] + else: + item.append(value) + return fields + +class ScanViewRequestHandler(SimpleHTTPRequestHandler): + server_version = "ScanViewServer/" + __version__ + dynamic_mtime = time.time() + + def do_HEAD(self): + try: + SimpleHTTPRequestHandler.do_HEAD(self) + except Exception as e: + self.handle_exception(e) + + def do_GET(self): + try: + SimpleHTTPRequestHandler.do_GET(self) + except Exception as e: + self.handle_exception(e) + + def do_POST(self): + """Serve a POST request.""" + try: + length = self.headers.getheader('content-length') or "0" + try: + length = int(length) + except: + length = 0 + content = self.rfile.read(length) + fields = parse_query(content) + f = self.send_head(fields) + if f: + self.copyfile(f, self.wfile) + f.close() + except Exception as e: + self.handle_exception(e) + + def log_message(self, format, *args): + if self.server.options.debug: + sys.stderr.write("%s: SERVER: %s - - [%s] %s\n" % + (sys.argv[0], + self.address_string(), + self.log_date_time_string(), + format%args)) + + def load_report(self, report): + path = os.path.join(self.server.root, 'report-%s.html'%report) + data = open(path).read() + keys = {} + for item in kBugKeyValueRE.finditer(data): + k,v = item.groups() + keys[k] = v + return keys + + def load_crashes(self): + path = posixpath.join(self.server.root, 'index.html') + data = open(path).read() + problems = [] + for item in kReportCrashEntryRE.finditer(data): + fieldData = item.group(1) + fields = dict([i.groups() for i in + kReportCrashEntryKeyValueRE.finditer(fieldData)]) + problems.append(fields) + return problems + + def handle_exception(self, exc): + import traceback + s = StringIO() + print("INTERNAL ERROR\n", file=s) + traceback.print_exc(file=s) + f = self.send_string(s.getvalue(), 'text/plain') + if f: + self.copyfile(f, self.wfile) + f.close() + + def get_scalar_field(self, name): + if name in self.fields: + return self.fields[name][0] + else: + return None + + def submit_bug(self, c): + title = self.get_scalar_field('title') + description = self.get_scalar_field('description') + report = self.get_scalar_field('report') + reporterIndex = self.get_scalar_field('reporter') + files = [] + for fileID in self.fields.get('files',[]): + try: + i = int(fileID) + except: + i = None + if i is None or i<0 or i>=len(c.files): + return (False, 'Invalid file ID') + files.append(c.files[i]) + + if not title: + return (False, "Missing title.") + if not description: + return (False, "Missing description.") + try: + reporterIndex = int(reporterIndex) + except: + return (False, "Invalid report method.") + + # Get the reporter and parameters. + reporter = self.server.reporters[reporterIndex] + parameters = {} + for o in reporter.getParameters(): + name = '%s_%s'%(reporter.getName(),o.getName()) + if name not in self.fields: + return (False, + 'Missing field "%s" for %s report method.'%(name, + reporter.getName())) + parameters[o.getName()] = self.get_scalar_field(name) + + # Update config defaults. + if report != 'None': + self.server.config.set('ScanView', 'reporter', reporterIndex) + for o in reporter.getParameters(): + if o.saveConfigValue(): + name = o.getName() + self.server.config.set(reporter.getName(), name, parameters[name]) + + # Create the report. + bug = Reporter.BugReport(title, description, files) + + # Kick off a reporting thread. + t = ReporterThread(bug, reporter, parameters, self.server) + t.start() + + # Wait for thread to die... + while t.isAlive(): + time.sleep(.25) + submitStatus = t.status + + return (t.success, t.status) + + def send_report_submit(self): + report = self.get_scalar_field('report') + c = self.get_report_context(report) + if c.reportSource is None: + reportingFor = "Report Crashes > " + fileBug = """\ +<a href="/report_crashes">File Bug</a> > """%locals() + else: + reportingFor = '<a href="/%s">Report %s</a> > ' % (c.reportSource, + report) + fileBug = '<a href="/report/%s">File Bug</a> > ' % report + title = self.get_scalar_field('title') + description = self.get_scalar_field('description') + + res,message = self.submit_bug(c) + + if res: + statusClass = 'SubmitOk' + statusName = 'Succeeded' + else: + statusClass = 'SubmitFail' + statusName = 'Failed' + + result = """ +<head> + <title>Bug Submission</title> + <link rel="stylesheet" type="text/css" href="/scanview.css" /> +</head> +<body> +<h3> +<a href="/">Summary</a> > +%(reportingFor)s +%(fileBug)s +Submit</h3> +<form name="form" action=""> +<table class="form"> +<tr><td> +<table class="form_group"> +<tr> + <td class="form_clabel">Title:</td> + <td class="form_value"> + <input type="text" name="title" size="50" value="%(title)s" disabled> + </td> +</tr> +<tr> + <td class="form_label">Description:</td> + <td class="form_value"> +<textarea rows="10" cols="80" name="description" disabled> +%(description)s +</textarea> + </td> +</table> +</td></tr> +</table> +</form> +<h1 class="%(statusClass)s">Submission %(statusName)s</h1> +%(message)s +<p> +<hr> +<a href="/">Return to Summary</a> +</body> +</html>"""%locals() + return self.send_string(result) + + def send_open_report(self, report): + try: + keys = self.load_report(report) + except IOError: + return self.send_error(400, 'Invalid report.') + + file = keys.get('FILE') + if not file or not posixpath.exists(file): + return self.send_error(400, 'File does not exist: "%s"' % file) + + import startfile + if self.server.options.debug: + print('%s: SERVER: opening "%s"'%(sys.argv[0], + file), file=sys.stderr) + + status = startfile.open(file) + if status: + res = 'Opened: "%s"' % file + else: + res = 'Open failed: "%s"' % file + + return self.send_string(res, 'text/plain') + + def get_report_context(self, report): + class Context(object): + pass + if report is None or report == 'None': + data = self.load_crashes() + # Don't allow empty reports. + if not data: + raise ValueError('No crashes detected!') + c = Context() + c.title = 'clang static analyzer failures' + + stderrSummary = "" + for item in data: + if 'stderr' in item: + path = posixpath.join(self.server.root, item['stderr']) + if os.path.exists(path): + lns = itertools.islice(open(path), 0, 10) + stderrSummary += '%s\n--\n%s' % (item.get('src', + '<unknown>'), + ''.join(lns)) + + c.description = """\ +The clang static analyzer failed on these inputs: +%s + +STDERR Summary +-------------- +%s +""" % ('\n'.join([item.get('src','<unknown>') for item in data]), + stderrSummary) + c.reportSource = None + c.navMarkup = "Report Crashes > " + c.files = [] + for item in data: + c.files.append(item.get('src','')) + c.files.append(posixpath.join(self.server.root, + item.get('file',''))) + c.files.append(posixpath.join(self.server.root, + item.get('clangfile',''))) + c.files.append(posixpath.join(self.server.root, + item.get('stderr',''))) + c.files.append(posixpath.join(self.server.root, + item.get('info',''))) + # Just in case something failed, ignore files which don't + # exist. + c.files = [f for f in c.files + if os.path.exists(f) and os.path.isfile(f)] + else: + # Check that this is a valid report. + path = posixpath.join(self.server.root, 'report-%s.html' % report) + if not posixpath.exists(path): + raise ValueError('Invalid report ID') + keys = self.load_report(report) + c = Context() + c.title = keys.get('DESC','clang error (unrecognized') + c.description = """\ +Bug reported by the clang static analyzer. + +Description: %s +File: %s +Line: %s +"""%(c.title, keys.get('FILE','<unknown>'), keys.get('LINE', '<unknown>')) + c.reportSource = 'report-%s.html' % report + c.navMarkup = """<a href="/%s">Report %s</a> > """ % (c.reportSource, + report) + + c.files = [path] + return c + + def send_report(self, report, configOverrides=None): + def getConfigOption(section, field): + if (configOverrides is not None and + section in configOverrides and + field in configOverrides[section]): + return configOverrides[section][field] + return self.server.config.get(section, field) + + # report is None is used for crashes + try: + c = self.get_report_context(report) + except ValueError as e: + return self.send_error(400, e.message) + + title = c.title + description= c.description + reportingFor = c.navMarkup + if c.reportSource is None: + extraIFrame = "" + else: + extraIFrame = """\ +<iframe src="/%s" width="100%%" height="40%%" + scrolling="auto" frameborder="1"> + <a href="/%s">View Bug Report</a> +</iframe>""" % (c.reportSource, c.reportSource) + + reporterSelections = [] + reporterOptions = [] + + try: + active = int(getConfigOption('ScanView','reporter')) + except: + active = 0 + for i,r in enumerate(self.server.reporters): + selected = (i == active) + if selected: + selectedStr = ' selected' + else: + selectedStr = '' + reporterSelections.append('<option value="%d"%s>%s</option>'%(i,selectedStr,r.getName())) + options = '\n'.join([ o.getHTML(r,title,getConfigOption) for o in r.getParameters()]) + display = ('none','')[selected] + reporterOptions.append("""\ +<tr id="%sReporterOptions" style="display:%s"> + <td class="form_label">%s Options</td> + <td class="form_value"> + <table class="form_inner_group"> +%s + </table> + </td> +</tr> +"""%(r.getName(),display,r.getName(),options)) + reporterSelections = '\n'.join(reporterSelections) + reporterOptionsDivs = '\n'.join(reporterOptions) + reportersArray = '[%s]'%(','.join([repr(r.getName()) for r in self.server.reporters])) + + if c.files: + fieldSize = min(5, len(c.files)) + attachFileOptions = '\n'.join(["""\ +<option value="%d" selected>%s</option>""" % (i,v) for i,v in enumerate(c.files)]) + attachFileRow = """\ +<tr> + <td class="form_label">Attach:</td> + <td class="form_value"> +<select style="width:100%%" name="files" multiple size=%d> +%s +</select> + </td> +</tr> +""" % (min(5, len(c.files)), attachFileOptions) + else: + attachFileRow = "" + + result = """<html> +<head> + <title>File Bug</title> + <link rel="stylesheet" type="text/css" href="/scanview.css" /> +</head> +<script language="javascript" type="text/javascript"> +var reporters = %(reportersArray)s; +function updateReporterOptions() { + index = document.getElementById('reporter').selectedIndex; + for (var i=0; i < reporters.length; ++i) { + o = document.getElementById(reporters[i] + "ReporterOptions"); + if (i == index) { + o.style.display = ""; + } else { + o.style.display = "none"; + } + } +} +</script> +<body onLoad="updateReporterOptions()"> +<h3> +<a href="/">Summary</a> > +%(reportingFor)s +File Bug</h3> +<form name="form" action="/report_submit" method="post"> +<input type="hidden" name="report" value="%(report)s"> + +<table class="form"> +<tr><td> +<table class="form_group"> +<tr> + <td class="form_clabel">Title:</td> + <td class="form_value"> + <input type="text" name="title" size="50" value="%(title)s"> + </td> +</tr> +<tr> + <td class="form_label">Description:</td> + <td class="form_value"> +<textarea rows="10" cols="80" name="description"> +%(description)s +</textarea> + </td> +</tr> + +%(attachFileRow)s + +</table> +<br> +<table class="form_group"> +<tr> + <td class="form_clabel">Method:</td> + <td class="form_value"> + <select id="reporter" name="reporter" onChange="updateReporterOptions()"> + %(reporterSelections)s + </select> + </td> +</tr> +%(reporterOptionsDivs)s +</table> +<br> +</td></tr> +<tr><td class="form_submit"> + <input align="right" type="submit" name="Submit" value="Submit"> +</td></tr> +</table> +</form> + +%(extraIFrame)s + +</body> +</html>"""%locals() + + return self.send_string(result) + + def send_head(self, fields=None): + if (self.server.options.onlyServeLocal and + self.client_address[0] != '127.0.0.1'): + return self.send_error(401, 'Unauthorized host.') + + if fields is None: + fields = {} + self.fields = fields + + o = urlparse(self.path) + self.fields = parse_query(o.query, fields) + path = posixpath.normpath(unquote(o.path)) + + # Split the components and strip the root prefix. + components = path.split('/')[1:] + + # Special case some top-level entries. + if components: + name = components[0] + if len(components)==2: + if name=='report': + return self.send_report(components[1]) + elif name=='open': + return self.send_open_report(components[1]) + elif len(components)==1: + if name=='quit': + self.server.halt() + return self.send_string('Goodbye.', 'text/plain') + elif name=='report_submit': + return self.send_report_submit() + elif name=='report_crashes': + overrides = { 'ScanView' : {}, + 'Radar' : {}, + 'Email' : {} } + for i,r in enumerate(self.server.reporters): + if r.getName() == 'Radar': + overrides['ScanView']['reporter'] = i + break + overrides['Radar']['Component'] = 'llvm - checker' + overrides['Radar']['Component Version'] = 'X' + return self.send_report(None, overrides) + elif name=='favicon.ico': + return self.send_path(posixpath.join(kShare,'bugcatcher.ico')) + + # Match directory entries. + if components[-1] == '': + components[-1] = 'index.html' + + relpath = '/'.join(components) + path = posixpath.join(self.server.root, relpath) + + if self.server.options.debug > 1: + print('%s: SERVER: sending path "%s"'%(sys.argv[0], + path), file=sys.stderr) + return self.send_path(path) + + def send_404(self): + self.send_error(404, "File not found") + return None + + def send_path(self, path): + # If the requested path is outside the root directory, do not open it + rel = os.path.abspath(path) + if not rel.startswith(os.path.abspath(self.server.root)): + return self.send_404() + + ctype = self.guess_type(path) + if ctype.startswith('text/'): + # Patch file instead + return self.send_patched_file(path, ctype) + else: + mode = 'rb' + try: + f = open(path, mode) + except IOError: + return self.send_404() + return self.send_file(f, ctype) + + def send_file(self, f, ctype): + # Patch files to add links, but skip binary files. + self.send_response(200) + self.send_header("Content-type", ctype) + fs = os.fstat(f.fileno()) + self.send_header("Content-Length", str(fs[6])) + self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) + self.end_headers() + return f + + def send_string(self, s, ctype='text/html', headers=True, mtime=None): + encoded_s = s.encode() + if headers: + self.send_response(200) + self.send_header("Content-type", ctype) + self.send_header("Content-Length", str(len(encoded_s))) + if mtime is None: + mtime = self.dynamic_mtime + self.send_header("Last-Modified", self.date_time_string(mtime)) + self.end_headers() + return BytesIO(encoded_s) + + def send_patched_file(self, path, ctype): + # Allow a very limited set of variables. This is pretty gross. + variables = {} + variables['report'] = '' + m = kReportFileRE.match(path) + if m: + variables['report'] = m.group(2) + + try: + f = open(path,'rb') + except IOError: + return self.send_404() + fs = os.fstat(f.fileno()) + data = f.read().decode('utf-8') + for a,b in kReportReplacements: + data = a.sub(b % variables, data) + return self.send_string(data, ctype, mtime=fs.st_mtime) + + +def create_server(address, options, root): + import Reporter + + reporters = Reporter.getReporters() + + return ScanViewServer(address, ScanViewRequestHandler, + root, + reporters, + options) diff --git a/gnu/llvm/clang/tools/scan-view/share/bugcatcher.ico b/gnu/llvm/clang/tools/scan-view/share/bugcatcher.ico Binary files differnew file mode 100644 index 00000000000..22d39b59203 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/share/bugcatcher.ico diff --git a/gnu/llvm/clang/tools/scan-view/share/startfile.py b/gnu/llvm/clang/tools/scan-view/share/startfile.py new file mode 100644 index 00000000000..9eb548bc432 --- /dev/null +++ b/gnu/llvm/clang/tools/scan-view/share/startfile.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Utility for opening a file using the default application in a cross-platform +manner. Modified from http://code.activestate.com/recipes/511443/. +""" + +__version__ = '1.1x' +__all__ = ['open'] + +import os +import sys +import webbrowser +import subprocess + +_controllers = {} +_open = None + + +class BaseController(object): + '''Base class for open program controllers.''' + + def __init__(self, name): + self.name = name + + def open(self, filename): + raise NotImplementedError + + +class Controller(BaseController): + '''Controller for a generic open program.''' + + def __init__(self, *args): + super(Controller, self).__init__(os.path.basename(args[0])) + self.args = list(args) + + def _invoke(self, cmdline): + if sys.platform[:3] == 'win': + closefds = False + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + else: + closefds = True + startupinfo = None + + if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or + sys.platform == 'darwin'): + inout = file(os.devnull, 'r+') + else: + # for TTY programs, we need stdin/out + inout = None + + # if possible, put the child precess in separate process group, + # so keyboard interrupts don't affect child precess as well as + # Python + setsid = getattr(os, 'setsid', None) + if not setsid: + setsid = getattr(os, 'setpgrp', None) + + pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout, + stderr=inout, close_fds=closefds, + preexec_fn=setsid, startupinfo=startupinfo) + + # It is assumed that this kind of tools (gnome-open, kfmclient, + # exo-open, xdg-open and open for OSX) immediately exit after launching + # the specific application + returncode = pipe.wait() + if hasattr(self, 'fixreturncode'): + returncode = self.fixreturncode(returncode) + return not returncode + + def open(self, filename): + if isinstance(filename, basestring): + cmdline = self.args + [filename] + else: + # assume it is a sequence + cmdline = self.args + filename + try: + return self._invoke(cmdline) + except OSError: + return False + + +# Platform support for Windows +if sys.platform[:3] == 'win': + + class Start(BaseController): + '''Controller for the win32 start program through os.startfile.''' + + def open(self, filename): + try: + os.startfile(filename) + except WindowsError: + # [Error 22] No application is associated with the specified + # file for this operation: '<URL>' + return False + else: + return True + + _controllers['windows-default'] = Start('start') + _open = _controllers['windows-default'].open + + +# Platform support for MacOS +elif sys.platform == 'darwin': + _controllers['open']= Controller('open') + _open = _controllers['open'].open + + +# Platform support for Unix +else: + + try: + from commands import getoutput + except ImportError: + from subprocess import getoutput + + # @WARNING: use the private API of the webbrowser module + from webbrowser import _iscommand + + class KfmClient(Controller): + '''Controller for the KDE kfmclient program.''' + + def __init__(self, kfmclient='kfmclient'): + super(KfmClient, self).__init__(kfmclient, 'exec') + self.kde_version = self.detect_kde_version() + + def detect_kde_version(self): + kde_version = None + try: + info = getoutput('kde-config --version') + + for line in info.splitlines(): + if line.startswith('KDE'): + kde_version = line.split(':')[-1].strip() + break + except (OSError, RuntimeError): + pass + + return kde_version + + def fixreturncode(self, returncode): + if returncode is not None and self.kde_version > '3.5.4': + return returncode + else: + return os.EX_OK + + def detect_desktop_environment(): + '''Checks for known desktop environments + + Return the desktop environments name, lowercase (kde, gnome, xfce) + or "generic" + + ''' + + desktop_environment = 'generic' + + if os.environ.get('KDE_FULL_SESSION') == 'true': + desktop_environment = 'kde' + elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): + desktop_environment = 'gnome' + else: + try: + info = getoutput('xprop -root _DT_SAVE_MODE') + if ' = "xfce4"' in info: + desktop_environment = 'xfce' + except (OSError, RuntimeError): + pass + + return desktop_environment + + + def register_X_controllers(): + if _iscommand('kfmclient'): + _controllers['kde-open'] = KfmClient() + + for command in ('gnome-open', 'exo-open', 'xdg-open'): + if _iscommand(command): + _controllers[command] = Controller(command) + + def get(): + controllers_map = { + 'gnome': 'gnome-open', + 'kde': 'kde-open', + 'xfce': 'exo-open', + } + + desktop_environment = detect_desktop_environment() + + try: + controller_name = controllers_map[desktop_environment] + return _controllers[controller_name].open + + except KeyError: + if 'xdg-open' in _controllers: + return _controllers['xdg-open'].open + else: + return webbrowser.open + + + if os.environ.get("DISPLAY"): + register_X_controllers() + _open = get() + + +def open(filename): + '''Open a file or a URL in the registered default application.''' + + return _open(filename) |